diff mbox

[v3] Sort the fw_cfg file list

Message ID 1458154028-4934-1-git-send-email-minyard@acm.org (mailing list archive)
State New, archived
Headers show

Commit Message

Corey Minyard March 16, 2016, 6:47 p.m. UTC
From: Gerd Hoffmann <kraxel@redhat.com>

Entries are inserted in filename order instead of being
appended to the end in case sorting is enabled.

This will avoid any future issues of moving the file creation
around, it doesn't matter what order they are created now,
the will always be in filename order.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>

Added machine type handling for compatibility.  This was
a fairly complex change, this will preserve the order of fw_cfg
for older versions no matter what order the firmware files
actually come in.  A list is kept of the correct legacy order
and the entries will be inserted based upon their order in
the list.  Except that some entries are ordered (in a specific
area of the list) based upon what order they appear on the
command line.  Special handling is added for those entries.

Signed-off-by: Corey Minyard <cminyard@mvista.com>
---

A new version of the sorted fw_cfg, with new backwards
compatibility code.  This is bigger than I would like, but
I can't think of a good way to split it up that would make
much sense.

This is more for review, anyway, I'm sure there are issues with
this.

I'm not too excited about fw_cfg_set_order_override(), but I can't
think of a better way to handle this.  You can't use filename or
prefixes, the same filenames appear in different places for VGA
ROMs depending on if they are PCI or ISA.

 hw/i386/pc.c              |   4 ++
 hw/i386/pc_piix.c         |   1 +
 hw/i386/pc_q35.c          |   1 +
 hw/nvram/fw_cfg.c         | 127 +++++++++++++++++++++++++++++++++++++++++++---
 include/hw/boards.h       |   3 +-
 include/hw/nvram/fw_cfg.h |   7 +++
 vl.c                      |   4 ++
 7 files changed, 138 insertions(+), 9 deletions(-)

Comments

Michael S. Tsirkin March 17, 2016, 4:02 p.m. UTC | #1
On Wed, Mar 16, 2016 at 01:47:08PM -0500, minyard@acm.org wrote:
> From: Gerd Hoffmann <kraxel@redhat.com>
> 
> Entries are inserted in filename order instead of being
> appended to the end in case sorting is enabled.
> 
> This will avoid any future issues of moving the file creation
> around, it doesn't matter what order they are created now,
> the will always be in filename order.
> 
> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> 
> Added machine type handling for compatibility.  This was
> a fairly complex change, this will preserve the order of fw_cfg
> for older versions no matter what order the firmware files
> actually come in.  A list is kept of the correct legacy order
> and the entries will be inserted based upon their order in
> the list.  Except that some entries are ordered (in a specific
> area of the list) based upon what order they appear on the
> command line.  Special handling is added for those entries.
> 
> Signed-off-by: Corey Minyard <cminyard@mvista.com>
> ---
> 
> A new version of the sorted fw_cfg, with new backwards
> compatibility code.  This is bigger than I would like, but
> I can't think of a good way to split it up that would make
> much sense.
> 
> This is more for review, anyway, I'm sure there are issues with
> this.
> 
> I'm not too excited about fw_cfg_set_order_override(), but I can't
> think of a better way to handle this.  You can't use filename or
> prefixes, the same filenames appear in different places for VGA
> ROMs depending on if they are PCI or ISA.

I don't see another good solution either.
However, I would like to make these NOPs with
new machine types.

This way down the road we can drop this mess.


