xref: /openbmc/qemu/hw/nvram/fw_cfg.c (revision a3fb4e93a3a7cf2be355c41cd550bef856f5ffe4)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * QEMU Firmware configuration device emulation
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright (c) 2008 Gleb Natapov
549ab747fSPaolo Bonzini  *
649ab747fSPaolo Bonzini  * Permission is hereby granted, free of charge, to any person obtaining a copy
749ab747fSPaolo Bonzini  * of this software and associated documentation files (the "Software"), to deal
849ab747fSPaolo Bonzini  * in the Software without restriction, including without limitation the rights
949ab747fSPaolo Bonzini  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1049ab747fSPaolo Bonzini  * copies of the Software, and to permit persons to whom the Software is
1149ab747fSPaolo Bonzini  * furnished to do so, subject to the following conditions:
1249ab747fSPaolo Bonzini  *
1349ab747fSPaolo Bonzini  * The above copyright notice and this permission notice shall be included in
1449ab747fSPaolo Bonzini  * all copies or substantial portions of the Software.
1549ab747fSPaolo Bonzini  *
1649ab747fSPaolo Bonzini  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1749ab747fSPaolo Bonzini  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1849ab747fSPaolo Bonzini  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1949ab747fSPaolo Bonzini  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2049ab747fSPaolo Bonzini  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2149ab747fSPaolo Bonzini  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2249ab747fSPaolo Bonzini  * THE SOFTWARE.
2349ab747fSPaolo Bonzini  */
24922a01a0SMarkus Armbruster 
250430891cSPeter Maydell #include "qemu/osdep.h"
262c65db5eSPaolo Bonzini #include "qemu/datadir.h"
2749ab747fSPaolo Bonzini #include "sysemu/sysemu.h"
28a4c0d1deSMarc Marí #include "sysemu/dma.h"
2971e8a915SMarkus Armbruster #include "sysemu/reset.h"
300068b069SPhilippe Mathieu-Daudé #include "exec/address-spaces.h"
31cfc58cf3SEduardo Habkost #include "hw/boards.h"
3249ab747fSPaolo Bonzini #include "hw/nvram/fw_cfg.h"
33a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
3449ab747fSPaolo Bonzini #include "hw/sysbus.h"
35ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h"
36d6454270SMarkus Armbruster #include "migration/vmstate.h"
3749ab747fSPaolo Bonzini #include "trace.h"
3849ab747fSPaolo Bonzini #include "qemu/error-report.h"
39922a01a0SMarkus Armbruster #include "qemu/option.h"
4049ab747fSPaolo Bonzini #include "qemu/config-file.h"
41f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
42e12f3a13SLaszlo Ersek #include "qapi/error.h"
43394f0f72SShameer Kolothum #include "hw/acpi/aml-build.h"
440abd3888SJiahui Cen #include "hw/pci/pci_bus.h"
45785a7383SSunil V L #include "hw/loader.h"
4649ab747fSPaolo Bonzini 
47a5b3ebfdSLaszlo Ersek #define FW_CFG_FILE_SLOTS_DFLT 0x20
48a5b3ebfdSLaszlo Ersek 
49a4c0d1deSMarc Marí /* FW_CFG_VERSION bits */
50a4c0d1deSMarc Marí #define FW_CFG_VERSION      0x01
51a4c0d1deSMarc Marí #define FW_CFG_VERSION_DMA  0x02
52a4c0d1deSMarc Marí 
53a4c0d1deSMarc Marí /* FW_CFG_DMA_CONTROL bits */
54a4c0d1deSMarc Marí #define FW_CFG_DMA_CTL_ERROR   0x01
55a4c0d1deSMarc Marí #define FW_CFG_DMA_CTL_READ    0x02
56a4c0d1deSMarc Marí #define FW_CFG_DMA_CTL_SKIP    0x04
57a4c0d1deSMarc Marí #define FW_CFG_DMA_CTL_SELECT  0x08
58baf2d5bfSMichael S. Tsirkin #define FW_CFG_DMA_CTL_WRITE   0x10
59a4c0d1deSMarc Marí 
602cc06a88SKevin O'Connor #define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */
612cc06a88SKevin O'Connor 
6239736e18SMark Cave-Ayland struct FWCfgEntry {
6349ab747fSPaolo Bonzini     uint32_t len;
64baf2d5bfSMichael S. Tsirkin     bool allow_write;
6549ab747fSPaolo Bonzini     uint8_t *data;
6649ab747fSPaolo Bonzini     void *callback_opaque;
676f6f4aecSMarc-André Lureau     FWCfgCallback select_cb;
685f9252f7SMarc-André Lureau     FWCfgWriteCallback write_cb;
695712db6aSLaszlo Ersek };
705712db6aSLaszlo Ersek 
711f80b0d6SPhilippe Mathieu-Daudé /**
721f80b0d6SPhilippe Mathieu-Daudé  * key_name:
731f80b0d6SPhilippe Mathieu-Daudé  *
741f80b0d6SPhilippe Mathieu-Daudé  * @key: The uint16 selector key.
751f80b0d6SPhilippe Mathieu-Daudé  *
761f80b0d6SPhilippe Mathieu-Daudé  * Returns: The stringified name if the selector refers to a well-known
771f80b0d6SPhilippe Mathieu-Daudé  *          numerically defined item, or NULL on key lookup failure.
781f80b0d6SPhilippe Mathieu-Daudé  */
key_name(uint16_t key)791f80b0d6SPhilippe Mathieu-Daudé static const char *key_name(uint16_t key)
801f80b0d6SPhilippe Mathieu-Daudé {
811f80b0d6SPhilippe Mathieu-Daudé     static const char *fw_cfg_wellknown_keys[FW_CFG_FILE_FIRST] = {
821f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_SIGNATURE] = "signature",
831f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_ID] = "id",
841f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_UUID] = "uuid",
851f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_RAM_SIZE] = "ram_size",
861f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_NOGRAPHIC] = "nographic",
871f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_NB_CPUS] = "nb_cpus",
881f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_MACHINE_ID] = "machine_id",
891f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_KERNEL_ADDR] = "kernel_addr",
901f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_KERNEL_SIZE] = "kernel_size",
911f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_KERNEL_CMDLINE] = "kernel_cmdline",
921f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_INITRD_ADDR] = "initrd_addr",
931f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_INITRD_SIZE] = "initdr_size",
941f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_BOOT_DEVICE] = "boot_device",
951f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_NUMA] = "numa",
961f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_BOOT_MENU] = "boot_menu",
971f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_MAX_CPUS] = "max_cpus",
981f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_KERNEL_ENTRY] = "kernel_entry",
991f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_KERNEL_DATA] = "kernel_data",
1001f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_INITRD_DATA] = "initrd_data",
1011f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_CMDLINE_ADDR] = "cmdline_addr",
1021f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_CMDLINE_SIZE] = "cmdline_size",
1031f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_CMDLINE_DATA] = "cmdline_data",
1041f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_SETUP_ADDR] = "setup_addr",
1051f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_SETUP_SIZE] = "setup_size",
1061f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_SETUP_DATA] = "setup_data",
1071f80b0d6SPhilippe Mathieu-Daudé         [FW_CFG_FILE_DIR] = "file_dir",
1081f80b0d6SPhilippe Mathieu-Daudé     };
1091f80b0d6SPhilippe Mathieu-Daudé 
1101f80b0d6SPhilippe Mathieu-Daudé     if (key & FW_CFG_ARCH_LOCAL) {
111b15c0f7dSPhilippe Mathieu-Daudé         return fw_cfg_arch_key_name(key);
1121f80b0d6SPhilippe Mathieu-Daudé     }
1131f80b0d6SPhilippe Mathieu-Daudé     if (key < FW_CFG_FILE_FIRST) {
1141f80b0d6SPhilippe Mathieu-Daudé         return fw_cfg_wellknown_keys[key];
1151f80b0d6SPhilippe Mathieu-Daudé     }
1161f80b0d6SPhilippe Mathieu-Daudé 
1171f80b0d6SPhilippe Mathieu-Daudé     return NULL;
1181f80b0d6SPhilippe Mathieu-Daudé }
1191f80b0d6SPhilippe Mathieu-Daudé 
trace_key_name(uint16_t key)1201f80b0d6SPhilippe Mathieu-Daudé static inline const char *trace_key_name(uint16_t key)
1211f80b0d6SPhilippe Mathieu-Daudé {
1221f80b0d6SPhilippe Mathieu-Daudé     const char *name = key_name(key);
1231f80b0d6SPhilippe Mathieu-Daudé 
1241f80b0d6SPhilippe Mathieu-Daudé     return name ? name : "unknown";
1251f80b0d6SPhilippe Mathieu-Daudé }
1261f80b0d6SPhilippe Mathieu-Daudé 
12749ab747fSPaolo Bonzini #define JPG_FILE 0
12849ab747fSPaolo Bonzini #define BMP_FILE 1
12949ab747fSPaolo Bonzini 
read_splashfile(char * filename,gsize * file_sizep,int * file_typep)1303d1bba20SPeter Crosthwaite static char *read_splashfile(char *filename, gsize *file_sizep,
13149ab747fSPaolo Bonzini                              int *file_typep)
13249ab747fSPaolo Bonzini {
13349ab747fSPaolo Bonzini     GError *err = NULL;
13449ab747fSPaolo Bonzini     gchar *content;
13549ab747fSPaolo Bonzini     int file_type;
13649ab747fSPaolo Bonzini     unsigned int filehead;
13749ab747fSPaolo Bonzini     int bmp_bpp;
13849ab747fSPaolo Bonzini 
139bed66336SLi Qiang     if (!g_file_get_contents(filename, &content, file_sizep, &err)) {
140bed66336SLi Qiang         error_report("failed to read splash file '%s': %s",
141bed66336SLi Qiang                      filename, err->message);
14249ab747fSPaolo Bonzini         g_error_free(err);
14349ab747fSPaolo Bonzini         return NULL;
14449ab747fSPaolo Bonzini     }
14549ab747fSPaolo Bonzini 
14649ab747fSPaolo Bonzini     /* check file size */
14749ab747fSPaolo Bonzini     if (*file_sizep < 30) {
14849ab747fSPaolo Bonzini         goto error;
14949ab747fSPaolo Bonzini     }
15049ab747fSPaolo Bonzini 
15149ab747fSPaolo Bonzini     /* check magic ID */
1523b777a79SPhilippe Mathieu-Daudé     filehead = lduw_le_p(content);
15349ab747fSPaolo Bonzini     if (filehead == 0xd8ff) {
15449ab747fSPaolo Bonzini         file_type = JPG_FILE;
15549ab747fSPaolo Bonzini     } else if (filehead == 0x4d42) {
15649ab747fSPaolo Bonzini         file_type = BMP_FILE;
15749ab747fSPaolo Bonzini     } else {
15849ab747fSPaolo Bonzini         goto error;
15949ab747fSPaolo Bonzini     }
16049ab747fSPaolo Bonzini 
16149ab747fSPaolo Bonzini     /* check BMP bpp */
16249ab747fSPaolo Bonzini     if (file_type == BMP_FILE) {
1633b777a79SPhilippe Mathieu-Daudé         bmp_bpp = lduw_le_p(&content[28]);
16449ab747fSPaolo Bonzini         if (bmp_bpp != 24) {
16549ab747fSPaolo Bonzini             goto error;
16649ab747fSPaolo Bonzini         }
16749ab747fSPaolo Bonzini     }
16849ab747fSPaolo Bonzini 
16949ab747fSPaolo Bonzini     /* return values */
17049ab747fSPaolo Bonzini     *file_typep = file_type;
17149ab747fSPaolo Bonzini 
17249ab747fSPaolo Bonzini     return content;
17349ab747fSPaolo Bonzini 
17449ab747fSPaolo Bonzini error:
17549ab747fSPaolo Bonzini     error_report("splash file '%s' format not recognized; must be JPEG "
17649ab747fSPaolo Bonzini                  "or 24 bit BMP", filename);
17749ab747fSPaolo Bonzini     g_free(content);
17849ab747fSPaolo Bonzini     return NULL;
17949ab747fSPaolo Bonzini }
18049ab747fSPaolo Bonzini 
fw_cfg_bootsplash(FWCfgState * s)18149ab747fSPaolo Bonzini static void fw_cfg_bootsplash(FWCfgState *s)
18249ab747fSPaolo Bonzini {
18349ab747fSPaolo Bonzini     char *filename, *file_data;
1843d1bba20SPeter Crosthwaite     gsize file_size;
18549ab747fSPaolo Bonzini     int file_type;
18649ab747fSPaolo Bonzini 
18749ab747fSPaolo Bonzini     /* insert splash time if user configurated */
18897ec4d21SPaolo Bonzini     if (current_machine->boot_config.has_splash_time) {
18997ec4d21SPaolo Bonzini         int64_t bst_val = current_machine->boot_config.splash_time;
1903b3df3e5SLaszlo Ersek         uint16_t bst_le16;
1913b3df3e5SLaszlo Ersek 
19249ab747fSPaolo Bonzini         /* validate the input */
1936912bb0bSLi Qiang         if (bst_val < 0 || bst_val > 0xffff) {
1946912bb0bSLi Qiang             error_report("splash-time is invalid,"
1956912bb0bSLi Qiang                          "it should be a value between 0 and 65535");
1966912bb0bSLi Qiang             exit(1);
19749ab747fSPaolo Bonzini         }
19849ab747fSPaolo Bonzini         /* use little endian format */
1993b3df3e5SLaszlo Ersek         bst_le16 = cpu_to_le16(bst_val);
2003b3df3e5SLaszlo Ersek         fw_cfg_add_file(s, "etc/boot-menu-wait",
2013b3df3e5SLaszlo Ersek                         g_memdup(&bst_le16, sizeof bst_le16), sizeof bst_le16);
20249ab747fSPaolo Bonzini     }
20349ab747fSPaolo Bonzini 
20449ab747fSPaolo Bonzini     /* insert splash file if user configurated */
205fe8ac1faSMarkus Armbruster     if (current_machine->boot_config.splash) {
20697ec4d21SPaolo Bonzini         const char *boot_splash_filename = current_machine->boot_config.splash;
20749ab747fSPaolo Bonzini         filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename);
20849ab747fSPaolo Bonzini         if (filename == NULL) {
2096912bb0bSLi Qiang             error_report("failed to find file '%s'", boot_splash_filename);
21049ab747fSPaolo Bonzini             return;
21149ab747fSPaolo Bonzini         }
21249ab747fSPaolo Bonzini 
21349ab747fSPaolo Bonzini         /* loading file data */
21449ab747fSPaolo Bonzini         file_data = read_splashfile(filename, &file_size, &file_type);
21549ab747fSPaolo Bonzini         if (file_data == NULL) {
21649ab747fSPaolo Bonzini             g_free(filename);
21749ab747fSPaolo Bonzini             return;
21849ab747fSPaolo Bonzini         }
21949ab747fSPaolo Bonzini         g_free(boot_splash_filedata);
22049ab747fSPaolo Bonzini         boot_splash_filedata = (uint8_t *)file_data;
22149ab747fSPaolo Bonzini 
22249ab747fSPaolo Bonzini         /* insert data */
22349ab747fSPaolo Bonzini         if (file_type == JPG_FILE) {
22449ab747fSPaolo Bonzini             fw_cfg_add_file(s, "bootsplash.jpg",
22596f209b9SPhilippe Mathieu-Daudé                             boot_splash_filedata, file_size);
22649ab747fSPaolo Bonzini         } else {
22749ab747fSPaolo Bonzini             fw_cfg_add_file(s, "bootsplash.bmp",
22896f209b9SPhilippe Mathieu-Daudé                             boot_splash_filedata, file_size);
22949ab747fSPaolo Bonzini         }
23049ab747fSPaolo Bonzini         g_free(filename);
23149ab747fSPaolo Bonzini     }
23249ab747fSPaolo Bonzini }
23349ab747fSPaolo Bonzini 
fw_cfg_reboot(FWCfgState * s)23449ab747fSPaolo Bonzini static void fw_cfg_reboot(FWCfgState *s)
23549ab747fSPaolo Bonzini {
23620a19220SDr. David Alan Gilbert     uint64_t rt_val = -1;
23704da9735SLi Qiang     uint32_t rt_le32;
23849ab747fSPaolo Bonzini 
23997ec4d21SPaolo Bonzini     if (current_machine->boot_config.has_reboot_timeout) {
24097ec4d21SPaolo Bonzini         rt_val = current_machine->boot_config.reboot_timeout;
24120a19220SDr. David Alan Gilbert 
24249ab747fSPaolo Bonzini         /* validate the input */
24320a19220SDr. David Alan Gilbert         if (rt_val > 0xffff && rt_val != (uint64_t)-1) {
244ee5d0f89SLi Qiang             error_report("reboot timeout is invalid,"
24520a19220SDr. David Alan Gilbert                          "it should be a value between -1 and 65535");
246ee5d0f89SLi Qiang             exit(1);
24749ab747fSPaolo Bonzini         }
248ee5d0f89SLi Qiang     }
249ee5d0f89SLi Qiang 
25004da9735SLi Qiang     rt_le32 = cpu_to_le32(rt_val);
25104da9735SLi Qiang     fw_cfg_add_file(s, "etc/boot-fail-wait", g_memdup(&rt_le32, 4), 4);
25249ab747fSPaolo Bonzini }
25349ab747fSPaolo Bonzini 
fw_cfg_write(FWCfgState * s,uint8_t value)25449ab747fSPaolo Bonzini static void fw_cfg_write(FWCfgState *s, uint8_t value)
25549ab747fSPaolo Bonzini {
256023e3148SGabriel L. Somlo     /* nothing, write support removed in QEMU v2.4+ */
25749ab747fSPaolo Bonzini }
25849ab747fSPaolo Bonzini 
fw_cfg_file_slots(const FWCfgState * s)259e12f3a13SLaszlo Ersek static inline uint16_t fw_cfg_file_slots(const FWCfgState *s)
260e12f3a13SLaszlo Ersek {
261e12f3a13SLaszlo Ersek     return s->file_slots;
262e12f3a13SLaszlo Ersek }
263e12f3a13SLaszlo Ersek 
264e12f3a13SLaszlo Ersek /* Note: this function returns an exclusive limit. */
fw_cfg_max_entry(const FWCfgState * s)265e12f3a13SLaszlo Ersek static inline uint32_t fw_cfg_max_entry(const FWCfgState *s)
266e12f3a13SLaszlo Ersek {
267e12f3a13SLaszlo Ersek     return FW_CFG_FILE_FIRST + fw_cfg_file_slots(s);
268e12f3a13SLaszlo Ersek }
269e12f3a13SLaszlo Ersek 
fw_cfg_select(FWCfgState * s,uint16_t key)27049ab747fSPaolo Bonzini static int fw_cfg_select(FWCfgState *s, uint16_t key)
27149ab747fSPaolo Bonzini {
2723bef7e8aSGabriel L. Somlo     int arch, ret;
2733bef7e8aSGabriel L. Somlo     FWCfgEntry *e;
27449ab747fSPaolo Bonzini 
27549ab747fSPaolo Bonzini     s->cur_offset = 0;
276e12f3a13SLaszlo Ersek     if ((key & FW_CFG_ENTRY_MASK) >= fw_cfg_max_entry(s)) {
27749ab747fSPaolo Bonzini         s->cur_entry = FW_CFG_INVALID;
27849ab747fSPaolo Bonzini         ret = 0;
27949ab747fSPaolo Bonzini     } else {
28049ab747fSPaolo Bonzini         s->cur_entry = key;
28149ab747fSPaolo Bonzini         ret = 1;
2823bef7e8aSGabriel L. Somlo         /* entry successfully selected, now run callback if present */
2833bef7e8aSGabriel L. Somlo         arch = !!(key & FW_CFG_ARCH_LOCAL);
2843bef7e8aSGabriel L. Somlo         e = &s->entries[arch][key & FW_CFG_ENTRY_MASK];
2856f6f4aecSMarc-André Lureau         if (e->select_cb) {
2866f6f4aecSMarc-André Lureau             e->select_cb(e->callback_opaque);
2873bef7e8aSGabriel L. Somlo         }
28849ab747fSPaolo Bonzini     }
28949ab747fSPaolo Bonzini 
2901f80b0d6SPhilippe Mathieu-Daudé     trace_fw_cfg_select(s, key, trace_key_name(key), ret);
29149ab747fSPaolo Bonzini     return ret;
29249ab747fSPaolo Bonzini }
29349ab747fSPaolo Bonzini 
fw_cfg_data_read(void * opaque,hwaddr addr,unsigned size)29438bf2093SGabriel L. Somlo static uint64_t fw_cfg_data_read(void *opaque, hwaddr addr, unsigned size)
29538bf2093SGabriel L. Somlo {
29638bf2093SGabriel L. Somlo     FWCfgState *s = opaque;
29738bf2093SGabriel L. Somlo     int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
29838bf2093SGabriel L. Somlo     FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
29938bf2093SGabriel L. Somlo                     &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
30038bf2093SGabriel L. Somlo     uint64_t value = 0;
30138bf2093SGabriel L. Somlo 
30238bf2093SGabriel L. Somlo     assert(size > 0 && size <= sizeof(value));
30338bf2093SGabriel L. Somlo     if (s->cur_entry != FW_CFG_INVALID && e->data && s->cur_offset < e->len) {
30438bf2093SGabriel L. Somlo         /* The least significant 'size' bytes of the return value are
30538bf2093SGabriel L. Somlo          * expected to contain a string preserving portion of the item
30638bf2093SGabriel L. Somlo          * data, padded with zeros on the right in case we run out early.
30738bf2093SGabriel L. Somlo          * In technical terms, we're composing the host-endian representation
30838bf2093SGabriel L. Somlo          * of the big endian interpretation of the fw_cfg string.
30938bf2093SGabriel L. Somlo          */
31038bf2093SGabriel L. Somlo         do {
31138bf2093SGabriel L. Somlo             value = (value << 8) | e->data[s->cur_offset++];
31238bf2093SGabriel L. Somlo         } while (--size && s->cur_offset < e->len);
31338bf2093SGabriel L. Somlo         /* If size is still not zero, we *did* run out early, so continue
31438bf2093SGabriel L. Somlo          * left-shifting, to add the appropriate number of padding zeros
31538bf2093SGabriel L. Somlo          * on the right.
31638bf2093SGabriel L. Somlo          */
31738bf2093SGabriel L. Somlo         value <<= 8 * size;
31838bf2093SGabriel L. Somlo     }
31938bf2093SGabriel L. Somlo 
32038bf2093SGabriel L. Somlo     trace_fw_cfg_read(s, value);
32138bf2093SGabriel L. Somlo     return value;
32238bf2093SGabriel L. Somlo }
32338bf2093SGabriel L. Somlo 
fw_cfg_data_mem_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)32449ab747fSPaolo Bonzini static void fw_cfg_data_mem_write(void *opaque, hwaddr addr,
32549ab747fSPaolo Bonzini                                   uint64_t value, unsigned size)
32649ab747fSPaolo Bonzini {
327cfaadf0eSLaszlo Ersek     FWCfgState *s = opaque;
32836b62ae6SLaszlo Ersek     unsigned i = size;
329cfaadf0eSLaszlo Ersek 
33036b62ae6SLaszlo Ersek     do {
33136b62ae6SLaszlo Ersek         fw_cfg_write(s, value >> (8 * --i));
33236b62ae6SLaszlo Ersek     } while (i);
333cfaadf0eSLaszlo Ersek }
334cfaadf0eSLaszlo Ersek 
fw_cfg_dma_transfer(FWCfgState * s)335a4c0d1deSMarc Marí static void fw_cfg_dma_transfer(FWCfgState *s)
336a4c0d1deSMarc Marí {
337a4c0d1deSMarc Marí     dma_addr_t len;
338a4c0d1deSMarc Marí     FWCfgDmaAccess dma;
339a4c0d1deSMarc Marí     int arch;
340a4c0d1deSMarc Marí     FWCfgEntry *e;
341baf2d5bfSMichael S. Tsirkin     int read = 0, write = 0;
342a4c0d1deSMarc Marí     dma_addr_t dma_addr;
343a4c0d1deSMarc Marí 
344a4c0d1deSMarc Marí     /* Reset the address before the next access */
345a4c0d1deSMarc Marí     dma_addr = s->dma_addr;
346a4c0d1deSMarc Marí     s->dma_addr = 0;
347a4c0d1deSMarc Marí 
348ba06fe8aSPhilippe Mathieu-Daudé     if (dma_memory_read(s->dma_as, dma_addr,
349ba06fe8aSPhilippe Mathieu-Daudé                         &dma, sizeof(dma), MEMTXATTRS_UNSPECIFIED)) {
350a4c0d1deSMarc Marí         stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control),
3512280c27aSPhilippe Mathieu-Daudé                    FW_CFG_DMA_CTL_ERROR, MEMTXATTRS_UNSPECIFIED);
352a4c0d1deSMarc Marí         return;
353a4c0d1deSMarc Marí     }
354a4c0d1deSMarc Marí 
355a4c0d1deSMarc Marí     dma.address = be64_to_cpu(dma.address);
356a4c0d1deSMarc Marí     dma.length = be32_to_cpu(dma.length);
357a4c0d1deSMarc Marí     dma.control = be32_to_cpu(dma.control);
358a4c0d1deSMarc Marí 
359a4c0d1deSMarc Marí     if (dma.control & FW_CFG_DMA_CTL_SELECT) {
360a4c0d1deSMarc Marí         fw_cfg_select(s, dma.control >> 16);
361a4c0d1deSMarc Marí     }
362a4c0d1deSMarc Marí 
363a4c0d1deSMarc Marí     arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
36466f8fd9dSGabriel L. Somlo     e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
36566f8fd9dSGabriel L. Somlo         &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
366a4c0d1deSMarc Marí 
367a4c0d1deSMarc Marí     if (dma.control & FW_CFG_DMA_CTL_READ) {
368a4c0d1deSMarc Marí         read = 1;
369baf2d5bfSMichael S. Tsirkin         write = 0;
370baf2d5bfSMichael S. Tsirkin     } else if (dma.control & FW_CFG_DMA_CTL_WRITE) {
371baf2d5bfSMichael S. Tsirkin         read = 0;
372baf2d5bfSMichael S. Tsirkin         write = 1;
373a4c0d1deSMarc Marí     } else if (dma.control & FW_CFG_DMA_CTL_SKIP) {
374a4c0d1deSMarc Marí         read = 0;
375baf2d5bfSMichael S. Tsirkin         write = 0;
376a4c0d1deSMarc Marí     } else {
377a4c0d1deSMarc Marí         dma.length = 0;
378a4c0d1deSMarc Marí     }
379a4c0d1deSMarc Marí 
380a4c0d1deSMarc Marí     dma.control = 0;
381a4c0d1deSMarc Marí 
382a4c0d1deSMarc Marí     while (dma.length > 0 && !(dma.control & FW_CFG_DMA_CTL_ERROR)) {
383a4c0d1deSMarc Marí         if (s->cur_entry == FW_CFG_INVALID || !e->data ||
384a4c0d1deSMarc Marí                                 s->cur_offset >= e->len) {
385a4c0d1deSMarc Marí             len = dma.length;
386a4c0d1deSMarc Marí 
387a4c0d1deSMarc Marí             /* If the access is not a read access, it will be a skip access,
388a4c0d1deSMarc Marí              * tested before.
389a4c0d1deSMarc Marí              */
390a4c0d1deSMarc Marí             if (read) {
3917a36e42dSPhilippe Mathieu-Daudé                 if (dma_memory_set(s->dma_as, dma.address, 0, len,
3927a36e42dSPhilippe Mathieu-Daudé                                    MEMTXATTRS_UNSPECIFIED)) {
393a4c0d1deSMarc Marí                     dma.control |= FW_CFG_DMA_CTL_ERROR;
394a4c0d1deSMarc Marí                 }
395a4c0d1deSMarc Marí             }
396baf2d5bfSMichael S. Tsirkin             if (write) {
397baf2d5bfSMichael S. Tsirkin                 dma.control |= FW_CFG_DMA_CTL_ERROR;
398baf2d5bfSMichael S. Tsirkin             }
399a4c0d1deSMarc Marí         } else {
400a4c0d1deSMarc Marí             if (dma.length <= (e->len - s->cur_offset)) {
401a4c0d1deSMarc Marí                 len = dma.length;
402a4c0d1deSMarc Marí             } else {
403a4c0d1deSMarc Marí                 len = (e->len - s->cur_offset);
404a4c0d1deSMarc Marí             }
405a4c0d1deSMarc Marí 
406a4c0d1deSMarc Marí             /* If the access is not a read access, it will be a skip access,
407a4c0d1deSMarc Marí              * tested before.
408a4c0d1deSMarc Marí              */
409a4c0d1deSMarc Marí             if (read) {
410a4c0d1deSMarc Marí                 if (dma_memory_write(s->dma_as, dma.address,
411ba06fe8aSPhilippe Mathieu-Daudé                                      &e->data[s->cur_offset], len,
412ba06fe8aSPhilippe Mathieu-Daudé                                      MEMTXATTRS_UNSPECIFIED)) {
413a4c0d1deSMarc Marí                     dma.control |= FW_CFG_DMA_CTL_ERROR;
414a4c0d1deSMarc Marí                 }
415a4c0d1deSMarc Marí             }
416baf2d5bfSMichael S. Tsirkin             if (write) {
417baf2d5bfSMichael S. Tsirkin                 if (!e->allow_write ||
418baf2d5bfSMichael S. Tsirkin                     len != dma.length ||
419baf2d5bfSMichael S. Tsirkin                     dma_memory_read(s->dma_as, dma.address,
420ba06fe8aSPhilippe Mathieu-Daudé                                     &e->data[s->cur_offset], len,
421ba06fe8aSPhilippe Mathieu-Daudé                                     MEMTXATTRS_UNSPECIFIED)) {
422baf2d5bfSMichael S. Tsirkin                     dma.control |= FW_CFG_DMA_CTL_ERROR;
4235f9252f7SMarc-André Lureau                 } else if (e->write_cb) {
4245f9252f7SMarc-André Lureau                     e->write_cb(e->callback_opaque, s->cur_offset, len);
425baf2d5bfSMichael S. Tsirkin                 }
426baf2d5bfSMichael S. Tsirkin             }
427a4c0d1deSMarc Marí 
428a4c0d1deSMarc Marí             s->cur_offset += len;
429a4c0d1deSMarc Marí         }
430a4c0d1deSMarc Marí 
431a4c0d1deSMarc Marí         dma.address += len;
432a4c0d1deSMarc Marí         dma.length  -= len;
433a4c0d1deSMarc Marí 
434a4c0d1deSMarc Marí     }
435a4c0d1deSMarc Marí 
436a4c0d1deSMarc Marí     stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control),
4372280c27aSPhilippe Mathieu-Daudé                 dma.control, MEMTXATTRS_UNSPECIFIED);
438a4c0d1deSMarc Marí 
439a4c0d1deSMarc Marí     trace_fw_cfg_read(s, 0);
440a4c0d1deSMarc Marí }
441a4c0d1deSMarc Marí 
fw_cfg_dma_mem_read(void * opaque,hwaddr addr,unsigned size)4422cc06a88SKevin O'Connor static uint64_t fw_cfg_dma_mem_read(void *opaque, hwaddr addr,
4432cc06a88SKevin O'Connor                                     unsigned size)
4442cc06a88SKevin O'Connor {
4452cc06a88SKevin O'Connor     /* Return a signature value (and handle various read sizes) */
4462cc06a88SKevin O'Connor     return extract64(FW_CFG_DMA_SIGNATURE, (8 - addr - size) * 8, size * 8);
4472cc06a88SKevin O'Connor }
4482cc06a88SKevin O'Connor 
fw_cfg_dma_mem_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)449a4c0d1deSMarc Marí static void fw_cfg_dma_mem_write(void *opaque, hwaddr addr,
450a4c0d1deSMarc Marí                                  uint64_t value, unsigned size)
451a4c0d1deSMarc Marí {
452a4c0d1deSMarc Marí     FWCfgState *s = opaque;
453a4c0d1deSMarc Marí 
454a4c0d1deSMarc Marí     if (size == 4) {
455a4c0d1deSMarc Marí         if (addr == 0) {
456a4c0d1deSMarc Marí             /* FWCfgDmaAccess high address */
457a4c0d1deSMarc Marí             s->dma_addr = value << 32;
458a4c0d1deSMarc Marí         } else if (addr == 4) {
459a4c0d1deSMarc Marí             /* FWCfgDmaAccess low address */
460a4c0d1deSMarc Marí             s->dma_addr |= value;
461a4c0d1deSMarc Marí             fw_cfg_dma_transfer(s);
462a4c0d1deSMarc Marí         }
463a4c0d1deSMarc Marí     } else if (size == 8 && addr == 0) {
464a4c0d1deSMarc Marí         s->dma_addr = value;
465a4c0d1deSMarc Marí         fw_cfg_dma_transfer(s);
466a4c0d1deSMarc Marí     }
467a4c0d1deSMarc Marí }
468a4c0d1deSMarc Marí 
fw_cfg_dma_mem_valid(void * opaque,hwaddr addr,unsigned size,bool is_write,MemTxAttrs attrs)469a4c0d1deSMarc Marí static bool fw_cfg_dma_mem_valid(void *opaque, hwaddr addr,
4708372d383SPeter Maydell                                  unsigned size, bool is_write,
4718372d383SPeter Maydell                                  MemTxAttrs attrs)
472a4c0d1deSMarc Marí {
4732cc06a88SKevin O'Connor     return !is_write || ((size == 4 && (addr == 0 || addr == 4)) ||
474a4c0d1deSMarc Marí                          (size == 8 && addr == 0));
475a4c0d1deSMarc Marí }
476a4c0d1deSMarc Marí 
fw_cfg_data_mem_valid(void * opaque,hwaddr addr,unsigned size,bool is_write,MemTxAttrs attrs)477cfaadf0eSLaszlo Ersek static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr,
4788372d383SPeter Maydell                                   unsigned size, bool is_write,
4798372d383SPeter Maydell                                   MemTxAttrs attrs)
480cfaadf0eSLaszlo Ersek {
481cfaadf0eSLaszlo Ersek     return addr == 0;
48249ab747fSPaolo Bonzini }
48349ab747fSPaolo Bonzini 
fw_cfg_ctl_mem_read(void * opaque,hwaddr addr,unsigned size)4842247936aSLi Qiang static uint64_t fw_cfg_ctl_mem_read(void *opaque, hwaddr addr, unsigned size)
4852247936aSLi Qiang {
4862247936aSLi Qiang     return 0;
4872247936aSLi Qiang }
4882247936aSLi Qiang 
fw_cfg_ctl_mem_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)48949ab747fSPaolo Bonzini static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr,
49049ab747fSPaolo Bonzini                                  uint64_t value, unsigned size)
49149ab747fSPaolo Bonzini {
49249ab747fSPaolo Bonzini     fw_cfg_select(opaque, (uint16_t)value);
49349ab747fSPaolo Bonzini }
49449ab747fSPaolo Bonzini 
fw_cfg_ctl_mem_valid(void * opaque,hwaddr addr,unsigned size,bool is_write,MemTxAttrs attrs)49549ab747fSPaolo Bonzini static bool fw_cfg_ctl_mem_valid(void *opaque, hwaddr addr,
4968372d383SPeter Maydell                                  unsigned size, bool is_write,
4978372d383SPeter Maydell                                  MemTxAttrs attrs)
49849ab747fSPaolo Bonzini {
49949ab747fSPaolo Bonzini     return is_write && size == 2;
50049ab747fSPaolo Bonzini }
50149ab747fSPaolo Bonzini 
fw_cfg_comb_write(void * opaque,hwaddr addr,uint64_t value,unsigned size)50249ab747fSPaolo Bonzini static void fw_cfg_comb_write(void *opaque, hwaddr addr,
50349ab747fSPaolo Bonzini                               uint64_t value, unsigned size)
50449ab747fSPaolo Bonzini {
50549ab747fSPaolo Bonzini     switch (size) {
50649ab747fSPaolo Bonzini     case 1:
50749ab747fSPaolo Bonzini         fw_cfg_write(opaque, (uint8_t)value);
50849ab747fSPaolo Bonzini         break;
50949ab747fSPaolo Bonzini     case 2:
51049ab747fSPaolo Bonzini         fw_cfg_select(opaque, (uint16_t)value);
51149ab747fSPaolo Bonzini         break;
51249ab747fSPaolo Bonzini     }
51349ab747fSPaolo Bonzini }
51449ab747fSPaolo Bonzini 
fw_cfg_comb_valid(void * opaque,hwaddr addr,unsigned size,bool is_write,MemTxAttrs attrs)51549ab747fSPaolo Bonzini static bool fw_cfg_comb_valid(void *opaque, hwaddr addr,
5168372d383SPeter Maydell                               unsigned size, bool is_write,
5178372d383SPeter Maydell                               MemTxAttrs attrs)
51849ab747fSPaolo Bonzini {
51949ab747fSPaolo Bonzini     return (size == 1) || (is_write && size == 2);
52049ab747fSPaolo Bonzini }
52149ab747fSPaolo Bonzini 
52249ab747fSPaolo Bonzini static const MemoryRegionOps fw_cfg_ctl_mem_ops = {
5232247936aSLi Qiang     .read = fw_cfg_ctl_mem_read,
52449ab747fSPaolo Bonzini     .write = fw_cfg_ctl_mem_write,
525d789c845SLaszlo Ersek     .endianness = DEVICE_BIG_ENDIAN,
52649ab747fSPaolo Bonzini     .valid.accepts = fw_cfg_ctl_mem_valid,
52749ab747fSPaolo Bonzini };
52849ab747fSPaolo Bonzini 
52949ab747fSPaolo Bonzini static const MemoryRegionOps fw_cfg_data_mem_ops = {
53038bf2093SGabriel L. Somlo     .read = fw_cfg_data_read,
53149ab747fSPaolo Bonzini     .write = fw_cfg_data_mem_write,
532d789c845SLaszlo Ersek     .endianness = DEVICE_BIG_ENDIAN,
53349ab747fSPaolo Bonzini     .valid = {
53449ab747fSPaolo Bonzini         .min_access_size = 1,
53549ab747fSPaolo Bonzini         .max_access_size = 1,
536cfaadf0eSLaszlo Ersek         .accepts = fw_cfg_data_mem_valid,
53749ab747fSPaolo Bonzini     },
53849ab747fSPaolo Bonzini };
53949ab747fSPaolo Bonzini 
54049ab747fSPaolo Bonzini static const MemoryRegionOps fw_cfg_comb_mem_ops = {
5416c8d56a2SGabriel L. Somlo     .read = fw_cfg_data_read,
54249ab747fSPaolo Bonzini     .write = fw_cfg_comb_write,
5436fdf98f2SPaolo Bonzini     .endianness = DEVICE_LITTLE_ENDIAN,
54449ab747fSPaolo Bonzini     .valid.accepts = fw_cfg_comb_valid,
54549ab747fSPaolo Bonzini };
54649ab747fSPaolo Bonzini 
547a4c0d1deSMarc Marí static const MemoryRegionOps fw_cfg_dma_mem_ops = {
5482cc06a88SKevin O'Connor     .read = fw_cfg_dma_mem_read,
549a4c0d1deSMarc Marí     .write = fw_cfg_dma_mem_write,
550a4c0d1deSMarc Marí     .endianness = DEVICE_BIG_ENDIAN,
551a4c0d1deSMarc Marí     .valid.accepts = fw_cfg_dma_mem_valid,
552a4c0d1deSMarc Marí     .valid.max_access_size = 8,
553a4c0d1deSMarc Marí     .impl.max_access_size = 8,
554a4c0d1deSMarc Marí };
555a4c0d1deSMarc Marí 
fw_cfg_reset(DeviceState * d)55649ab747fSPaolo Bonzini static void fw_cfg_reset(DeviceState *d)
55749ab747fSPaolo Bonzini {
5582ce92a11SHu Tao     FWCfgState *s = FW_CFG(d);
55949ab747fSPaolo Bonzini 
5603bef7e8aSGabriel L. Somlo     /* we never register a read callback for FW_CFG_SIGNATURE */
5613bef7e8aSGabriel L. Somlo     fw_cfg_select(s, FW_CFG_SIGNATURE);
56249ab747fSPaolo Bonzini }
56349ab747fSPaolo Bonzini 
56449ab747fSPaolo Bonzini /* Save restore 32 bit int as uint16_t
56549ab747fSPaolo Bonzini    This is a Big hack, but it is how the old state did it.
56649ab747fSPaolo Bonzini    Or we broke compatibility in the state, or we can't use struct tm
56749ab747fSPaolo Bonzini  */
56849ab747fSPaolo Bonzini 
get_uint32_as_uint16(QEMUFile * f,void * pv,size_t size,const VMStateField * field)5692c21ee76SJianjun Duan static int get_uint32_as_uint16(QEMUFile *f, void *pv, size_t size,
57003fee66fSMarc-André Lureau                                 const VMStateField *field)
57149ab747fSPaolo Bonzini {
57249ab747fSPaolo Bonzini     uint32_t *v = pv;
57349ab747fSPaolo Bonzini     *v = qemu_get_be16(f);
57449ab747fSPaolo Bonzini     return 0;
57549ab747fSPaolo Bonzini }
57649ab747fSPaolo Bonzini 
put_unused(QEMUFile * f,void * pv,size_t size,const VMStateField * field,JSONWriter * vmdesc)57703fee66fSMarc-André Lureau static int put_unused(QEMUFile *f, void *pv, size_t size,
5783ddba9a9SMarkus Armbruster                       const VMStateField *field, JSONWriter *vmdesc)
57949ab747fSPaolo Bonzini {
58049ab747fSPaolo Bonzini     fprintf(stderr, "uint32_as_uint16 is only used for backward compatibility.\n");
58149ab747fSPaolo Bonzini     fprintf(stderr, "This functions shouldn't be called.\n");
5822c21ee76SJianjun Duan 
5832c21ee76SJianjun Duan     return 0;
58449ab747fSPaolo Bonzini }
58549ab747fSPaolo Bonzini 
58649ab747fSPaolo Bonzini static const VMStateInfo vmstate_hack_uint32_as_uint16 = {
58749ab747fSPaolo Bonzini     .name = "int32_as_uint16",
58849ab747fSPaolo Bonzini     .get  = get_uint32_as_uint16,
58949ab747fSPaolo Bonzini     .put  = put_unused,
59049ab747fSPaolo Bonzini };
59149ab747fSPaolo Bonzini 
59249ab747fSPaolo Bonzini #define VMSTATE_UINT16_HACK(_f, _s, _t)                                    \
59349ab747fSPaolo Bonzini     VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_hack_uint32_as_uint16, uint32_t)
59449ab747fSPaolo Bonzini 
59549ab747fSPaolo Bonzini 
is_version_1(void * opaque,int version_id)59649ab747fSPaolo Bonzini static bool is_version_1(void *opaque, int version_id)
59749ab747fSPaolo Bonzini {
59849ab747fSPaolo Bonzini     return version_id == 1;
59949ab747fSPaolo Bonzini }
60049ab747fSPaolo Bonzini 
fw_cfg_dma_enabled(void * opaque)601b2a575a1SMarc Marí bool fw_cfg_dma_enabled(void *opaque)
602a4c0d1deSMarc Marí {
603a4c0d1deSMarc Marí     FWCfgState *s = opaque;
604a4c0d1deSMarc Marí 
605a4c0d1deSMarc Marí     return s->dma_enabled;
606a4c0d1deSMarc Marí }
607a4c0d1deSMarc Marí 
fw_cfg_acpi_mr_restore(void * opaque)608394f0f72SShameer Kolothum static bool fw_cfg_acpi_mr_restore(void *opaque)
609394f0f72SShameer Kolothum {
610394f0f72SShameer Kolothum     FWCfgState *s = opaque;
611394f0f72SShameer Kolothum     bool mr_aligned;
612394f0f72SShameer Kolothum 
6138e3b0cbbSMarc-André Lureau     mr_aligned = QEMU_IS_ALIGNED(s->table_mr_size, qemu_real_host_page_size()) &&
6148e3b0cbbSMarc-André Lureau                  QEMU_IS_ALIGNED(s->linker_mr_size, qemu_real_host_page_size()) &&
6158e3b0cbbSMarc-André Lureau                  QEMU_IS_ALIGNED(s->rsdp_mr_size, qemu_real_host_page_size());
616394f0f72SShameer Kolothum     return s->acpi_mr_restore && !mr_aligned;
617394f0f72SShameer Kolothum }
618394f0f72SShameer Kolothum 
fw_cfg_update_mr(FWCfgState * s,uint16_t key,size_t size)619394f0f72SShameer Kolothum static void fw_cfg_update_mr(FWCfgState *s, uint16_t key, size_t size)
620394f0f72SShameer Kolothum {
621394f0f72SShameer Kolothum     MemoryRegion *mr;
622394f0f72SShameer Kolothum     ram_addr_t offset;
623394f0f72SShameer Kolothum     int arch = !!(key & FW_CFG_ARCH_LOCAL);
624394f0f72SShameer Kolothum     void *ptr;
625394f0f72SShameer Kolothum 
626394f0f72SShameer Kolothum     key &= FW_CFG_ENTRY_MASK;
627394f0f72SShameer Kolothum     assert(key < fw_cfg_max_entry(s));
628394f0f72SShameer Kolothum 
629394f0f72SShameer Kolothum     ptr = s->entries[arch][key].data;
630394f0f72SShameer Kolothum     mr = memory_region_from_host(ptr, &offset);
631394f0f72SShameer Kolothum 
632394f0f72SShameer Kolothum     memory_region_ram_resize(mr, size, &error_abort);
633394f0f72SShameer Kolothum }
634394f0f72SShameer Kolothum 
fw_cfg_acpi_mr_restore_post_load(void * opaque,int version_id)635394f0f72SShameer Kolothum static int fw_cfg_acpi_mr_restore_post_load(void *opaque, int version_id)
636394f0f72SShameer Kolothum {
637394f0f72SShameer Kolothum     FWCfgState *s = opaque;
638394f0f72SShameer Kolothum     int i, index;
639394f0f72SShameer Kolothum 
640394f0f72SShameer Kolothum     assert(s->files);
641394f0f72SShameer Kolothum 
642394f0f72SShameer Kolothum     index = be32_to_cpu(s->files->count);
643394f0f72SShameer Kolothum 
644394f0f72SShameer Kolothum     for (i = 0; i < index; i++) {
645394f0f72SShameer Kolothum         if (!strcmp(s->files->f[i].name, ACPI_BUILD_TABLE_FILE)) {
646394f0f72SShameer Kolothum             fw_cfg_update_mr(s, FW_CFG_FILE_FIRST + i, s->table_mr_size);
647394f0f72SShameer Kolothum         } else if (!strcmp(s->files->f[i].name, ACPI_BUILD_LOADER_FILE)) {
648394f0f72SShameer Kolothum             fw_cfg_update_mr(s, FW_CFG_FILE_FIRST + i, s->linker_mr_size);
649394f0f72SShameer Kolothum         } else if (!strcmp(s->files->f[i].name, ACPI_BUILD_RSDP_FILE)) {
650394f0f72SShameer Kolothum             fw_cfg_update_mr(s, FW_CFG_FILE_FIRST + i, s->rsdp_mr_size);
651394f0f72SShameer Kolothum         }
652394f0f72SShameer Kolothum     }
653394f0f72SShameer Kolothum 
654394f0f72SShameer Kolothum     return 0;
655394f0f72SShameer Kolothum }
656394f0f72SShameer Kolothum 
657a4c0d1deSMarc Marí static const VMStateDescription vmstate_fw_cfg_dma = {
658a4c0d1deSMarc Marí     .name = "fw_cfg/dma",
659a4c0d1deSMarc Marí     .needed = fw_cfg_dma_enabled,
66018d10e61SRichard Henderson     .fields = (const VMStateField[]) {
661a4c0d1deSMarc Marí         VMSTATE_UINT64(dma_addr, FWCfgState),
662a4c0d1deSMarc Marí         VMSTATE_END_OF_LIST()
663a4c0d1deSMarc Marí     },
664a4c0d1deSMarc Marí };
665a4c0d1deSMarc Marí 
666394f0f72SShameer Kolothum static const VMStateDescription vmstate_fw_cfg_acpi_mr = {
667394f0f72SShameer Kolothum     .name = "fw_cfg/acpi_mr",
668394f0f72SShameer Kolothum     .version_id = 1,
669394f0f72SShameer Kolothum     .minimum_version_id = 1,
670394f0f72SShameer Kolothum     .needed = fw_cfg_acpi_mr_restore,
671394f0f72SShameer Kolothum     .post_load = fw_cfg_acpi_mr_restore_post_load,
67218d10e61SRichard Henderson     .fields = (const VMStateField[]) {
673394f0f72SShameer Kolothum         VMSTATE_UINT64(table_mr_size, FWCfgState),
674394f0f72SShameer Kolothum         VMSTATE_UINT64(linker_mr_size, FWCfgState),
675394f0f72SShameer Kolothum         VMSTATE_UINT64(rsdp_mr_size, FWCfgState),
676394f0f72SShameer Kolothum         VMSTATE_END_OF_LIST()
677394f0f72SShameer Kolothum     },
678394f0f72SShameer Kolothum };
679394f0f72SShameer Kolothum 
68049ab747fSPaolo Bonzini static const VMStateDescription vmstate_fw_cfg = {
68149ab747fSPaolo Bonzini     .name = "fw_cfg",
68249ab747fSPaolo Bonzini     .version_id = 2,
68349ab747fSPaolo Bonzini     .minimum_version_id = 1,
68418d10e61SRichard Henderson     .fields = (const VMStateField[]) {
68549ab747fSPaolo Bonzini         VMSTATE_UINT16(cur_entry, FWCfgState),
68649ab747fSPaolo Bonzini         VMSTATE_UINT16_HACK(cur_offset, FWCfgState, is_version_1),
68749ab747fSPaolo Bonzini         VMSTATE_UINT32_V(cur_offset, FWCfgState, 2),
68849ab747fSPaolo Bonzini         VMSTATE_END_OF_LIST()
689a4c0d1deSMarc Marí     },
69018d10e61SRichard Henderson     .subsections = (const VMStateDescription * const []) {
691a4c0d1deSMarc Marí         &vmstate_fw_cfg_dma,
692394f0f72SShameer Kolothum         &vmstate_fw_cfg_acpi_mr,
693a4c0d1deSMarc Marí         NULL,
69449ab747fSPaolo Bonzini     }
69549ab747fSPaolo Bonzini };
69649ab747fSPaolo Bonzini 
fw_cfg_add_bytes_callback(FWCfgState * s,uint16_t key,FWCfgCallback select_cb,FWCfgWriteCallback write_cb,void * callback_opaque,void * data,size_t len,bool read_only)697ae80d81cSMichael S. Tsirkin static void fw_cfg_add_bytes_callback(FWCfgState *s, uint16_t key,
6986f6f4aecSMarc-André Lureau                                       FWCfgCallback select_cb,
6995f9252f7SMarc-André Lureau                                       FWCfgWriteCallback write_cb,
700d87072ceSMichael S. Tsirkin                                       void *callback_opaque,
701baf2d5bfSMichael S. Tsirkin                                       void *data, size_t len,
702baf2d5bfSMichael S. Tsirkin                                       bool read_only)
70349ab747fSPaolo Bonzini {
70449ab747fSPaolo Bonzini     int arch = !!(key & FW_CFG_ARCH_LOCAL);
70549ab747fSPaolo Bonzini 
70649ab747fSPaolo Bonzini     key &= FW_CFG_ENTRY_MASK;
70749ab747fSPaolo Bonzini 
708e12f3a13SLaszlo Ersek     assert(key < fw_cfg_max_entry(s) && len < UINT32_MAX);
7090f9b2141SGabriel L. Somlo     assert(s->entries[arch][key].data == NULL); /* avoid key conflict */
71049ab747fSPaolo Bonzini 
71149ab747fSPaolo Bonzini     s->entries[arch][key].data = data;
71249ab747fSPaolo Bonzini     s->entries[arch][key].len = (uint32_t)len;
7136f6f4aecSMarc-André Lureau     s->entries[arch][key].select_cb = select_cb;
7145f9252f7SMarc-André Lureau     s->entries[arch][key].write_cb = write_cb;
715d87072ceSMichael S. Tsirkin     s->entries[arch][key].callback_opaque = callback_opaque;
716baf2d5bfSMichael S. Tsirkin     s->entries[arch][key].allow_write = !read_only;
717d87072ceSMichael S. Tsirkin }
718d87072ceSMichael S. Tsirkin 
fw_cfg_modify_bytes_read(FWCfgState * s,uint16_t key,void * data,size_t len)719bdbb5b17SGonglei static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key,
720bdbb5b17SGonglei                                               void *data, size_t len)
721bdbb5b17SGonglei {
722bdbb5b17SGonglei     void *ptr;
723bdbb5b17SGonglei     int arch = !!(key & FW_CFG_ARCH_LOCAL);
724bdbb5b17SGonglei 
725bdbb5b17SGonglei     key &= FW_CFG_ENTRY_MASK;
726bdbb5b17SGonglei 
727e12f3a13SLaszlo Ersek     assert(key < fw_cfg_max_entry(s) && len < UINT32_MAX);
728bdbb5b17SGonglei 
729bdbb5b17SGonglei     /* return the old data to the function caller, avoid memory leak */
730bdbb5b17SGonglei     ptr = s->entries[arch][key].data;
731bdbb5b17SGonglei     s->entries[arch][key].data = data;
732bdbb5b17SGonglei     s->entries[arch][key].len = len;
733bdbb5b17SGonglei     s->entries[arch][key].callback_opaque = NULL;
734baf2d5bfSMichael S. Tsirkin     s->entries[arch][key].allow_write = false;
735bdbb5b17SGonglei 
736bdbb5b17SGonglei     return ptr;
737bdbb5b17SGonglei }
738bdbb5b17SGonglei 
fw_cfg_add_bytes(FWCfgState * s,uint16_t key,void * data,size_t len)739d87072ceSMichael S. Tsirkin void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len)
740d87072ceSMichael S. Tsirkin {
7411f80b0d6SPhilippe Mathieu-Daudé     trace_fw_cfg_add_bytes(key, trace_key_name(key), len);
7425f9252f7SMarc-André Lureau     fw_cfg_add_bytes_callback(s, key, NULL, NULL, NULL, data, len, true);
74349ab747fSPaolo Bonzini }
74449ab747fSPaolo Bonzini 
fw_cfg_add_string(FWCfgState * s,uint16_t key,const char * value)74549ab747fSPaolo Bonzini void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value)
74649ab747fSPaolo Bonzini {
74749ab747fSPaolo Bonzini     size_t sz = strlen(value) + 1;
74849ab747fSPaolo Bonzini 
7491f80b0d6SPhilippe Mathieu-Daudé     trace_fw_cfg_add_string(key, trace_key_name(key), value);
750e7ae771fSStefan Weil     fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz);
75149ab747fSPaolo Bonzini }
75249ab747fSPaolo Bonzini 
fw_cfg_modify_string(FWCfgState * s,uint16_t key,const char * value)753e5f6aa31SSergio Lopez void fw_cfg_modify_string(FWCfgState *s, uint16_t key, const char *value)
754e5f6aa31SSergio Lopez {
755e5f6aa31SSergio Lopez     size_t sz = strlen(value) + 1;
756e5f6aa31SSergio Lopez     char *old;
757e5f6aa31SSergio Lopez 
758e5f6aa31SSergio Lopez     old = fw_cfg_modify_bytes_read(s, key, g_memdup(value, sz), sz);
759e5f6aa31SSergio Lopez     g_free(old);
760e5f6aa31SSergio Lopez }
761e5f6aa31SSergio Lopez 
fw_cfg_add_i16(FWCfgState * s,uint16_t key,uint16_t value)76249ab747fSPaolo Bonzini void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
76349ab747fSPaolo Bonzini {
76449ab747fSPaolo Bonzini     uint16_t *copy;
76549ab747fSPaolo Bonzini 
76649ab747fSPaolo Bonzini     copy = g_malloc(sizeof(value));
76749ab747fSPaolo Bonzini     *copy = cpu_to_le16(value);
7681f80b0d6SPhilippe Mathieu-Daudé     trace_fw_cfg_add_i16(key, trace_key_name(key), value);
76949ab747fSPaolo Bonzini     fw_cfg_add_bytes(s, key, copy, sizeof(value));
77049ab747fSPaolo Bonzini }
77149ab747fSPaolo Bonzini 
fw_cfg_modify_i16(FWCfgState * s,uint16_t key,uint16_t value)7721edd34b6SGabriel L. Somlo void fw_cfg_modify_i16(FWCfgState *s, uint16_t key, uint16_t value)
7731edd34b6SGabriel L. Somlo {
7741edd34b6SGabriel L. Somlo     uint16_t *copy, *old;
7751edd34b6SGabriel L. Somlo 
7761edd34b6SGabriel L. Somlo     copy = g_malloc(sizeof(value));
7771edd34b6SGabriel L. Somlo     *copy = cpu_to_le16(value);
7781edd34b6SGabriel L. Somlo     old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value));
7791edd34b6SGabriel L. Somlo     g_free(old);
7801edd34b6SGabriel L. Somlo }
7811edd34b6SGabriel L. Somlo 
fw_cfg_add_i32(FWCfgState * s,uint16_t key,uint32_t value)78249ab747fSPaolo Bonzini void fw_cfg_add_i32(FWCfgState *s, uint16_t key, uint32_t value)
78349ab747fSPaolo Bonzini {
78449ab747fSPaolo Bonzini     uint32_t *copy;
78549ab747fSPaolo Bonzini 
78649ab747fSPaolo Bonzini     copy = g_malloc(sizeof(value));
78749ab747fSPaolo Bonzini     *copy = cpu_to_le32(value);
7881f80b0d6SPhilippe Mathieu-Daudé     trace_fw_cfg_add_i32(key, trace_key_name(key), value);
78949ab747fSPaolo Bonzini     fw_cfg_add_bytes(s, key, copy, sizeof(value));
79049ab747fSPaolo Bonzini }
79149ab747fSPaolo Bonzini 
fw_cfg_modify_i32(FWCfgState * s,uint16_t key,uint32_t value)792e5f6aa31SSergio Lopez void fw_cfg_modify_i32(FWCfgState *s, uint16_t key, uint32_t value)
793e5f6aa31SSergio Lopez {
794e5f6aa31SSergio Lopez     uint32_t *copy, *old;
795e5f6aa31SSergio Lopez 
796e5f6aa31SSergio Lopez     copy = g_malloc(sizeof(value));
797e5f6aa31SSergio Lopez     *copy = cpu_to_le32(value);
798e5f6aa31SSergio Lopez     old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value));
799e5f6aa31SSergio Lopez     g_free(old);
800e5f6aa31SSergio Lopez }
801e5f6aa31SSergio Lopez 
fw_cfg_add_i64(FWCfgState * s,uint16_t key,uint64_t value)80249ab747fSPaolo Bonzini void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
80349ab747fSPaolo Bonzini {
80449ab747fSPaolo Bonzini     uint64_t *copy;
80549ab747fSPaolo Bonzini 
80649ab747fSPaolo Bonzini     copy = g_malloc(sizeof(value));
80749ab747fSPaolo Bonzini     *copy = cpu_to_le64(value);
8081f80b0d6SPhilippe Mathieu-Daudé     trace_fw_cfg_add_i64(key, trace_key_name(key), value);
80949ab747fSPaolo Bonzini     fw_cfg_add_bytes(s, key, copy, sizeof(value));
81049ab747fSPaolo Bonzini }
81149ab747fSPaolo Bonzini 
fw_cfg_modify_i64(FWCfgState * s,uint16_t key,uint64_t value)812e5f6aa31SSergio Lopez void fw_cfg_modify_i64(FWCfgState *s, uint16_t key, uint64_t value)
813e5f6aa31SSergio Lopez {
814e5f6aa31SSergio Lopez     uint64_t *copy, *old;
815e5f6aa31SSergio Lopez 
816e5f6aa31SSergio Lopez     copy = g_malloc(sizeof(value));
817e5f6aa31SSergio Lopez     *copy = cpu_to_le64(value);
818e5f6aa31SSergio Lopez     old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value));
819e5f6aa31SSergio Lopez     g_free(old);
820e5f6aa31SSergio Lopez }
821e5f6aa31SSergio Lopez 
fw_cfg_set_order_override(FWCfgState * s,int order)822bab47d9aSGerd Hoffmann void fw_cfg_set_order_override(FWCfgState *s, int order)
823bab47d9aSGerd Hoffmann {
824bab47d9aSGerd Hoffmann     assert(s->fw_cfg_order_override == 0);
825bab47d9aSGerd Hoffmann     s->fw_cfg_order_override = order;
826bab47d9aSGerd Hoffmann }
827bab47d9aSGerd Hoffmann 
fw_cfg_reset_order_override(FWCfgState * s)828bab47d9aSGerd Hoffmann void fw_cfg_reset_order_override(FWCfgState *s)
829bab47d9aSGerd Hoffmann {
830bab47d9aSGerd Hoffmann     assert(s->fw_cfg_order_override != 0);
831bab47d9aSGerd Hoffmann     s->fw_cfg_order_override = 0;
832bab47d9aSGerd Hoffmann }
833bab47d9aSGerd Hoffmann 
834bab47d9aSGerd Hoffmann /*
835bab47d9aSGerd Hoffmann  * This is the legacy order list.  For legacy systems, files are in
836bab47d9aSGerd Hoffmann  * the fw_cfg in the order defined below, by the "order" value.  Note
837bab47d9aSGerd Hoffmann  * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a
838bab47d9aSGerd Hoffmann  * specific area, but there may be more than one and they occur in the
839bab47d9aSGerd Hoffmann  * order that the user specifies them on the command line.  Those are
840bab47d9aSGerd Hoffmann  * handled in a special manner, using the order override above.
841bab47d9aSGerd Hoffmann  *
842bab47d9aSGerd Hoffmann  * For non-legacy, the files are sorted by filename to avoid this kind
843bab47d9aSGerd Hoffmann  * of complexity in the future.
844bab47d9aSGerd Hoffmann  *
845bab47d9aSGerd Hoffmann  * This is only for x86, other arches don't implement versioning so
846bab47d9aSGerd Hoffmann  * they won't set legacy mode.
847bab47d9aSGerd Hoffmann  */
848bab47d9aSGerd Hoffmann static struct {
849bab47d9aSGerd Hoffmann     const char *name;
850bab47d9aSGerd Hoffmann     int order;
851bab47d9aSGerd Hoffmann } fw_cfg_order[] = {
852bab47d9aSGerd Hoffmann     { "etc/boot-menu-wait", 10 },
853bab47d9aSGerd Hoffmann     { "bootsplash.jpg", 11 },
854bab47d9aSGerd Hoffmann     { "bootsplash.bmp", 12 },
855bab47d9aSGerd Hoffmann     { "etc/boot-fail-wait", 15 },
856bab47d9aSGerd Hoffmann     { "etc/smbios/smbios-tables", 20 },
857bab47d9aSGerd Hoffmann     { "etc/smbios/smbios-anchor", 30 },
858bab47d9aSGerd Hoffmann     { "etc/e820", 40 },
859bab47d9aSGerd Hoffmann     { "etc/reserved-memory-end", 50 },
860bab47d9aSGerd Hoffmann     { "genroms/kvmvapic.bin", 55 },
861bab47d9aSGerd Hoffmann     { "genroms/linuxboot.bin", 60 },
862bab47d9aSGerd Hoffmann     { }, /* VGA ROMs from pc_vga_init come here, 70. */
863bab47d9aSGerd Hoffmann     { }, /* NIC option ROMs from pc_nic_init come here, 80. */
864bab47d9aSGerd Hoffmann     { "etc/system-states", 90 },
865bab47d9aSGerd Hoffmann     { }, /* User ROMs come here, 100. */
866bab47d9aSGerd Hoffmann     { }, /* Device FW comes here, 110. */
867bab47d9aSGerd Hoffmann     { "etc/extra-pci-roots", 120 },
868bab47d9aSGerd Hoffmann     { "etc/acpi/tables", 130 },
869bab47d9aSGerd Hoffmann     { "etc/table-loader", 140 },
870bab47d9aSGerd Hoffmann     { "etc/tpm/log", 150 },
871bab47d9aSGerd Hoffmann     { "etc/acpi/rsdp", 160 },
872bab47d9aSGerd Hoffmann     { "bootorder", 170 },
873bf21fe94SPaolo Bonzini     { "etc/msr_feature_control", 180 },
874bab47d9aSGerd Hoffmann 
875bab47d9aSGerd Hoffmann #define FW_CFG_ORDER_OVERRIDE_LAST 200
876bab47d9aSGerd Hoffmann };
877bab47d9aSGerd Hoffmann 
878394f0f72SShameer Kolothum /*
879394f0f72SShameer Kolothum  * Any sub-page size update to these table MRs will be lost during migration,
880394f0f72SShameer Kolothum  * as we use aligned size in ram_load_precopy() -> qemu_ram_resize() path.
8819b4b4e51SMichael Tokarev  * In order to avoid the inconsistency in sizes save them separately and
882394f0f72SShameer Kolothum  * migrate over in vmstate post_load().
883394f0f72SShameer Kolothum  */
fw_cfg_acpi_mr_save(FWCfgState * s,const char * filename,size_t len)884394f0f72SShameer Kolothum static void fw_cfg_acpi_mr_save(FWCfgState *s, const char *filename, size_t len)
885394f0f72SShameer Kolothum {
886394f0f72SShameer Kolothum     if (!strcmp(filename, ACPI_BUILD_TABLE_FILE)) {
887394f0f72SShameer Kolothum         s->table_mr_size = len;
888394f0f72SShameer Kolothum     } else if (!strcmp(filename, ACPI_BUILD_LOADER_FILE)) {
889394f0f72SShameer Kolothum         s->linker_mr_size = len;
890394f0f72SShameer Kolothum     } else if (!strcmp(filename, ACPI_BUILD_RSDP_FILE)) {
891394f0f72SShameer Kolothum         s->rsdp_mr_size = len;
892394f0f72SShameer Kolothum     }
893394f0f72SShameer Kolothum }
894394f0f72SShameer Kolothum 
get_fw_cfg_order(FWCfgState * s,const char * name)895bab47d9aSGerd Hoffmann static int get_fw_cfg_order(FWCfgState *s, const char *name)
896bab47d9aSGerd Hoffmann {
897bab47d9aSGerd Hoffmann     int i;
898bab47d9aSGerd Hoffmann 
899a8d38f3bSCao jin     if (s->fw_cfg_order_override > 0) {
900bab47d9aSGerd Hoffmann         return s->fw_cfg_order_override;
901a8d38f3bSCao jin     }
902bab47d9aSGerd Hoffmann 
903bab47d9aSGerd Hoffmann     for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) {
904a8d38f3bSCao jin         if (fw_cfg_order[i].name == NULL) {
905bab47d9aSGerd Hoffmann             continue;
906a8d38f3bSCao jin         }
907a8d38f3bSCao jin 
908a8d38f3bSCao jin         if (strcmp(name, fw_cfg_order[i].name) == 0) {
909bab47d9aSGerd Hoffmann             return fw_cfg_order[i].order;
910bab47d9aSGerd Hoffmann         }
911a8d38f3bSCao jin     }
912a8d38f3bSCao jin 
913bab47d9aSGerd Hoffmann     /* Stick unknown stuff at the end. */
9143dc6f869SAlistair Francis     warn_report("Unknown firmware file in legacy mode: %s", name);
915bab47d9aSGerd Hoffmann     return FW_CFG_ORDER_OVERRIDE_LAST;
916bab47d9aSGerd Hoffmann }
917bab47d9aSGerd Hoffmann 
fw_cfg_add_file_callback(FWCfgState * s,const char * filename,FWCfgCallback select_cb,FWCfgWriteCallback write_cb,void * callback_opaque,void * data,size_t len,bool read_only)918d87072ceSMichael S. Tsirkin void fw_cfg_add_file_callback(FWCfgState *s,  const char *filename,
9196f6f4aecSMarc-André Lureau                               FWCfgCallback select_cb,
9205f9252f7SMarc-André Lureau                               FWCfgWriteCallback write_cb,
9216f6f4aecSMarc-André Lureau                               void *callback_opaque,
922baf2d5bfSMichael S. Tsirkin                               void *data, size_t len, bool read_only)
92349ab747fSPaolo Bonzini {
924bab47d9aSGerd Hoffmann     int i, index, count;
92549ab747fSPaolo Bonzini     size_t dsize;
926bab47d9aSGerd Hoffmann     MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
927bab47d9aSGerd Hoffmann     int order = 0;
92849ab747fSPaolo Bonzini 
92949ab747fSPaolo Bonzini     if (!s->files) {
930e12f3a13SLaszlo Ersek         dsize = sizeof(uint32_t) + sizeof(FWCfgFile) * fw_cfg_file_slots(s);
93149ab747fSPaolo Bonzini         s->files = g_malloc0(dsize);
93249ab747fSPaolo Bonzini         fw_cfg_add_bytes(s, FW_CFG_FILE_DIR, s->files, dsize);
93349ab747fSPaolo Bonzini     }
93449ab747fSPaolo Bonzini 
935bab47d9aSGerd Hoffmann     count = be32_to_cpu(s->files->count);
936e12f3a13SLaszlo Ersek     assert(count < fw_cfg_file_slots(s));
93749ab747fSPaolo Bonzini 
938bab47d9aSGerd Hoffmann     /* Find the insertion point. */
939bab47d9aSGerd Hoffmann     if (mc->legacy_fw_cfg_order) {
940bab47d9aSGerd Hoffmann         /*
941bab47d9aSGerd Hoffmann          * Sort by order. For files with the same order, we keep them
942bab47d9aSGerd Hoffmann          * in the sequence in which they were added.
943bab47d9aSGerd Hoffmann          */
944bab47d9aSGerd Hoffmann         order = get_fw_cfg_order(s, filename);
945bab47d9aSGerd Hoffmann         for (index = count;
946bab47d9aSGerd Hoffmann              index > 0 && order < s->entry_order[index - 1];
947bab47d9aSGerd Hoffmann              index--);
948bab47d9aSGerd Hoffmann     } else {
949bab47d9aSGerd Hoffmann         /* Sort by file name. */
950bab47d9aSGerd Hoffmann         for (index = count;
951bab47d9aSGerd Hoffmann              index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0;
952bab47d9aSGerd Hoffmann              index--);
953bab47d9aSGerd Hoffmann     }
954bab47d9aSGerd Hoffmann 
955bab47d9aSGerd Hoffmann     /*
956bab47d9aSGerd Hoffmann      * Move all the entries from the index point and after down one
957bab47d9aSGerd Hoffmann      * to create a slot for the new entry.  Because calculations are
958bab47d9aSGerd Hoffmann      * being done with the index, make it so that "i" is the current
959bab47d9aSGerd Hoffmann      * index and "i - 1" is the one being copied from, thus the
960bab47d9aSGerd Hoffmann      * unusual start and end in the for statement.
961bab47d9aSGerd Hoffmann      */
962d6b6abc5SMarcel Apfelbaum     for (i = count; i > index; i--) {
963bab47d9aSGerd Hoffmann         s->files->f[i] = s->files->f[i - 1];
964bab47d9aSGerd Hoffmann         s->files->f[i].select = cpu_to_be16(FW_CFG_FILE_FIRST + i);
965bab47d9aSGerd Hoffmann         s->entries[0][FW_CFG_FILE_FIRST + i] =
966bab47d9aSGerd Hoffmann             s->entries[0][FW_CFG_FILE_FIRST + i - 1];
967bab47d9aSGerd Hoffmann         s->entry_order[i] = s->entry_order[i - 1];
968bab47d9aSGerd Hoffmann     }
969bab47d9aSGerd Hoffmann 
970bab47d9aSGerd Hoffmann     memset(&s->files->f[index], 0, sizeof(FWCfgFile));
971bab47d9aSGerd Hoffmann     memset(&s->entries[0][FW_CFG_FILE_FIRST + index], 0, sizeof(FWCfgEntry));
972bab47d9aSGerd Hoffmann 
973bab47d9aSGerd Hoffmann     pstrcpy(s->files->f[index].name, sizeof(s->files->f[index].name), filename);
974bab47d9aSGerd Hoffmann     for (i = 0; i <= count; i++) {
975bab47d9aSGerd Hoffmann         if (i != index &&
976bab47d9aSGerd Hoffmann             strcmp(s->files->f[index].name, s->files->f[i].name) == 0) {
9770eb973f9SGabriel L. Somlo             error_report("duplicate fw_cfg file name: %s",
9780eb973f9SGabriel L. Somlo                          s->files->f[index].name);
9790eb973f9SGabriel L. Somlo             exit(1);
98049ab747fSPaolo Bonzini         }
98149ab747fSPaolo Bonzini     }
98249ab747fSPaolo Bonzini 
9836f6f4aecSMarc-André Lureau     fw_cfg_add_bytes_callback(s, FW_CFG_FILE_FIRST + index,
9845f9252f7SMarc-André Lureau                               select_cb, write_cb,
9856f6f4aecSMarc-André Lureau                               callback_opaque, data, len,
986baf2d5bfSMichael S. Tsirkin                               read_only);
9870eb973f9SGabriel L. Somlo 
98849ab747fSPaolo Bonzini     s->files->f[index].size   = cpu_to_be32(len);
98949ab747fSPaolo Bonzini     s->files->f[index].select = cpu_to_be16(FW_CFG_FILE_FIRST + index);
990bab47d9aSGerd Hoffmann     s->entry_order[index] = order;
99149ab747fSPaolo Bonzini     trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
99249ab747fSPaolo Bonzini 
993bab47d9aSGerd Hoffmann     s->files->count = cpu_to_be32(count+1);
994394f0f72SShameer Kolothum     fw_cfg_acpi_mr_save(s, filename, len);
99549ab747fSPaolo Bonzini }
99649ab747fSPaolo Bonzini 
fw_cfg_add_file(FWCfgState * s,const char * filename,void * data,size_t len)997d87072ceSMichael S. Tsirkin void fw_cfg_add_file(FWCfgState *s,  const char *filename,
998d87072ceSMichael S. Tsirkin                      void *data, size_t len)
999d87072ceSMichael S. Tsirkin {
10005f9252f7SMarc-André Lureau     fw_cfg_add_file_callback(s, filename, NULL, NULL, NULL, data, len, true);
1001d87072ceSMichael S. Tsirkin }
1002d87072ceSMichael S. Tsirkin 
fw_cfg_modify_file(FWCfgState * s,const char * filename,void * data,size_t len)1003bdbb5b17SGonglei void *fw_cfg_modify_file(FWCfgState *s, const char *filename,
1004bdbb5b17SGonglei                         void *data, size_t len)
100549ab747fSPaolo Bonzini {
1006bdbb5b17SGonglei     int i, index;
1007f3b37668SGonglei     void *ptr = NULL;
1008bdbb5b17SGonglei 
1009bdbb5b17SGonglei     assert(s->files);
1010bdbb5b17SGonglei 
1011bdbb5b17SGonglei     index = be32_to_cpu(s->files->count);
1012bdbb5b17SGonglei 
1013bdbb5b17SGonglei     for (i = 0; i < index; i++) {
1014bdbb5b17SGonglei         if (strcmp(filename, s->files->f[i].name) == 0) {
1015f3b37668SGonglei             ptr = fw_cfg_modify_bytes_read(s, FW_CFG_FILE_FIRST + i,
1016bdbb5b17SGonglei                                            data, len);
1017f3b37668SGonglei             s->files->f[i].size   = cpu_to_be32(len);
1018394f0f72SShameer Kolothum             fw_cfg_acpi_mr_save(s, filename, len);
1019f3b37668SGonglei             return ptr;
1020bdbb5b17SGonglei         }
1021bdbb5b17SGonglei     }
1022d6b6abc5SMarcel Apfelbaum 
1023d6b6abc5SMarcel Apfelbaum     assert(index < fw_cfg_file_slots(s));
1024d6b6abc5SMarcel Apfelbaum 
1025bdbb5b17SGonglei     /* add new one */
10265f9252f7SMarc-André Lureau     fw_cfg_add_file_callback(s, filename, NULL, NULL, NULL, data, len, true);
1027bdbb5b17SGonglei     return NULL;
1028bdbb5b17SGonglei }
1029bdbb5b17SGonglei 
fw_cfg_add_from_generator(FWCfgState * s,const char * filename,const char * gen_id,Error ** errp)103007719518SPhilippe Mathieu-Daudé bool fw_cfg_add_from_generator(FWCfgState *s, const char *filename,
103132031489SPhilippe Mathieu-Daudé                                const char *gen_id, Error **errp)
103232031489SPhilippe Mathieu-Daudé {
103332031489SPhilippe Mathieu-Daudé     FWCfgDataGeneratorClass *klass;
103432031489SPhilippe Mathieu-Daudé     GByteArray *array;
103532031489SPhilippe Mathieu-Daudé     Object *obj;
103632031489SPhilippe Mathieu-Daudé     gsize size;
103732031489SPhilippe Mathieu-Daudé 
103832031489SPhilippe Mathieu-Daudé     obj = object_resolve_path_component(object_get_objects_root(), gen_id);
103932031489SPhilippe Mathieu-Daudé     if (!obj) {
104032031489SPhilippe Mathieu-Daudé         error_setg(errp, "Cannot find object ID '%s'", gen_id);
104107719518SPhilippe Mathieu-Daudé         return false;
104232031489SPhilippe Mathieu-Daudé     }
104332031489SPhilippe Mathieu-Daudé     if (!object_dynamic_cast(obj, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE)) {
104432031489SPhilippe Mathieu-Daudé         error_setg(errp, "Object ID '%s' is not a '%s' subclass",
104532031489SPhilippe Mathieu-Daudé                    gen_id, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE);
104607719518SPhilippe Mathieu-Daudé         return false;
104732031489SPhilippe Mathieu-Daudé     }
104832031489SPhilippe Mathieu-Daudé     klass = FW_CFG_DATA_GENERATOR_GET_CLASS(obj);
10498b4b5275SVladimir Sementsov-Ogievskiy     array = klass->get_data(obj, errp);
1050a3ad5834SPhilippe Mathieu-Daudé     if (!array) {
105107719518SPhilippe Mathieu-Daudé         return false;
105232031489SPhilippe Mathieu-Daudé     }
105332031489SPhilippe Mathieu-Daudé     size = array->len;
10544318432cSLaszlo Ersek     fw_cfg_add_file(s, filename, g_byte_array_free(array, FALSE), size);
105507719518SPhilippe Mathieu-Daudé 
105607719518SPhilippe Mathieu-Daudé     return true;
105732031489SPhilippe Mathieu-Daudé }
105832031489SPhilippe Mathieu-Daudé 
fw_cfg_add_extra_pci_roots(PCIBus * bus,FWCfgState * s)10590abd3888SJiahui Cen void fw_cfg_add_extra_pci_roots(PCIBus *bus, FWCfgState *s)
10600abd3888SJiahui Cen {
10610abd3888SJiahui Cen     int extra_hosts = 0;
10620abd3888SJiahui Cen 
10630abd3888SJiahui Cen     if (!bus) {
10640abd3888SJiahui Cen         return;
10650abd3888SJiahui Cen     }
10660abd3888SJiahui Cen 
10670abd3888SJiahui Cen     QLIST_FOREACH(bus, &bus->child, sibling) {
10680abd3888SJiahui Cen         /* look for expander root buses */
10690abd3888SJiahui Cen         if (pci_bus_is_root(bus)) {
10700abd3888SJiahui Cen             extra_hosts++;
10710abd3888SJiahui Cen         }
10720abd3888SJiahui Cen     }
10730abd3888SJiahui Cen 
10740abd3888SJiahui Cen     if (extra_hosts && s) {
10750abd3888SJiahui Cen         uint64_t *val = g_malloc(sizeof(*val));
10760abd3888SJiahui Cen         *val = cpu_to_le64(extra_hosts);
10770abd3888SJiahui Cen         fw_cfg_add_file(s, "etc/extra-pci-roots", val, sizeof(*val));
10780abd3888SJiahui Cen     }
10790abd3888SJiahui Cen }
10800abd3888SJiahui Cen 
fw_cfg_machine_reset(void * opaque)1081bdbb5b17SGonglei static void fw_cfg_machine_reset(void *opaque)
1082bdbb5b17SGonglei {
1083aea60a13SSam Eiderman     MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
1084aea60a13SSam Eiderman     FWCfgState *s = opaque;
1085bdbb5b17SGonglei     void *ptr;
108649ab747fSPaolo Bonzini     size_t len;
1087aea60a13SSam Eiderman     char *buf;
108849ab747fSPaolo Bonzini 
1089aea60a13SSam Eiderman     buf = get_boot_devices_list(&len);
1090aea60a13SSam Eiderman     ptr = fw_cfg_modify_file(s, "bootorder", (uint8_t *)buf, len);
1091bdbb5b17SGonglei     g_free(ptr);
1092aea60a13SSam Eiderman 
1093aea60a13SSam Eiderman     if (!mc->legacy_fw_cfg_order) {
1094aea60a13SSam Eiderman         buf = get_boot_devices_lchs_list(&len);
1095aea60a13SSam Eiderman         ptr = fw_cfg_modify_file(s, "bios-geometry", (uint8_t *)buf, len);
1096aea60a13SSam Eiderman         g_free(ptr);
1097aea60a13SSam Eiderman     }
1098bdbb5b17SGonglei }
1099bdbb5b17SGonglei 
fw_cfg_machine_ready(struct Notifier * n,void * data)1100bdbb5b17SGonglei static void fw_cfg_machine_ready(struct Notifier *n, void *data)
1101bdbb5b17SGonglei {
1102bdbb5b17SGonglei     FWCfgState *s = container_of(n, FWCfgState, machine_ready);
1103bdbb5b17SGonglei     qemu_register_reset(fw_cfg_machine_reset, s);
110449ab747fSPaolo Bonzini }
110549ab747fSPaolo Bonzini 
1106394f0f72SShameer Kolothum static Property fw_cfg_properties[] = {
1107394f0f72SShameer Kolothum     DEFINE_PROP_BOOL("acpi-mr-restore", FWCfgState, acpi_mr_restore, true),
1108394f0f72SShameer Kolothum     DEFINE_PROP_END_OF_LIST(),
1109394f0f72SShameer Kolothum };
11105712db6aSLaszlo Ersek 
fw_cfg_common_realize(DeviceState * dev,Error ** errp)111138f3adc3SMark Cave-Ayland static void fw_cfg_common_realize(DeviceState *dev, Error **errp)
111249ab747fSPaolo Bonzini {
11135712db6aSLaszlo Ersek     FWCfgState *s = FW_CFG(dev);
1114cfc58cf3SEduardo Habkost     MachineState *machine = MACHINE(qdev_get_machine());
11153c1aa733SMark Cave-Ayland     uint32_t version = FW_CFG_VERSION;
111649ab747fSPaolo Bonzini 
111738f3adc3SMark Cave-Ayland     if (!fw_cfg_find()) {
111838f3adc3SMark Cave-Ayland         error_setg(errp, "at most one %s device is permitted", TYPE_FW_CFG);
111938f3adc3SMark Cave-Ayland         return;
112038f3adc3SMark Cave-Ayland     }
112110a584b2SHu Tao 
112249ab747fSPaolo Bonzini     fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4);
11239c5ce8dbSFam Zheng     fw_cfg_add_bytes(s, FW_CFG_UUID, &qemu_uuid, 16);
1124cfc58cf3SEduardo Habkost     fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)!machine->enable_graphics);
112597ec4d21SPaolo Bonzini     fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)(machine->boot_config.has_menu && machine->boot_config.menu));
112649ab747fSPaolo Bonzini     fw_cfg_bootsplash(s);
112749ab747fSPaolo Bonzini     fw_cfg_reboot(s);
112849ab747fSPaolo Bonzini 
11293c1aa733SMark Cave-Ayland     if (s->dma_enabled) {
11303c1aa733SMark Cave-Ayland         version |= FW_CFG_VERSION_DMA;
11313c1aa733SMark Cave-Ayland     }
11323c1aa733SMark Cave-Ayland 
11333c1aa733SMark Cave-Ayland     fw_cfg_add_i32(s, FW_CFG_ID, version);
11343c1aa733SMark Cave-Ayland 
113549ab747fSPaolo Bonzini     s->machine_ready.notify = fw_cfg_machine_ready;
113649ab747fSPaolo Bonzini     qemu_add_machine_init_done_notifier(&s->machine_ready);
113749ab747fSPaolo Bonzini }
113849ab747fSPaolo Bonzini 
fw_cfg_init_io_dma(uint32_t iobase,uint32_t dma_iobase,AddressSpace * dma_as)1139a4c0d1deSMarc Marí FWCfgState *fw_cfg_init_io_dma(uint32_t iobase, uint32_t dma_iobase,
1140a4c0d1deSMarc Marí                                 AddressSpace *dma_as)
114149ab747fSPaolo Bonzini {
11425712db6aSLaszlo Ersek     DeviceState *dev;
114391685323SMark Cave-Ayland     SysBusDevice *sbd;
114491685323SMark Cave-Ayland     FWCfgIoState *ios;
1145a4c0d1deSMarc Marí     FWCfgState *s;
11460068b069SPhilippe Mathieu-Daudé     MemoryRegion *iomem = get_system_io();
1147e6915b5fSLaszlo Ersek     bool dma_requested = dma_iobase && dma_as;
114849ab747fSPaolo Bonzini 
11493e80f690SMarkus Armbruster     dev = qdev_new(TYPE_FW_CFG_IO);
1150e6915b5fSLaszlo Ersek     if (!dma_requested) {
1151e6915b5fSLaszlo Ersek         qdev_prop_set_bit(dev, "dma_enabled", false);
1152e6915b5fSLaszlo Ersek     }
11535712db6aSLaszlo Ersek 
115438f3adc3SMark Cave-Ayland     object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG,
1155d2623129SMarkus Armbruster                               OBJECT(dev));
115691685323SMark Cave-Ayland 
115791685323SMark Cave-Ayland     sbd = SYS_BUS_DEVICE(dev);
11583c6ef471SMarkus Armbruster     sysbus_realize_and_unref(sbd, &error_fatal);
115991685323SMark Cave-Ayland     ios = FW_CFG_IO(dev);
11600068b069SPhilippe Mathieu-Daudé     memory_region_add_subregion(iomem, iobase, &ios->comb_iomem);
116191685323SMark Cave-Ayland 
1162a4c0d1deSMarc Marí     s = FW_CFG(dev);
1163a4c0d1deSMarc Marí 
1164e6915b5fSLaszlo Ersek     if (s->dma_enabled) {
1165a4c0d1deSMarc Marí         /* 64 bits for the address field */
1166a4c0d1deSMarc Marí         s->dma_as = dma_as;
1167a4c0d1deSMarc Marí         s->dma_addr = 0;
11680068b069SPhilippe Mathieu-Daudé         memory_region_add_subregion(iomem, dma_iobase, &s->dma_iomem);
116956383955SHu Tao     }
117056383955SHu Tao 
1171a4c0d1deSMarc Marí     return s;
1172a4c0d1deSMarc Marí }
1173a4c0d1deSMarc Marí 
fw_cfg_init_mem_wide(hwaddr ctl_addr,hwaddr data_addr,uint32_t data_width,hwaddr dma_addr,AddressSpace * dma_as)1174a4c0d1deSMarc Marí FWCfgState *fw_cfg_init_mem_wide(hwaddr ctl_addr,
1175a4c0d1deSMarc Marí                                  hwaddr data_addr, uint32_t data_width,
1176a4c0d1deSMarc Marí                                  hwaddr dma_addr, AddressSpace *dma_as)
117756383955SHu Tao {
11785712db6aSLaszlo Ersek     DeviceState *dev;
11795712db6aSLaszlo Ersek     SysBusDevice *sbd;
1180a4c0d1deSMarc Marí     FWCfgState *s;
1181e6915b5fSLaszlo Ersek     bool dma_requested = dma_addr && dma_as;
118256383955SHu Tao 
11833e80f690SMarkus Armbruster     dev = qdev_new(TYPE_FW_CFG_MEM);
11846c87e3d5SLaszlo Ersek     qdev_prop_set_uint32(dev, "data_width", data_width);
1185e6915b5fSLaszlo Ersek     if (!dma_requested) {
1186e6915b5fSLaszlo Ersek         qdev_prop_set_bit(dev, "dma_enabled", false);
1187e6915b5fSLaszlo Ersek     }
1188cfaadf0eSLaszlo Ersek 
118938f3adc3SMark Cave-Ayland     object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG,
1190d2623129SMarkus Armbruster                               OBJECT(dev));
11915712db6aSLaszlo Ersek 
11925712db6aSLaszlo Ersek     sbd = SYS_BUS_DEVICE(dev);
11933c6ef471SMarkus Armbruster     sysbus_realize_and_unref(sbd, &error_fatal);
11945712db6aSLaszlo Ersek     sysbus_mmio_map(sbd, 0, ctl_addr);
11955712db6aSLaszlo Ersek     sysbus_mmio_map(sbd, 1, data_addr);
11965712db6aSLaszlo Ersek 
1197a4c0d1deSMarc Marí     s = FW_CFG(dev);
1198a4c0d1deSMarc Marí 
1199e6915b5fSLaszlo Ersek     if (s->dma_enabled) {
1200a4c0d1deSMarc Marí         s->dma_as = dma_as;
1201a4c0d1deSMarc Marí         s->dma_addr = 0;
1202a4c0d1deSMarc Marí         sysbus_mmio_map(sbd, 2, dma_addr);
1203a4c0d1deSMarc Marí     }
1204a4c0d1deSMarc Marí 
1205a4c0d1deSMarc Marí     return s;
120649ab747fSPaolo Bonzini }
120749ab747fSPaolo Bonzini 
fw_cfg_init_mem(hwaddr ctl_addr,hwaddr data_addr)12086c87e3d5SLaszlo Ersek FWCfgState *fw_cfg_init_mem(hwaddr ctl_addr, hwaddr data_addr)
12096c87e3d5SLaszlo Ersek {
12106c87e3d5SLaszlo Ersek     return fw_cfg_init_mem_wide(ctl_addr, data_addr,
1211a4c0d1deSMarc Marí                                 fw_cfg_data_mem_ops.valid.max_access_size,
1212a4c0d1deSMarc Marí                                 0, NULL);
12136c87e3d5SLaszlo Ersek }
12146c87e3d5SLaszlo Ersek 
12155712db6aSLaszlo Ersek 
fw_cfg_find(void)1216600c60b7SMichael S. Tsirkin FWCfgState *fw_cfg_find(void)
1217600c60b7SMichael S. Tsirkin {
12186e99c075SMark Cave-Ayland     /* Returns NULL unless there is exactly one fw_cfg device */
12196e99c075SMark Cave-Ayland     return FW_CFG(object_resolve_path_type("", TYPE_FW_CFG, NULL));
1220600c60b7SMichael S. Tsirkin }
1221600c60b7SMichael S. Tsirkin 
load_image_to_fw_cfg(FWCfgState * fw_cfg,uint16_t size_key,uint16_t data_key,const char * image_name,bool try_decompress)1222785a7383SSunil V L void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
1223785a7383SSunil V L                           uint16_t data_key, const char *image_name,
1224785a7383SSunil V L                           bool try_decompress)
1225785a7383SSunil V L {
1226785a7383SSunil V L     size_t size = -1;
1227785a7383SSunil V L     uint8_t *data;
1228785a7383SSunil V L 
1229785a7383SSunil V L     if (image_name == NULL) {
1230785a7383SSunil V L         return;
1231785a7383SSunil V L     }
1232785a7383SSunil V L 
1233785a7383SSunil V L     if (try_decompress) {
1234785a7383SSunil V L         size = load_image_gzipped_buffer(image_name,
1235785a7383SSunil V L                                          LOAD_IMAGE_MAX_GUNZIP_BYTES, &data);
1236785a7383SSunil V L     }
1237785a7383SSunil V L 
1238785a7383SSunil V L     if (size == (size_t)-1) {
1239785a7383SSunil V L         gchar *contents;
1240785a7383SSunil V L         gsize length;
1241785a7383SSunil V L 
1242785a7383SSunil V L         if (!g_file_get_contents(image_name, &contents, &length, NULL)) {
1243785a7383SSunil V L             error_report("failed to load \"%s\"", image_name);
1244785a7383SSunil V L             exit(1);
1245785a7383SSunil V L         }
1246785a7383SSunil V L         size = length;
1247785a7383SSunil V L         data = (uint8_t *)contents;
1248785a7383SSunil V L     }
1249785a7383SSunil V L 
1250785a7383SSunil V L     fw_cfg_add_i32(fw_cfg, size_key, size);
1251785a7383SSunil V L     fw_cfg_add_bytes(fw_cfg, data_key, data, size);
1252785a7383SSunil V L }
125338f3adc3SMark Cave-Ayland 
fw_cfg_class_init(ObjectClass * klass,void * data)125449ab747fSPaolo Bonzini static void fw_cfg_class_init(ObjectClass *klass, void *data)
125549ab747fSPaolo Bonzini {
125649ab747fSPaolo Bonzini     DeviceClass *dc = DEVICE_CLASS(klass);
125749ab747fSPaolo Bonzini 
1258*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, fw_cfg_reset);
125949ab747fSPaolo Bonzini     dc->vmsd = &vmstate_fw_cfg;
1260394f0f72SShameer Kolothum 
1261394f0f72SShameer Kolothum     device_class_set_props(dc, fw_cfg_properties);
126249ab747fSPaolo Bonzini }
126349ab747fSPaolo Bonzini 
126449ab747fSPaolo Bonzini static const TypeInfo fw_cfg_info = {
1265600c60b7SMichael S. Tsirkin     .name          = TYPE_FW_CFG,
126649ab747fSPaolo Bonzini     .parent        = TYPE_SYS_BUS_DEVICE,
1267e061fa3cSMarkus Armbruster     .abstract      = true,
126849ab747fSPaolo Bonzini     .instance_size = sizeof(FWCfgState),
126949ab747fSPaolo Bonzini     .class_init    = fw_cfg_class_init,
127049ab747fSPaolo Bonzini };
127149ab747fSPaolo Bonzini 
fw_cfg_file_slots_allocate(FWCfgState * s,Error ** errp)1272e12f3a13SLaszlo Ersek static void fw_cfg_file_slots_allocate(FWCfgState *s, Error **errp)
1273e12f3a13SLaszlo Ersek {
1274e12f3a13SLaszlo Ersek     uint16_t file_slots_max;
1275e12f3a13SLaszlo Ersek 
1276e12f3a13SLaszlo Ersek     if (fw_cfg_file_slots(s) < FW_CFG_FILE_SLOTS_MIN) {
1277e12f3a13SLaszlo Ersek         error_setg(errp, "\"file_slots\" must be at least 0x%x",
1278e12f3a13SLaszlo Ersek                    FW_CFG_FILE_SLOTS_MIN);
1279e12f3a13SLaszlo Ersek         return;
1280e12f3a13SLaszlo Ersek     }
1281e12f3a13SLaszlo Ersek 
1282e12f3a13SLaszlo Ersek     /* (UINT16_MAX & FW_CFG_ENTRY_MASK) is the highest inclusive selector value
1283e12f3a13SLaszlo Ersek      * that we permit. The actual (exclusive) value coming from the
1284e12f3a13SLaszlo Ersek      * configuration is (FW_CFG_FILE_FIRST + fw_cfg_file_slots(s)). */
1285e12f3a13SLaszlo Ersek     file_slots_max = (UINT16_MAX & FW_CFG_ENTRY_MASK) - FW_CFG_FILE_FIRST + 1;
1286e12f3a13SLaszlo Ersek     if (fw_cfg_file_slots(s) > file_slots_max) {
1287e12f3a13SLaszlo Ersek         error_setg(errp, "\"file_slots\" must not exceed 0x%" PRIx16,
1288e12f3a13SLaszlo Ersek                    file_slots_max);
1289e12f3a13SLaszlo Ersek         return;
1290e12f3a13SLaszlo Ersek     }
1291e12f3a13SLaszlo Ersek 
1292e12f3a13SLaszlo Ersek     s->entries[0] = g_new0(FWCfgEntry, fw_cfg_max_entry(s));
1293e12f3a13SLaszlo Ersek     s->entries[1] = g_new0(FWCfgEntry, fw_cfg_max_entry(s));
1294e12f3a13SLaszlo Ersek     s->entry_order = g_new0(int, fw_cfg_max_entry(s));
1295e12f3a13SLaszlo Ersek }
12965712db6aSLaszlo Ersek 
12975712db6aSLaszlo Ersek static Property fw_cfg_io_properties[] = {
1298a4c0d1deSMarc Marí     DEFINE_PROP_BOOL("dma_enabled", FWCfgIoState, parent_obj.dma_enabled,
1299e6915b5fSLaszlo Ersek                      true),
1300e12f3a13SLaszlo Ersek     DEFINE_PROP_UINT16("x-file-slots", FWCfgIoState, parent_obj.file_slots,
1301a5b3ebfdSLaszlo Ersek                        FW_CFG_FILE_SLOTS_DFLT),
13025712db6aSLaszlo Ersek     DEFINE_PROP_END_OF_LIST(),
13035712db6aSLaszlo Ersek };
13045712db6aSLaszlo Ersek 
fw_cfg_io_realize(DeviceState * dev,Error ** errp)13055712db6aSLaszlo Ersek static void fw_cfg_io_realize(DeviceState *dev, Error **errp)
13065712db6aSLaszlo Ersek {
13078b4b5275SVladimir Sementsov-Ogievskiy     ERRP_GUARD();
13085712db6aSLaszlo Ersek     FWCfgIoState *s = FW_CFG_IO(dev);
1309e12f3a13SLaszlo Ersek 
13108b4b5275SVladimir Sementsov-Ogievskiy     fw_cfg_file_slots_allocate(FW_CFG(s), errp);
13118b4b5275SVladimir Sementsov-Ogievskiy     if (*errp) {
1312e12f3a13SLaszlo Ersek         return;
1313e12f3a13SLaszlo Ersek     }
13145712db6aSLaszlo Ersek 
1315ce9a2aa3SGabriel L. Somlo     /* when using port i/o, the 8-bit data register ALWAYS overlaps
1316ce9a2aa3SGabriel L. Somlo      * with half of the 16-bit control register. Hence, the total size
1317ce9a2aa3SGabriel L. Somlo      * of the i/o region used is FW_CFG_CTL_SIZE */
13185712db6aSLaszlo Ersek     memory_region_init_io(&s->comb_iomem, OBJECT(s), &fw_cfg_comb_mem_ops,
1319a4c0d1deSMarc Marí                           FW_CFG(s), "fwcfg", FW_CFG_CTL_SIZE);
1320a4c0d1deSMarc Marí 
1321a4c0d1deSMarc Marí     if (FW_CFG(s)->dma_enabled) {
1322a4c0d1deSMarc Marí         memory_region_init_io(&FW_CFG(s)->dma_iomem, OBJECT(s),
1323a4c0d1deSMarc Marí                               &fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma",
1324a4c0d1deSMarc Marí                               sizeof(dma_addr_t));
1325a4c0d1deSMarc Marí     }
132638f3adc3SMark Cave-Ayland 
132738f3adc3SMark Cave-Ayland     fw_cfg_common_realize(dev, errp);
13285712db6aSLaszlo Ersek }
13295712db6aSLaszlo Ersek 
fw_cfg_io_class_init(ObjectClass * klass,void * data)13305712db6aSLaszlo Ersek static void fw_cfg_io_class_init(ObjectClass *klass, void *data)
13315712db6aSLaszlo Ersek {
13325712db6aSLaszlo Ersek     DeviceClass *dc = DEVICE_CLASS(klass);
13335712db6aSLaszlo Ersek 
13345712db6aSLaszlo Ersek     dc->realize = fw_cfg_io_realize;
13354f67d30bSMarc-André Lureau     device_class_set_props(dc, fw_cfg_io_properties);
13365712db6aSLaszlo Ersek }
13375712db6aSLaszlo Ersek 
13385712db6aSLaszlo Ersek static const TypeInfo fw_cfg_io_info = {
13395712db6aSLaszlo Ersek     .name          = TYPE_FW_CFG_IO,
13405712db6aSLaszlo Ersek     .parent        = TYPE_FW_CFG,
13415712db6aSLaszlo Ersek     .instance_size = sizeof(FWCfgIoState),
13425712db6aSLaszlo Ersek     .class_init    = fw_cfg_io_class_init,
13435712db6aSLaszlo Ersek };
13445712db6aSLaszlo Ersek 
13455712db6aSLaszlo Ersek 
1346cfaadf0eSLaszlo Ersek static Property fw_cfg_mem_properties[] = {
1347cfaadf0eSLaszlo Ersek     DEFINE_PROP_UINT32("data_width", FWCfgMemState, data_width, -1),
1348a4c0d1deSMarc Marí     DEFINE_PROP_BOOL("dma_enabled", FWCfgMemState, parent_obj.dma_enabled,
1349e6915b5fSLaszlo Ersek                      true),
1350e12f3a13SLaszlo Ersek     DEFINE_PROP_UINT16("x-file-slots", FWCfgMemState, parent_obj.file_slots,
1351a5b3ebfdSLaszlo Ersek                        FW_CFG_FILE_SLOTS_DFLT),
1352cfaadf0eSLaszlo Ersek     DEFINE_PROP_END_OF_LIST(),
1353cfaadf0eSLaszlo Ersek };
1354cfaadf0eSLaszlo Ersek 
fw_cfg_mem_realize(DeviceState * dev,Error ** errp)13555712db6aSLaszlo Ersek static void fw_cfg_mem_realize(DeviceState *dev, Error **errp)
13565712db6aSLaszlo Ersek {
13578b4b5275SVladimir Sementsov-Ogievskiy     ERRP_GUARD();
13585712db6aSLaszlo Ersek     FWCfgMemState *s = FW_CFG_MEM(dev);
13595712db6aSLaszlo Ersek     SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
1360cfaadf0eSLaszlo Ersek     const MemoryRegionOps *data_ops = &fw_cfg_data_mem_ops;
1361e12f3a13SLaszlo Ersek 
13628b4b5275SVladimir Sementsov-Ogievskiy     fw_cfg_file_slots_allocate(FW_CFG(s), errp);
13638b4b5275SVladimir Sementsov-Ogievskiy     if (*errp) {
1364e12f3a13SLaszlo Ersek         return;
1365e12f3a13SLaszlo Ersek     }
13665712db6aSLaszlo Ersek 
13675712db6aSLaszlo Ersek     memory_region_init_io(&s->ctl_iomem, OBJECT(s), &fw_cfg_ctl_mem_ops,
1368a4c0d1deSMarc Marí                           FW_CFG(s), "fwcfg.ctl", FW_CFG_CTL_SIZE);
13695712db6aSLaszlo Ersek     sysbus_init_mmio(sbd, &s->ctl_iomem);
13705712db6aSLaszlo Ersek 
1371cfaadf0eSLaszlo Ersek     if (s->data_width > data_ops->valid.max_access_size) {
1372695e2fc2SPeter Maydell         s->wide_data_ops = *data_ops;
1373cfaadf0eSLaszlo Ersek 
1374cfaadf0eSLaszlo Ersek         s->wide_data_ops.valid.max_access_size = s->data_width;
1375cfaadf0eSLaszlo Ersek         s->wide_data_ops.impl.max_access_size  = s->data_width;
1376cfaadf0eSLaszlo Ersek         data_ops = &s->wide_data_ops;
1377cfaadf0eSLaszlo Ersek     }
1378cfaadf0eSLaszlo Ersek     memory_region_init_io(&s->data_iomem, OBJECT(s), data_ops, FW_CFG(s),
1379cfaadf0eSLaszlo Ersek                           "fwcfg.data", data_ops->valid.max_access_size);
13805712db6aSLaszlo Ersek     sysbus_init_mmio(sbd, &s->data_iomem);
1381a4c0d1deSMarc Marí 
1382a4c0d1deSMarc Marí     if (FW_CFG(s)->dma_enabled) {
1383a4c0d1deSMarc Marí         memory_region_init_io(&FW_CFG(s)->dma_iomem, OBJECT(s),
1384a4c0d1deSMarc Marí                               &fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma",
1385a4c0d1deSMarc Marí                               sizeof(dma_addr_t));
1386a4c0d1deSMarc Marí         sysbus_init_mmio(sbd, &FW_CFG(s)->dma_iomem);
1387a4c0d1deSMarc Marí     }
138838f3adc3SMark Cave-Ayland 
138938f3adc3SMark Cave-Ayland     fw_cfg_common_realize(dev, errp);
13905712db6aSLaszlo Ersek }
13915712db6aSLaszlo Ersek 
fw_cfg_mem_class_init(ObjectClass * klass,void * data)13925712db6aSLaszlo Ersek static void fw_cfg_mem_class_init(ObjectClass *klass, void *data)
13935712db6aSLaszlo Ersek {
13945712db6aSLaszlo Ersek     DeviceClass *dc = DEVICE_CLASS(klass);
13955712db6aSLaszlo Ersek 
13965712db6aSLaszlo Ersek     dc->realize = fw_cfg_mem_realize;
13974f67d30bSMarc-André Lureau     device_class_set_props(dc, fw_cfg_mem_properties);
13985712db6aSLaszlo Ersek }
13995712db6aSLaszlo Ersek 
14005712db6aSLaszlo Ersek static const TypeInfo fw_cfg_mem_info = {
14015712db6aSLaszlo Ersek     .name          = TYPE_FW_CFG_MEM,
14025712db6aSLaszlo Ersek     .parent        = TYPE_FW_CFG,
14035712db6aSLaszlo Ersek     .instance_size = sizeof(FWCfgMemState),
14045712db6aSLaszlo Ersek     .class_init    = fw_cfg_mem_class_init,
14055712db6aSLaszlo Ersek };
14065712db6aSLaszlo Ersek 
fw_cfg_register_types(void)140749ab747fSPaolo Bonzini static void fw_cfg_register_types(void)
140849ab747fSPaolo Bonzini {
140949ab747fSPaolo Bonzini     type_register_static(&fw_cfg_info);
14105712db6aSLaszlo Ersek     type_register_static(&fw_cfg_io_info);
14115712db6aSLaszlo Ersek     type_register_static(&fw_cfg_mem_info);
141249ab747fSPaolo Bonzini }
141349ab747fSPaolo Bonzini 
141449ab747fSPaolo Bonzini type_init(fw_cfg_register_types)
1415