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