>  hw/i386/pc.c              |   4 ++
>  hw/i386/pc_piix.c         |   1 +
>  hw/i386/pc_q35.c          |   1 +
>  hw/nvram/fw_cfg.c         | 127 +++++++++++++++++++++++++++++++++++++++++++---
>  include/hw/boards.h       |   3 +-
>  include/hw/nvram/fw_cfg.h |   7 +++
>  vl.c                      |   4 ++
>  7 files changed, 138 insertions(+), 9 deletions(-)
> 
> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> index 56ec6cd..707753b 100644
> --- a/hw/i386/pc.c
> +++ b/hw/i386/pc.c
> @@ -1406,6 +1406,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
>  {
>      DeviceState *dev = NULL;
>  
> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA);
>      if (pci_bus) {
>          PCIDevice *pcidev = pci_vga_init(pci_bus);
>          dev = pcidev ? &pcidev->qdev : NULL;
> @@ -1413,6 +1414,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
>          ISADevice *isadev = isa_vga_init(isa_bus);
>          dev = isadev ? DEVICE(isadev) : NULL;
>      }
> +    fw_cfg_reset_order_override();
>      return dev;
>  }
>  
> @@ -1541,6 +1543,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
>  {
>      int i;
>  
> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC);
>      for (i = 0; i < nb_nics; i++) {
>          NICInfo *nd = &nd_table[i];
>  
> @@ -1550,6 +1553,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
>              pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
>          }
>      }
> +    fw_cfg_reset_order_override();
>  }
>  
>  void pc_pci_device_init(PCIBus *pci_bus)
> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> index 6f8c2cd..447703b 100644
> --- a/hw/i386/pc_piix.c
> +++ b/hw/i386/pc_piix.c
> @@ -429,6 +429,7 @@ static void pc_i440fx_2_5_machine_options(MachineClass *m)
>      m->alias = NULL;
>      m->is_default = 0;
>      pcmc->save_tsc_khz = false;
> +    m->legacy_fw_cfg_order = 1;
>      SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
>  }
>  
> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
> index 46522c9..04f3609 100644
> --- a/hw/i386/pc_q35.c
> +++ b/hw/i386/pc_q35.c
> @@ -291,6 +291,7 @@ static void pc_q35_2_5_machine_options(MachineClass *m)
>      pc_q35_2_6_machine_options(m);
>      m->alias = NULL;
>      pcmc->save_tsc_khz = false;
> +    m->legacy_fw_cfg_order = 1;
>      SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
>  }
>  
> diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
> index 7866248..1693f26 100644
> --- a/hw/nvram/fw_cfg.c
> +++ b/hw/nvram/fw_cfg.c
> @@ -28,6 +28,7 @@
>  #include "hw/isa/isa.h"
>  #include "hw/nvram/fw_cfg.h"
>  #include "hw/sysbus.h"
> +#include "hw/boards.h"
>  #include "trace.h"
>  #include "qemu/error-report.h"
>  #include "qemu/config-file.h"
> @@ -68,6 +69,7 @@ struct FWCfgState {
>      /*< public >*/
>  
>      FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
> +    int entry_order[FW_CFG_MAX_ENTRY];
>      FWCfgFiles *files;
>      uint16_t cur_entry;
>      uint32_t cur_offset;
> @@ -664,12 +666,88 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
>      fw_cfg_add_bytes(s, key, copy, sizeof(value));
>  }
>  
> +static int fw_cfg_order_override = -1;
> +
> +void fw_cfg_set_order_override(int order)
> +{
> +    assert(fw_cfg_order_override == -1);
> +    fw_cfg_order_override = order;
> +}
> +
> +void fw_cfg_reset_order_override(void)
> +{
> +    assert(fw_cfg_order_override != -1);
> +    fw_cfg_order_override = -1;
> +}
> +
> +/*
> + * This is the legacy order list.  For legacy systems, files are in
> + * the fw_cfg in the order defined below, by the "order" value.  Note
> + * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a
> + * specific area, but there may be more than one and they occur in the
> + * order that the user specifies them on the command line.  Those are
> + * handled in a special manner, using the order override above.
> + *
> + * For non-legacy, the files are sorted by filename to avoid this kind
> + * of complexity in the future.
> + *
> + * This is only for x86, other arches don't implement versioning so
> + * they won't set legacy mode.
> + */
> +static struct {
> +    const char *name;
> +    int order;
> +} fw_cfg_order[] = {
> +    { "etc/boot-menu-wait", 10 },
> +    { "bootsplash.jpg", 11 },
> +    { "bootsplash.bmp", 12 },
> +    { "etc/boot-fail-wait", 15 },
> +    { "etc/smbios/smbios-tables", 20 },
> +    { "etc/smbios/smbios-anchor", 30 },
> +    { "etc/e820", 40 },
> +    { "etc/reserved-memory-end", 50 },
> +    { "genroms/linuxboot.bin", 60 },
> +    { }, /* VGA ROMs from pc_vga_init come here, 70. */
> +    { }, /* NIC option ROMs from pc_nic_init come here, 80. */
> +    { "etc/system-states", 90 },
> +    { }, /* User ROMs come here, 100. */
> +    { }, /* Device FW comes here, 110. */
> +    { "etc/extra-pci-roots", 120 },
> +    { "etc/acpi/tables", 130 },
> +    { "etc/table-loader", 140 },
> +    { "etc/tpm/log", 150 },
> +    { "etc/acpi/rsdp", 160 },
> +    { "bootorder", 170 },
> +
> +#define FW_CFG_ORDER_OVERRIDE_LAST 200
> +};
> +
> +static int get_fw_cfg_order(const char *name)
> +{
> +    int i;
> +
> +    if (fw_cfg_order_override >= 0)
> +	return fw_cfg_order_override;
> +
> +    for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) {
> +	if (fw_cfg_order[i].name == NULL)
> +	    continue;
> +	if (strcmp(name, fw_cfg_order[i].name) == 0)
> +	    return fw_cfg_order[i].order;
> +    }
> +    /* Stick unknown stuff at the end. */
> +    error_report("warning: Unknown firmware file in legacy mode: %s\n", name);
> +    return FW_CFG_ORDER_OVERRIDE_LAST;
> +}
> +
>  void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
>                                FWCfgReadCallback callback, void *callback_opaque,
>                                void *data, size_t len)
>  {
> -    int i, index;
> +    int i, index, count;
>      size_t dsize;
> +    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
> +    int order = 0;
>  
>      if (!s->files) {
>          dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
> @@ -677,13 +755,45 @@ void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
>          fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
>      }
>  
> -    index = be32_to_cpu(s->files->count);
> -    assert(index < FW_CFG_FILE_SLOTS);
> +    count = be32_to_cpu(s->files->count);
> +    assert(count < FW_CFG_FILE_SLOTS);
>  
> -    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
> -            filename);
> -    for (i = 0; i < index; i++) {
> -        if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
> +    /* Find the insertion point. */
> +    if (mc->legacy_fw_cfg_order) {
> +        /* Sort by order, and keep insertion order for the same order. */
> +        order = get_fw_cfg_order(filename);
> +        for (index = count;
> +             index > 0 && order < s->entry_order[index - 1];
> +             index--);
> +    } else {
> +        /* Sort by file name. */
> +        for (index = count;
> +             index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0;
> +             index--);
> +    }
> +
> +    /*
> +     * Move all the entries from the index point and after down one
> +     * to create a slot for the new entry.  Because calculations are
> +     * being done with the index, make it so that "i" is the current
> +     * index and "i - 1" is the one being copied from, thus the
> +     * unusual start and end in the for statement.
> +     */
> +    for (i = count + 1; i > index; i--) {
> +        s->files->f[i] = s->files->f[i - 1];
> +        s->files->f[i].select = cpu_to_be16(FW_CFG_FILE_FIRST + i);
> +        s->entries[0][FW_CFG_FILE_FIRST + i] =
> +            s->entries[0][FW_CFG_FILE_FIRST + i - 1];
> +        s->entry_order[i] = s->entry_order[i - 1];
> +    }
> +
> +    memset(&s->files->f[index], 0, sizeof(FWCfgFile));
> +    memset(&s->entries[0][FW_CFG_FILE_FIRST + index], 0, sizeof(FWCfgEntry));
> +
> +    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename);
> +    for (i = 0; i <= count; i++) {
> +        if (i != index &&
> +            strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
>              error_report("duplicate fw_cfg file name: %s",
>                           s->files->f[index].name);
>              exit(1);
> @@ -695,9 +805,10 @@ void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
>  
>      s->files->f[index].size   = cpu_to_be32(len);
>      s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
> +    s->entry_order[index] = order;
>      trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
>  
> -    s->files->count = cpu_to_be32(index+1);
> +    s->files->count = cpu_to_be32(count+1);
>  }
>  
>  void fw_cfg_add_file(FWCfgState *s,  const char *filename,
> diff --git a/include/hw/boards.h b/include/hw/boards.h
> index b5d7eae..b6567f7 100644
> --- a/include/hw/boards.h
> +++ b/include/hw/boards.h
> @@ -84,7 +84,8 @@ struct MachineClass {
>          no_cdrom:1,
>          no_sdcard:1,
>          has_dynamic_sysbus:1,
> -        pci_allow_0_address:1;
> +        pci_allow_0_address:1,
> +        legacy_fw_cfg_order:1;
>      int is_default;
>      const char *default_machine_opts;
>      const char *default_boot_order;
> diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h
> index 4315f4e..38bbfca 100644
> --- a/include/hw/nvram/fw_cfg.h
> +++ b/include/hw/nvram/fw_cfg.h
> @@ -57,6 +57,13 @@ typedef struct FWCfgFile {
>      char      name[FW_CFG_MAX_FILE_PATH];
>  } FWCfgFile;
>  
> +#define FW_CFG_ORDER_OVERRIDE_VGA    70
> +#define FW_CFG_ORDER_OVERRIDE_NIC    80
> +#define FW_CFG_ORDER_OVERRIDE_USER   100
> +#define FW_CFG_ORDER_OVERRIDE_DEVICE 110
> +void fw_cfg_set_order_override(int order);
> +void fw_cfg_reset_order_override(void);
> +
>  typedef struct FWCfgFiles {
>      uint32_t  count;
>      FWCfgFile f[];
> diff --git a/vl.c b/vl.c
> index 7a28982..6fad844 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -4520,10 +4520,12 @@ int main(int argc, char **argv, char **envp)
>  
>      numa_post_machine_init();
>  
> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_USER);
>      if (qemu_opts_foreach(qemu_find_opts("fw_cfg"),
>                            parse_fw_cfg, fw_cfg_find(), NULL) != 0) {
>          exit(1);
>      }
> +    fw_cfg_reset_order_override();
>  
>      /* init USB devices */
>      if (usb_enabled()) {
> @@ -4535,10 +4537,12 @@ int main(int argc, char **argv, char **envp)
>      igd_gfx_passthru();
>  
>      /* init generic devices */
> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE);
>      if (qemu_opts_foreach(qemu_find_opts("device"),
>                            device_init_func, NULL, NULL)) {
>          exit(1);
>      }
> +    fw_cfg_reset_order_override();
>  
>      /* Did we create any drives that we failed to create a device for? */
>      drive_check_orphaned();
> -- 
> 2.5.0
>
Corey Minyard March 17, 2016, 4:41 p.m. UTC | #2
On 03/17/2016 11:02 AM, Michael S. Tsirkin wrote:
> On Wed, Mar 16, 2016 at 01:47:08PM -0500, minyard@acm.org wrote:
>> From: Gerd Hoffmann <kraxel@redhat.com>
>>
>> Entries are inserted in filename order instead of being
>> appended to the end in case sorting is enabled.
>>
>> This will avoid any future issues of moving the file creation
>> around, it doesn't matter what order they are created now,
>> the will always be in filename order.
>>
>> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>>
>> Added machine type handling for compatibility.  This was
>> a fairly complex change, this will preserve the order of fw_cfg
>> for older versions no matter what order the firmware files
>> actually come in.  A list is kept of the correct legacy order
>> and the entries will be inserted based upon their order in
>> the list.  Except that some entries are ordered (in a specific
>> area of the list) based upon what order they appear on the
>> command line.  Special handling is added for those entries.
>>
>> Signed-off-by: Corey Minyard <cminyard@mvista.com>
>> ---
>>
>> A new version of the sorted fw_cfg, with new backwards
>> compatibility code.  This is bigger than I would like, but
>> I can't think of a good way to split it up that would make
>> much sense.
>>
>> This is more for review, anyway, I'm sure there are issues with
>> this.
>>
>> I'm not too excited about fw_cfg_set_order_override(), but I can't
>> think of a better way to handle this.  You can't use filename or
>> prefixes, the same filenames appear in different places for VGA
>> ROMs depending on if they are PCI or ISA.
> I don't see another good solution either.
> However, I would like to make these NOPs with
> new machine types.
>
> This way down the road we can drop this mess.

They should be NOPs with new machine types.  These
only have an effect if legacy_fw_cfg_order is set in the
machine class, and that's only set for older machines.

-corey

>
>>   hw/i386/pc.c              |   4 ++
>>   hw/i386/pc_piix.c         |   1 +
>>   hw/i386/pc_q35.c          |   1 +
>>   hw/nvram/fw_cfg.c         | 127 +++++++++++++++++++++++++++++++++++++++++++---
>>   include/hw/boards.h       |   3 +-
>>   include/hw/nvram/fw_cfg.h |   7 +++
>>   vl.c                      |   4 ++
>>   7 files changed, 138 insertions(+), 9 deletions(-)
>>
>> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
>> index 56ec6cd..707753b 100644
>> --- a/hw/i386/pc.c
>> +++ b/hw/i386/pc.c
>> @@ -1406,6 +1406,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
>>   {
>>       DeviceState *dev = NULL;
>>   
>> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA);
>>       if (pci_bus) {
>>           PCIDevice *pcidev = pci_vga_init(pci_bus);
>>           dev = pcidev ? &pcidev->qdev : NULL;
>> @@ -1413,6 +1414,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
>>           ISADevice *isadev = isa_vga_init(isa_bus);
>>           dev = isadev ? DEVICE(isadev) : NULL;
>>       }
>> +    fw_cfg_reset_order_override();
>>       return dev;
>>   }
>>   
>> @@ -1541,6 +1543,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
>>   {
>>       int i;
>>   
>> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC);
>>       for (i = 0; i < nb_nics; i++) {
>>           NICInfo *nd = &nd_table[i];
>>   
>> @@ -1550,6 +1553,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
>>               pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
>>           }
>>       }
>> +    fw_cfg_reset_order_override();
>>   }
>>   
>>   void pc_pci_device_init(PCIBus *pci_bus)
>> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
>> index 6f8c2cd..447703b 100644
>> --- a/hw/i386/pc_piix.c
>> +++ b/hw/i386/pc_piix.c
>> @@ -429,6 +429,7 @@ static void pc_i440fx_2_5_machine_options(MachineClass *m)
>>       m->alias = NULL;
>>       m->is_default = 0;
>>       pcmc->save_tsc_khz = false;
>> +    m->legacy_fw_cfg_order = 1;
>>       SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
>>   }
>>   
>> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
>> index 46522c9..04f3609 100644
>> --- a/hw/i386/pc_q35.c
>> +++ b/hw/i386/pc_q35.c
>> @@ -291,6 +291,7 @@ static void pc_q35_2_5_machine_options(MachineClass *m)
>>       pc_q35_2_6_machine_options(m);
>>       m->alias = NULL;
>>       pcmc->save_tsc_khz = false;
>> +    m->legacy_fw_cfg_order = 1;
>>       SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
>>   }
>>   
>> diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
>> index 7866248..1693f26 100644
>> --- a/hw/nvram/fw_cfg.c
>> +++ b/hw/nvram/fw_cfg.c
>> @@ -28,6 +28,7 @@
>>   #include "hw/isa/isa.h"
>>   #include "hw/nvram/fw_cfg.h"
>>   #include "hw/sysbus.h"
>> +#include "hw/boards.h"
>>   #include "trace.h"
>>   #include "qemu/error-report.h"
>>   #include "qemu/config-file.h"
>> @@ -68,6 +69,7 @@ struct FWCfgState {
>>       /*< public >*/
>>   
>>       FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
>> +    int entry_order[FW_CFG_MAX_ENTRY];
>>       FWCfgFiles *files;
>>       uint16_t cur_entry;
>>       uint32_t cur_offset;
>> @@ -664,12 +666,88 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
>>       fw_cfg_add_bytes(s, key, copy, sizeof(value));
>>   }
>>   
>> +static int fw_cfg_order_override = -1;
>> +
>> +void fw_cfg_set_order_override(int order)
>> +{
>> +    assert(fw_cfg_order_override == -1);
>> +    fw_cfg_order_override = order;
>> +}
>> +
>> +void fw_cfg_reset_order_override(void)
>> +{
>> +    assert(fw_cfg_order_override != -1);
>> +    fw_cfg_order_override = -1;
>> +}
>> +
>> +/*
>> + * This is the legacy order list.  For legacy systems, files are in
>> + * the fw_cfg in the order defined below, by the "order" value.  Note
>> + * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a
>> + * specific area, but there may be more than one and they occur in the
>> + * order that the user specifies them on the command line.  Those are
>> + * handled in a special manner, using the order override above.
>> + *
>> + * For non-legacy, the files are sorted by filename to avoid this kind
>> + * of complexity in the future.
>> + *
>> + * This is only for x86, other arches don't implement versioning so
>> + * they won't set legacy mode.
>> + */
>> +static struct {
>> +    const char *name;
>> +    int order;
>> +} fw_cfg_order[] = {
>> +    { "etc/boot-menu-wait", 10 },
>> +    { "bootsplash.jpg", 11 },
>> +    { "bootsplash.bmp", 12 },
>> +    { "etc/boot-fail-wait", 15 },
>> +    { "etc/smbios/smbios-tables", 20 },
>> +    { "etc/smbios/smbios-anchor", 30 },
>> +    { "etc/e820", 40 },
>> +    { "etc/reserved-memory-end", 50 },
>> +    { "genroms/linuxboot.bin", 60 },
>> +    { }, /* VGA ROMs from pc_vga_init come here, 70. */
>> +    { }, /* NIC option ROMs from pc_nic_init come here, 80. */
>> +    { "etc/system-states", 90 },
>> +    { }, /* User ROMs come here, 100. */
>> +    { }, /* Device FW comes here, 110. */
>> +    { "etc/extra-pci-roots", 120 },
>> +    { "etc/acpi/tables", 130 },
>> +    { "etc/table-loader", 140 },
>> +    { "etc/tpm/log", 150 },
>> +    { "etc/acpi/rsdp", 160 },
>> +    { "bootorder", 170 },
>> +
>> +#define FW_CFG_ORDER_OVERRIDE_LAST 200
>> +};
>> +
>> +static int get_fw_cfg_order(const char *name)
>> +{
>> +    int i;
>> +
>> +    if (fw_cfg_order_override >= 0)
>> +	return fw_cfg_order_override;
>> +
>> +    for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) {
>> +	if (fw_cfg_order[i].name == NULL)
>> +	    continue;
>> +	if (strcmp(name, fw_cfg_order[i].name) == 0)
>> +	    return fw_cfg_order[i].order;
>> +    }
>> +    /* Stick unknown stuff at the end. */
>> +    error_report("warning: Unknown firmware file in legacy mode: %s\n", name);
>> +    return FW_CFG_ORDER_OVERRIDE_LAST;
>> +}
>> +
>>   void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
>>                                 FWCfgReadCallback callback, void *callback_opaque,
>>                                 void *data, size_t len)
>>   {
>> -    int i, index;
>> +    int i, index, count;
>>       size_t dsize;
>> +    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
>> +    int order = 0;
>>   
>>       if (!s->files) {
>>           dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
>> @@ -677,13 +755,45 @@ void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
>>           fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
>>       }
>>   
>> -    index = be32_to_cpu(s->files->count);
>> -    assert(index < FW_CFG_FILE_SLOTS);
>> +    count = be32_to_cpu(s->files->count);
>> +    assert(count < FW_CFG_FILE_SLOTS);
>>   
>> -    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
>> -            filename);
>> -    for (i = 0; i < index; i++) {
>> -        if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
>> +    /* Find the insertion point. */
>> +    if (mc->legacy_fw_cfg_order) {
>> +        /* Sort by order, and keep insertion order for the same order. */
>> +        order = get_fw_cfg_order(filename);
>> +        for (index = count;
>> +             index > 0 && order < s->entry_order[index - 1];
>> +             index--);
>> +    } else {
>> +        /* Sort by file name. */
>> +        for (index = count;
>> +             index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0;
>> +             index--);
>> +    }
>> +
>> +    /*
>> +     * Move all the entries from the index point and after down one
>> +     * to create a slot for the new entry.  Because calculations are
>> +     * being done with the index, make it so that "i" is the current
>> +     * index and "i - 1" is the one being copied from, thus the
>> +     * unusual start and end in the for statement.
>> +     */
>> +    for (i = count + 1; i > index; i--) {
>> +        s->files->f[i] = s->files->f[i - 1];
>> +        s->files->f[i].select = cpu_to_be16(FW_CFG_FILE_FIRST + i);
>> +        s->entries[0][FW_CFG_FILE_FIRST + i] =
>> +            s->entries[0][FW_CFG_FILE_FIRST + i - 1];
>> +        s->entry_order[i] = s->entry_order[i - 1];
>> +    }
>> +
>> +    memset(&s->files->f[index], 0, sizeof(FWCfgFile));
>> +    memset(&s->entries[0][FW_CFG_FILE_FIRST + index], 0, sizeof(FWCfgEntry));
>> +
>> +    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename);
>> +    for (i = 0; i <= count; i++) {
>> +        if (i != index &&
>> +            strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
>>               error_report("duplicate fw_cfg file name: %s",
>>                            s->files->f[index].name);
>>               exit(1);
>> @@ -695,9 +805,10 @@ void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
>>   
>>       s->files->f[index].size   = cpu_to_be32(len);
>>       s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
>> +    s->entry_order[index] = order;
>>       trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
>>   
>> -    s->files->count = cpu_to_be32(index+1);
>> +    s->files->count = cpu_to_be32(count+1);
>>   }
>>   
>>   void fw_cfg_add_file(FWCfgState *s,  const char *filename,
>> diff --git a/include/hw/boards.h b/include/hw/boards.h
>> index b5d7eae..b6567f7 100644
>> --- a/include/hw/boards.h
>> +++ b/include/hw/boards.h
>> @@ -84,7 +84,8 @@ struct MachineClass {
>>           no_cdrom:1,
>>           no_sdcard:1,
>>           has_dynamic_sysbus:1,
>> -        pci_allow_0_address:1;
>> +        pci_allow_0_address:1,
>> +        legacy_fw_cfg_order:1;
>>       int is_default;
>>       const char *default_machine_opts;
>>       const char *default_boot_order;
>> diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h
>> index 4315f4e..38bbfca 100644
>> --- a/include/hw/nvram/fw_cfg.h
>> +++ b/include/hw/nvram/fw_cfg.h
>> @@ -57,6 +57,13 @@ typedef struct FWCfgFile {
>>       char      name[FW_CFG_MAX_FILE_PATH];
>>   } FWCfgFile;
>>   
>> +#define FW_CFG_ORDER_OVERRIDE_VGA    70
>> +#define FW_CFG_ORDER_OVERRIDE_NIC    80
>> +#define FW_CFG_ORDER_OVERRIDE_USER   100
>> +#define FW_CFG_ORDER_OVERRIDE_DEVICE 110
>> +void fw_cfg_set_order_override(int order);
>> +void fw_cfg_reset_order_override(void);
>> +
>>   typedef struct FWCfgFiles {
>>       uint32_t  count;
>>       FWCfgFile f[];
>> diff --git a/vl.c b/vl.c
>> index 7a28982..6fad844 100644
>> --- a/vl.c
>> +++ b/vl.c
>> @@ -4520,10 +4520,12 @@ int main(int argc, char **argv, char **envp)
>>   
>>       numa_post_machine_init();
>>   
>> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_USER);
>>       if (qemu_opts_foreach(qemu_find_opts("fw_cfg"),
>>                             parse_fw_cfg, fw_cfg_find(), NULL) != 0) {
>>           exit(1);
>>       }
>> +    fw_cfg_reset_order_override();
>>   
>>       /* init USB devices */
>>       if (usb_enabled()) {
>> @@ -4535,10 +4537,12 @@ int main(int argc, char **argv, char **envp)
>>       igd_gfx_passthru();
>>   
>>       /* init generic devices */
>> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE);
>>       if (qemu_opts_foreach(qemu_find_opts("device"),
>>                             device_init_func, NULL, NULL)) {
>>           exit(1);
>>       }
>> +    fw_cfg_reset_order_override();
>>   
>>       /* Did we create any drives that we failed to create a device for? */
>>       drive_check_orphaned();
>> -- 
>> 2.5.0
>>
Michael S. Tsirkin March 17, 2016, 7:58 p.m. UTC | #3
On Thu, Mar 17, 2016 at 11:41:45AM -0500, Corey Minyard wrote:
> On 03/17/2016 11:02 AM, Michael S. Tsirkin wrote:
> >On Wed, Mar 16, 2016 at 01:47:08PM -0500, minyard@acm.org wrote:
> >>From: Gerd Hoffmann <kraxel@redhat.com>
> >>
> >>Entries are inserted in filename order instead of being
> >>appended to the end in case sorting is enabled.
> >>
> >>This will avoid any future issues of moving the file creation
> >>around, it doesn't matter what order they are created now,
> >>the will always be in filename order.
> >>
> >>Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
> >>
> >>Added machine type handling for compatibility.  This was
> >>a fairly complex change, this will preserve the order of fw_cfg
> >>for older versions no matter what order the firmware files
> >>actually come in.  A list is kept of the correct legacy order
> >>and the entries will be inserted based upon their order in
> >>the list.  Except that some entries are ordered (in a specific
> >>area of the list) based upon what order they appear on the
> >>command line.  Special handling is added for those entries.
> >>
> >>Signed-off-by: Corey Minyard <cminyard@mvista.com>
> >>---
> >>
> >>A new version of the sorted fw_cfg, with new backwards
> >>compatibility code.  This is bigger than I would like, but
> >>I can't think of a good way to split it up that would make
> >>much sense.
> >>
> >>This is more for review, anyway, I'm sure there are issues with
> >>this.
> >>
> >>I'm not too excited about fw_cfg_set_order_override(), but I can't
> >>think of a better way to handle this.  You can't use filename or
> >>prefixes, the same filenames appear in different places for VGA
> >>ROMs depending on if they are PCI or ISA.
> >I don't see another good solution either.
> >However, I would like to make these NOPs with
> >new machine types.
> >
> >This way down the road we can drop this mess.
> 
> They should be NOPs with new machine types.  These
> only have an effect if legacy_fw_cfg_order is set in the
> machine class, and that's only set for older machines.
> 
> -corey

I didn't realize this.
OK.
From layering point of view, I think I would
like machines to call rom_set_order_override().
And then the fw cfg one should get fw cfg argument.

> >
> >>  hw/i386/pc.c              |   4 ++
> >>  hw/i386/pc_piix.c         |   1 +
> >>  hw/i386/pc_q35.c          |   1 +
> >>  hw/nvram/fw_cfg.c         | 127 +++++++++++++++++++++++++++++++++++++++++++---
> >>  include/hw/boards.h       |   3 +-
> >>  include/hw/nvram/fw_cfg.h |   7 +++
> >>  vl.c                      |   4 ++
> >>  7 files changed, 138 insertions(+), 9 deletions(-)
> >>
> >>diff --git a/hw/i386/pc.c b/hw/i386/pc.c
> >>index 56ec6cd..707753b 100644
> >>--- a/hw/i386/pc.c
> >>+++ b/hw/i386/pc.c
> >>@@ -1406,6 +1406,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
> >>  {
> >>      DeviceState *dev = NULL;
> >>+    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA);
> >>      if (pci_bus) {
> >>          PCIDevice *pcidev = pci_vga_init(pci_bus);
> >>          dev = pcidev ? &pcidev->qdev : NULL;
> >>@@ -1413,6 +1414,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
> >>          ISADevice *isadev = isa_vga_init(isa_bus);
> >>          dev = isadev ? DEVICE(isadev) : NULL;
> >>      }
> >>+    fw_cfg_reset_order_override();
> >>      return dev;
> >>  }
> >>@@ -1541,6 +1543,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
> >>  {
> >>      int i;
> >>+    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC);
> >>      for (i = 0; i < nb_nics; i++) {
> >>          NICInfo *nd = &nd_table[i];
> >>@@ -1550,6 +1553,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
> >>              pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
> >>          }
> >>      }
> >>+    fw_cfg_reset_order_override();
> >>  }
> >>  void pc_pci_device_init(PCIBus *pci_bus)
> >>diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
> >>index 6f8c2cd..447703b 100644
> >>--- a/hw/i386/pc_piix.c
> >>+++ b/hw/i386/pc_piix.c
> >>@@ -429,6 +429,7 @@ static void pc_i440fx_2_5_machine_options(MachineClass *m)
> >>      m->alias = NULL;
> >>      m->is_default = 0;
> >>      pcmc->save_tsc_khz = false;
> >>+    m->legacy_fw_cfg_order = 1;
> >>      SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
> >>  }
> >>diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
> >>index 46522c9..04f3609 100644
> >>--- a/hw/i386/pc_q35.c
> >>+++ b/hw/i386/pc_q35.c
> >>@@ -291,6 +291,7 @@ static void pc_q35_2_5_machine_options(MachineClass *m)
> >>      pc_q35_2_6_machine_options(m);
> >>      m->alias = NULL;
> >>      pcmc->save_tsc_khz = false;
> >>+    m->legacy_fw_cfg_order = 1;
> >>      SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
> >>  }
> >>diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
> >>index 7866248..1693f26 100644
> >>--- a/hw/nvram/fw_cfg.c
> >>+++ b/hw/nvram/fw_cfg.c
> >>@@ -28,6 +28,7 @@
> >>  #include "hw/isa/isa.h"
> >>  #include "hw/nvram/fw_cfg.h"
> >>  #include "hw/sysbus.h"
> >>+#include "hw/boards.h"
> >>  #include "trace.h"
> >>  #include "qemu/error-report.h"
> >>  #include "qemu/config-file.h"
> >>@@ -68,6 +69,7 @@ struct FWCfgState {
> >>      /*< public >*/
> >>      FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
> >>+    int entry_order[FW_CFG_MAX_ENTRY];
> >>      FWCfgFiles *files;
> >>      uint16_t cur_entry;
> >>      uint32_t cur_offset;
> >>@@ -664,12 +666,88 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
> >>      fw_cfg_add_bytes(s, key, copy, sizeof(value));
> >>  }
> >>+static int fw_cfg_order_override = -1;
> >>+
> >>+void fw_cfg_set_order_override(int order)
> >>+{
> >>+    assert(fw_cfg_order_override == -1);
> >>+    fw_cfg_order_override = order;
> >>+}
> >>+
> >>+void fw_cfg_reset_order_override(void)
> >>+{
> >>+    assert(fw_cfg_order_override != -1);
> >>+    fw_cfg_order_override = -1;
> >>+}
> >>+
> >>+/*
> >>+ * This is the legacy order list.  For legacy systems, files are in
> >>+ * the fw_cfg in the order defined below, by the "order" value.  Note
> >>+ * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a
> >>+ * specific area, but there may be more than one and they occur in the
> >>+ * order that the user specifies them on the command line.  Those are
> >>+ * handled in a special manner, using the order override above.
> >>+ *
> >>+ * For non-legacy, the files are sorted by filename to avoid this kind
> >>+ * of complexity in the future.
> >>+ *
> >>+ * This is only for x86, other arches don't implement versioning so
> >>+ * they won't set legacy mode.
> >>+ */
> >>+static struct {
> >>+    const char *name;
> >>+    int order;
> >>+} fw_cfg_order[] = {
> >>+    { "etc/boot-menu-wait", 10 },
> >>+    { "bootsplash.jpg", 11 },
> >>+    { "bootsplash.bmp", 12 },
> >>+    { "etc/boot-fail-wait", 15 },
> >>+    { "etc/smbios/smbios-tables", 20 },
> >>+    { "etc/smbios/smbios-anchor", 30 },
> >>+    { "etc/e820", 40 },
> >>+    { "etc/reserved-memory-end", 50 },
> >>+    { "genroms/linuxboot.bin", 60 },
> >>+    { }, /* VGA ROMs from pc_vga_init come here, 70. */
> >>+    { }, /* NIC option ROMs from pc_nic_init come here, 80. */
> >>+    { "etc/system-states", 90 },
> >>+    { }, /* User ROMs come here, 100. */
> >>+    { }, /* Device FW comes here, 110. */
> >>+    { "etc/extra-pci-roots", 120 },
> >>+    { "etc/acpi/tables", 130 },
> >>+    { "etc/table-loader", 140 },
> >>+    { "etc/tpm/log", 150 },
> >>+    { "etc/acpi/rsdp", 160 },
> >>+    { "bootorder", 170 },
> >>+
> >>+#define FW_CFG_ORDER_OVERRIDE_LAST 200
> >>+};
> >>+
> >>+static int get_fw_cfg_order(const char *name)
> >>+{
> >>+    int i;
> >>+
> >>+    if (fw_cfg_order_override >= 0)
> >>+	return fw_cfg_order_override;
> >>+
> >>+    for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) {
> >>+	if (fw_cfg_order[i].name == NULL)
> >>+	    continue;
> >>+	if (strcmp(name, fw_cfg_order[i].name) == 0)
> >>+	    return fw_cfg_order[i].order;
> >>+    }
> >>+    /* Stick unknown stuff at the end. */
> >>+    error_report("warning: Unknown firmware file in legacy mode: %s\n", name);
> >>+    return FW_CFG_ORDER_OVERRIDE_LAST;
> >>+}
> >>+
> >>  void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
> >>                                FWCfgReadCallback callback, void *callback_opaque,
> >>                                void *data, size_t len)
> >>  {
> >>-    int i, index;
> >>+    int i, index, count;
> >>      size_t dsize;
> >>+    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
> >>+    int order = 0;
> >>      if (!s->files) {
> >>          dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
> >>@@ -677,13 +755,45 @@ void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
> >>          fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
> >>      }
> >>-    index = be32_to_cpu(s->files->count);
> >>-    assert(index < FW_CFG_FILE_SLOTS);
> >>+    count = be32_to_cpu(s->files->count);
> >>+    assert(count < FW_CFG_FILE_SLOTS);
> >>-    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
> >>-            filename);
> >>-    for (i = 0; i < index; i++) {
> >>-        if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
> >>+    /* Find the insertion point. */
> >>+    if (mc->legacy_fw_cfg_order) {
> >>+        /* Sort by order, and keep insertion order for the same order. */

Pls rephrase for readability.


> >>+        order = get_fw_cfg_order(filename);
> >>+        for (index = count;
> >>+             index > 0 && order < s->entry_order[index - 1];
> >>+             index--);
> >>+    } else {
> >>+        /* Sort by file name. */
> >>+        for (index = count;
> >>+             index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0;
> >>+             index--);
> >>+    }
> >>+
> >>+    /*
> >>+     * Move all the entries from the index point and after down one
> >>+     * to create a slot for the new entry.  Because calculations are
> >>+     * being done with the index, make it so that "i" is the current
> >>+     * index and "i - 1" is the one being copied from, thus the
> >>+     * unusual start and end in the for statement.
> >>+     */
> >>+    for (i = count + 1; i > index; i--) {
> >>+        s->files->f[i] = s->files->f[i - 1];
> >>+        s->files->f[i].select = cpu_to_be16(FW_CFG_FILE_FIRST + i);
> >>+        s->entries[0][FW_CFG_FILE_FIRST + i] =
> >>+            s->entries[0][FW_CFG_FILE_FIRST + i - 1];
> >>+        s->entry_order[i] = s->entry_order[i - 1];
> >>+    }
> >>+
> >>+    memset(&s->files->f[index], 0, sizeof(FWCfgFile));
> >>+    memset(&s->entries[0][FW_CFG_FILE_FIRST + index], 0, sizeof(FWCfgEntry));
> >>+
> >>+    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename);
> >>+    for (i = 0; i <= count; i++) {
> >>+        if (i != index &&
> >>+            strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
> >>              error_report("duplicate fw_cfg file name: %s",
> >>                           s->files->f[index].name);
> >>              exit(1);
> >>@@ -695,9 +805,10 @@ void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
> >>      s->files->f[index].size   = cpu_to_be32(len);
> >>      s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
> >>+    s->entry_order[index] = order;
> >>      trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
> >>-    s->files->count = cpu_to_be32(index+1);
> >>+    s->files->count = cpu_to_be32(count+1);
> >>  }
> >>  void fw_cfg_add_file(FWCfgState *s,  const char *filename,
> >>diff --git a/include/hw/boards.h b/include/hw/boards.h
> >>index b5d7eae..b6567f7 100644
> >>--- a/include/hw/boards.h
> >>+++ b/include/hw/boards.h
> >>@@ -84,7 +84,8 @@ struct MachineClass {
> >>          no_cdrom:1,
> >>          no_sdcard:1,
> >>          has_dynamic_sysbus:1,
> >>-        pci_allow_0_address:1;
> >>+        pci_allow_0_address:1,
> >>+        legacy_fw_cfg_order:1;
> >>      int is_default;
> >>      const char *default_machine_opts;
> >>      const char *default_boot_order;
> >>diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h
> >>index 4315f4e..38bbfca 100644
> >>--- a/include/hw/nvram/fw_cfg.h
> >>+++ b/include/hw/nvram/fw_cfg.h
> >>@@ -57,6 +57,13 @@ typedef struct FWCfgFile {
> >>      char      name[FW_CFG_MAX_FILE_PATH];
> >>  } FWCfgFile;
> >>+#define FW_CFG_ORDER_OVERRIDE_VGA    70
> >>+#define FW_CFG_ORDER_OVERRIDE_NIC    80
> >>+#define FW_CFG_ORDER_OVERRIDE_USER   100
> >>+#define FW_CFG_ORDER_OVERRIDE_DEVICE 110
> >>+void fw_cfg_set_order_override(int order);
> >>+void fw_cfg_reset_order_override(void);
> >>+
> >>  typedef struct FWCfgFiles {
> >>      uint32_t  count;
> >>      FWCfgFile f[];
> >>diff --git a/vl.c b/vl.c
> >>index 7a28982..6fad844 100644
> >>--- a/vl.c
> >>+++ b/vl.c
> >>@@ -4520,10 +4520,12 @@ int main(int argc, char **argv, char **envp)
> >>      numa_post_machine_init();
> >>+    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_USER);
> >>      if (qemu_opts_foreach(qemu_find_opts("fw_cfg"),
> >>                            parse_fw_cfg, fw_cfg_find(), NULL) != 0) {
> >>          exit(1);
> >>      }
> >>+    fw_cfg_reset_order_override();
> >>      /* init USB devices */
> >>      if (usb_enabled()) {
> >>@@ -4535,10 +4537,12 @@ int main(int argc, char **argv, char **envp)
> >>      igd_gfx_passthru();
> >>      /* init generic devices */
> >>+    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE);
> >>      if (qemu_opts_foreach(qemu_find_opts("device"),
> >>                            device_init_func, NULL, NULL)) {
> >>          exit(1);
> >>      }
> >>+    fw_cfg_reset_order_override();
> >>      /* Did we create any drives that we failed to create a device for? */
> >>      drive_check_orphaned();
> >>-- 
> >>2.5.0
> >>
Corey Minyard March 17, 2016, 9:30 p.m. UTC | #4
On 03/17/2016 02:58 PM, Michael S. Tsirkin wrote:
> On Thu, Mar 17, 2016 at 11:41:45AM -0500, Corey Minyard wrote:
>> On 03/17/2016 11:02 AM, Michael S. Tsirkin wrote:
>>> On Wed, Mar 16, 2016 at 01:47:08PM -0500, minyard@acm.org wrote:
>>>> From: Gerd Hoffmann <kraxel@redhat.com>
>>>>
>>>> Entries are inserted in filename order instead of being
>>>> appended to the end in case sorting is enabled.
>>>>
>>>> This will avoid any future issues of moving the file creation
>>>> around, it doesn't matter what order they are created now,
>>>> the will always be in filename order.
>>>>
>>>> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
>>>>
>>>> Added machine type handling for compatibility.  This was
>>>> a fairly complex change, this will preserve the order of fw_cfg
>>>> for older versions no matter what order the firmware files
>>>> actually come in.  A list is kept of the correct legacy order
>>>> and the entries will be inserted based upon their order in
>>>> the list.  Except that some entries are ordered (in a specific
>>>> area of the list) based upon what order they appear on the
>>>> command line.  Special handling is added for those entries.
>>>>
>>>> Signed-off-by: Corey Minyard <cminyard@mvista.com>
>>>> ---
>>>>
>>>> A new version of the sorted fw_cfg, with new backwards
>>>> compatibility code.  This is bigger than I would like, but
>>>> I can't think of a good way to split it up that would make
>>>> much sense.
>>>>
>>>> This is more for review, anyway, I'm sure there are issues with
>>>> this.
>>>>
>>>> I'm not too excited about fw_cfg_set_order_override(), but I can't
>>>> think of a better way to handle this.  You can't use filename or
>>>> prefixes, the same filenames appear in different places for VGA
>>>> ROMs depending on if they are PCI or ISA.
>>> I don't see another good solution either.
>>> However, I would like to make these NOPs with
>>> new machine types.
>>>
>>> This way down the road we can drop this mess.
>> They should be NOPs with new machine types.  These
>> only have an effect if legacy_fw_cfg_order is set in the
>> machine class, and that's only set for older machines.
>>
>> -corey
> I didn't realize this.
> OK.
>  From layering point of view, I think I would
> like machines to call rom_set_order_override().
> And then the fw cfg one should get fw cfg argument.

Do you mean you want to add rom_set_order_override()
function that gets called from most places that then
calls fw_cfg_set_order_override() with the fw_cfg as an
argument?  Yeah, I can see that makes sense.  Then
you can put the override order variable in FWCfgState.

>>>>   hw/i386/pc.c              |   4 ++
>>>>   hw/i386/pc_piix.c         |   1 +
>>>>   hw/i386/pc_q35.c          |   1 +
>>>>   hw/nvram/fw_cfg.c         | 127 +++++++++++++++++++++++++++++++++++++++++++---
>>>>   include/hw/boards.h       |   3 +-
>>>>   include/hw/nvram/fw_cfg.h |   7 +++
>>>>   vl.c                      |   4 ++
>>>>   7 files changed, 138 insertions(+), 9 deletions(-)
>>>>
>>>> diff --git a/hw/i386/pc.c b/hw/i386/pc.c
>>>> index 56ec6cd..707753b 100644
>>>> --- a/hw/i386/pc.c
>>>> +++ b/hw/i386/pc.c
>>>> @@ -1406,6 +1406,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
>>>>   {
>>>>       DeviceState *dev = NULL;
>>>> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA);
>>>>       if (pci_bus) {
>>>>           PCIDevice *pcidev = pci_vga_init(pci_bus);
>>>>           dev = pcidev ? &pcidev->qdev : NULL;
>>>> @@ -1413,6 +1414,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
>>>>           ISADevice *isadev = isa_vga_init(isa_bus);
>>>>           dev = isadev ? DEVICE(isadev) : NULL;
>>>>       }
>>>> +    fw_cfg_reset_order_override();
>>>>       return dev;
>>>>   }
>>>> @@ -1541,6 +1543,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
>>>>   {
>>>>       int i;
>>>> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC);
>>>>       for (i = 0; i < nb_nics; i++) {
>>>>           NICInfo *nd = &nd_table[i];
>>>> @@ -1550,6 +1553,7 @@ void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
>>>>               pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
>>>>           }
>>>>       }
>>>> +    fw_cfg_reset_order_override();
>>>>   }
>>>>   void pc_pci_device_init(PCIBus *pci_bus)
>>>> diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
>>>> index 6f8c2cd..447703b 100644
>>>> --- a/hw/i386/pc_piix.c
>>>> +++ b/hw/i386/pc_piix.c
>>>> @@ -429,6 +429,7 @@ static void pc_i440fx_2_5_machine_options(MachineClass *m)
>>>>       m->alias = NULL;
>>>>       m->is_default = 0;
>>>>       pcmc->save_tsc_khz = false;
>>>> +    m->legacy_fw_cfg_order = 1;
>>>>       SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
>>>>   }
>>>> diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
>>>> index 46522c9..04f3609 100644
>>>> --- a/hw/i386/pc_q35.c
>>>> +++ b/hw/i386/pc_q35.c
>>>> @@ -291,6 +291,7 @@ static void pc_q35_2_5_machine_options(MachineClass *m)
>>>>       pc_q35_2_6_machine_options(m);
>>>>       m->alias = NULL;
>>>>       pcmc->save_tsc_khz = false;
>>>> +    m->legacy_fw_cfg_order = 1;
>>>>       SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
>>>>   }
>>>> diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
>>>> index 7866248..1693f26 100644
>>>> --- a/hw/nvram/fw_cfg.c
>>>> +++ b/hw/nvram/fw_cfg.c
>>>> @@ -28,6 +28,7 @@
>>>>   #include "hw/isa/isa.h"
>>>>   #include "hw/nvram/fw_cfg.h"
>>>>   #include "hw/sysbus.h"
>>>> +#include "hw/boards.h"
>>>>   #include "trace.h"
>>>>   #include "qemu/error-report.h"
>>>>   #include "qemu/config-file.h"
>>>> @@ -68,6 +69,7 @@ struct FWCfgState {
>>>>       /*< public >*/
>>>>       FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
>>>> +    int entry_order[FW_CFG_MAX_ENTRY];
>>>>       FWCfgFiles *files;
>>>>       uint16_t cur_entry;
>>>>       uint32_t cur_offset;
>>>> @@ -664,12 +666,88 @@ void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
>>>>       fw_cfg_add_bytes(s, key, copy, sizeof(value));
>>>>   }
>>>> +static int fw_cfg_order_override = -1;
>>>> +
>>>> +void fw_cfg_set_order_override(int order)
>>>> +{
>>>> +    assert(fw_cfg_order_override == -1);
>>>> +    fw_cfg_order_override = order;
>>>> +}
>>>> +
>>>> +void fw_cfg_reset_order_override(void)
>>>> +{
>>>> +    assert(fw_cfg_order_override != -1);
>>>> +    fw_cfg_order_override = -1;
>>>> +}
>>>> +
>>>> +/*
>>>> + * This is the legacy order list.  For legacy systems, files are in
>>>> + * the fw_cfg in the order defined below, by the "order" value.  Note
>>>> + * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a
>>>> + * specific area, but there may be more than one and they occur in the
>>>> + * order that the user specifies them on the command line.  Those are
>>>> + * handled in a special manner, using the order override above.
>>>> + *
>>>> + * For non-legacy, the files are sorted by filename to avoid this kind
>>>> + * of complexity in the future.
>>>> + *
>>>> + * This is only for x86, other arches don't implement versioning so
>>>> + * they won't set legacy mode.
>>>> + */
>>>> +static struct {
>>>> +    const char *name;
>>>> +    int order;
>>>> +} fw_cfg_order[] = {
>>>> +    { "etc/boot-menu-wait", 10 },
>>>> +    { "bootsplash.jpg", 11 },
>>>> +    { "bootsplash.bmp", 12 },
>>>> +    { "etc/boot-fail-wait", 15 },
>>>> +    { "etc/smbios/smbios-tables", 20 },
>>>> +    { "etc/smbios/smbios-anchor", 30 },
>>>> +    { "etc/e820", 40 },
>>>> +    { "etc/reserved-memory-end", 50 },
>>>> +    { "genroms/linuxboot.bin", 60 },
>>>> +    { }, /* VGA ROMs from pc_vga_init come here, 70. */
>>>> +    { }, /* NIC option ROMs from pc_nic_init come here, 80. */
>>>> +    { "etc/system-states", 90 },
>>>> +    { }, /* User ROMs come here, 100. */
>>>> +    { }, /* Device FW comes here, 110. */
>>>> +    { "etc/extra-pci-roots", 120 },
>>>> +    { "etc/acpi/tables", 130 },
>>>> +    { "etc/table-loader", 140 },
>>>> +    { "etc/tpm/log", 150 },
>>>> +    { "etc/acpi/rsdp", 160 },
>>>> +    { "bootorder", 170 },
>>>> +
>>>> +#define FW_CFG_ORDER_OVERRIDE_LAST 200
>>>> +};
>>>> +
>>>> +static int get_fw_cfg_order(const char *name)
>>>> +{
>>>> +    int i;
>>>> +
>>>> +    if (fw_cfg_order_override >= 0)
>>>> +	return fw_cfg_order_override;
>>>> +
>>>> +    for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) {
>>>> +	if (fw_cfg_order[i].name == NULL)
>>>> +	    continue;
>>>> +	if (strcmp(name, fw_cfg_order[i].name) == 0)
>>>> +	    return fw_cfg_order[i].order;
>>>> +    }
>>>> +    /* Stick unknown stuff at the end. */
>>>> +    error_report("warning: Unknown firmware file in legacy mode: %s\n", name);
>>>> +    return FW_CFG_ORDER_OVERRIDE_LAST;
>>>> +}
>>>> +
>>>>   void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
>>>>                                 FWCfgReadCallback callback, void *callback_opaque,
>>>>                                 void *data, size_t len)
>>>>   {
>>>> -    int i, index;
>>>> +    int i, index, count;
>>>>       size_t dsize;
>>>> +    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
>>>> +    int order = 0;
>>>>       if (!s->files) {
>>>>           dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
>>>> @@ -677,13 +755,45 @@ void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
>>>>           fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
>>>>       }
>>>> -    index = be32_to_cpu(s->files->count);
>>>> -    assert(index < FW_CFG_FILE_SLOTS);
>>>> +    count = be32_to_cpu(s->files->count);
>>>> +    assert(count < FW_CFG_FILE_SLOTS);
>>>> -    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
>>>> -            filename);
>>>> -    for (i = 0; i < index; i++) {
>>>> -        if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
>>>> +    /* Find the insertion point. */
>>>> +    if (mc->legacy_fw_cfg_order) {
>>>> +        /* Sort by order, and keep insertion order for the same order. */
> Pls rephrase for readability.

Yeah, reading that again, it's pretty hard to parse.

-corey

>
>>>> +        order = get_fw_cfg_order(filename);
>>>> +        for (index = count;
>>>> +             index > 0 && order < s->entry_order[index - 1];
>>>> +             index--);
>>>> +    } else {
>>>> +        /* Sort by file name. */
>>>> +        for (index = count;
>>>> +             index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0;
>>>> +             index--);
>>>> +    }
>>>> +
>>>> +    /*
>>>> +     * Move all the entries from the index point and after down one
>>>> +     * to create a slot for the new entry.  Because calculations are
>>>> +     * being done with the index, make it so that "i" is the current
>>>> +     * index and "i - 1" is the one being copied from, thus the
>>>> +     * unusual start and end in the for statement.
>>>> +     */
>>>> +    for (i = count + 1; i > index; i--) {
>>>> +        s->files->f[i] = s->files->f[i - 1];
>>>> +        s->files->f[i].select = cpu_to_be16(FW_CFG_FILE_FIRST + i);
>>>> +        s->entries[0][FW_CFG_FILE_FIRST + i] =
>>>> +            s->entries[0][FW_CFG_FILE_FIRST + i - 1];
>>>> +        s->entry_order[i] = s->entry_order[i - 1];
>>>> +    }
>>>> +
>>>> +    memset(&s->files->f[index], 0, sizeof(FWCfgFile));
>>>> +    memset(&s->entries[0][FW_CFG_FILE_FIRST + index], 0, sizeof(FWCfgEntry));
>>>> +
>>>> +    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename);
>>>> +    for (i = 0; i <= count; i++) {
>>>> +        if (i != index &&
>>>> +            strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
>>>>               error_report("duplicate fw_cfg file name: %s",
>>>>                            s->files->f[index].name);
>>>>               exit(1);
>>>> @@ -695,9 +805,10 @@ void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
>>>>       s->files->f[index].size   = cpu_to_be32(len);
>>>>       s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
>>>> +    s->entry_order[index] = order;
>>>>       trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
>>>> -    s->files->count = cpu_to_be32(index+1);
>>>> +    s->files->count = cpu_to_be32(count+1);
>>>>   }
>>>>   void fw_cfg_add_file(FWCfgState *s,  const char *filename,
>>>> diff --git a/include/hw/boards.h b/include/hw/boards.h
>>>> index b5d7eae..b6567f7 100644
>>>> --- a/include/hw/boards.h
>>>> +++ b/include/hw/boards.h
>>>> @@ -84,7 +84,8 @@ struct MachineClass {
>>>>           no_cdrom:1,
>>>>           no_sdcard:1,
>>>>           has_dynamic_sysbus:1,
>>>> -        pci_allow_0_address:1;
>>>> +        pci_allow_0_address:1,
>>>> +        legacy_fw_cfg_order:1;
>>>>       int is_default;
>>>>       const char *default_machine_opts;
>>>>       const char *default_boot_order;
>>>> diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h
>>>> index 4315f4e..38bbfca 100644
>>>> --- a/include/hw/nvram/fw_cfg.h
>>>> +++ b/include/hw/nvram/fw_cfg.h
>>>> @@ -57,6 +57,13 @@ typedef struct FWCfgFile {
>>>>       char      name[FW_CFG_MAX_FILE_PATH];
>>>>   } FWCfgFile;
>>>> +#define FW_CFG_ORDER_OVERRIDE_VGA    70
>>>> +#define FW_CFG_ORDER_OVERRIDE_NIC    80
>>>> +#define FW_CFG_ORDER_OVERRIDE_USER   100
>>>> +#define FW_CFG_ORDER_OVERRIDE_DEVICE 110
>>>> +void fw_cfg_set_order_override(int order);
>>>> +void fw_cfg_reset_order_override(void);
>>>> +
>>>>   typedef struct FWCfgFiles {
>>>>       uint32_t  count;
>>>>       FWCfgFile f[];
>>>> diff --git a/vl.c b/vl.c
>>>> index 7a28982..6fad844 100644
>>>> --- a/vl.c
>>>> +++ b/vl.c
>>>> @@ -4520,10 +4520,12 @@ int main(int argc, char **argv, char **envp)
>>>>       numa_post_machine_init();
>>>> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_USER);
>>>>       if (qemu_opts_foreach(qemu_find_opts("fw_cfg"),
>>>>                             parse_fw_cfg, fw_cfg_find(), NULL) != 0) {
>>>>           exit(1);
>>>>       }
>>>> +    fw_cfg_reset_order_override();
>>>>       /* init USB devices */
>>>>       if (usb_enabled()) {
>>>> @@ -4535,10 +4537,12 @@ int main(int argc, char **argv, char **envp)
>>>>       igd_gfx_passthru();
>>>>       /* init generic devices */
>>>> +    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE);
>>>>       if (qemu_opts_foreach(qemu_find_opts("device"),
>>>>                             device_init_func, NULL, NULL)) {
>>>>           exit(1);
>>>>       }
>>>> +    fw_cfg_reset_order_override();
>>>>       /* Did we create any drives that we failed to create a device for? */
>>>>       drive_check_orphaned();
>>>> -- 
>>>> 2.5.0
>>>>
diff mbox

Patch

diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 56ec6cd..707753b 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -1406,6 +1406,7 @@  DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
 {
     DeviceState *dev = NULL;
 
+    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA);
     if (pci_bus) {
         PCIDevice *pcidev = pci_vga_init(pci_bus);
         dev = pcidev ? &pcidev->qdev : NULL;
@@ -1413,6 +1414,7 @@  DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus)
         ISADevice *isadev = isa_vga_init(isa_bus);
         dev = isadev ? DEVICE(isadev) : NULL;
     }
+    fw_cfg_reset_order_override();
     return dev;
 }
 
@@ -1541,6 +1543,7 @@  void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
 {
     int i;
 
+    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC);
     for (i = 0; i < nb_nics; i++) {
         NICInfo *nd = &nd_table[i];
 
@@ -1550,6 +1553,7 @@  void pc_nic_init(ISABus *isa_bus, PCIBus *pci_bus)
             pci_nic_init_nofail(nd, pci_bus, "e1000", NULL);
         }
     }
