[SeaBIOS] [PATCH] QEMU DMA: Add ability to write back to fw_cfg file

Kevin O'Connor kevin at koconnor.net
Fri Jan 20 17:08:54 CET 2017


On Thu, Jan 19, 2017 at 10:20:50PM -0800, ben at skyportsystems.com wrote:
> From: Ben Warren <ben at skyportsystems.com>
> 
> This adds a new command to the DMA protocol.  The command allows the
> memory allocation of a fw_cfg file by BIOS and subsequent return of
> the allocated address to QEMU.  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 - see my comments below.

[...]
> --- 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 {
> +        outsb(PORT_QEMU_CFG_DATA, buf, len);
> +    }
> +}
> +
> +static void
>  qemu_cfg_skip(int len)
>  {
>      if (len == 0) {
> @@ -280,6 +294,19 @@ 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 {
> +        qemu_cfg_select(e);
> +        qemu_cfg_write(buf, len);
> +    }
> +}
> +
>  struct qemu_romfile_s {
>      struct romfile_s file;
>      int select, skip;
> @@ -303,6 +330,24 @@ qemu_cfg_read_file(struct romfile_s *file, void *dst, u32 maxlen)
>      return file->size;
>  }
>  
> +static int
> +qemu_cfg_write_file(void *src, struct romfile_s *file, u32 maxlen)
> +{
> +    if (file->size < maxlen)
> +        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;
> +}

I'd prefer if we could break this patch into two parts - one part to
add the write interface to fw_cfg and one part to add
ALLOCATE_RET_ADDR.  I'm fine with both interfaces, but the QEMU parts
still need to be committed prior to committing to SeaBIOS.  Having the
patch in separate parts will make it both easier to review and commit
at their appropriate times.

> +
>  static void
>  qemu_romfile_add(char *name, int select, int skip, int size)
>  {
> @@ -317,6 +362,7 @@ qemu_romfile_add(char *name, int select, int skip, int size)
>      qfile->select = select;
>      qfile->skip = skip;
>      qfile->file.copy = qemu_cfg_read_file;
> +    qfile->file.write_back = qemu_cfg_write_file;
>      romfile_add(&qfile->file);
>  }

I don't think it is worthwhile to extend the romfile mechanism with a
write_back() method.  Instead, I'd export qemu_cfg_write_file(),
extend it to verify the passed in romfile is a fw_cfg file (file->copy
== qemu_cfg_read_file), and change the callers to call it directly.

>  
> diff --git a/src/fw/paravirt.h b/src/fw/paravirt.h
> index d8eb7c4..479eaf9 100644
> --- a/src/fw/paravirt.h
> +++ b/src/fw/paravirt.h
> @@ -43,6 +43,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
> diff --git a/src/fw/romfile_loader.c b/src/fw/romfile_loader.c
> index f4b17ff..945572f 100644
> --- a/src/fw/romfile_loader.c
> +++ b/src/fw/romfile_loader.c
> @@ -127,6 +127,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 */
> +    memcpy(&addr, &alloc_file->data, sizeof(addr));
> +    addr = cpu_to_le64(addr);
> +    memcpy(addr_file->data, &addr, sizeof(addr));
> +
> +    if (!addr_file->file->write_back)
> +        goto err;
> +
> +    ret = addr_file->file->write_back(&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 +193,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..ea161e8 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

Minor nit - a tab snuck in here.

> +         */
> +        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 {
> diff --git a/src/romfile.h b/src/romfile.h
> index c6d62a1..8694cc1 100644
> --- a/src/romfile.h
> +++ b/src/romfile.h
> @@ -9,6 +9,7 @@ struct romfile_s {
>      char name[128];
>      u32 size;
>      int (*copy)(struct romfile_s *file, void *dest, u32 maxlen);
> +    int (*write_back)(void *src, struct romfile_s *file, u32 maxlen);
>  };
>  void romfile_add(struct romfile_s *file);
>  struct romfile_s *romfile_findprefix(const char *prefix, struct romfile_s *prev);

-Kevin



More information about the SeaBIOS mailing list