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 | 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