+    fw_cfg_reset_order_override();
 }
 
 void pc_pci_device_init(PCIBus *pci_bus)
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 6f8c2cd..447703b 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -429,6 +429,7 @@  static void pc_i440fx_2_5_machine_options(MachineClass *m)
     m->alias = NULL;
     m->is_default = 0;
     pcmc->save_tsc_khz = false;
+    m->legacy_fw_cfg_order = 1;
     SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
 }
 
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 46522c9..04f3609 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -291,6 +291,7 @@  static void pc_q35_2_5_machine_options(MachineClass *m)
     pc_q35_2_6_machine_options(m);
     m->alias = NULL;
     pcmc->save_tsc_khz = false;
+    m->legacy_fw_cfg_order = 1;
     SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
 }
 
diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
index 7866248..1693f26 100644
--- a/hw/nvram/fw_cfg.c
+++ b/hw/nvram/fw_cfg.c
@@ -28,6 +28,7 @@ 
 #include "hw/isa/isa.h"
 #include "hw/nvram/fw_cfg.h"
 #include "hw/sysbus.h"
+#include "hw/boards.h"
 #include "trace.h"
 #include "qemu/error-report.h"
 #include "qemu/config-file.h"
@@ -68,6 +69,7 @@  struct FWCfgState {
     /*< public >*/
 
     FWCfgEntry entries[2][FW_CFG_MAX_ENTRY];
+    int entry_order[FW_CFG_MAX_ENTRY];
     FWCfgFiles *files;
     uint16_t cur_entry;
     uint32_t cur_offset;
@@ -664,12 +666,88 @@  void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
     fw_cfg_add_bytes(s, key, copy, sizeof(value));
 }
 
