[SeaBIOS] [RFC 1/2] Add QEMU fw_cfg dma interface support
Marc Marí
markmb at redhat.com
Tue Jul 21 18:06:10 CEST 2015
Add detection and support for the QEMU fw_cfg dma interface.
Signed-off-by: Marc Marí <markmb at 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);
--
2.4.3
More information about the SeaBIOS
mailing list