48972f8cad
Add a new option rom for the multiboot loader, using DMA transfers to copy data instead of "rep insb". This significantly lowers QEMU's startup latency by a factor of about 40, for example, going from 30sec to 0.8sec when loading modules of 120MB in size. Signed-off-by: Marcus Hähnel <marcus.haehnel@kernkonzept.com> Signed-off-by: Adam Lackorzynski <adam@l4re.org> [Modified to keep the non-DMA code depending on #ifdef USE_FW_CFG_DMA; do not write below stack. - Paolo] Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
231 lines
5.6 KiB
C
231 lines
5.6 KiB
C
/*
|
|
* Common Option ROM Functions
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
* Copyright Novell Inc, 2009
|
|
* Authors: Alexander Graf <agraf@suse.de>
|
|
*/
|
|
|
|
|
|
#define FW_CFG_KERNEL_ADDR 0x07
|
|
#define FW_CFG_KERNEL_SIZE 0x08
|
|
#define FW_CFG_KERNEL_CMDLINE 0x09
|
|
#define FW_CFG_INITRD_ADDR 0x0a
|
|
#define FW_CFG_INITRD_SIZE 0x0b
|
|
#define FW_CFG_KERNEL_ENTRY 0x10
|
|
#define FW_CFG_KERNEL_DATA 0x11
|
|
#define FW_CFG_INITRD_DATA 0x12
|
|
#define FW_CFG_CMDLINE_ADDR 0x13
|
|
#define FW_CFG_CMDLINE_SIZE 0x14
|
|
#define FW_CFG_CMDLINE_DATA 0x15
|
|
#define FW_CFG_SETUP_ADDR 0x16
|
|
#define FW_CFG_SETUP_SIZE 0x17
|
|
#define FW_CFG_SETUP_DATA 0x18
|
|
|
|
#define BIOS_CFG_IOPORT_CFG 0x510
|
|
#define BIOS_CFG_IOPORT_DATA 0x511
|
|
|
|
#define FW_CFG_DMA_CTL_ERROR 0x01
|
|
#define FW_CFG_DMA_CTL_READ 0x02
|
|
#define FW_CFG_DMA_CTL_SKIP 0x04
|
|
#define FW_CFG_DMA_CTL_SELECT 0x08
|
|
#define FW_CFG_DMA_CTL_WRITE 0x10
|
|
|
|
#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */
|
|
|
|
#define BIOS_CFG_DMA_ADDR_HIGH 0x514
|
|
#define BIOS_CFG_DMA_ADDR_LOW 0x518
|
|
|
|
/* Break the translation block flow so -d cpu shows us values */
|
|
#define DEBUG_HERE \
|
|
jmp 1f; \
|
|
1:
|
|
|
|
/*
|
|
* Read a variable from the fw_cfg device.
|
|
* Clobbers: %edx
|
|
* Out: %eax
|
|
*/
|
|
.macro read_fw VAR
|
|
mov $\VAR, %ax
|
|
mov $BIOS_CFG_IOPORT_CFG, %dx
|
|
outw %ax, (%dx)
|
|
mov $BIOS_CFG_IOPORT_DATA, %dx
|
|
inb (%dx), %al
|
|
shl $8, %eax
|
|
inb (%dx), %al
|
|
shl $8, %eax
|
|
inb (%dx), %al
|
|
shl $8, %eax
|
|
inb (%dx), %al
|
|
bswap %eax
|
|
.endm
|
|
|
|
|
|
/*
|
|
* Read data from the fw_cfg device using DMA.
|
|
* Clobbers: %edx, %eax, ADDR, SIZE, memory[%esp-16] to memory[%esp]
|
|
*/
|
|
.macro read_fw_dma VAR, SIZE, ADDR
|
|
/* Address */
|
|
bswapl \ADDR
|
|
pushl \ADDR
|
|
|
|
/* We only support 32 bit target addresses */
|
|
xorl %eax, %eax
|
|
pushl %eax
|
|
mov $BIOS_CFG_DMA_ADDR_HIGH, %dx
|
|
outl %eax, (%dx)
|
|
|
|
/* Size */
|
|
bswapl \SIZE
|
|
pushl \SIZE
|
|
|
|
/* Control */
|
|
movl $(\VAR << 16) | (FW_CFG_DMA_CTL_READ | FW_CFG_DMA_CTL_SELECT), %eax
|
|
bswapl %eax
|
|
pushl %eax
|
|
|
|
movl %esp, %eax /* Address of the struct we generated */
|
|
bswapl %eax
|
|
mov $BIOS_CFG_DMA_ADDR_LOW, %dx
|
|
outl %eax, (%dx) /* Initiate DMA */
|
|
|
|
1: mov (%esp), %eax /* Wait for completion */
|
|
bswapl %eax
|
|
testl $~FW_CFG_DMA_CTL_ERROR, %eax
|
|
jnz 1b
|
|
addl $16, %esp
|
|
.endm
|
|
|
|
|
|
/*
|
|
* Read a blob from the fw_cfg device using DMA
|
|
* Requires _ADDR, _SIZE and _DATA values for the parameter.
|
|
*
|
|
* Clobbers: %eax, %edx, %es, %ecx, %edi and adresses %esp-20 to %esp
|
|
*/
|
|
#ifdef USE_FW_CFG_DMA
|
|
#define read_fw_blob_dma(var) \
|
|
read_fw var ## _SIZE; \
|
|
mov %eax, %ecx; \
|
|
read_fw var ## _ADDR; \
|
|
mov %eax, %edi ;\
|
|
read_fw_dma var ## _DATA, %ecx, %edi
|
|
#else
|
|
#define read_fw_blob_dma(var) read_fw_blob(var)
|
|
#endif
|
|
|
|
#define read_fw_blob_pre(var) \
|
|
read_fw var ## _SIZE; \
|
|
mov %eax, %ecx; \
|
|
mov $var ## _DATA, %ax; \
|
|
mov $BIOS_CFG_IOPORT_CFG, %edx; \
|
|
outw %ax, (%dx); \
|
|
mov $BIOS_CFG_IOPORT_DATA, %dx; \
|
|
cld
|
|
|
|
/*
|
|
* Read a blob from the fw_cfg device.
|
|
* Requires _ADDR, _SIZE and _DATA values for the parameter.
|
|
*
|
|
* Clobbers: %eax, %edx, %es, %ecx, %edi
|
|
*/
|
|
#define read_fw_blob(var) \
|
|
read_fw var ## _ADDR; \
|
|
mov %eax, %edi; \
|
|
read_fw_blob_pre(var); \
|
|
/* old as(1) doesn't like this insn so emit the bytes instead: \
|
|
rep insb (%dx), %es:(%edi); \
|
|
*/ \
|
|
.dc.b 0xf3,0x6c
|
|
|
|
/*
|
|
* Read a blob from the fw_cfg device in forced addr32 mode.
|
|
* Requires _ADDR, _SIZE and _DATA values for the parameter.
|
|
*
|
|
* Clobbers: %eax, %edx, %es, %ecx, %edi
|
|
*/
|
|
#define read_fw_blob_addr32(var) \
|
|
read_fw var ## _ADDR; \
|
|
mov %eax, %edi; \
|
|
read_fw_blob_pre(var); \
|
|
/* old as(1) doesn't like this insn so emit the bytes instead: \
|
|
addr32 rep insb (%dx), %es:(%edi); \
|
|
*/ \
|
|
.dc.b 0x67,0xf3,0x6c
|
|
|
|
/*
|
|
* Read a blob from the fw_cfg device in forced addr32 mode, address is in %edi.
|
|
* Requires _SIZE and _DATA values for the parameter.
|
|
*
|
|
* Clobbers: %eax, %edx, %edi, %es, %ecx
|
|
*/
|
|
#define read_fw_blob_addr32_edi(var) \
|
|
read_fw_blob_pre(var); \
|
|
/* old as(1) doesn't like this insn so emit the bytes instead: \
|
|
addr32 rep insb (%dx), %es:(%edi); \
|
|
*/ \
|
|
.dc.b 0x67,0xf3,0x6c
|
|
|
|
#define OPTION_ROM_START \
|
|
.code16; \
|
|
.text; \
|
|
.global _start; \
|
|
_start:; \
|
|
.short 0xaa55; \
|
|
.byte (_end - _start) / 512;
|
|
|
|
#define BOOT_ROM_START \
|
|
OPTION_ROM_START \
|
|
lret; \
|
|
.org 0x18; \
|
|
.short 0; \
|
|
.short _pnph; \
|
|
_pnph: \
|
|
.ascii "$PnP"; \
|
|
.byte 0x01; \
|
|
.byte ( _pnph_len / 16 ); \
|
|
.short 0x0000; \
|
|
.byte 0x00; \
|
|
.byte 0x00; \
|
|
.long 0x00000000; \
|
|
.short _manufacturer; \
|
|
.short _product; \
|
|
.long 0x00000000; \
|
|
.short 0x0000; \
|
|
.short 0x0000; \
|
|
.short _bev; \
|
|
.short 0x0000; \
|
|
.short 0x0000; \
|
|
.equ _pnph_len, . - _pnph; \
|
|
_bev:; \
|
|
/* DS = CS */ \
|
|
movw %cs, %ax; \
|
|
movw %ax, %ds;
|
|
|
|
#define OPTION_ROM_END \
|
|
.byte 0; \
|
|
.align 512, 0; \
|
|
_end:
|
|
|
|
#define BOOT_ROM_END \
|
|
_manufacturer:; \
|
|
.asciz "QEMU"; \
|
|
_product:; \
|
|
.asciz BOOT_ROM_PRODUCT; \
|
|
OPTION_ROM_END
|
|
|