[SeaBIOS] [PATCH 1/2] Add QEMU fw_cfg DMA interface

Marc Marí markmb at redhat.com
Thu Aug 6 11:02:32 CET 2015


Add support for the new fw_cfg DMA interface. The protocol is explained in
QEMU documentation.

Signed-off-by: Marc Marí <markmb at redhat.com>
---
 src/fw/paravirt.c | 48 +++++++++++++++++++++++++++++++++++++++++++++---
 src/fw/paravirt.h | 17 +++++++++++++++++
 2 files changed, 62 insertions(+), 3 deletions(-)

diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c
index db22ae8..287bf23 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;
+
+inline 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.
@@ -201,14 +208,39 @@ qemu_cfg_select(u16 f)
 static void
 qemu_cfg_read(void *buf, int len)
 {
-    insb(PORT_QEMU_CFG_DATA, buf, len);
+    if (qemu_cfg_dma_enabled()) {
+        QemuCfgDmaAccess access;
+
+        access.address = (u64)(u32)buf;
+        access.length = len;
+        access.control = QEMU_CFG_DMA_CTL_READ;
+
+        /*
+         * The out is done before the write of the variables on memory. This
+         * causes misread on the QEMU side.
+         */
+        barrier();
+
+        outl((u32)&access, PORT_QEMU_CFG_DMA_ADDR);
+        while(access.length != 0 && !(access.control & QEMU_CFG_DMA_CTL_ERROR));
+    } else {
+        insb(PORT_QEMU_CFG_DATA, buf, len);
+    }
 }
 
 static void
 qemu_cfg_skip(int len)
 {
-    while (len--)
-        inb(PORT_QEMU_CFG_DATA);
+    if (len == 0) {
+        return;
+    }
+
+    if (qemu_cfg_dma_enabled()) {
+        qemu_cfg_read(NULL, len);
+    } else {
+        while (len--)
+            inb(PORT_QEMU_CFG_DATA);
+    }
 }
 
 static void
@@ -422,8 +454,18 @@ void qemu_cfg_init(void)
     for (i = 0; i < 4; i++)
         if (inb(PORT_QEMU_CFG_DATA) != sig[i])
             return;
+
     dprintf(1, "Found QEMU fw_cfg\n");
 
+    // Detect DMA interface.
+    u32 id;
+    qemu_cfg_read_entry(&id, QEMU_CFG_ID, sizeof(id));
+
+    if (id == QEMU_CFG_DMA_ID) {
+        dprintf(1, "QEMU fw_cfg DMA interface supported\n");
+        cfg_dma_enabled = 1;
+    }
+
     // Populate romfiles for legacy fw_cfg entries
     qemu_cfg_legacy();
 
diff --git a/src/fw/paravirt.h b/src/fw/paravirt.h
index 95ffb92..05b4997 100644
--- a/src/fw/paravirt.h
+++ b/src/fw/paravirt.h
@@ -9,6 +9,12 @@
 #define PF_XEN      (1<<1)
 #define PF_KVM      (1<<2)
 
+typedef struct QemuCfgDmaAccess {
+    u64 address;
+    u32 length;
+    u32 control;
+} QemuCfgDmaAccess;
+
 extern u32 RamSize;
 extern u64 RamSizeOver4G;
 extern int PlatformRunningOn;
@@ -29,9 +35,20 @@ static inline int runningOnKVM(void) {
 #define PORT_SMI_STATUS        0x00b3
 #define PORT_QEMU_CFG_CTL      0x0510
 #define PORT_QEMU_CFG_DATA     0x0511
+#define PORT_QEMU_CFG_DMA_ADDR 0x0512
+
+// 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
+
+// QEMU_CFG_DMA ID bit
+#define QEMU_CFG_DMA_ID 2
 
+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
-- 
2.4.3




More information about the SeaBIOS mailing list