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);