*/
#include "qemu/osdep.h"
-#include "hw/hw.h"
+#include "qemu/datadir.h"
#include "sysemu/sysemu.h"
#include "sysemu/dma.h"
+#include "sysemu/reset.h"
#include "hw/boards.h"
#include "hw/nvram/fw_cfg.h"
+#include "hw/qdev-properties.h"
#include "hw/sysbus.h"
+#include "migration/qemu-file-types.h"
+#include "migration/vmstate.h"
#include "trace.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "qemu/cutils.h"
#include "qapi/error.h"
+#include "hw/acpi/aml-build.h"
+#include "hw/pci/pci_bus.h"
#define FW_CFG_FILE_SLOTS_DFLT 0x20
static void fw_cfg_reboot(FWCfgState *s)
{
const char *reboot_timeout = NULL;
- int64_t rt_val = -1;
+ uint64_t rt_val = -1;
uint32_t rt_le32;
/* get user configuration */
if (reboot_timeout) {
rt_val = qemu_opt_get_number(opts, "reboot-timeout", -1);
+
/* validate the input */
- if (rt_val < 0 || rt_val > 0xffff) {
+ if (rt_val > 0xffff && rt_val != (uint64_t)-1) {
error_report("reboot timeout is invalid,"
- "it should be a value between 0 and 65535");
+ "it should be a value between -1 and 65535");
exit(1);
}
}
dma_addr = s->dma_addr;
s->dma_addr = 0;
- if (dma_memory_read(s->dma_as, dma_addr, &dma, sizeof(dma))) {
+ if (dma_memory_read(s->dma_as, dma_addr,
+ &dma, sizeof(dma), MEMTXATTRS_UNSPECIFIED)) {
stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control),
- FW_CFG_DMA_CTL_ERROR);
+ FW_CFG_DMA_CTL_ERROR, MEMTXATTRS_UNSPECIFIED);
return;
}
* tested before.
*/
if (read) {
- if (dma_memory_set(s->dma_as, dma.address, 0, len)) {
+ if (dma_memory_set(s->dma_as, dma.address, 0, len,
+ MEMTXATTRS_UNSPECIFIED)) {
dma.control |= FW_CFG_DMA_CTL_ERROR;
}
}
*/
if (read) {
if (dma_memory_write(s->dma_as, dma.address,
- &e->data[s->cur_offset], len)) {
+ &e->data[s->cur_offset], len,
+ MEMTXATTRS_UNSPECIFIED)) {
dma.control |= FW_CFG_DMA_CTL_ERROR;
}
}
if (!e->allow_write ||
len != dma.length ||
dma_memory_read(s->dma_as, dma.address,
- &e->data[s->cur_offset], len)) {
+ &e->data[s->cur_offset], len,
+ MEMTXATTRS_UNSPECIFIED)) {
dma.control |= FW_CFG_DMA_CTL_ERROR;
} else if (e->write_cb) {
e->write_cb(e->callback_opaque, s->cur_offset, len);
}
stl_be_dma(s->dma_as, dma_addr + offsetof(FWCfgDmaAccess, control),
- dma.control);
+ dma.control, MEMTXATTRS_UNSPECIFIED);
trace_fw_cfg_read(s, 0);
}
}
static int put_unused(QEMUFile *f, void *pv, size_t size,
- const VMStateField *field, QJSON *vmdesc)
+ const VMStateField *field, JSONWriter *vmdesc)
{
fprintf(stderr, "uint32_as_uint16 is only used for backward compatibility.\n");
fprintf(stderr, "This functions shouldn't be called.\n");
return s->dma_enabled;
}
+static bool fw_cfg_acpi_mr_restore(void *opaque)
+{
+ FWCfgState *s = opaque;
+ bool mr_aligned;
+
+ mr_aligned = QEMU_IS_ALIGNED(s->table_mr_size, qemu_real_host_page_size()) &&
+ QEMU_IS_ALIGNED(s->linker_mr_size, qemu_real_host_page_size()) &&
+ QEMU_IS_ALIGNED(s->rsdp_mr_size, qemu_real_host_page_size());
+ return s->acpi_mr_restore && !mr_aligned;
+}
+
+static void fw_cfg_update_mr(FWCfgState *s, uint16_t key, size_t size)
+{
+ MemoryRegion *mr;
+ ram_addr_t offset;
+ int arch = !!(key & FW_CFG_ARCH_LOCAL);
+ void *ptr;
+
+ key &= FW_CFG_ENTRY_MASK;
+ assert(key < fw_cfg_max_entry(s));
+
+ ptr = s->entries[arch][key].data;
+ mr = memory_region_from_host(ptr, &offset);
+
+ memory_region_ram_resize(mr, size, &error_abort);
+}
+
+static int fw_cfg_acpi_mr_restore_post_load(void *opaque, int version_id)
+{
+ FWCfgState *s = opaque;
+ int i, index;
+
+ assert(s->files);
+
+ index = be32_to_cpu(s->files->count);
+
+ for (i = 0; i < index; i++) {
+ if (!strcmp(s->files->f[i].name, ACPI_BUILD_TABLE_FILE)) {
+ fw_cfg_update_mr(s, FW_CFG_FILE_FIRST + i, s->table_mr_size);
+ } else if (!strcmp(s->files->f[i].name, ACPI_BUILD_LOADER_FILE)) {
+ fw_cfg_update_mr(s, FW_CFG_FILE_FIRST + i, s->linker_mr_size);
+ } else if (!strcmp(s->files->f[i].name, ACPI_BUILD_RSDP_FILE)) {
+ fw_cfg_update_mr(s, FW_CFG_FILE_FIRST + i, s->rsdp_mr_size);
+ }
+ }
+
+ return 0;
+}
+
static const VMStateDescription vmstate_fw_cfg_dma = {
.name = "fw_cfg/dma",
.needed = fw_cfg_dma_enabled,
},
};
+static const VMStateDescription vmstate_fw_cfg_acpi_mr = {
+ .name = "fw_cfg/acpi_mr",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = fw_cfg_acpi_mr_restore,
+ .post_load = fw_cfg_acpi_mr_restore_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(table_mr_size, FWCfgState),
+ VMSTATE_UINT64(linker_mr_size, FWCfgState),
+ VMSTATE_UINT64(rsdp_mr_size, FWCfgState),
+ VMSTATE_END_OF_LIST()
+ },
+};
+
static const VMStateDescription vmstate_fw_cfg = {
.name = "fw_cfg",
.version_id = 2,
},
.subsections = (const VMStateDescription*[]) {
&vmstate_fw_cfg_dma,
+ &vmstate_fw_cfg_acpi_mr,
NULL,
}
};
fw_cfg_add_bytes(s, key, g_memdup(value, sz), sz);
}
+void fw_cfg_modify_string(FWCfgState *s, uint16_t key, const char *value)
+{
+ size_t sz = strlen(value) + 1;
+ char *old;
+
+ old = fw_cfg_modify_bytes_read(s, key, g_memdup(value, sz), sz);
+ g_free(old);
+}
+
void fw_cfg_add_i16(FWCfgState *s, uint16_t key, uint16_t value)
{
uint16_t *copy;
fw_cfg_add_bytes(s, key, copy, sizeof(value));
}
+void fw_cfg_modify_i32(FWCfgState *s, uint16_t key, uint32_t value)
+{
+ uint32_t *copy, *old;
+
+ copy = g_malloc(sizeof(value));
+ *copy = cpu_to_le32(value);
+ old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value));
+ g_free(old);
+}
+
void fw_cfg_add_i64(FWCfgState *s, uint16_t key, uint64_t value)
{
uint64_t *copy;
fw_cfg_add_bytes(s, key, copy, sizeof(value));
}
+void fw_cfg_modify_i64(FWCfgState *s, uint16_t key, uint64_t value)
+{
+ uint64_t *copy, *old;
+
+ copy = g_malloc(sizeof(value));
+ *copy = cpu_to_le64(value);
+ old = fw_cfg_modify_bytes_read(s, key, copy, sizeof(value));
+ g_free(old);
+}
+
void fw_cfg_set_order_override(FWCfgState *s, int order)
{
assert(s->fw_cfg_order_override == 0);
{ "etc/tpm/log", 150 },
{ "etc/acpi/rsdp", 160 },
{ "bootorder", 170 },
+ { "etc/msr_feature_control", 180 },
#define FW_CFG_ORDER_OVERRIDE_LAST 200
};
+/*
+ * Any sub-page size update to these table MRs will be lost during migration,
+ * as we use aligned size in ram_load_precopy() -> qemu_ram_resize() path.
+ * In order to avoid the inconsistency in sizes save them seperately and
+ * migrate over in vmstate post_load().
+ */
+static void fw_cfg_acpi_mr_save(FWCfgState *s, const char *filename, size_t len)
+{
+ if (!strcmp(filename, ACPI_BUILD_TABLE_FILE)) {
+ s->table_mr_size = len;
+ } else if (!strcmp(filename, ACPI_BUILD_LOADER_FILE)) {
+ s->linker_mr_size = len;
+ } else if (!strcmp(filename, ACPI_BUILD_RSDP_FILE)) {
+ s->rsdp_mr_size = len;
+ }
+}
+
static int get_fw_cfg_order(FWCfgState *s, const char *name)
{
int i;
trace_fw_cfg_add_file(s, index, s->files->f[index].name, len);
s->files->count = cpu_to_be32(count+1);
+ fw_cfg_acpi_mr_save(s, filename, len);
}
void fw_cfg_add_file(FWCfgState *s, const char *filename,
ptr = fw_cfg_modify_bytes_read(s, FW_CFG_FILE_FIRST + i,
data, len);
s->files->f[i].size = cpu_to_be32(len);
+ fw_cfg_acpi_mr_save(s, filename, len);
return ptr;
}
}
return NULL;
}
+bool fw_cfg_add_from_generator(FWCfgState *s, const char *filename,
+ const char *gen_id, Error **errp)
+{
+ FWCfgDataGeneratorClass *klass;
+ GByteArray *array;
+ Object *obj;
+ gsize size;
+
+ obj = object_resolve_path_component(object_get_objects_root(), gen_id);
+ if (!obj) {
+ error_setg(errp, "Cannot find object ID '%s'", gen_id);
+ return false;
+ }
+ if (!object_dynamic_cast(obj, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE)) {
+ error_setg(errp, "Object ID '%s' is not a '%s' subclass",
+ gen_id, TYPE_FW_CFG_DATA_GENERATOR_INTERFACE);
+ return false;
+ }
+ klass = FW_CFG_DATA_GENERATOR_GET_CLASS(obj);
+ array = klass->get_data(obj, errp);
+ if (!array) {
+ return false;
+ }
+ size = array->len;
+ fw_cfg_add_file(s, filename, g_byte_array_free(array, FALSE), size);
+
+ return true;
+}
+
+void fw_cfg_add_extra_pci_roots(PCIBus *bus, FWCfgState *s)
+{
+ int extra_hosts = 0;
+
+ if (!bus) {
+ return;
+ }
+
+ QLIST_FOREACH(bus, &bus->child, sibling) {
+ /* look for expander root buses */
+ if (pci_bus_is_root(bus)) {
+ extra_hosts++;
+ }
+ }
+
+ if (extra_hosts && s) {
+ uint64_t *val = g_malloc(sizeof(*val));
+ *val = cpu_to_le64(extra_hosts);
+ fw_cfg_add_file(s, "etc/extra-pci-roots", val, sizeof(*val));
+ }
+}
+
static void fw_cfg_machine_reset(void *opaque)
{
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+ FWCfgState *s = opaque;
void *ptr;
size_t len;
- FWCfgState *s = opaque;
- char *bootindex = get_boot_devices_list(&len);
+ char *buf;
- ptr = fw_cfg_modify_file(s, "bootorder", (uint8_t *)bootindex, len);
+ buf = get_boot_devices_list(&len);
+ ptr = fw_cfg_modify_file(s, "bootorder", (uint8_t *)buf, len);
g_free(ptr);
+
+ if (!mc->legacy_fw_cfg_order) {
+ buf = get_boot_devices_lchs_list(&len);
+ ptr = fw_cfg_modify_file(s, "bios-geometry", (uint8_t *)buf, len);
+ g_free(ptr);
+ }
}
static void fw_cfg_machine_ready(struct Notifier *n, void *data)
qemu_register_reset(fw_cfg_machine_reset, s);
}
-
+static Property fw_cfg_properties[] = {
+ DEFINE_PROP_BOOL("acpi-mr-restore", FWCfgState, acpi_mr_restore, true),
+ DEFINE_PROP_END_OF_LIST(),
+};
static void fw_cfg_common_realize(DeviceState *dev, Error **errp)
{
FWCfgState *s;
bool dma_requested = dma_iobase && dma_as;
- dev = qdev_create(NULL, TYPE_FW_CFG_IO);
+ dev = qdev_new(TYPE_FW_CFG_IO);
if (!dma_requested) {
qdev_prop_set_bit(dev, "dma_enabled", false);
}
object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG,
- OBJECT(dev), NULL);
- qdev_init_nofail(dev);
+ OBJECT(dev));
sbd = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sbd, &error_fatal);
ios = FW_CFG_IO(dev);
sysbus_add_io(sbd, iobase, &ios->comb_iomem);
FWCfgState *s;
bool dma_requested = dma_addr && dma_as;
- dev = qdev_create(NULL, TYPE_FW_CFG_MEM);
+ dev = qdev_new(TYPE_FW_CFG_MEM);
qdev_prop_set_uint32(dev, "data_width", data_width);
if (!dma_requested) {
qdev_prop_set_bit(dev, "dma_enabled", false);
}
object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG,
- OBJECT(dev), NULL);
- qdev_init_nofail(dev);
+ OBJECT(dev));
sbd = SYS_BUS_DEVICE(dev);
+ sysbus_realize_and_unref(sbd, &error_fatal);
sysbus_mmio_map(sbd, 0, ctl_addr);
sysbus_mmio_map(sbd, 1, data_addr);
dc->reset = fw_cfg_reset;
dc->vmsd = &vmstate_fw_cfg;
+
+ device_class_set_props(dc, fw_cfg_properties);
}
static const TypeInfo fw_cfg_info = {
static void fw_cfg_io_realize(DeviceState *dev, Error **errp)
{
+ ERRP_GUARD();
FWCfgIoState *s = FW_CFG_IO(dev);
- Error *local_err = NULL;
- fw_cfg_file_slots_allocate(FW_CFG(s), &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ fw_cfg_file_slots_allocate(FW_CFG(s), errp);
+ if (*errp) {
return;
}
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = fw_cfg_io_realize;
- dc->props = fw_cfg_io_properties;
+ device_class_set_props(dc, fw_cfg_io_properties);
}
static const TypeInfo fw_cfg_io_info = {
static void fw_cfg_mem_realize(DeviceState *dev, Error **errp)
{
+ ERRP_GUARD();
FWCfgMemState *s = FW_CFG_MEM(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
const MemoryRegionOps *data_ops = &fw_cfg_data_mem_ops;
- Error *local_err = NULL;
- fw_cfg_file_slots_allocate(FW_CFG(s), &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ fw_cfg_file_slots_allocate(FW_CFG(s), errp);
+ if (*errp) {
return;
}
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = fw_cfg_mem_realize;
- dc->props = fw_cfg_mem_properties;
+ device_class_set_props(dc, fw_cfg_mem_properties);
}
static const TypeInfo fw_cfg_mem_info = {
.class_init = fw_cfg_mem_class_init,
};
-
static void fw_cfg_register_types(void)
{
type_register_static(&fw_cfg_info);