[SeaBIOS] [PATCH v2 1/2] Add QEMU fw_cfg DMA interface
Marc Marí
markmb at redhat.com
Mon Aug 31 09:12:01 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 | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
src/fw/paravirt.h | 25 +++++++++++++++++----
2 files changed, 83 insertions(+), 7 deletions(-)
diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c
index db22ae8..26af499 100644
--- a/src/fw/paravirt.c
+++ b/src/fw/paravirt.c
@@ -23,6 +23,7 @@
#include "util.h" // pci_setup
#include "x86.h" // cpuid
#include "xen.h" // xen_biostable_setup
+#include "stacks.h" // yield
// Amount of continuous ram under 4Gig
u32 RamSize;
@@ -30,6 +31,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.
@@ -199,16 +207,57 @@ qemu_cfg_select(u16 f)
}
static void
+qemu_cfg_dma_transfer(u64 address, u32 length, u32 control)
+{
+ QemuCfgDmaAccess access;
+ void *p = &access;
+
+ *(u64 *)(p + offsetof(QemuCfgDmaAccess, address)) =
+ cpu_to_be64(address);
+ *(u32 *)(p + offsetof(QemuCfgDmaAccess, length)) =
+ cpu_to_be32(length);
+ *(u32 *)(p + offsetof(QemuCfgDmaAccess, control)) =
+ cpu_to_be32(control);
+
+ barrier();
+
+ outl((u32)p, PORT_QEMU_CFG_DMA_ADDR_LOW);
+
+ u32 len;
+ u32 ctl;
+
+ do {
+ yield();
+ len = be32_to_cpu(*(u32 *)(p +
+ offsetof(QemuCfgDmaAccess, length)));
+ ctl = be32_to_cpu(*(u32 *)(p +
+ offsetof(QemuCfgDmaAccess, control)));
+ } while(len != 0 && !(ctl & QEMU_CFG_DMA_CTL_ERROR));
+}
+
+static void
qemu_cfg_read(void *buf, int len)
{
- insb(PORT_QEMU_CFG_DATA, buf, len);
+ if (qemu_cfg_dma_enabled()) {
+ qemu_cfg_dma_transfer((u64)(u32)buf, len, QEMU_CFG_DMA_CTL_READ);
+ } 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_dma_transfer(0, len, QEMU_CFG_DMA_CTL_SKIP);
+ } else {
+ while (len--)
+ inb(PORT_QEMU_CFG_DATA);
+ }
}
static void
@@ -422,8 +471,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_VERSION_DMA) {
+ 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..5e5c61e 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 {
+ u32 control;
+ u32 length;
+ u64 address;
+} PACKED QemuCfgDmaAccess;
+
extern u32 RamSize;
extern u64 RamSizeOver4G;
extern int PlatformRunningOn;
@@ -25,11 +31,22 @@ static inline int runningOnKVM(void) {
}
// Common paravirt ports.
-#define PORT_SMI_CMD 0x00b2
-#define PORT_SMI_STATUS 0x00b3
-#define PORT_QEMU_CFG_CTL 0x0510
-#define PORT_QEMU_CFG_DATA 0x0511
+#define PORT_SMI_CMD 0x00b2
+#define PORT_SMI_STATUS 0x00b3
+#define PORT_QEMU_CFG_CTL 0x0510
+#define PORT_QEMU_CFG_DATA 0x0511
+#define PORT_QEMU_CFG_DMA_ADDR_LOW 0x0514
+#define PORT_QEMU_CFG_DMA_ADDR_HIGH 0x0518
+
+// QEMU_CFG_DMA_CONTROL bits
+#define QEMU_CFG_DMA_CTL_ERROR 0x01
+#define QEMU_CFG_DMA_CTL_READ 0x02
+#define QEMU_CFG_DMA_CTL_SKIP 0x04
+
+// QEMU_CFG_DMA ID bit
+#define QEMU_CFG_VERSION_DMA 2
+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