+static int fw_cfg_order_override = -1;
+
+void fw_cfg_set_order_override(int order)
+{
+    assert(fw_cfg_order_override == -1);
+    fw_cfg_order_override = order;
+}
+
+void fw_cfg_reset_order_override(void)
+{
+    assert(fw_cfg_order_override != -1);
+    fw_cfg_order_override = -1;
+}
+
+/*
+ * This is the legacy order list.  For legacy systems, files are in
+ * the fw_cfg in the order defined below, by the "order" value.  Note
+ * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a
+ * specific area, but there may be more than one and they occur in the
+ * order that the user specifies them on the command line.  Those are
+ * handled in a special manner, using the order override above.
+ *
+ * For non-legacy, the files are sorted by filename to avoid this kind
+ * of complexity in the future.
+ *
+ * This is only for x86, other arches don't implement versioning so
+ * they won't set legacy mode.
+ */
+static struct {
+    const char *name;
+    int order;
+} fw_cfg_order[] = {
+    { "etc/boot-menu-wait", 10 },
+    { "bootsplash.jpg", 11 },
+    { "bootsplash.bmp", 12 },
+    { "etc/boot-fail-wait", 15 },
+    { "etc/smbios/smbios-tables", 20 },
+    { "etc/smbios/smbios-anchor", 30 },
+    { "etc/e820", 40 },
+    { "etc/reserved-memory-end", 50 },
+    { "genroms/linuxboot.bin", 60 },
+    { }, /* VGA ROMs from pc_vga_init come here, 70. */
+    { }, /* NIC option ROMs from pc_nic_init come here, 80. */
+    { "etc/system-states", 90 },
+    { }, /* User ROMs come here, 100. */
+    { }, /* Device FW comes here, 110. */
+    { "etc/extra-pci-roots", 120 },
+    { "etc/acpi/tables", 130 },
+    { "etc/table-loader", 140 },
+    { "etc/tpm/log", 150 },
+    { "etc/acpi/rsdp", 160 },
+    { "bootorder", 170 },
+
+#define FW_CFG_ORDER_OVERRIDE_LAST 200
+};
+
+static int get_fw_cfg_order(const char *name)
+{
+    int i;
+
+    if (fw_cfg_order_override >= 0)
+	return fw_cfg_order_override;
+
+    for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) {
+	if (fw_cfg_order[i].name == NULL)
+	    continue;
+	if (strcmp(name, fw_cfg_order[i].name) == 0)
+	    return fw_cfg_order[i].order;
+    }
+    /* Stick unknown stuff at the end. */
+    error_report("warning: Unknown firmware file in legacy mode: %s\n", name);
+    return FW_CFG_ORDER_OVERRIDE_LAST;
+}
+
 void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
                               FWCfgReadCallback callback, void *callback_opaque,
                               void *data, size_t len)
 {
-    int i, index;
+    int i, index, count;
     size_t dsize;
+    MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+    int order = 0;
 
     if (!s->files) {
         dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * FW_CFG_FILE_SLOTS;
@@ -677,13 +755,45 @@  void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
         fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
     }
 
-    index = be32_to_cpu(s->files->count);
-    assert(index < FW_CFG_FILE_SLOTS);
+    count = be32_to_cpu(s->files->count);
+    assert(count < FW_CFG_FILE_SLOTS);
 
-    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name),
-            filename);
-    for (i = 0; i < index; i++) {
-        if (strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
+    /* Find the insertion point. */
+    if (mc->legacy_fw_cfg_order) {
+        /* Sort by order, and keep insertion order for the same order. */
+        order = get_fw_cfg_order(filename);
+        for (index = count;
+             index > 0 && order < s->entry_order[index - 1];
+             index--);
+    } else {
+        /* Sort by file name. */
+        for (index = count;
+             index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0;
+             index--);
+    }
+
+    /*
+     * Move all the entries from the index point and after down one
+     * to create a slot for the new entry.  Because calculations are
+     * being done with the index, make it so that "i" is the current
+     * index and "i - 1" is the one being copied from, thus the
+     * unusual start and end in the for statement.
+     */
+    for (i = count + 1; i > index; i--) {
+        s->files->f[i] = s->files->f[i - 1];
+        s->files->f[i].select = cpu_to_be16(FW_CFG_FILE_FIRST + i);
+        s->entries[0][FW_CFG_FILE_FIRST + i] =
+            s->entries[0][FW_CFG_FILE_FIRST + i - 1];
+        s->entry_order[i] = s->entry_order[i - 1];
+    }
+
+    memset(&s->files->f[index], 0, sizeof(FWCfgFile));
+    memset(&s->entries[0][FW_CFG_FILE_FIRST + index], 0, sizeof(FWCfgEntry));
+
+    pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename);
+    for (i = 0; i <= count; i++) {
+        if (i != index &&
+            strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
             error_report("duplicate fw_cfg file name: %s",
                          s->files->f[index].name);
             exit(1);
@@ -695,9 +805,10 @@  void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
 
     s->files->f[index].size   = cpu_to_be32(len);
     s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
+    s->entry_order[index] = order;
     trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
 
-    s->files->count = cpu_to_be32(index+1);
+    s->files->count = cpu_to_be32(count+1);
 }
 
 void fw_cfg_add_file(FWCfgState *s,  const char *filename,
diff --git a/include/hw/boards.h b/include/hw/boards.h
index b5d7eae..b6567f7 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -84,7 +84,8 @@  struct MachineClass {
         no_cdrom:1,
         no_sdcard:1,
         has_dynamic_sysbus:1,
-        pci_allow_0_address:1;
+        pci_allow_0_address:1,
+        legacy_fw_cfg_order:1;
     int is_default;
     const char *default_machine_opts;
     const char *default_boot_order;
diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h
index 4315f4e..38bbfca 100644
--- a/include/hw/nvram/fw_cfg.h
+++ b/include/hw/nvram/fw_cfg.h
@@ -57,6 +57,13 @@  typedef struct FWCfgFile {
     char      name[FW_CFG_MAX_FILE_PATH];
 } FWCfgFile;
 
+#define FW_CFG_ORDER_OVERRIDE_VGA    70
+#define FW_CFG_ORDER_OVERRIDE_NIC    80
+#define FW_CFG_ORDER_OVERRIDE_USER   100
+#define FW_CFG_ORDER_OVERRIDE_DEVICE 110
+void fw_cfg_set_order_override(int order);
+void fw_cfg_reset_order_override(void);
+
 typedef struct FWCfgFiles {
     uint32_t  count;
     FWCfgFile f[];
diff --git a/vl.c b/vl.c
index 7a28982..6fad844 100644
--- a/vl.c
+++ b/vl.c
@@ -4520,10 +4520,12 @@  int main(int argc, char **argv, char **envp)
 
     numa_post_machine_init();
 
+    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_USER);
     if (qemu_opts_foreach(qemu_find_opts("fw_cfg"),
                           parse_fw_cfg, fw_cfg_find(), NULL) != 0) {
         exit(1);
     }
+    fw_cfg_reset_order_override();
 
     /* init USB devices */
     if (usb_enabled()) {
@@ -4535,10 +4537,12 @@  int main(int argc, char **argv, char **envp)
     igd_gfx_passthru();
 
     /* init generic devices */
+    fw_cfg_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE);
     if (qemu_opts_foreach(qemu_find_opts("device"),
                           device_init_func, NULL, NULL)) {
         exit(1);
     }
+    fw_cfg_reset_order_override();
 
     /* Did we create any drives that we failed to create a device for? */
     drive_check_orphaned();