Add support for the new fw_cfg DMA interface. The protocol is explained in QEMU documentation.
Signed-off-by: Marc MarĂ markmb@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);