[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