These patches, with the support in QEMU (patches in the QEMU mailing list) make the Linux kernel setup and boot approximately 10x faster.
TODO: * Other ways to boot when detecting fw_cfg DMA interface? * Is 0xfef00000 a good address? Is there any better address? Is it possible to not make it hardcoded?
Marc Marí (2): Add QEMU fw_cfg dma interface support Add Linux boot code for the fw_cfg dma interface
src/boot.c | 14 ++- src/config.h | 1 + src/fw/paravirt.c | 298 +++++++++++++++++++++++++++++++++++++++++++----------- src/fw/paravirt.h | 50 +++++++++ src/romlayout.S | 20 ++++ 5 files changed, 321 insertions(+), 62 deletions(-)
Add detection and support for the QEMU fw_cfg dma interface.
Signed-off-by: Marc Marí markmb@redhat.com --- src/config.h | 1 + src/fw/paravirt.c | 230 +++++++++++++++++++++++++++++++++++++++--------------- src/fw/paravirt.h | 49 ++++++++++++ 3 files changed, 219 insertions(+), 61 deletions(-)
diff --git a/src/config.h b/src/config.h index 4bfebe8..fdce374 100644 --- a/src/config.h +++ b/src/config.h @@ -41,6 +41,7 @@ #define BUILD_BIOS_TMP_ADDR 0x30000 #define BUILD_SMM_INIT_ADDR 0x30000 #define BUILD_SMM_ADDR 0xa0000 +#define BUILD_CFG_DMA_ADDR 0xfef00000
#define BUILD_PCIMEM_START 0xe0000000 #define BUILD_PCIMEM_END 0xfec00000 /* IOAPIC is mapped at */ diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c index db22ae8..b7ab169 100644 --- a/src/fw/paravirt.c +++ b/src/fw/paravirt.c @@ -30,6 +30,13 @@ u32 RamSize; u64 RamSizeOver4G; // Type of emulator platform. int PlatformRunningOn VARFSEG; +// cfg_dma enabled +int cfg_dma_enabled = 0; + +int qemu_cfg_dma_enabled(void) +{ + return cfg_dma_enabled; +}
/* This CPUID returns the signature 'KVMKVMKVM' in ebx, ecx, and edx. It * should be used to determine that a VM is running under KVM. @@ -177,21 +184,6 @@ qemu_platform_setup(void) * QEMU firmware config (fw_cfg) interface ****************************************************************/
-// List of QEMU fw_cfg entries. DO NOT ADD MORE. (All new content -// should be passed via the fw_cfg "file" interface.) -#define QEMU_CFG_SIGNATURE 0x00 -#define QEMU_CFG_ID 0x01 -#define QEMU_CFG_UUID 0x02 -#define QEMU_CFG_NUMA 0x0d -#define QEMU_CFG_BOOT_MENU 0x0e -#define QEMU_CFG_MAX_CPUS 0x0f -#define QEMU_CFG_FILE_DIR 0x19 -#define QEMU_CFG_ARCH_LOCAL 0x8000 -#define QEMU_CFG_ACPI_TABLES (QEMU_CFG_ARCH_LOCAL + 0) -#define QEMU_CFG_SMBIOS_ENTRIES (QEMU_CFG_ARCH_LOCAL + 1) -#define QEMU_CFG_IRQ0_OVERRIDE (QEMU_CFG_ARCH_LOCAL + 2) -#define QEMU_CFG_E820_TABLE (QEMU_CFG_ARCH_LOCAL + 3) - static void qemu_cfg_select(u16 f) { @@ -212,10 +204,45 @@ qemu_cfg_skip(int len) }
static void +qemu_cfg_dma_writel(void *addr, u32 value) +{ + writel(addr, cpu_to_be32(value)); +} + +static void +qemu_cfg_dma_writew(void *addr, u16 value) +{ + writew(addr, cpu_to_be16(value)); +} + +static u32 +qemu_cfg_dma_readl(void *addr) +{ + return be32_to_cpu(readl(addr)); +} + +static void +qemu_cfg_dma_read_entry(void *buf, int e, int offset, int len) +{ + qemu_cfg_dma_writew(QEMU_CFG_DMA_CTL, e); + qemu_cfg_dma_writel(QEMU_CFG_DMA_ADDR_LO, (u32)buf & 0xffffffff); + qemu_cfg_dma_writel(QEMU_CFG_DMA_ADDR_HI, 0); + qemu_cfg_dma_writel(QEMU_CFG_DMA_LEN, len); + qemu_cfg_dma_writel(QEMU_CFG_DMA_OFFSET, offset); + qemu_cfg_dma_writel(QEMU_CFG_DMA_CONTROL, QEMU_CFG_DMA_CTL_READ); + while(qemu_cfg_dma_readl(QEMU_CFG_DMA_LEN) != 0 && + !(qemu_cfg_dma_readl(QEMU_CFG_DMA_CONTROL) & QEMU_CFG_DMA_CTL_ERROR)); +} + +static void qemu_cfg_read_entry(void *buf, int e, int len) { - qemu_cfg_select(e); - qemu_cfg_read(buf, len); + if (cfg_dma_enabled) { + qemu_cfg_dma_read_entry(buf, e, 0, len); + } else { + qemu_cfg_select(e); + qemu_cfg_read(buf, len); + } }
struct qemu_romfile_s { @@ -230,9 +257,14 @@ qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen) return -1; struct qemu_romfile_s *qfile; qfile = container_of(file, struct qemu_romfile_s, file); - qemu_cfg_select(qfile->select); - qemu_cfg_skip(qfile->skip); - qemu_cfg_read(dst, file->size); + + if (cfg_dma_enabled) { + qemu_cfg_dma_read_entry(dst, qfile->select, qfile->skip, file->size); + } else { + qemu_cfg_select(qfile->select); + qemu_cfg_skip(qfile->skip); + qemu_cfg_read(dst, file->size); + } return file->size; }
@@ -320,11 +352,24 @@ qemu_cfg_e820(void) u32 count32; qemu_cfg_read_entry(&count32, QEMU_CFG_E820_TABLE, sizeof(count32)); if (count32) { - struct e820_reservation entry; - int i; - for (i = 0; i < count32; i++) { - qemu_cfg_read(&entry, sizeof(entry)); - add_e820(entry.address, entry.length, entry.type); + if (cfg_dma_enabled) { + struct e820_reservation entry; + int i, offset = sizeof(count32); + + for (i = 0; i < count32; i++) { + qemu_cfg_dma_read_entry(&entry, QEMU_CFG_E820_TABLE, + offset, sizeof(entry)); + add_e820(entry.address, entry.length, entry.type); + offset += sizeof(entry); + } + } else { + struct e820_reservation entry; + int i; + + for (i = 0; i < count32; i++) { + qemu_cfg_read(&entry, sizeof(entry)); + add_e820(entry.address, entry.length, entry.type); + } } } else if (runningOnKVM()) { // Backwards compatibility - provide hard coded range. @@ -369,37 +414,71 @@ qemu_cfg_legacy(void) char name[128]; u16 cnt; qemu_cfg_read_entry(&cnt, QEMU_CFG_ACPI_TABLES, sizeof(cnt)); + int i, offset = sizeof(cnt); - for (i = 0; i < cnt; i++) { - u16 len; - qemu_cfg_read(&len, sizeof(len)); - offset += sizeof(len); - snprintf(name, sizeof(name), "acpi/table%d", i); - qemu_romfile_add(name, QEMU_CFG_ACPI_TABLES, offset, len); - qemu_cfg_skip(len); - offset += len; + if (cfg_dma_enabled) { + for (i = 0; i < cnt; i++) { + u16 len; + qemu_cfg_dma_read_entry(&len, QEMU_CFG_ACPI_TABLES, + offset, sizeof(len)); + offset += sizeof(len); + snprintf(name, sizeof(name), "acpi/table%d", i); + qemu_romfile_add(name, QEMU_CFG_ACPI_TABLES, offset, len); + offset += len; + } + } else { + for (i = 0; i < cnt; i++) { + u16 len; + qemu_cfg_read(&len, sizeof(len)); + offset += sizeof(len); + snprintf(name, sizeof(name), "acpi/table%d", i); + qemu_romfile_add(name, QEMU_CFG_ACPI_TABLES, offset, len); + qemu_cfg_skip(len); + offset += len; + } }
// SMBIOS info qemu_cfg_read_entry(&cnt, QEMU_CFG_SMBIOS_ENTRIES, sizeof(cnt)); offset = sizeof(cnt); - for (i = 0; i < cnt; i++) { - struct qemu_smbios_header header; - qemu_cfg_read(&header, sizeof(header)); - if (header.headertype == SMBIOS_FIELD_ENTRY) { - snprintf(name, sizeof(name), "smbios/field%d-%d" - , header.tabletype, header.fieldoffset); - qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES - , offset + sizeof(header) - , header.length - sizeof(header)); - } else { - snprintf(name, sizeof(name), "smbios/table%d-%d" - , header.tabletype, i); - qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES - , offset + 3, header.length - 3); + if (cfg_dma_enabled) { + for (i = 0; i < cnt; i++) { + struct qemu_smbios_header header; + qemu_cfg_dma_read_entry(&header, QEMU_CFG_SMBIOS_ENTRIES, + offset, sizeof(header)); + if (header.headertype == SMBIOS_FIELD_ENTRY) { + snprintf(name, sizeof(name), "smbios/field%d-%d" + , header.tabletype, header.fieldoffset); + qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES + , offset + sizeof(header) + , header.length - sizeof(header)); + } else { + snprintf(name, sizeof(name), "smbios/table%d-%d" + , header.tabletype, i); + qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES + , offset + 3, header.length - 3); + } + offset += header.length; + } + } else { + for (i = 0; i < cnt; i++) { + struct qemu_smbios_header header; + qemu_cfg_read(&header, sizeof(header)); + if (header.headertype == SMBIOS_FIELD_ENTRY) { + snprintf(name, sizeof(name), "smbios/field%d-%d" + , header.tabletype, header.fieldoffset); + qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES + , offset + sizeof(header) + , header.length - sizeof(header)); + } else { + snprintf(name, sizeof(name), "smbios/table%d-%d" + , header.tabletype, i); + qemu_romfile_add(name, QEMU_CFG_SMBIOS_ENTRIES + , offset + 3, header.length - 3); + } + qemu_cfg_skip(header.length - sizeof(header)); + offset += header.length; } - qemu_cfg_skip(header.length - sizeof(header)); - offset += header.length; } }
@@ -415,13 +494,29 @@ void qemu_cfg_init(void) if (!runningOnQEMU()) return;
+ // Detect fw_cfg DMA interface + qemu_cfg_dma_writel(QEMU_CFG_DMA_ADDR_LO, 0x12345678); + if (qemu_cfg_dma_readl(QEMU_CFG_DMA_ADDR_LO) == 0x12345678) { + char sig[5]; + cfg_dma_enabled = 1; + qemu_cfg_read_entry(sig, QEMU_CFG_SIGNATURE, 4); + sig[4] = '\0'; + if (strcmp(sig, "QEMU") != 0) { + cfg_dma_enabled = 0; + } + } else { + cfg_dma_enabled = 0; + } + // Detect fw_cfg interface. - qemu_cfg_select(QEMU_CFG_SIGNATURE); - char *sig = "QEMU"; - int i; - for (i = 0; i < 4; i++) - if (inb(PORT_QEMU_CFG_DATA) != sig[i]) - return; + if (!cfg_dma_enabled) { + qemu_cfg_select(QEMU_CFG_SIGNATURE); + char *sig = "QEMU"; + int i; + for (i = 0; i < 4; i++) + if (inb(PORT_QEMU_CFG_DATA) != sig[i]) + return; + } dprintf(1, "Found QEMU fw_cfg\n");
// Populate romfiles for legacy fw_cfg entries @@ -431,12 +526,25 @@ void qemu_cfg_init(void) u32 count; qemu_cfg_read_entry(&count, QEMU_CFG_FILE_DIR, sizeof(count)); count = be32_to_cpu(count); - u32 e; - for (e = 0; e < count; e++) { - struct QemuCfgFile qfile; - qemu_cfg_read(&qfile, sizeof(qfile)); - qemu_romfile_add(qfile.name, be16_to_cpu(qfile.select) - , 0, be32_to_cpu(qfile.size)); + + if (cfg_dma_enabled) { + u32 e, offset = sizeof(count); + for (e = 0; e < count; e++) { + struct QemuCfgFile qfile; + qemu_cfg_dma_read_entry(&qfile, QEMU_CFG_FILE_DIR, offset, + sizeof(qfile)); + qemu_romfile_add(qfile.name, be16_to_cpu(qfile.select) + , 0, be32_to_cpu(qfile.size)); + offset += sizeof(qfile); + } + } else { + u32 e; + for (e = 0; e < count; e++) { + struct QemuCfgFile qfile; + qemu_cfg_read(&qfile, sizeof(qfile)); + qemu_romfile_add(qfile.name, be16_to_cpu(qfile.select) + , 0, be32_to_cpu(qfile.size)); + } }
qemu_cfg_e820(); diff --git a/src/fw/paravirt.h b/src/fw/paravirt.h index 95ffb92..a5fe913 100644 --- a/src/fw/paravirt.h +++ b/src/fw/paravirt.h @@ -30,6 +30,55 @@ static inline int runningOnKVM(void) { #define PORT_QEMU_CFG_CTL 0x0510 #define PORT_QEMU_CFG_DATA 0x0511
+// List of QEMU DMA fw_cfg addresses. +#define QEMU_CFG_DMA_DATA (u8 *)(BUILD_CFG_DMA_ADDR + 0) +#define QEMU_CFG_DMA_CTL (u8 *)(BUILD_CFG_DMA_ADDR + 8) +#define QEMU_CFG_DMA_ADDR_LO (u8 *)(BUILD_CFG_DMA_ADDR + 10) +#define QEMU_CFG_DMA_ADDR_HI (u8 *)(BUILD_CFG_DMA_ADDR + 14) +#define QEMU_CFG_DMA_LEN (u8 *)(BUILD_CFG_DMA_ADDR + 18) +#define QEMU_CFG_DMA_OFFSET (u8 *)(BUILD_CFG_DMA_ADDR + 22) +#define QEMU_CFG_DMA_CONTROL (u8 *)(BUILD_CFG_DMA_ADDR + 26) + +/* QEMU_CFG_DMA_CONTROL bits */ +#define QEMU_CFG_DMA_CTL_ERROR 0x01 +#define QEMU_CFG_DMA_CTL_READ 0x02 +#define QEMU_CFG_DMA_CTL_MASK 0x03 + +// List of QEMU fw_cfg entries. DO NOT ADD MORE. (All new content +// should be passed via the fw_cfg "file" interface.) +#define QEMU_CFG_SIGNATURE 0x00 +#define QEMU_CFG_ID 0x01 +#define QEMU_CFG_UUID 0x02 +#define QEMU_CFG_RAM_SIZE 0x03 +#define QEMU_CFG_NOGRAPHIC 0x04 +#define QEMU_CFG_NB_CPUS 0x05 +#define QEMU_CFG_MACHINE_ID 0x06 +#define QEMU_CFG_KERNEL_ADDR 0x07 +#define QEMU_CFG_KERNEL_SIZE 0x08 +#define QEMU_CFG_KERNEL_CMDLINE 0x09 +#define QEMU_CFG_INITRD_ADDR 0x0a +#define QEMU_CFG_INITRD_SIZE 0x0b +#define QEMU_CFG_BOOT_DEVICE 0x0c +#define QEMU_CFG_NUMA 0x0d +#define QEMU_CFG_BOOT_MENU 0x0e +#define QEMU_CFG_MAX_CPUS 0x0f +#define QEMU_CFG_KERNEL_ENTRY 0x10 +#define QEMU_CFG_KERNEL_DATA 0x11 +#define QEMU_CFG_INITRD_DATA 0x12 +#define QEMU_CFG_CMDLINE_ADDR 0x13 +#define QEMU_CFG_CMDLINE_SIZE 0x14 +#define QEMU_CFG_CMDLINE_DATA 0x15 +#define QEMU_CFG_SETUP_ADDR 0x16 +#define QEMU_CFG_SETUP_SIZE 0x17 +#define QEMU_CFG_SETUP_DATA 0x18 +#define QEMU_CFG_FILE_DIR 0x19 +#define QEMU_CFG_ARCH_LOCAL 0x8000 +#define QEMU_CFG_ACPI_TABLES (QEMU_CFG_ARCH_LOCAL + 0) +#define QEMU_CFG_SMBIOS_ENTRIES (QEMU_CFG_ARCH_LOCAL + 1) +#define QEMU_CFG_IRQ0_OVERRIDE (QEMU_CFG_ARCH_LOCAL + 2) +#define QEMU_CFG_E820_TABLE (QEMU_CFG_ARCH_LOCAL + 3) + +int qemu_cfg_dma_enabled(void); void qemu_preinit(void); void qemu_platform_setup(void); void qemu_cfg_init(void);
It's probably not good to override all the ROM options. Better ways can be discussed.
Signed-off-by: Marc Marí markmb@redhat.com --- src/boot.c | 14 +++++++++++- src/fw/paravirt.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/fw/paravirt.h | 1 + src/romlayout.S | 20 ++++++++++++++++ 4 files changed, 102 insertions(+), 1 deletion(-)
diff --git a/src/boot.c b/src/boot.c index e0f73a3..7d187c5 100644 --- a/src/boot.c +++ b/src/boot.c @@ -684,6 +684,14 @@ boot_rom(u32 vector) call_boot_entry(so, 0); }
+// Boot from a linuxboot ROM when QEMU cfg is in DMA mode +static void +boot_linux_cfg_dma(void) +{ + printf("Booting Linux from fw_cfg...\n"); + qemu_cfg_dma_boot_linux(); +} + // Unable to find bootable device - warn user and eventually retry. static void boot_fail(void) @@ -732,7 +740,11 @@ do_boot(int seq_nr) boot_cbfs((void*)ie->vector); break; case IPL_TYPE_BEV: - boot_rom(ie->vector); + if (qemu_cfg_dma_enabled()) { + boot_linux_cfg_dma(); + } else { + boot_rom(ie->vector); + } break; case IPL_TYPE_HALT: boot_fail(); diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c index b7ab169..ada8574 100644 --- a/src/fw/paravirt.c +++ b/src/fw/paravirt.c @@ -23,6 +23,8 @@ #include "util.h" // pci_setup #include "x86.h" // cpuid #include "xen.h" // xen_biostable_setup +#include "bregs.h" // struct bregs +#include "stacks.h" // farcall16big
// Amount of continuous ram under 4Gig u32 RamSize; @@ -554,3 +556,69 @@ void qemu_cfg_init(void) dprintf(1, "Moving pm_base to 0x%x\n", acpi_pm_base); } } + +void qemu_cfg_dma_boot_linux(void) +{ + dprintf(1, "Loading kernel\n"); + void *setup_addr; + u32 setup_size; + qemu_cfg_read_entry(&setup_addr, QEMU_CFG_SETUP_ADDR, 4); + qemu_cfg_read_entry(&setup_size, QEMU_CFG_SETUP_SIZE, 4); + qemu_cfg_read_entry(setup_addr, QEMU_CFG_SETUP_DATA, setup_size); + + if (readl(setup_addr + 0x202) != 0x53726448) { + dprintf(1, "Not valid kernel\n"); + return; + } + + u16 protocol = readw(setup_addr + 0x206); + if (protocol < 0x203) { + dprintf(1, "Old kernel (v %x) not supported\n", protocol); + return; + } + + void *kernel_addr; + u32 kernel_size; + + qemu_cfg_read_entry(&kernel_addr, QEMU_CFG_KERNEL_ADDR, 4); + qemu_cfg_read_entry(&kernel_size, QEMU_CFG_KERNEL_SIZE, 4); + qemu_cfg_read_entry(kernel_addr, QEMU_CFG_KERNEL_DATA, kernel_size); + + void *cmdline_addr; + u32 cmdline_size; + qemu_cfg_read_entry(&cmdline_addr, QEMU_CFG_CMDLINE_ADDR, 4); + qemu_cfg_read_entry(&cmdline_size, QEMU_CFG_CMDLINE_SIZE, 4); + if (cmdline_size) { + qemu_cfg_read_entry(cmdline_addr, QEMU_CFG_CMDLINE_DATA, cmdline_size); + } + + void *initrd_addr; + u32 initrd_size; + qemu_cfg_read_entry(&initrd_addr, QEMU_CFG_INITRD_ADDR, 4); + qemu_cfg_read_entry(&initrd_size, QEMU_CFG_INITRD_SIZE, 4); + if (initrd_size) { + qemu_cfg_read_entry(initrd_addr, QEMU_CFG_INITRD_DATA, initrd_size); + } + + // Last configurations + writel(setup_addr + 0x228, cmdline_addr); + writeb(setup_addr + 0x210, 0xB0); + writeb(setup_addr + 0x211, readb(setup_addr + 0x211) | 0x80); + writew(setup_addr + 0x224, cmdline_addr - setup_addr - 0x200); + writel(setup_addr + 0x218, initrd_addr); + writel(setup_addr + 0x21c, initrd_size); + + dprintf(1, "Jumping to kernel %d@%x %d@%x %d@%x %d@%x\n" + , setup_size, (u32)setup_addr, cmdline_size, (u32)cmdline_addr + , kernel_size, (u32)kernel_addr, initrd_size, (u32)initrd_addr); + struct bregs br; + memset(&br, 0, sizeof(br)); + extern void kernel_stub(void); + br.ebx = (u32)setup_addr >> 4; + br.edx = (u32)cmdline_addr - (u32)setup_addr - 16; + br.code = SEGOFF(SEG_BIOS, (u32)kernel_stub - BUILD_BIOS_ADDR); + + outb(0xa, 0xf4); + + farcall16big(&br); +} diff --git a/src/fw/paravirt.h b/src/fw/paravirt.h index a5fe913..c6a97c4 100644 --- a/src/fw/paravirt.h +++ b/src/fw/paravirt.h @@ -82,5 +82,6 @@ int qemu_cfg_dma_enabled(void); void qemu_preinit(void); void qemu_platform_setup(void); void qemu_cfg_init(void); +void qemu_cfg_dma_boot_linux();
#endif diff --git a/src/romlayout.S b/src/romlayout.S index 7938e22..1c641c2 100644 --- a/src/romlayout.S +++ b/src/romlayout.S @@ -196,6 +196,26 @@ __farcall16: IRQ_TRAMPOLINE 1c IRQ_TRAMPOLINE 4a
+ DECLFUNC kernel_stub +kernel_stub: + movw %bx, %ds + movw %bx, %es + movw %bx, %fs + movw %bx, %gs + movw %bx, %ss + movl %edx, %esp + addw $0x20, %bx + pushw %bx // push CS + pushw $0 // push IP + xorl %eax, %eax + xorl %ebx, %ebx + xorl %ecx, %ecx + xorl %edx, %edx + xorl %edi, %edi + xorl %esi, %esi + xorl %ebp, %ebp + lretw +
/**************************************************************** * Misc. entry points.
On Di, 2015-07-21 at 18:06 +0200, Marc Marí wrote:
It's probably not good to override all the ROM options. Better ways can be discussed.
source code for the option rom doing linux kernel (and multiboot kernel) boot is in pc-bios/optionrom/ (qemu tree). Patching that one to use fw_cfg dma if available should get simliar speedups, but without compatiblity issues.
cheers, Gerd
On Wed, 19 Aug 2015 00:27:54 +0200 Gerd Hoffmann kraxel@redhat.com wrote:
On Di, 2015-07-21 at 18:06 +0200, Marc Marí wrote:
It's probably not good to override all the ROM options. Better ways can be discussed.
source code for the option rom doing linux kernel (and multiboot kernel) boot is in pc-bios/optionrom/ (qemu tree). Patching that one to use fw_cfg dma if available should get simliar speedups, but without compatiblity issues.
The problem with patching the current ROMs, is that they are written in assembly. So they would need a complete rewrite in C. Writing a DMA guest in assembly I think it is a nightmare (but it can be done, of course).
But it is true that I overlooked that point. I might get into this change in the future.
Thanks Marc
On Mi, 2015-08-19 at 09:57 +0200, Marc Marí wrote:
On Wed, 19 Aug 2015 00:27:54 +0200 Gerd Hoffmann kraxel@redhat.com wrote:
On Di, 2015-07-21 at 18:06 +0200, Marc Marí wrote:
It's probably not good to override all the ROM options. Better ways can be discussed.
source code for the option rom doing linux kernel (and multiboot kernel) boot is in pc-bios/optionrom/ (qemu tree). Patching that one to use fw_cfg dma if available should get simliar speedups, but without compatiblity issues.
The problem with patching the current ROMs, is that they are written in assembly. So they would need a complete rewrite in C. Writing a DMA guest in assembly I think it is a nightmare (but it can be done, of course).
Yes, doesn't look that easy. Especially when probing for the dma fwcfg at rumtime. We could have just two versions of the roms and let qemu pick the correct one depending in whenever fwcfg dma is enabled or not.
Doing this in seabios looks appealing too as seabios needs the code for fw_cfg support anyway and it is already written in C. It needs some more care through so things like bootorder and pxeboot continue to work. Maybe seabios could match the rom name (the one which appears in the boot menu) to figure whenever it should kick a direct kernel boot.
cheers, Gerd
On Wed, 19 Aug 2015 19:31:30 +0200 Gerd Hoffmann kraxel@redhat.com wrote:
On Mi, 2015-08-19 at 09:57 +0200, Marc Marí wrote:
On Wed, 19 Aug 2015 00:27:54 +0200 Gerd Hoffmann kraxel@redhat.com wrote:
On Di, 2015-07-21 at 18:06 +0200, Marc Marí wrote:
It's probably not good to override all the ROM options. Better ways can be discussed.
source code for the option rom doing linux kernel (and multiboot kernel) boot is in pc-bios/optionrom/ (qemu tree). Patching that one to use fw_cfg dma if available should get simliar speedups, but without compatiblity issues.
The problem with patching the current ROMs, is that they are written in assembly. So they would need a complete rewrite in C. Writing a DMA guest in assembly I think it is a nightmare (but it can be done, of course).
Yes, doesn't look that easy. Especially when probing for the dma fwcfg at rumtime. We could have just two versions of the roms and let qemu pick the correct one depending in whenever fwcfg dma is enabled or not.
Doing this in seabios looks appealing too as seabios needs the code for fw_cfg support anyway and it is already written in C. It needs some more care through so things like bootorder and pxeboot continue to work. Maybe seabios could match the rom name (the one which appears in the boot menu) to figure whenever it should kick a direct kernel boot.
Have you looked at the other email thread?: http://www.seabios.org/pipermail/seabios/2015-August/009603.html
There's some work missing, but a basic sketch of this ROM problem is here: http://www.seabios.org/pipermail/seabios/2015-August/009606.html
Thanks Marc
Hi,
Doing this in seabios looks appealing too as seabios needs the code for fw_cfg support anyway and it is already written in C. It needs some more care through so things like bootorder and pxeboot continue to work. Maybe seabios could match the rom name (the one which appears in the boot menu) to figure whenever it should kick a direct kernel boot.
Have you looked at the other email thread?: http://www.seabios.org/pipermail/seabios/2015-August/009603.html
There's some work missing, but a basic sketch of this ROM problem is here: http://www.seabios.org/pipermail/seabios/2015-August/009606.html
Yep, saw that. pxe roms are IPL_TYPE_BEV too, so taking over this way will break network boot.
qemu_cfg_dma_boot_linux() itself looks sane to me. Just the way it gets kicked needs some refining. And in the future we probably want split qemu_cfg_dma_boot_linux() into smaller functions for stuff like booting the kernel from flash.
cheers, Gerd
On Wed, Aug 19, 2015 at 07:31:30PM +0200, Gerd Hoffmann wrote:
Doing this in seabios looks appealing too as seabios needs the code for fw_cfg support anyway and it is already written in C. It needs some more care through so things like bootorder and pxeboot continue to work. Maybe seabios could match the rom name (the one which appears in the boot menu) to figure whenever it should kick a direct kernel boot.
If new IPL code is added to SeaBIOS for vmlinux booting (similar to the ipl code for cbfs payload booting), then it should be possible for qemu to add an entry to the bootorder file to instruct SeaBIOS to boot that IPL first. I think that method should coexist with bootorder/pxeboot without any special work.
-Kevin
On Tue, Jul 21, 2015 at 06:06:09PM +0200, Marc Marí wrote:
These patches, with the support in QEMU (patches in the QEMU mailing list) make the Linux kernel setup and boot approximately 10x faster.
Thanks. How do the timings with this support compare with the qboot and seabios+cbfs runs?
-Kevin
On Tue, 21 Jul 2015 12:47:47 -0400 "Kevin O'Connor" kevin@koconnor.net wrote:
On Tue, Jul 21, 2015 at 06:06:09PM +0200, Marc Marí wrote:
These patches, with the support in QEMU (patches in the QEMU mailing list) make the Linux kernel setup and boot approximately 10x faster.
Thanks. How do the timings with this support compare with the qboot and seabios+cbfs runs?
-Kevin
The boot time for a standard SeaBIOS (with the default configuration options) is this:
QEMU startup time: .032 BIOS startup time: .154 fw_cfg setup time: .375 Total time: .562
The boot time for a standard SeaBIOS with the CBFS patch is this:
QEMU startup time: .036 BIOS startup time: .184 Kernel setup time: .003 Total time: .223
The boot time for a standard SeaBIOS with the fw_cfg DMA interface patch is this:
QEMU startup time: .036 BIOS startup time: .140 Kernel setup time: .026 Total time: .202
The time of getting the kernel image ready and boot it has improved by 10x, although the time is not as good as with CBFS.
But the BIOS time, with all the setup from the fw_cfg interface, has also improved.
There are some pending optimizations in the host and in this code that can make these times even better. I'll compare things more in detail when these are done.
Thanks Marc
On Wed, Jul 22, 2015 at 05:01:02PM +0200, Marc Marí wrote:
On Tue, 21 Jul 2015 12:47:47 -0400 "Kevin O'Connor" kevin@koconnor.net wrote:
On Tue, Jul 21, 2015 at 06:06:09PM +0200, Marc Marí wrote:
These patches, with the support in QEMU (patches in the QEMU mailing list) make the Linux kernel setup and boot approximately 10x faster.
Thanks. How do the timings with this support compare with the qboot and seabios+cbfs runs?
The boot time for a standard SeaBIOS (with the default configuration options) is this:
QEMU startup time: .032 BIOS startup time: .154 fw_cfg setup time: .375 Total time: .562
The boot time for a standard SeaBIOS with the CBFS patch is this:
QEMU startup time: .036 BIOS startup time: .184 Kernel setup time: .003 Total time: .223
The boot time for a standard SeaBIOS with the fw_cfg DMA interface patch is this:
QEMU startup time: .036 BIOS startup time: .140 Kernel setup time: .026 Total time: .202
Thanks. In case anyone is curious, if your runs are similar to my runs then the standard SeaBIOS time is likely:
100ms - ps2port reset wait (see ugly patch below to get around it) ~18ms - vga init (can avoid by adding "-device VGA,romfile=" to qemu) ~12ms - shadow ram enable/lock ~10ms - debug writes (avoid with seabios CONFIG_DEBUG_LEVEL=0) ~4ms - pci init 2ms - cpu timestamp counter calibration
-Kevin
--- a/src/hw/ps2port.c +++ b/src/hw/ps2port.c @@ -6,6 +6,7 @@ // This file may be distributed under the terms of the GNU LGPLv3 license.
#include "biosvar.h" // GET_LOW +#include "fw/paravirt.h" // runningOnQEMU #include "output.h" // dprintf #include "pic.h" // pic_eoi1 #include "ps2port.h" // ps2_kbd_command @@ -481,7 +482,8 @@ ps2_keyboard_setup(void *data) int spinupdelay = romfile_loadint("etc/ps2-keyboard-spinup", 0); u32 end = timer_calc(spinupdelay); for (;;) { - ret = ps2_kbd_command(ATKBD_CMD_RESET_BAT, param); + int cmd = runningOnQEMU() ? ATKBD_CMD_RESET_BAT1 : ATKBD_CMD_RESET_BAT; + ret = ps2_kbd_command(cmd, param); if (!ret) break; if (timer_check(end)) { diff --git a/src/hw/ps2port.h b/src/hw/ps2port.h index dc0e430..2748624 100644 --- a/src/hw/ps2port.h +++ b/src/hw/ps2port.h @@ -27,6 +27,7 @@ #define ATKBD_CMD_ENABLE 0x00f4 #define ATKBD_CMD_RESET_DIS 0x00f5 #define ATKBD_CMD_RESET_BAT 0x02ff +#define ATKBD_CMD_RESET_BAT1 0x01ff
// Mouse commands #define PSMOUSE_CMD_SETSCALE11 0x00e6