From: Ben Warren ben@skyportsystems.com
This patch set adds the capability to write to QEMU across the DMA link and adds a higher-level command to allocate a fw_cfg file and write its address back to another, writeable fw_cfg file.
The initial use case is for Windows VM Generation ID, where QEMU needs to change the contents of fw_cfg data at runtime, while still having BIOS allocate and manage the memory.
v1->v2: separated patch into two functional units changed so writes only occur over the DMA interface fixed coding style removed change to romfile struct definition (removed new write_back method)
Ben Warren (2): QEMU DMA: Add DMA write capability QEMU fw_cfg: Add command to write back address of file
src/fw/paravirt.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/fw/paravirt.h | 3 +++ src/fw/romfile_loader.c | 38 ++++++++++++++++++++++++++++++++++++++ src/fw/romfile_loader.h | 23 ++++++++++++++++++++--- 4 files changed, 110 insertions(+), 3 deletions(-)
From: Ben Warren ben@skyportsystems.com
This allows BIOS to write data back to QEMU using the DMA interface and provides a higher-level abstraction to write to a fw_cfg file
Signed-off-by: Ben Warren ben@skyportsystems.com --- src/fw/paravirt.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/fw/paravirt.h | 3 +++ 2 files changed, 52 insertions(+)
diff --git a/src/fw/paravirt.c b/src/fw/paravirt.c index 6de70f6..66404c7 100644 --- a/src/fw/paravirt.c +++ b/src/fw/paravirt.c @@ -253,6 +253,20 @@ qemu_cfg_read(void *buf, int len) }
static void +qemu_cfg_write(void *buf, int len) +{ + if (len == 0) { + return; + } + + if (qemu_cfg_dma_enabled()) { + qemu_cfg_dma_transfer(buf, len, QEMU_CFG_DMA_CTL_WRITE); + } else { + warn_internalerror(); + } +} + +static void qemu_cfg_skip(int len) { if (len == 0) { @@ -280,6 +294,18 @@ qemu_cfg_read_entry(void *buf, int e, int len) } }
+static void +qemu_cfg_write_entry(void *buf, int e, int len) +{ + if (qemu_cfg_dma_enabled()) { + u32 control = (e << 16) | QEMU_CFG_DMA_CTL_SELECT + | QEMU_CFG_DMA_CTL_WRITE; + qemu_cfg_dma_transfer(buf, len, control); + } else { + warn_internalerror(); + } +} + struct qemu_romfile_s { struct romfile_s file; int select, skip; @@ -303,6 +329,29 @@ qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen) return file->size; }
+int +qemu_cfg_write_file(void *src, struct romfile_s *file, u32 maxlen) +{ + if (file->size < maxlen) + return -1; + + if (!qemu_cfg_dma_enabled() || (file->copy != qemu_cfg_read_file)) { + warn_internalerror(); + return -1; + } + struct qemu_romfile_s *qfile; + qfile = container_of(file, struct qemu_romfile_s, file); + if (qfile->skip == 0) { + /* Do it in one transfer */ + qemu_cfg_write_entry(src, qfile->select, file->size); + } else { + qemu_cfg_select(qfile->select); + qemu_cfg_skip(qfile->skip); + qemu_cfg_write(src, file->size); + } + return file->size; +} + static void qemu_romfile_add(char *name, int select, int skip, int size) { diff --git a/src/fw/paravirt.h b/src/fw/paravirt.h index d8eb7c4..f88449a 100644 --- a/src/fw/paravirt.h +++ b/src/fw/paravirt.h @@ -3,6 +3,7 @@
#include "config.h" // CONFIG_* #include "biosvar.h" // GET_GLOBAL +#include "romfile.h" // struct romfile_s
// Types of paravirtualized platforms. #define PF_QEMU (1<<0) @@ -43,6 +44,7 @@ static inline int runningOnKVM(void) { #define QEMU_CFG_DMA_CTL_READ 0x02 #define QEMU_CFG_DMA_CTL_SKIP 0x04 #define QEMU_CFG_DMA_CTL_SELECT 0x08 +#define QEMU_CFG_DMA_CTL_WRITE 0x10
// QEMU_CFG_DMA ID bit #define QEMU_CFG_VERSION_DMA 2 @@ -53,5 +55,6 @@ void qemu_platform_setup(void); void qemu_cfg_init(void);
u16 qemu_get_present_cpus_count(void); +int qemu_cfg_write_file(void *src, struct romfile_s *file, u32 maxlen);
#endif
From: Ben Warren ben@skyportsystems.com
The command allows the memory allocation of a fw_cfg file by BIOS and subsequent return of the allocated address to QEMU.
Signed-off-by: Ben Warren ben@skyportsystems.com --- src/fw/romfile_loader.c | 38 ++++++++++++++++++++++++++++++++++++++ src/fw/romfile_loader.h | 23 ++++++++++++++++++++--- 2 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/src/fw/romfile_loader.c b/src/fw/romfile_loader.c index f4b17ff..1b3fb3e 100644 --- a/src/fw/romfile_loader.c +++ b/src/fw/romfile_loader.c @@ -5,6 +5,7 @@ #include "romfile.h" // struct romfile_s #include "malloc.h" // Zone*, _malloc #include "output.h" // warn_* +#include "paravirt.h" // qemu_cfg_write_file
struct romfile_loader_file { struct romfile_s *file; @@ -127,6 +128,38 @@ err: warn_internalerror(); }
+static void romfile_loader_return_addr(struct romfile_loader_entry_s *entry, + struct romfile_loader_files *files) +{ + struct romfile_loader_file *alloc_file; + struct romfile_loader_file *addr_file; + u64 addr; + int ret; + + alloc_file = romfile_loader_find(entry->alloc_ret_file, files); + addr_file = romfile_loader_find(entry->alloc_ret_addr_file, files); + + if (!alloc_file || !addr_file || !alloc_file->data || !addr_file->data || + addr_file->file->size < sizeof(addr)) + goto err; + + /* Get the address of the just-allocated file + * and stuff it in the address file */ + void *data_ptr = alloc_file->data; + + memcpy(&addr, &data_ptr, sizeof(data_ptr)); + addr = cpu_to_le64(addr); + memcpy(addr_file->data, &addr, sizeof(addr)); + + /* Only supported on QEMU */ + ret = qemu_cfg_write_file(&addr, addr_file->file, sizeof(addr)); + if (ret != sizeof(addr)) + goto err; + return; +err: + warn_internalerror(); +} + int romfile_loader_execute(const char *name) { struct romfile_loader_entry_s *entry; @@ -161,6 +194,11 @@ int romfile_loader_execute(const char *name) break; case ROMFILE_LOADER_COMMAND_ADD_CHECKSUM: romfile_loader_add_checksum(entry, files); + break; + case ROMFILE_LOADER_COMMAND_ALLOCATE_RET_ADDR: + romfile_loader_allocate(entry, files); + romfile_loader_return_addr(entry, files); + break; default: /* Skip commands that we don't recognize. */ break; diff --git a/src/fw/romfile_loader.h b/src/fw/romfile_loader.h index 15eab2a..44a6cb8 100644 --- a/src/fw/romfile_loader.h +++ b/src/fw/romfile_loader.h @@ -51,15 +51,32 @@ struct romfile_loader_entry_s { u32 cksum_length; };
+ /* + * COMMAND_ALLOCATE_RETURN_ADDR - allocate a table from @alloc_file + * subject to @alloc_ret_align alignment (must be power of 2) + * and @alloc_ret_zone (can be HIGH or FSEG) requirements. + * Additionally, return the address of the allocation in + * @addr_file. + * + * This may be used instead of COMMAND_ALLOCATE + */ + struct { + char alloc_ret_file[ROMFILE_LOADER_FILESZ]; + u32 alloc_ret_align; + u8 alloc_ret_zone; + char alloc_ret_addr_file[ROMFILE_LOADER_FILESZ]; + }; + /* padding */ char pad[124]; }; };
enum { - ROMFILE_LOADER_COMMAND_ALLOCATE = 0x1, - ROMFILE_LOADER_COMMAND_ADD_POINTER = 0x2, - ROMFILE_LOADER_COMMAND_ADD_CHECKSUM = 0x3, + ROMFILE_LOADER_COMMAND_ALLOCATE = 0x1, + ROMFILE_LOADER_COMMAND_ADD_POINTER = 0x2, + ROMFILE_LOADER_COMMAND_ADD_CHECKSUM = 0x3, + ROMFILE_LOADER_COMMAND_ALLOCATE_RET_ADDR = 0x4, };
enum {
On Fri, Jan 20, 2017 at 02:33:14PM -0800, ben@skyportsystems.com wrote:
From: Ben Warren ben@skyportsystems.com
This patch set adds the capability to write to QEMU across the DMA link and adds a higher-level command to allocate a fw_cfg file and write its address back to another, writeable fw_cfg file.
The initial use case is for Windows VM Generation ID, where QEMU needs to change the contents of fw_cfg data at runtime, while still having BIOS allocate and manage the memory.
Thanks. The patches look fine to me. If no one else comments, I'll commit when ALLOCATE_RET_ADDR is accepted in QEMU.
-Kevin
On 01/24/17 23:17, Kevin O'Connor wrote:
On Fri, Jan 20, 2017 at 02:33:14PM -0800, ben@skyportsystems.com wrote:
From: Ben Warren ben@skyportsystems.com
This patch set adds the capability to write to QEMU across the DMA link and adds a higher-level command to allocate a fw_cfg file and write its address back to another, writeable fw_cfg file.
The initial use case is for Windows VM Generation ID, where QEMU needs to change the contents of fw_cfg data at runtime, while still having BIOS allocate and manage the memory.
Thanks. The patches look fine to me. If no one else comments, I'll commit when ALLOCATE_RET_ADDR is accepted in QEMU.
Thanks -- we're presently discussing that patch:
http://lists.nongnu.org/archive/html/qemu-devel/2017-01/msg05325.html http://lists.nongnu.org/archive/html/qemu-devel/2017-01/msg05337.html
I think an update will be necessary for the SeaBIOS series. The fw_cfg file that is supposed to receive the allocation address -- that is, the "address file" --, for the allocated / downloaded fw_cfg file, should *not* itself be allocated / downloaded.
In the current version of the SeaBIOS series, qemu_cfg_write_file() takes a "struct romfile_s *file" parameter, for identifying the fw_cfg file to write to. And patch #2 locates that file with romfile_loader_find().
However, romfile_loader_find() should *not* know about the address file -- romfile_loader_find() can locate files which have been allocated / downloaded, but the address file is not such a file.
Instead, the 2nd parameter of qemu_cfg_write_file() should be determined by calling romfile_find(), with the name of the address file. This will locate the right entry simply from the fw_cfg file directory. (See qemu_cfg_init() -> qemu_romfile_add() -> romfile_add().)
Sorry about pointing this out somewhat late, but I got around reviewing Ben's QEMU series only this dawn.
Thanks Laszlo
On Wed, Jan 25, 2017 at 12:37:36PM +0100, Laszlo Ersek wrote:
Instead, the 2nd parameter of qemu_cfg_write_file() should be determined by calling romfile_find(), with the name of the address file. This will locate the right entry simply from the fw_cfg file directory. (See qemu_cfg_init() -> qemu_romfile_add() -> romfile_add().)
Good catch. I agree.
-Kevin