aboutsummaryrefslogtreecommitdiff
path: root/hw/core
diff options
context:
space:
mode:
Diffstat (limited to 'hw/core')
-rw-r--r--hw/core/bus.c80
-rw-r--r--hw/core/clock-vmstate.c7
-rw-r--r--hw/core/clock.c10
-rw-r--r--hw/core/cpu-common.c107
-rw-r--r--hw/core/cpu-sysemu.c11
-rw-r--r--hw/core/generic-loader.c13
-rw-r--r--hw/core/gpio.c198
-rw-r--r--hw/core/guest-loader.c2
-rw-r--r--hw/core/irq.c24
-rw-r--r--hw/core/loader-fit.c2
-rw-r--r--hw/core/loader.c439
-rw-r--r--hw/core/machine-hmp-cmds.c251
-rw-r--r--hw/core/machine-qmp-cmds.c202
-rw-r--r--hw/core/machine-smp.c275
-rw-r--r--hw/core/machine.c654
-rw-r--r--hw/core/meson.build40
-rw-r--r--hw/core/nmi.c3
-rw-r--r--hw/core/numa.c58
-rw-r--r--hw/core/or-irq.c24
-rw-r--r--hw/core/ptimer.c5
-rw-r--r--hw/core/qdev-clock.c2
-rw-r--r--hw/core/qdev-hotplug.c73
-rw-r--r--hw/core/qdev-properties-system.c196
-rw-r--r--hw/core/qdev-properties.c289
-rw-r--r--hw/core/qdev.c364
-rw-r--r--hw/core/reset.c165
-rw-r--r--hw/core/resetcontainer.c77
-rw-r--r--hw/core/resettable.c3
-rw-r--r--hw/core/sysbus-fdt.c572
-rw-r--r--hw/core/sysbus.c25
-rw-r--r--hw/core/trace-events9
-rw-r--r--hw/core/uboot_image.h213
-rw-r--r--hw/core/vm-change-state-handler.c14
33 files changed, 3305 insertions, 1102 deletions
diff --git a/hw/core/bus.c b/hw/core/bus.c
index 9cfbc3a687..b9d89495cd 100644
--- a/hw/core/bus.c
+++ b/hw/core/bus.c
@@ -99,7 +99,8 @@ static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb,
}
}
-static void qbus_init(BusState *bus, DeviceState *parent, const char *name)
+static void qbus_init_internal(BusState *bus, DeviceState *parent,
+ const char *name)
{
const char *typename = object_get_typename(OBJECT(bus));
BusClass *bc;
@@ -151,19 +152,19 @@ static void bus_unparent(Object *obj)
bus->parent = NULL;
}
-void qbus_create_inplace(void *bus, size_t size, const char *typename,
- DeviceState *parent, const char *name)
+void qbus_init(void *bus, size_t size, const char *typename,
+ DeviceState *parent, const char *name)
{
object_initialize(bus, size, typename);
- qbus_init(bus, parent, name);
+ qbus_init_internal(bus, parent, name);
}
-BusState *qbus_create(const char *typename, DeviceState *parent, const char *name)
+BusState *qbus_new(const char *typename, DeviceState *parent, const char *name)
{
BusState *bus;
bus = BUS(object_new(typename));
- qbus_init(bus, parent, name);
+ qbus_init_internal(bus, parent, name);
return bus;
}
@@ -231,57 +232,6 @@ static char *default_bus_get_fw_dev_path(DeviceState *dev)
return g_strdup(object_get_typename(OBJECT(dev)));
}
-/**
- * bus_phases_reset:
- * Transition reset method for buses to allow moving
- * smoothly from legacy reset method to multi-phases
- */
-static void bus_phases_reset(BusState *bus)
-{
- ResettableClass *rc = RESETTABLE_GET_CLASS(bus);
-
- if (rc->phases.enter) {
- rc->phases.enter(OBJECT(bus), RESET_TYPE_COLD);
- }
- if (rc->phases.hold) {
- rc->phases.hold(OBJECT(bus));
- }
- if (rc->phases.exit) {
- rc->phases.exit(OBJECT(bus));
- }
-}
-
-static void bus_transitional_reset(Object *obj)
-{
- BusClass *bc = BUS_GET_CLASS(obj);
-
- /*
- * This will call either @bus_phases_reset (for multi-phases transitioned
- * buses) or a bus's specific method for not-yet transitioned buses.
- * In both case, it does not reset children.
- */
- if (bc->reset) {
- bc->reset(BUS(obj));
- }
-}
-
-/**
- * bus_get_transitional_reset:
- * check if the bus's class is ready for multi-phase
- */
-static ResettableTrFunction bus_get_transitional_reset(Object *obj)
-{
- BusClass *dc = BUS_GET_CLASS(obj);
- if (dc->reset != bus_phases_reset) {
- /*
- * dc->reset has been overridden by a subclass,
- * the bus is not ready for multi phase yet.
- */
- return bus_transitional_reset;
- }
- return NULL;
-}
-
static void bus_class_init(ObjectClass *class, void *data)
{
BusClass *bc = BUS_CLASS(class);
@@ -292,22 +242,6 @@ static void bus_class_init(ObjectClass *class, void *data)
rc->get_state = bus_get_reset_state;
rc->child_foreach = bus_reset_child_foreach;
-
- /*
- * @bus_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 Bus class to extend a multi-phases-reset
- * Bus 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
- * bus_class_set_parent_reset() (or similar means) to override the
- * reset method will still work as expected. @bus_phases_reset function
- * will be registered as the parent reset method and effectively call
- * parent reset phases.
- */
- bc->reset = bus_phases_reset;
- rc->get_transitional_function = bus_get_transitional_reset;
}
static void qbus_finalize(Object *obj)
diff --git a/hw/core/clock-vmstate.c b/hw/core/clock-vmstate.c
index 9d9174ffbd..e831fc596f 100644
--- a/hw/core/clock-vmstate.c
+++ b/hw/core/clock-vmstate.c
@@ -41,9 +41,10 @@ const VMStateDescription vmstate_muldiv = {
.version_id = 1,
.minimum_version_id = 1,
.needed = muldiv_needed,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT32(multiplier, Clock),
VMSTATE_UINT32(divider, Clock),
+ VMSTATE_END_OF_LIST()
},
};
@@ -52,11 +53,11 @@ const VMStateDescription vmstate_clock = {
.version_id = 0,
.minimum_version_id = 0,
.pre_load = clock_pre_load,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT64(period, Clock),
VMSTATE_END_OF_LIST()
},
- .subsections = (const VMStateDescription*[]) {
+ .subsections = (const VMStateDescription * const []) {
&vmstate_muldiv,
NULL
},
diff --git a/hw/core/clock.c b/hw/core/clock.c
index 916875e07a..a19c7db7df 100644
--- a/hw/core/clock.c
+++ b/hw/core/clock.c
@@ -68,7 +68,7 @@ static uint64_t clock_get_child_period(Clock *clk)
{
/*
* Return the period to be used for child clocks, which is the parent
- * clock period adjusted for for multiplier and divider effects.
+ * clock period adjusted for multiplier and divider effects.
*/
return muldiv64(clk->period, clk->multiplier, clk->divider);
}
@@ -143,14 +143,20 @@ char *clock_display_freq(Clock *clk)
return freq_to_str(clock_get_hz(clk));
}
-void clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider)
+bool clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider)
{
assert(divider != 0);
+ if (clk->multiplier == multiplier && clk->divider == divider) {
+ return false;
+ }
+
trace_clock_set_mul_div(CLOCK_PATH(clk), clk->multiplier, multiplier,
clk->divider, divider);
clk->multiplier = multiplier;
clk->divider = divider;
+
+ return true;
}
static void clock_initfn(Object *obj)
diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c
index e2f5a64604..4bd9c70a83 100644
--- a/hw/core/cpu-common.c
+++ b/hw/core/cpu-common.c
@@ -22,17 +22,14 @@
#include "qapi/error.h"
#include "hw/core/cpu.h"
#include "sysemu/hw_accel.h"
-#include "qemu/notify.h"
#include "qemu/log.h"
#include "qemu/main-loop.h"
#include "exec/log.h"
-#include "exec/cpu-common.h"
-#include "qemu/error-report.h"
-#include "qemu/qemu-print.h"
+#include "exec/gdbstub.h"
#include "sysemu/tcg.h"
#include "hw/boards.h"
#include "hw/qdev-properties.h"
-#include "trace/trace-root.h"
+#include "trace.h"
#include "qemu/plugin.h"
CPUState *cpu_by_arch_id(int64_t id)
@@ -70,14 +67,14 @@ CPUState *cpu_create(const char *typename)
* BQL here if we need to. cpu_interrupt assumes it is held.*/
void cpu_reset_interrupt(CPUState *cpu, int mask)
{
- bool need_lock = !qemu_mutex_iothread_locked();
+ bool need_lock = !bql_locked();
if (need_lock) {
- qemu_mutex_lock_iothread();
+ bql_lock();
}
cpu->interrupt_request &= ~mask;
if (need_lock) {
- qemu_mutex_unlock_iothread();
+ bql_unlock();
}
}
@@ -86,7 +83,7 @@ void cpu_exit(CPUState *cpu)
qatomic_set(&cpu->exit_request, 1);
/* Ensure cpu_exec will see the exit request after TCG has exited. */
smp_wmb();
- qatomic_set(&cpu->icount_decr_ptr->u16.high, -1);
+ qatomic_set(&cpu->neg.icount_decr.u16.high, -1);
}
static int cpu_common_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg)
@@ -113,12 +110,12 @@ void cpu_reset(CPUState *cpu)
{
device_cold_reset(DEVICE(cpu));
- trace_guest_cpu_reset(cpu);
+ trace_cpu_reset(cpu->cpu_index);
}
-static void cpu_common_reset(DeviceState *dev)
+static void cpu_common_reset_hold(Object *obj)
{
- CPUState *cpu = CPU(dev);
+ CPUState *cpu = CPU(obj);
CPUClass *cc = CPU_GET_CLASS(cpu);
if (qemu_loglevel_mask(CPU_LOG_RESET)) {
@@ -130,17 +127,13 @@ static void cpu_common_reset(DeviceState *dev)
cpu->halted = cpu->start_powered_off;
cpu->mem_io_pc = 0;
cpu->icount_extra = 0;
- qatomic_set(&cpu->icount_decr_ptr->u32, 0);
- cpu->can_do_io = 1;
+ qatomic_set(&cpu->neg.icount_decr.u32, 0);
+ cpu->neg.can_do_io = true;
cpu->exception_index = -1;
cpu->crash_occurred = false;
cpu->cflags_next_tb = -1;
- if (tcg_enabled()) {
- cpu_tb_jmp_cache_clear(cpu);
-
- tcg_flush_softmmu_tlb(cpu);
- }
+ cpu_exec_reset_hold(cpu);
}
static bool cpu_common_has_work(CPUState *cs)
@@ -150,10 +143,20 @@ static bool cpu_common_has_work(CPUState *cs)
ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model)
{
- CPUClass *cc = CPU_CLASS(object_class_by_name(typename));
+ ObjectClass *oc;
+ CPUClass *cc;
+
+ oc = object_class_by_name(typename);
+ cc = CPU_CLASS(oc);
+ assert(cc->class_by_name);
+ assert(cpu_model);
+ oc = cc->class_by_name(cpu_model);
+ if (object_class_dynamic_cast(oc, typename) &&
+ !object_class_is_abstract(oc)) {
+ return oc;
+ }
- assert(cpu_model && cc->class_by_name);
- return cc->class_by_name(cpu_model);
+ return NULL;
}
static void cpu_common_parse_features(const char *typename, char *features,
@@ -187,6 +190,13 @@ static void cpu_common_parse_features(const char *typename, char *features,
}
}
+#ifdef CONFIG_PLUGIN
+static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused)
+{
+ qemu_plugin_vcpu_init_hook(cpu);
+}
+#endif
+
static void cpu_common_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cpu = CPU(dev);
@@ -197,8 +207,7 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp)
* no need to check the ignore_memory_transaction_failures board flag.
*/
if (object_dynamic_cast(machine, TYPE_MACHINE)) {
- ObjectClass *oc = object_get_class(machine);
- MachineClass *mc = MACHINE_CLASS(oc);
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
if (mc) {
cpu->ignore_memory_transaction_failures =
@@ -211,33 +220,45 @@ static void cpu_common_realizefn(DeviceState *dev, Error **errp)
cpu_resume(cpu);
}
+ /* Plugin initialization must wait until the cpu start executing code */
+#ifdef CONFIG_PLUGIN
+ if (tcg_enabled()) {
+ cpu->plugin_state = qemu_plugin_create_vcpu_state();
+ async_run_on_cpu(cpu, qemu_plugin_vcpu_init__async, RUN_ON_CPU_NULL);
+ }
+#endif
+
/* NOTE: latest generic point where the cpu is fully realized */
- trace_init_vcpu(cpu);
}
static void cpu_common_unrealizefn(DeviceState *dev)
{
CPUState *cpu = CPU(dev);
+ /* Call the plugin hook before clearing the cpu is fully unrealized */
+ if (tcg_enabled()) {
+ qemu_plugin_vcpu_exit_hook(cpu);
+ }
+
/* NOTE: latest generic point before the cpu is fully unrealized */
- trace_fini_vcpu(cpu);
cpu_exec_unrealizefn(cpu);
}
static void cpu_common_initfn(Object *obj)
{
CPUState *cpu = CPU(obj);
- CPUClass *cc = CPU_GET_CLASS(obj);
+ gdb_init_cpu(cpu);
cpu->cpu_index = UNASSIGNED_CPU_INDEX;
cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX;
- cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs;
- /* *-user doesn't have configurable SMP topology */
- /* the default value is changed by qemu_init_vcpu() for softmmu */
+ /* 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;
qemu_mutex_init(&cpu->work_mutex);
+ qemu_lockcnt_init(&cpu->in_ioctl_lock);
QSIMPLEQ_INIT(&cpu->work_list);
QTAILQ_INIT(&cpu->breakpoints);
QTAILQ_INIT(&cpu->watchpoints);
@@ -249,6 +270,8 @@ static void cpu_common_finalize(Object *obj)
{
CPUState *cpu = CPU(obj);
+ g_array_free(cpu->gdb_regs, TRUE);
+ qemu_lockcnt_destroy(&cpu->in_ioctl_lock);
qemu_mutex_destroy(&cpu->work_mutex);
}
@@ -257,24 +280,10 @@ static int64_t cpu_common_get_arch_id(CPUState *cpu)
return cpu->cpu_index;
}
-static Property cpu_common_props[] = {
-#ifndef CONFIG_USER_ONLY
- /* Create a memory property for softmmu CPU object,
- * so users can wire up its memory. (This can't go in hw/core/cpu.c
- * because that file is compiled only once for both user-mode
- * and system builds.) 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 *),
-#endif
- DEFINE_PROP_BOOL("start-powered-off", CPUState, start_powered_off, false),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void cpu_class_init(ObjectClass *klass, void *data)
+static void cpu_common_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
CPUClass *k = CPU_CLASS(klass);
k->parse_features = cpu_common_parse_features;
@@ -285,8 +294,8 @@ static void cpu_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_CPU, dc->categories);
dc->realize = cpu_common_realizefn;
dc->unrealize = cpu_common_unrealizefn;
- dc->reset = cpu_common_reset;
- device_class_set_props(dc, cpu_common_props);
+ rc->phases.hold = cpu_common_reset_hold;
+ cpu_class_init_props(dc);
/*
* Reason: CPUs still need special care by board code: wiring up
* IRQs, adding reset handlers, halting non-first CPUs, ...
@@ -302,7 +311,7 @@ static const TypeInfo cpu_type_info = {
.instance_finalize = cpu_common_finalize,
.abstract = true,
.class_size = sizeof(CPUClass),
- .class_init = cpu_class_init,
+ .class_init = cpu_common_class_init,
};
static void cpu_register_types(void)
diff --git a/hw/core/cpu-sysemu.c b/hw/core/cpu-sysemu.c
index 00253f8929..d0d6a910f9 100644
--- a/hw/core/cpu-sysemu.c
+++ b/hw/core/cpu-sysemu.c
@@ -34,17 +34,17 @@ bool cpu_paging_enabled(const CPUState *cpu)
return false;
}
-void cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list,
+bool cpu_get_memory_mapping(CPUState *cpu, MemoryMappingList *list,
Error **errp)
{
CPUClass *cc = CPU_GET_CLASS(cpu);
if (cc->sysemu_ops->get_memory_mapping) {
- cc->sysemu_ops->get_memory_mapping(cpu, list, errp);
- return;
+ 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,
@@ -69,11 +69,10 @@ hwaddr cpu_get_phys_page_debug(CPUState *cpu, vaddr addr)
int cpu_asidx_from_attrs(CPUState *cpu, MemTxAttrs attrs)
{
- CPUClass *cc = CPU_GET_CLASS(cpu);
int ret = 0;
- if (cc->sysemu_ops->asidx_from_attrs) {
- ret = cc->sysemu_ops->asidx_from_attrs(cpu, attrs);
+ 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;
diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c
index d14f932eea..d4b5c501d8 100644
--- a/hw/core/generic-loader.c
+++ b/hw/core/generic-loader.c
@@ -24,7 +24,7 @@
* callback that does the memory operations.
* This device allows the user to monkey patch memory. To be able to do
- * this it needs a backend to manage the datas, the same as other
+ * this it needs a backend to manage the data, the same as other
* memory-related devices. In this case as the backend is so trivial we
* have merged it with the frontend instead of creating and maintaining a
* separate backend.
@@ -56,8 +56,9 @@ static void generic_loader_reset(void *opaque)
}
if (s->data_len) {
- assert(s->data_len < sizeof(s->data));
- dma_memory_write(s->cpu->as, s->addr, &s->data, s->data_len);
+ assert(s->data_len <= sizeof(s->data));
+ dma_memory_write(s->cpu->as, s->addr, &s->data, s->data_len,
+ MEMTXATTRS_UNSPECIFIED);
}
}
@@ -66,7 +67,7 @@ static void generic_loader_realize(DeviceState *dev, Error **errp)
GenericLoaderState *s = GENERIC_LOADER(dev);
hwaddr entry;
int big_endian;
- int size = 0;
+ ssize_t size = 0;
s->set_pc = false;
@@ -165,7 +166,7 @@ static void generic_loader_realize(DeviceState *dev, Error **errp)
}
}
- /* Convert the data endiannes */
+ /* Convert the data endianness */
if (s->data_be) {
s->data = cpu_to_be64(s->data);
} else {
@@ -206,7 +207,7 @@ static void generic_loader_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
-static TypeInfo generic_loader_info = {
+static const TypeInfo generic_loader_info = {
.name = TYPE_GENERIC_LOADER,
.parent = TYPE_DEVICE,
.instance_size = sizeof(GenericLoaderState),
diff --git a/hw/core/gpio.c b/hw/core/gpio.c
new file mode 100644
index 0000000000..80d07a6ec9
--- /dev/null
+++ b/hw/core/gpio.c
@@ -0,0 +1,198 @@
+/*
+ * qdev GPIO helpers
+ *
+ * Copyright (c) 2009 CodeSourcery
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-core.h"
+#include "hw/irq.h"
+#include "qapi/error.h"
+
+static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev,
+ const char *name)
+{
+ NamedGPIOList *ngl;
+
+ QLIST_FOREACH(ngl, &dev->gpios, node) {
+ /* NULL is a valid and matchable name. */
+ if (g_strcmp0(name, ngl->name) == 0) {
+ return ngl;
+ }
+ }
+
+ ngl = g_malloc0(sizeof(*ngl));
+ ngl->name = g_strdup(name);
+ QLIST_INSERT_HEAD(&dev->gpios, ngl, node);
+ return ngl;
+}
+
+void qdev_init_gpio_in_named_with_opaque(DeviceState *dev,
+ qemu_irq_handler handler,
+ void *opaque,
+ const char *name, int n)
+{
+ int i;
+ NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
+
+ assert(gpio_list->num_out == 0 || !name);
+ gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler,
+ opaque, n);
+
+ if (!name) {
+ name = "unnamed-gpio-in";
+ }
+ for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) {
+ gchar *propname = g_strdup_printf("%s[%u]", name, i);
+
+ object_property_add_child(OBJECT(dev), propname,
+ OBJECT(gpio_list->in[i]));
+ g_free(propname);
+ }
+
+ gpio_list->num_in += n;
+}
+
+void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
+{
+ qdev_init_gpio_in_named(dev, handler, NULL, n);
+}
+
+void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
+ const char *name, int n)
+{
+ int i;
+ NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
+
+ assert(gpio_list->num_in == 0 || !name);
+
+ if (!name) {
+ name = "unnamed-gpio-out";
+ }
+ memset(pins, 0, sizeof(*pins) * n);
+ for (i = 0; i < n; ++i) {
+ gchar *propname = g_strdup_printf("%s[%u]", name,
+ gpio_list->num_out + i);
+
+ object_property_add_link(OBJECT(dev), propname, TYPE_IRQ,
+ (Object **)&pins[i],
+ object_property_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
+ g_free(propname);
+ }
+ gpio_list->num_out += n;
+}
+
+void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
+{
+ qdev_init_gpio_out_named(dev, pins, NULL, n);
+}
+
+qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n)
+{
+ NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
+
+ assert(n >= 0 && n < gpio_list->num_in);
+ return gpio_list->in[n];
+}
+
+qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
+{
+ return qdev_get_gpio_in_named(dev, NULL, n);
+}
+
+void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n,
+ qemu_irq input_pin)
+{
+ char *propname = g_strdup_printf("%s[%d]",
+ 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"),
+ "non-qdev-gpio[*]", OBJECT(input_pin));
+ }
+ object_property_set_link(OBJECT(dev), propname,
+ OBJECT(input_pin), &error_abort);
+ g_free(propname);
+}
+
+qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n)
+{
+ g_autofree char *propname = g_strdup_printf("%s[%d]",
+ name ? name : "unnamed-gpio-out", n);
+
+ qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
+ NULL);
+
+ return ret;
+}
+
+/* disconnect a GPIO output, returning the disconnected input (if any) */
+
+static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
+ const char *name, int n)
+{
+ char *propname = g_strdup_printf("%s[%d]",
+ name ? name : "unnamed-gpio-out", n);
+
+ qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
+ NULL);
+ if (ret) {
+ object_property_set_link(OBJECT(dev), propname, NULL, NULL);
+ }
+ g_free(propname);
+ return ret;
+}
+
+qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt,
+ const char *name, int n)
+{
+ qemu_irq disconnected = qdev_disconnect_gpio_out_named(dev, name, n);
+ qdev_connect_gpio_out_named(dev, name, n, icpt);
+ return disconnected;
+}
+
+void qdev_connect_gpio_out(DeviceState *dev, int n, qemu_irq input_pin)
+{
+ qdev_connect_gpio_out_named(dev, NULL, n, input_pin);
+}
+
+void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
+ const char *name)
+{
+ int i;
+ NamedGPIOList *ngl = qdev_get_named_gpio_list(dev, name);
+
+ for (i = 0; i < ngl->num_in; i++) {
+ const char *nm = ngl->name ? ngl->name : "unnamed-gpio-in";
+ char *propname = g_strdup_printf("%s[%d]", nm, i);
+
+ object_property_add_alias(OBJECT(container), propname,
+ OBJECT(dev), propname);
+ g_free(propname);
+ }
+ for (i = 0; i < ngl->num_out; i++) {
+ const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out";
+ char *propname = g_strdup_printf("%s[%d]", nm, i);
+
+ object_property_add_alias(OBJECT(container), propname,
+ OBJECT(dev), propname);
+ g_free(propname);
+ }
+ QLIST_REMOVE(ngl, node);
+ QLIST_INSERT_HEAD(&container->gpios, ngl, node);
+}
diff --git a/hw/core/guest-loader.c b/hw/core/guest-loader.c
index d3f9d1a06e..391c875a29 100644
--- a/hw/core/guest-loader.c
+++ b/hw/core/guest-loader.c
@@ -129,7 +129,7 @@ static void guest_loader_class_init(ObjectClass *klass, void *data)
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
-static TypeInfo guest_loader_info = {
+static const TypeInfo guest_loader_info = {
.name = TYPE_GUEST_LOADER,
.parent = TYPE_DEVICE,
.instance_size = sizeof(GuestLoaderState),
diff --git a/hw/core/irq.c b/hw/core/irq.c
index 8a9cbdd556..3f14e2dda7 100644
--- a/hw/core/irq.c
+++ b/hw/core/irq.c
@@ -26,8 +26,7 @@
#include "hw/irq.h"
#include "qom/object.h"
-DECLARE_INSTANCE_CHECKER(struct IRQState, IRQ,
- TYPE_IRQ)
+OBJECT_DECLARE_SIMPLE_TYPE(IRQState, IRQ)
struct IRQState {
Object parent_obj;
@@ -68,7 +67,7 @@ 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)
{
- struct IRQState *irq;
+ IRQState *irq;
irq = IRQ(object_new(TYPE_IRQ));
irq->handler = handler;
@@ -94,7 +93,7 @@ void qemu_free_irq(qemu_irq irq)
static void qemu_notirq(void *opaque, int line, int level)
{
- struct IRQState *irq = opaque;
+ IRQState *irq = opaque;
irq->handler(irq->opaque, irq->n, !level);
}
@@ -106,21 +105,6 @@ qemu_irq qemu_irq_invert(qemu_irq irq)
return qemu_allocate_irq(qemu_notirq, irq, 0);
}
-static void qemu_splitirq(void *opaque, int line, int level)
-{
- struct IRQState **irq = opaque;
- irq[0]->handler(irq[0]->opaque, irq[0]->n, level);
- irq[1]->handler(irq[1]->opaque, irq[1]->n, level);
-}
-
-qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2)
-{
- qemu_irq *s = g_malloc0(2 * sizeof(qemu_irq));
- s[0] = irq1;
- s[1] = irq2;
- return qemu_allocate_irq(qemu_splitirq, s, 0);
-}
-
void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n)
{
int i;
@@ -135,7 +119,7 @@ void qemu_irq_intercept_in(qemu_irq *gpio_in, qemu_irq_handler handler, int n)
static const TypeInfo irq_type_info = {
.name = TYPE_IRQ,
.parent = TYPE_OBJECT,
- .instance_size = sizeof(struct IRQState),
+ .instance_size = sizeof(IRQState),
};
static void irq_register_types(void)
diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c
index b7c7b3ba94..9f20007dbb 100644
--- a/hw/core/loader-fit.c
+++ b/hw/core/loader-fit.c
@@ -120,6 +120,7 @@ static int fit_load_kernel(const struct fit_loader *ldr, const void *itb,
int cfg, void *opaque, hwaddr *pend,
Error **errp)
{
+ ERRP_GUARD();
const char *name;
const void *data;
const void *load_data;
@@ -178,6 +179,7 @@ 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)
{
+ ERRP_GUARD();
Error *err = NULL;
const char *name;
const void *data;
diff --git a/hw/core/loader.c b/hw/core/loader.c
index c623318b73..b8e52f3fb0 100644
--- a/hw/core/loader.c
+++ b/hw/core/loader.c
@@ -35,7 +35,7 @@
*
* 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
+ * 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
@@ -43,9 +43,11 @@
*/
#include "qemu/osdep.h"
-#include "qemu-common.h"
#include "qemu/datadir.h"
+#include "qemu/error-report.h"
#include "qapi/error.h"
+#include "qapi/qapi-commands-machine.h"
+#include "qapi/type-helpers.h"
#include "trace.h"
#include "hw/hw.h"
#include "disas/disas.h"
@@ -60,6 +62,7 @@
#include "hw/boards.h"
#include "qemu/cutils.h"
#include "sysemu/runstate.h"
+#include "tcg/debuginfo.h"
#include <zlib.h>
@@ -113,17 +116,17 @@ ssize_t read_targphys(const char *name,
return did;
}
-int load_image_targphys(const char *filename,
- hwaddr addr, uint64_t max_sz)
+ssize_t load_image_targphys(const char *filename,
+ hwaddr addr, uint64_t max_sz)
{
return load_image_targphys_as(filename, addr, max_sz, NULL);
}
/* return the size or -1 if error */
-int load_image_targphys_as(const char *filename,
- hwaddr addr, uint64_t max_sz, AddressSpace *as)
+ssize_t load_image_targphys_as(const char *filename,
+ hwaddr addr, uint64_t max_sz, AddressSpace *as)
{
- int size;
+ ssize_t size;
size = get_image_size(filename);
if (size < 0 || size > max_sz) {
@@ -137,9 +140,9 @@ int load_image_targphys_as(const char *filename,
return size;
}
-int load_image_mr(const char *filename, MemoryRegion *mr)
+ssize_t load_image_mr(const char *filename, MemoryRegion *mr)
{
- int size;
+ ssize_t size;
if (!memory_access_is_direct(mr, false)) {
/* Can only load an image into RAM or ROM */
@@ -208,8 +211,8 @@ static void bswap_ahdr(struct exec *e)
#define ZMAGIC 0413
#define QMAGIC 0314
#define _N_HDROFF(x) (1024 - sizeof (struct exec))
-#define N_TXTOFF(x) \
- (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \
+#define N_TXTOFF(x) \
+ (N_MAGIC(x) == ZMAGIC ? _N_HDROFF((x)) + sizeof (struct exec) : \
(N_MAGIC(x) == QMAGIC ? 0 : sizeof (struct exec)))
#define N_TXTADDR(x, target_page_size) (N_MAGIC(x) == QMAGIC ? target_page_size : 0)
#define _N_SEGMENT_ROUND(x, target_page_size) (((x) + target_page_size - 1) & ~(target_page_size - 1))
@@ -221,8 +224,8 @@ static void bswap_ahdr(struct exec *e)
: (_N_SEGMENT_ROUND (_N_TXTENDADDR(x, target_page_size), target_page_size)))
-int load_aout(const char *filename, hwaddr addr, int max_sz,
- int bswap_needed, hwaddr target_page_size)
+ssize_t load_aout(const char *filename, hwaddr addr, int max_sz,
+ int bswap_needed, hwaddr target_page_size)
{
int fd;
ssize_t size, ret;
@@ -298,10 +301,10 @@ static void *load_at(int fd, off_t offset, size_t size)
#define ELF_CLASS ELFCLASS32
#include "elf.h"
-#define SZ 32
+#define SZ 32
#define elf_word uint32_t
-#define elf_sword int32_t
-#define bswapSZs bswap32s
+#define elf_sword int32_t
+#define bswapSZs bswap32s
#include "hw/elf_ops.h"
#undef elfhdr
@@ -314,19 +317,19 @@ static void *load_at(int fd, off_t offset, size_t size)
#undef elf_sword
#undef bswapSZs
#undef SZ
-#define elfhdr elf64_hdr
-#define elf_phdr elf64_phdr
-#define elf_note elf64_note
-#define elf_shdr elf64_shdr
-#define elf_sym elf64_sym
+#define elfhdr elf64_hdr
+#define elf_phdr elf64_phdr
+#define elf_note elf64_note
+#define elf_shdr elf64_shdr
+#define elf_sym elf64_sym
#define elf_rela elf64_rela
#define elf_word uint64_t
-#define elf_sword int64_t
-#define bswapSZs bswap64s
-#define SZ 64
+#define elf_sword int64_t
+#define bswapSZs bswap64s
+#define SZ 64
#include "hw/elf_ops.h"
-const char *load_elf_strerror(int error)
+const char *load_elf_strerror(ssize_t error)
{
switch (error) {
case 0:
@@ -402,12 +405,12 @@ fail:
}
/* return < 0 if error, otherwise the number of bytes loaded in memory */
-int 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,
- int elf_machine, int clear_lsb, int data_swab)
+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,
+ 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,
@@ -415,12 +418,13 @@ int load_elf(const char *filename,
}
/* return < 0 if error, otherwise the number of bytes loaded in memory */
-int 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,
- int elf_machine, int clear_lsb, int data_swab, AddressSpace *as)
+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,
+ 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,
@@ -428,13 +432,13 @@ int load_elf_as(const char *filename,
}
/* return < 0 if error, otherwise the number of bytes loaded in memory */
-int 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)
+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,
@@ -444,16 +448,17 @@ int load_elf_ram(const char *filename,
}
/* return < 0 if error, otherwise the number of bytes loaded in memory */
-int load_elf_ram_sym(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, symbol_fn_t sym_cb)
+ssize_t load_elf_ram_sym(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, symbol_fn_t sym_cb)
{
- int fd, data_order, target_data_order, must_swab, ret = ELF_LOAD_FAILED;
+ int fd, data_order, target_data_order, must_swab;
+ ssize_t ret = ELF_LOAD_FAILED;
uint8_t e_ident[EI_NIDENT];
fd = open(filename, O_RDONLY | O_BINARY);
@@ -470,7 +475,7 @@ int load_elf_ram_sym(const char *filename,
ret = ELF_LOAD_NOT_ELF;
goto fail;
}
-#ifdef HOST_WORDS_BIGENDIAN
+#if HOST_BIG_ENDIAN
data_order = ELFDATA2MSB;
#else
data_order = ELFDATA2LSB;
@@ -500,6 +505,10 @@ int load_elf_ram_sym(const char *filename,
clear_lsb, data_swab, as, load_rom, sym_cb);
}
+ if (ret > 0) {
+ debuginfo_report_elf(filename, fd, 0);
+ }
+
fail:
close(fd);
return ret;
@@ -507,7 +516,7 @@ int load_elf_ram_sym(const char *filename,
static void bswap_uboot_header(uboot_image_header_t *hdr)
{
-#ifndef HOST_WORDS_BIGENDIAN
+#if !HOST_BIG_ENDIAN
bswap32s(&hdr->ih_magic);
bswap32s(&hdr->ih_hcrc);
bswap32s(&hdr->ih_time);
@@ -519,7 +528,7 @@ static void bswap_uboot_header(uboot_image_header_t *hdr)
}
-#define ZALLOC_ALIGNMENT 16
+#define ZALLOC_ALIGNMENT 16
static void *zalloc(void *x, unsigned items, unsigned size)
{
@@ -539,17 +548,17 @@ static void zfree(void *x, void *addr)
}
-#define HEAD_CRC 2
-#define EXTRA_FIELD 4
-#define ORIG_NAME 8
-#define COMMENT 0x10
-#define RESERVED 0xe0
+#define HEAD_CRC 2
+#define EXTRA_FIELD 4
+#define ORIG_NAME 8
+#define COMMENT 0x10
+#define RESERVED 0xe0
-#define DEFLATED 8
+#define DEFLATED 8
ssize_t gunzip(void *dst, size_t dstlen, uint8_t *src, size_t srclen)
{
- z_stream s;
+ z_stream s = {};
ssize_t dstbytes;
int r, i, flags;
@@ -614,13 +623,14 @@ toosmall:
}
/* Load a U-Boot image. */
-static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
- int *is_linux, uint8_t image_type,
- uint64_t (*translate_fn)(void *, uint64_t),
- void *translate_opaque, AddressSpace *as)
+static ssize_t load_uboot_image(const char *filename, hwaddr *ep,
+ hwaddr *loadaddr, int *is_linux,
+ uint8_t image_type,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque, AddressSpace *as)
{
int fd;
- int size;
+ ssize_t size;
hwaddr address;
uboot_image_header_t h;
uboot_image_header_t *hdr = &h;
@@ -693,6 +703,21 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr,
if (is_linux) {
if (hdr->ih_os == IH_OS_LINUX) {
*is_linux = 1;
+ } else if (hdr->ih_os == IH_OS_VXWORKS) {
+ /*
+ * VxWorks 7 uses the same boot interface as the Linux kernel
+ * on Arm (64-bit only), PowerPC and RISC-V architectures.
+ */
+ switch (hdr->ih_arch) {
+ case IH_ARCH_ARM64:
+ case IH_ARCH_PPC:
+ case IH_ARCH_RISCV:
+ *is_linux = 1;
+ break;
+ default:
+ *is_linux = 0;
+ break;
+ }
} else {
*is_linux = 0;
}
@@ -742,40 +767,40 @@ out:
return ret;
}
-int load_uimage(const char *filename, hwaddr *ep, hwaddr *loadaddr,
- int *is_linux,
- uint64_t (*translate_fn)(void *, uint64_t),
- void *translate_opaque)
+ssize_t load_uimage(const char *filename, hwaddr *ep, hwaddr *loadaddr,
+ int *is_linux,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque)
{
return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL,
translate_fn, translate_opaque, NULL);
}
-int load_uimage_as(const char *filename, hwaddr *ep, hwaddr *loadaddr,
- int *is_linux,
- uint64_t (*translate_fn)(void *, uint64_t),
- void *translate_opaque, AddressSpace *as)
+ssize_t load_uimage_as(const char *filename, hwaddr *ep, hwaddr *loadaddr,
+ int *is_linux,
+ uint64_t (*translate_fn)(void *, uint64_t),
+ void *translate_opaque, AddressSpace *as)
{
return load_uboot_image(filename, ep, loadaddr, is_linux, IH_TYPE_KERNEL,
translate_fn, translate_opaque, as);
}
/* Load a ramdisk. */
-int load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz)
+ssize_t load_ramdisk(const char *filename, hwaddr addr, uint64_t max_sz)
{
return load_ramdisk_as(filename, addr, max_sz, NULL);
}
-int load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz,
- AddressSpace *as)
+ssize_t load_ramdisk_as(const char *filename, hwaddr addr, uint64_t max_sz,
+ AddressSpace *as)
{
return load_uboot_image(filename, NULL, &addr, NULL, IH_TYPE_RAMDISK,
NULL, NULL, as);
}
/* Load a gzip-compressed kernel to a dynamically allocated buffer. */
-int load_image_gzipped_buffer(const char *filename, uint64_t max_sz,
- uint8_t **buffer)
+ssize_t load_image_gzipped_buffer(const char *filename, uint64_t max_sz,
+ uint8_t **buffer)
{
uint8_t *compressed_data = NULL;
uint8_t *data = NULL;
@@ -820,9 +845,9 @@ int load_image_gzipped_buffer(const char *filename, uint64_t max_sz,
}
/* Load a gzip-compressed kernel. */
-int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz)
+ssize_t load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz)
{
- int bytes;
+ ssize_t bytes;
uint8_t *data;
bytes = load_image_gzipped_buffer(filename, max_sz, &data);
@@ -833,6 +858,97 @@ int load_image_gzipped(const char *filename, hwaddr addr, uint64_t max_sz)
return bytes;
}
+/* The PE/COFF MS-DOS stub magic number */
+#define EFI_PE_MSDOS_MAGIC "MZ"
+
+/*
+ * The Linux header magic number for a EFI PE/COFF
+ * image targeting an unspecified architecture.
+ */
+#define EFI_PE_LINUX_MAGIC "\xcd\x23\x82\x81"
+
+/*
+ * Bootable Linux kernel images may be packaged as EFI zboot images, which are
+ * self-decompressing executables when loaded via EFI. The compressed payload
+ * can also be extracted from the image and decompressed by a non-EFI loader.
+ *
+ * The de facto specification for this format is at the following URL:
+ *
+ * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/firmware/efi/libstub/zboot-header.S
+ *
+ * This definition is based on Linux upstream commit 29636a5ce87beba.
+ */
+struct linux_efi_zboot_header {
+ uint8_t msdos_magic[2]; /* PE/COFF 'MZ' magic number */
+ uint8_t reserved0[2];
+ uint8_t zimg[4]; /* "zimg" for Linux EFI zboot images */
+ uint32_t payload_offset; /* LE offset to compressed payload */
+ uint32_t payload_size; /* LE size of the compressed payload */
+ uint8_t reserved1[8];
+ char compression_type[32]; /* Compression type, NUL terminated */
+ uint8_t linux_magic[4]; /* Linux header magic */
+ uint32_t pe_header_offset; /* LE offset to the PE header */
+};
+
+/*
+ * Check whether *buffer points to a Linux EFI zboot image in memory.
+ *
+ * If it does, attempt to decompress it to a new buffer, and free the old one.
+ * If any of this fails, return an error to the caller.
+ *
+ * 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)
+{
+ const struct linux_efi_zboot_header *header;
+ uint8_t *data = NULL;
+ int ploff, plsize;
+ ssize_t bytes;
+
+ /* ignore if this is too small to be a EFI zboot image */
+ if (*size < sizeof(*header)) {
+ return 0;
+ }
+
+ header = (struct linux_efi_zboot_header *)*buffer;
+
+ /* ignore if this is not a Linux EFI zboot image */
+ if (memcmp(&header->msdos_magic, EFI_PE_MSDOS_MAGIC, 2) != 0 ||
+ memcmp(&header->zimg, "zimg", 4) != 0 ||
+ memcmp(&header->linux_magic, EFI_PE_LINUX_MAGIC, 4) != 0) {
+ return 0;
+ }
+
+ if (strcmp(header->compression_type, "gzip") != 0) {
+ fprintf(stderr,
+ "unable to handle EFI zboot image with \"%.*s\" compression\n",
+ (int)sizeof(header->compression_type) - 1,
+ header->compression_type);
+ return -1;
+ }
+
+ ploff = ldl_le_p(&header->payload_offset);
+ plsize = ldl_le_p(&header->payload_size);
+
+ if (ploff < 0 || plsize < 0 || ploff + plsize > *size) {
+ fprintf(stderr, "unable to handle corrupt EFI zboot image\n");
+ return -1;
+ }
+
+ data = g_malloc(LOAD_IMAGE_MAX_GUNZIP_BYTES);
+ bytes = gunzip(data, LOAD_IMAGE_MAX_GUNZIP_BYTES, *buffer + ploff, plsize);
+ if (bytes < 0) {
+ fprintf(stderr, "failed to decompress EFI zboot image\n");
+ g_free(data);
+ return -1;
+ }
+
+ g_free(*buffer);
+ *buffer = g_realloc(data, bytes);
+ *size = bytes;
+ return bytes;
+}
+
/*
* Functions for reboot-persistent memory regions.
* - used for vga bios and option roms.
@@ -952,14 +1068,15 @@ static void *rom_set_mr(Rom *rom, Object *owner, const char *name, bool ro)
return data;
}
-int rom_add_file(const char *file, const char *fw_dir,
- hwaddr addr, int32_t bootindex,
- bool option_rom, MemoryRegion *mr,
- AddressSpace *as)
+ssize_t rom_add_file(const char *file, const char *fw_dir,
+ hwaddr addr, int32_t bootindex,
+ bool has_option_rom, MemoryRegion *mr,
+ AddressSpace *as)
{
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
Rom *rom;
- int rc, fd = -1;
+ ssize_t rc;
+ int fd = -1;
char devpath[100];
if (as && mr) {
@@ -1001,7 +1118,7 @@ int rom_add_file(const char *file, const char *fw_dir,
lseek(fd, 0, SEEK_SET);
rc = read(fd, rom->data, rom->datasize);
if (rc != rom->datasize) {
- fprintf(stderr, "rom: file %-20s: read error: rc=%d (expected %zd)\n",
+ fprintf(stderr, "rom: file %-20s: read error: rc=%zd (expected %zd)\n",
rom->name, rc, rom->datasize);
goto err;
}
@@ -1022,7 +1139,7 @@ int rom_add_file(const char *file, const char *fw_dir,
basename);
snprintf(devpath, sizeof(devpath), "/rom@%s", fw_file_name);
- if ((!option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) {
+ if ((!has_option_rom || mc->option_rom_has_mr) && mc->rom_file_has_mr) {
data = rom_set_mr(rom, OBJECT(fw_cfg), devpath, true);
} else {
data = rom->data;
@@ -1034,7 +1151,7 @@ int rom_add_file(const char *file, const char *fw_dir,
rom->mr = mr;
snprintf(devpath, sizeof(devpath), "/rom@%s", file);
} else {
- snprintf(devpath, sizeof(devpath), "/rom@" TARGET_FMT_plx, addr);
+ snprintf(devpath, sizeof(devpath), "/rom@" HWADDR_FMT_plx, addr);
}
}
@@ -1120,12 +1237,12 @@ int rom_add_elf_program(const char *name, GMappedFile *mapped_file, void *data,
return 0;
}
-int rom_add_vga(const char *file)
+ssize_t rom_add_vga(const char *file)
{
return rom_add_file(file, "vgaroms", 0, -1, true, NULL, NULL);
}
-int rom_add_option(const char *file, int32_t bootindex)
+ssize_t rom_add_option(const char *file, int32_t bootindex)
{
return rom_add_file(file, "genroms", 0, bootindex, true, NULL, NULL);
}
@@ -1160,9 +1277,13 @@ static void rom_reset(void *unused)
if (rom->mr) {
void *host = memory_region_get_ram_ptr(rom->mr);
memcpy(host, rom->data, rom->datasize);
+ memset(host + rom->datasize, 0, rom->romsize - rom->datasize);
} else {
address_space_write_rom(rom->as, rom->addr, MEMTXATTRS_UNSPECIFIED,
rom->data, rom->datasize);
+ address_space_set(rom->as, rom->addr + rom->datasize, 0,
+ rom->romsize - rom->datasize,
+ MEMTXATTRS_UNSPECIFIED);
}
if (rom->isrom) {
/* rom needs to be written only once */
@@ -1214,10 +1335,10 @@ static void rom_print_one_overlap_error(Rom *last_rom, Rom *rom)
"\nThe following two regions overlap (in the %s address space):\n",
rom_as_name(rom));
error_printf(
- " %s (addresses 0x" TARGET_FMT_plx " - 0x" TARGET_FMT_plx ")\n",
+ " %s (addresses 0x" HWADDR_FMT_plx " - 0x" HWADDR_FMT_plx ")\n",
last_rom->name, last_rom->addr, last_rom->addr + last_rom->romsize);
error_printf(
- " %s (addresses 0x" TARGET_FMT_plx " - 0x" TARGET_FMT_plx ")\n",
+ " %s (addresses 0x" HWADDR_FMT_plx " - 0x" HWADDR_FMT_plx ")\n",
rom->name, rom->addr, rom->addr + rom->romsize);
}
@@ -1325,6 +1446,92 @@ static Rom *find_rom(hwaddr addr, size_t size)
return NULL;
}
+typedef struct RomSec {
+ hwaddr base;
+ int se; /* start/end flag */
+} RomSec;
+
+
+/*
+ * Sort into address order. We break ties between rom-startpoints
+ * and rom-endpoints in favour of the startpoint, by sorting the 0->1
+ * transition before the 1->0 transition. Either way round would
+ * 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)
+{
+ RomSec *ra = (RomSec *) a;
+ RomSec *rb = (RomSec *) b;
+
+ if (ra->base == rb->base) {
+ return ra->se - rb->se;
+ }
+ return ra->base > rb->base ? 1 : -1;
+}
+
+static GList *add_romsec_to_list(GList *secs, hwaddr base, int se)
+{
+ RomSec *cand = g_new(RomSec, 1);
+ cand->base = base;
+ cand->se = se;
+ return g_list_prepend(secs, cand);
+}
+
+RomGap rom_find_largest_gap_between(hwaddr base, size_t size)
+{
+ Rom *rom;
+ RomSec *cand;
+ RomGap res = {0, 0};
+ hwaddr gapstart = base;
+ GList *it, *secs = NULL;
+ int count = 0;
+
+ QTAILQ_FOREACH(rom, &roms, next) {
+ /* Ignore blobs being loaded to special places */
+ if (rom->mr || rom->fw_file) {
+ continue;
+ }
+ /* ignore anything finishing below base */
+ if (rom->addr + rom->romsize <= base) {
+ continue;
+ }
+ /* ignore anything starting above the region */
+ if (rom->addr >= base + size) {
+ continue;
+ }
+
+ /* Save the start and end of each relevant ROM */
+ secs = add_romsec_to_list(secs, rom->addr, 1);
+
+ if (rom->addr + rom->romsize < base + size) {
+ secs = add_romsec_to_list(secs, rom->addr + rom->romsize, -1);
+ }
+ }
+
+ /* sentinel */
+ secs = add_romsec_to_list(secs, base + size, 1);
+
+ secs = g_list_sort(secs, sort_secs);
+
+ for (it = g_list_first(secs); it; it = g_list_next(it)) {
+ cand = (RomSec *) it->data;
+ if (count == 0 && count + cand->se == 1) {
+ size_t gap = cand->base - gapstart;
+ if (gap > res.size) {
+ res.base = gapstart;
+ res.size = gap;
+ }
+ } else if (count == 1 && count + cand->se == 0) {
+ gapstart = cand->base;
+ }
+ count += cand->se;
+ }
+
+ g_list_free_full(secs, g_free);
+ return res;
+}
+
/*
* Copies memory from registered ROMs to dest. Any memory that is contained in
* a ROM between addr and addr + size is copied. Note that this can involve
@@ -1472,32 +1679,35 @@ void *rom_ptr_for_as(AddressSpace *as, hwaddr addr, size_t size)
return cbdata.rom;
}
-void hmp_info_roms(Monitor *mon, const QDict *qdict)
+HumanReadableText *qmp_x_query_roms(Error **errp)
{
Rom *rom;
+ g_autoptr(GString) buf = g_string_new("");
QTAILQ_FOREACH(rom, &roms, next) {
if (rom->mr) {
- monitor_printf(mon, "%s"
- " size=0x%06zx name=\"%s\"\n",
- memory_region_name(rom->mr),
- rom->romsize,
- rom->name);
+ g_string_append_printf(buf, "%s"
+ " size=0x%06zx name=\"%s\"\n",
+ memory_region_name(rom->mr),
+ rom->romsize,
+ rom->name);
} else if (!rom->fw_file) {
- monitor_printf(mon, "addr=" TARGET_FMT_plx
- " size=0x%06zx mem=%s name=\"%s\"\n",
- rom->addr, rom->romsize,
- rom->isrom ? "rom" : "ram",
- rom->name);
+ g_string_append_printf(buf, "addr=" HWADDR_FMT_plx
+ " size=0x%06zx mem=%s name=\"%s\"\n",
+ rom->addr, rom->romsize,
+ rom->isrom ? "rom" : "ram",
+ rom->name);
} else {
- monitor_printf(mon, "fw=%s/%s"
- " size=0x%06zx name=\"%s\"\n",
- rom->fw_dir,
- rom->fw_file,
- rom->romsize,
- rom->name);
+ g_string_append_printf(buf, "fw=%s/%s"
+ " size=0x%06zx name=\"%s\"\n",
+ rom->fw_dir,
+ rom->fw_file,
+ rom->romsize,
+ rom->name);
}
}
+
+ return human_readable_text_from_str(buf);
}
typedef enum HexRecord HexRecord;
@@ -1735,11 +1945,12 @@ out:
}
/* return size or -1 if error */
-int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as)
+ssize_t load_targphys_hex_as(const char *filename, hwaddr *entry,
+ AddressSpace *as)
{
gsize hex_blob_size;
gchar *hex_blob;
- int total_size = 0;
+ ssize_t total_size = 0;
if (!g_file_get_contents(filename, &hex_blob, &hex_blob_size, NULL)) {
return -1;
diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c
index 76b22b00d6..a6ff6a4875 100644
--- a/hw/core/machine-hmp-cmds.c
+++ b/hw/core/machine-hmp-cmds.c
@@ -53,8 +53,7 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict)
HotpluggableCPUList *saved = l;
CpuInstanceProperties *c;
- if (err != NULL) {
- hmp_handle_error(mon, err);
+ if (hmp_handle_error(mon, err)) {
return;
}
@@ -63,7 +62,7 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict)
monitor_printf(mon, " type: \"%s\"\n", l->value->type);
monitor_printf(mon, " vcpus_count: \"%" PRIu64 "\"\n",
l->value->vcpus_count);
- if (l->value->has_qom_path) {
+ if (l->value->qom_path) {
monitor_printf(mon, " qom_path: \"%s\"\n", l->value->qom_path);
}
@@ -72,12 +71,22 @@ void hmp_hotpluggable_cpus(Monitor *mon, const QDict *qdict)
if (c->has_node_id) {
monitor_printf(mon, " node-id: \"%" PRIu64 "\"\n", c->node_id);
}
+ if (c->has_drawer_id) {
+ monitor_printf(mon, " drawer-id: \"%" PRIu64 "\"\n", c->drawer_id);
+ }
+ if (c->has_book_id) {
+ monitor_printf(mon, " book-id: \"%" PRIu64 "\"\n", c->book_id);
+ }
if (c->has_socket_id) {
monitor_printf(mon, " socket-id: \"%" PRIu64 "\"\n", c->socket_id);
}
if (c->has_die_id) {
monitor_printf(mon, " die-id: \"%" PRIu64 "\"\n", c->die_id);
}
+ if (c->has_cluster_id) {
+ monitor_printf(mon, " cluster-id: \"%" PRIu64 "\"\n",
+ c->cluster_id);
+ }
if (c->has_core_id) {
monitor_printf(mon, " core-id: \"%" PRIu64 "\"\n", c->core_id);
}
@@ -132,37 +141,225 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, err);
}
-void hmp_info_numa(Monitor *mon, const QDict *qdict)
+void hmp_info_kvm(Monitor *mon, const QDict *qdict)
{
- int i, nb_numa_nodes;
- NumaNodeMem *node_mem;
- CpuInfoFastList *cpu_list, *cpu;
- MachineState *ms = MACHINE(qdev_get_machine());
+ KvmInfo *info;
+
+ info = qmp_query_kvm(NULL);
+ monitor_printf(mon, "kvm support: ");
+ if (info->present) {
+ monitor_printf(mon, "%s\n", info->enabled ? "enabled" : "disabled");
+ } else {
+ monitor_printf(mon, "not compiled\n");
+ }
+
+ qapi_free_KvmInfo(info);
+}
+
+void hmp_info_uuid(Monitor *mon, const QDict *qdict)
+{
+ UuidInfo *info;
- nb_numa_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
- monitor_printf(mon, "%d nodes\n", nb_numa_nodes);
- if (!nb_numa_nodes) {
+ info = qmp_query_uuid(NULL);
+ monitor_printf(mon, "%s\n", info->UUID);
+ qapi_free_UuidInfo(info);
+}
+
+void hmp_info_balloon(Monitor *mon, const QDict *qdict)
+{
+ BalloonInfo *info;
+ Error *err = NULL;
+
+ info = qmp_query_balloon(&err);
+ if (hmp_handle_error(mon, err)) {
return;
}
- cpu_list = qmp_query_cpus_fast(&error_abort);
- node_mem = g_new0(NumaNodeMem, nb_numa_nodes);
+ monitor_printf(mon, "balloon: actual=%" PRId64 "\n", info->actual >> 20);
+
+ qapi_free_BalloonInfo(info);
+}
+
+void hmp_system_reset(Monitor *mon, const QDict *qdict)
+{
+ qmp_system_reset(NULL);
+}
+
+void hmp_system_powerdown(Monitor *mon, const QDict *qdict)
+{
+ qmp_system_powerdown(NULL);
+}
+
+void hmp_memsave(Monitor *mon, const QDict *qdict)
+{
+ uint32_t size = qdict_get_int(qdict, "size");
+ const char *filename = qdict_get_str(qdict, "filename");
+ uint64_t addr = qdict_get_int(qdict, "val");
+ Error *err = NULL;
+ int cpu_index = monitor_get_cpu_index(mon);
+
+ if (cpu_index < 0) {
+ monitor_printf(mon, "No CPU available\n");
+ return;
+ }
+
+ qmp_memsave(addr, size, filename, true, cpu_index, &err);
+ hmp_handle_error(mon, err);
+}
+
+void hmp_pmemsave(Monitor *mon, const QDict *qdict)
+{
+ uint32_t size = qdict_get_int(qdict, "size");
+ const char *filename = qdict_get_str(qdict, "filename");
+ uint64_t addr = qdict_get_int(qdict, "val");
+ Error *err = NULL;
+
+ qmp_pmemsave(addr, size, filename, &err);
+ hmp_handle_error(mon, err);
+}
+
+void hmp_system_wakeup(Monitor *mon, const QDict *qdict)
+{
+ Error *err = NULL;
+
+ qmp_system_wakeup(&err);
+ hmp_handle_error(mon, err);
+}
+
+void hmp_nmi(Monitor *mon, const QDict *qdict)
+{
+ Error *err = NULL;
+
+ qmp_inject_nmi(&err);
+ hmp_handle_error(mon, err);
+}
+
+void hmp_balloon(Monitor *mon, const QDict *qdict)
+{
+ int64_t value = qdict_get_int(qdict, "value");
+ Error *err = NULL;
+
+ qmp_balloon(value, &err);
+ hmp_handle_error(mon, err);
+}
+
+void hmp_info_memory_devices(Monitor *mon, const QDict *qdict)
+{
+ Error *err = NULL;
+ MemoryDeviceInfoList *info_list = qmp_query_memory_devices(&err);
+ MemoryDeviceInfoList *info;
+ VirtioPMEMDeviceInfo *vpi;
+ VirtioMEMDeviceInfo *vmi;
+ MemoryDeviceInfo *value;
+ PCDIMMDeviceInfo *di;
+ SgxEPCDeviceInfo *se;
+ HvBalloonDeviceInfo *hi;
- query_numa_node_mem(node_mem, ms);
- for (i = 0; i < nb_numa_nodes; i++) {
- monitor_printf(mon, "node %d cpus:", i);
- for (cpu = cpu_list; cpu; cpu = cpu->next) {
- if (cpu->value->has_props && cpu->value->props->has_node_id &&
- cpu->value->props->node_id == i) {
- monitor_printf(mon, " %" PRIi64, cpu->value->cpu_index);
+ for (info = info_list; info; info = info->next) {
+ value = info->value;
+
+ if (value) {
+ switch (value->type) {
+ case MEMORY_DEVICE_INFO_KIND_DIMM:
+ case MEMORY_DEVICE_INFO_KIND_NVDIMM:
+ di = value->type == MEMORY_DEVICE_INFO_KIND_DIMM ?
+ value->u.dimm.data : value->u.nvdimm.data;
+ monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
+ MemoryDeviceInfoKind_str(value->type),
+ di->id ? di->id : "");
+ monitor_printf(mon, " addr: 0x%" PRIx64 "\n", di->addr);
+ monitor_printf(mon, " slot: %" PRId64 "\n", di->slot);
+ monitor_printf(mon, " node: %" PRId64 "\n", di->node);
+ monitor_printf(mon, " size: %" PRIu64 "\n", di->size);
+ monitor_printf(mon, " memdev: %s\n", di->memdev);
+ monitor_printf(mon, " hotplugged: %s\n",
+ di->hotplugged ? "true" : "false");
+ monitor_printf(mon, " hotpluggable: %s\n",
+ di->hotpluggable ? "true" : "false");
+ break;
+ case MEMORY_DEVICE_INFO_KIND_VIRTIO_PMEM:
+ vpi = value->u.virtio_pmem.data;
+ monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
+ MemoryDeviceInfoKind_str(value->type),
+ vpi->id ? vpi->id : "");
+ monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vpi->memaddr);
+ monitor_printf(mon, " size: %" PRIu64 "\n", vpi->size);
+ monitor_printf(mon, " memdev: %s\n", vpi->memdev);
+ break;
+ case MEMORY_DEVICE_INFO_KIND_VIRTIO_MEM:
+ vmi = value->u.virtio_mem.data;
+ monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
+ MemoryDeviceInfoKind_str(value->type),
+ vmi->id ? vmi->id : "");
+ monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", vmi->memaddr);
+ monitor_printf(mon, " node: %" PRId64 "\n", vmi->node);
+ monitor_printf(mon, " requested-size: %" PRIu64 "\n",
+ vmi->requested_size);
+ monitor_printf(mon, " size: %" PRIu64 "\n", vmi->size);
+ monitor_printf(mon, " max-size: %" PRIu64 "\n", vmi->max_size);
+ monitor_printf(mon, " block-size: %" PRIu64 "\n",
+ vmi->block_size);
+ monitor_printf(mon, " memdev: %s\n", vmi->memdev);
+ break;
+ case MEMORY_DEVICE_INFO_KIND_SGX_EPC:
+ se = value->u.sgx_epc.data;
+ monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
+ MemoryDeviceInfoKind_str(value->type),
+ se->id ? se->id : "");
+ monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n", se->memaddr);
+ monitor_printf(mon, " size: %" PRIu64 "\n", se->size);
+ monitor_printf(mon, " node: %" PRId64 "\n", se->node);
+ monitor_printf(mon, " memdev: %s\n", se->memdev);
+ break;
+ case MEMORY_DEVICE_INFO_KIND_HV_BALLOON:
+ hi = value->u.hv_balloon.data;
+ monitor_printf(mon, "Memory device [%s]: \"%s\"\n",
+ MemoryDeviceInfoKind_str(value->type),
+ hi->id ? hi->id : "");
+ if (hi->has_memaddr) {
+ monitor_printf(mon, " memaddr: 0x%" PRIx64 "\n",
+ hi->memaddr);
+ }
+ monitor_printf(mon, " max-size: %" PRIu64 "\n", hi->max_size);
+ if (hi->memdev) {
+ monitor_printf(mon, " memdev: %s\n", hi->memdev);
+ }
+ break;
+ default:
+ g_assert_not_reached();
}
}
- monitor_printf(mon, "\n");
- monitor_printf(mon, "node %d size: %" PRId64 " MB\n", i,
- node_mem[i].node_mem >> 20);
- monitor_printf(mon, "node %d plugged: %" PRId64 " MB\n", i,
- node_mem[i].node_plugged_mem >> 20);
}
- qapi_free_CpuInfoFastList(cpu_list);
- g_free(node_mem);
+
+ qapi_free_MemoryDeviceInfoList(info_list);
+ hmp_handle_error(mon, err);
+}
+
+void hmp_info_vm_generation_id(Monitor *mon, const QDict *qdict)
+{
+ Error *err = NULL;
+ GuidInfo *info = qmp_query_vm_generation_id(&err);
+ if (info) {
+ monitor_printf(mon, "%s\n", info->guid);
+ }
+ hmp_handle_error(mon, err);
+ qapi_free_GuidInfo(info);
+}
+
+void hmp_info_memory_size_summary(Monitor *mon, const QDict *qdict)
+{
+ Error *err = NULL;
+ MemoryInfo *info = qmp_query_memory_size_summary(&err);
+ if (info) {
+ monitor_printf(mon, "base memory: %" PRIu64 "\n",
+ info->base_memory);
+
+ if (info->has_plugged_memory) {
+ monitor_printf(mon, "plugged memory: %" PRIu64 "\n",
+ info->plugged_memory);
+ }
+
+ qapi_free_MemoryInfo(info);
+ }
+ hmp_handle_error(mon, err);
}
diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c
index 216fdfaf3a..c20829b9ae 100644
--- a/hw/core/machine-qmp-cmds.c
+++ b/hw/core/machine-qmp-cmds.c
@@ -8,31 +8,23 @@
*/
#include "qemu/osdep.h"
+#include "hw/acpi/vmgenid.h"
#include "hw/boards.h"
+#include "hw/intc/intc.h"
+#include "hw/mem/memory-device.h"
#include "qapi/error.h"
#include "qapi/qapi-builtin-visit.h"
#include "qapi/qapi-commands-machine.h"
-#include "qapi/qmp/qerror.h"
#include "qapi/qmp/qobject.h"
#include "qapi/qobject-input-visitor.h"
-#include "qemu/main-loop.h"
+#include "qapi/type-helpers.h"
+#include "qemu/uuid.h"
#include "qom/qom-qobject.h"
#include "sysemu/hostmem.h"
#include "sysemu/hw_accel.h"
#include "sysemu/numa.h"
#include "sysemu/runstate.h"
-
-static void cpustate_to_cpuinfo_s390(CpuInfoS390 *info, const CPUState *cpu)
-{
-#ifdef TARGET_S390X
- S390CPU *s390_cpu = S390_CPU(cpu);
- CPUS390XState *env = &s390_cpu->env;
-
- info->cpu_state = env->cpu_state;
-#else
- abort();
-#endif
-}
+#include "sysemu/sysemu.h"
/*
* fast means: we NEVER interrupt vCPU threads to retrieve
@@ -43,7 +35,7 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp)
MachineState *ms = MACHINE(qdev_get_machine());
MachineClass *mc = MACHINE_GET_CLASS(ms);
CpuInfoFastList *head = NULL, **tail = &head;
- SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME,
+ SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, target_name(),
-1, &error_abort);
CPUState *cpu;
@@ -54,8 +46,7 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp)
value->qom_path = object_get_canonical_path(OBJECT(cpu));
value->thread_id = cpu->thread_id;
- value->has_props = !!mc->cpu_index_to_instance_props;
- if (value->has_props) {
+ if (mc->cpu_index_to_instance_props) {
CpuInstanceProperties *props;
props = g_malloc0(sizeof(*props));
*props = mc->cpu_index_to_instance_props(ms, cpu->cpu_index);
@@ -63,8 +54,8 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp)
}
value->target = target;
- if (target == SYS_EMU_TARGET_S390X) {
- cpustate_to_cpuinfo_s390(&value->u.s390x, cpu);
+ if (cpu->cc->query_cpu_fast) {
+ cpu->cc->query_cpu_fast(cpu, value);
}
QAPI_LIST_APPEND(tail, value);
@@ -89,7 +80,6 @@ MachineInfoList *qmp_query_machines(Error **errp)
}
if (mc->alias) {
- info->has_alias = true;
info->alias = g_strdup(mc->alias);
}
@@ -98,13 +88,12 @@ MachineInfoList *qmp_query_machines(Error **errp)
info->hotpluggable_cpus = mc->has_hotpluggable_cpus;
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);
- info->has_default_cpu_type = true;
}
if (mc->default_ram_id) {
info->default_ram_id = g_strdup(mc->default_ram_id);
- info->has_default_ram_id = true;
}
QAPI_LIST_PREPEND(mach_list, info);
@@ -126,7 +115,7 @@ TargetInfo *qmp_query_target(Error **errp)
{
TargetInfo *info = g_malloc0(sizeof(*info));
- info->arch = qapi_enum_parse(&SysEmuTarget_lookup, TARGET_NAME, -1,
+ info->arch = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1,
&error_abort);
return info;
@@ -138,7 +127,7 @@ HotpluggableCPUList *qmp_query_hotpluggable_cpus(Error **errp)
MachineClass *mc = MACHINE_GET_CLASS(ms);
if (!mc->has_hotpluggable_cpus) {
- error_setg(errp, QERR_FEATURE_DISABLED, "query-hotpluggable-cpus");
+ error_setg(errp, "machine does not support hot-plugging CPUs");
return NULL;
}
@@ -167,7 +156,6 @@ static int query_memdev(Object *obj, void *opaque)
m = g_malloc0(sizeof(*m));
m->id = g_strdup(object_get_canonical_path_component(obj));
- m->has_id = !!m->id;
m->size = object_property_get_uint(obj, "size", &error_abort);
m->merge = object_property_get_bool(obj, "merge", &error_abort);
@@ -204,3 +192,167 @@ MemdevList *qmp_query_memdev(Error **errp)
object_child_foreach(obj, query_memdev, &list);
return list;
}
+
+HumanReadableText *qmp_x_query_numa(Error **errp)
+{
+ g_autoptr(GString) buf = g_string_new("");
+ int i, nb_numa_nodes;
+ NumaNodeMem *node_mem;
+ CpuInfoFastList *cpu_list, *cpu;
+ MachineState *ms = MACHINE(qdev_get_machine());
+
+ nb_numa_nodes = ms->numa_state ? ms->numa_state->num_nodes : 0;
+ g_string_append_printf(buf, "%d nodes\n", nb_numa_nodes);
+ if (!nb_numa_nodes) {
+ goto done;
+ }
+
+ cpu_list = qmp_query_cpus_fast(&error_abort);
+ node_mem = g_new0(NumaNodeMem, nb_numa_nodes);
+
+ query_numa_node_mem(node_mem, ms);
+ for (i = 0; i < nb_numa_nodes; i++) {
+ g_string_append_printf(buf, "node %d cpus:", i);
+ for (cpu = cpu_list; cpu; cpu = cpu->next) {
+ if (cpu->value->props && cpu->value->props->has_node_id &&
+ cpu->value->props->node_id == i) {
+ g_string_append_printf(buf, " %" PRIi64, cpu->value->cpu_index);
+ }
+ }
+ g_string_append_printf(buf, "\n");
+ g_string_append_printf(buf, "node %d size: %" PRId64 " MB\n", i,
+ node_mem[i].node_mem >> 20);
+ g_string_append_printf(buf, "node %d plugged: %" PRId64 " MB\n", i,
+ node_mem[i].node_plugged_mem >> 20);
+ }
+ qapi_free_CpuInfoFastList(cpu_list);
+ g_free(node_mem);
+
+ done:
+ return human_readable_text_from_str(buf);
+}
+
+KvmInfo *qmp_query_kvm(Error **errp)
+{
+ KvmInfo *info = g_malloc0(sizeof(*info));
+
+ info->enabled = kvm_enabled();
+ info->present = accel_find("kvm");
+
+ return info;
+}
+
+UuidInfo *qmp_query_uuid(Error **errp)
+{
+ UuidInfo *info = g_malloc0(sizeof(*info));
+
+ info->UUID = qemu_uuid_unparse_strdup(&qemu_uuid);
+ return info;
+}
+
+void qmp_system_reset(Error **errp)
+{
+ qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET);
+}
+
+void qmp_system_powerdown(Error **errp)
+{
+ qemu_system_powerdown_request();
+}
+
+void qmp_system_wakeup(Error **errp)
+{
+ if (!qemu_wakeup_suspend_enabled()) {
+ error_setg(errp,
+ "wake-up from suspend is not supported by this guest");
+ return;
+ }
+
+ qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp);
+}
+
+MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp)
+{
+ return qmp_memory_device_list();
+}
+
+MemoryInfo *qmp_query_memory_size_summary(Error **errp)
+{
+ MemoryInfo *mem_info = g_new0(MemoryInfo, 1);
+ MachineState *ms = MACHINE(qdev_get_machine());
+
+ mem_info->base_memory = ms->ram_size;
+
+ mem_info->plugged_memory = get_plugged_memory_size();
+ mem_info->has_plugged_memory =
+ mem_info->plugged_memory != (uint64_t)-1;
+
+ return mem_info;
+}
+
+HumanReadableText *qmp_x_query_ramblock(Error **errp)
+{
+ g_autoptr(GString) buf = ram_block_format();
+
+ return human_readable_text_from_str(buf);
+}
+
+static int qmp_x_query_irq_foreach(Object *obj, void *opaque)
+{
+ InterruptStatsProvider *intc;
+ InterruptStatsProviderClass *k;
+ GString *buf = opaque;
+
+ if (object_dynamic_cast(obj, TYPE_INTERRUPT_STATS_PROVIDER)) {
+ intc = INTERRUPT_STATS_PROVIDER(obj);
+ k = INTERRUPT_STATS_PROVIDER_GET_CLASS(obj);
+ uint64_t *irq_counts;
+ unsigned int nb_irqs, i;
+ if (k->get_statistics &&
+ k->get_statistics(intc, &irq_counts, &nb_irqs)) {
+ if (nb_irqs > 0) {
+ g_string_append_printf(buf, "IRQ statistics for %s:\n",
+ object_get_typename(obj));
+ for (i = 0; i < nb_irqs; i++) {
+ if (irq_counts[i] > 0) {
+ g_string_append_printf(buf, "%2d: %" PRId64 "\n", i,
+ irq_counts[i]);
+ }
+ }
+ }
+ } else {
+ g_string_append_printf(buf,
+ "IRQ statistics not available for %s.\n",
+ object_get_typename(obj));
+ }
+ }
+
+ return 0;
+}
+
+HumanReadableText *qmp_x_query_irq(Error **errp)
+{
+ g_autoptr(GString) buf = g_string_new("");
+
+ object_child_foreach_recursive(object_get_root(),
+ qmp_x_query_irq_foreach, buf);
+
+ return human_readable_text_from_str(buf);
+}
+
+GuidInfo *qmp_query_vm_generation_id(Error **errp)
+{
+ GuidInfo *info;
+ VmGenIdState *vms;
+ Object *obj = find_vmgenid_dev();
+
+ if (!obj) {
+ error_setg(errp, "VM Generation ID device not found");
+ return NULL;
+ }
+ vms = VMGENID(obj);
+
+ info = g_malloc0(sizeof(*info));
+ info->guid = qemu_uuid_unparse_strdup(&vms->guid);
+ return info;
+}
diff --git a/hw/core/machine-smp.c b/hw/core/machine-smp.c
new file mode 100644
index 0000000000..27864c9507
--- /dev/null
+++ b/hw/core/machine-smp.c
@@ -0,0 +1,275 @@
+/*
+ * QEMU Machine core (related to -smp parsing)
+ *
+ * Copyright (c) 2021 Huawei Technologies Co., Ltd
+ *
+ * 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/>.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/boards.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+
+
+/*
+ * Report information of a machine's supported CPU topology hierarchy.
+ * Topology members will be ordered from the largest to the smallest
+ * in the string.
+ */
+static char *cpu_hierarchy_to_string(MachineState *ms)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ GString *s = g_string_new(NULL);
+
+ if (mc->smp_props.drawers_supported) {
+ g_string_append_printf(s, "drawers (%u) * ", ms->smp.drawers);
+ }
+
+ if (mc->smp_props.books_supported) {
+ g_string_append_printf(s, "books (%u) * ", ms->smp.books);
+ }
+
+ g_string_append_printf(s, "sockets (%u)", ms->smp.sockets);
+
+ if (mc->smp_props.dies_supported) {
+ g_string_append_printf(s, " * dies (%u)", ms->smp.dies);
+ }
+
+ if (mc->smp_props.clusters_supported) {
+ g_string_append_printf(s, " * clusters (%u)", ms->smp.clusters);
+ }
+
+ g_string_append_printf(s, " * cores (%u)", ms->smp.cores);
+ g_string_append_printf(s, " * threads (%u)", ms->smp.threads);
+
+ return g_string_free(s, false);
+}
+
+/*
+ * machine_parse_smp_config: Generic function used to parse the given
+ * SMP configuration
+ *
+ * Any missing parameter in "cpus/maxcpus/sockets/cores/threads" will be
+ * automatically computed based on the provided ones.
+ *
+ * In the calculation of omitted sockets/cores/threads: we prefer sockets
+ * over cores over threads before 6.2, while preferring cores over sockets
+ * over threads since 6.2.
+ *
+ * In the calculation of cpus/maxcpus: When both maxcpus and cpus are omitted,
+ * maxcpus will be computed from the given parameters and cpus will be set
+ * equal to maxcpus. When only one of maxcpus and cpus is given then the
+ * omitted one will be set to its given counterpart's value. Both maxcpus and
+ * cpus may be specified, but maxcpus must be equal to or greater than cpus.
+ *
+ * For compatibility, apart from the parameters that will be computed, newly
+ * introduced topology members which are likely to be target specific should
+ * be directly set as 1 if they are omitted (e.g. dies for PC since 4.1).
+ */
+void machine_parse_smp_config(MachineState *ms,
+ const SMPConfiguration *config, Error **errp)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ unsigned cpus = config->has_cpus ? config->cpus : 0;
+ unsigned drawers = config->has_drawers ? config->drawers : 0;
+ unsigned books = config->has_books ? config->books : 0;
+ unsigned sockets = config->has_sockets ? config->sockets : 0;
+ unsigned dies = config->has_dies ? config->dies : 0;
+ unsigned clusters = config->has_clusters ? config->clusters : 0;
+ unsigned cores = config->has_cores ? config->cores : 0;
+ unsigned threads = config->has_threads ? config->threads : 0;
+ unsigned maxcpus = config->has_maxcpus ? config->maxcpus : 0;
+ unsigned total_cpus;
+
+ /*
+ * Specified CPU topology parameters must be greater than zero,
+ * explicit configuration like "cpus=0" is not allowed.
+ */
+ if ((config->has_cpus && config->cpus == 0) ||
+ (config->has_drawers && config->drawers == 0) ||
+ (config->has_books && config->books == 0) ||
+ (config->has_sockets && config->sockets == 0) ||
+ (config->has_dies && config->dies == 0) ||
+ (config->has_clusters && config->clusters == 0) ||
+ (config->has_cores && config->cores == 0) ||
+ (config->has_threads && config->threads == 0) ||
+ (config->has_maxcpus && config->maxcpus == 0)) {
+ error_setg(errp, "Invalid CPU topology: "
+ "CPU topology parameters must be greater than zero");
+ return;
+ }
+
+ /*
+ * If not supported by the machine, a topology parameter must be
+ * omitted.
+ */
+ if (!mc->smp_props.clusters_supported && config->has_clusters) {
+ if (config->clusters > 1) {
+ error_setg(errp, "clusters not supported by this "
+ "machine's CPU topology");
+ return;
+ } else {
+ /* Here clusters only equals 1 since we've checked zero case. */
+ warn_report("Deprecated CPU topology (considered invalid): "
+ "Unsupported clusters parameter mustn't be "
+ "specified as 1");
+ }
+ }
+ clusters = clusters > 0 ? clusters : 1;
+
+ if (!mc->smp_props.dies_supported && config->has_dies) {
+ if (config->dies > 1) {
+ error_setg(errp, "dies not supported by this "
+ "machine's CPU topology");
+ return;
+ } else {
+ /* Here dies only equals 1 since we've checked zero case. */
+ warn_report("Deprecated CPU topology (considered invalid): "
+ "Unsupported dies parameter mustn't be "
+ "specified as 1");
+ }
+ }
+ dies = dies > 0 ? dies : 1;
+
+ if (!mc->smp_props.books_supported && config->has_books) {
+ if (config->books > 1) {
+ error_setg(errp, "books not supported by this "
+ "machine's CPU topology");
+ return;
+ } else {
+ /* Here books only equals 1 since we've checked zero case. */
+ warn_report("Deprecated CPU topology (considered invalid): "
+ "Unsupported books parameter mustn't be "
+ "specified as 1");
+ }
+ }
+ books = books > 0 ? books : 1;
+
+ if (!mc->smp_props.drawers_supported && config->has_drawers) {
+ if (config->drawers > 1) {
+ error_setg(errp, "drawers not supported by this "
+ "machine's CPU topology");
+ return;
+ } else {
+ /* Here drawers only equals 1 since we've checked zero case. */
+ warn_report("Deprecated CPU topology (considered invalid): "
+ "Unsupported drawers parameter mustn't be "
+ "specified as 1");
+ }
+ }
+ drawers = drawers > 0 ? drawers : 1;
+
+ /* compute missing values based on the provided ones */
+ if (cpus == 0 && maxcpus == 0) {
+ sockets = sockets > 0 ? sockets : 1;
+ cores = cores > 0 ? cores : 1;
+ threads = threads > 0 ? threads : 1;
+ } else {
+ maxcpus = maxcpus > 0 ? maxcpus : cpus;
+
+ if (mc->smp_props.prefer_sockets) {
+ /* prefer sockets over cores before 6.2 */
+ if (sockets == 0) {
+ cores = cores > 0 ? cores : 1;
+ threads = threads > 0 ? threads : 1;
+ sockets = maxcpus /
+ (drawers * books * dies * clusters * cores * threads);
+ } else if (cores == 0) {
+ threads = threads > 0 ? threads : 1;
+ cores = maxcpus /
+ (drawers * books * sockets * dies * clusters * threads);
+ }
+ } else {
+ /* prefer cores over sockets since 6.2 */
+ if (cores == 0) {
+ sockets = sockets > 0 ? sockets : 1;
+ threads = threads > 0 ? threads : 1;
+ cores = maxcpus /
+ (drawers * books * sockets * dies * clusters * threads);
+ } else if (sockets == 0) {
+ threads = threads > 0 ? threads : 1;
+ sockets = maxcpus /
+ (drawers * books * dies * clusters * cores * threads);
+ }
+ }
+
+ /* try to calculate omitted threads at last */
+ if (threads == 0) {
+ threads = maxcpus /
+ (drawers * books * sockets * dies * clusters * cores);
+ }
+ }
+
+ total_cpus = drawers * books * sockets * dies * clusters * cores * threads;
+ maxcpus = maxcpus > 0 ? maxcpus : total_cpus;
+ cpus = cpus > 0 ? cpus : maxcpus;
+
+ ms->smp.cpus = cpus;
+ ms->smp.drawers = drawers;
+ ms->smp.books = books;
+ ms->smp.sockets = sockets;
+ ms->smp.dies = dies;
+ ms->smp.clusters = clusters;
+ ms->smp.cores = cores;
+ ms->smp.threads = threads;
+ ms->smp.max_cpus = maxcpus;
+
+ mc->smp_props.has_clusters = config->has_clusters;
+
+ /* sanity-check of the computed topology */
+ if (total_cpus != maxcpus) {
+ g_autofree char *topo_msg = cpu_hierarchy_to_string(ms);
+ error_setg(errp, "Invalid CPU topology: "
+ "product of the hierarchy must match maxcpus: "
+ "%s != maxcpus (%u)",
+ topo_msg, maxcpus);
+ return;
+ }
+
+ if (maxcpus < cpus) {
+ g_autofree char *topo_msg = cpu_hierarchy_to_string(ms);
+ error_setg(errp, "Invalid CPU topology: "
+ "maxcpus must be equal to or greater than smp: "
+ "%s == maxcpus (%u) < smp_cpus (%u)",
+ topo_msg, maxcpus, cpus);
+ return;
+ }
+
+ if (ms->smp.cpus < mc->min_cpus) {
+ error_setg(errp, "Invalid SMP CPUs %d. The min CPUs "
+ "supported by machine '%s' is %d",
+ ms->smp.cpus,
+ mc->name, mc->min_cpus);
+ return;
+ }
+
+ if (ms->smp.max_cpus > mc->max_cpus) {
+ error_setg(errp, "Invalid SMP CPUs %d. The max CPUs "
+ "supported by machine '%s' is %d",
+ ms->smp.max_cpus,
+ mc->name, mc->max_cpus);
+ return;
+ }
+}
+
+unsigned int machine_topo_get_cores_per_socket(const MachineState *ms)
+{
+ return ms->smp.cores * ms->smp.clusters * ms->smp.dies;
+}
+
+unsigned int machine_topo_get_threads_per_socket(const MachineState *ms)
+{
+ return ms->smp.threads * machine_topo_get_cores_per_socket(ms);
+}
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 067f42b528..582c2df37a 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -11,33 +11,86 @@
*/
#include "qemu/osdep.h"
-#include "qemu/option.h"
-#include "qapi/qmp/qerror.h"
+#include "qemu/accel.h"
#include "sysemu/replay.h"
-#include "qemu/units.h"
#include "hw/boards.h"
#include "hw/loader.h"
#include "qapi/error.h"
-#include "qapi/qapi-visit-common.h"
#include "qapi/qapi-visit-machine.h"
-#include "qapi/visitor.h"
-#include "hw/sysbus.h"
+#include "qom/object_interfaces.h"
#include "sysemu/cpus.h"
#include "sysemu/sysemu.h"
#include "sysemu/reset.h"
#include "sysemu/runstate.h"
-#include "sysemu/numa.h"
-#include "qemu/error-report.h"
+#include "sysemu/xen.h"
#include "sysemu/qtest.h"
-#include "hw/pci/pci.h"
+#include "hw/pci/pci_bridge.h"
#include "hw/mem/nvdimm.h"
#include "migration/global_state.h"
-#include "migration/vmstate.h"
#include "exec/confidential-guest-support.h"
-#include "hw/virtio/virtio.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_6_1[] = {};
+GlobalProperty hw_compat_9_0[] = {};
+const size_t hw_compat_9_0_len = G_N_ELEMENTS(hw_compat_9_0);
+
+GlobalProperty hw_compat_8_2[] = {
+ { "migration", "zero-page-detection", "legacy"},
+ { TYPE_VIRTIO_IOMMU_PCI, "granule", "4k" },
+ { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "64" },
+};
+const size_t hw_compat_8_2_len = G_N_ELEMENTS(hw_compat_8_2);
+
+GlobalProperty hw_compat_8_1[] = {
+ { TYPE_PCI_BRIDGE, "x-pci-express-writeable-slt-bug", "true" },
+ { "ramfb", "x-migrate", "off" },
+ { "vfio-pci-nohotplug", "x-ramfb-migrate", "off" },
+ { "igb", "x-pcie-flr-init", "off" },
+};
+const size_t hw_compat_8_1_len = G_N_ELEMENTS(hw_compat_8_1);
+
+GlobalProperty hw_compat_8_0[] = {
+ { "migration", "multifd-flush-after-each-section", "on"},
+ { TYPE_PCI_DEVICE, "x-pcie-ari-nextfn-1", "on" },
+ { TYPE_VIRTIO_NET, "host_uso", "off"},
+ { TYPE_VIRTIO_NET, "guest_uso4", "off"},
+ { TYPE_VIRTIO_NET, "guest_uso6", "off"},
+};
+const size_t hw_compat_8_0_len = G_N_ELEMENTS(hw_compat_8_0);
+
+GlobalProperty hw_compat_7_2[] = {
+ { "e1000e", "migrate-timadj", "off" },
+ { "virtio-mem", "x-early-migration", "false" },
+ { "migration", "x-preempt-pre-7-2", "true" },
+ { TYPE_PCI_DEVICE, "x-pcie-err-unc-mask", "off" },
+};
+const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2);
+
+GlobalProperty hw_compat_7_1[] = {
+ { "virtio-device", "queue_reset", "false" },
+ { "virtio-rng-pci", "vectors", "0" },
+ { "virtio-rng-pci-transitional", "vectors", "0" },
+ { "virtio-rng-pci-non-transitional", "vectors", "0" },
+};
+const size_t hw_compat_7_1_len = G_N_ELEMENTS(hw_compat_7_1);
+
+GlobalProperty hw_compat_7_0[] = {
+ { "arm-gicv3-common", "force-8-bit-prio", "on" },
+ { "nvme-ns", "eui64-default", "on"},
+};
+const size_t hw_compat_7_0_len = G_N_ELEMENTS(hw_compat_7_0);
+
+GlobalProperty hw_compat_6_2[] = {
+ { "PIIX4_PM", "x-not-migrate-acpi-index", "on"},
+};
+const size_t hw_compat_6_2_len = G_N_ELEMENTS(hw_compat_6_2);
+
+GlobalProperty hw_compat_6_1[] = {
+ { "vhost-user-vsock-device", "seqpacket", "off" },
+ { "nvme-ns", "shared", "off" },
+};
const size_t hw_compat_6_1_len = G_N_ELEMENTS(hw_compat_6_1);
GlobalProperty hw_compat_6_0[] = {
@@ -46,6 +99,7 @@ GlobalProperty hw_compat_6_0[] = {
{ "nvme-ns", "eui64-default", "off"},
{ "e1000", "init-vet", "off" },
{ "e1000e", "init-vet", "off" },
+ { "vhost-vsock-device", "seqpacket", "off" },
};
const size_t hw_compat_6_0_len = G_N_ELEMENTS(hw_compat_6_0);
@@ -53,7 +107,8 @@ GlobalProperty hw_compat_5_2[] = {
{ "ICH9-LPC", "smm-compat", "on"},
{ "PIIX4_PM", "smm-compat", "on"},
{ "virtio-blk-device", "report-discard-granularity", "off" },
- { "virtio-net-pci", "vectors", "3"},
+ { "virtio-net-pci-base", "vectors", "3"},
+ { "nvme", "msix-exclusive-bar", "on"},
};
const size_t hw_compat_5_2_len = G_N_ELEMENTS(hw_compat_5_2);
@@ -511,6 +566,77 @@ static void machine_set_hmat(Object *obj, bool value, Error **errp)
ms->numa_state->hmat_enabled = value;
}
+static void machine_get_mem(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+ MemorySizeConfiguration mem = {
+ .has_size = true,
+ .size = ms->ram_size,
+ .has_max_size = !!ms->ram_slots,
+ .max_size = ms->maxram_size,
+ .has_slots = !!ms->ram_slots,
+ .slots = ms->ram_slots,
+ };
+ MemorySizeConfiguration *p_mem = &mem;
+
+ visit_type_MemorySizeConfiguration(v, name, &p_mem, &error_abort);
+}
+
+static void machine_set_mem(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ERRP_GUARD();
+ MachineState *ms = MACHINE(obj);
+ MachineClass *mc = MACHINE_GET_CLASS(obj);
+ MemorySizeConfiguration *mem;
+
+ if (!visit_type_MemorySizeConfiguration(v, name, &mem, errp)) {
+ return;
+ }
+
+ if (!mem->has_size) {
+ mem->has_size = true;
+ mem->size = mc->default_ram_size;
+ }
+ mem->size = QEMU_ALIGN_UP(mem->size, 8192);
+ if (mc->fixup_ram_size) {
+ mem->size = mc->fixup_ram_size(mem->size);
+ }
+ if ((ram_addr_t)mem->size != mem->size) {
+ error_setg(errp, "ram size too large");
+ goto out_free;
+ }
+
+ if (mem->has_max_size) {
+ if (mem->max_size < mem->size) {
+ error_setg(errp, "invalid value of maxmem: "
+ "maximum memory size (0x%" PRIx64 ") must be at least "
+ "the initial memory size (0x%" PRIx64 ")",
+ mem->max_size, mem->size);
+ goto out_free;
+ }
+ if (mem->has_slots && mem->slots && mem->max_size == mem->size) {
+ error_setg(errp, "invalid value of maxmem: "
+ "memory slots were specified but maximum memory size "
+ "(0x%" PRIx64 ") is equal to the initial memory size "
+ "(0x%" PRIx64 ")", mem->max_size, mem->size);
+ goto out_free;
+ }
+ ms->maxram_size = mem->max_size;
+ } else {
+ if (mem->has_slots) {
+ error_setg(errp, "slots specified but no max-size");
+ goto out_free;
+ }
+ ms->maxram_size = mem->size;
+ }
+ ms->ram_size = mem->size;
+ ms->ram_slots = mem->has_slots ? mem->slots : 0;
+out_free:
+ qapi_free_MemorySizeConfiguration(mem);
+}
+
static char *machine_get_nvdimm_persistence(Object *obj, Error **errp)
{
MachineState *ms = MACHINE(obj);
@@ -545,59 +671,48 @@ void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type)
bool device_is_dynamic_sysbus(MachineClass *mc, DeviceState *dev)
{
- bool allowed = false;
- strList *wl;
Object *obj = OBJECT(dev);
if (!object_dynamic_cast(obj, TYPE_SYS_BUS_DEVICE)) {
return false;
}
+ return device_type_is_dynamic_sysbus(mc, object_get_typename(obj));
+}
+
+bool device_type_is_dynamic_sysbus(MachineClass *mc, const char *type)
+{
+ bool allowed = false;
+ strList *wl;
+ ObjectClass *klass = object_class_by_name(type);
+
for (wl = mc->allowed_dynamic_sysbus_devices;
!allowed && wl;
wl = wl->next) {
- allowed |= !!object_dynamic_cast(obj, wl->value);
+ allowed |= !!object_class_dynamic_cast(klass, wl->value);
}
return allowed;
}
-static void validate_sysbus_device(SysBusDevice *sbdev, void *opaque)
-{
- MachineState *machine = opaque;
- MachineClass *mc = MACHINE_GET_CLASS(machine);
-
- if (!device_is_dynamic_sysbus(mc, DEVICE(sbdev))) {
- error_report("Option '-device %s' cannot be handled by this machine",
- object_class_get_name(object_get_class(OBJECT(sbdev))));
- exit(1);
- }
-}
-
-static char *machine_get_memdev(Object *obj, Error **errp)
+static char *machine_get_audiodev(Object *obj, Error **errp)
{
MachineState *ms = MACHINE(obj);
- return g_strdup(ms->ram_memdev_id);
+ return g_strdup(ms->audiodev);
}
-static void machine_set_memdev(Object *obj, const char *value, Error **errp)
+static void machine_set_audiodev(Object *obj, const char *value,
+ Error **errp)
{
MachineState *ms = MACHINE(obj);
- g_free(ms->ram_memdev_id);
- ms->ram_memdev_id = g_strdup(value);
-}
-
-static void machine_init_notify(Notifier *notifier, void *data)
-{
- MachineState *machine = MACHINE(qdev_get_machine());
+ if (!audio_state_by_name(value, errp)) {
+ return;
+ }
- /*
- * Loop through all dynamically created sysbus devices and check if they are
- * all allowed. If a device is not allowed, error out.
- */
- foreach_dynamic_sysbus_device(validate_sysbus_device, machine);
+ g_free(ms->audiodev);
+ ms->audiodev = g_strdup(value);
}
HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine)
@@ -610,7 +725,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine)
mc->possible_cpu_arch_ids(machine);
for (i = 0; i < machine->possible_cpus->len; i++) {
- Object *cpu;
+ CPUState *cpu;
HotpluggableCPU *cpu_item = g_new0(typeof(*cpu_item), 1);
cpu_item->type = g_strdup(machine->possible_cpus->cpus[i].type);
@@ -620,8 +735,7 @@ HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine)
cpu = machine->possible_cpus->cpus[i].cpu;
if (cpu) {
- cpu_item->has_qom_path = true;
- cpu_item->qom_path = object_get_canonical_path(cpu);
+ cpu_item->qom_path = object_get_canonical_path(OBJECT(cpu));
}
QAPI_LIST_PREPEND(head, cpu_item);
}
@@ -686,6 +800,11 @@ void machine_set_cpu_numa_node(MachineState *machine,
return;
}
+ if (props->has_cluster_id && !slot->props.has_cluster_id) {
+ error_setg(errp, "cluster-id is not supported");
+ return;
+ }
+
if (props->has_socket_id && !slot->props.has_socket_id) {
error_setg(errp, "socket-id is not supported");
return;
@@ -705,6 +824,11 @@ void machine_set_cpu_numa_node(MachineState *machine,
continue;
}
+ if (props->has_cluster_id &&
+ props->cluster_id != slot->props.cluster_id) {
+ continue;
+ }
+
if (props->has_die_id && props->die_id != slot->props.die_id) {
continue;
}
@@ -746,78 +870,22 @@ void machine_set_cpu_numa_node(MachineState *machine,
}
}
-static void smp_parse(MachineState *ms, SMPConfiguration *config, Error **errp)
-{
- unsigned cpus = config->has_cpus ? config->cpus : 0;
- unsigned sockets = config->has_sockets ? config->sockets : 0;
- unsigned cores = config->has_cores ? config->cores : 0;
- unsigned threads = config->has_threads ? config->threads : 0;
-
- if (config->has_dies && config->dies != 0 && config->dies != 1) {
- error_setg(errp, "dies not supported by this machine's CPU topology");
- return;
- }
-
- /* compute missing values, prefer sockets over cores over threads */
- if (cpus == 0 || sockets == 0) {
- cores = cores > 0 ? cores : 1;
- threads = threads > 0 ? threads : 1;
- if (cpus == 0) {
- sockets = sockets > 0 ? sockets : 1;
- cpus = cores * threads * sockets;
- } else {
- ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
- sockets = ms->smp.max_cpus / (cores * threads);
- }
- } else if (cores == 0) {
- threads = threads > 0 ? threads : 1;
- cores = cpus / (sockets * threads);
- cores = cores > 0 ? cores : 1;
- } else if (threads == 0) {
- threads = cpus / (cores * sockets);
- threads = threads > 0 ? threads : 1;
- } else if (sockets * cores * threads < cpus) {
- error_setg(errp, "cpu topology: "
- "sockets (%u) * cores (%u) * threads (%u) < "
- "smp_cpus (%u)",
- sockets, cores, threads, cpus);
- return;
- }
-
- ms->smp.max_cpus = config->has_maxcpus ? config->maxcpus : cpus;
-
- if (ms->smp.max_cpus < cpus) {
- error_setg(errp, "maxcpus must be equal to or greater than smp");
- return;
- }
-
- if (sockets * cores * threads != ms->smp.max_cpus) {
- error_setg(errp, "Invalid CPU topology: "
- "sockets (%u) * cores (%u) * threads (%u) "
- "!= maxcpus (%u)",
- sockets, cores, threads,
- ms->smp.max_cpus);
- return;
- }
-
- ms->smp.cpus = cpus;
- ms->smp.cores = cores;
- ms->smp.threads = threads;
- ms->smp.sockets = sockets;
-}
-
static void machine_get_smp(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
MachineState *ms = MACHINE(obj);
SMPConfiguration *config = &(SMPConfiguration){
- .has_cores = true, .cores = ms->smp.cores,
+ .has_cpus = true, .cpus = ms->smp.cpus,
+ .has_drawers = true, .drawers = ms->smp.drawers,
+ .has_books = true, .books = ms->smp.books,
.has_sockets = true, .sockets = ms->smp.sockets,
.has_dies = true, .dies = ms->smp.dies,
+ .has_clusters = true, .clusters = ms->smp.clusters,
+ .has_cores = true, .cores = ms->smp.cores,
.has_threads = true, .threads = ms->smp.threads,
- .has_cpus = true, .cpus = ms->smp.cpus,
.has_maxcpus = true, .maxcpus = ms->smp.max_cpus,
};
+
if (!visit_type_SMPConfiguration(v, name, &config, &error_abort)) {
return;
}
@@ -826,35 +894,83 @@ static void machine_get_smp(Object *obj, Visitor *v, const char *name,
static void machine_set_smp(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- MachineClass *mc = MACHINE_GET_CLASS(obj);
MachineState *ms = MACHINE(obj);
- SMPConfiguration *config;
- ERRP_GUARD();
+ g_autoptr(SMPConfiguration) config = NULL;
if (!visit_type_SMPConfiguration(v, name, &config, errp)) {
return;
}
- mc->smp_parse(ms, config, errp);
- if (*errp) {
- goto out_free;
+ machine_parse_smp_config(ms, config, errp);
+}
+
+static void machine_get_boot(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ MachineState *ms = MACHINE(obj);
+ BootConfiguration *config = &ms->boot_config;
+ visit_type_BootConfiguration(v, name, &config, &error_abort);
+}
+
+static void machine_free_boot_config(MachineState *ms)
+{
+ g_free(ms->boot_config.order);
+ g_free(ms->boot_config.once);
+ g_free(ms->boot_config.splash);
+}
+
+static void machine_copy_boot_config(MachineState *ms, BootConfiguration *config)
+{
+ MachineClass *machine_class = MACHINE_GET_CLASS(ms);
+
+ machine_free_boot_config(ms);
+ ms->boot_config = *config;
+ if (!config->order) {
+ ms->boot_config.order = g_strdup(machine_class->default_boot_order);
}
+}
- /* sanity-check smp_cpus and max_cpus against mc */
- if (ms->smp.cpus < mc->min_cpus) {
- error_setg(errp, "Invalid SMP CPUs %d. The min CPUs "
- "supported by machine '%s' is %d",
- ms->smp.cpus,
- mc->name, mc->min_cpus);
- } else if (ms->smp.max_cpus > mc->max_cpus) {
- error_setg(errp, "Invalid SMP CPUs %d. The max CPUs "
- "supported by machine '%s' is %d",
- current_machine->smp.max_cpus,
- mc->name, mc->max_cpus);
+static void machine_set_boot(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ERRP_GUARD();
+ MachineState *ms = MACHINE(obj);
+ BootConfiguration *config = NULL;
+
+ if (!visit_type_BootConfiguration(v, name, &config, errp)) {
+ return;
+ }
+ if (config->order) {
+ validate_bootdevices(config->order, errp);
+ if (*errp) {
+ goto out_free;
+ }
+ }
+ if (config->once) {
+ validate_bootdevices(config->once, errp);
+ if (*errp) {
+ goto out_free;
+ }
}
+ machine_copy_boot_config(ms, config);
+ /* Strings live in ms->boot_config. */
+ free(config);
+ return;
+
out_free:
- qapi_free_SMPConfiguration(config);
+ qapi_free_BootConfiguration(config);
+}
+
+void machine_add_audiodev_property(MachineClass *mc)
+{
+ ObjectClass *oc = OBJECT_CLASS(mc);
+
+ object_class_property_add_str(oc, "audiodev",
+ machine_get_audiodev,
+ machine_set_audiodev);
+ object_class_property_set_description(oc, "audiodev",
+ "Audiodev to use for default machine devices");
}
static void machine_class_init(ObjectClass *oc, void *data)
@@ -864,7 +980,6 @@ static void machine_class_init(ObjectClass *oc, void *data)
/* Default 128 MB as guest ram size */
mc->default_ram_size = 128 * MiB;
mc->rom_file_has_mr = true;
- mc->smp_parse = smp_parse;
/* numa node memory size aligned on 8MB by default.
* On Linux, each node's border has to be 8MB aligned
@@ -896,6 +1011,12 @@ static void machine_class_init(ObjectClass *oc, void *data)
object_class_property_set_description(oc, "dumpdtb",
"Dump current dtb to a file and quit");
+ object_class_property_add(oc, "boot", "BootConfiguration",
+ machine_get_boot, machine_set_boot,
+ NULL, NULL);
+ object_class_property_set_description(oc, "boot",
+ "Boot configuration");
+
object_class_property_add(oc, "smp", "SMPConfiguration",
machine_get_smp, machine_set_smp,
NULL, NULL);
@@ -957,11 +1078,18 @@ static void machine_class_init(ObjectClass *oc, void *data)
object_class_property_set_description(oc, "memory-encryption",
"Set memory encryption object to use");
- object_class_property_add_str(oc, "memory-backend",
- machine_get_memdev, machine_set_memdev);
+ object_class_property_add_link(oc, "memory-backend", TYPE_MEMORY_BACKEND,
+ offsetof(MachineState, memdev), object_property_allow_set_link,
+ OBJ_PROP_LINK_STRONG);
object_class_property_set_description(oc, "memory-backend",
"Set RAM backend"
"Valid value is ID of hostmem based backend");
+
+ object_class_property_add(oc, "memory", "MemorySizeConfiguration",
+ machine_get_mem, machine_set_mem,
+ NULL, NULL);
+ object_class_property_set_description(oc, "memory",
+ "Memory size configuration");
}
static void machine_class_base_init(ObjectClass *oc, void *data)
@@ -992,10 +1120,10 @@ static void machine_initfn(Object *obj)
ms->mem_merge = true;
ms->enable_graphics = true;
ms->kernel_cmdline = g_strdup("");
+ ms->ram_size = mc->default_ram_size;
+ ms->maxram_size = mc->default_ram_size;
if (mc->nvdimm_supported) {
- Object *obj = OBJECT(ms);
-
ms->nvdimms_state = g_new0(NVDIMMState, 1);
object_property_add_bool(obj, "nvdimm",
machine_get_nvdimm, machine_set_nvdimm);
@@ -1021,23 +1149,25 @@ static void machine_initfn(Object *obj)
"Table (HMAT)");
}
- /* Register notifier when init is done for sysbus sanity checks */
- ms->sysbus_notifier.notify = machine_init_notify;
- qemu_add_machine_init_done_notifier(&ms->sysbus_notifier);
-
/* default to mc->default_cpus */
ms->smp.cpus = mc->default_cpus;
ms->smp.max_cpus = mc->default_cpus;
- ms->smp.cores = 1;
+ ms->smp.drawers = 1;
+ ms->smp.books = 1;
+ ms->smp.sockets = 1;
ms->smp.dies = 1;
+ ms->smp.clusters = 1;
+ ms->smp.cores = 1;
ms->smp.threads = 1;
- ms->smp.sockets = 1;
+
+ machine_copy_boot_config(ms, &(BootConfiguration){ 0 });
}
static void machine_finalize(Object *obj)
{
MachineState *ms = MACHINE(obj);
+ machine_free_boot_config(ms);
g_free(ms->kernel_filename);
g_free(ms->initrd_filename);
g_free(ms->kernel_cmdline);
@@ -1048,6 +1178,7 @@ static void machine_finalize(Object *obj)
g_free(ms->device_memory);
g_free(ms->nvdimms_state);
g_free(ms->numa_state);
+ g_free(ms->audiodev);
}
bool machine_usb(MachineState *machine)
@@ -1070,6 +1201,11 @@ bool machine_mem_merge(MachineState *machine)
return machine->mem_merge;
}
+bool machine_require_guest_memfd(MachineState *machine)
+{
+ return machine->require_guest_memfd;
+}
+
static char *cpu_slot_to_string(const CPUArchId *cpu)
{
GString *s = g_string_new(NULL);
@@ -1077,8 +1213,17 @@ static char *cpu_slot_to_string(const CPUArchId *cpu)
g_string_append_printf(s, "socket-id: %"PRId64, cpu->props.socket_id);
}
if (cpu->props.has_die_id) {
+ if (s->len) {
+ g_string_append_printf(s, ", ");
+ }
g_string_append_printf(s, "die-id: %"PRId64, cpu->props.die_id);
}
+ if (cpu->props.has_cluster_id) {
+ if (s->len) {
+ g_string_append_printf(s, ", ");
+ }
+ g_string_append_printf(s, "cluster-id: %"PRId64, cpu->props.cluster_id);
+ }
if (cpu->props.has_core_id) {
if (s->len) {
g_string_append_printf(s, ", ");
@@ -1101,9 +1246,7 @@ static void numa_validate_initiator(NumaState *numa_state)
for (i = 0; i < numa_state->num_nodes; i++) {
if (numa_info[i].initiator == MAX_NODES) {
- error_report("The initiator of NUMA node %d is missing, use "
- "'-numa node,initiator' option to declare it", i);
- exit(1);
+ continue;
}
if (!numa_info[numa_info[i].initiator].present) {
@@ -1175,12 +1318,51 @@ static void machine_numa_finish_cpu_init(MachineState *machine)
g_string_free(s, true);
}
+static void validate_cpu_cluster_to_numa_boundary(MachineState *ms)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(ms);
+ NumaState *state = ms->numa_state;
+ const CPUArchIdList *possible_cpus = mc->possible_cpu_arch_ids(ms);
+ const CPUArchId *cpus = possible_cpus->cpus;
+ int i, j;
+
+ if (qtest_enabled() || state->num_nodes <= 1 || possible_cpus->len <= 1) {
+ return;
+ }
+
+ /*
+ * The Linux scheduling domain can't be parsed when the multiple CPUs
+ * in one cluster have been associated with different NUMA nodes. However,
+ * it's fine to associate one NUMA node with CPUs in different clusters.
+ */
+ for (i = 0; i < possible_cpus->len; i++) {
+ for (j = i + 1; j < possible_cpus->len; j++) {
+ if (cpus[i].props.has_socket_id &&
+ cpus[i].props.has_cluster_id &&
+ cpus[i].props.has_node_id &&
+ cpus[j].props.has_socket_id &&
+ cpus[j].props.has_cluster_id &&
+ cpus[j].props.has_node_id &&
+ cpus[i].props.socket_id == cpus[j].props.socket_id &&
+ cpus[i].props.cluster_id == cpus[j].props.cluster_id &&
+ cpus[i].props.node_id != cpus[j].props.node_id) {
+ warn_report("CPU-%d and CPU-%d in socket-%" PRId64 "-cluster-%" PRId64
+ " have been associated with node-%" PRId64 " and node-%" PRId64
+ " respectively. It can cause OSes like Linux to"
+ " misbehave", i, j, cpus[i].props.socket_id,
+ cpus[i].props.cluster_id, cpus[i].props.node_id,
+ cpus[j].props.node_id);
+ }
+ }
+ }
+}
+
MemoryRegion *machine_consume_memdev(MachineState *machine,
HostMemoryBackend *backend)
{
MemoryRegion *ret = host_memory_backend_get_memory(backend);
- if (memory_region_is_mapped(ret)) {
+ if (host_memory_backend_is_mapped(backend)) {
error_report("memory backend %s can't be used multiple times.",
object_get_canonical_path_component(OBJECT(backend)));
exit(EXIT_FAILURE);
@@ -1190,66 +1372,163 @@ MemoryRegion *machine_consume_memdev(MachineState *machine,
return ret;
}
-void machine_run_board_init(MachineState *machine)
+static bool create_default_memdev(MachineState *ms, const char *path, Error **errp)
{
- MachineClass *machine_class = MACHINE_GET_CLASS(machine);
+ 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]) {
+ /* Only a single CPU type allowed: use it as default. */
+ return mc->valid_cpu_types[0];
+ }
+ return mc->default_cpu_type;
+}
+
+static bool is_cpu_type_supported(const MachineState *machine, Error **errp)
+{
+ MachineClass *mc = MACHINE_GET_CLASS(machine);
ObjectClass *oc = object_class_by_name(machine->cpu_type);
CPUClass *cc;
+ int i;
+
+ /*
+ * Check if the user specified CPU type is supported when the valid
+ * CPU types have been determined. Note that the user specified CPU
+ * type is provided through '-cpu' option.
+ */
+ if (mc->valid_cpu_types) {
+ assert(mc->valid_cpu_types[0] != NULL);
+ for (i = 0; mc->valid_cpu_types[i]; i++) {
+ if (object_class_dynamic_cast(oc, mc->valid_cpu_types[i])) {
+ break;
+ }
+ }
+
+ /* The user specified CPU type isn't valid */
+ if (!mc->valid_cpu_types[i]) {
+ g_autofree char *requested = cpu_model_from_type(machine->cpu_type);
+ error_setg(errp, "Invalid CPU model: %s", requested);
+ if (!mc->valid_cpu_types[1]) {
+ g_autofree char *model = cpu_model_from_type(
+ mc->valid_cpu_types[0]);
+ error_append_hint(errp, "The only valid type is: %s\n", model);
+ } else {
+ error_append_hint(errp, "The valid models are: ");
+ for (i = 0; mc->valid_cpu_types[i]; i++) {
+ g_autofree char *model = cpu_model_from_type(
+ mc->valid_cpu_types[i]);
+ error_append_hint(errp, "%s%s",
+ model,
+ mc->valid_cpu_types[i + 1] ? ", " : "");
+ }
+ error_append_hint(errp, "\n");
+ }
+
+ return false;
+ }
+ }
+
+ /* Check if CPU type is deprecated and warn if so */
+ cc = CPU_CLASS(oc);
+ assert(cc != NULL);
+ if (cc->deprecation_note) {
+ warn_report("CPU model %s is deprecated -- %s",
+ machine->cpu_type, cc->deprecation_note);
+ }
+
+ return true;
+}
+
+void machine_run_board_init(MachineState *machine, const char *mem_path, Error **errp)
+{
+ ERRP_GUARD();
+ MachineClass *machine_class = MACHINE_GET_CLASS(machine);
/* This checkpoint is required by replay to separate prior clock
reading from the other reads, because timer polling functions query
clock values from the log. */
replay_checkpoint(CHECKPOINT_INIT);
- if (machine->ram_memdev_id) {
- Object *o;
- o = object_resolve_path_type(machine->ram_memdev_id,
- TYPE_MEMORY_BACKEND, NULL);
- machine->ram = machine_consume_memdev(machine, MEMORY_BACKEND(o));
+ if (!xen_enabled()) {
+ /* On 32-bit hosts, QEMU is limited by virtual address space */
+ if (machine->ram_size > (2047 << 20) && HOST_LONG_BITS == 32) {
+ error_setg(errp, "at most 2047 MB RAM can be simulated");
+ return;
+ }
+ }
+
+ if (machine->memdev) {
+ ram_addr_t backend_size = object_property_get_uint(OBJECT(machine->memdev),
+ "size", &error_abort);
+ if (backend_size != machine->ram_size) {
+ error_setg(errp, "Machine memory size does not match the size of the memory backend");
+ return;
+ }
+ } else if (machine_class->default_ram_id && machine->ram_size &&
+ numa_uses_legacy_mem()) {
+ if (object_property_find(object_get_objects_root(),
+ machine_class->default_ram_id)) {
+ error_setg(errp, "object's id '%s' is reserved for the default"
+ " RAM backend, it can't be used for any other purposes",
+ machine_class->default_ram_id);
+ error_append_hint(errp,
+ "Change the object's 'id' to something else or disable"
+ " automatic creation of the default RAM backend by setting"
+ " 'memory-backend=%s' with '-machine'.\n",
+ machine_class->default_ram_id);
+ return;
+ }
+ if (!create_default_memdev(current_machine, mem_path, errp)) {
+ return;
+ }
}
if (machine->numa_state) {
numa_complete_configuration(machine);
if (machine->numa_state->num_nodes) {
machine_numa_finish_cpu_init(machine);
- }
- }
-
- /* If the machine supports the valid_cpu_types check and the user
- * specified a CPU with -cpu check here that the user CPU is supported.
- */
- if (machine_class->valid_cpu_types && machine->cpu_type) {
- int i;
-
- for (i = 0; machine_class->valid_cpu_types[i]; i++) {
- if (object_class_dynamic_cast(oc,
- machine_class->valid_cpu_types[i])) {
- /* The user specificed CPU is in the valid field, we are
- * good to go.
- */
- break;
+ if (machine_class->cpu_cluster_has_numa_boundary) {
+ validate_cpu_cluster_to_numa_boundary(machine);
}
}
+ }
- if (!machine_class->valid_cpu_types[i]) {
- /* The user specified CPU is not valid */
- error_report("Invalid CPU type: %s", machine->cpu_type);
- error_printf("The valid types are: %s",
- machine_class->valid_cpu_types[0]);
- for (i = 1; machine_class->valid_cpu_types[i]; i++) {
- error_printf(", %s", machine_class->valid_cpu_types[i]);
- }
- error_printf("\n");
-
- exit(1);
- }
+ if (!machine->ram && machine->memdev) {
+ machine->ram = machine_consume_memdev(machine, machine->memdev);
}
- /* Check if CPU type is deprecated and warn if so */
- cc = CPU_CLASS(oc);
- if (cc && cc->deprecation_note) {
- warn_report("CPU model %s is deprecated -- %s", machine->cpu_type,
- cc->deprecation_note);
+ /* Check if the CPU type is supported */
+ if (machine->cpu_type && !is_cpu_type_supported(machine, errp)) {
+ return;
}
if (machine->cgs) {
@@ -1297,9 +1576,9 @@ void qdev_machine_creation_done(void)
{
cpu_synchronize_all_post_init();
- if (current_machine->boot_once) {
- qemu_boot_set(current_machine->boot_once, &error_fatal);
- qemu_register_reset(restore_boot_order, g_strdup(current_machine->boot_order));
+ if (current_machine->boot_config.once) {
+ qemu_boot_set(current_machine->boot_config.once, &error_fatal);
+ qemu_register_reset(restore_boot_order, g_strdup(current_machine->boot_config.order));
}
/*
@@ -1312,14 +1591,13 @@ void qdev_machine_creation_done(void)
/* TODO: once all bus devices are qdevified, this should be done
* when bus is created by qdev.c */
/*
- * TODO: If we had a main 'reset container' that the whole system
- * lived in, we could reset that using the multi-phase reset
- * APIs. For the moment, we just reset the sysbus, which will cause
+ * This is where we arrange for the sysbus to be reset when the
+ * whole simulation is reset. In turn, resetting the sysbus will cause
* all devices hanging off it (and all their child buses, recursively)
* to be reset. Note that this will *not* reset any Device objects
* which are not attached to some part of the qbus tree!
*/
- qemu_register_reset(resettable_cold_reset_fn, sysbus_get_default());
+ qemu_register_resettable(OBJECT(sysbus_get_default()));
notifier_list_notify(&machine_init_done_notifiers, NULL);
diff --git a/hw/core/meson.build b/hw/core/meson.build
index 18f44fb7c2..f20d4143f7 100644
--- a/hw/core/meson.build
+++ b/hw/core/meson.build
@@ -1,45 +1,47 @@
# core qdev-related obj files, also used by *-user and unit tests
-hwcore_files = files(
+hwcore_ss.add(files(
'bus.c',
- 'hotplug.c',
'qdev-properties.c',
'qdev.c',
- 'reset.c',
+ 'resetcontainer.c',
'resettable.c',
'vmstate-if.c',
# irq.c needed for qdev GPIO handling:
'irq.c',
'clock.c',
'qdev-clock.c',
-)
+))
common_ss.add(files('cpu-common.c'))
-common_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
-common_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c'))
-common_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c'))
-common_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c'))
-common_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c'))
-common_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c'))
-common_ss.add(when: 'CONFIG_REGISTER', if_true: files('register.c'))
-common_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c'))
-common_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c'))
+common_ss.add(files('machine-smp.c'))
+system_ss.add(when: 'CONFIG_FITLOADER', if_true: files('loader-fit.c'))
+system_ss.add(when: 'CONFIG_GENERIC_LOADER', if_true: files('generic-loader.c'))
+system_ss.add(when: ['CONFIG_GUEST_LOADER', fdt], if_true: files('guest-loader.c'))
+system_ss.add(when: 'CONFIG_OR_IRQ', if_true: files('or-irq.c'))
+system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('platform-bus.c'))
+system_ss.add(when: 'CONFIG_PTIMER', if_true: files('ptimer.c'))
+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'))
-softmmu_ss.add(files(
+system_ss.add(files(
'cpu-sysemu.c',
'fw-path-provider.c',
+ 'gpio.c',
+ 'hotplug.c',
'loader.c',
'machine-hmp-cmds.c',
+ 'machine-qmp-cmds.c',
'machine.c',
'nmi.c',
'null-machine.c',
+ 'numa.c',
'qdev-fw.c',
+ 'qdev-hotplug.c',
'qdev-properties-system.c',
+ 'reset.c',
'sysbus.c',
'vm-change-state-handler.c',
'clock-vmstate.c',
))
-
-specific_ss.add(when: 'CONFIG_SOFTMMU', if_true: files(
- 'machine-qmp-cmds.c',
- 'numa.c',
-))
diff --git a/hw/core/nmi.c b/hw/core/nmi.c
index 481c4b3c7e..a7bce8a04a 100644
--- a/hw/core/nmi.c
+++ b/hw/core/nmi.c
@@ -22,7 +22,6 @@
#include "qemu/osdep.h"
#include "hw/nmi.h"
#include "qapi/error.h"
-#include "qapi/qmp/qerror.h"
#include "qemu/module.h"
#include "monitor/monitor.h"
@@ -70,7 +69,7 @@ void nmi_monitor_handle(int cpu_index, Error **errp)
if (ns.handled) {
error_propagate(errp, ns.err);
} else {
- error_setg(errp, QERR_UNSUPPORTED);
+ error_setg(errp, "machine does not provide NMIs");
}
}
diff --git a/hw/core/numa.c b/hw/core/numa.c
index 510d096a88..f8ce332cfe 100644
--- a/hw/core/numa.c
+++ b/hw/core/numa.c
@@ -28,7 +28,6 @@
#include "sysemu/numa.h"
#include "exec/cpu-common.h"
#include "exec/ramlist.h"
-#include "qemu/bitmap.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
#include "qapi/opts-visitor.h"
@@ -36,7 +35,6 @@
#include "sysemu/qtest.h"
#include "hw/core/cpu.h"
#include "hw/mem/pc-dimm.h"
-#include "migration/vmstate.h"
#include "hw/boards.h"
#include "hw/mem/memory-device.h"
#include "qemu/option.h"
@@ -130,9 +128,9 @@ static void parse_numa_node(MachineState *ms, NumaNodeOptions *node,
}
}
- have_memdevs = have_memdevs ? : node->has_memdev;
- have_mem = have_mem ? : node->has_mem;
- if ((node->has_mem && have_memdevs) || (node->has_memdev && have_mem)) {
+ have_memdevs = have_memdevs || node->memdev;
+ have_mem = have_mem || node->has_mem;
+ if ((node->has_mem && have_memdevs) || (node->memdev && have_mem)) {
error_setg(errp, "numa configuration should use either mem= or memdev=,"
"mixing both is not allowed");
return;
@@ -152,7 +150,7 @@ static void parse_numa_node(MachineState *ms, NumaNodeOptions *node,
" use -numa node,memdev instead");
}
}
- if (node->has_memdev) {
+ if (node->memdev) {
Object *o;
o = object_resolve_path_type(node->memdev, TYPE_MEMORY_BACKEND, NULL);
if (!o) {
@@ -229,7 +227,8 @@ void parse_numa_hmat_lb(NumaState *numa_state, NumaHmatLBOptions *node,
node->target, numa_state->num_nodes);
return;
}
- if (!numa_info[node->initiator].has_cpu) {
+ if (!numa_info[node->initiator].has_cpu &&
+ !numa_info[node->initiator].has_gi) {
error_setg(errp, "Invalid initiator=%d, it isn't an "
"initiator proximity domain", node->initiator);
return;
@@ -531,10 +530,17 @@ static int parse_numa(void *opaque, QemuOpts *opts, Error **errp)
/* Fix up legacy suffix-less format */
if ((object->type == NUMA_OPTIONS_TYPE_NODE) && object->u.node.has_mem) {
const char *mem_str = qemu_opt_get(opts, "mem");
- qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem);
+ int ret = qemu_strtosz_MiB(mem_str, NULL, &object->u.node.mem);
+
+ if (ret < 0) {
+ error_setg_errno(&err, -ret, "could not parse memory size '%s'",
+ mem_str);
+ }
}
- set_numa_options(ms, object, &err);
+ if (!err) {
+ set_numa_options(ms, object, &err);
+ }
qapi_free_NumaOptions(object);
if (err) {
@@ -695,7 +701,7 @@ void numa_complete_configuration(MachineState *ms)
}
if (!numa_uses_legacy_mem() && mc->default_ram_id) {
- if (ms->ram_memdev_id) {
+ if (ms->memdev) {
error_report("'-machine memory-backend' and '-numa memdev'"
" properties are mutually exclusive");
exit(1);
@@ -756,6 +762,7 @@ static void numa_stat_memory_devices(NumaNodeMem node_mem[])
PCDIMMDeviceInfo *pcdimm_info;
VirtioPMEMDeviceInfo *vpi;
VirtioMEMDeviceInfo *vmi;
+ SgxEPCDeviceInfo *se;
for (info = info_list; info; info = info->next) {
MemoryDeviceInfo *value = info->value;
@@ -781,6 +788,11 @@ static void numa_stat_memory_devices(NumaNodeMem node_mem[])
node_mem[vmi->node].node_mem += vmi->size;
node_mem[vmi->node].node_plugged_mem += vmi->size;
break;
+ case MEMORY_DEVICE_INFO_KIND_SGX_EPC:
+ se = value->u.sgx_epc.data;
+ node_mem[se->node].node_mem += se->size;
+ node_mem[se->node].node_plugged_mem = 0;
+ break;
default:
g_assert_not_reached();
}
@@ -816,6 +828,19 @@ static int ram_block_notify_add_single(RAMBlock *rb, void *opaque)
return 0;
}
+static int ram_block_notify_remove_single(RAMBlock *rb, void *opaque)
+{
+ const ram_addr_t max_size = qemu_ram_get_max_length(rb);
+ const ram_addr_t size = qemu_ram_get_used_length(rb);
+ void *host = qemu_ram_get_host_addr(rb);
+ RAMBlockNotifier *notifier = opaque;
+
+ if (host) {
+ notifier->ram_block_removed(notifier, host, size, max_size);
+ }
+ return 0;
+}
+
void ram_block_notifier_add(RAMBlockNotifier *n)
{
QLIST_INSERT_HEAD(&ram_list.ramblock_notifiers, n, next);
@@ -829,13 +854,18 @@ void ram_block_notifier_add(RAMBlockNotifier *n)
void ram_block_notifier_remove(RAMBlockNotifier *n)
{
QLIST_REMOVE(n, next);
+
+ if (n->ram_block_removed) {
+ qemu_ram_foreach_block(ram_block_notify_remove_single, n);
+ }
}
void ram_block_notify_add(void *host, size_t size, size_t max_size)
{
RAMBlockNotifier *notifier;
+ RAMBlockNotifier *next;
- QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) {
+ QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) {
if (notifier->ram_block_added) {
notifier->ram_block_added(notifier, host, size, max_size);
}
@@ -845,8 +875,9 @@ void ram_block_notify_add(void *host, size_t size, size_t max_size)
void ram_block_notify_remove(void *host, size_t size, size_t max_size)
{
RAMBlockNotifier *notifier;
+ RAMBlockNotifier *next;
- QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) {
+ QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) {
if (notifier->ram_block_removed) {
notifier->ram_block_removed(notifier, host, size, max_size);
}
@@ -856,8 +887,9 @@ void ram_block_notify_remove(void *host, size_t size, size_t max_size)
void ram_block_notify_resize(void *host, size_t old_size, size_t new_size)
{
RAMBlockNotifier *notifier;
+ RAMBlockNotifier *next;
- QLIST_FOREACH(notifier, &ram_list.ramblock_notifiers, next) {
+ QLIST_FOREACH_SAFE(notifier, &ram_list.ramblock_notifiers, next, next) {
if (notifier->ram_block_resized) {
notifier->ram_block_resized(notifier, host, old_size, new_size);
}
diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c
index d8f3754e96..13907df026 100644
--- a/hw/core/or-irq.c
+++ b/hw/core/or-irq.c
@@ -31,7 +31,7 @@
static void or_irq_handler(void *opaque, int n, int level)
{
- qemu_or_irq *s = OR_IRQ(opaque);
+ OrIRQState *s = OR_IRQ(opaque);
int or_level = 0;
int i;
@@ -46,7 +46,7 @@ static void or_irq_handler(void *opaque, int n, int level)
static void or_irq_reset(DeviceState *dev)
{
- qemu_or_irq *s = OR_IRQ(dev);
+ OrIRQState *s = OR_IRQ(dev);
int i;
for (i = 0; i < MAX_OR_LINES; i++) {
@@ -56,7 +56,7 @@ static void or_irq_reset(DeviceState *dev)
static void or_irq_realize(DeviceState *dev, Error **errp)
{
- qemu_or_irq *s = OR_IRQ(dev);
+ OrIRQState *s = OR_IRQ(dev);
assert(s->num_lines <= MAX_OR_LINES);
@@ -65,7 +65,7 @@ static void or_irq_realize(DeviceState *dev, Error **errp)
static void or_irq_init(Object *obj)
{
- qemu_or_irq *s = OR_IRQ(obj);
+ OrIRQState *s = OR_IRQ(obj);
qdev_init_gpio_out(DEVICE(obj), &s->out_irq, 1);
}
@@ -84,7 +84,7 @@ static void or_irq_init(Object *obj)
static bool vmstate_extras_needed(void *opaque)
{
- qemu_or_irq *s = OR_IRQ(opaque);
+ OrIRQState *s = OR_IRQ(opaque);
return s->num_lines >= OLD_MAX_OR_LINES;
}
@@ -94,8 +94,8 @@ static const VMStateDescription vmstate_or_irq_extras = {
.version_id = 1,
.minimum_version_id = 1,
.needed = vmstate_extras_needed,
- .fields = (VMStateField[]) {
- VMSTATE_VARRAY_UINT16_UNSAFE(levels, qemu_or_irq, num_lines, 0,
+ .fields = (const VMStateField[]) {
+ VMSTATE_VARRAY_UINT16_UNSAFE(levels, OrIRQState, num_lines, 0,
vmstate_info_bool, bool),
VMSTATE_END_OF_LIST(),
},
@@ -105,18 +105,18 @@ static const VMStateDescription vmstate_or_irq = {
.name = TYPE_OR_IRQ,
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
- VMSTATE_BOOL_SUB_ARRAY(levels, qemu_or_irq, 0, OLD_MAX_OR_LINES),
+ .fields = (const VMStateField[]) {
+ VMSTATE_BOOL_SUB_ARRAY(levels, OrIRQState, 0, OLD_MAX_OR_LINES),
VMSTATE_END_OF_LIST(),
},
- .subsections = (const VMStateDescription*[]) {
+ .subsections = (const VMStateDescription * const []) {
&vmstate_or_irq_extras,
NULL
},
};
static Property or_irq_properties[] = {
- DEFINE_PROP_UINT16("num-lines", qemu_or_irq, num_lines, 1),
+ DEFINE_PROP_UINT16("num-lines", OrIRQState, num_lines, 1),
DEFINE_PROP_END_OF_LIST(),
};
@@ -136,7 +136,7 @@ static void or_irq_class_init(ObjectClass *klass, void *data)
static const TypeInfo or_irq_type_info = {
.name = TYPE_OR_IRQ,
.parent = TYPE_DEVICE,
- .instance_size = sizeof(qemu_or_irq),
+ .instance_size = sizeof(OrIRQState),
.instance_init = or_irq_init,
.class_init = or_irq_class_init,
};
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index 6ba19fd965..b1517592c6 100644
--- a/hw/core/ptimer.c
+++ b/hw/core/ptimer.c
@@ -10,11 +10,10 @@
#include "hw/ptimer.h"
#include "migration/vmstate.h"
#include "qemu/host-utils.h"
-#include "sysemu/replay.h"
+#include "exec/replay-core.h"
#include "sysemu/cpu-timers.h"
#include "sysemu/qtest.h"
#include "block/aio.h"
-#include "sysemu/cpus.h"
#include "hw/clock.h"
#define DELTA_ADJUST 1
@@ -442,7 +441,7 @@ const VMStateDescription vmstate_ptimer = {
.name = "ptimer",
.version_id = 1,
.minimum_version_id = 1,
- .fields = (VMStateField[]) {
+ .fields = (const VMStateField[]) {
VMSTATE_UINT8(enabled, ptimer_state),
VMSTATE_UINT64(limit, ptimer_state),
VMSTATE_UINT64(delta, ptimer_state),
diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c
index 117f4c6ea4..82799577f3 100644
--- a/hw/core/qdev-clock.c
+++ b/hw/core/qdev-clock.c
@@ -134,7 +134,7 @@ void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
Clock **clkp;
/* offset cannot be inside the DeviceState part */
assert(elem->offset > sizeof(DeviceState));
- clkp = (Clock **)(((void *) dev) + elem->offset);
+ clkp = ((void *)dev) + elem->offset;
if (elem->is_output) {
*clkp = qdev_init_clock_out(dev, elem->name);
} else {
diff --git a/hw/core/qdev-hotplug.c b/hw/core/qdev-hotplug.c
new file mode 100644
index 0000000000..d495d0e9c7
--- /dev/null
+++ b/hw/core/qdev-hotplug.c
@@ -0,0 +1,73 @@
+/*
+ * QDev Hotplug handlers
+ *
+ * Copyright (c) Red Hat
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-core.h"
+#include "hw/boards.h"
+
+HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev)
+{
+ MachineState *machine;
+ MachineClass *mc;
+ Object *m_obj = qdev_get_machine();
+
+ if (object_dynamic_cast(m_obj, TYPE_MACHINE)) {
+ machine = MACHINE(m_obj);
+ mc = MACHINE_GET_CLASS(machine);
+ if (mc->get_hotplug_handler) {
+ return mc->get_hotplug_handler(machine, dev);
+ }
+ }
+
+ return NULL;
+}
+
+bool qdev_hotplug_allowed(DeviceState *dev, Error **errp)
+{
+ MachineState *machine;
+ MachineClass *mc;
+ Object *m_obj = qdev_get_machine();
+
+ if (object_dynamic_cast(m_obj, TYPE_MACHINE)) {
+ machine = MACHINE(m_obj);
+ mc = MACHINE_GET_CLASS(machine);
+ if (mc->hotplug_allowed) {
+ return mc->hotplug_allowed(machine, dev, errp);
+ }
+ }
+
+ return true;
+}
+
+HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev)
+{
+ if (dev->parent_bus) {
+ return dev->parent_bus->hotplug_handler;
+ }
+ return NULL;
+}
+
+HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
+{
+ HotplugHandler *hotplug_ctrl = qdev_get_machine_hotplug_handler(dev);
+
+ if (hotplug_ctrl == NULL && dev->parent_bus) {
+ hotplug_ctrl = qdev_get_bus_hotplug_handler(dev);
+ }
+ return hotplug_ctrl;
+}
+
+/* can be used as ->unplug() callback for the simple cases */
+void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
+ DeviceState *dev, Error **errp)
+{
+ qdev_unrealize(dev);
+}
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
index e71f5d64d1..d79d6f4b53 100644
--- a/hw/core/qdev-properties-system.c
+++ b/hw/core/qdev-properties-system.c
@@ -18,6 +18,7 @@
#include "qapi/qapi-types-block.h"
#include "qapi/qapi-types-machine.h"
#include "qapi/qapi-types-migration.h"
+#include "qapi/qapi-visit-virtio.h"
#include "qapi/qmp/qerror.h"
#include "qemu/ctype.h"
#include "qemu/cutils.h"
@@ -32,6 +33,8 @@
#include "sysemu/blockdev.h"
#include "net/net.h"
#include "hw/pci/pci.h"
+#include "hw/pci/pcie.h"
+#include "hw/i386/x86.h"
#include "util/block-helpers.h"
static bool check_prop_still_unset(Object *obj, const char *name,
@@ -105,7 +108,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name,
}
if (*ptr) {
- /* BlockBackend alread exists. So, we want to change attached node */
+ /* BlockBackend already exists. So, we want to change attached node */
blk = *ptr;
ctx = blk_get_aio_context(blk);
bs = bdrv_lookup_bs(NULL, str, errp);
@@ -118,9 +121,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name,
"node");
}
- aio_context_acquire(ctx);
blk_replace_bs(blk, bs, errp);
- aio_context_release(ctx);
return;
}
@@ -141,8 +142,9 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name,
* aware of iothreads require their BlockBackends to be in the main
* AioContext.
*/
- ctx = iothread ? bdrv_get_aio_context(bs) : qemu_get_aio_context();
- blk = blk_new(ctx, 0, BLK_PERM_ALL);
+ ctx = bdrv_get_aio_context(bs);
+ blk = blk_new(iothread ? ctx : qemu_get_aio_context(),
+ 0, BLK_PERM_ALL);
blk_created = true;
ret = blk_insert_bs(blk, bs, errp);
@@ -201,12 +203,8 @@ static void release_drive(Object *obj, const char *name, void *opaque)
BlockBackend **ptr = object_field_prop_ptr(obj, prop);
if (*ptr) {
- AioContext *ctx = blk_get_aio_context(*ptr);
-
- aio_context_acquire(ctx);
blockdev_auto_del(*ptr);
blk_detach_dev(*ptr, dev);
- aio_context_release(ctx);
}
}
@@ -244,6 +242,7 @@ static void get_chr(Object *obj, Visitor *v, const char *name, void *opaque,
static void set_chr(Object *obj, Visitor *v, const char *name, void *opaque,
Error **errp)
{
+ ERRP_GUARD();
Property *prop = opaque;
CharBackend *be = object_field_prop_ptr(obj, prop);
Chardev *s;
@@ -431,6 +430,12 @@ static void set_netdev(Object *obj, Visitor *v, const char *name,
goto out;
}
+ if (peers[i]->info->check_peer_type) {
+ if (!peers[i]->info->check_peer_type(peers[i], obj->class, errp)) {
+ goto out;
+ }
+ }
+
ncs[i] = peers[i];
ncs[i]->queue_index = i;
}
@@ -438,7 +443,7 @@ static void set_netdev(Object *obj, Visitor *v, const char *name,
peers_ptr->queues = queues;
out:
- error_set_from_qdev_prop_error(errp, err, obj, name, str);
+ error_set_from_qdev_prop_error(errp, err, obj, prop->name, str);
g_free(str);
}
@@ -468,24 +473,16 @@ static void set_audiodev(Object *obj, Visitor *v, const char* name,
Property *prop = opaque;
QEMUSoundCard *card = object_field_prop_ptr(obj, prop);
AudioState *state;
- int err = 0;
- char *str;
+ g_autofree char *str = NULL;
if (!visit_type_str(v, name, &str, errp)) {
return;
}
- state = audio_state_by_name(str);
-
- if (!state) {
- err = -ENOENT;
- goto out;
+ state = audio_state_by_name(str, errp);
+ if (state) {
+ card->state = state;
}
- card->state = state;
-
-out:
- error_set_from_qdev_prop_error(errp, err, obj, name, str);
- g_free(str);
}
const PropertyInfo qdev_prop_audiodev = {
@@ -551,13 +548,38 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
/* --- lost tick policy --- */
+static void qdev_propinfo_set_losttickpolicy(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ Property *prop = opaque;
+ int *ptr = object_field_prop_ptr(obj, prop);
+ int value;
+
+ if (!visit_type_enum(v, name, &value, prop->info->enum_table, errp)) {
+ return;
+ }
+
+ if (value == LOST_TICK_POLICY_SLEW) {
+ MachineState *ms = MACHINE(qdev_get_machine());
+
+ if (!object_dynamic_cast(OBJECT(ms), TYPE_X86_MACHINE)) {
+ error_setg(errp,
+ "the 'slew' policy is only available for x86 machines");
+ return;
+ }
+ }
+
+ *ptr = value;
+}
+
QEMU_BUILD_BUG_ON(sizeof(LostTickPolicy) != sizeof(int));
const PropertyInfo qdev_prop_losttickpolicy = {
.name = "LostTickPolicy",
.enum_table = &LostTickPolicy_lookup,
.get = qdev_propinfo_get_enum,
- .set = qdev_propinfo_set_enum,
+ .set = qdev_propinfo_set_losttickpolicy,
.set_default_value = qdev_propinfo_set_default_value_enum,
};
@@ -644,6 +666,44 @@ const PropertyInfo qdev_prop_multifd_compression = {
.set_default_value = qdev_propinfo_set_default_value_enum,
};
+/* --- MigMode --- */
+
+QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int));
+
+const PropertyInfo qdev_prop_mig_mode = {
+ .name = "MigMode",
+ .description = "mig_mode values, "
+ "normal,cpr-reboot",
+ .enum_table = &MigMode_lookup,
+ .get = qdev_propinfo_get_enum,
+ .set = qdev_propinfo_set_enum,
+ .set_default_value = qdev_propinfo_set_default_value_enum,
+};
+
+/* --- GranuleMode --- */
+
+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",
+ .enum_table = &GranuleMode_lookup,
+ .get = qdev_propinfo_get_enum,
+ .set = qdev_propinfo_set_enum,
+ .set_default_value = qdev_propinfo_set_default_value_enum,
+};
+
+const PropertyInfo qdev_prop_zero_page_detection = {
+ .name = "ZeroPageDetection",
+ .description = "zero_page_detection values, "
+ "none,legacy,multifd",
+ .enum_table = &ZeroPageDetection_lookup,
+ .get = qdev_propinfo_get_enum,
+ .set = qdev_propinfo_set_enum,
+ .set_default_value = qdev_propinfo_set_default_value_enum,
+};
+
/* --- Reserved Region --- */
/*
@@ -662,7 +722,7 @@ static void get_reserved_region(Object *obj, Visitor *v, const char *name,
int rc;
rc = snprintf(buffer, sizeof(buffer), "0x%"PRIx64":0x%"PRIx64":%u",
- rr->low, rr->high, rr->type);
+ range_lob(&rr->range), range_upb(&rr->range), rr->type);
assert(rc < sizeof(buffer));
visit_type_str(v, name, &p, errp);
@@ -673,18 +733,16 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name,
{
Property *prop = opaque;
ReservedRegion *rr = object_field_prop_ptr(obj, prop);
- Error *local_err = NULL;
const char *endptr;
+ uint64_t lob, upb;
char *str;
int ret;
- visit_type_str(v, name, &str, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!visit_type_str(v, name, &str, errp)) {
return;
}
- ret = qemu_strtou64(str, &endptr, 16, &rr->low);
+ ret = qemu_strtou64(str, &endptr, 16, &lob);
if (ret) {
error_setg(errp, "start address of '%s'"
" must be a hexadecimal integer", name);
@@ -694,7 +752,7 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name,
goto separator_error;
}
- ret = qemu_strtou64(endptr + 1, &endptr, 16, &rr->high);
+ ret = qemu_strtou64(endptr + 1, &endptr, 16, &upb);
if (ret) {
error_setg(errp, "end address of '%s'"
" must be a hexadecimal integer", name);
@@ -704,6 +762,8 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name,
goto separator_error;
}
+ range_set_bounds(&rr->range, lob, upb);
+
ret = qemu_strtoui(endptr + 1, &endptr, 10, &rr->type);
if (ret) {
error_setg(errp, "type of '%s'"
@@ -906,7 +966,7 @@ const PropertyInfo qdev_prop_off_auto_pcibar = {
.set_default_value = qdev_propinfo_set_default_value_enum,
};
-/* --- PCIELinkSpeed 2_5/5/8/16 -- */
+/* --- PCIELinkSpeed 2_5/5/8/16/32/64 -- */
static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
@@ -928,6 +988,12 @@ static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name,
case QEMU_PCI_EXP_LNK_16GT:
speed = PCIE_LINK_SPEED_16;
break;
+ case QEMU_PCI_EXP_LNK_32GT:
+ speed = PCIE_LINK_SPEED_32;
+ break;
+ case QEMU_PCI_EXP_LNK_64GT:
+ speed = PCIE_LINK_SPEED_64;
+ break;
default:
/* Unreachable */
abort();
@@ -961,6 +1027,12 @@ static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name,
case PCIE_LINK_SPEED_16:
*p = QEMU_PCI_EXP_LNK_16GT;
break;
+ case PCIE_LINK_SPEED_32:
+ *p = QEMU_PCI_EXP_LNK_32GT;
+ break;
+ case PCIE_LINK_SPEED_64:
+ *p = QEMU_PCI_EXP_LNK_64GT;
+ break;
default:
/* Unreachable */
abort();
@@ -969,7 +1041,7 @@ static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name,
const PropertyInfo qdev_prop_pcie_link_speed = {
.name = "PCIELinkSpeed",
- .description = "2_5/5/8/16",
+ .description = "2_5/5/8/16/32/64",
.enum_table = &PCIELinkSpeed_lookup,
.get = get_prop_pcielinkspeed,
.set = set_prop_pcielinkspeed,
@@ -1071,7 +1143,7 @@ static void get_uuid(Object *obj, Visitor *v, const char *name, void *opaque,
{
Property *prop = opaque;
QemuUUID *uuid = object_field_prop_ptr(obj, prop);
- char buffer[UUID_FMT_LEN + 1];
+ char buffer[UUID_STR_LEN];
char *p = buffer;
qemu_uuid_unparse(uuid, buffer);
@@ -1113,3 +1185,61 @@ const PropertyInfo qdev_prop_uuid = {
.set = set_uuid,
.set_default_value = set_default_uuid_auto,
};
+
+/* --- s390 cpu entitlement policy --- */
+
+QEMU_BUILD_BUG_ON(sizeof(CpuS390Entitlement) != sizeof(int));
+
+const PropertyInfo qdev_prop_cpus390entitlement = {
+ .name = "CpuS390Entitlement",
+ .description = "low/medium (default)/high",
+ .enum_table = &CpuS390Entitlement_lookup,
+ .get = qdev_propinfo_get_enum,
+ .set = qdev_propinfo_set_enum,
+ .set_default_value = qdev_propinfo_set_default_value_enum,
+};
+
+/* --- IOThreadVirtQueueMappingList --- */
+
+static void get_iothread_vq_mapping_list(Object *obj, Visitor *v,
+ const char *name, void *opaque, Error **errp)
+{
+ IOThreadVirtQueueMappingList **prop_ptr =
+ object_field_prop_ptr(obj, opaque);
+
+ visit_type_IOThreadVirtQueueMappingList(v, name, prop_ptr, errp);
+}
+
+static void set_iothread_vq_mapping_list(Object *obj, Visitor *v,
+ const char *name, void *opaque, Error **errp)
+{
+ IOThreadVirtQueueMappingList **prop_ptr =
+ object_field_prop_ptr(obj, opaque);
+ IOThreadVirtQueueMappingList *list;
+
+ if (!visit_type_IOThreadVirtQueueMappingList(v, name, &list, errp)) {
+ return;
+ }
+
+ qapi_free_IOThreadVirtQueueMappingList(*prop_ptr);
+ *prop_ptr = list;
+}
+
+static void release_iothread_vq_mapping_list(Object *obj,
+ const char *name, void *opaque)
+{
+ IOThreadVirtQueueMappingList **prop_ptr =
+ object_field_prop_ptr(obj, opaque);
+
+ qapi_free_IOThreadVirtQueueMappingList(*prop_ptr);
+ *prop_ptr = NULL;
+}
+
+const PropertyInfo qdev_prop_iothread_vq_mapping_list = {
+ .name = "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,
+};
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index c34aac6ebc..86a583574d 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -2,13 +2,14 @@
#include "hw/qdev-properties.h"
#include "qapi/error.h"
#include "qapi/qapi-types-misc.h"
-#include "qapi/qmp/qerror.h"
+#include "qapi/qmp/qlist.h"
#include "qemu/ctype.h"
#include "qemu/error-report.h"
#include "qapi/visitor.h"
#include "qemu/units.h"
#include "qemu/cutils.h"
#include "qdev-prop-internal.h"
+#include "qom/qom-qobject.h"
void qdev_prop_set_after_realize(DeviceState *dev, const char *name,
Error **errp)
@@ -428,6 +429,25 @@ const PropertyInfo qdev_prop_int64 = {
.set_default_value = qdev_propinfo_set_default_value_int,
};
+static void set_uint64_checkmask(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ Property *prop = opaque;
+ uint64_t *ptr = object_field_prop_ptr(obj, prop);
+
+ visit_type_uint64(v, name, ptr, errp);
+ if (*ptr & ~prop->bitmask) {
+ error_setg(errp, "Property value for '%s' has bits outside mask '0x%" PRIx64 "'",
+ name, prop->bitmask);
+ }
+}
+
+const PropertyInfo qdev_prop_uint64_checkmask = {
+ .name = "uint64",
+ .get = get_uint64,
+ .set = set_uint64_checkmask,
+};
+
/* --- string --- */
static void release_string(Object *obj, const char *name, void *opaque)
@@ -525,98 +545,206 @@ const PropertyInfo qdev_prop_size32 = {
/* --- support for array properties --- */
-/* Used as an opaque for the object properties we add for each
- * array element. Note that the struct Property must be first
- * in the struct so that a pointer to this works as the opaque
- * for the underlying element's property hooks as well as for
- * our own release callback.
+typedef struct ArrayElementList ArrayElementList;
+
+struct ArrayElementList {
+ ArrayElementList *next;
+ void *value;
+};
+
+/*
+ * Given an array property @parent_prop in @obj, return a Property for a
+ * 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.
*/
-typedef struct {
- struct Property prop;
- char *propname;
- ObjectPropertyRelease *release;
-} ArrayElementProperty;
-
-/* object property release callback for array element properties:
- * we call the underlying element's property release hook, and
- * then free the memory we allocated when we added the property.
+static Property array_elem_prop(Object *obj, Property *parent_prop,
+ const char *name, char *elem)
+{
+ return (Property) {
+ .info = parent_prop->arrayinfo,
+ .name = name,
+ /*
+ * This ugly piece of pointer arithmetic sets up the offset so
+ * that when the underlying release hook calls qdev_get_prop_ptr
+ * they get the right answer despite the array element not actually
+ * being inside the device struct.
+ */
+ .offset = (uintptr_t)elem - (uintptr_t)obj,
+ };
+}
+
+/*
+ * Object property release callback for array properties: We call the
+ * underlying element's property release hook for each element.
+ *
+ * Note that it is the responsibility of the individual device's deinit
+ * to free the array proper.
*/
-static void array_element_release(Object *obj, const char *name, void *opaque)
+static void release_prop_array(Object *obj, const char *name, void *opaque)
{
- ArrayElementProperty *p = opaque;
- if (p->release) {
- p->release(obj, name, opaque);
+ Property *prop = opaque;
+ uint32_t *alenptr = object_field_prop_ptr(obj, prop);
+ void **arrayptr = (void *)obj + prop->arrayoffset;
+ char *elem = *arrayptr;
+ int i;
+
+ if (!prop->arrayinfo->release) {
+ return;
+ }
+
+ for (i = 0; i < *alenptr; i++) {
+ Property elem_prop = array_elem_prop(obj, prop, name, elem);
+ prop->arrayinfo->release(obj, NULL, &elem_prop);
+ elem += prop->arrayfieldsize;
}
- g_free(p->propname);
- g_free(p);
}
-static void set_prop_arraylen(Object *obj, Visitor *v, const char *name,
- void *opaque, Error **errp)
+/*
+ * Setter for an array property. This sets both the array length (which
+ * is technically the property field in the object) and the array itself
+ * (a pointer to which is stored in the additional field described by
+ * prop->arrayoffset).
+ */
+static void set_prop_array(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
{
- /* Setter for the property which defines the length of a
- * variable-sized property array. As well as actually setting the
- * array-length field in the device struct, we have to create the
- * array itself and dynamically add the corresponding properties.
- */
+ ERRP_GUARD();
Property *prop = opaque;
uint32_t *alenptr = object_field_prop_ptr(obj, prop);
void **arrayptr = (void *)obj + prop->arrayoffset;
- void *eltptr;
- const char *arrayname;
- int i;
+ ArrayElementList *list, *elem, *next;
+ const size_t size = sizeof(*list);
+ char *elemptr;
+ bool ok = true;
if (*alenptr) {
error_setg(errp, "array size property %s may not be set more than once",
name);
return;
}
- if (!visit_type_uint32(v, name, alenptr, errp)) {
+
+ if (!visit_start_list(v, name, (GenericList **) &list, size, errp)) {
return;
}
- if (!*alenptr) {
+
+ /* Read the whole input into a temporary list */
+ elem = list;
+ while (elem) {
+ Property elem_prop;
+
+ elem->value = g_malloc0(prop->arrayfieldsize);
+ elem_prop = array_elem_prop(obj, prop, name, elem->value);
+ prop->arrayinfo->set(obj, v, NULL, &elem_prop, errp);
+ if (*errp) {
+ ok = false;
+ goto out_obj;
+ }
+ if (*alenptr == INT_MAX) {
+ error_setg(errp, "array is too big");
+ return;
+ }
+ (*alenptr)++;
+ elem = (ArrayElementList *) visit_next_list(v, (GenericList*) elem,
+ size);
+ }
+
+ ok = visit_check_list(v, errp);
+out_obj:
+ visit_end_list(v, (void**) &list);
+
+ if (!ok) {
+ for (elem = list; elem; elem = next) {
+ Property elem_prop = array_elem_prop(obj, prop, name,
+ elem->value);
+ if (prop->arrayinfo->release) {
+ prop->arrayinfo->release(obj, NULL, &elem_prop);
+ }
+ next = elem->next;
+ g_free(elem->value);
+ g_free(elem);
+ }
return;
}
- /* DEFINE_PROP_ARRAY guarantees that name should start with this prefix;
- * strip it off so we can get the name of the array itself.
+ /*
+ * Now that we know how big the array has to be, move the data over to a
+ * linear array and free the temporary list.
*/
- assert(strncmp(name, PROP_ARRAY_LEN_PREFIX,
- strlen(PROP_ARRAY_LEN_PREFIX)) == 0);
- arrayname = name + strlen(PROP_ARRAY_LEN_PREFIX);
+ *arrayptr = g_malloc_n(*alenptr, prop->arrayfieldsize);
+ elemptr = *arrayptr;
+ for (elem = list; elem; elem = next) {
+ memcpy(elemptr, elem->value, prop->arrayfieldsize);
+ elemptr += prop->arrayfieldsize;
+ next = elem->next;
+ g_free(elem->value);
+ g_free(elem);
+ }
+}
- /* Note that it is the responsibility of the individual device's deinit
- * to free the array proper.
- */
- *arrayptr = eltptr = g_malloc0(*alenptr * prop->arrayfieldsize);
- for (i = 0; i < *alenptr; i++, eltptr += prop->arrayfieldsize) {
- char *propname = g_strdup_printf("%s[%d]", arrayname, i);
- ArrayElementProperty *arrayprop = g_new0(ArrayElementProperty, 1);
- arrayprop->release = prop->arrayinfo->release;
- arrayprop->propname = propname;
- arrayprop->prop.info = prop->arrayinfo;
- arrayprop->prop.name = propname;
- /* This ugly piece of pointer arithmetic sets up the offset so
- * that when the underlying get/set hooks call qdev_get_prop_ptr
- * they get the right answer despite the array element not actually
- * being inside the device struct.
- */
- arrayprop->prop.offset = eltptr - (void *)obj;
- assert(object_field_prop_ptr(obj, &arrayprop->prop) == eltptr);
- object_property_add(obj, propname,
- arrayprop->prop.info->name,
- field_prop_getter(arrayprop->prop.info),
- field_prop_setter(arrayprop->prop.info),
- array_element_release,
- arrayprop);
+static void get_prop_array(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ ERRP_GUARD();
+ Property *prop = opaque;
+ uint32_t *alenptr = object_field_prop_ptr(obj, prop);
+ void **arrayptr = (void *)obj + prop->arrayoffset;
+ char *elemptr = *arrayptr;
+ ArrayElementList *list = NULL, *elem;
+ ArrayElementList **tail = &list;
+ const size_t size = sizeof(*list);
+ int i;
+ bool ok;
+
+ /* At least the string output visitor needs a real list */
+ for (i = 0; i < *alenptr; i++) {
+ elem = g_new0(ArrayElementList, 1);
+ elem->value = elemptr;
+ elemptr += prop->arrayfieldsize;
+
+ *tail = elem;
+ tail = &elem->next;
+ }
+
+ if (!visit_start_list(v, name, (GenericList **) &list, size, errp)) {
+ return;
+ }
+
+ elem = list;
+ while (elem) {
+ Property elem_prop = array_elem_prop(obj, prop, name, elem->value);
+ prop->arrayinfo->get(obj, v, NULL, &elem_prop, errp);
+ if (*errp) {
+ goto out_obj;
+ }
+ elem = (ArrayElementList *) visit_next_list(v, (GenericList*) elem,
+ size);
+ }
+
+ /* visit_check_list() can only fail for input visitors */
+ ok = visit_check_list(v, errp);
+ assert(ok);
+
+out_obj:
+ visit_end_list(v, (void**) &list);
+
+ while (list) {
+ elem = list;
+ list = elem->next;
+ g_free(elem);
}
}
-const PropertyInfo qdev_prop_arraylen = {
- .name = "uint32",
- .get = get_uint32,
- .set = set_prop_arraylen,
- .set_default_value = qdev_propinfo_set_default_value_uint,
+static void default_prop_array(ObjectProperty *op, const Property *prop)
+{
+ object_property_set_default_list(op);
+}
+
+const PropertyInfo qdev_prop_array = {
+ .name = "list",
+ .get = get_prop_array,
+ .set = set_prop_array,
+ .release = release_prop_array,
+ .set_default_value = default_prop_array,
};
/* --- public helpers --- */
@@ -663,7 +791,7 @@ void error_set_from_qdev_prop_error(Error **errp, int ret, Object *obj,
break;
default:
case -EINVAL:
- error_setg(errp, QERR_PROPERTY_VALUE_BAD,
+ error_setg(errp, "Property '%s.%s' doesn't take value '%s'",
object_get_typename(obj), name, value);
break;
case -ENOENT:
@@ -720,6 +848,13 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value)
&error_abort);
}
+void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values)
+{
+ object_property_set_qobject(OBJECT(dev), name, QOBJECT(values),
+ &error_abort);
+ qobject_unref(values);
+}
+
static GPtrArray *global_props(void)
{
static GPtrArray *gp;
@@ -940,16 +1075,18 @@ void device_class_set_props(DeviceClass *dc, Property *props)
void qdev_alias_all_properties(DeviceState *target, Object *source)
{
ObjectClass *class;
- Property *prop;
+ ObjectPropertyIterator iter;
+ ObjectProperty *prop;
class = object_get_class(OBJECT(target));
- do {
- DeviceClass *dc = DEVICE_CLASS(class);
- for (prop = dc->props_; prop && prop->name; prop++) {
- object_property_add_alias(source, prop->name,
- OBJECT(target), prop->name);
+ object_class_property_iter_init(&iter, class);
+ while ((prop = object_property_iter_next(&iter))) {
+ if (object_property_find(source, prop->name)) {
+ continue; /* skip duplicate properties */
}
- class = object_class_get_parent(class);
- } while (class != object_class_by_name(TYPE_DEVICE));
+
+ object_property_add_alias(source, prop->name,
+ OBJECT(target), prop->name);
+ }
}
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index cefc5eaa0a..00efaf1bd1 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -28,11 +28,10 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qapi-events-qdev.h"
-#include "qapi/qmp/qerror.h"
+#include "qapi/qmp/qdict.h"
#include "qapi/visitor.h"
#include "qemu/error-report.h"
#include "qemu/option.h"
-#include "hw/hotplug.h"
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/boards.h"
@@ -147,8 +146,21 @@ bool qdev_set_parent_bus(DeviceState *dev, BusState *bus, Error **errp)
DeviceState *qdev_new(const char *name)
{
- if (!object_class_by_name(name)) {
- module_load_qom_one(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));
}
@@ -211,14 +223,17 @@ void device_listener_unregister(DeviceListener *listener)
QTAILQ_REMOVE(&device_listeners, listener, link);
}
-bool qdev_should_hide_device(QemuOpts *opts)
+bool qdev_should_hide_device(const QDict *opts, bool from_json, Error **errp)
{
+ ERRP_GUARD();
DeviceListener *listener;
QTAILQ_FOREACH(listener, &device_listeners, link) {
if (listener->hide_device) {
- if (listener->hide_device(listener, opts)) {
+ if (listener->hide_device(listener, opts, from_json, errp)) {
return true;
+ } else if (*errp) {
+ return false;
}
}
}
@@ -234,112 +249,6 @@ void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id,
dev->alias_required_for_version = required_for_version;
}
-HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev)
-{
- MachineState *machine;
- MachineClass *mc;
- Object *m_obj = qdev_get_machine();
-
- if (object_dynamic_cast(m_obj, TYPE_MACHINE)) {
- machine = MACHINE(m_obj);
- mc = MACHINE_GET_CLASS(machine);
- if (mc->get_hotplug_handler) {
- return mc->get_hotplug_handler(machine, dev);
- }
- }
-
- return NULL;
-}
-
-bool qdev_hotplug_allowed(DeviceState *dev, Error **errp)
-{
- MachineState *machine;
- MachineClass *mc;
- Object *m_obj = qdev_get_machine();
-
- if (object_dynamic_cast(m_obj, TYPE_MACHINE)) {
- machine = MACHINE(m_obj);
- mc = MACHINE_GET_CLASS(machine);
- if (mc->hotplug_allowed) {
- return mc->hotplug_allowed(machine, dev, errp);
- }
- }
-
- return true;
-}
-
-HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev)
-{
- if (dev->parent_bus) {
- return dev->parent_bus->hotplug_handler;
- }
- return NULL;
-}
-
-HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
-{
- HotplugHandler *hotplug_ctrl = qdev_get_machine_hotplug_handler(dev);
-
- if (hotplug_ctrl == NULL && dev->parent_bus) {
- hotplug_ctrl = qdev_get_bus_hotplug_handler(dev);
- }
- return hotplug_ctrl;
-}
-
-static int qdev_prereset(DeviceState *dev, void *opaque)
-{
- trace_qdev_reset_tree(dev, object_get_typename(OBJECT(dev)));
- return 0;
-}
-
-static int qbus_prereset(BusState *bus, void *opaque)
-{
- trace_qbus_reset_tree(bus, object_get_typename(OBJECT(bus)));
- return 0;
-}
-
-static int qdev_reset_one(DeviceState *dev, void *opaque)
-{
- device_legacy_reset(dev);
-
- return 0;
-}
-
-static int qbus_reset_one(BusState *bus, void *opaque)
-{
- BusClass *bc = BUS_GET_CLASS(bus);
- trace_qbus_reset(bus, object_get_typename(OBJECT(bus)));
- if (bc->reset) {
- bc->reset(bus);
- }
- return 0;
-}
-
-void qdev_reset_all(DeviceState *dev)
-{
- trace_qdev_reset_all(dev, object_get_typename(OBJECT(dev)));
- qdev_walk_children(dev, qdev_prereset, qbus_prereset,
- qdev_reset_one, qbus_reset_one, NULL);
-}
-
-void qdev_reset_all_fn(void *opaque)
-{
- qdev_reset_all(DEVICE(opaque));
-}
-
-void qbus_reset_all(BusState *bus)
-{
- trace_qbus_reset_all(bus, object_get_typename(OBJECT(bus)));
- qbus_walk_children(bus, qdev_prereset, qbus_prereset,
- qdev_reset_one, qbus_reset_one, NULL);
-}
-
-void qbus_reset_all_fn(void *opaque)
-{
- BusState *bus = opaque;
- qbus_reset_all(bus);
-}
-
void device_cold_reset(DeviceState *dev)
{
resettable_reset(OBJECT(dev), RESET_TYPE_COLD);
@@ -367,13 +276,6 @@ static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb,
}
}
-/* can be used as ->unplug() callback for the simple cases */
-void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
- DeviceState *dev, Error **errp)
-{
- qdev_unrealize(dev);
-}
-
bool qdev_realize(DeviceState *dev, BusState *bus, Error **errp)
{
assert(!dev->realized && !dev->parent_bus);
@@ -427,185 +329,11 @@ bool qdev_machine_modified(void)
return qdev_hot_added || qdev_hot_removed;
}
-BusState *qdev_get_parent_bus(DeviceState *dev)
+BusState *qdev_get_parent_bus(const DeviceState *dev)
{
return dev->parent_bus;
}
-static NamedGPIOList *qdev_get_named_gpio_list(DeviceState *dev,
- const char *name)
-{
- NamedGPIOList *ngl;
-
- QLIST_FOREACH(ngl, &dev->gpios, node) {
- /* NULL is a valid and matchable name. */
- if (g_strcmp0(name, ngl->name) == 0) {
- return ngl;
- }
- }
-
- ngl = g_malloc0(sizeof(*ngl));
- ngl->name = g_strdup(name);
- QLIST_INSERT_HEAD(&dev->gpios, ngl, node);
- return ngl;
-}
-
-void qdev_init_gpio_in_named_with_opaque(DeviceState *dev,
- qemu_irq_handler handler,
- void *opaque,
- const char *name, int n)
-{
- int i;
- NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
-
- assert(gpio_list->num_out == 0 || !name);
- gpio_list->in = qemu_extend_irqs(gpio_list->in, gpio_list->num_in, handler,
- opaque, n);
-
- if (!name) {
- name = "unnamed-gpio-in";
- }
- for (i = gpio_list->num_in; i < gpio_list->num_in + n; i++) {
- gchar *propname = g_strdup_printf("%s[%u]", name, i);
-
- object_property_add_child(OBJECT(dev), propname,
- OBJECT(gpio_list->in[i]));
- g_free(propname);
- }
-
- gpio_list->num_in += n;
-}
-
-void qdev_init_gpio_in(DeviceState *dev, qemu_irq_handler handler, int n)
-{
- qdev_init_gpio_in_named(dev, handler, NULL, n);
-}
-
-void qdev_init_gpio_out_named(DeviceState *dev, qemu_irq *pins,
- const char *name, int n)
-{
- int i;
- NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
-
- assert(gpio_list->num_in == 0 || !name);
-
- if (!name) {
- name = "unnamed-gpio-out";
- }
- memset(pins, 0, sizeof(*pins) * n);
- for (i = 0; i < n; ++i) {
- gchar *propname = g_strdup_printf("%s[%u]", name,
- gpio_list->num_out + i);
-
- object_property_add_link(OBJECT(dev), propname, TYPE_IRQ,
- (Object **)&pins[i],
- object_property_allow_set_link,
- OBJ_PROP_LINK_STRONG);
- g_free(propname);
- }
- gpio_list->num_out += n;
-}
-
-void qdev_init_gpio_out(DeviceState *dev, qemu_irq *pins, int n)
-{
- qdev_init_gpio_out_named(dev, pins, NULL, n);
-}
-
-qemu_irq qdev_get_gpio_in_named(DeviceState *dev, const char *name, int n)
-{
- NamedGPIOList *gpio_list = qdev_get_named_gpio_list(dev, name);
-
- assert(n >= 0 && n < gpio_list->num_in);
- return gpio_list->in[n];
-}
-
-qemu_irq qdev_get_gpio_in(DeviceState *dev, int n)
-{
- return qdev_get_gpio_in_named(dev, NULL, n);
-}
-
-void qdev_connect_gpio_out_named(DeviceState *dev, const char *name, int n,
- qemu_irq pin)
-{
- char *propname = g_strdup_printf("%s[%d]",
- name ? name : "unnamed-gpio-out", n);
- if (pin && !OBJECT(pin)->parent) {
- /* We need a name for object_property_set_link to work */
- object_property_add_child(container_get(qdev_get_machine(),
- "/unattached"),
- "non-qdev-gpio[*]", OBJECT(pin));
- }
- object_property_set_link(OBJECT(dev), propname, OBJECT(pin), &error_abort);
- g_free(propname);
-}
-
-qemu_irq qdev_get_gpio_out_connector(DeviceState *dev, const char *name, int n)
-{
- g_autofree char *propname = g_strdup_printf("%s[%d]",
- name ? name : "unnamed-gpio-out", n);
-
- qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
- NULL);
-
- return ret;
-}
-
-/* disconnect a GPIO output, returning the disconnected input (if any) */
-
-static qemu_irq qdev_disconnect_gpio_out_named(DeviceState *dev,
- const char *name, int n)
-{
- char *propname = g_strdup_printf("%s[%d]",
- name ? name : "unnamed-gpio-out", n);
-
- qemu_irq ret = (qemu_irq)object_property_get_link(OBJECT(dev), propname,
- NULL);
- if (ret) {
- object_property_set_link(OBJECT(dev), propname, NULL, NULL);
- }
- g_free(propname);
- return ret;
-}
-
-qemu_irq qdev_intercept_gpio_out(DeviceState *dev, qemu_irq icpt,
- const char *name, int n)
-{
- qemu_irq disconnected = qdev_disconnect_gpio_out_named(dev, name, n);
- qdev_connect_gpio_out_named(dev, name, n, icpt);
- return disconnected;
-}
-
-void qdev_connect_gpio_out(DeviceState * dev, int n, qemu_irq pin)
-{
- qdev_connect_gpio_out_named(dev, NULL, n, pin);
-}
-
-void qdev_pass_gpios(DeviceState *dev, DeviceState *container,
- const char *name)
-{
- int i;
- NamedGPIOList *ngl = qdev_get_named_gpio_list(dev, name);
-
- for (i = 0; i < ngl->num_in; i++) {
- const char *nm = ngl->name ? ngl->name : "unnamed-gpio-in";
- char *propname = g_strdup_printf("%s[%d]", nm, i);
-
- object_property_add_alias(OBJECT(container), propname,
- OBJECT(dev), propname);
- g_free(propname);
- }
- for (i = 0; i < ngl->num_out; i++) {
- const char *nm = ngl->name ? ngl->name : "unnamed-gpio-out";
- char *propname = g_strdup_printf("%s[%d]", nm, i);
-
- object_property_add_alias(OBJECT(container), propname,
- OBJECT(dev), propname);
- g_free(propname);
- }
- QLIST_REMOVE(ngl, node);
- QLIST_INSERT_HEAD(&container->gpios, ngl, node);
-}
-
BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
{
BusState *bus;
@@ -698,6 +426,26 @@ char *qdev_get_dev_path(DeviceState *dev)
return NULL;
}
+void qdev_add_unplug_blocker(DeviceState *dev, Error *reason)
+{
+ dev->unplug_blockers = g_slist_prepend(dev->unplug_blockers, reason);
+}
+
+void qdev_del_unplug_blocker(DeviceState *dev, Error *reason)
+{
+ dev->unplug_blockers = g_slist_remove(dev->unplug_blockers, reason);
+}
+
+bool qdev_unplug_blocked(DeviceState *dev, Error **errp)
+{
+ if (dev->unplug_blockers) {
+ error_propagate(errp, error_copy(dev->unplug_blockers->data));
+ return true;
+ }
+
+ return false;
+}
+
static bool device_get_realized(Object *obj, Error **errp)
{
DeviceState *dev = DEVICE(obj);
@@ -730,7 +478,8 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
static int unattached_count;
if (dev->hotplugged && !dc->hotpluggable) {
- error_setg(errp, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj));
+ error_setg(errp, "Device '%s' does not support hotplugging",
+ object_get_typename(obj));
return;
}
@@ -934,6 +683,8 @@ static void device_finalize(Object *obj)
DeviceState *dev = DEVICE(obj);
+ g_assert(!dev->unplug_blockers);
+
QLIST_FOREACH_SAFE(ngl, &dev->gpios, node, next) {
QLIST_REMOVE(ngl, node);
qemu_free_irqs(ngl->in, ngl->num_in);
@@ -950,12 +701,13 @@ static void device_finalize(Object *obj)
if (dev->pending_deleted_event) {
g_assert(dev->canonical_path);
- qapi_event_send_device_deleted(!!dev->id, dev->id, dev->canonical_path);
+ qapi_event_send_device_deleted(dev->id, dev->canonical_path);
g_free(dev->canonical_path);
dev->canonical_path = NULL;
}
- qemu_opts_del(dev->opts);
+ qobject_unref(dev->opts);
+ g_free(dev->id);
}
static void device_class_base_init(ObjectClass *class, void *data)
@@ -1116,16 +868,6 @@ void device_class_set_parent_unrealize(DeviceClass *dc,
dc->unrealize = dev_unrealize;
}
-void device_legacy_reset(DeviceState *dev)
-{
- DeviceClass *klass = DEVICE_GET_CLASS(dev);
-
- trace_qdev_reset(dev, object_get_typename(OBJECT(dev)));
- if (klass->reset) {
- klass->reset(dev);
- }
-}
-
Object *qdev_get_machine(void)
{
static Object *dev;
@@ -1137,6 +879,14 @@ Object *qdev_get_machine(void)
return dev;
}
+char *qdev_get_human_name(DeviceState *dev)
+{
+ g_assert(dev != NULL);
+
+ return dev->id ?
+ g_strdup(dev->id) : object_get_canonical_path(OBJECT(dev));
+}
+
static MachineInitPhase machine_phase;
bool phase_check(MachineInitPhase phase)
diff --git a/hw/core/reset.c b/hw/core/reset.c
index 9c477f2bf5..d50da7e304 100644
--- a/hw/core/reset.c
+++ b/hw/core/reset.c
@@ -24,49 +24,164 @@
*/
#include "qemu/osdep.h"
-#include "qemu/queue.h"
#include "sysemu/reset.h"
+#include "hw/resettable.h"
+#include "hw/core/resetcontainer.h"
-/* reset/shutdown handler */
+/*
+ * Return a pointer to the singleton container that holds all the Resettable
+ * items that will be reset when qemu_devices_reset() is called.
+ */
+static ResettableContainer *get_root_reset_container(void)
+{
+ static ResettableContainer *root_reset_container;
+
+ if (!root_reset_container) {
+ root_reset_container =
+ RESETTABLE_CONTAINER(object_new(TYPE_RESETTABLE_CONTAINER));
+ }
+ return root_reset_container;
+}
+
+/*
+ * Reason why the currently in-progress qemu_devices_reset() was called.
+ * If we made at least SHUTDOWN_CAUSE_SNAPSHOT_LOAD have a corresponding
+ * ResetType we could perhaps avoid the need for this global.
+ */
+static ShutdownCause device_reset_reason;
-typedef struct QEMUResetEntry {
- QTAILQ_ENTRY(QEMUResetEntry) entry;
+/*
+ * This is an Object which implements Resettable simply to call the
+ * callback function in the hold phase.
+ */
+#define TYPE_LEGACY_RESET "legacy-reset"
+OBJECT_DECLARE_SIMPLE_TYPE(LegacyReset, LEGACY_RESET)
+
+struct LegacyReset {
+ Object parent;
+ ResettableState reset_state;
QEMUResetHandler *func;
void *opaque;
-} QEMUResetEntry;
+ bool skip_on_snapshot_load;
+};
-static QTAILQ_HEAD(, QEMUResetEntry) reset_handlers =
- QTAILQ_HEAD_INITIALIZER(reset_handlers);
+OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(LegacyReset, legacy_reset, LEGACY_RESET, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { })
+
+static ResettableState *legacy_reset_get_state(Object *obj)
+{
+ LegacyReset *lr = LEGACY_RESET(obj);
+ return &lr->reset_state;
+}
+
+static void legacy_reset_hold(Object *obj)
+{
+ LegacyReset *lr = LEGACY_RESET(obj);
+
+ if (device_reset_reason == SHUTDOWN_CAUSE_SNAPSHOT_LOAD &&
+ lr->skip_on_snapshot_load) {
+ return;
+ }
+ lr->func(lr->opaque);
+}
+
+static void legacy_reset_init(Object *obj)
+{
+}
+
+static void legacy_reset_finalize(Object *obj)
+{
+}
+
+static void legacy_reset_class_init(ObjectClass *klass, void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->get_state = legacy_reset_get_state;
+ rc->phases.hold = legacy_reset_hold;
+}
void qemu_register_reset(QEMUResetHandler *func, void *opaque)
{
- QEMUResetEntry *re = g_malloc0(sizeof(QEMUResetEntry));
+ Object *obj = object_new(TYPE_LEGACY_RESET);
+ LegacyReset *lr = LEGACY_RESET(obj);
- re->func = func;
- re->opaque = opaque;
- QTAILQ_INSERT_TAIL(&reset_handlers, re, entry);
+ lr->func = func;
+ lr->opaque = opaque;
+ qemu_register_resettable(obj);
}
-void qemu_unregister_reset(QEMUResetHandler *func, void *opaque)
+void qemu_register_reset_nosnapshotload(QEMUResetHandler *func, void *opaque)
{
- QEMUResetEntry *re;
-
- QTAILQ_FOREACH(re, &reset_handlers, entry) {
- if (re->func == func && re->opaque == opaque) {
- QTAILQ_REMOVE(&reset_handlers, re, entry);
- g_free(re);
- return;
- }
+ Object *obj = object_new(TYPE_LEGACY_RESET);
+ LegacyReset *lr = LEGACY_RESET(obj);
+
+ lr->func = func;
+ lr->opaque = opaque;
+ lr->skip_on_snapshot_load = true;
+ qemu_register_resettable(obj);
+}
+
+typedef struct FindLegacyInfo {
+ QEMUResetHandler *func;
+ void *opaque;
+ LegacyReset *lr;
+} FindLegacyInfo;
+
+static void find_legacy_reset_cb(Object *obj, void *opaque, ResetType type)
+{
+ LegacyReset *lr;
+ FindLegacyInfo *fli = opaque;
+
+ /* Not everything in the ResettableContainer will be a LegacyReset */
+ lr = LEGACY_RESET(object_dynamic_cast(obj, TYPE_LEGACY_RESET));
+ if (lr && lr->func == fli->func && lr->opaque == fli->opaque) {
+ fli->lr = lr;
}
}
-void qemu_devices_reset(void)
+static LegacyReset *find_legacy_reset(QEMUResetHandler *func, void *opaque)
+{
+ /*
+ * Find the LegacyReset with the specified func and opaque,
+ * by getting the ResettableContainer to call our callback for
+ * every item in it.
+ */
+ ResettableContainer *rootcon = get_root_reset_container();
+ ResettableClass *rc = RESETTABLE_GET_CLASS(rootcon);
+ FindLegacyInfo fli;
+
+ fli.func = func;
+ fli.opaque = opaque;
+ fli.lr = NULL;
+ rc->child_foreach(OBJECT(rootcon), find_legacy_reset_cb,
+ &fli, RESET_TYPE_COLD);
+ return fli.lr;
+}
+
+void qemu_unregister_reset(QEMUResetHandler *func, void *opaque)
{
- QEMUResetEntry *re, *nre;
+ Object *obj = OBJECT(find_legacy_reset(func, opaque));
- /* reset all devices */
- QTAILQ_FOREACH_SAFE(re, &reset_handlers, entry, nre) {
- re->func(re->opaque);
+ if (obj) {
+ qemu_unregister_resettable(obj);
+ object_unref(obj);
}
}
+void qemu_register_resettable(Object *obj)
+{
+ resettable_container_add(get_root_reset_container(), obj);
+}
+
+void qemu_unregister_resettable(Object *obj)
+{
+ resettable_container_remove(get_root_reset_container(), obj);
+}
+
+void qemu_devices_reset(ShutdownCause reason)
+{
+ device_reset_reason = reason;
+
+ /* Reset the simulation */
+ resettable_reset(OBJECT(get_root_reset_container()), RESET_TYPE_COLD);
+}
diff --git a/hw/core/resetcontainer.c b/hw/core/resetcontainer.c
new file mode 100644
index 0000000000..e4ece68e83
--- /dev/null
+++ b/hw/core/resetcontainer.c
@@ -0,0 +1,77 @@
+/*
+ * Reset container
+ *
+ * Copyright (c) 2024 Linaro, Ltd
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+/*
+ * The "reset container" is an object which implements the Resettable
+ * interface. It contains a list of arbitrary other objects which also
+ * implement Resettable. Resetting the reset container resets all the
+ * objects in it.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/resettable.h"
+#include "hw/core/resetcontainer.h"
+
+struct ResettableContainer {
+ Object parent;
+ ResettableState reset_state;
+ GPtrArray *children;
+};
+
+OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(ResettableContainer, resettable_container, RESETTABLE_CONTAINER, OBJECT, { TYPE_RESETTABLE_INTERFACE }, { })
+
+void resettable_container_add(ResettableContainer *rc, Object *obj)
+{
+ INTERFACE_CHECK(void, obj, TYPE_RESETTABLE_INTERFACE);
+ g_ptr_array_add(rc->children, obj);
+}
+
+void resettable_container_remove(ResettableContainer *rc, Object *obj)
+{
+ g_ptr_array_remove(rc->children, obj);
+}
+
+static ResettableState *resettable_container_get_state(Object *obj)
+{
+ ResettableContainer *rc = RESETTABLE_CONTAINER(obj);
+ return &rc->reset_state;
+}
+
+static void resettable_container_child_foreach(Object *obj,
+ ResettableChildCallback cb,
+ void *opaque, ResetType type)
+{
+ ResettableContainer *rc = RESETTABLE_CONTAINER(obj);
+ unsigned int len = rc->children->len;
+
+ for (unsigned int i = 0; i < len; i++) {
+ cb(g_ptr_array_index(rc->children, i), opaque, type);
+ /* Detect callbacks trying to unregister themselves */
+ assert(len == rc->children->len);
+ }
+}
+
+static void resettable_container_init(Object *obj)
+{
+ ResettableContainer *rc = RESETTABLE_CONTAINER(obj);
+
+ rc->children = g_ptr_array_new();
+}
+
+static void resettable_container_finalize(Object *obj)
+{
+}
+
+static void resettable_container_class_init(ObjectClass *klass, void *data)
+{
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ rc->get_state = resettable_container_get_state;
+ rc->child_foreach = resettable_container_child_foreach;
+}
diff --git a/hw/core/resettable.c b/hw/core/resettable.c
index 96a99ce39e..c3df75c6ba 100644
--- a/hw/core/resettable.c
+++ b/hw/core/resettable.c
@@ -201,12 +201,11 @@ static void resettable_phase_exit(Object *obj, void *opaque, ResetType type)
resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type);
assert(s->count > 0);
- if (s->count == 1) {
+ 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)) {
rc->phases.exit(obj);
}
- s->count = 0;
}
s->exit_phase_in_progress = false;
trace_resettable_phase_exit_end(obj, obj_typename, s->count);
diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c
new file mode 100644
index 0000000000..eebcd28f9a
--- /dev/null
+++ b/hw/core/sysbus-fdt.c
@@ -0,0 +1,572 @@
+/*
+ * ARM Platform Bus device tree generation helpers
+ *
+ * Copyright (c) 2014 Linaro Limited
+ *
+ * Authors:
+ * Alex Graf <agraf@suse.de>
+ * Eric Auger <eric.auger@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2 or later, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include <libfdt.h>
+#ifdef CONFIG_LINUX
+#include <linux/vfio.h>
+#endif
+#include "hw/core/sysbus-fdt.h"
+#include "qemu/error-report.h"
+#include "sysemu/device_tree.h"
+#include "sysemu/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/display/ramfb.h"
+#include "hw/arm/fdt.h"
+
+/*
+ * internal struct that contains the information to create dynamic
+ * sysbus device node
+ */
+typedef struct PlatformBusFDTData {
+ void *fdt; /* device tree handle */
+ int irq_start; /* index of the first IRQ usable by platform bus devices */
+ const char *pbus_node_name; /* name of the platform bus node */
+ PlatformBusDevice *pbus;
+} PlatformBusFDTData;
+
+/* struct that allows to match a device and create its FDT node */
+typedef struct BindingEntry {
+ const char *typename;
+ const char *compat;
+ int (*add_fn)(SysBusDevice *sbdev, void *opaque);
+ bool (*match_fn)(SysBusDevice *sbdev, const struct BindingEntry *combo);
+} BindingEntry;
+
+/* helpers */
+
+typedef struct HostProperty {
+ const char *name;
+ bool optional;
+} HostProperty;
+
+#ifdef CONFIG_LINUX
+
+/**
+ * copy_properties_from_host
+ *
+ * copies properties listed in an array from host device tree to
+ * guest device tree. If a non optional property is not found, the
+ * function asserts. An optional property is ignored if not found
+ * in the host device tree.
+ * @props: array of HostProperty to copy
+ * @nb_props: number of properties in the array
+ * @host_dt: host device tree blob
+ * @guest_dt: guest device tree blob
+ * @node_path: host dt node path where the property is supposed to be
+ found
+ * @nodename: guest node name the properties should be added to
+ */
+static void copy_properties_from_host(HostProperty *props, int nb_props,
+ void *host_fdt, void *guest_fdt,
+ char *node_path, char *nodename)
+{
+ int i, prop_len;
+ const void *r;
+ Error *err = NULL;
+
+ for (i = 0; i < nb_props; i++) {
+ r = qemu_fdt_getprop(host_fdt, node_path,
+ props[i].name,
+ &prop_len,
+ &err);
+ if (r) {
+ qemu_fdt_setprop(guest_fdt, nodename,
+ props[i].name, r, prop_len);
+ } else {
+ if (props[i].optional && prop_len == -FDT_ERR_NOTFOUND) {
+ /* optional property does not exist */
+ error_free(err);
+ } else {
+ error_report_err(err);
+ }
+ if (!props[i].optional) {
+ /* mandatory property not found: bail out */
+ exit(1);
+ }
+ err = NULL;
+ }
+ }
+}
+
+/* clock properties whose values are copied/pasted from host */
+static HostProperty clock_copied_properties[] = {
+ {"compatible", false},
+ {"#clock-cells", false},
+ {"clock-frequency", true},
+ {"clock-output-names", true},
+};
+
+/**
+ * fdt_build_clock_node
+ *
+ * Build a guest clock node, used as a dependency from a passthrough'ed
+ * device. Most information are retrieved from the host clock node.
+ * Also check the host clock is a fixed one.
+ *
+ * @host_fdt: host device tree blob from which info are retrieved
+ * @guest_fdt: guest device tree blob where the clock node is added
+ * @host_phandle: phandle of the clock in host device tree
+ * @guest_phandle: phandle to assign to the guest node
+ */
+static void fdt_build_clock_node(void *host_fdt, void *guest_fdt,
+ uint32_t host_phandle,
+ uint32_t guest_phandle)
+{
+ char *node_path = NULL;
+ char *nodename;
+ const void *r;
+ int ret, node_offset, prop_len, path_len = 16;
+
+ node_offset = fdt_node_offset_by_phandle(host_fdt, host_phandle);
+ if (node_offset <= 0) {
+ error_report("not able to locate clock handle %d in host device tree",
+ host_phandle);
+ exit(1);
+ }
+ node_path = g_malloc(path_len);
+ while ((ret = fdt_get_path(host_fdt, node_offset, node_path, path_len))
+ == -FDT_ERR_NOSPACE) {
+ path_len += 16;
+ node_path = g_realloc(node_path, path_len);
+ }
+ if (ret < 0) {
+ error_report("not able to retrieve node path for clock handle %d",
+ host_phandle);
+ exit(1);
+ }
+
+ r = qemu_fdt_getprop(host_fdt, node_path, "compatible", &prop_len,
+ &error_fatal);
+ if (strcmp(r, "fixed-clock")) {
+ error_report("clock handle %d is not a fixed clock", host_phandle);
+ exit(1);
+ }
+
+ nodename = strrchr(node_path, '/');
+ qemu_fdt_add_subnode(guest_fdt, nodename);
+
+ copy_properties_from_host(clock_copied_properties,
+ ARRAY_SIZE(clock_copied_properties),
+ host_fdt, guest_fdt,
+ node_path, nodename);
+
+ qemu_fdt_setprop_cell(guest_fdt, nodename, "phandle", guest_phandle);
+
+ g_free(node_path);
+}
+
+/**
+ * sysfs_to_dt_name: convert the name found in sysfs into the node name
+ * for instance e0900000.xgmac is converted into xgmac@e0900000
+ * @sysfs_name: directory name in sysfs
+ *
+ * returns the device tree name upon success or NULL in case the sysfs name
+ * does not match the expected format
+ */
+static char *sysfs_to_dt_name(const char *sysfs_name)
+{
+ gchar **substrings = g_strsplit(sysfs_name, ".", 2);
+ char *dt_name = NULL;
+
+ if (!substrings || !substrings[0] || !substrings[1]) {
+ goto out;
+ }
+ dt_name = g_strdup_printf("%s@%s", substrings[1], substrings[0]);
+out:
+ g_strfreev(substrings);
+ return dt_name;
+}
+
+/* Device Specific Code */
+
+/**
+ * add_calxeda_midway_xgmac_fdt_node
+ *
+ * Generates a simple node with following properties:
+ * compatible string, regs, interrupts, dma-coherent
+ */
+static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ PlatformBusFDTData *data = opaque;
+ PlatformBusDevice *pbus = data->pbus;
+ void *fdt = data->fdt;
+ const char *parent_node = data->pbus_node_name;
+ int compat_str_len, i;
+ char *nodename;
+ uint32_t *irq_attr, *reg_attr;
+ uint64_t mmio_base, irq_number;
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
+ VFIODevice *vbasedev = &vdev->vbasedev;
+
+ mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
+ nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
+ vbasedev->name, mmio_base);
+ qemu_fdt_add_subnode(fdt, nodename);
+
+ compat_str_len = strlen(vdev->compat) + 1;
+ qemu_fdt_setprop(fdt, nodename, "compatible",
+ vdev->compat, compat_str_len);
+
+ qemu_fdt_setprop(fdt, nodename, "dma-coherent", "", 0);
+
+ reg_attr = g_new(uint32_t, vbasedev->num_regions * 2);
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
+ reg_attr[2 * i] = cpu_to_be32(mmio_base);
+ reg_attr[2 * i + 1] = cpu_to_be32(
+ memory_region_size(vdev->regions[i]->mem));
+ }
+ qemu_fdt_setprop(fdt, nodename, "reg", reg_attr,
+ vbasedev->num_regions * 2 * sizeof(uint32_t));
+
+ irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
+ for (i = 0; i < vbasedev->num_irqs; i++) {
+ irq_number = platform_bus_get_irqn(pbus, sbdev , i)
+ + data->irq_start;
+ irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
+ irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
+ irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ }
+ qemu_fdt_setprop(fdt, nodename, "interrupts",
+ irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
+ g_free(irq_attr);
+ g_free(reg_attr);
+ g_free(nodename);
+ return 0;
+}
+
+/* AMD xgbe properties whose values are copied/pasted from host */
+static HostProperty amd_xgbe_copied_properties[] = {
+ {"compatible", false},
+ {"dma-coherent", true},
+ {"amd,per-channel-interrupt", true},
+ {"phy-mode", false},
+ {"mac-address", true},
+ {"amd,speed-set", false},
+ {"amd,serdes-blwc", true},
+ {"amd,serdes-cdr-rate", true},
+ {"amd,serdes-pq-skew", true},
+ {"amd,serdes-tx-amp", true},
+ {"amd,serdes-dfe-tap-config", true},
+ {"amd,serdes-dfe-tap-enable", true},
+ {"clock-names", false},
+};
+
+/**
+ * add_amd_xgbe_fdt_node
+ *
+ * Generates the combined xgbe/phy node following kernel >=4.2
+ * binding documentation:
+ * Documentation/devicetree/bindings/net/amd-xgbe.txt:
+ * Also 2 clock nodes are created (dma and ptp)
+ *
+ * Asserts in case of error
+ */
+static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ PlatformBusFDTData *data = opaque;
+ PlatformBusDevice *pbus = data->pbus;
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
+ VFIODevice *vbasedev = &vdev->vbasedev;
+ VFIOINTp *intp;
+ const char *parent_node = data->pbus_node_name;
+ char **node_path, *nodename, *dt_name;
+ void *guest_fdt = data->fdt, *host_fdt;
+ const void *r;
+ int i, prop_len;
+ uint32_t *irq_attr, *reg_attr;
+ const uint32_t *host_clock_phandles;
+ uint64_t mmio_base, irq_number;
+ uint32_t guest_clock_phandles[2];
+
+ host_fdt = load_device_tree_from_sysfs();
+
+ dt_name = sysfs_to_dt_name(vbasedev->name);
+ if (!dt_name) {
+ error_report("%s incorrect sysfs device name %s",
+ __func__, vbasedev->name);
+ exit(1);
+ }
+ node_path = qemu_fdt_node_path(host_fdt, dt_name, vdev->compat,
+ &error_fatal);
+ if (!node_path || !node_path[0]) {
+ error_report("%s unable to retrieve node path for %s/%s",
+ __func__, dt_name, vdev->compat);
+ exit(1);
+ }
+
+ if (node_path[1]) {
+ error_report("%s more than one node matching %s/%s!",
+ __func__, dt_name, vdev->compat);
+ exit(1);
+ }
+
+ g_free(dt_name);
+
+ if (vbasedev->num_regions != 5) {
+ error_report("%s Does the host dt node combine XGBE/PHY?", __func__);
+ exit(1);
+ }
+
+ /* generate nodes for DMA_CLK and PTP_CLK */
+ r = qemu_fdt_getprop(host_fdt, node_path[0], "clocks",
+ &prop_len, &error_fatal);
+ if (prop_len != 8) {
+ error_report("%s clocks property should contain 2 handles", __func__);
+ exit(1);
+ }
+ host_clock_phandles = r;
+ guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt);
+ guest_clock_phandles[1] = qemu_fdt_alloc_phandle(guest_fdt);
+
+ /**
+ * clock handles fetched from host dt are in be32 layout whereas
+ * rest of the code uses cpu layout. Also guest clock handles are
+ * in cpu layout.
+ */
+ fdt_build_clock_node(host_fdt, guest_fdt,
+ be32_to_cpu(host_clock_phandles[0]),
+ guest_clock_phandles[0]);
+
+ fdt_build_clock_node(host_fdt, guest_fdt,
+ be32_to_cpu(host_clock_phandles[1]),
+ guest_clock_phandles[1]);
+
+ /* combined XGBE/PHY node */
+ mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
+ nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node,
+ vbasedev->name, mmio_base);
+ qemu_fdt_add_subnode(guest_fdt, nodename);
+
+ copy_properties_from_host(amd_xgbe_copied_properties,
+ ARRAY_SIZE(amd_xgbe_copied_properties),
+ host_fdt, guest_fdt,
+ node_path[0], nodename);
+
+ qemu_fdt_setprop_cells(guest_fdt, nodename, "clocks",
+ guest_clock_phandles[0],
+ guest_clock_phandles[1]);
+
+ reg_attr = g_new(uint32_t, vbasedev->num_regions * 2);
+ for (i = 0; i < vbasedev->num_regions; i++) {
+ mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i);
+ reg_attr[2 * i] = cpu_to_be32(mmio_base);
+ reg_attr[2 * i + 1] = cpu_to_be32(
+ memory_region_size(vdev->regions[i]->mem));
+ }
+ qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr,
+ vbasedev->num_regions * 2 * sizeof(uint32_t));
+
+ irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3);
+ for (i = 0; i < vbasedev->num_irqs; i++) {
+ irq_number = platform_bus_get_irqn(pbus, sbdev , i)
+ + data->irq_start;
+ irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI);
+ irq_attr[3 * i + 1] = cpu_to_be32(irq_number);
+ /*
+ * General device interrupt and PCS auto-negotiation interrupts are
+ * level-sensitive while the 4 per-channel interrupts are edge
+ * sensitive
+ */
+ QLIST_FOREACH(intp, &vdev->intp_list, next) {
+ if (intp->pin == i) {
+ break;
+ }
+ }
+ if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) {
+ irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI);
+ } else {
+ irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI);
+ }
+ }
+ qemu_fdt_setprop(guest_fdt, nodename, "interrupts",
+ irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t));
+
+ g_free(host_fdt);
+ g_strfreev(node_path);
+ g_free(irq_attr);
+ g_free(reg_attr);
+ g_free(nodename);
+ return 0;
+}
+
+/* DT compatible matching */
+static bool vfio_platform_match(SysBusDevice *sbdev,
+ const BindingEntry *entry)
+{
+ VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev);
+ const char *compat;
+ unsigned int n;
+
+ for (n = vdev->num_compat, compat = vdev->compat; n > 0;
+ n--, compat += strlen(compat) + 1) {
+ if (!strcmp(entry->compat, compat)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+#define VFIO_PLATFORM_BINDING(compat, add_fn) \
+ {TYPE_VFIO_PLATFORM, (compat), (add_fn), vfio_platform_match}
+
+#endif /* CONFIG_LINUX */
+
+#ifdef CONFIG_TPM
+/*
+ * add_tpm_tis_fdt_node: Create a DT node for TPM TIS
+ *
+ * See kernel documentation:
+ * Documentation/devicetree/bindings/security/tpm/tpm_tis_mmio.txt
+ * Optional interrupt for command completion is not exposed
+ */
+static int add_tpm_tis_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ PlatformBusFDTData *data = opaque;
+ PlatformBusDevice *pbus = data->pbus;
+ void *fdt = data->fdt;
+ const char *parent_node = data->pbus_node_name;
+ char *nodename;
+ uint32_t reg_attr[2];
+ uint64_t mmio_base;
+
+ mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0);
+ nodename = g_strdup_printf("%s/tpm_tis@%" PRIx64, parent_node, mmio_base);
+ qemu_fdt_add_subnode(fdt, nodename);
+
+ qemu_fdt_setprop_string(fdt, nodename, "compatible", "tcg,tpm-tis-mmio");
+
+ reg_attr[0] = cpu_to_be32(mmio_base);
+ reg_attr[1] = cpu_to_be32(0x5000);
+ qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, 2 * sizeof(uint32_t));
+
+ g_free(nodename);
+ return 0;
+}
+#endif
+
+static int no_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ return 0;
+}
+
+/* Device type based matching */
+static bool type_match(SysBusDevice *sbdev, const BindingEntry *entry)
+{
+ return !strcmp(object_get_typename(OBJECT(sbdev)), entry->typename);
+}
+
+#define TYPE_BINDING(type, add_fn) {(type), NULL, (add_fn), NULL}
+
+/* list of supported dynamic sysbus bindings */
+static const BindingEntry bindings[] = {
+#ifdef CONFIG_LINUX
+ TYPE_BINDING(TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node),
+ TYPE_BINDING(TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node),
+ VFIO_PLATFORM_BINDING("amd,xgbe-seattle-v1a", add_amd_xgbe_fdt_node),
+#endif
+#ifdef CONFIG_TPM
+ TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node),
+#endif
+ TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node),
+ TYPE_BINDING("", NULL), /* last element */
+};
+
+/* Generic Code */
+
+/**
+ * add_fdt_node - add the device tree node of a dynamic sysbus device
+ *
+ * @sbdev: handle to the sysbus device
+ * @opaque: handle to the PlatformBusFDTData
+ *
+ * Checks the sysbus type belongs to the list of device types that
+ * are dynamically instantiable and if so call the node creation
+ * function.
+ */
+static void add_fdt_node(SysBusDevice *sbdev, void *opaque)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(bindings); i++) {
+ const BindingEntry *iter = &bindings[i];
+
+ if (type_match(sbdev, iter)) {
+ if (!iter->match_fn || iter->match_fn(sbdev, iter)) {
+ ret = iter->add_fn(sbdev, opaque);
+ assert(!ret);
+ return;
+ }
+ }
+ }
+ error_report("Device %s can not be dynamically instantiated",
+ qdev_fw_name(DEVICE(sbdev)));
+ exit(1);
+}
+
+void platform_bus_add_all_fdt_nodes(void *fdt, const char *intc, hwaddr addr,
+ hwaddr bus_size, int irq_start)
+{
+ const char platcomp[] = "qemu,platform\0simple-bus";
+ PlatformBusDevice *pbus;
+ DeviceState *dev;
+ gchar *node;
+
+ assert(fdt);
+
+ node = g_strdup_printf("/platform-bus@%"PRIx64, addr);
+
+ /* Create a /platform node that we can put all devices into */
+ qemu_fdt_add_subnode(fdt, node);
+ qemu_fdt_setprop(fdt, node, "compatible", platcomp, sizeof(platcomp));
+
+ /* Our platform bus region is less than 32bits, so 1 cell is enough for
+ * address and size
+ */
+ qemu_fdt_setprop_cells(fdt, node, "#size-cells", 1);
+ qemu_fdt_setprop_cells(fdt, node, "#address-cells", 1);
+ qemu_fdt_setprop_cells(fdt, node, "ranges", 0, addr >> 32, addr, bus_size);
+
+ qemu_fdt_setprop_phandle(fdt, node, "interrupt-parent", intc);
+
+ dev = qdev_find_recursive(sysbus_get_default(), TYPE_PLATFORM_BUS_DEVICE);
+ pbus = PLATFORM_BUS_DEVICE(dev);
+
+ PlatformBusFDTData data = {
+ .fdt = fdt,
+ .irq_start = irq_start,
+ .pbus_node_name = node,
+ .pbus = pbus,
+ };
+
+ /* Loop through all dynamic sysbus devices and create their node */
+ foreach_dynamic_sysbus_device(add_fdt_node, &data);
+
+ g_free(node);
+}
diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c
index aaae8e23cc..ad34fb7344 100644
--- a/hw/core/sysbus.c
+++ b/hw/core/sysbus.c
@@ -269,7 +269,7 @@ static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent)
for (i = 0; i < s->num_mmio; i++) {
size = memory_region_size(s->mmio[i].memory);
- monitor_printf(mon, "%*smmio " TARGET_FMT_plx "/" TARGET_FMT_plx "\n",
+ monitor_printf(mon, "%*smmio " HWADDR_FMT_plx "/" HWADDR_FMT_plx "\n",
indent, "", s->mmio[i].addr, size);
}
}
@@ -289,7 +289,7 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev)
}
}
if (s->num_mmio) {
- return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev),
+ return g_strdup_printf("%s@" HWADDR_FMT_plx, qdev_fw_name(dev),
s->mmio[0].addr);
}
if (s->num_pio) {
@@ -298,17 +298,6 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev)
return g_strdup(qdev_fw_name(dev));
}
-void sysbus_add_io(SysBusDevice *dev, hwaddr addr,
- MemoryRegion *mem)
-{
- memory_region_add_subregion(get_system_io(), addr, mem);
-}
-
-MemoryRegion *sysbus_address_space(SysBusDevice *dev)
-{
- return get_system_memory();
-}
-
static void sysbus_device_class_init(ObjectClass *klass, void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
@@ -340,11 +329,13 @@ static BusState *main_system_bus;
static void main_system_bus_create(void)
{
- /* assign main_system_bus before qbus_create_inplace()
- * in order to make "if (bus != sysbus_get_default())" work */
+ /*
+ * 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_create_inplace(main_system_bus, system_bus_info.instance_size,
- TYPE_SYSTEM_BUS, NULL, "main-system-bus");
+ qbus_init(main_system_bus, system_bus_info.instance_size,
+ TYPE_SYSTEM_BUS, NULL, "main-system-bus");
OBJECT(main_system_bus)->free = g_free;
}
diff --git a/hw/core/trace-events b/hw/core/trace-events
index 9b3ecce3b2..2cf085ac66 100644
--- a/hw/core/trace-events
+++ b/hw/core/trace-events
@@ -2,12 +2,6 @@
loader_write_rom(const char *name, uint64_t gpa, uint64_t size, bool isrom) "%s: @0x%"PRIx64" size=0x%"PRIx64" ROM=%d"
# qdev.c
-qdev_reset(void *obj, const char *objtype) "obj=%p(%s)"
-qdev_reset_all(void *obj, const char *objtype) "obj=%p(%s)"
-qdev_reset_tree(void *obj, const char *objtype) "obj=%p(%s)"
-qbus_reset(void *obj, const char *objtype) "obj=%p(%s)"
-qbus_reset_all(void *obj, const char *objtype) "obj=%p(%s)"
-qbus_reset_tree(void *obj, const char *objtype) "obj=%p(%s)"
qdev_update_parent_bus(void *obj, const char *objtype, void *oldp, const char *oldptype, void *newp, const char *newptype) "obj=%p(%s) old_parent=%p(%s) new_parent=%p(%s)"
# resettable.c
@@ -35,3 +29,6 @@ clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', %"PRIu64"Hz->%"PRI
clock_propagate(const char *clk) "'%s'"
clock_update(const char *clk, const char *src, uint64_t hz, int cb) "'%s', src='%s', val=%"PRIu64"Hz cb=%d"
clock_set_mul_div(const char *clk, uint32_t oldmul, uint32_t mul, uint32_t olddiv, uint32_t div) "'%s', mul: %u -> %u, div: %u -> %u"
+
+# cpu-common.c
+cpu_reset(int cpu_index) "%d"
diff --git a/hw/core/uboot_image.h b/hw/core/uboot_image.h
index 608022de6e..18ac293359 100644
--- a/hw/core/uboot_image.h
+++ b/hw/core/uboot_image.h
@@ -1,23 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
/*
+ * (C) Copyright 2008 Semihalf
+ *
* (C) Copyright 2000-2005
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * 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/>.
- *
********************************************************************
* NOTE: This header file defines an interface to U-Boot. Including
* this (unmodified) header file in another file is considered normal
@@ -31,50 +17,83 @@
/*
* Operating System Codes
+ *
+ * The following are exposed to uImage header.
+ * New IDs *MUST* be appended at the end of the list and *NEVER*
+ * inserted for backward compatibility.
*/
-#define IH_OS_INVALID 0 /* Invalid OS */
-#define IH_OS_OPENBSD 1 /* OpenBSD */
-#define IH_OS_NETBSD 2 /* NetBSD */
-#define IH_OS_FREEBSD 3 /* FreeBSD */
-#define IH_OS_4_4BSD 4 /* 4.4BSD */
-#define IH_OS_LINUX 5 /* Linux */
-#define IH_OS_SVR4 6 /* SVR4 */
-#define IH_OS_ESIX 7 /* Esix */
-#define IH_OS_SOLARIS 8 /* Solaris */
-#define IH_OS_IRIX 9 /* Irix */
-#define IH_OS_SCO 10 /* SCO */
-#define IH_OS_DELL 11 /* Dell */
-#define IH_OS_NCR 12 /* NCR */
-#define IH_OS_LYNXOS 13 /* LynxOS */
-#define IH_OS_VXWORKS 14 /* VxWorks */
-#define IH_OS_PSOS 15 /* pSOS */
-#define IH_OS_QNX 16 /* QNX */
-#define IH_OS_U_BOOT 17 /* Firmware */
-#define IH_OS_RTEMS 18 /* RTEMS */
-#define IH_OS_ARTOS 19 /* ARTOS */
-#define IH_OS_UNITY 20 /* Unity OS */
+enum {
+ IH_OS_INVALID = 0, /* Invalid OS */
+ IH_OS_OPENBSD, /* OpenBSD */
+ IH_OS_NETBSD, /* NetBSD */
+ IH_OS_FREEBSD, /* FreeBSD */
+ IH_OS_4_4BSD, /* 4.4BSD */
+ IH_OS_LINUX, /* Linux */
+ IH_OS_SVR4, /* SVR4 */
+ IH_OS_ESIX, /* Esix */
+ IH_OS_SOLARIS, /* Solaris */
+ IH_OS_IRIX, /* Irix */
+ IH_OS_SCO, /* SCO */
+ IH_OS_DELL, /* Dell */
+ IH_OS_NCR, /* NCR */
+ IH_OS_LYNXOS, /* LynxOS */
+ IH_OS_VXWORKS, /* VxWorks */
+ IH_OS_PSOS, /* pSOS */
+ IH_OS_QNX, /* QNX */
+ IH_OS_U_BOOT, /* Firmware */
+ IH_OS_RTEMS, /* RTEMS */
+ IH_OS_ARTOS, /* ARTOS */
+ IH_OS_UNITY, /* Unity OS */
+ IH_OS_INTEGRITY, /* INTEGRITY */
+ IH_OS_OSE, /* OSE */
+ IH_OS_PLAN9, /* Plan 9 */
+ IH_OS_OPENRTOS, /* OpenRTOS */
+ IH_OS_ARM_TRUSTED_FIRMWARE, /* ARM Trusted Firmware */
+ IH_OS_TEE, /* Trusted Execution Environment */
+ IH_OS_OPENSBI, /* RISC-V OpenSBI */
+ IH_OS_EFI, /* EFI Firmware (e.g. GRUB2) */
+
+ IH_OS_COUNT,
+};
/*
* CPU Architecture Codes (supported by Linux)
+ *
+ * The following are exposed to uImage header.
+ * New IDs *MUST* be appended at the end of the list and *NEVER*
+ * inserted for backward compatibility.
*/
-#define IH_CPU_INVALID 0 /* Invalid CPU */
-#define IH_CPU_ALPHA 1 /* Alpha */
-#define IH_CPU_ARM 2 /* ARM */
-#define IH_CPU_I386 3 /* Intel x86 */
-#define IH_CPU_IA64 4 /* IA64 */
-#define IH_CPU_MIPS 5 /* MIPS */
-#define IH_CPU_MIPS64 6 /* MIPS 64 Bit */
-#define IH_CPU_PPC 7 /* PowerPC */
-#define IH_CPU_S390 8 /* IBM S390 */
-#define IH_CPU_SH 9 /* SuperH */
-#define IH_CPU_SPARC 10 /* Sparc */
-#define IH_CPU_SPARC64 11 /* Sparc 64 Bit */
-#define IH_CPU_M68K 12 /* M68K */
-#define IH_CPU_NIOS 13 /* Nios-32 */
-#define IH_CPU_MICROBLAZE 14 /* MicroBlaze */
-#define IH_CPU_NIOS2 15 /* Nios-II */
-#define IH_CPU_BLACKFIN 16 /* Blackfin */
-#define IH_CPU_AVR32 17 /* AVR32 */
+enum {
+ IH_ARCH_INVALID = 0, /* Invalid CPU */
+ IH_ARCH_ALPHA, /* Alpha */
+ IH_ARCH_ARM, /* ARM */
+ IH_ARCH_I386, /* Intel x86 */
+ IH_ARCH_IA64, /* IA64 */
+ IH_ARCH_MIPS, /* MIPS */
+ IH_ARCH_MIPS64, /* MIPS 64 Bit */
+ IH_ARCH_PPC, /* PowerPC */
+ IH_ARCH_S390, /* IBM S390 */
+ IH_ARCH_SH, /* SuperH */
+ IH_ARCH_SPARC, /* Sparc */
+ IH_ARCH_SPARC64, /* Sparc 64 Bit */
+ IH_ARCH_M68K, /* M68K */
+ IH_ARCH_NIOS, /* Nios-32 */
+ IH_ARCH_MICROBLAZE, /* MicroBlaze */
+ IH_ARCH_NIOS2, /* Nios-II */
+ IH_ARCH_BLACKFIN, /* Blackfin */
+ IH_ARCH_AVR32, /* AVR32 */
+ IH_ARCH_ST200, /* STMicroelectronics ST200 */
+ IH_ARCH_SANDBOX, /* Sandbox architecture (test only) */
+ IH_ARCH_NDS32, /* ANDES Technology - NDS32 */
+ IH_ARCH_OPENRISC, /* OpenRISC 1000 */
+ IH_ARCH_ARM64, /* ARM64 */
+ IH_ARCH_ARC, /* Synopsys DesignWare ARC */
+ IH_ARCH_X86_64, /* AMD x86_64, Intel and Via */
+ IH_ARCH_XTENSA, /* Xtensa */
+ IH_ARCH_RISCV, /* RISC-V */
+
+ IH_ARCH_COUNT,
+};
/*
* Image Types
@@ -113,33 +132,85 @@
* U-Boot's command interpreter; this feature is especially
* useful when you configure U-Boot to use a real shell (hush)
* as command interpreter (=> Shell Scripts).
+ *
+ * The following are exposed to uImage header.
+ * New IDs *MUST* be appended at the end of the list and *NEVER*
+ * inserted for backward compatibility.
*/
-#define IH_TYPE_INVALID 0 /* Invalid Image */
-#define IH_TYPE_STANDALONE 1 /* Standalone Program */
-#define IH_TYPE_KERNEL 2 /* OS Kernel Image */
-#define IH_TYPE_RAMDISK 3 /* RAMDisk Image */
-#define IH_TYPE_MULTI 4 /* Multi-File Image */
-#define IH_TYPE_FIRMWARE 5 /* Firmware Image */
-#define IH_TYPE_SCRIPT 6 /* Script file */
-#define IH_TYPE_FILESYSTEM 7 /* Filesystem Image (any type) */
-#define IH_TYPE_FLATDT 8 /* Binary Flat Device Tree Blob */
-#define IH_TYPE_KERNEL_NOLOAD 14 /* OS Kernel Image (noload) */
+enum {
+ IH_TYPE_INVALID = 0, /* Invalid Image */
+ IH_TYPE_STANDALONE, /* Standalone Program */
+ IH_TYPE_KERNEL, /* OS Kernel Image */
+ IH_TYPE_RAMDISK, /* RAMDisk Image */
+ IH_TYPE_MULTI, /* Multi-File Image */
+ IH_TYPE_FIRMWARE, /* Firmware Image */
+ IH_TYPE_SCRIPT, /* Script file */
+ IH_TYPE_FILESYSTEM, /* Filesystem Image (any type) */
+ IH_TYPE_FLATDT, /* Binary Flat Device Tree Blob */
+ IH_TYPE_KWBIMAGE, /* Kirkwood Boot Image */
+ IH_TYPE_IMXIMAGE, /* Freescale IMXBoot Image */
+ IH_TYPE_UBLIMAGE, /* Davinci UBL Image */
+ IH_TYPE_OMAPIMAGE, /* TI OMAP Config Header Image */
+ IH_TYPE_AISIMAGE, /* TI Davinci AIS Image */
+ /* OS Kernel Image, can run from any load address */
+ IH_TYPE_KERNEL_NOLOAD,
+ IH_TYPE_PBLIMAGE, /* Freescale PBL Boot Image */
+ IH_TYPE_MXSIMAGE, /* Freescale MXSBoot Image */
+ IH_TYPE_GPIMAGE, /* TI Keystone GPHeader Image */
+ IH_TYPE_ATMELIMAGE, /* ATMEL ROM bootable Image */
+ IH_TYPE_SOCFPGAIMAGE, /* Altera SOCFPGA CV/AV Preloader */
+ IH_TYPE_X86_SETUP, /* x86 setup.bin Image */
+ IH_TYPE_LPC32XXIMAGE, /* x86 setup.bin Image */
+ IH_TYPE_LOADABLE, /* A list of typeless images */
+ IH_TYPE_RKIMAGE, /* Rockchip Boot Image */
+ IH_TYPE_RKSD, /* Rockchip SD card */
+ IH_TYPE_RKSPI, /* Rockchip SPI image */
+ IH_TYPE_ZYNQIMAGE, /* Xilinx Zynq Boot Image */
+ IH_TYPE_ZYNQMPIMAGE, /* Xilinx ZynqMP Boot Image */
+ IH_TYPE_ZYNQMPBIF, /* Xilinx ZynqMP Boot Image (bif) */
+ IH_TYPE_FPGA, /* FPGA Image */
+ IH_TYPE_VYBRIDIMAGE, /* VYBRID .vyb Image */
+ IH_TYPE_TEE, /* Trusted Execution Environment OS Image */
+ IH_TYPE_FIRMWARE_IVT, /* Firmware Image with HABv4 IVT */
+ IH_TYPE_PMMC, /* TI Power Management Micro-Controller Firmware */
+ IH_TYPE_STM32IMAGE, /* STMicroelectronics STM32 Image */
+ IH_TYPE_SOCFPGAIMAGE_V1, /* Altera SOCFPGA A10 Preloader */
+ IH_TYPE_MTKIMAGE, /* MediaTek BootROM loadable Image */
+ IH_TYPE_IMX8MIMAGE, /* Freescale IMX8MBoot Image */
+ IH_TYPE_IMX8IMAGE, /* Freescale IMX8Boot Image */
+ IH_TYPE_COPRO, /* Coprocessor Image for remoteproc*/
+ IH_TYPE_SUNXI_EGON, /* Allwinner eGON Boot Image */
+
+ IH_TYPE_COUNT, /* Number of image types */
+};
/*
* Compression Types
+ *
+ * The following are exposed to uImage header.
+ * New IDs *MUST* be appended at the end of the list and *NEVER*
+ * inserted for backward compatibility.
*/
-#define IH_COMP_NONE 0 /* No Compression Used */
-#define IH_COMP_GZIP 1 /* gzip Compression Used */
-#define IH_COMP_BZIP2 2 /* bzip2 Compression Used */
+enum {
+ IH_COMP_NONE = 0, /* No Compression Used */
+ IH_COMP_GZIP, /* gzip Compression Used */
+ IH_COMP_BZIP2, /* bzip2 Compression Used */
+ IH_COMP_LZMA, /* lzma Compression Used */
+ IH_COMP_LZO, /* lzo Compression Used */
+ IH_COMP_LZ4, /* lz4 Compression Used */
+ IH_COMP_ZSTD, /* zstd Compression Used */
+
+ IH_COMP_COUNT,
+};
#define IH_MAGIC 0x27051956 /* Image Magic Number */
#define IH_NMLEN 32 /* Image Name Length */
/*
- * all data in network byte order (aka natural aka bigendian)
+ * Legacy format image header,
+ * all data in network byte order (aka natural aka bigendian).
*/
-
typedef struct uboot_image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
diff --git a/hw/core/vm-change-state-handler.c b/hw/core/vm-change-state-handler.c
index 1f3630986d..8e2639224e 100644
--- a/hw/core/vm-change-state-handler.c
+++ b/hw/core/vm-change-state-handler.c
@@ -56,7 +56,19 @@ VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev,
VMChangeStateHandler *cb,
void *opaque)
{
+ return qdev_add_vm_change_state_handler_full(dev, cb, NULL, opaque);
+}
+
+/*
+ * Exactly like qdev_add_vm_change_state_handler() but passes a prepare_cb
+ * argument too.
+ */
+VMChangeStateEntry *qdev_add_vm_change_state_handler_full(
+ DeviceState *dev, VMChangeStateHandler *cb,
+ VMChangeStateHandler *prepare_cb, void *opaque)
+{
int depth = qdev_get_dev_tree_depth(dev);
- return qemu_add_vm_change_state_handler_prio(cb, opaque, depth);
+ return qemu_add_vm_change_state_handler_prio_full(cb, prepare_cb, opaque,
+ depth);
}