diff options
Diffstat (limited to 'hw/core')
40 files changed, 2227 insertions, 832 deletions
diff --git a/hw/core/Kconfig b/hw/core/Kconfig index 24411f5..d1bdf76 100644 --- a/hw/core/Kconfig +++ b/hw/core/Kconfig @@ -34,3 +34,7 @@ config REGISTER config SPLIT_IRQ bool + +config EIF + bool + depends on LIBCBOR && GNUTLS diff --git a/hw/core/bus.c b/hw/core/bus.c index b9d8949..bddfc22 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -232,7 +232,7 @@ static char *default_bus_get_fw_dev_path(DeviceState *dev) return g_strdup(object_get_typename(OBJECT(dev))); } -static void bus_class_init(ObjectClass *class, void *data) +static void bus_class_init(ObjectClass *class, const void *data) { BusClass *bc = BUS_CLASS(class); ResettableClass *rc = RESETTABLE_CLASS(class); @@ -260,7 +260,7 @@ static const TypeInfo bus_info = { .instance_init = qbus_initfn, .instance_finalize = qbus_finalize, .class_init = bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_RESETTABLE_INTERFACE }, { } }, diff --git a/hw/core/clock.c b/hw/core/clock.c index e212865..9c90676 100644 --- a/hw/core/clock.c +++ b/hw/core/clock.c @@ -13,6 +13,8 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qapi/visitor.h" +#include "system/qtest.h" #include "hw/clock.h" #include "trace.h" @@ -42,16 +44,12 @@ Clock *clock_new(Object *parent, const char *name) void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque, unsigned int events) { + assert(OBJECT(clk)->parent); clk->callback = cb; clk->callback_opaque = opaque; clk->callback_events = events; } -void clock_clear_callback(Clock *clk) -{ - clock_set_callback(clk, NULL, NULL, 0); -} - bool clock_set(Clock *clk, uint64_t period) { if (clk->period == period) { @@ -158,6 +156,25 @@ bool clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider) return true; } +static void clock_period_prop_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + Clock *clk = CLOCK(obj); + uint64_t period = clock_get(clk); + visit_type_uint64(v, name, &period, errp); +} + +static void clock_unparent(Object *obj) +{ + /* + * Callback are registered by the parent, which might die anytime after + * it's unparented the children. Avoid having a callback to a deleted + * object in case the clock is still referenced somewhere else (eg: by + * a clock output). + */ + clock_set_callback(CLOCK(obj), NULL, NULL, 0); +} + static void clock_initfn(Object *obj) { Clock *clk = CLOCK(obj); @@ -166,6 +183,11 @@ static void clock_initfn(Object *obj) clk->divider = 1; QLIST_INIT(&clk->children); + + if (qtest_enabled()) { + object_property_add(obj, "qtest-clock-period", "uint64", + clock_period_prop_get, NULL, NULL, NULL); + } } static void clock_finalizefn(Object *obj) @@ -184,11 +206,17 @@ static void clock_finalizefn(Object *obj) g_free(clk->canonical_path); } +static void clock_class_init(ObjectClass *klass, const void *data) +{ + klass->unparent = clock_unparent; +} + static const TypeInfo clock_info = { .name = TYPE_CLOCK, .parent = TYPE_OBJECT, .instance_size = sizeof(Clock), .instance_init = clock_initfn, + .class_init = clock_class_init, .instance_finalize = clock_finalizefn, }; diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index d2e3e45..39e674a 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -21,12 +21,16 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/core/cpu.h" -#include "sysemu/hw_accel.h" +#include "system/hw_accel.h" #include "qemu/log.h" #include "qemu/main-loop.h" +#include "qemu/lockcnt.h" +#include "qemu/error-report.h" +#include "qemu/qemu-print.h" +#include "qemu/target-info.h" #include "exec/log.h" #include "exec/gdbstub.h" -#include "sysemu/tcg.h" +#include "system/tcg.h" #include "hw/boards.h" #include "hw/qdev-properties.h" #include "trace.h" @@ -39,9 +43,7 @@ CPUState *cpu_by_arch_id(int64_t id) CPUState *cpu; CPU_FOREACH(cpu) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->get_arch_id(cpu) == id) { + if (cpu->cc->get_arch_id(cpu) == id) { return cpu; } } @@ -100,11 +102,9 @@ static int cpu_common_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg) void cpu_dump_state(CPUState *cpu, FILE *f, int flags) { - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->dump_state) { + if (cpu->cc->dump_state) { cpu_synchronize_state(cpu); - cc->dump_state(cpu, f, flags); + cpu->cc->dump_state(cpu, f, flags); } } @@ -118,11 +118,10 @@ void cpu_reset(CPUState *cpu) static void cpu_common_reset_hold(Object *obj, ResetType type) { CPUState *cpu = CPU(obj); - CPUClass *cc = CPU_GET_CLASS(cpu); if (qemu_loglevel_mask(CPU_LOG_RESET)) { qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); - log_cpu_state(cpu, cc->reset_dump_flags); + log_cpu_state(cpu, cpu->cc->reset_dump_flags); } cpu->interrupt_request = 0; @@ -138,11 +137,6 @@ static void cpu_common_reset_hold(Object *obj, ResetType type) cpu_exec_reset_hold(cpu); } -static bool cpu_common_has_work(CPUState *cs) -{ - return false; -} - ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) { ObjectClass *oc; @@ -161,6 +155,21 @@ ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) return NULL; } +char *cpu_model_from_type(const char *typename) +{ + g_autofree char *suffix = g_strdup_printf("-%s", target_cpu_type()); + + if (!object_class_by_name(typename)) { + return NULL; + } + + if (g_str_has_suffix(typename, suffix)) { + return g_strndup(typename, strlen(typename) - strlen(suffix)); + } + + return g_strdup(typename); +} + static void cpu_common_parse_features(const char *typename, char *features, Error **errp) { @@ -192,6 +201,49 @@ static void cpu_common_parse_features(const char *typename, char *features, } } +const char *parse_cpu_option(const char *cpu_option) +{ + ObjectClass *oc; + CPUClass *cc; + gchar **model_pieces; + const char *cpu_type; + + model_pieces = g_strsplit(cpu_option, ",", 2); + if (!model_pieces[0]) { + error_report("-cpu option cannot be empty"); + exit(1); + } + + oc = cpu_class_by_name(target_cpu_type(), model_pieces[0]); + if (oc == NULL) { + error_report("unable to find CPU model '%s'", model_pieces[0]); + g_strfreev(model_pieces); + exit(EXIT_FAILURE); + } + + cpu_type = object_class_get_name(oc); + cc = CPU_CLASS(oc); + cc->parse_features(cpu_type, model_pieces[1], &error_fatal); + g_strfreev(model_pieces); + return cpu_type; +} + +bool cpu_exec_realizefn(CPUState *cpu, Error **errp) +{ + if (!accel_cpu_common_realize(cpu, errp)) { + return false; + } + + gdb_init_cpu(cpu); + + /* Wait until cpu initialization complete before exposing cpu. */ + cpu_list_add(cpu); + + cpu_vmstate_register(cpu); + + return true; +} + static void cpu_common_realizefn(DeviceState *dev, Error **errp) { CPUState *cpu = CPU(dev); @@ -233,18 +285,34 @@ static void cpu_common_unrealizefn(DeviceState *dev) cpu_exec_unrealizefn(cpu); } +void cpu_exec_unrealizefn(CPUState *cpu) +{ + cpu_vmstate_unregister(cpu); + + cpu_list_remove(cpu); + /* + * Now that the vCPU has been removed from the RCU list, we can call + * accel_cpu_common_unrealize, which may free fields using call_rcu. + */ + accel_cpu_common_unrealize(cpu); +} + static void cpu_common_initfn(Object *obj) { CPUState *cpu = CPU(obj); - gdb_init_cpu(cpu); + cpu_exec_class_post_init(CPU_GET_CLASS(obj)); + + /* cache the cpu class for the hotpath */ + cpu->cc = CPU_GET_CLASS(cpu); + cpu->cpu_index = UNASSIGNED_CPU_INDEX; cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX; + cpu->as = NULL; + cpu->num_ases = 0; /* user-mode doesn't have configurable SMP topology */ /* the default value is changed by qemu_init_vcpu() for system-mode */ - cpu->nr_cores = 1; cpu->nr_threads = 1; - cpu->cflags_next_tb = -1; /* allocate storage for thread info, initialise condition variables */ cpu->thread = g_new0(QemuThread, 1); @@ -282,7 +350,10 @@ static void cpu_common_finalize(Object *obj) } #endif free_queued_cpu_work(cpu); - g_array_free(cpu->gdb_regs, TRUE); + /* If cleanup didn't happen in context to gdb_unregister_coprocessor_all */ + if (cpu->gdb_regs) { + g_array_free(cpu->gdb_regs, TRUE); + } qemu_lockcnt_destroy(&cpu->in_ioctl_lock); qemu_mutex_destroy(&cpu->work_mutex); qemu_cond_destroy(cpu->halt_cond); @@ -295,7 +366,7 @@ static int64_t cpu_common_get_arch_id(CPUState *cpu) return cpu->cpu_index; } -static void cpu_common_class_init(ObjectClass *klass, void *data) +static void cpu_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -303,7 +374,6 @@ static void cpu_common_class_init(ObjectClass *klass, void *data) k->parse_features = cpu_common_parse_features; k->get_arch_id = cpu_common_get_arch_id; - k->has_work = cpu_common_has_work; k->gdb_read_register = cpu_common_gdb_read_register; k->gdb_write_register = cpu_common_gdb_write_register; set_bit(DEVICE_CATEGORY_CPU, dc->categories); @@ -335,3 +405,32 @@ static void cpu_register_types(void) } type_init(cpu_register_types) + +static void cpu_list_entry(gpointer data, gpointer user_data) +{ + CPUClass *cc = CPU_CLASS(OBJECT_CLASS(data)); + const char *typename = object_class_get_name(OBJECT_CLASS(data)); + g_autofree char *model = cpu_model_from_type(typename); + + if (cc->deprecation_note) { + qemu_printf(" %s (deprecated)\n", model); + } else { + qemu_printf(" %s\n", model); + } +} + +void list_cpus(void) +{ + CPUClass *cc = CPU_CLASS(object_class_by_name(target_cpu_type())); + + if (cc->list_cpus) { + cc->list_cpus(); + } else { + GSList *list; + + list = object_class_get_list_sorted(TYPE_CPU, false); + qemu_printf("Available CPUs:\n"); + g_slist_foreach(list, cpu_list_entry, NULL); + g_slist_free(list); + } +} diff --git a/hw/core/cpu-sysemu.c b/hw/core/cpu-sysemu.c deleted file mode 100644 index 2a9a2a4..0000000 --- a/hw/core/cpu-sysemu.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - * QEMU CPU model (system emulation specific) - * - * Copyright (c) 2012-2014 SUSE LINUX Products GmbH - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see - * <http://www.gnu.org/licenses/gpl-2.0.html> - */ - -#include "qemu/osdep.h" -#include "qapi/error.h" -#include "exec/tswap.h" -#include "hw/core/sysemu-cpu-ops.h" - -bool cpu_paging_enabled(const CPUState *cpu) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->get_paging_enabled) { - return cc->sysemu_ops->get_paging_enabled(cpu); - } - - return false; -} - -bool cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, - Error **errp) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->get_memory_mapping) { - return cc->sysemu_ops->get_memory_mapping(cpu, list, errp); - } - - error_setg(errp, "Obtaining memory mappings is unsupported on this CPU."); - return false; -} - -hwaddr cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, - MemTxAttrs *attrs) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->get_phys_page_attrs_debug) { - return cc->sysemu_ops->get_phys_page_attrs_debug(cpu, addr, attrs); - } - /* Fallback for CPUs which don't implement the _attrs_ hook */ - *attrs = MEMTXATTRS_UNSPECIFIED; - return cc->sysemu_ops->get_phys_page_debug(cpu, addr); -} - -hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr) -{ - MemTxAttrs attrs = {}; - - return cpu_get_phys_page_attrs_debug(cpu, addr, &attrs); -} - -int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs) -{ - int ret = 0; - - if (cpu->cc->sysemu_ops->asidx_from_attrs) { - ret = cpu->cc->sysemu_ops->asidx_from_attrs(cpu, attrs); - assert(ret < cpu->num_ases && ret >= 0); - } - return ret; -} - -int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, - void *opaque) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf32_qemunote) { - return 0; - } - return (*cc->sysemu_ops->write_elf32_qemunote)(f, cpu, opaque); -} - -int cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, - int cpuid, void *opaque) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf32_note) { - return -1; - } - return (*cc->sysemu_ops->write_elf32_note)(f, cpu, cpuid, opaque); -} - -int cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, - void *opaque) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf64_qemunote) { - return 0; - } - return (*cc->sysemu_ops->write_elf64_qemunote)(f, cpu, opaque); -} - -int cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, - int cpuid, void *opaque) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (!cc->sysemu_ops->write_elf64_note) { - return -1; - } - return (*cc->sysemu_ops->write_elf64_note)(f, cpu, cpuid, opaque); -} - -bool cpu_virtio_is_big_endian(CPUState *cpu) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - - if (cc->sysemu_ops->virtio_is_big_endian) { - return cc->sysemu_ops->virtio_is_big_endian(cpu); - } - return target_words_bigendian(); -} - -GuestPanicInformation *cpu_get_crash_info(CPUState *cpu) -{ - CPUClass *cc = CPU_GET_CLASS(cpu); - GuestPanicInformation *res = NULL; - - if (cc->sysemu_ops->get_crash_info) { - res = cc->sysemu_ops->get_crash_info(cpu); - } - return res; -} diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c new file mode 100644 index 0000000..3c84176 --- /dev/null +++ b/hw/core/cpu-system.c @@ -0,0 +1,305 @@ +/* + * QEMU CPU model (system specific) + * + * Copyright (c) 2012-2014 SUSE LINUX Products GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see + * <http://www.gnu.org/licenses/gpl-2.0.html> + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "system/address-spaces.h" +#include "exec/cputlb.h" +#include "system/memory.h" +#include "exec/tb-flush.h" +#include "exec/tswap.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "hw/core/sysemu-cpu-ops.h" +#include "migration/vmstate.h" +#include "system/tcg.h" + +bool cpu_has_work(CPUState *cpu) +{ + return cpu->cc->sysemu_ops->has_work(cpu); +} + +bool cpu_paging_enabled(const CPUState *cpu) +{ + if (cpu->cc->sysemu_ops->get_paging_enabled) { + return cpu->cc->sysemu_ops->get_paging_enabled(cpu); + } + + return false; +} + +bool cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list, + Error **errp) +{ + if (cpu->cc->sysemu_ops->get_memory_mapping) { + return cpu->cc->sysemu_ops->get_memory_mapping(cpu, list, errp); + } + + error_setg(errp, "Obtaining memory mappings is unsupported on this CPU."); + return false; +} + +hwaddr cpu_get_phys_page_attrs_debug(CPUState *cpu, vaddr addr, + MemTxAttrs *attrs) +{ + hwaddr paddr; + + if (cpu->cc->sysemu_ops->get_phys_page_attrs_debug) { + paddr = cpu->cc->sysemu_ops->get_phys_page_attrs_debug(cpu, addr, + attrs); + } else { + /* Fallback for CPUs which don't implement the _attrs_ hook */ + *attrs = MEMTXATTRS_UNSPECIFIED; + paddr = cpu->cc->sysemu_ops->get_phys_page_debug(cpu, addr); + } + /* Indicate that this is a debug access. */ + attrs->debug = 1; + return paddr; +} + +hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr) +{ + MemTxAttrs attrs = {}; + + return cpu_get_phys_page_attrs_debug(cpu, addr, &attrs); +} + +int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs) +{ + int ret = 0; + + if (cpu->cc->sysemu_ops->asidx_from_attrs) { + ret = cpu->cc->sysemu_ops->asidx_from_attrs(cpu, attrs); + assert(ret < cpu->num_ases && ret >= 0); + } + return ret; +} + +int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, + void *opaque) +{ + if (!cpu->cc->sysemu_ops->write_elf32_qemunote) { + return 0; + } + return (*cpu->cc->sysemu_ops->write_elf32_qemunote)(f, cpu, opaque); +} + +int cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cpu, + int cpuid, void *opaque) +{ + if (!cpu->cc->sysemu_ops->write_elf32_note) { + return -1; + } + return (*cpu->cc->sysemu_ops->write_elf32_note)(f, cpu, cpuid, opaque); +} + +int cpu_write_elf64_qemunote(WriteCoreDumpFunction f, CPUState *cpu, + void *opaque) +{ + if (!cpu->cc->sysemu_ops->write_elf64_qemunote) { + return 0; + } + return (*cpu->cc->sysemu_ops->write_elf64_qemunote)(f, cpu, opaque); +} + +int cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, + int cpuid, void *opaque) +{ + if (!cpu->cc->sysemu_ops->write_elf64_note) { + return -1; + } + return (*cpu->cc->sysemu_ops->write_elf64_note)(f, cpu, cpuid, opaque); +} + +bool cpu_virtio_is_big_endian(CPUState *cpu) +{ + if (cpu->cc->sysemu_ops->virtio_is_big_endian) { + return cpu->cc->sysemu_ops->virtio_is_big_endian(cpu); + } + return target_big_endian(); +} + +GuestPanicInformation *cpu_get_crash_info(CPUState *cpu) +{ + GuestPanicInformation *res = NULL; + + if (cpu->cc->sysemu_ops->get_crash_info) { + res = cpu->cc->sysemu_ops->get_crash_info(cpu); + } + return res; +} + +static const Property cpu_system_props[] = { + /* + * Create a memory property for system CPU object, so users can + * wire up its memory. The default if no link is set up is to use + * the system address space. + */ + DEFINE_PROP_LINK("memory", CPUState, memory, TYPE_MEMORY_REGION, + MemoryRegion *), +}; + +static bool cpu_get_start_powered_off(Object *obj, Error **errp) +{ + CPUState *cpu = CPU(obj); + return cpu->start_powered_off; +} + +static void cpu_set_start_powered_off(Object *obj, bool value, Error **errp) +{ + CPUState *cpu = CPU(obj); + cpu->start_powered_off = value; +} + +void cpu_class_init_props(DeviceClass *dc) +{ + ObjectClass *oc = OBJECT_CLASS(dc); + + /* + * We can't use DEFINE_PROP_BOOL in the Property array for this + * property, because we want this to be settable after realize. + */ + object_class_property_add_bool(oc, "start-powered-off", + cpu_get_start_powered_off, + cpu_set_start_powered_off); + + device_class_set_props(dc, cpu_system_props); +} + +void cpu_exec_class_post_init(CPUClass *cc) +{ + /* Check mandatory SysemuCPUOps handlers */ + g_assert(cc->sysemu_ops->has_work); +} + +void cpu_exec_initfn(CPUState *cpu) +{ + cpu->memory = get_system_memory(); + object_ref(OBJECT(cpu->memory)); +} + +static int cpu_common_post_load(void *opaque, int version_id) +{ + if (tcg_enabled()) { + CPUState *cpu = opaque; + + /* + * 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the + * version_id is increased. + */ + cpu->interrupt_request &= ~0x01; + + tlb_flush(cpu); + + /* + * loadvm has just updated the content of RAM, bypassing the + * usual mechanisms that ensure we flush TBs for writes to + * memory we've translated code from. So we must flush all TBs, + * which will now be stale. + */ + tb_flush(cpu); + } + + return 0; +} + +static int cpu_common_pre_load(void *opaque) +{ + CPUState *cpu = opaque; + + cpu->exception_index = -1; + + return 0; +} + +static bool cpu_common_exception_index_needed(void *opaque) +{ + CPUState *cpu = opaque; + + return tcg_enabled() && cpu->exception_index != -1; +} + +static const VMStateDescription vmstate_cpu_common_exception_index = { + .name = "cpu_common/exception_index", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_common_exception_index_needed, + .fields = (const VMStateField[]) { + VMSTATE_INT32(exception_index, CPUState), + VMSTATE_END_OF_LIST() + } +}; + +static bool cpu_common_crash_occurred_needed(void *opaque) +{ + CPUState *cpu = opaque; + + return cpu->crash_occurred; +} + +static const VMStateDescription vmstate_cpu_common_crash_occurred = { + .name = "cpu_common/crash_occurred", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_common_crash_occurred_needed, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(crash_occurred, CPUState), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_cpu_common = { + .name = "cpu_common", + .version_id = 1, + .minimum_version_id = 1, + .pre_load = cpu_common_pre_load, + .post_load = cpu_common_post_load, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(halted, CPUState), + VMSTATE_UINT32(interrupt_request, CPUState), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_cpu_common_exception_index, + &vmstate_cpu_common_crash_occurred, + NULL + } +}; + +void cpu_vmstate_register(CPUState *cpu) +{ + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_register(NULL, cpu->cpu_index, &vmstate_cpu_common, cpu); + } + if (cpu->cc->sysemu_ops->legacy_vmsd != NULL) { + vmstate_register(NULL, cpu->cpu_index, + cpu->cc->sysemu_ops->legacy_vmsd, cpu); + } +} + +void cpu_vmstate_unregister(CPUState *cpu) +{ + if (cpu->cc->sysemu_ops->legacy_vmsd != NULL) { + vmstate_unregister(NULL, cpu->cc->sysemu_ops->legacy_vmsd, cpu); + } + if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { + vmstate_unregister(NULL, &vmstate_cpu_common, cpu); + } +} diff --git a/hw/core/cpu-user.c b/hw/core/cpu-user.c new file mode 100644 index 0000000..7176791 --- /dev/null +++ b/hw/core/cpu-user.c @@ -0,0 +1,49 @@ +/* + * QEMU CPU model (user specific) + * + * Copyright (c) Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/qdev-core.h" +#include "hw/qdev-properties.h" +#include "hw/core/cpu.h" +#include "migration/vmstate.h" + +static const Property cpu_user_props[] = { + /* + * Create a property for the user-only object, so users can + * adjust prctl(PR_SET_UNALIGN) from the command-line. + * Has no effect if the target does not support the feature. + */ + DEFINE_PROP_BOOL("prctl-unalign-sigbus", CPUState, + prctl_unalign_sigbus, false), +}; + +void cpu_class_init_props(DeviceClass *dc) +{ + device_class_set_props(dc, cpu_user_props); +} + +void cpu_exec_class_post_init(CPUClass *cc) +{ + /* nothing to do */ +} + +void cpu_exec_initfn(CPUState *cpu) +{ + /* nothing to do */ +} + +void cpu_vmstate_register(CPUState *cpu) +{ + assert(qdev_get_vmsd(DEVICE(cpu)) == NULL || + qdev_get_vmsd(DEVICE(cpu))->unmigratable); +} + +void cpu_vmstate_unregister(CPUState *cpu) +{ + /* nothing to do */ +} diff --git a/hw/core/eif.c b/hw/core/eif.c new file mode 100644 index 0000000..513caec --- /dev/null +++ b/hw/core/eif.c @@ -0,0 +1,709 @@ +/* + * EIF (Enclave Image Format) related helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "qapi/error.h" +#include "crypto/hash.h" +#include "crypto/x509-utils.h" +#include <zlib.h> /* for crc32 */ +#include <cbor.h> + +#include "hw/core/eif.h" + +#define MAX_SECTIONS 32 + +/* members are ordered according to field order in .eif file */ +typedef struct EifHeader { + uint8_t magic[4]; /* must be .eif in ascii i.e., [46, 101, 105, 102] */ + uint16_t version; + uint16_t flags; + uint64_t default_memory; + uint64_t default_cpus; + uint16_t reserved; + uint16_t section_cnt; + uint64_t section_offsets[MAX_SECTIONS]; + uint64_t section_sizes[MAX_SECTIONS]; + uint32_t unused; + uint32_t eif_crc32; +} QEMU_PACKED EifHeader; + +/* members are ordered according to field order in .eif file */ +typedef struct EifSectionHeader { + /* + * 0 = invalid, 1 = kernel, 2 = cmdline, 3 = ramdisk, 4 = signature, + * 5 = metadata + */ + uint16_t section_type; + uint16_t flags; + uint64_t section_size; +} QEMU_PACKED EifSectionHeader; + +enum EifSectionTypes { + EIF_SECTION_INVALID = 0, + EIF_SECTION_KERNEL = 1, + EIF_SECTION_CMDLINE = 2, + EIF_SECTION_RAMDISK = 3, + EIF_SECTION_SIGNATURE = 4, + EIF_SECTION_METADATA = 5, + EIF_SECTION_MAX = 6, +}; + +static const char *section_type_to_string(uint16_t type) +{ + const char *str; + switch (type) { + case EIF_SECTION_INVALID: + str = "invalid"; + break; + case EIF_SECTION_KERNEL: + str = "kernel"; + break; + case EIF_SECTION_CMDLINE: + str = "cmdline"; + break; + case EIF_SECTION_RAMDISK: + str = "ramdisk"; + break; + case EIF_SECTION_SIGNATURE: + str = "signature"; + break; + case EIF_SECTION_METADATA: + str = "metadata"; + break; + default: + str = "unknown"; + break; + } + + return str; +} + +static bool read_eif_header(FILE *f, EifHeader *header, uint32_t *crc, + Error **errp) +{ + size_t got; + size_t header_size = sizeof(*header); + + got = fread(header, 1, header_size, f); + if (got != header_size) { + error_setg(errp, "Failed to read EIF header"); + return false; + } + + if (memcmp(header->magic, ".eif", 4) != 0) { + error_setg(errp, "Invalid EIF image. Magic mismatch."); + return false; + } + + /* Exclude header->eif_crc32 field from CRC calculation */ + *crc = crc32(*crc, (uint8_t *)header, header_size - 4); + + header->version = be16_to_cpu(header->version); + header->flags = be16_to_cpu(header->flags); + header->default_memory = be64_to_cpu(header->default_memory); + header->default_cpus = be64_to_cpu(header->default_cpus); + header->reserved = be16_to_cpu(header->reserved); + header->section_cnt = be16_to_cpu(header->section_cnt); + + for (int i = 0; i < MAX_SECTIONS; ++i) { + header->section_offsets[i] = be64_to_cpu(header->section_offsets[i]); + } + + for (int i = 0; i < MAX_SECTIONS; ++i) { + header->section_sizes[i] = be64_to_cpu(header->section_sizes[i]); + if (header->section_sizes[i] > SSIZE_MAX) { + error_setg(errp, "Invalid EIF image. Section size out of bounds"); + return false; + } + } + + header->unused = be32_to_cpu(header->unused); + header->eif_crc32 = be32_to_cpu(header->eif_crc32); + return true; +} + +static bool read_eif_section_header(FILE *f, EifSectionHeader *section_header, + uint32_t *crc, Error **errp) +{ + size_t got; + size_t section_header_size = sizeof(*section_header); + + got = fread(section_header, 1, section_header_size, f); + if (got != section_header_size) { + error_setg(errp, "Failed to read EIF section header"); + return false; + } + + *crc = crc32(*crc, (uint8_t *)section_header, section_header_size); + + section_header->section_type = be16_to_cpu(section_header->section_type); + section_header->flags = be16_to_cpu(section_header->flags); + section_header->section_size = be64_to_cpu(section_header->section_size); + return true; +} + +/* + * Upon success, the caller is responsible for unlinking and freeing *tmp_path. + */ +static bool get_tmp_file(const char *template, char **tmp_path, Error **errp) +{ + int tmp_fd; + + *tmp_path = NULL; + tmp_fd = g_file_open_tmp(template, tmp_path, NULL); + if (tmp_fd < 0 || *tmp_path == NULL) { + error_setg(errp, "Failed to create temporary file for template %s", + template); + return false; + } + + close(tmp_fd); + return true; +} + +static void safe_fclose(FILE *f) +{ + if (f) { + fclose(f); + } +} + +static void safe_unlink(char *f) +{ + if (f) { + unlink(f); + } +} + +/* + * Upon success, the caller is reponsible for unlinking and freeing *kernel_path + */ +static bool read_eif_kernel(FILE *f, uint64_t size, char **kernel_path, + QCryptoHash *hash0, QCryptoHash *hash1, + uint32_t *crc, Error **errp) +{ + size_t got; + FILE *tmp_file = NULL; + uint8_t *kernel = g_try_malloc(size); + if (!kernel) { + error_setg(errp, "Out of memory reading kernel section"); + goto cleanup; + } + + *kernel_path = NULL; + if (!get_tmp_file("eif-kernel-XXXXXX", kernel_path, errp)) { + goto cleanup; + } + + tmp_file = fopen(*kernel_path, "wb"); + if (tmp_file == NULL) { + error_setg_errno(errp, errno, "Failed to open temporary file %s", + *kernel_path); + goto cleanup; + } + + got = fread(kernel, 1, size, f); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to read EIF kernel section data"); + goto cleanup; + } + + got = fwrite(kernel, 1, size, tmp_file); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to write EIF kernel section data to temporary" + " file"); + goto cleanup; + } + + *crc = crc32(*crc, kernel, size); + if (qcrypto_hash_update(hash0, (char *)kernel, size, errp) != 0 || + qcrypto_hash_update(hash1, (char *)kernel, size, errp) != 0) { + goto cleanup; + } + g_free(kernel); + fclose(tmp_file); + + return true; + + cleanup: + safe_fclose(tmp_file); + + safe_unlink(*kernel_path); + g_free(*kernel_path); + *kernel_path = NULL; + + g_free(kernel); + return false; +} + +static bool read_eif_cmdline(FILE *f, uint64_t size, char *cmdline, + QCryptoHash *hash0, QCryptoHash *hash1, + uint32_t *crc, Error **errp) +{ + size_t got = fread(cmdline, 1, size, f); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to read EIF cmdline section data"); + return false; + } + + *crc = crc32(*crc, (uint8_t *)cmdline, size); + if (qcrypto_hash_update(hash0, cmdline, size, errp) != 0 || + qcrypto_hash_update(hash1, cmdline, size, errp) != 0) { + return false; + } + return true; +} + +static bool read_eif_ramdisk(FILE *eif, FILE *initrd, uint64_t size, + QCryptoHash *hash0, QCryptoHash *h, uint32_t *crc, + Error **errp) +{ + size_t got; + bool ret = false; + uint8_t *ramdisk = g_try_malloc(size); + if (!ramdisk) { + error_setg(errp, "Out of memory reading initrd section"); + goto cleanup; + } + + got = fread(ramdisk, 1, size, eif); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to read EIF ramdisk section data"); + goto cleanup; + } + + got = fwrite(ramdisk, 1, size, initrd); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to write EIF ramdisk data to temporary file"); + goto cleanup; + } + + *crc = crc32(*crc, ramdisk, size); + if (qcrypto_hash_update(hash0, (char *)ramdisk, size, errp) != 0 || + qcrypto_hash_update(h, (char *)ramdisk, size, errp) != 0) { + goto cleanup; + } + ret = true; + + cleanup: + g_free(ramdisk); + return ret; +} + +static bool get_signature_fingerprint_sha384(FILE *eif, uint64_t size, + uint8_t *sha384, + uint32_t *crc, + Error **errp) +{ + size_t got; + g_autofree uint8_t *sig = NULL; + g_autofree uint8_t *cert = NULL; + cbor_item_t *item = NULL; + cbor_item_t *pcr0 = NULL; + size_t len; + size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; + struct cbor_pair *pair; + struct cbor_load_result result; + bool ret = false; + + sig = g_try_malloc(size); + if (!sig) { + error_setg(errp, "Out of memory reading signature section"); + goto cleanup; + } + + got = fread(sig, 1, size, eif); + if ((uint64_t) got != size) { + error_setg(errp, "Failed to read EIF signature section data"); + goto cleanup; + } + + *crc = crc32(*crc, sig, size); + + item = cbor_load(sig, size, &result); + if (!item || result.error.code != CBOR_ERR_NONE) { + error_setg(errp, "Failed to load signature section data as CBOR"); + goto cleanup; + } + if (!cbor_isa_array(item) || cbor_array_size(item) < 1) { + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + pcr0 = cbor_array_get(item, 0); + if (!pcr0) { + error_setg(errp, "Failed to get PCR0 signature"); + goto cleanup; + } + if (!cbor_isa_map(pcr0) || cbor_map_size(pcr0) != 2) { + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + pair = cbor_map_handle(pcr0); + if (!cbor_isa_string(pair->key) || cbor_string_length(pair->key) != 19 || + memcmp(cbor_string_handle(pair->key), "signing_certificate", 19) != 0) { + error_setg(errp, "Invalid signautre CBOR"); + goto cleanup; + } + if (!cbor_isa_array(pair->value)) { + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + len = cbor_array_size(pair->value); + if (len == 0) { + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + cert = g_try_malloc(len); + if (!cert) { + error_setg(errp, "Out of memory reading signature section"); + goto cleanup; + } + + for (int i = 0; i < len; ++i) { + cbor_item_t *tmp = cbor_array_get(pair->value, i); + if (!tmp) { + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + if (!cbor_isa_uint(tmp) || cbor_int_get_width(tmp) != CBOR_INT_8) { + cbor_decref(&tmp); + error_setg(errp, "Invalid signature CBOR"); + goto cleanup; + } + cert[i] = cbor_get_uint8(tmp); + cbor_decref(&tmp); + } + + if (qcrypto_get_x509_cert_fingerprint(cert, len, QCRYPTO_HASH_ALGO_SHA384, + sha384, &hash_len, errp)) { + goto cleanup; + } + + ret = true; + + cleanup: + if (pcr0) { + cbor_decref(&pcr0); + } + if (item) { + cbor_decref(&item); + } + return ret; +} + +/* Expects file to have offset 0 before this function is called */ +static long get_file_size(FILE *f, Error **errp) +{ + long size; + + if (fseek(f, 0, SEEK_END) != 0) { + error_setg_errno(errp, errno, "Failed to seek to the end of file"); + return -1; + } + + size = ftell(f); + if (size == -1) { + error_setg_errno(errp, errno, "Failed to get offset"); + return -1; + } + + if (fseek(f, 0, SEEK_SET) != 0) { + error_setg_errno(errp, errno, "Failed to seek back to the start"); + return -1; + } + + return size; +} + +static bool get_SHA384_hash(QCryptoHash *h, uint8_t *hash, Error **errp) +{ + size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; + return qcrypto_hash_finalize_bytes(h, &hash, &hash_len, errp) == 0; +} + +/* + * Upon success, the caller is reponsible for unlinking and freeing + * *kernel_path, *initrd_path and freeing *cmdline. + */ +bool read_eif_file(const char *eif_path, const char *machine_initrd, + char **kernel_path, char **initrd_path, char **cmdline, + uint8_t *image_hash, uint8_t *bootstrap_hash, + uint8_t *app_hash, uint8_t *fingerprint_hash, + bool *signature_found, Error **errp) +{ + FILE *f = NULL; + FILE *machine_initrd_f = NULL; + FILE *initrd_path_f = NULL; + long machine_initrd_size; + uint32_t crc = 0; + EifHeader eif_header; + bool seen_sections[EIF_SECTION_MAX] = {false}; + /* kernel + ramdisks + cmdline SHA384 hash */ + g_autoptr(QCryptoHash) hash0 = NULL; + /* kernel + boot ramdisk + cmdline SHA384 hash */ + g_autoptr(QCryptoHash) hash1 = NULL; + /* application ramdisk(s) SHA384 hash */ + g_autoptr(QCryptoHash) hash2 = NULL; + + *signature_found = false; + *kernel_path = *initrd_path = *cmdline = NULL; + + hash0 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); + if (!hash0) { + goto cleanup; + } + hash1 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); + if (!hash1) { + goto cleanup; + } + hash2 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); + if (!hash2) { + goto cleanup; + } + + f = fopen(eif_path, "rb"); + if (f == NULL) { + error_setg_errno(errp, errno, "Failed to open %s", eif_path); + goto cleanup; + } + + if (!read_eif_header(f, &eif_header, &crc, errp)) { + goto cleanup; + } + + if (eif_header.version < 4) { + error_setg(errp, "Expected EIF version 4 or greater"); + goto cleanup; + } + + if (eif_header.flags != 0) { + error_setg(errp, "Expected EIF flags to be 0"); + goto cleanup; + } + + if (eif_header.section_cnt > MAX_SECTIONS) { + error_setg(errp, "EIF header section count must not be greater than " + "%d but found %d", MAX_SECTIONS, eif_header.section_cnt); + goto cleanup; + } + + for (int i = 0; i < eif_header.section_cnt; ++i) { + EifSectionHeader hdr; + uint16_t section_type; + + if (eif_header.section_offsets[i] > OFF_MAX) { + error_setg(errp, "Invalid EIF image. Section offset out of bounds"); + goto cleanup; + } + if (fseek(f, eif_header.section_offsets[i], SEEK_SET) != 0) { + error_setg_errno(errp, errno, "Failed to offset to %" PRIu64 " in EIF file", + eif_header.section_offsets[i]); + goto cleanup; + } + + if (!read_eif_section_header(f, &hdr, &crc, errp)) { + goto cleanup; + } + + if (hdr.flags != 0) { + error_setg(errp, "Expected EIF section header flags to be 0"); + goto cleanup; + } + + if (eif_header.section_sizes[i] != hdr.section_size) { + error_setg(errp, "EIF section size mismatch between header and " + "section header: header %" PRIu64 ", section header %" PRIu64, + eif_header.section_sizes[i], + hdr.section_size); + goto cleanup; + } + + section_type = hdr.section_type; + + switch (section_type) { + case EIF_SECTION_KERNEL: + if (seen_sections[EIF_SECTION_KERNEL]) { + error_setg(errp, "Invalid EIF image. More than 1 kernel " + "section"); + goto cleanup; + } + + if (!read_eif_kernel(f, hdr.section_size, kernel_path, hash0, + hash1, &crc, errp)) { + goto cleanup; + } + + break; + case EIF_SECTION_CMDLINE: + { + uint64_t size; + if (seen_sections[EIF_SECTION_CMDLINE]) { + error_setg(errp, "Invalid EIF image. More than 1 cmdline " + "section"); + goto cleanup; + } + size = hdr.section_size; + *cmdline = g_try_malloc(size + 1); + if (!*cmdline) { + error_setg(errp, "Out of memory reading command line section"); + goto cleanup; + } + if (!read_eif_cmdline(f, size, *cmdline, hash0, hash1, &crc, + errp)) { + goto cleanup; + } + (*cmdline)[size] = '\0'; + + break; + } + case EIF_SECTION_RAMDISK: + { + QCryptoHash *h = hash2; + if (!seen_sections[EIF_SECTION_RAMDISK]) { + /* + * If this is the first time we are seeing a ramdisk section, + * we need to: + * 1) hash it into bootstrap (hash1) instead of app (hash2) + * along with image (hash0) + * 2) create the initrd temporary file. + */ + h = hash1; + if (!get_tmp_file("eif-initrd-XXXXXX", initrd_path, errp)) { + goto cleanup; + } + initrd_path_f = fopen(*initrd_path, "wb"); + if (initrd_path_f == NULL) { + error_setg_errno(errp, errno, "Failed to open file %s", + *initrd_path); + goto cleanup; + } + } + + if (!read_eif_ramdisk(f, initrd_path_f, hdr.section_size, hash0, h, + &crc, errp)) { + goto cleanup; + } + + break; + } + case EIF_SECTION_SIGNATURE: + *signature_found = true; + if (!get_signature_fingerprint_sha384(f, hdr.section_size, + fingerprint_hash, &crc, + errp)) { + goto cleanup; + } + break; + default: + /* other sections including invalid or unknown sections */ + { + uint8_t *buf; + size_t got; + uint64_t size = hdr.section_size; + buf = g_try_malloc(size); + if (!buf) { + error_setg(errp, "Out of memory reading unknown section"); + goto cleanup; + } + got = fread(buf, 1, size, f); + if ((uint64_t) got != size) { + g_free(buf); + error_setg(errp, "Failed to read EIF %s section data", + section_type_to_string(section_type)); + goto cleanup; + } + crc = crc32(crc, buf, size); + g_free(buf); + break; + } + } + + if (section_type < EIF_SECTION_MAX) { + seen_sections[section_type] = true; + } + } + + if (!seen_sections[EIF_SECTION_KERNEL]) { + error_setg(errp, "Invalid EIF image. No kernel section."); + goto cleanup; + } + if (!seen_sections[EIF_SECTION_CMDLINE]) { + error_setg(errp, "Invalid EIF image. No cmdline section."); + goto cleanup; + } + if (!seen_sections[EIF_SECTION_RAMDISK]) { + error_setg(errp, "Invalid EIF image. No ramdisk section."); + goto cleanup; + } + + if (eif_header.eif_crc32 != crc) { + error_setg(errp, "CRC mismatch. Expected %u but header has %u.", + crc, eif_header.eif_crc32); + goto cleanup; + } + + /* + * Let's append the initrd file from "-initrd" option if any. Although + * we pass the crc pointer to read_eif_ramdisk, it is not useful anymore. + * We have already done the crc mismatch check above this code. + */ + if (machine_initrd) { + machine_initrd_f = fopen(machine_initrd, "rb"); + if (machine_initrd_f == NULL) { + error_setg_errno(errp, errno, "Failed to open initrd file %s", + machine_initrd); + goto cleanup; + } + + machine_initrd_size = get_file_size(machine_initrd_f, errp); + if (machine_initrd_size == -1) { + goto cleanup; + } + + if (!read_eif_ramdisk(machine_initrd_f, initrd_path_f, + machine_initrd_size, hash0, hash2, &crc, errp)) { + goto cleanup; + } + } + + if (!get_SHA384_hash(hash0, image_hash, errp)) { + goto cleanup; + } + if (!get_SHA384_hash(hash1, bootstrap_hash, errp)) { + goto cleanup; + } + if (!get_SHA384_hash(hash2, app_hash, errp)) { + goto cleanup; + } + + fclose(f); + fclose(initrd_path_f); + safe_fclose(machine_initrd_f); + return true; + + cleanup: + safe_fclose(f); + safe_fclose(initrd_path_f); + safe_fclose(machine_initrd_f); + + safe_unlink(*kernel_path); + g_free(*kernel_path); + *kernel_path = NULL; + + safe_unlink(*initrd_path); + g_free(*initrd_path); + *initrd_path = NULL; + + g_free(*cmdline); + *cmdline = NULL; + + return false; +} diff --git a/hw/core/eif.h b/hw/core/eif.h new file mode 100644 index 0000000..fed3cb5 --- /dev/null +++ b/hw/core/eif.h @@ -0,0 +1,22 @@ +/* + * EIF (Enclave Image Format) related helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef HW_CORE_EIF_H +#define HW_CORE_EIF_H + +bool read_eif_file(const char *eif_path, const char *machine_initrd, + char **kernel_path, char **initrd_path, + char **kernel_cmdline, uint8_t *image_sha384, + uint8_t *bootstrap_sha384, uint8_t *app_sha384, + uint8_t *fingerprint_sha384, bool *signature_found, + Error **errp); + +#endif + diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index ea8628b..e72bbde 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -31,9 +31,8 @@ */ #include "qemu/osdep.h" -#include "exec/tswap.h" -#include "sysemu/dma.h" -#include "sysemu/reset.h" +#include "system/dma.h" +#include "system/reset.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/qdev-properties.h" @@ -48,11 +47,8 @@ static void generic_loader_reset(void *opaque) GenericLoaderState *s = GENERIC_LOADER(opaque); if (s->set_pc) { - CPUClass *cc = CPU_GET_CLASS(s->cpu); cpu_reset(s->cpu); - if (cc) { - cc->set_pc(s->cpu, s->addr); - } + cpu_set_pc(s->cpu, s->addr); } if (s->data_len) { @@ -66,7 +62,6 @@ static void generic_loader_realize(DeviceState *dev, Error **errp) { GenericLoaderState *s = GENERIC_LOADER(dev); hwaddr entry; - int big_endian; ssize_t size = 0; s->set_pc = false; @@ -134,14 +129,12 @@ static void generic_loader_realize(DeviceState *dev, Error **errp) s->cpu = first_cpu; } - big_endian = target_words_bigendian(); - if (s->file) { AddressSpace *as = s->cpu ? s->cpu->as : NULL; if (!s->force_raw) { size = load_elf_as(s->file, NULL, NULL, NULL, &entry, NULL, NULL, - NULL, big_endian, 0, 0, 0, as); + NULL, ELFDATANONE, 0, 0, 0, as); if (size < 0) { size = load_uimage_as(s->file, &entry, NULL, NULL, NULL, NULL, @@ -179,7 +172,7 @@ static void generic_loader_unrealize(DeviceState *dev) qemu_unregister_reset(generic_loader_reset, dev); } -static Property generic_loader_props[] = { +static const Property generic_loader_props[] = { DEFINE_PROP_UINT64("addr", GenericLoaderState, addr, 0), DEFINE_PROP_UINT64("data", GenericLoaderState, data, 0), DEFINE_PROP_UINT8("data-len", GenericLoaderState, data_len, 0), @@ -187,10 +180,9 @@ static Property generic_loader_props[] = { DEFINE_PROP_UINT32("cpu-num", GenericLoaderState, cpu_num, CPU_NONE), DEFINE_PROP_BOOL("force-raw", GenericLoaderState, force_raw, false), DEFINE_PROP_STRING("file", GenericLoaderState, file), - DEFINE_PROP_END_OF_LIST(), }; -static void generic_loader_class_init(ObjectClass *klass, void *data) +static void generic_loader_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/gpio.c b/hw/core/gpio.c index 80d07a6..6e32a8e 100644 --- a/hw/core/gpio.c +++ b/hw/core/gpio.c @@ -121,8 +121,7 @@ void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n, name ? name : "unnamed-gpio-out", n); if (input_pin && !OBJECT(input_pin)->parent) { /* We need a name for object_property_set_link to work */ - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), + object_property_add_child(machine_get_container("unattached"), "non-qdev-gpio[*]", OBJECT(input_pin)); } object_property_set_link(OBJECT(dev), propname, diff --git a/hw/core/guest-loader.c b/hw/core/guest-loader.c index 391c875..3db89d7 100644 --- a/hw/core/guest-loader.c +++ b/hw/core/guest-loader.c @@ -26,13 +26,13 @@ #include "qemu/osdep.h" #include "hw/core/cpu.h" -#include "sysemu/dma.h" +#include "system/dma.h" #include "hw/loader.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/module.h" #include "guest-loader.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include "hw/boards.h" /* @@ -111,15 +111,14 @@ static void guest_loader_realize(DeviceState *dev, Error **errp) loader_insert_platform_data(s, size, errp); } -static Property guest_loader_props[] = { +static const Property guest_loader_props[] = { DEFINE_PROP_UINT64("addr", GuestLoaderState, addr, 0), DEFINE_PROP_STRING("kernel", GuestLoaderState, kernel), DEFINE_PROP_STRING("bootargs", GuestLoaderState, args), DEFINE_PROP_STRING("initrd", GuestLoaderState, initrd), - DEFINE_PROP_END_OF_LIST(), }; -static void guest_loader_class_init(ObjectClass *klass, void *data) +static void guest_loader_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/irq.c b/hw/core/irq.c index 3f14e2d..6dd8d47 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -26,16 +26,6 @@ #include "hw/irq.h" #include "qom/object.h" -OBJECT_DECLARE_SIMPLE_TYPE(IRQState, IRQ) - -struct IRQState { - Object parent_obj; - - qemu_irq_handler handler; - void *opaque; - int n; -}; - void qemu_set_irq(qemu_irq irq, int level) { if (!irq) @@ -44,6 +34,29 @@ void qemu_set_irq(qemu_irq irq, int level) irq->handler(irq->opaque, irq->n, level); } +static void init_irq_fields(IRQState *irq, qemu_irq_handler handler, + void *opaque, int n) +{ + irq->handler = handler; + irq->opaque = opaque; + irq->n = n; +} + +void qemu_init_irq(IRQState *irq, qemu_irq_handler handler, void *opaque, + int n) +{ + object_initialize(irq, sizeof(*irq), TYPE_IRQ); + init_irq_fields(irq, handler, opaque, n); +} + +void qemu_init_irqs(IRQState irq[], size_t count, + qemu_irq_handler handler, void *opaque) +{ + for (size_t i = 0; i < count; i++) { + qemu_init_irq(&irq[i], handler, opaque, i); + } +} + qemu_irq *qemu_extend_irqs(qemu_irq *old, int n_old, qemu_irq_handler handler, void *opaque, int n) { @@ -67,13 +80,8 @@ qemu_irq *qemu_allocate_irqs(qemu_irq_handler handler, void *opaque, int n) qemu_irq qemu_allocate_irq(qemu_irq_handler handler, void *opaque, int n) { - IRQState *irq; - - irq = IRQ(object_new(TYPE_IRQ)); - irq->handler = handler; - irq->opaque = opaque; - irq->n = n; - + IRQState *irq = IRQ(object_new(TYPE_IRQ)); + init_irq_fields(irq, handler, opaque, n); return irq; } diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 7ccc9d5..2dea485 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -20,20 +20,20 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/units.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/loader.h" #include "hw/loader-fit.h" #include "qemu/cutils.h" #include "qemu/error-report.h" -#include "sysemu/device_tree.h" +#include "system/device_tree.h" #include <libfdt.h> #include <zlib.h> #define FIT_LOADER_MAX_PATH (128) -static const void *fit_load_image_alloc(const void *itb, const char *name, - int *poff, size_t *psz, Error **errp) +static void *fit_load_image_alloc(const void *itb, const char *name, + int *poff, size_t *psz, Error **errp) { const void *data; const char *comp; @@ -80,11 +80,11 @@ static const void *fit_load_image_alloc(const void *itb, const char *name, return NULL; } - data = g_realloc(uncomp_data, uncomp_len); + uncomp_data = g_realloc(uncomp_data, uncomp_len); if (psz) { *psz = uncomp_len; } - return data; + return uncomp_data; } error_setg(errp, "unknown compression '%s'", comp); @@ -177,13 +177,12 @@ out: static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, int cfg, void *opaque, const void *match_data, - hwaddr kernel_end, Error **errp) + hwaddr kernel_end, void **pfdt, Error **errp) { ERRP_GUARD(); Error *err = NULL; const char *name; - const void *data; - const void *load_data; + void *data; hwaddr load_addr; int img_off; size_t sz; @@ -194,7 +193,7 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, return 0; } - load_data = data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); + data = fit_load_image_alloc(itb, name, &img_off, &sz, errp); if (!data) { error_prepend(errp, "unable to load FDT image from FIT: "); return -EINVAL; @@ -211,19 +210,23 @@ static int fit_load_fdt(const struct fit_loader *ldr, const void *itb, } if (ldr->fdt_filter) { - load_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); + void *filtered_data; + + filtered_data = ldr->fdt_filter(opaque, data, match_data, &load_addr); + if (filtered_data != data) { + g_free(data); + data = filtered_data; + } } load_addr = ldr->addr_to_phys(opaque, load_addr); - sz = fdt_totalsize(load_data); - rom_add_blob_fixed(name, load_data, sz, load_addr); + sz = fdt_totalsize(data); + rom_add_blob_fixed(name, data, sz, load_addr); - ret = 0; + *pfdt = data; + return 0; out: g_free((void *) data); - if (data != load_data) { - g_free((void *) load_data); - } return ret; } @@ -259,7 +262,8 @@ out: return ret; } -int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) +int load_fit(const struct fit_loader *ldr, const char *filename, + void **pfdt, void *opaque) { Error *err = NULL; const struct fit_loader_match *match; @@ -323,7 +327,7 @@ int load_fit(const struct fit_loader *ldr, const char *filename, void *opaque) goto out; } - ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end, + ret = fit_load_fdt(ldr, itb, cfg_off, opaque, match_data, kernel_end, pfdt, &err); if (ret) { error_report_err(err); diff --git a/hw/core/loader.c b/hw/core/loader.c index 31593a1..e7056ba 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -51,17 +51,18 @@ #include "trace.h" #include "hw/hw.h" #include "disas/disas.h" +#include "migration/cpr.h" #include "migration/vmstate.h" #include "monitor/monitor.h" -#include "sysemu/reset.h" -#include "sysemu/sysemu.h" +#include "system/reset.h" +#include "system/system.h" #include "uboot_image.h" #include "hw/loader.h" #include "hw/nvram/fw_cfg.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/boards.h" #include "qemu/cutils.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" #include "tcg/debuginfo.h" #include <zlib.h> @@ -144,7 +145,7 @@ ssize_t load_image_mr(const char *filename, MemoryRegion *mr) { ssize_t size; - if (!memory_access_is_direct(mr, false)) { + if (!memory_access_is_direct(mr, false, MEMTXATTRS_UNSPECIFIED)) { /* Can only load an image into RAM or ROM */ return -1; } @@ -225,7 +226,7 @@ static void bswap_ahdr(struct exec *e) ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, - int bswap_needed, hwaddr target_page_size) + bool big_endian, hwaddr target_page_size) { int fd; ssize_t size, ret; @@ -240,7 +241,7 @@ ssize_t load_aout(const char *filename, hwaddr addr, int max_sz, if (size < 0) goto fail; - if (bswap_needed) { + if (big_endian != HOST_BIG_ENDIAN) { bswap_ahdr(&e); } @@ -409,11 +410,11 @@ ssize_t load_elf(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, + uint64_t *highaddr, uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab) { return load_elf_as(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, big_endian, + pentry, lowaddr, highaddr, pflags, elf_data_order, elf_machine, clear_lsb, data_swab, NULL); } @@ -422,29 +423,15 @@ ssize_t load_elf_as(const char *filename, uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, - uint64_t *highaddr, uint32_t *pflags, int big_endian, + uint64_t *highaddr, uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as) { - return load_elf_ram(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, big_endian, - elf_machine, clear_lsb, data_swab, as, true); -} - -/* return < 0 if error, otherwise the number of bytes loaded in memory */ -ssize_t load_elf_ram(const char *filename, - uint64_t (*elf_note_fn)(void *, void *, bool), - uint64_t (*translate_fn)(void *, uint64_t), - void *translate_opaque, uint64_t *pentry, - uint64_t *lowaddr, uint64_t *highaddr, uint32_t *pflags, - int big_endian, int elf_machine, int clear_lsb, - int data_swab, AddressSpace *as, bool load_rom) -{ return load_elf_ram_sym(filename, elf_note_fn, translate_fn, translate_opaque, - pentry, lowaddr, highaddr, pflags, big_endian, + pentry, lowaddr, highaddr, pflags, elf_data_order, elf_machine, clear_lsb, data_swab, as, - load_rom, NULL); + true, NULL); } /* return < 0 if error, otherwise the number of bytes loaded in memory */ @@ -453,11 +440,12 @@ ssize_t load_elf_ram_sym(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, - uint32_t *pflags, int big_endian, int elf_machine, + uint32_t *pflags, int elf_data_order, int elf_machine, int clear_lsb, int data_swab, AddressSpace *as, bool load_rom, symbol_fn_t sym_cb) { - int fd, data_order, target_data_order, must_swab; + const int host_data_order = HOST_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB; + int fd, must_swab; ssize_t ret = ELF_LOAD_FAILED; uint8_t e_ident[EI_NIDENT]; @@ -475,23 +463,14 @@ ssize_t load_elf_ram_sym(const char *filename, ret = ELF_LOAD_NOT_ELF; goto fail; } -#if HOST_BIG_ENDIAN - data_order = ELFDATA2MSB; -#else - data_order = ELFDATA2LSB; -#endif - must_swab = data_order != e_ident[EI_DATA]; - if (big_endian) { - target_data_order = ELFDATA2MSB; - } else { - target_data_order = ELFDATA2LSB; - } - if (target_data_order != e_ident[EI_DATA]) { + if (elf_data_order != ELFDATANONE && elf_data_order != e_ident[EI_DATA]) { ret = ELF_LOAD_WRONG_ENDIAN; goto fail; } + must_swab = host_data_order != e_ident[EI_DATA]; + lseek(fd, 0, SEEK_SET); if (e_ident[EI_CLASS] == ELFCLASS64) { ret = load_elf64(filename, fd, elf_note_fn, @@ -886,11 +865,11 @@ struct linux_efi_zboot_header { * * If the image is not a Linux EFI zboot image, do nothing and return success. */ -ssize_t unpack_efi_zboot_image(uint8_t **buffer, int *size) +ssize_t unpack_efi_zboot_image(uint8_t **buffer, ssize_t *size) { const struct linux_efi_zboot_header *header; uint8_t *data = NULL; - int ploff, plsize; + ssize_t ploff, plsize; ssize_t bytes; /* ignore if this is too small to be a EFI zboot image */ @@ -1051,7 +1030,9 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro) vmstate_register_ram_global(rom->mr); data = memory_region_get_ram_ptr(rom->mr); - memcpy(data, rom->data, rom->datasize); + if (!cpr_is_incoming()) { + memcpy(data, rom->data, rom->datasize); + } return data; } @@ -1352,20 +1333,6 @@ void rom_set_fw(FWCfgState *f) fw_cfg = f; } -void rom_set_order_override(int order) -{ - if (!fw_cfg) - return; - fw_cfg_set_order_override(fw_cfg, order); -} - -void rom_reset_order_override(void) -{ - if (!fw_cfg) - return; - fw_cfg_reset_order_override(fw_cfg); -} - void rom_transaction_begin(void) { Rom *rom; @@ -1429,7 +1396,7 @@ typedef struct RomSec { * work, but this way saves a little work later by avoiding * dealing with "gaps" of 0 length. */ -static gint sort_secs(gconstpointer a, gconstpointer b) +static gint sort_secs(gconstpointer a, gconstpointer b, gpointer d) { RomSec *ra = (RomSec *) a; RomSec *rb = (RomSec *) b; @@ -1482,7 +1449,7 @@ RomGap rom_find_largest_gap_between(hwaddr base, size_t size) /* sentinel */ secs = add_romsec_to_list(secs, base + size, 1); - secs = g_list_sort(secs, sort_secs); + secs = g_list_sort_with_data(secs, sort_secs, NULL); for (it = g_list_first(secs); it; it = g_list_next(it)) { cand = (RomSec *) it->data; diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 8701f00..c6325cd 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -19,10 +19,10 @@ #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/string-output-visitor.h" #include "qemu/error-report.h" -#include "sysemu/numa.h" +#include "system/numa.h" #include "hw/boards.h" void hmp_info_cpus(Monitor *mon, const QDict *qdict) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 130217d..d82043e 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -15,16 +15,18 @@ #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qmp/qobject.h" +#include "qobject/qobject.h" #include "qapi/qobject-input-visitor.h" #include "qapi/type-helpers.h" #include "qemu/uuid.h" +#include "qemu/target-info.h" #include "qom/qom-qobject.h" -#include "sysemu/hostmem.h" -#include "sysemu/hw_accel.h" -#include "sysemu/numa.h" -#include "sysemu/runstate.h" -#include "sysemu/sysemu.h" +#include "system/hostmem.h" +#include "system/hw_accel.h" +#include "system/numa.h" +#include "system/runstate.h" +#include "system/system.h" +#include "hw/s390x/storage-keys.h" /* * fast means: we NEVER interrupt vCPU threads to retrieve @@ -72,6 +74,7 @@ MachineInfoList *qmp_query_machines(bool has_compat_props, bool compat_props, for (el = machines; el; el = el->next) { MachineClass *mc = el->data; + const char *default_cpu_type = machine_class_default_cpu_type(mc); MachineInfo *info; info = g_malloc0(sizeof(*info)); @@ -90,8 +93,8 @@ MachineInfoList *qmp_query_machines(bool has_compat_props, bool compat_props, info->numa_mem_supported = mc->numa_mem_supported; info->deprecated = !!mc->deprecation_reason; info->acpi = !!object_class_property_find(OBJECT_CLASS(mc), "acpi"); - if (mc->default_cpu_type) { - info->default_cpu_type = g_strdup(mc->default_cpu_type); + if (default_cpu_type) { + info->default_cpu_type = g_strdup(default_cpu_type); } if (mc->default_ram_id) { info->default_ram_id = g_strdup(mc->default_ram_id); @@ -132,9 +135,9 @@ CurrentMachineParams *qmp_query_current_machine(Error **errp) return params; } -TargetInfo *qmp_query_target(Error **errp) +QemuTargetInfo *qmp_query_target(Error **errp) { - TargetInfo *info = g_malloc0(sizeof(*info)); + QemuTargetInfo *info = g_malloc0(sizeof(*info)); info->arch = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, &error_abort); @@ -406,3 +409,16 @@ GuidInfo *qmp_query_vm_generation_id(Error **errp) info->guid = qemu_uuid_unparse_strdup(&vms->guid); return info; } + +void qmp_dump_skeys(const char *filename, Error **errp) +{ + ObjectClass *mc = object_get_class(qdev_get_machine()); + ObjectClass *oc = object_class_dynamic_cast(mc, TYPE_DUMP_SKEYS_INTERFACE); + + if (!oc) { + error_setg(errp, "Storage keys information not available" + " for this architecture"); + return; + } + DUMP_SKEYS_INTERFACE_CLASS(oc)->qmp_dump_skeys(filename, errp); +} diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c index 5d8d7ed..0be0ac0 100644 --- a/hw/core/machine-smp.c +++ b/hw/core/machine-smp.c @@ -261,6 +261,82 @@ void machine_parse_smp_config(MachineState *ms, } } +static bool machine_check_topo_support(MachineState *ms, + CpuTopologyLevel topo, + Error **errp) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if ((topo == CPU_TOPOLOGY_LEVEL_MODULE && !mc->smp_props.modules_supported) || + (topo == CPU_TOPOLOGY_LEVEL_CLUSTER && !mc->smp_props.clusters_supported) || + (topo == CPU_TOPOLOGY_LEVEL_DIE && !mc->smp_props.dies_supported) || + (topo == CPU_TOPOLOGY_LEVEL_BOOK && !mc->smp_props.books_supported) || + (topo == CPU_TOPOLOGY_LEVEL_DRAWER && !mc->smp_props.drawers_supported)) { + error_setg(errp, + "Invalid topology level: %s. " + "The topology level is not supported by this machine", + CpuTopologyLevel_str(topo)); + return false; + } + + return true; +} + +bool machine_parse_smp_cache(MachineState *ms, + const SmpCachePropertiesList *caches, + Error **errp) +{ + MachineClass *mc = MACHINE_GET_CLASS(ms); + const SmpCachePropertiesList *node; + DECLARE_BITMAP(caches_bitmap, CACHE_LEVEL_AND_TYPE__MAX); + + bitmap_zero(caches_bitmap, CACHE_LEVEL_AND_TYPE__MAX); + for (node = caches; node; node = node->next) { + /* Prohibit users from repeating settings. */ + if (test_bit(node->value->cache, caches_bitmap)) { + error_setg(errp, + "Invalid cache properties: %s. " + "The cache properties are duplicated", + CacheLevelAndType_str(node->value->cache)); + return false; + } + + machine_set_cache_topo_level(ms, node->value->cache, + node->value->topology); + set_bit(node->value->cache, caches_bitmap); + } + + for (int i = 0; i < CACHE_LEVEL_AND_TYPE__MAX; i++) { + const SmpCacheProperties *props = &ms->smp_cache.props[i]; + + /* + * Reject non "default" topology level if the cache isn't + * supported by the machine. + */ + if (props->topology != CPU_TOPOLOGY_LEVEL_DEFAULT && + !mc->smp_props.cache_supported[props->cache]) { + error_setg(errp, + "%s cache topology not supported by this machine", + CacheLevelAndType_str(props->cache)); + return false; + } + + if (props->topology == CPU_TOPOLOGY_LEVEL_THREAD) { + error_setg(errp, + "%s level cache not supported by this machine", + CpuTopologyLevel_str(props->topology)); + return false; + } + + if (!machine_check_topo_support(ms, props->topology, errp)) { + return false; + } + } + + mc->smp_props.has_caches = true; + return true; +} + unsigned int machine_topo_get_cores_per_socket(const MachineState *ms) { return ms->smp.cores * ms->smp.modules * ms->smp.clusters * ms->smp.dies; @@ -270,3 +346,63 @@ unsigned int machine_topo_get_threads_per_socket(const MachineState *ms) { return ms->smp.threads * machine_topo_get_cores_per_socket(ms); } + +CpuTopologyLevel machine_get_cache_topo_level(const MachineState *ms, + CacheLevelAndType cache) +{ + return ms->smp_cache.props[cache].topology; +} + +void machine_set_cache_topo_level(MachineState *ms, CacheLevelAndType cache, + CpuTopologyLevel level) +{ + ms->smp_cache.props[cache].topology = level; +} + +/* + * When both cache1 and cache2 are configured with specific topology levels + * (not default level), is cache1's topology level higher than cache2? + */ +static bool smp_cache_topo_cmp(const SmpCache *smp_cache, + CacheLevelAndType cache1, + CacheLevelAndType cache2) +{ + /* + * Before comparing, the "default" topology level should be replaced + * with the specific level. + */ + assert(smp_cache->props[cache1].topology != CPU_TOPOLOGY_LEVEL_DEFAULT); + + return smp_cache->props[cache1].topology > smp_cache->props[cache2].topology; +} + +/* + * Currently, we have no way to expose the arch-specific default cache model + * because the cache model is sometimes related to the CPU model (e.g., i386). + * + * We can only check the correctness of the cache topology after the arch loads + * the user-configured cache model from MachineState and consumes the special + * "default" level by replacing it with the specific level. + */ +bool machine_check_smp_cache(const MachineState *ms, Error **errp) +{ + if (smp_cache_topo_cmp(&ms->smp_cache, CACHE_LEVEL_AND_TYPE_L1D, + CACHE_LEVEL_AND_TYPE_L2) || + smp_cache_topo_cmp(&ms->smp_cache, CACHE_LEVEL_AND_TYPE_L1I, + CACHE_LEVEL_AND_TYPE_L2)) { + error_setg(errp, + "Invalid smp cache topology. " + "L2 cache topology level shouldn't be lower than L1 cache"); + return false; + } + + if (smp_cache_topo_cmp(&ms->smp_cache, CACHE_LEVEL_AND_TYPE_L2, + CACHE_LEVEL_AND_TYPE_L3)) { + error_setg(errp, + "Invalid smp cache topology. " + "L3 cache topology level shouldn't be lower than L2 cache"); + return false; + } + + return true; +} diff --git a/hw/core/machine.c b/hw/core/machine.c index bc38cad..e869821 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -11,35 +11,61 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qemu/accel.h" -#include "sysemu/replay.h" +#include "system/replay.h" #include "hw/boards.h" #include "hw/loader.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/qapi-visit-machine.h" +#include "qapi/qapi-commands-machine.h" #include "qemu/madvise.h" #include "qom/object_interfaces.h" -#include "sysemu/cpus.h" -#include "sysemu/sysemu.h" -#include "sysemu/reset.h" -#include "sysemu/runstate.h" -#include "sysemu/xen.h" -#include "sysemu/qtest.h" +#include "system/cpus.h" +#include "system/system.h" +#include "system/reset.h" +#include "system/runstate.h" +#include "system/xen.h" +#include "system/qtest.h" #include "hw/pci/pci_bridge.h" #include "hw/mem/nvdimm.h" #include "migration/global_state.h" -#include "exec/confidential-guest-support.h" +#include "system/confidential-guest-support.h" #include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-net.h" #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" +GlobalProperty hw_compat_10_0[] = { + { "scsi-hd", "dpofua", "off" }, +}; +const size_t hw_compat_10_0_len = G_N_ELEMENTS(hw_compat_10_0); + +GlobalProperty hw_compat_9_2[] = { + { "arm-cpu", "backcompat-pauth-default-use-qarma5", "true"}, + { "virtio-balloon-pci", "vectors", "0" }, + { "virtio-balloon-pci-transitional", "vectors", "0" }, + { "virtio-balloon-pci-non-transitional", "vectors", "0" }, + { "virtio-mem-pci", "vectors", "0" }, + { "migration", "multifd-clean-tls-termination", "false" }, + { "migration", "send-switchover-start", "off"}, + { "vfio-pci", "x-migration-multifd-transfer", "off" }, +}; +const size_t hw_compat_9_2_len = G_N_ELEMENTS(hw_compat_9_2); + +GlobalProperty hw_compat_9_1[] = { + { TYPE_PCI_DEVICE, "x-pcie-ext-tag", "false" }, +}; +const size_t hw_compat_9_1_len = G_N_ELEMENTS(hw_compat_9_1); + GlobalProperty hw_compat_9_0[] = { - {"arm-cpu", "backcompat-cntfrq", "true" }, - {"scsi-disk-base", "migrate-emulated-scsi-request", "false" }, - {"vfio-pci", "skip-vsc-check", "false" }, + { "arm-cpu", "backcompat-cntfrq", "true" }, + { "scsi-hd", "migrate-emulated-scsi-request", "false" }, + { "scsi-cd", "migrate-emulated-scsi-request", "false" }, + { "vfio-pci", "skip-vsc-check", "false" }, { "virtio-pci", "x-pcie-pm-no-soft-reset", "off" }, - {"sd-card", "spec_version", "2" }, + { "sd-card", "spec_version", "2" }, }; const size_t hw_compat_9_0_len = G_N_ELEMENTS(hw_compat_9_0); @@ -259,51 +285,6 @@ GlobalProperty hw_compat_2_6[] = { }; const size_t hw_compat_2_6_len = G_N_ELEMENTS(hw_compat_2_6); -GlobalProperty hw_compat_2_5[] = { - { "isa-fdc", "fallback", "144" }, - { "pvscsi", "x-old-pci-configuration", "on" }, - { "pvscsi", "x-disable-pcie", "on" }, - { "vmxnet3", "x-old-msi-offsets", "on" }, - { "vmxnet3", "x-disable-pcie", "on" }, -}; -const size_t hw_compat_2_5_len = G_N_ELEMENTS(hw_compat_2_5); - -GlobalProperty hw_compat_2_4[] = { - { "e1000", "extra_mac_registers", "off" }, - { "virtio-pci", "x-disable-pcie", "on" }, - { "virtio-pci", "migrate-extra", "off" }, - { "fw_cfg_mem", "dma_enabled", "off" }, - { "fw_cfg_io", "dma_enabled", "off" } -}; -const size_t hw_compat_2_4_len = G_N_ELEMENTS(hw_compat_2_4); - -GlobalProperty hw_compat_2_3[] = { - { "virtio-blk-pci", "any_layout", "off" }, - { "virtio-balloon-pci", "any_layout", "off" }, - { "virtio-serial-pci", "any_layout", "off" }, - { "virtio-9p-pci", "any_layout", "off" }, - { "virtio-rng-pci", "any_layout", "off" }, - { TYPE_PCI_DEVICE, "x-pcie-lnksta-dllla", "off" }, - { "migration", "send-configuration", "off" }, - { "migration", "send-section-footer", "off" }, - { "migration", "store-global-state", "off" }, -}; -const size_t hw_compat_2_3_len = G_N_ELEMENTS(hw_compat_2_3); - -GlobalProperty hw_compat_2_2[] = {}; -const size_t hw_compat_2_2_len = G_N_ELEMENTS(hw_compat_2_2); - -GlobalProperty hw_compat_2_1[] = { - { "intel-hda", "old_msi_addr", "on" }, - { "VGA", "qemu-extended-regs", "off" }, - { "secondary-vga", "qemu-extended-regs", "off" }, - { "virtio-scsi-pci", "any_layout", "off" }, - { "usb-mouse", "usb_version", "1" }, - { "usb-kbd", "usb_version", "1" }, - { "virtio-pci", "virtio-pci-bus-master-bug-migration", "on" }, -}; -const size_t hw_compat_2_1_len = G_N_ELEMENTS(hw_compat_2_1); - MachineState *current_machine; static char *machine_get_kernel(Object *obj, Error **errp) @@ -321,6 +302,21 @@ static void machine_set_kernel(Object *obj, const char *value, Error **errp) ms->kernel_filename = g_strdup(value); } +static char *machine_get_shim(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->shim_filename); +} + +static void machine_set_shim(Object *obj, const char *value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + g_free(ms->shim_filename); + ms->shim_filename = g_strdup(value); +} + static char *machine_get_initrd(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -456,6 +452,22 @@ static void machine_set_mem_merge(Object *obj, bool value, Error **errp) ms->mem_merge = value; } +#ifdef CONFIG_POSIX +static bool machine_get_aux_ram_share(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->aux_ram_share; +} + +static void machine_set_aux_ram_share(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->aux_ram_share = value; +} +#endif + static bool machine_get_usb(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -617,11 +629,19 @@ static void machine_set_mem(Object *obj, Visitor *v, const char *name, mem->size = mc->fixup_ram_size(mem->size); } if ((ram_addr_t)mem->size != mem->size) { - error_setg(errp, "ram size too large"); + error_setg(errp, "ram size %llu exceeds permitted maximum %llu", + (unsigned long long)mem->size, + (unsigned long long)RAM_ADDR_MAX); goto out_free; } if (mem->has_max_size) { + if ((ram_addr_t)mem->max_size != mem->max_size) { + error_setg(errp, "ram size %llu exceeds permitted maximum %llu", + (unsigned long long)mem->max_size, + (unsigned long long)RAM_ADDR_MAX); + goto out_free; + } if (mem->max_size < mem->size) { error_setg(errp, "invalid value of maxmem: " "maximum memory size (0x%" PRIx64 ") must be at least " @@ -928,6 +948,40 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name, machine_parse_smp_config(ms, config, errp); } +static void machine_get_smp_cache(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MachineState *ms = MACHINE(obj); + SmpCache *cache = &ms->smp_cache; + SmpCachePropertiesList *head = NULL; + SmpCachePropertiesList **tail = &head; + + for (int i = 0; i < CACHE_LEVEL_AND_TYPE__MAX; i++) { + SmpCacheProperties *node = g_new(SmpCacheProperties, 1); + + node->cache = cache->props[i].cache; + node->topology = cache->props[i].topology; + QAPI_LIST_APPEND(tail, node); + } + + visit_type_SmpCachePropertiesList(v, name, &head, errp); + qapi_free_SmpCachePropertiesList(head); +} + +static void machine_set_smp_cache(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + MachineState *ms = MACHINE(obj); + SmpCachePropertiesList *caches; + + if (!visit_type_SmpCachePropertiesList(v, name, &caches, errp)) { + return; + } + + machine_parse_smp_cache(ms, caches, errp); + qapi_free_SmpCachePropertiesList(caches); +} + static void machine_get_boot(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -997,24 +1051,70 @@ void machine_add_audiodev_property(MachineClass *mc) "Audiodev to use for default machine devices"); } -static void machine_class_init(ObjectClass *oc, void *data) +static bool create_default_memdev(MachineState *ms, const char *path, + Error **errp) +{ + Object *obj; + MachineClass *mc = MACHINE_GET_CLASS(ms); + bool r = false; + + obj = object_new(path ? TYPE_MEMORY_BACKEND_FILE : TYPE_MEMORY_BACKEND_RAM); + if (path) { + if (!object_property_set_str(obj, "mem-path", path, errp)) { + goto out; + } + } + if (!object_property_set_int(obj, "size", ms->ram_size, errp)) { + goto out; + } + object_property_add_child(object_get_objects_root(), mc->default_ram_id, + obj); + /* Ensure backend's memory region name is equal to mc->default_ram_id */ + if (!object_property_set_bool(obj, "x-use-canonical-path-for-ramblock-id", + false, errp)) { + goto out; + } + if (!user_creatable_complete(USER_CREATABLE(obj), errp)) { + goto out; + } + r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp); + +out: + object_unref(obj); + return r; +} + +static void machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); /* Default 128 MB as guest ram size */ mc->default_ram_size = 128 * MiB; mc->rom_file_has_mr = true; + /* + * SMBIOS 3.1.0 7.18.5 Memory Device — Extended Size + * use max possible value that could be encoded into + * 'Extended Size' field (2047Tb). + */ + mc->smbios_memory_device_size = 2047 * TiB; /* numa node memory size aligned on 8MB by default. * On Linux, each node's border has to be 8MB aligned */ mc->numa_mem_align_shift = 23; + mc->create_default_memdev = create_default_memdev; + object_class_property_add_str(oc, "kernel", machine_get_kernel, machine_set_kernel); object_class_property_set_description(oc, "kernel", "Linux kernel image file"); + object_class_property_add_str(oc, "shim", + machine_get_shim, machine_set_shim); + object_class_property_set_description(oc, "shim", + "shim.efi file"); + object_class_property_add_str(oc, "initrd", machine_get_initrd, machine_set_initrd); object_class_property_set_description(oc, "initrd", @@ -1047,6 +1147,11 @@ static void machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "smp", "CPU topology"); + object_class_property_add(oc, "smp-cache", "SmpCachePropertiesWrapper", + machine_get_smp_cache, machine_set_smp_cache, NULL, NULL); + object_class_property_set_description(oc, "smp-cache", + "Cache properties list for SMP machine"); + object_class_property_add(oc, "phandle-start", "int", machine_get_phandle_start, machine_set_phandle_start, NULL, NULL); @@ -1068,6 +1173,12 @@ static void machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, "mem-merge", "Enable/disable memory merge support"); +#ifdef CONFIG_POSIX + object_class_property_add_bool(oc, "aux-ram-share", + machine_get_aux_ram_share, + machine_set_aux_ram_share); +#endif + object_class_property_add_bool(oc, "usb", machine_get_usb, machine_set_usb); object_class_property_set_description(oc, "usb", @@ -1116,7 +1227,7 @@ static void machine_class_init(ObjectClass *oc, void *data) "Memory size configuration"); } -static void machine_class_base_init(ObjectClass *oc, void *data) +static void machine_class_base_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->max_cpus = mc->max_cpus ?: 1; @@ -1137,9 +1248,6 @@ static void machine_initfn(Object *obj) MachineState *ms = MACHINE(obj); MachineClass *mc = MACHINE_GET_CLASS(obj); - container_get(obj, "/peripheral"); - container_get(obj, "/peripheral-anon"); - ms->dump_guest_core = true; ms->mem_merge = (QEMU_MADV_MERGEABLE != QEMU_MADV_INVALID); ms->enable_graphics = true; @@ -1185,6 +1293,11 @@ static void machine_initfn(Object *obj) ms->smp.cores = 1; ms->smp.threads = 1; + for (int i = 0; i < CACHE_LEVEL_AND_TYPE__MAX; i++) { + ms->smp_cache.props[i].cache = (CacheLevelAndType)i; + ms->smp_cache.props[i].topology = CPU_TOPOLOGY_LEVEL_DEFAULT; + } + machine_copy_boot_config(ms, &(BootConfiguration){ 0 }); } @@ -1403,38 +1516,6 @@ MemoryRegion *machine_consume_memdev(MachineState *machine, return ret; } -static bool create_default_memdev(MachineState *ms, const char *path, Error **errp) -{ - Object *obj; - MachineClass *mc = MACHINE_GET_CLASS(ms); - bool r = false; - - obj = object_new(path ? TYPE_MEMORY_BACKEND_FILE : TYPE_MEMORY_BACKEND_RAM); - if (path) { - if (!object_property_set_str(obj, "mem-path", path, errp)) { - goto out; - } - } - if (!object_property_set_int(obj, "size", ms->ram_size, errp)) { - goto out; - } - object_property_add_child(object_get_objects_root(), mc->default_ram_id, - obj); - /* Ensure backend's memory region name is equal to mc->default_ram_id */ - if (!object_property_set_bool(obj, "x-use-canonical-path-for-ramblock-id", - false, errp)) { - goto out; - } - if (!user_creatable_complete(USER_CREATABLE(obj), errp)) { - goto out; - } - r = object_property_set_link(OBJECT(ms), "memory-backend", obj, errp); - -out: - object_unref(obj); - return r; -} - const char *machine_class_default_cpu_type(MachineClass *mc) { if (mc->valid_cpu_types && !mc->valid_cpu_types[1]) { @@ -1538,7 +1619,9 @@ void machine_run_board_init(MachineState *machine, const char *mem_path, Error * machine_class->default_ram_id); return; } - if (!create_default_memdev(current_machine, mem_path, errp)) { + + if (!machine_class->create_default_memdev(current_machine, mem_path, + errp)) { return; } } @@ -1603,6 +1686,22 @@ void qemu_remove_machine_init_done_notifier(Notifier *notify) notifier_remove(notify); } +static void handle_machine_dumpdtb(MachineState *ms) +{ + if (!ms->dumpdtb) { + return; + } +#ifdef CONFIG_FDT + qmp_dumpdtb(ms->dumpdtb, &error_fatal); + exit(0); +#else + error_report("This machine doesn't have an FDT"); + error_printf("(this machine type definitely doesn't use FDT, and " + "this QEMU doesn't have FDT support compiled in)\n"); + exit(1); +#endif +} + void qdev_machine_creation_done(void) { cpu_synchronize_all_post_init(); @@ -1632,6 +1731,12 @@ void qdev_machine_creation_done(void) notifier_list_notify(&machine_init_done_notifiers, NULL); + /* + * If the user used -machine dumpdtb=file.dtb to request that we + * dump the DTB to a file, do it now, and exit. + */ + handle_machine_dumpdtb(current_machine); + if (rom_check_and_register_reset() != 0) { exit(1); } diff --git a/hw/core/meson.build b/hw/core/meson.build index a3d9bab..b5a545a 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -24,9 +24,10 @@ system_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c')) system_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) +system_ss.add(when: 'CONFIG_EIF', if_true: [files('eif.c'), zlib, libcbor, gnutls]) system_ss.add(files( - 'cpu-sysemu.c', + 'cpu-system.c', 'fw-path-provider.c', 'gpio.c', 'hotplug.c', @@ -45,3 +46,7 @@ system_ss.add(files( 'vm-change-state-handler.c', 'clock-vmstate.c', )) +user_ss.add(files( + 'cpu-user.c', + 'qdev-user.c', +)) diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c index f586a4b..a6e477a 100644 --- a/hw/core/null-machine.c +++ b/hw/core/null-machine.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/boards.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/core/cpu.h" static void machine_none_init(MachineState *mch) @@ -53,7 +53,6 @@ static void machine_none_machine_init(MachineClass *mc) mc->no_parallel = 1; mc->no_floppy = 1; mc->no_cdrom = 1; - mc->no_sdcard = 1; } DEFINE_MACHINE("none", machine_none_machine_init) diff --git a/hw/core/numa.c b/hw/core/numa.c index f8ce332..218576f 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -24,15 +24,15 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "sysemu/hostmem.h" -#include "sysemu/numa.h" +#include "system/hostmem.h" +#include "system/numa.h" #include "exec/cpu-common.h" #include "exec/ramlist.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "qapi/opts-visitor.h" #include "qapi/qapi-visit-machine.h" -#include "sysemu/qtest.h" +#include "system/qtest.h" #include "hw/core/cpu.h" #include "hw/mem/pc-dimm.h" #include "hw/boards.h" @@ -249,7 +249,7 @@ void parse_numa_hmat_lb(NumaState *numa_state, NumaHmatLBOptions *node, lb_data.initiator = node->initiator; lb_data.target = node->target; - if (node->data_type <= HMATLB_DATA_TYPE_WRITE_LATENCY) { + if (node->data_type <= HMAT_LB_DATA_TYPE_WRITE_LATENCY) { /* Input latency data */ if (!node->has_latency) { @@ -313,7 +313,7 @@ void parse_numa_hmat_lb(NumaState *numa_state, NumaHmatLBOptions *node, numa_info[node->target].lb_info_provided |= BIT(0); } lb_data.data = node->latency; - } else if (node->data_type >= HMATLB_DATA_TYPE_ACCESS_BANDWIDTH) { + } else if (node->data_type >= HMAT_LB_DATA_TYPE_ACCESS_BANDWIDTH) { /* Input bandwidth data */ if (!node->has_bandwidth) { error_setg(errp, "Missing 'bandwidth' option"); @@ -380,7 +380,7 @@ void parse_numa_hmat_lb(NumaState *numa_state, NumaHmatLBOptions *node, } lb_data.data = node->bandwidth; } else { - assert(0); + g_assert_not_reached(); } g_array_append_val(hmat_lb->list, lb_data); diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c index 13907df..3942c70 100644 --- a/hw/core/or-irq.c +++ b/hw/core/or-irq.c @@ -115,16 +115,15 @@ static const VMStateDescription vmstate_or_irq = { }, }; -static Property or_irq_properties[] = { +static const Property or_irq_properties[] = { DEFINE_PROP_UINT16("num-lines", OrIRQState, num_lines, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void or_irq_class_init(ObjectClass *klass, void *data) +static void or_irq_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - dc->reset = or_irq_reset; + device_class_set_legacy_reset(dc, or_irq_reset); device_class_set_props(dc, or_irq_properties); dc->realize = or_irq_realize; dc->vmsd = &vmstate_or_irq; diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index b8487b2..6950063 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -145,9 +145,12 @@ static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, * the target device's memory region */ for (off = 0; off < pbus->mmio_size; off += alignment) { - if (!memory_region_find(&pbus->mmio, off, size).mr) { + MemoryRegion *mr = memory_region_find(&pbus->mmio, off, size).mr; + if (!mr) { found_region = true; break; + } else { + memory_region_unref(mr); } } @@ -201,13 +204,12 @@ static void platform_bus_realize(DeviceState *dev, Error **errp) plaform_bus_refresh_irqs(pbus); } -static Property platform_bus_properties[] = { +static const Property platform_bus_properties[] = { DEFINE_PROP_UINT32("num_irqs", PlatformBusDevice, num_irqs, 0), DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0), - DEFINE_PROP_END_OF_LIST() }; -static void platform_bus_class_init(ObjectClass *klass, void *data) +static void platform_bus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index b151759..0aeb10f 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -11,8 +11,8 @@ #include "migration/vmstate.h" #include "qemu/host-utils.h" #include "exec/replay-core.h" -#include "sysemu/cpu-timers.h" -#include "sysemu/qtest.h" +#include "exec/icount.h" +#include "system/qtest.h" #include "block/aio.h" #include "hw/clock.h" @@ -83,7 +83,7 @@ static void ptimer_reload(ptimer_state *s, int delta_adjust) delta = s->delta = s->limit; } - if (s->period == 0) { + if (s->period == 0 && s->period_frac == 0) { if (!qtest_enabled()) { fprintf(stderr, "Timer with period zero, disabling\n"); } @@ -309,7 +309,7 @@ void ptimer_run(ptimer_state *s, int oneshot) assert(s->in_transaction); - if (was_disabled && s->period == 0) { + if (was_disabled && s->period == 0 && s->period_frac == 0) { if (!qtest_enabled()) { fprintf(stderr, "Timer with period zero, disabling\n"); } diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c index 8279957..dacafa4 100644 --- a/hw/core/qdev-clock.c +++ b/hw/core/qdev-clock.c @@ -22,7 +22,7 @@ * Add a new clock in a device */ static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name, - bool output, Clock *clk) + bool alias, bool output, Clock *clk) { NamedClockList *ncl; @@ -38,39 +38,8 @@ static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name, */ ncl = g_new0(NamedClockList, 1); ncl->name = g_strdup(name); + ncl->alias = alias; ncl->output = output; - ncl->alias = (clk != NULL); - - /* - * Trying to create a clock whose name clashes with some other - * clock or property is a bug in the caller and we will abort(). - */ - if (clk == NULL) { - clk = CLOCK(object_new(TYPE_CLOCK)); - object_property_add_child(OBJECT(dev), name, OBJECT(clk)); - if (output) { - /* - * Remove object_new()'s initial reference. - * Note that for inputs, the reference created by object_new() - * will be deleted in qdev_finalize_clocklist(). - */ - object_unref(OBJECT(clk)); - } - } else { - object_property_add_link(OBJECT(dev), name, - object_get_typename(OBJECT(clk)), - (Object **) &ncl->clock, - NULL, OBJ_PROP_LINK_STRONG); - /* - * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk - * object reference count gets decremented on property deletion. - * However object_property_add_link does not increment it since it - * doesn't know the linked object. Increment it here to ensure the - * aliased clock stays alive during this device life-time. - */ - object_ref(OBJECT(clk)); - } - ncl->clock = clk; QLIST_INSERT_HEAD(&dev->clocks, ncl, node); @@ -84,14 +53,11 @@ void qdev_finalize_clocklist(DeviceState *dev) QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) { QLIST_REMOVE(ncl, node); - if (!ncl->output && !ncl->alias) { + if (!ncl->alias) { /* * We kept a reference on the input clock to ensure it lives up to - * this point so we can safely remove the callback. - * It avoids having a callback to a deleted object if ncl->clock - * is still referenced somewhere else (eg: by a clock output). + * this point; it is used by the monitor to show the frequency. */ - clock_clear_callback(ncl->clock); object_unref(OBJECT(ncl->clock)); } g_free(ncl->name); @@ -101,29 +67,25 @@ void qdev_finalize_clocklist(DeviceState *dev) Clock *qdev_init_clock_out(DeviceState *dev, const char *name) { - NamedClockList *ncl; - - assert(name); - - ncl = qdev_init_clocklist(dev, name, true, NULL); + Clock *clk = CLOCK(object_new(TYPE_CLOCK)); + object_property_add_child(OBJECT(dev), name, OBJECT(clk)); - return ncl->clock; + qdev_init_clocklist(dev, name, false, true, clk); + return clk; } Clock *qdev_init_clock_in(DeviceState *dev, const char *name, ClockCallback *callback, void *opaque, unsigned int events) { - NamedClockList *ncl; - - assert(name); - - ncl = qdev_init_clocklist(dev, name, false, NULL); + Clock *clk = CLOCK(object_new(TYPE_CLOCK)); + object_property_add_child(OBJECT(dev), name, OBJECT(clk)); + qdev_init_clocklist(dev, name, false, false, clk); if (callback) { - clock_set_callback(ncl->clock, callback, opaque, events); + clock_set_callback(clk, callback, opaque, events); } - return ncl->clock; + return clk; } void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks) @@ -194,15 +156,25 @@ Clock *qdev_get_clock_out(DeviceState *dev, const char *name) Clock *qdev_alias_clock(DeviceState *dev, const char *name, DeviceState *alias_dev, const char *alias_name) { - NamedClockList *ncl; - - assert(name && alias_name); + NamedClockList *ncl = qdev_get_clocklist(dev, name); + Clock *clk = ncl->clock; - ncl = qdev_get_clocklist(dev, name); + ncl = qdev_init_clocklist(alias_dev, alias_name, true, ncl->output, clk); - qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock); + object_property_add_link(OBJECT(alias_dev), alias_name, + TYPE_CLOCK, + (Object **) &ncl->clock, + NULL, OBJ_PROP_LINK_STRONG); + /* + * Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk + * object reference count gets decremented on property deletion. + * However object_property_add_link does not increment it since it + * doesn't know the linked object. Increment it here to ensure the + * aliased clock stays alive during this device life-time. + */ + object_ref(OBJECT(clk)); - return ncl->clock; + return clk; } void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source) diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c index d495d0e..ff176dc 100644 --- a/hw/core/qdev-hotplug.c +++ b/hw/core/qdev-hotplug.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "hw/qdev-core.h" #include "hw/boards.h" +#include "qapi/error.h" HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) { @@ -30,12 +31,48 @@ HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) return NULL; } -bool qdev_hotplug_allowed(DeviceState *dev, Error **errp) +static bool qdev_hotplug_unplug_allowed_common(DeviceState *dev, BusState *bus, + Error **errp) +{ + DeviceClass *dc = DEVICE_GET_CLASS(dev); + + if (!dc->hotpluggable) { + error_setg(errp, "Device '%s' does not support hotplugging", + object_get_typename(OBJECT(dev))); + return false; + } + + if (bus) { + if (!qbus_is_hotpluggable(bus)) { + error_setg(errp, "Bus '%s' does not support hotplugging", + bus->name); + return false; + } + } else { + if (!qdev_get_machine_hotplug_handler(dev)) { + /* + * No bus, no machine hotplug handler --> device is not hotpluggable + */ + error_setg(errp, + "Device '%s' can not be hotplugged on this machine", + object_get_typename(OBJECT(dev))); + return false; + } + } + + return true; +} + +bool qdev_hotplug_allowed(DeviceState *dev, BusState *bus, Error **errp) { MachineState *machine; MachineClass *mc; Object *m_obj = qdev_get_machine(); + if (!qdev_hotplug_unplug_allowed_common(dev, bus, errp)) { + return false; + } + if (object_dynamic_cast(m_obj, TYPE_MACHINE)) { machine = MACHINE(m_obj); mc = MACHINE_GET_CLASS(machine); @@ -47,6 +84,12 @@ bool qdev_hotplug_allowed(DeviceState *dev, Error **errp) return true; } +bool qdev_hotunplug_allowed(DeviceState *dev, Error **errp) +{ + return !qdev_unplug_blocked(dev, errp) && + qdev_hotplug_unplug_allowed_common(dev, dev->parent_bus, errp); +} + HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev) { if (dev->parent_bus) { diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index f13350b..24e145d 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -29,8 +29,8 @@ #include "audio/audio.h" #include "chardev/char-fe.h" -#include "sysemu/block-backend.h" -#include "sysemu/blockdev.h" +#include "system/block-backend.h" +#include "system/blockdev.h" #include "net/net.h" #include "hw/pci/pci.h" #include "hw/pci/pcie.h" @@ -58,13 +58,39 @@ static bool check_prop_still_unset(Object *obj, const char *name, return false; } +bool qdev_prop_sanitize_s390x_loadparm(uint8_t *loadparm, const char *str, + Error **errp) +{ + int i, len; + + len = strlen(str); + if (len > 8) { + error_setg(errp, "'loadparm' can only contain up to 8 characters"); + return false; + } + + for (i = 0; i < len; i++) { + uint8_t c = qemu_toupper(str[i]); /* mimic HMC */ + + if (qemu_isalnum(c) || c == '.' || c == ' ') { + loadparm[i] = c; + } else { + error_setg(errp, + "invalid character in 'loadparm': '%c' (ASCII 0x%02x)", + c, c); + return false; + } + } + + return true; +} /* --- drive --- */ static void get_drive(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; void **ptr = object_field_prop_ptr(obj, prop); const char *value; char *p; @@ -90,7 +116,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, void *opaque, bool iothread, Error **errp) { DeviceState *dev = DEVICE(obj); - Property *prop = opaque; + const Property *prop = opaque; void **ptr = object_field_prop_ptr(obj, prop); char *str; BlockBackend *blk; @@ -119,6 +145,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, if (ctx != bdrv_get_aio_context(bs)) { error_setg(errp, "Different aio context is not supported for new " "node"); + return; } blk_replace_bs(blk, bs, errp); @@ -199,7 +226,7 @@ static void set_drive_iothread(Object *obj, Visitor *v, const char *name, static void release_drive(Object *obj, const char *name, void *opaque) { DeviceState *dev = DEVICE(obj); - Property *prop = opaque; + const Property *prop = opaque; BlockBackend **ptr = object_field_prop_ptr(obj, prop); if (*ptr) { @@ -209,7 +236,7 @@ static void release_drive(Object *obj, const char *name, void *opaque) } const PropertyInfo qdev_prop_drive = { - .name = "str", + .type = "str", .description = "Node name or ID of a block device to use as a backend", .realized_set_allowed = true, .get = get_drive, @@ -218,7 +245,7 @@ const PropertyInfo qdev_prop_drive = { }; const PropertyInfo qdev_prop_drive_iothread = { - .name = "str", + .type = "str", .description = "Node name or ID of a block device to use as a backend", .realized_set_allowed = true, .get = get_drive, @@ -243,7 +270,7 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { ERRP_GUARD(); - Property *prop = opaque; + const Property *prop = opaque; CharBackend *be = object_field_prop_ptr(obj, prop); Chardev *s; char *str; @@ -279,14 +306,14 @@ static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque, static void release_chr(Object *obj, const char *name, void *opaque) { - Property *prop = opaque; + const Property *prop = opaque; CharBackend *be = object_field_prop_ptr(obj, prop); qemu_chr_fe_deinit(be, false); } const PropertyInfo qdev_prop_chr = { - .name = "str", + .type = "str", .description = "ID of a chardev to use as a backend", .get = get_chr, .set = set_chr, @@ -303,7 +330,7 @@ const PropertyInfo qdev_prop_chr = { static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; MACAddr *mac = object_field_prop_ptr(obj, prop); char buffer[2 * 6 + 5 + 1]; char *p = buffer; @@ -318,7 +345,7 @@ static void get_mac(Object *obj, Visitor *v, const char *name, void *opaque, static void set_mac(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; MACAddr *mac = object_field_prop_ptr(obj, prop); int i, pos; char *str; @@ -360,7 +387,7 @@ inval: } const PropertyInfo qdev_prop_macaddr = { - .name = "str", + .type = "str", .description = "Ethernet 6-byte MAC Address, example: 52:54:00:12:34:56", .get = get_mac, .set = set_mac, @@ -380,7 +407,7 @@ void qdev_prop_set_macaddr(DeviceState *dev, const char *name, static void get_netdev(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; NICPeers *peers_ptr = object_field_prop_ptr(obj, prop); char *p = g_strdup(peers_ptr->ncs[0] ? peers_ptr->ncs[0]->name : ""); @@ -391,7 +418,7 @@ static void get_netdev(Object *obj, Visitor *v, const char *name, static void set_netdev(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; NICPeers *peers_ptr = object_field_prop_ptr(obj, prop); NetClientState **ncs = peers_ptr->ncs; NetClientState *peers[MAX_QUEUE_NUM]; @@ -448,7 +475,7 @@ out: } const PropertyInfo qdev_prop_netdev = { - .name = "str", + .type = "str", .description = "ID of a netdev to use as a backend", .get = get_netdev, .set = set_netdev, @@ -459,7 +486,7 @@ const PropertyInfo qdev_prop_netdev = { static void get_audiodev(Object *obj, Visitor *v, const char* name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; QEMUSoundCard *card = object_field_prop_ptr(obj, prop); char *p = g_strdup(audio_get_id(card)); @@ -470,7 +497,7 @@ static void get_audiodev(Object *obj, Visitor *v, const char* name, static void set_audiodev(Object *obj, Visitor *v, const char* name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; QEMUSoundCard *card = object_field_prop_ptr(obj, prop); AudioState *state; g_autofree char *str = NULL; @@ -486,7 +513,7 @@ static void set_audiodev(Object *obj, Visitor *v, const char* name, } const PropertyInfo qdev_prop_audiodev = { - .name = "str", + .type = "str", .description = "ID of an audiodev to use as a backend", /* release done on shutdown */ .get = get_audiodev, @@ -552,7 +579,7 @@ static void qdev_propinfo_set_losttickpolicy(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int *ptr = object_field_prop_ptr(obj, prop); int value; @@ -576,7 +603,8 @@ static void qdev_propinfo_set_losttickpolicy(Object *obj, Visitor *v, QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int)); const PropertyInfo qdev_prop_losttickpolicy = { - .name = "LostTickPolicy", + .type = "LostTickPolicy", + .description = "Policy for handling lost ticks (discard/delay/slew)", .enum_table = &LostTickPolicy_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_losttickpolicy, @@ -588,25 +616,21 @@ const PropertyInfo qdev_prop_losttickpolicy = { static void set_blocksize(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - DeviceState *dev = DEVICE(obj); - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); uint64_t value; - Error *local_err = NULL; if (!visit_type_size(v, name, &value, errp)) { return; } - check_block_size(dev->id ? : "", name, value, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (!check_block_size(name, value, errp)) { return; } *ptr = value; } const PropertyInfo qdev_prop_blocksize = { - .name = "size", + .type = "size", .description = "A power of two between " MIN_BLOCK_SIZE_STR " and " MAX_BLOCK_SIZE_STR, .get = qdev_propinfo_get_size32, @@ -619,9 +643,8 @@ const PropertyInfo qdev_prop_blocksize = { QEMU_BUILD_BUG_ON(sizeof(BlockdevOnError) != sizeof(int)); const PropertyInfo qdev_prop_blockdev_on_error = { - .name = "BlockdevOnError", - .description = "Error handling policy, " - "report/ignore/enospc/stop/auto", + .type = "BlockdevOnError", + .description = "Error handling policy (report/ignore/enospc/stop/auto)", .enum_table = &BlockdevOnError_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -633,9 +656,9 @@ const PropertyInfo qdev_prop_blockdev_on_error = { QEMU_BUILD_BUG_ON(sizeof(BiosAtaTranslation) != sizeof(int)); const PropertyInfo qdev_prop_bios_chs_trans = { - .name = "BiosAtaTranslation", - .description = "Logical CHS translation algorithm, " - "auto/none/lba/large/rechs", + .type = "BiosAtaTranslation", + .description = "Logical CHS translation algorithm " + " (auto/none/lba/large/rechs)", .enum_table = &BiosAtaTranslation_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -645,9 +668,8 @@ const PropertyInfo qdev_prop_bios_chs_trans = { /* --- FDC default drive types */ const PropertyInfo qdev_prop_fdc_drive_type = { - .name = "FdcDriveType", - .description = "FDC drive type, " - "144/288/120/none/auto", + .type = "FloppyDriveType", + .description = "Floppy drive type (144/288/120/none/auto)", .enum_table = &FloppyDriveType_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -657,9 +679,9 @@ const PropertyInfo qdev_prop_fdc_drive_type = { /* --- MultiFDCompression --- */ const PropertyInfo qdev_prop_multifd_compression = { - .name = "MultiFDCompression", - .description = "multifd_compression values, " - "none/zlib/zstd/qpl/uadk", + .type = "MultiFDCompression", + .description = "multifd_compression values" + " (none/zlib/zstd/qpl/uadk/qatzip)", .enum_table = &MultiFDCompression_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -671,9 +693,8 @@ const PropertyInfo qdev_prop_multifd_compression = { QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int)); const PropertyInfo qdev_prop_mig_mode = { - .name = "MigMode", - .description = "mig_mode values, " - "normal,cpr-reboot", + .type = "MigMode", + .description = "Migration mode (normal/cpr-reboot)", .enum_table = &MigMode_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -685,9 +706,8 @@ const PropertyInfo qdev_prop_mig_mode = { QEMU_BUILD_BUG_ON(sizeof(GranuleMode) != sizeof(int)); const PropertyInfo qdev_prop_granule_mode = { - .name = "GranuleMode", - .description = "granule_mode values, " - "4k, 8k, 16k, 64k, host", + .type = "GranuleMode", + .description = "Granule page size (4k/8k/16k/64k/host)", .enum_table = &GranuleMode_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -695,9 +715,8 @@ const PropertyInfo qdev_prop_granule_mode = { }; const PropertyInfo qdev_prop_zero_page_detection = { - .name = "ZeroPageDetection", - .description = "zero_page_detection values, " - "none,legacy,multifd", + .type = "ZeroPageDetection", + .description = "Zero page detection (none/legacy/multifd)", .enum_table = &ZeroPageDetection_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, @@ -715,7 +734,7 @@ const PropertyInfo qdev_prop_zero_page_detection = { static void get_reserved_region(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; ReservedRegion *rr = object_field_prop_ptr(obj, prop); char buffer[64]; char *p = buffer; @@ -731,7 +750,7 @@ static void get_reserved_region(Object *obj, Visitor *v, const char *name, static void set_reserved_region(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; ReservedRegion *rr = object_field_prop_ptr(obj, prop); const char *endptr; uint64_t lob, upb; @@ -775,11 +794,10 @@ separator_error: error_setg(errp, "reserved region fields must be separated with ':'"); out: g_free(str); - return; } const PropertyInfo qdev_prop_reserved_region = { - .name = "reserved_region", + .type = "str", .description = "Reserved Region, example: 0xFEE00000:0xFEEFFFFF:0", .get = get_reserved_region, .set = set_reserved_region, @@ -793,43 +811,61 @@ const PropertyInfo qdev_prop_reserved_region = { static void set_pci_devfn(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; + g_autofree GenericAlternate *alt; int32_t value, *ptr = object_field_prop_ptr(obj, prop); unsigned int slot, fn, n; - char *str; + g_autofree char *str = NULL; + + if (!visit_start_alternate(v, name, &alt, sizeof(*alt), errp)) { + return; + } + + switch (alt->type) { + case QTYPE_QSTRING: + if (!visit_type_str(v, name, &str, errp)) { + goto out; + } - if (!visit_type_str(v, name, &str, NULL)) { + if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) { + fn = 0; + if (sscanf(str, "%x%n", &slot, &n) != 1) { + goto invalid; + } + } + if (str[n] != '\0' || fn > 7 || slot > 31) { + goto invalid; + } + *ptr = slot << 3 | fn; + break; + + case QTYPE_QNUM: if (!visit_type_int32(v, name, &value, errp)) { - return; + goto out; } if (value < -1 || value > 255) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", "a value between -1 and 255"); - return; + goto out; } *ptr = value; - return; - } + break; - if (sscanf(str, "%x.%x%n", &slot, &fn, &n) != 2) { - fn = 0; - if (sscanf(str, "%x%n", &slot, &n) != 1) { - goto invalid; - } - } - if (str[n] != '\0' || fn > 7 || slot > 31) { - goto invalid; + default: + error_setg(errp, "Invalid parameter type for '%s', expected int or str", + name ? name : "null"); + goto out; } - *ptr = slot << 3 | fn; - g_free(str); - return; + + goto out; invalid: error_set_from_qdev_prop_error(errp, EINVAL, obj, name, str); - g_free(str); +out: + visit_end_alternate(v, (void **) &alt); } -static int print_pci_devfn(Object *obj, Property *prop, char *dest, +static int print_pci_devfn(Object *obj, const Property *prop, char *dest, size_t len) { int32_t *ptr = object_field_prop_ptr(obj, prop); @@ -842,7 +878,7 @@ static int print_pci_devfn(Object *obj, Property *prop, char *dest, } const PropertyInfo qdev_prop_pci_devfn = { - .name = "int32", + .type = "str", .description = "Slot and optional function number, example: 06.0 or 06", .print = print_pci_devfn, .get = qdev_propinfo_get_int32, @@ -855,7 +891,7 @@ const PropertyInfo qdev_prop_pci_devfn = { static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIHostDeviceAddress *addr = object_field_prop_ptr(obj, prop); char buffer[] = "ffff:ff:ff.f"; char *p = buffer; @@ -881,7 +917,7 @@ static void get_pci_host_devaddr(Object *obj, Visitor *v, const char *name, static void set_pci_host_devaddr(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIHostDeviceAddress *addr = object_field_prop_ptr(obj, prop); char *str, *p; char *e; @@ -948,8 +984,8 @@ inval: } const PropertyInfo qdev_prop_pci_host_devaddr = { - .name = "str", - .description = "Address (bus/device/function) of " + .type = "str", + .description = "Address (bus:device.function) of " "the host device, example: 04:10.0", .get = get_pci_host_devaddr, .set = set_pci_host_devaddr, @@ -958,7 +994,7 @@ const PropertyInfo qdev_prop_pci_host_devaddr = { /* --- OffAutoPCIBAR off/auto/bar0/bar1/bar2/bar3/bar4/bar5 --- */ const PropertyInfo qdev_prop_off_auto_pcibar = { - .name = "OffAutoPCIBAR", + .type = "OffAutoPCIBAR", .description = "off/auto/bar0/bar1/bar2/bar3/bar4/bar5", .enum_table = &OffAutoPCIBAR_lookup, .get = qdev_propinfo_get_enum, @@ -971,7 +1007,7 @@ const PropertyInfo qdev_prop_off_auto_pcibar = { static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkSpeed *p = object_field_prop_ptr(obj, prop); int speed; @@ -1005,7 +1041,7 @@ static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkSpeed *p = object_field_prop_ptr(obj, prop); int speed; @@ -1040,7 +1076,7 @@ static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_pcie_link_speed = { - .name = "PCIELinkSpeed", + .type = "PCIELinkSpeed", .description = "2_5/5/8/16/32/64", .enum_table = &PCIELinkSpeed_lookup, .get = get_prop_pcielinkspeed, @@ -1053,7 +1089,7 @@ const PropertyInfo qdev_prop_pcie_link_speed = { static void get_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkWidth *p = object_field_prop_ptr(obj, prop); int width; @@ -1090,7 +1126,7 @@ static void get_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, static void set_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; PCIExpLinkWidth *p = object_field_prop_ptr(obj, prop); int width; @@ -1128,7 +1164,7 @@ static void set_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_pcie_link_width = { - .name = "PCIELinkWidth", + .type = "PCIELinkWidth", .description = "1/2/4/8/12/16/32", .enum_table = &PCIELinkWidth_lookup, .get = get_prop_pcielinkwidth, @@ -1141,7 +1177,7 @@ const PropertyInfo qdev_prop_pcie_link_width = { static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; QemuUUID *uuid = object_field_prop_ptr(obj, prop); char buffer[UUID_STR_LEN]; char *p = buffer; @@ -1156,7 +1192,7 @@ static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque, static void set_uuid(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; QemuUUID *uuid = object_field_prop_ptr(obj, prop); char *str; @@ -1178,7 +1214,7 @@ static void set_default_uuid_auto(ObjectProperty *op, const Property *prop) } const PropertyInfo qdev_prop_uuid = { - .name = "str", + .type = "str", .description = "UUID (aka GUID) or \"" UUID_VALUE_AUTO "\" for random value (default)", .get = get_uuid, @@ -1188,12 +1224,12 @@ const PropertyInfo qdev_prop_uuid = { /* --- s390 cpu entitlement policy --- */ -QEMU_BUILD_BUG_ON(sizeof(CpuS390Entitlement) != sizeof(int)); +QEMU_BUILD_BUG_ON(sizeof(S390CpuEntitlement) != sizeof(int)); const PropertyInfo qdev_prop_cpus390entitlement = { - .name = "CpuS390Entitlement", - .description = "low/medium (default)/high", - .enum_table = &CpuS390Entitlement_lookup, + .type = "S390CpuEntitlement", + .description = "auto/low/medium/high (default medium)", + .enum_table = &S390CpuEntitlement_lookup, .get = qdev_propinfo_get_enum, .set = qdev_propinfo_set_enum, .set_default_value = qdev_propinfo_set_default_value_enum, @@ -1236,10 +1272,30 @@ static void release_iothread_vq_mapping_list(Object *obj, } const PropertyInfo qdev_prop_iothread_vq_mapping_list = { - .name = "IOThreadVirtQueueMappingList", + .type = "IOThreadVirtQueueMappingList", .description = "IOThread virtqueue mapping list [{\"iothread\":\"<id>\", " "\"vqs\":[1,2,3,...]},...]", .get = get_iothread_vq_mapping_list, .set = set_iothread_vq_mapping_list, .release = release_iothread_vq_mapping_list, }; + +/* --- Endian modes */ + +const PropertyInfo qdev_prop_endian_mode = { + .type = "EndianMode", + .description = "Endian mode, big/little/unspecified", + .enum_table = &EndianMode_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; + +const PropertyInfo qdev_prop_vmapple_virtio_blk_variant = { + .type = "VMAppleVirtioBlkVariant", + .description = "unspecified/root/aux", + .enum_table = &VMAppleVirtioBlkVariant_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 86a5835..147b3ff 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -2,7 +2,7 @@ #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qapi/qapi-types-misc.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "qemu/ctype.h" #include "qemu/error-report.h" #include "qapi/visitor.h" @@ -51,7 +51,7 @@ void qdev_prop_allow_set_link_before_realize(const Object *obj, } } -void *object_field_prop_ptr(Object *obj, Property *prop) +void *object_field_prop_ptr(Object *obj, const Property *prop) { void *ptr = obj; ptr += prop->offset; @@ -61,7 +61,7 @@ void *object_field_prop_ptr(Object *obj, Property *prop) static void field_prop_get(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; return prop->info->get(obj, v, name, opaque, errp); } @@ -78,7 +78,7 @@ static ObjectPropertyAccessor *field_prop_getter(const PropertyInfo *info) static void field_prop_set(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; if (!qdev_prop_allow_set(obj, name, prop->info, errp)) { return; @@ -100,7 +100,7 @@ static ObjectPropertyAccessor *field_prop_setter(const PropertyInfo *info) void qdev_propinfo_get_enum(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int *ptr = object_field_prop_ptr(obj, prop); visit_type_enum(v, name, ptr, prop->info->enum_table, errp); @@ -109,7 +109,7 @@ void qdev_propinfo_get_enum(Object *obj, Visitor *v, const char *name, void qdev_propinfo_set_enum(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int *ptr = object_field_prop_ptr(obj, prop); visit_type_enum(v, name, ptr, prop->info->enum_table, errp); @@ -122,22 +122,15 @@ void qdev_propinfo_set_default_value_enum(ObjectProperty *op, qapi_enum_lookup(prop->info->enum_table, prop->defval.i)); } -const PropertyInfo qdev_prop_enum = { - .name = "enum", - .get = qdev_propinfo_get_enum, - .set = qdev_propinfo_set_enum, - .set_default_value = qdev_propinfo_set_default_value_enum, -}; - /* Bit */ -static uint32_t qdev_get_prop_mask(Property *prop) +static uint32_t qdev_get_prop_mask(const Property *prop) { assert(prop->info == &qdev_prop_bit); return 0x1 << prop->bitnr; } -static void bit_prop_set(Object *obj, Property *props, bool val) +static void bit_prop_set(Object *obj, const Property *props, bool val) { uint32_t *p = object_field_prop_ptr(obj, props); uint32_t mask = qdev_get_prop_mask(props); @@ -151,7 +144,7 @@ static void bit_prop_set(Object *obj, Property *props, bool val) static void prop_get_bit(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *p = object_field_prop_ptr(obj, prop); bool value = (*p & qdev_get_prop_mask(prop)) != 0; @@ -161,7 +154,7 @@ static void prop_get_bit(Object *obj, Visitor *v, const char *name, static void prop_set_bit(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool value; if (!visit_type_bool(v, name, &value, errp)) { @@ -176,7 +169,7 @@ static void set_default_value_bool(ObjectProperty *op, const Property *prop) } const PropertyInfo qdev_prop_bit = { - .name = "bool", + .type = "bool", .description = "on/off", .get = prop_get_bit, .set = prop_set_bit, @@ -185,13 +178,13 @@ const PropertyInfo qdev_prop_bit = { /* Bit64 */ -static uint64_t qdev_get_prop_mask64(Property *prop) +static uint64_t qdev_get_prop_mask64(const Property *prop) { assert(prop->info == &qdev_prop_bit64); return 0x1ull << prop->bitnr; } -static void bit64_prop_set(Object *obj, Property *props, bool val) +static void bit64_prop_set(Object *obj, const Property *props, bool val) { uint64_t *p = object_field_prop_ptr(obj, props); uint64_t mask = qdev_get_prop_mask64(props); @@ -205,7 +198,7 @@ static void bit64_prop_set(Object *obj, Property *props, bool val) static void prop_get_bit64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *p = object_field_prop_ptr(obj, prop); bool value = (*p & qdev_get_prop_mask64(prop)) != 0; @@ -215,7 +208,7 @@ static void prop_get_bit64(Object *obj, Visitor *v, const char *name, static void prop_set_bit64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool value; if (!visit_type_bool(v, name, &value, errp)) { @@ -225,7 +218,7 @@ static void prop_set_bit64(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_bit64 = { - .name = "bool", + .type = "bool", .description = "on/off", .get = prop_get_bit64, .set = prop_set_bit64, @@ -237,7 +230,7 @@ const PropertyInfo qdev_prop_bit64 = { static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool *ptr = object_field_prop_ptr(obj, prop); visit_type_bool(v, name, ptr, errp); @@ -246,14 +239,15 @@ static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque, static void set_bool(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; bool *ptr = object_field_prop_ptr(obj, prop); visit_type_bool(v, name, ptr, errp); } const PropertyInfo qdev_prop_bool = { - .name = "bool", + .type = "bool", + .description = "on/off", .get = get_bool, .set = set_bool, .set_default_value = set_default_value_bool, @@ -264,7 +258,7 @@ const PropertyInfo qdev_prop_bool = { static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint8_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint8(v, name, ptr, errp); @@ -273,7 +267,7 @@ static void get_uint8(Object *obj, Visitor *v, const char *name, void *opaque, static void set_uint8(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint8_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint8(v, name, ptr, errp); @@ -292,7 +286,7 @@ void qdev_propinfo_set_default_value_uint(ObjectProperty *op, } const PropertyInfo qdev_prop_uint8 = { - .name = "uint8", + .type = "uint8", .get = get_uint8, .set = set_uint8, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -303,7 +297,7 @@ const PropertyInfo qdev_prop_uint8 = { static void get_uint16(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint16_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint16(v, name, ptr, errp); @@ -312,14 +306,14 @@ static void get_uint16(Object *obj, Visitor *v, const char *name, static void set_uint16(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint16_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint16(v, name, ptr, errp); } const PropertyInfo qdev_prop_uint16 = { - .name = "uint16", + .type = "uint16", .get = get_uint16, .set = set_uint16, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -330,7 +324,7 @@ const PropertyInfo qdev_prop_uint16 = { static void get_uint32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint32(v, name, ptr, errp); @@ -339,7 +333,7 @@ static void get_uint32(Object *obj, Visitor *v, const char *name, static void set_uint32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint32(v, name, ptr, errp); @@ -348,7 +342,7 @@ static void set_uint32(Object *obj, Visitor *v, const char *name, void qdev_propinfo_get_int32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int32(v, name, ptr, errp); @@ -357,21 +351,21 @@ void qdev_propinfo_get_int32(Object *obj, Visitor *v, const char *name, static void set_int32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int32_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int32(v, name, ptr, errp); } const PropertyInfo qdev_prop_uint32 = { - .name = "uint32", + .type = "uint32", .get = get_uint32, .set = set_uint32, .set_default_value = qdev_propinfo_set_default_value_uint, }; const PropertyInfo qdev_prop_int32 = { - .name = "int32", + .type = "int32", .get = qdev_propinfo_get_int32, .set = set_int32, .set_default_value = qdev_propinfo_set_default_value_int, @@ -382,7 +376,7 @@ const PropertyInfo qdev_prop_int32 = { static void get_uint64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint64(v, name, ptr, errp); @@ -391,7 +385,7 @@ static void get_uint64(Object *obj, Visitor *v, const char *name, static void set_uint64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint64(v, name, ptr, errp); @@ -400,7 +394,7 @@ static void set_uint64(Object *obj, Visitor *v, const char *name, static void get_int64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int64(v, name, ptr, errp); @@ -409,21 +403,21 @@ static void get_int64(Object *obj, Visitor *v, const char *name, static void set_int64(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; int64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_int64(v, name, ptr, errp); } const PropertyInfo qdev_prop_uint64 = { - .name = "uint64", + .type = "uint64", .get = get_uint64, .set = set_uint64, .set_default_value = qdev_propinfo_set_default_value_uint, }; const PropertyInfo qdev_prop_int64 = { - .name = "int64", + .type = "int64", .get = get_int64, .set = set_int64, .set_default_value = qdev_propinfo_set_default_value_int, @@ -432,7 +426,7 @@ const PropertyInfo qdev_prop_int64 = { static void set_uint64_checkmask(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_uint64(v, name, ptr, errp); @@ -443,23 +437,60 @@ static void set_uint64_checkmask(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_uint64_checkmask = { - .name = "uint64", + .type = "uint64", .get = get_uint64, .set = set_uint64_checkmask, }; +/* --- pointer-size integer --- */ + +static void get_usize(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + const Property *prop = opaque; + +#if HOST_LONG_BITS == 32 + uint32_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint32(v, name, ptr, errp); +#else + uint64_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint64(v, name, ptr, errp); +#endif +} + +static void set_usize(Object *obj, Visitor *v, const char *name, void *opaque, + Error **errp) +{ + const Property *prop = opaque; + +#if HOST_LONG_BITS == 32 + uint32_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint32(v, name, ptr, errp); +#else + uint64_t *ptr = object_field_prop_ptr(obj, prop); + visit_type_uint64(v, name, ptr, errp); +#endif +} + +const PropertyInfo qdev_prop_usize = { + .type = "usize", + .get = get_usize, + .set = set_usize, + .set_default_value = qdev_propinfo_set_default_value_uint, +}; + /* --- string --- */ static void release_string(Object *obj, const char *name, void *opaque) { - Property *prop = opaque; + const Property *prop = opaque; g_free(*(char **)object_field_prop_ptr(obj, prop)); } static void get_string(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; char **ptr = object_field_prop_ptr(obj, prop); if (!*ptr) { @@ -473,7 +504,7 @@ static void get_string(Object *obj, Visitor *v, const char *name, static void set_string(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; char **ptr = object_field_prop_ptr(obj, prop); char *str; @@ -485,7 +516,7 @@ static void set_string(Object *obj, Visitor *v, const char *name, } const PropertyInfo qdev_prop_string = { - .name = "str", + .type = "str", .release = release_string, .get = get_string, .set = set_string, @@ -494,7 +525,7 @@ const PropertyInfo qdev_prop_string = { /* --- on/off/auto --- */ const PropertyInfo qdev_prop_on_off_auto = { - .name = "OnOffAuto", + .type = "OnOffAuto", .description = "on/off/auto", .enum_table = &OnOffAuto_lookup, .get = qdev_propinfo_get_enum, @@ -507,7 +538,7 @@ const PropertyInfo qdev_prop_on_off_auto = { void qdev_propinfo_get_size32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); uint64_t value = *ptr; @@ -517,7 +548,7 @@ void qdev_propinfo_get_size32(Object *obj, Visitor *v, const char *name, static void set_size32(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *ptr = object_field_prop_ptr(obj, prop); uint64_t value; @@ -537,7 +568,7 @@ static void set_size32(Object *obj, Visitor *v, const char *name, void *opaque, } const PropertyInfo qdev_prop_size32 = { - .name = "size", + .type = "size", .get = qdev_propinfo_get_size32, .set = set_size32, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -557,7 +588,7 @@ struct ArrayElementList { * specific element of the array. Arrays are backed by an uint32_t length field * and an element array. @elem points at an element in this element array. */ -static Property array_elem_prop(Object *obj, Property *parent_prop, +static Property array_elem_prop(Object *obj, const Property *parent_prop, const char *name, char *elem) { return (Property) { @@ -582,7 +613,7 @@ static Property array_elem_prop(Object *obj, Property *parent_prop, */ static void release_prop_array(Object *obj, const char *name, void *opaque) { - Property *prop = opaque; + const Property *prop = opaque; uint32_t *alenptr = object_field_prop_ptr(obj, prop); void **arrayptr = (void *)obj + prop->arrayoffset; char *elem = *arrayptr; @@ -609,7 +640,7 @@ static void set_prop_array(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { ERRP_GUARD(); - Property *prop = opaque; + const Property *prop = opaque; uint32_t *alenptr = object_field_prop_ptr(obj, prop); void **arrayptr = (void *)obj + prop->arrayoffset; ArrayElementList *list, *elem, *next; @@ -685,7 +716,7 @@ static void get_prop_array(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { ERRP_GUARD(); - Property *prop = opaque; + const Property *prop = opaque; uint32_t *alenptr = object_field_prop_ptr(obj, prop); void **arrayptr = (void *)obj + prop->arrayoffset; char *elemptr = *arrayptr; @@ -740,7 +771,7 @@ static void default_prop_array(ObjectProperty *op, const Property *prop) } const PropertyInfo qdev_prop_array = { - .name = "list", + .type = "list", .get = get_prop_array, .set = set_prop_array, .release = release_prop_array, @@ -749,29 +780,26 @@ const PropertyInfo qdev_prop_array = { /* --- public helpers --- */ -static Property *qdev_prop_walk(Property *props, const char *name) +static const Property *qdev_prop_walk(DeviceClass *cls, const char *name) { - if (!props) { - return NULL; - } - while (props->name) { - if (strcmp(props->name, name) == 0) { - return props; + for (int i = 0, n = cls->props_count_; i < n; ++i) { + const Property *prop = &cls->props_[i]; + if (strcmp(prop->name, name) == 0) { + return prop; } - props++; } return NULL; } -static Property *qdev_prop_find(DeviceState *dev, const char *name) +static const Property *qdev_prop_find(DeviceState *dev, const char *name) { ObjectClass *class; - Property *prop; + const Property *prop; /* device properties */ class = object_get_class(OBJECT(dev)); do { - prop = qdev_prop_walk(DEVICE_CLASS(class)->props_, name); + prop = qdev_prop_walk(DEVICE_CLASS(class), name); if (prop) { return prop; } @@ -840,7 +868,7 @@ void qdev_prop_set_string(DeviceState *dev, const char *name, const char *value) void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) { - Property *prop; + const Property *prop; prop = qdev_prop_find(dev, name); object_property_set_str(OBJECT(dev), name, @@ -931,7 +959,7 @@ void qdev_prop_set_globals(DeviceState *dev) static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_size(v, name, ptr, errp); @@ -940,14 +968,14 @@ static void get_size(Object *obj, Visitor *v, const char *name, void *opaque, static void set_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; uint64_t *ptr = object_field_prop_ptr(obj, prop); visit_type_size(v, name, ptr, errp); } const PropertyInfo qdev_prop_size = { - .name = "size", + .type = "size", .get = get_size, .set = set_size, .set_default_value = qdev_propinfo_set_default_value_uint, @@ -956,7 +984,7 @@ const PropertyInfo qdev_prop_size = { /* --- object link property --- */ static ObjectProperty *create_link_property(ObjectClass *oc, const char *name, - Property *prop) + const Property *prop) { return object_class_property_add_link(oc, name, prop->link_type, prop->offset, @@ -965,22 +993,22 @@ static ObjectProperty *create_link_property(ObjectClass *oc, const char *name, } const PropertyInfo qdev_prop_link = { - .name = "link", + .type = "link", .create = create_link_property, }; -void qdev_property_add_static(DeviceState *dev, Property *prop) +void qdev_property_add_static(DeviceState *dev, const Property *prop) { Object *obj = OBJECT(dev); ObjectProperty *op; assert(!prop->info->create); - op = object_property_add(obj, prop->name, prop->info->name, + op = object_property_add(obj, prop->name, prop->info->type, field_prop_getter(prop->info), field_prop_setter(prop->info), prop->info->release, - prop); + (Property *)prop); object_property_set_description(obj, prop->name, prop->info->description); @@ -994,7 +1022,7 @@ void qdev_property_add_static(DeviceState *dev, Property *prop) } static void qdev_class_add_property(DeviceClass *klass, const char *name, - Property *prop) + const Property *prop) { ObjectClass *oc = OBJECT_CLASS(klass); ObjectProperty *op; @@ -1003,11 +1031,11 @@ static void qdev_class_add_property(DeviceClass *klass, const char *name, op = prop->info->create(oc, name, prop); } else { op = object_class_property_add(oc, - name, prop->info->name, + name, prop->info->type, field_prop_getter(prop->info), field_prop_setter(prop->info), prop->info->release, - prop); + (Property *)prop); } if (prop->set_default) { prop->info->set_default_value(op, prop); @@ -1023,7 +1051,7 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - Property *prop = opaque; + const Property *prop = opaque; char buffer[1024]; char *ptr = buffer; @@ -1046,7 +1074,7 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, * Do not use this in new code! QOM Properties added through this interface * will be given names in the "legacy" namespace. */ -static void qdev_class_add_legacy_property(DeviceClass *dc, Property *prop) +static void qdev_class_add_legacy_property(DeviceClass *dc, const Property *prop) { g_autofree char *name = NULL; @@ -1058,15 +1086,21 @@ static void qdev_class_add_legacy_property(DeviceClass *dc, Property *prop) name = g_strdup_printf("legacy-%s", prop->name); object_class_property_add(OBJECT_CLASS(dc), name, "str", prop->info->print ? qdev_get_legacy_property : prop->info->get, - NULL, NULL, prop); + NULL, NULL, (Property *)prop); } -void device_class_set_props(DeviceClass *dc, Property *props) +void device_class_set_props_n(DeviceClass *dc, const Property *props, size_t n) { - Property *prop; + /* We used a hole in DeviceClass because that's still a lot. */ + assert(n <= UINT16_MAX); + assert(n != 0); dc->props_ = props; - for (prop = props; prop && prop->name; prop++) { + dc->props_count_ = n; + + for (size_t i = 0; i < n; ++i) { + const Property *prop = &props[i]; + assert(prop->name); qdev_class_add_legacy_property(dc, prop); qdev_class_add_property(dc, prop->name, prop); } diff --git a/hw/core/qdev-user.c b/hw/core/qdev-user.c new file mode 100644 index 0000000..3d421d8 --- /dev/null +++ b/hw/core/qdev-user.c @@ -0,0 +1,19 @@ +/* + * QDev helpers specific to user emulation. + * + * Copyright 2025 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "qom/object.h" +#include "hw/qdev-core.h" + +void qdev_create_fake_machine(void) +{ + Object *fake_machine_obj; + + fake_machine_obj = object_property_add_new_container(object_get_root(), + "machine"); + object_property_add_new_container(fake_machine_obj, "unattached"); +} diff --git a/hw/core/qdev.c b/hw/core/qdev.c index f3a996f..f600226 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qapi-events-qdev.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/visitor.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -146,31 +146,16 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp) DeviceState *qdev_new(const char *name) { - ObjectClass *oc = object_class_by_name(name); -#ifdef CONFIG_MODULES - if (!oc) { - int rv = module_load_qom(name, &error_fatal); - if (rv > 0) { - oc = object_class_by_name(name); - } else { - error_report("could not find a module for type '%s'", name); - exit(1); - } - } -#endif - if (!oc) { - error_report("unknown type '%s'", name); - abort(); - } return DEVICE(object_new(name)); } DeviceState *qdev_try_new(const char *name) { - if (!module_object_class_by_name(name)) { + ObjectClass *oc = module_object_class_by_name(name); + if (!oc) { return NULL; } - return DEVICE(object_new(name)); + return DEVICE(object_new_with_class(oc)); } static QTAILQ_HEAD(, DeviceListener) device_listeners @@ -491,8 +476,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp) if (!obj->parent) { gchar *name = g_strdup_printf("device[%d]", unattached_count++); - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), + object_property_add_child(machine_get_container("unattached"), name, obj); unattached_parent = true; g_free(name); @@ -706,11 +690,10 @@ static void device_finalize(Object *obj) dev->canonical_path = NULL; } - qobject_unref(dev->opts); g_free(dev->id); } -static void device_class_base_init(ObjectClass *class, void *data) +static void device_class_base_init(ObjectClass *class, const void *data) { DeviceClass *klass = DEVICE_CLASS(class); @@ -718,6 +701,7 @@ static void device_class_base_init(ObjectClass *class, void *data) * so do not propagate them to the subclasses. */ klass->props_ = NULL; + klass->props_count_ = 0; } static void device_unparent(Object *obj) @@ -747,58 +731,7 @@ device_vmstate_if_get_id(VMStateIf *obj) return qdev_get_dev_path(dev); } -/** - * device_phases_reset: - * Transition reset method for devices to allow moving - * smoothly from legacy reset method to multi-phases - */ -static void device_phases_reset(DeviceState *dev) -{ - ResettableClass *rc = RESETTABLE_GET_CLASS(dev); - - if (rc->phases.enter) { - rc->phases.enter(OBJECT(dev), RESET_TYPE_COLD); - } - if (rc->phases.hold) { - rc->phases.hold(OBJECT(dev), RESET_TYPE_COLD); - } - if (rc->phases.exit) { - rc->phases.exit(OBJECT(dev), RESET_TYPE_COLD); - } -} - -static void device_transitional_reset(Object *obj) -{ - DeviceClass *dc = DEVICE_GET_CLASS(obj); - - /* - * This will call either @device_phases_reset (for multi-phases transitioned - * devices) or a device's specific method for not-yet transitioned devices. - * In both case, it does not reset children. - */ - if (dc->reset) { - dc->reset(DEVICE(obj)); - } -} - -/** - * device_get_transitional_reset: - * check if the device's class is ready for multi-phase - */ -static ResettableTrFunction device_get_transitional_reset(Object *obj) -{ - DeviceClass *dc = DEVICE_GET_CLASS(obj); - if (dc->reset != device_phases_reset) { - /* - * dc->reset has been overridden by a subclass, - * the device is not ready for multi phase yet. - */ - return device_transitional_reset; - } - return NULL; -} - -static void device_class_init(ObjectClass *class, void *data) +static void device_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); VMStateIfClass *vc = VMSTATE_IF_CLASS(class); @@ -819,20 +752,12 @@ static void device_class_init(ObjectClass *class, void *data) rc->child_foreach = device_reset_child_foreach; /* - * @device_phases_reset is put as the default reset method below, allowing - * to do the multi-phase transition from base classes to leaf classes. It - * allows a legacy-reset Device class to extend a multi-phases-reset - * Device class for the following reason: - * + If a base class B has been moved to multi-phase, then it does not - * override this default reset method and may have defined phase methods. - * + A child class C (extending class B) which uses - * device_class_set_parent_reset() (or similar means) to override the - * reset method will still work as expected. @device_phases_reset function - * will be registered as the parent reset method and effectively call - * parent reset phases. + * A NULL legacy_reset implies a three-phase reset device. Devices can + * only be reset using three-phase aware mechanisms, but we still support + * for transitional purposes leaf classes which set the old legacy_reset + * method via device_class_set_legacy_reset(). */ - dc->reset = device_phases_reset; - rc->get_transitional_function = device_get_transitional_reset; + dc->legacy_reset = NULL; object_class_property_add_bool(class, "realized", device_get_realized, device_set_realized); @@ -844,12 +769,30 @@ static void device_class_init(ObjectClass *class, void *data) offsetof(DeviceState, parent_bus), NULL, 0); } -void device_class_set_parent_reset(DeviceClass *dc, - DeviceReset dev_reset, - DeviceReset *parent_reset) +static void do_legacy_reset(Object *obj, ResetType type) { - *parent_reset = dc->reset; - dc->reset = dev_reset; + DeviceClass *dc = DEVICE_GET_CLASS(obj); + + dc->legacy_reset(DEVICE(obj)); +} + +void device_class_set_legacy_reset(DeviceClass *dc, DeviceReset dev_reset) +{ + /* + * A legacy DeviceClass::reset has identical semantics to the + * three-phase "hold" method, with no "enter" or "exit" + * behaviour. Classes that use this legacy function must be leaf + * classes that do not chain up to their parent class reset. + * There is no mechanism for resetting a device that does not + * use the three-phase APIs, so the only place which calls + * the legacy_reset hook is do_legacy_reset(). + */ + ResettableClass *rc = RESETTABLE_CLASS(dc); + + rc->phases.enter = NULL; + rc->phases.hold = do_legacy_reset; + rc->phases.exit = NULL; + dc->legacy_reset = dev_reset; } void device_class_set_parent_realize(DeviceClass *dc, @@ -873,12 +816,28 @@ Object *qdev_get_machine(void) static Object *dev; if (dev == NULL) { - dev = container_get(object_get_root(), "/machine"); + dev = object_resolve_path_component(object_get_root(), "machine"); + /* + * Any call to this function before machine is created is treated + * as a programming error as of now. + */ + assert(dev); } return dev; } +Object *machine_get_container(const char *name) +{ + Object *container, *machine; + + machine = qdev_get_machine(); + container = object_resolve_path_component(machine, name); + assert(object_dynamic_cast(container, TYPE_CONTAINER)); + + return container; +} + char *qdev_get_human_name(DeviceState *dev) { g_assert(dev != NULL); @@ -911,7 +870,7 @@ static const TypeInfo device_type_info = { .class_init = device_class_init, .abstract = true, .class_size = sizeof(DeviceClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_VMSTATE_IF }, { TYPE_RESETTABLE_INTERFACE }, { } diff --git a/hw/core/register.c b/hw/core/register.c index 95b0150..8f63d9f 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -319,7 +319,7 @@ void register_finalize_block(RegisterInfoArray *r_array) g_free(r_array); } -static void register_class_init(ObjectClass *oc, void *data) +static void register_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/core/reset.c b/hw/core/reset.c index 58dfc8d..65f82fa 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -24,7 +24,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/reset.h" +#include "system/reset.h" #include "hw/resettable.h" #include "hw/core/resetcontainer.h" @@ -84,7 +84,7 @@ static void legacy_reset_finalize(Object *obj) { } -static void legacy_reset_class_init(ObjectClass *klass, void *data) +static void legacy_reset_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -170,11 +170,8 @@ void qemu_unregister_resettable(Object *obj) resettable_container_remove(get_root_reset_container(), obj); } -void qemu_devices_reset(ShutdownCause reason) +void qemu_devices_reset(ResetType type) { - ResetType type = (reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD) ? - RESET_TYPE_SNAPSHOT_LOAD : RESET_TYPE_COLD; - /* Reset the simulation */ resettable_reset(OBJECT(get_root_reset_container()), type); } diff --git a/hw/core/resetcontainer.c b/hw/core/resetcontainer.c index e4ece68..5ff1700 100644 --- a/hw/core/resetcontainer.c +++ b/hw/core/resetcontainer.c @@ -68,7 +68,8 @@ static void resettable_container_finalize(Object *obj) { } -static void resettable_container_class_init(ObjectClass *klass, void *data) +static void resettable_container_class_init(ObjectClass *klass, + const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/core/resettable.c b/hw/core/resettable.c index 6dd3e3d..5cdb4a4 100644 --- a/hw/core/resettable.c +++ b/hw/core/resettable.c @@ -93,20 +93,6 @@ static void resettable_child_foreach(ResettableClass *rc, Object *obj, } } -/** - * resettable_get_tr_func: - * helper to fetch transitional reset callback if any. - */ -static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc, - Object *obj) -{ - ResettableTrFunction tr_func = NULL; - if (rc->get_transitional_function) { - tr_func = rc->get_transitional_function(obj); - } - return tr_func; -} - static void resettable_phase_enter(Object *obj, void *opaque, ResetType type) { ResettableClass *rc = RESETTABLE_GET_CLASS(obj); @@ -146,7 +132,7 @@ static void resettable_phase_enter(Object *obj, void *opaque, ResetType type) if (action_needed) { trace_resettable_phase_enter_exec(obj, obj_typename, type, !!rc->phases.enter); - if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) { + if (rc->phases.enter) { rc->phases.enter(obj, type); } s->hold_phase_pending = true; @@ -171,12 +157,8 @@ static void resettable_phase_hold(Object *obj, void *opaque, ResetType type) /* exec hold phase */ if (s->hold_phase_pending) { s->hold_phase_pending = false; - ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj); trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold); - if (tr_func) { - trace_resettable_transitional_function(obj, obj_typename); - tr_func(obj); - } else if (rc->phases.hold) { + if (rc->phases.hold) { rc->phases.hold(obj, type); } } @@ -199,7 +181,7 @@ static void resettable_phase_exit(Object *obj, void *opaque, ResetType type) assert(s->count > 0); if (--s->count == 0) { trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit); - if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) { + if (rc->phases.exit) { rc->phases.exit(obj, type); } } diff --git a/hw/core/split-irq.c b/hw/core/split-irq.c index 3b90af2..f8b4875 100644 --- a/hw/core/split-irq.c +++ b/hw/core/split-irq.c @@ -59,12 +59,11 @@ static void split_irq_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, s->out_irq, s->num_lines); } -static Property split_irq_properties[] = { +static const Property split_irq_properties[] = { DEFINE_PROP_UINT16("num-lines", SplitIRQ, num_lines, 1), - DEFINE_PROP_END_OF_LIST(), }; -static void split_irq_class_init(ObjectClass *klass, void *data) +static void split_irq_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index eebcd28..c339a27 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -29,13 +29,15 @@ #endif #include "hw/core/sysbus-fdt.h" #include "qemu/error-report.h" -#include "sysemu/device_tree.h" -#include "sysemu/tpm.h" +#include "system/device_tree.h" +#include "system/tpm.h" #include "hw/platform-bus.h" #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" #include "hw/vfio/vfio-amd-xgbe.h" +#include "hw/vfio/vfio-region.h" #include "hw/display/ramfb.h" +#include "hw/uefi/var-service-api.h" #include "hw/arm/fdt.h" /* @@ -471,6 +473,28 @@ static int add_tpm_tis_fdt_node(SysBusDevice *sbdev, void *opaque) } #endif +static int add_uefi_vars_node(SysBusDevice *sbdev, void *opaque) +{ + PlatformBusFDTData *data = opaque; + PlatformBusDevice *pbus = data->pbus; + const char *parent_node = data->pbus_node_name; + void *fdt = data->fdt; + uint64_t mmio_base; + char *nodename; + + mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, + UEFI_VARS_FDT_NODE, mmio_base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, + "compatible", UEFI_VARS_FDT_COMPAT); + qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + 1, mmio_base, + 1, UEFI_VARS_REGS_SIZE); + g_free(nodename); + return 0; +} + static int no_fdt_node(SysBusDevice *sbdev, void *opaque) { return 0; @@ -495,6 +519,7 @@ static const BindingEntry bindings[] = { TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), #endif TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node), + TYPE_BINDING(TYPE_UEFI_VARS_SYSBUS, add_uefi_vars_node), TYPE_BINDING("", NULL), /* last element */ }; diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index ad34fb7..e71367a 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -19,10 +19,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qemu/module.h" #include "hw/sysbus.h" #include "monitor/monitor.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *sysbus_get_fw_dev_path(DeviceState *dev); @@ -65,14 +64,14 @@ void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque) }; /* Loop through all sysbus devices that were spawned outside the machine */ - container = container_get(qdev_get_machine(), "/peripheral"); + container = machine_get_container("peripheral"); find_sysbus_device(container, &find); - container = container_get(qdev_get_machine(), "/peripheral-anon"); + container = machine_get_container("peripheral-anon"); find_sysbus_device(container, &find); } -static void system_bus_class_init(ObjectClass *klass, void *data) +static void system_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -80,13 +79,6 @@ static void system_bus_class_init(ObjectClass *klass, void *data) k->get_fw_dev_path = sysbus_get_fw_dev_path; } -static const TypeInfo system_bus_info = { - .name = TYPE_SYSTEM_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(BusState), - .class_init = system_bus_class_init, -}; - /* Check whether an IRQ source exists */ bool sysbus_has_irq(SysBusDevice *dev, int n) { @@ -154,16 +146,6 @@ static void sysbus_mmio_map_common(SysBusDevice *dev, int n, hwaddr addr, } } -void sysbus_mmio_unmap(SysBusDevice *dev, int n) -{ - assert(n >= 0 && n < dev->num_mmio); - - if (dev->mmio[n].addr != (hwaddr)-1) { - memory_region_del_subregion(get_system_memory(), dev->mmio[n].memory); - dev->mmio[n].addr = (hwaddr)-1; - } -} - void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr) { sysbus_mmio_map_common(dev, n, addr, false, 0); @@ -298,7 +280,7 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev) return g_strdup(qdev_fw_name(dev)); } -static void sysbus_device_class_init(ObjectClass *klass, void *data) +static void sysbus_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = sysbus_device_realize; @@ -316,15 +298,6 @@ static void sysbus_device_class_init(ObjectClass *klass, void *data) k->user_creatable = false; } -static const TypeInfo sysbus_device_type_info = { - .name = TYPE_SYS_BUS_DEVICE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(SysBusDevice), - .abstract = true, - .class_size = sizeof(SysBusDeviceClass), - .class_init = sysbus_device_class_init, -}; - static BusState *main_system_bus; static void main_system_bus_create(void) @@ -333,8 +306,8 @@ static void main_system_bus_create(void) * assign main_system_bus before qbus_init() * in order to make "if (bus != sysbus_get_default())" work */ - main_system_bus = g_malloc0(system_bus_info.instance_size); - qbus_init(main_system_bus, system_bus_info.instance_size, + main_system_bus = g_new0(BusState, 1); + qbus_init(main_system_bus, sizeof(BusState), TYPE_SYSTEM_BUS, NULL, "main-system-bus"); OBJECT(main_system_bus)->free = g_free; } @@ -347,10 +320,36 @@ BusState *sysbus_get_default(void) return main_system_bus; } -static void sysbus_register_types(void) +static void dynamic_sysbus_device_class_init(ObjectClass *klass, + const void *data) { - type_register_static(&system_bus_info); - type_register_static(&sysbus_device_type_info); + DeviceClass *k = DEVICE_CLASS(klass); + + k->user_creatable = true; + k->hotpluggable = false; } -type_init(sysbus_register_types) +static const TypeInfo sysbus_types[] = { + { + .name = TYPE_SYSTEM_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(BusState), + .class_init = system_bus_class_init, + }, + { + .name = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SysBusDevice), + .abstract = true, + .class_size = sizeof(SysBusDeviceClass), + .class_init = sysbus_device_class_init, + }, + { + .name = TYPE_DYNAMIC_SYS_BUS_DEVICE, + .parent = TYPE_SYS_BUS_DEVICE, + .class_init = dynamic_sysbus_device_class_init, + .abstract = true, + } +}; + +DEFINE_TYPES(sysbus_types) diff --git a/hw/core/uboot_image.h b/hw/core/uboot_image.h index 18ac293..e4dcfb0 100644 --- a/hw/core/uboot_image.h +++ b/hw/core/uboot_image.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* * (C) Copyright 2008 Semihalf * diff --git a/hw/core/vm-change-state-handler.c b/hw/core/vm-change-state-handler.c index 8e26392..99c642b 100644 --- a/hw/core/vm-change-state-handler.c +++ b/hw/core/vm-change-state-handler.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "hw/qdev-core.h" -#include "sysemu/runstate.h" +#include "system/runstate.h" static int qdev_get_dev_tree_depth(DeviceState *dev) { @@ -40,6 +40,7 @@ static int qdev_get_dev_tree_depth(DeviceState *dev) * qdev_add_vm_change_state_handler: * @dev: the device that owns this handler * @cb: the callback function to be invoked + * @cb_ret: the callback function with return value to be invoked * @opaque: user data passed to the callback function * * This function works like qemu_add_vm_change_state_handler() except callbacks @@ -50,25 +51,30 @@ static int qdev_get_dev_tree_depth(DeviceState *dev) * controller's callback is invoked before the children on its bus when the VM * starts running. The order is reversed when the VM stops running. * + * Note that the parameter `cb` and `cb_ret` are mutually exclusive. + * * Returns: an entry to be freed with qemu_del_vm_change_state_handler() */ VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev, VMChangeStateHandler *cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque) { - return qdev_add_vm_change_state_handler_full(dev, cb, NULL, opaque); + assert(!cb || !cb_ret); + return qdev_add_vm_change_state_handler_full(dev, cb, NULL, cb_ret, opaque); } /* * Exactly like qdev_add_vm_change_state_handler() but passes a prepare_cb - * argument too. + * and the cb_ret arguments too. */ VMChangeStateEntry *qdev_add_vm_change_state_handler_full( - DeviceState *dev, VMChangeStateHandler *cb, - VMChangeStateHandler *prepare_cb, void *opaque) + DeviceState *dev, VMChangeStateHandler *cb, VMChangeStateHandler *prepare_cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque) { int depth = qdev_get_dev_tree_depth(dev); - return qemu_add_vm_change_state_handler_prio_full(cb, prepare_cb, opaque, - depth); + assert(!cb || !cb_ret); + return qemu_add_vm_change_state_handler_prio_full(cb, prepare_cb, cb_ret, + opaque, depth); } |