diff options
51 files changed, 1071 insertions, 497 deletions
@@ -751,7 +751,7 @@ void bdrv_drain_all(void) * * This function should be called when a tracked request is completing. */ -static void tracked_request_end(BdrvTrackedRequest *req) +static void coroutine_fn tracked_request_end(BdrvTrackedRequest *req) { if (req->serialising) { qatomic_dec(&req->bs->serialising_in_flight); @@ -2038,7 +2038,6 @@ echo "CCAS=$ccas" >> $config_host_mak echo "CPP=$cpp" >> $config_host_mak echo "OBJCOPY=$objcopy" >> $config_host_mak echo "LD=$ld" >> $config_host_mak -echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak echo "QEMU_CXXFLAGS=$QEMU_CXXFLAGS" >> $config_host_mak echo "QEMU_OBJCFLAGS=$QEMU_OBJCFLAGS" >> $config_host_mak diff --git a/hw/arm/nseries.c b/hw/arm/nseries.c index 9c1cafa..692c94c 100644 --- a/hw/arm/nseries.c +++ b/hw/arm/nseries.c @@ -1365,7 +1365,7 @@ static void n8x0_init(MachineState *machine, } if (option_rom[0].name && - (machine->boot_order[0] == 'n' || !machine->kernel_filename)) { + (machine->boot_config.order[0] == 'n' || !machine->kernel_filename)) { uint8_t *nolo_tags = g_new(uint8_t, 0x10000); /* No, wait, better start at the ROM. */ s->mpu->cpu->env.regs[15] = OMAP2_Q2_BASE + 0x400000; diff --git a/hw/core/machine.c b/hw/core/machine.c index 700c1e7..3264c1e 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -21,12 +21,14 @@ #include "qapi/qapi-visit-common.h" #include "qapi/qapi-visit-machine.h" #include "qapi/visitor.h" +#include "qom/object_interfaces.h" #include "hw/sysbus.h" #include "sysemu/cpus.h" #include "sysemu/sysemu.h" #include "sysemu/reset.h" #include "sysemu/runstate.h" #include "sysemu/numa.h" +#include "sysemu/xen.h" #include "qemu/error-report.h" #include "sysemu/qtest.h" #include "hw/pci/pci.h" @@ -36,6 +38,7 @@ #include "exec/confidential-guest-support.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-pci.h" +#include "qom/object_interfaces.h" GlobalProperty hw_compat_7_0[] = {}; const size_t hw_compat_7_0_len = G_N_ELEMENTS(hw_compat_7_0); @@ -523,6 +526,78 @@ 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) +{ + MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); + MemorySizeConfiguration *mem; + + ERRP_GUARD(); + + 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); @@ -581,21 +656,6 @@ bool device_type_is_dynamic_sysbus(MachineClass *mc, const char *type) return allowed; } -static char *machine_get_memdev(Object *obj, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - return g_strdup(ms->ram_memdev_id); -} - -static void machine_set_memdev(Object *obj, const char *value, Error **errp) -{ - MachineState *ms = MACHINE(obj); - - g_free(ms->ram_memdev_id); - ms->ram_memdev_id = g_strdup(value); -} - HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine) { int i; @@ -784,6 +844,65 @@ static void machine_set_smp(Object *obj, Visitor *v, const char *name, 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->has_order) { + ms->boot_config.has_order = true; + ms->boot_config.order = g_strdup(machine_class->default_boot_order); + } +} + +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->has_order) { + validate_bootdevices(config->order, errp); + if (*errp) { + goto out_free; + } + } + if (config->has_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_BootConfiguration(config); +} + static void machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -822,6 +941,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); @@ -883,11 +1008,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) @@ -918,6 +1050,8 @@ 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); @@ -955,12 +1089,15 @@ static void machine_initfn(Object *obj) ms->smp.clusters = 1; ms->smp.cores = 1; ms->smp.threads = 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); @@ -1122,7 +1259,40 @@ 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) +{ + 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; +} + + +void machine_run_board_init(MachineState *machine, const char *mem_path, Error **errp) { MachineClass *machine_class = MACHINE_GET_CLASS(machine); ObjectClass *oc = object_class_by_name(machine->cpu_type); @@ -1133,11 +1303,26 @@ void machine_run_board_init(MachineState *machine) 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 (!create_default_memdev(current_machine, mem_path, errp)) { + return; + } } if (machine->numa_state) { @@ -1147,6 +1332,10 @@ void machine_run_board_init(MachineState *machine) } } + if (!machine->ram && machine->memdev) { + machine->ram = machine_consume_memdev(machine, machine->memdev); + } + /* 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. */ @@ -1229,9 +1418,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.has_once) { + qemu_boot_set(current_machine->boot_config.once, &error_fatal); + qemu_register_reset(restore_boot_order, g_strdup(current_machine->boot_config.order)); } /* diff --git a/hw/core/numa.c b/hw/core/numa.c index 1aa05dc..26d8e5f 100644 --- a/hw/core/numa.c +++ b/hw/core/numa.c @@ -695,7 +695,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); diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 4d054ca..d1e174b 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -147,7 +147,7 @@ static FWCfgState *create_fw_cfg(MachineState *ms) fw_cfg_add_file(fw_cfg, "/etc/power-button-addr", g_memdup(&val, sizeof(val)), sizeof(val)); - fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ms->boot_order[0]); + fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ms->boot_config.order[0]); qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); return fw_cfg; @@ -391,8 +391,8 @@ static void machine_hppa_init(MachineState *machine) * mode (kernel_entry=1), and to boot from CD (gr[24]='d') * or hard disc * (gr[24]='c'). */ - kernel_entry = boot_menu ? 1 : 0; - cpu[0]->env.gr[24] = machine->boot_order[0]; + kernel_entry = machine->boot_config.has_menu ? machine->boot_config.menu : 0; + cpu[0]->env.gr[24] = machine->boot_config.order[0]; } /* We jump to the firmware entry routine and pass the diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 23bba9d..305d2c0 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -675,7 +675,7 @@ void pc_cmos_init(PCMachineState *pcms, object_property_set_link(OBJECT(pcms), "rtc_state", OBJECT(s), &error_abort); - set_boot_dev(s, MACHINE(pcms)->boot_order, &error_fatal); + set_boot_dev(s, MACHINE(pcms)->boot_config.order, &error_fatal); val = 0; val |= 0x02; /* FPU is there */ diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 4c185c7..f843dd9 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -813,124 +813,6 @@ static void pc_i440fx_1_4_machine_options(MachineClass *m) DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4_fn, pc_i440fx_1_4_machine_options); -typedef struct { - uint16_t gpu_device_id; - uint16_t pch_device_id; - uint8_t pch_revision_id; -} IGDDeviceIDInfo; - -/* In real world different GPU should have different PCH. But actually - * the different PCH DIDs likely map to different PCH SKUs. We do the - * same thing for the GPU. For PCH, the different SKUs are going to be - * all the same silicon design and implementation, just different - * features turn on and off with fuses. The SW interfaces should be - * consistent across all SKUs in a given family (eg LPT). But just same - * features may not be supported. - * - * Most of these different PCH features probably don't matter to the - * Gfx driver, but obviously any difference in display port connections - * will so it should be fine with any PCH in case of passthrough. - * - * So currently use one PCH version, 0x8c4e, to cover all HSW(Haswell) - * scenarios, 0x9cc3 for BDW(Broadwell). - */ -static const IGDDeviceIDInfo igd_combo_id_infos[] = { - /* HSW Classic */ - {0x0402, 0x8c4e, 0x04}, /* HSWGT1D, HSWD_w7 */ - {0x0406, 0x8c4e, 0x04}, /* HSWGT1M, HSWM_w7 */ - {0x0412, 0x8c4e, 0x04}, /* HSWGT2D, HSWD_w7 */ - {0x0416, 0x8c4e, 0x04}, /* HSWGT2M, HSWM_w7 */ - {0x041E, 0x8c4e, 0x04}, /* HSWGT15D, HSWD_w7 */ - /* HSW ULT */ - {0x0A06, 0x8c4e, 0x04}, /* HSWGT1UT, HSWM_w7 */ - {0x0A16, 0x8c4e, 0x04}, /* HSWGT2UT, HSWM_w7 */ - {0x0A26, 0x8c4e, 0x06}, /* HSWGT3UT, HSWM_w7 */ - {0x0A2E, 0x8c4e, 0x04}, /* HSWGT3UT28W, HSWM_w7 */ - {0x0A1E, 0x8c4e, 0x04}, /* HSWGT2UX, HSWM_w7 */ - {0x0A0E, 0x8c4e, 0x04}, /* HSWGT1ULX, HSWM_w7 */ - /* HSW CRW */ - {0x0D26, 0x8c4e, 0x04}, /* HSWGT3CW, HSWM_w7 */ - {0x0D22, 0x8c4e, 0x04}, /* HSWGT3CWDT, HSWD_w7 */ - /* HSW Server */ - {0x041A, 0x8c4e, 0x04}, /* HSWSVGT2, HSWD_w7 */ - /* HSW SRVR */ - {0x040A, 0x8c4e, 0x04}, /* HSWSVGT1, HSWD_w7 */ - /* BSW */ - {0x1606, 0x9cc3, 0x03}, /* BDWULTGT1, BDWM_w7 */ - {0x1616, 0x9cc3, 0x03}, /* BDWULTGT2, BDWM_w7 */ - {0x1626, 0x9cc3, 0x03}, /* BDWULTGT3, BDWM_w7 */ - {0x160E, 0x9cc3, 0x03}, /* BDWULXGT1, BDWM_w7 */ - {0x161E, 0x9cc3, 0x03}, /* BDWULXGT2, BDWM_w7 */ - {0x1602, 0x9cc3, 0x03}, /* BDWHALOGT1, BDWM_w7 */ - {0x1612, 0x9cc3, 0x03}, /* BDWHALOGT2, BDWM_w7 */ - {0x1622, 0x9cc3, 0x03}, /* BDWHALOGT3, BDWM_w7 */ - {0x162B, 0x9cc3, 0x03}, /* BDWHALO28W, BDWM_w7 */ - {0x162A, 0x9cc3, 0x03}, /* BDWGT3WRKS, BDWM_w7 */ - {0x162D, 0x9cc3, 0x03}, /* BDWGT3SRVR, BDWM_w7 */ -}; - -static void isa_bridge_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - dc->desc = "ISA bridge faked to support IGD PT"; - set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->class_id = PCI_CLASS_BRIDGE_ISA; -}; - -static const TypeInfo isa_bridge_info = { - .name = "igd-passthrough-isa-bridge", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PCIDevice), - .class_init = isa_bridge_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - -static void pt_graphics_register_types(void) -{ - type_register_static(&isa_bridge_info); -} -type_init(pt_graphics_register_types) - -void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id) -{ - struct PCIDevice *bridge_dev; - int i, num; - uint16_t pch_dev_id = 0xffff; - uint8_t pch_rev_id = 0; - - num = ARRAY_SIZE(igd_combo_id_infos); - for (i = 0; i < num; i++) { - if (gpu_dev_id == igd_combo_id_infos[i].gpu_device_id) { - pch_dev_id = igd_combo_id_infos[i].pch_device_id; - pch_rev_id = igd_combo_id_infos[i].pch_revision_id; - } - } - - if (pch_dev_id == 0xffff) { - return; - } - - /* Currently IGD drivers always need to access PCH by 1f.0. */ - bridge_dev = pci_create_simple(bus, PCI_DEVFN(0x1f, 0), - "igd-passthrough-isa-bridge"); - - /* - * Note that vendor id is always PCI_VENDOR_ID_INTEL. - */ - if (!bridge_dev) { - fprintf(stderr, "set igd-passthrough-isa-bridge failed!\n"); - return; - } - pci_config_set_device_id(bridge_dev->config, pch_dev_id); - pci_config_set_revision(bridge_dev->config, pch_rev_id); -} - #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 4125cbe..d605f3f 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -178,21 +178,13 @@ error: static void fw_cfg_bootsplash(FWCfgState *s) { - const char *boot_splash_filename = NULL; - const char *boot_splash_time = NULL; char *filename, *file_data; gsize file_size; int file_type; - /* get user configuration */ - QemuOptsList *plist = qemu_find_opts("boot-opts"); - QemuOpts *opts = QTAILQ_FIRST(&plist->head); - boot_splash_filename = qemu_opt_get(opts, "splash"); - boot_splash_time = qemu_opt_get(opts, "splash-time"); - /* insert splash time if user configurated */ - if (boot_splash_time) { - int64_t bst_val = qemu_opt_get_number(opts, "splash-time", -1); + if (current_machine->boot_config.has_splash_time) { + int64_t bst_val = current_machine->boot_config.splash_time; uint16_t bst_le16; /* validate the input */ @@ -208,7 +200,8 @@ static void fw_cfg_bootsplash(FWCfgState *s) } /* insert splash file if user configurated */ - if (boot_splash_filename) { + if (current_machine->boot_config.has_splash) { + const char *boot_splash_filename = current_machine->boot_config.splash; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename); if (filename == NULL) { error_report("failed to find file '%s'", boot_splash_filename); @@ -238,17 +231,11 @@ static void fw_cfg_bootsplash(FWCfgState *s) static void fw_cfg_reboot(FWCfgState *s) { - const char *reboot_timeout = NULL; uint64_t rt_val = -1; uint32_t rt_le32; - /* get user configuration */ - QemuOptsList *plist = qemu_find_opts("boot-opts"); - QemuOpts *opts = QTAILQ_FIRST(&plist->head); - reboot_timeout = qemu_opt_get(opts, "reboot-timeout"); - - if (reboot_timeout) { - rt_val = qemu_opt_get_number(opts, "reboot-timeout", -1); + if (current_machine->boot_config.has_reboot_timeout) { + rt_val = current_machine->boot_config.reboot_timeout; /* validate the input */ if (rt_val > 0xffff && rt_val != (uint64_t)-1) { @@ -1133,7 +1120,7 @@ static void fw_cfg_common_realize(DeviceState *dev, Error **errp) fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4); fw_cfg_add_bytes(s, FW_CFG_UUID, &qemu_uuid, 16); fw_cfg_add_i16(s, FW_CFG_NOGRAPHIC, (uint16_t)!machine->enable_graphics); - fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)boot_menu); + fw_cfg_add_i16(s, FW_CFG_BOOT_MENU, (uint16_t)(machine->boot_config.has_menu && machine->boot_config.menu)); fw_cfg_bootsplash(s); fw_cfg_reboot(s); diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index e8ef1a9..c865921 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -111,7 +111,7 @@ static void ppc_core99_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; - const char *boot_device = machine->boot_order; + const char *boot_device = machine->boot_config.order; Core99MachineState *core99_machine = CORE99_MACHINE(machine); PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index fe2adb0..d62fdf0 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -82,7 +82,7 @@ static void ppc_heathrow_init(MachineState *machine) { ram_addr_t ram_size = machine->ram_size; const char *bios_name = machine->firmware ?: PROM_FILENAME; - const char *boot_device = machine->boot_order; + const char *boot_device = machine->boot_config.order; PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index bf622aa..a1cd450 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -381,7 +381,7 @@ static void ibm_40p_init(MachineState *machine) } boot_device = 'm'; } else { - boot_device = machine->boot_order[0]; + boot_device = machine->boot_config.order[0]; } fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)machine->smp.max_cpus); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 8bbae68..6de8005 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1044,8 +1044,8 @@ static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0)); } } - if (boot_menu) { - _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu))); + if (machine->boot_config.has_menu && machine->boot_config.menu) { + _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", true))); } _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width)); _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height)); diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 4b5eb77..8612684 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -290,13 +290,10 @@ static Property s390_ipl_properties[] = { static void s390_ipl_set_boot_menu(S390IPLState *ipl) { - QemuOptsList *plist = qemu_find_opts("boot-opts"); - QemuOpts *opts = QTAILQ_FIRST(&plist->head); - const char *tmp; unsigned long splash_time = 0; if (!get_boot_device(0)) { - if (boot_menu) { + if (current_machine->boot_config.has_menu && current_machine->boot_config.menu) { error_report("boot menu requires a bootindex to be specified for " "the IPL device"); } @@ -306,7 +303,7 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) switch (ipl->iplb.pbt) { case S390_IPL_TYPE_CCW: /* In the absence of -boot menu, use zipl parameters */ - if (!qemu_opt_get(opts, "menu")) { + if (!current_machine->boot_config.has_menu) { ipl->qipl.qipl_flags |= QIPL_FLAG_BM_OPTS_ZIPL; return; } @@ -314,26 +311,21 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) case S390_IPL_TYPE_QEMU_SCSI: break; default: - if (boot_menu) { + if (current_machine->boot_config.has_menu && current_machine->boot_config.menu) { error_report("boot menu is not supported for this device type"); } return; } - if (!boot_menu) { + if (!current_machine->boot_config.has_menu || !current_machine->boot_config.menu) { return; } ipl->qipl.qipl_flags |= QIPL_FLAG_BM_OPTS_CMD; - tmp = qemu_opt_get(opts, "splash-time"); - - if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) { - error_report("splash-time is invalid, forcing it to 0"); - ipl->qipl.boot_menu_timeout = 0; - return; + if (current_machine->boot_config.has_splash_time) { + splash_time = current_machine->boot_config.splash_time; } - if (splash_time > 0xffffffff) { error_report("splash-time is too large, forcing it to max value"); ipl->qipl.boot_menu_timeout = 0xffffffff; diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index b693eea..d928832 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -831,8 +831,7 @@ static void sun4m_hw_init(MachineState *machine) SysBusDevice *s; unsigned int smp_cpus = machine->smp.cpus; unsigned int max_cpus = machine->smp.max_cpus; - Object *ram_memdev = object_resolve_path_type(machine->ram_memdev_id, - TYPE_MEMORY_BACKEND, NULL); + HostMemoryBackend *ram_memdev = machine->memdev; NICInfo *nd = &nd_table[0]; if (machine->ram_size > hwdef->max_mem) { @@ -852,7 +851,7 @@ static void sun4m_hw_init(MachineState *machine) /* Create and map RAM frontend */ dev = qdev_new("memory"); - object_property_set_link(OBJECT(dev), "memdev", ram_memdev, &error_fatal); + object_property_set_link(OBJECT(dev), "memdev", OBJECT(ram_memdev), &error_fatal); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0); @@ -1050,7 +1049,7 @@ static void sun4m_hw_init(MachineState *machine) machine->ram_size, &initrd_size); nvram_init(nvram, (uint8_t *)&nd->macaddr, machine->kernel_cmdline, - machine->boot_order, machine->ram_size, kernel_size, + machine->boot_config.order, machine->ram_size, kernel_size, graphic_width, graphic_height, graphic_depth, hwdef->nvram_machine_id, "Sun4m"); @@ -1091,7 +1090,7 @@ static void sun4m_hw_init(MachineState *machine) } fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR); fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, machine->boot_order[0]); + fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, machine->boot_config.order[0]); qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 7c461d1..d1bc77d 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -695,7 +695,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem, &kernel_addr, &kernel_entry); sun4u_NVRAM_set_params(nvram, NVRAM_SIZE, "Sun4u", machine->ram_size, - machine->boot_order, + machine->boot_config.order, kernel_addr, kernel_size, machine->kernel_cmdline, initrd_addr, initrd_size, @@ -727,7 +727,7 @@ static void sun4uv_init(MemoryRegion *address_space_mem, } fw_cfg_add_i64(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); fw_cfg_add_i64(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); - fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, machine->boot_order[0]); + fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, machine->boot_config.order[0]); fw_cfg_add_i16(fw_cfg, FW_CFG_SPARC64_WIDTH, graphic_width); fw_cfg_add_i16(fw_cfg, FW_CFG_SPARC64_HEIGHT, graphic_height); diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index e409a86..4de8b6b 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -203,7 +203,6 @@ static int vhost_kernel_get_vq_index(struct vhost_dev *dev, int idx) return idx - dev->vq_index; } -#ifdef CONFIG_VHOST_VSOCK static int vhost_kernel_vsock_set_guest_cid(struct vhost_dev *dev, uint64_t guest_cid) { @@ -214,7 +213,6 @@ static int vhost_kernel_vsock_set_running(struct vhost_dev *dev, int start) { return vhost_kernel_call(dev, VHOST_VSOCK_SET_RUNNING, &start); } -#endif /* CONFIG_VHOST_VSOCK */ static void vhost_kernel_iotlb_read(void *opaque) { @@ -319,10 +317,8 @@ const VhostOps kernel_ops = { .vhost_set_owner = vhost_kernel_set_owner, .vhost_reset_device = vhost_kernel_reset_device, .vhost_get_vq_index = vhost_kernel_get_vq_index, -#ifdef CONFIG_VHOST_VSOCK .vhost_vsock_set_guest_cid = vhost_kernel_vsock_set_guest_cid, .vhost_vsock_set_running = vhost_kernel_vsock_set_running, -#endif /* CONFIG_VHOST_VSOCK */ .vhost_set_iotlb_callback = vhost_kernel_set_iotlb_callback, .vhost_send_device_iotlb_msg = vhost_kernel_send_device_iotlb_msg, }; diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 027190f..0ec7e52 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -60,7 +60,6 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "hw/xen/xen.h" -#include "hw/i386/pc.h" #include "hw/xen/xen-legacy-backend.h" #include "xen_pt.h" #include "qemu/range.h" @@ -702,17 +701,6 @@ static const MemoryListener xen_pt_io_listener = { .priority = 10, }; -static void -xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s, - XenHostPCIDevice *dev) -{ - uint16_t gpu_dev_id; - PCIDevice *d = &s->dev; - - gpu_dev_id = dev->device_id; - igd_passthrough_isa_bridge_create(pci_get_bus(d), gpu_dev_id); -} - /* destroy. */ static void xen_pt_destroy(PCIDevice *d) { diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h index 6b8e13c..e7c4316 100644 --- a/hw/xen/xen_pt.h +++ b/hw/xen/xen_pt.h @@ -43,6 +43,8 @@ OBJECT_DECLARE_SIMPLE_TYPE(XenPCIPassthroughState, XEN_PT_DEVICE) uint32_t igd_read_opregion(XenPCIPassthroughState *s); void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val); +void xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s, + XenHostPCIDevice *dev); /* function type for config reg */ typedef int (*xen_pt_conf_reg_init) diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index a3bc7e3..f303f67 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -289,3 +289,125 @@ void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val) (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT), (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT)); } + +typedef struct { + uint16_t gpu_device_id; + uint16_t pch_device_id; + uint8_t pch_revision_id; +} IGDDeviceIDInfo; + +/* + * In real world different GPU should have different PCH. But actually + * the different PCH DIDs likely map to different PCH SKUs. We do the + * same thing for the GPU. For PCH, the different SKUs are going to be + * all the same silicon design and implementation, just different + * features turn on and off with fuses. The SW interfaces should be + * consistent across all SKUs in a given family (eg LPT). But just same + * features may not be supported. + * + * Most of these different PCH features probably don't matter to the + * Gfx driver, but obviously any difference in display port connections + * will so it should be fine with any PCH in case of passthrough. + * + * So currently use one PCH version, 0x8c4e, to cover all HSW(Haswell) + * scenarios, 0x9cc3 for BDW(Broadwell). + */ +static const IGDDeviceIDInfo igd_combo_id_infos[] = { + /* HSW Classic */ + {0x0402, 0x8c4e, 0x04}, /* HSWGT1D, HSWD_w7 */ + {0x0406, 0x8c4e, 0x04}, /* HSWGT1M, HSWM_w7 */ + {0x0412, 0x8c4e, 0x04}, /* HSWGT2D, HSWD_w7 */ + {0x0416, 0x8c4e, 0x04}, /* HSWGT2M, HSWM_w7 */ + {0x041E, 0x8c4e, 0x04}, /* HSWGT15D, HSWD_w7 */ + /* HSW ULT */ + {0x0A06, 0x8c4e, 0x04}, /* HSWGT1UT, HSWM_w7 */ + {0x0A16, 0x8c4e, 0x04}, /* HSWGT2UT, HSWM_w7 */ + {0x0A26, 0x8c4e, 0x06}, /* HSWGT3UT, HSWM_w7 */ + {0x0A2E, 0x8c4e, 0x04}, /* HSWGT3UT28W, HSWM_w7 */ + {0x0A1E, 0x8c4e, 0x04}, /* HSWGT2UX, HSWM_w7 */ + {0x0A0E, 0x8c4e, 0x04}, /* HSWGT1ULX, HSWM_w7 */ + /* HSW CRW */ + {0x0D26, 0x8c4e, 0x04}, /* HSWGT3CW, HSWM_w7 */ + {0x0D22, 0x8c4e, 0x04}, /* HSWGT3CWDT, HSWD_w7 */ + /* HSW Server */ + {0x041A, 0x8c4e, 0x04}, /* HSWSVGT2, HSWD_w7 */ + /* HSW SRVR */ + {0x040A, 0x8c4e, 0x04}, /* HSWSVGT1, HSWD_w7 */ + /* BSW */ + {0x1606, 0x9cc3, 0x03}, /* BDWULTGT1, BDWM_w7 */ + {0x1616, 0x9cc3, 0x03}, /* BDWULTGT2, BDWM_w7 */ + {0x1626, 0x9cc3, 0x03}, /* BDWULTGT3, BDWM_w7 */ + {0x160E, 0x9cc3, 0x03}, /* BDWULXGT1, BDWM_w7 */ + {0x161E, 0x9cc3, 0x03}, /* BDWULXGT2, BDWM_w7 */ + {0x1602, 0x9cc3, 0x03}, /* BDWHALOGT1, BDWM_w7 */ + {0x1612, 0x9cc3, 0x03}, /* BDWHALOGT2, BDWM_w7 */ + {0x1622, 0x9cc3, 0x03}, /* BDWHALOGT3, BDWM_w7 */ + {0x162B, 0x9cc3, 0x03}, /* BDWHALO28W, BDWM_w7 */ + {0x162A, 0x9cc3, 0x03}, /* BDWGT3WRKS, BDWM_w7 */ + {0x162D, 0x9cc3, 0x03}, /* BDWGT3SRVR, BDWM_w7 */ +}; + +static void isa_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + dc->desc = "ISA bridge faked to support IGD PT"; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->class_id = PCI_CLASS_BRIDGE_ISA; +}; + +static const TypeInfo isa_bridge_info = { + .name = "igd-passthrough-isa-bridge", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = isa_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void pt_graphics_register_types(void) +{ + type_register_static(&isa_bridge_info); +} +type_init(pt_graphics_register_types) + +void xen_igd_passthrough_isa_bridge_create(XenPCIPassthroughState *s, + XenHostPCIDevice *dev) +{ + PCIBus *bus = pci_get_bus(&s->dev); + struct PCIDevice *bridge_dev; + int i, num; + const uint16_t gpu_dev_id = dev->device_id; + uint16_t pch_dev_id = 0xffff; + uint8_t pch_rev_id = 0; + + num = ARRAY_SIZE(igd_combo_id_infos); + for (i = 0; i < num; i++) { + if (gpu_dev_id == igd_combo_id_infos[i].gpu_device_id) { + pch_dev_id = igd_combo_id_infos[i].pch_device_id; + pch_rev_id = igd_combo_id_infos[i].pch_revision_id; + } + } + + if (pch_dev_id == 0xffff) { + return; + } + + /* Currently IGD drivers always need to access PCH by 1f.0. */ + bridge_dev = pci_create_simple(bus, PCI_DEVFN(0x1f, 0), + "igd-passthrough-isa-bridge"); + + /* + * Note that vendor id is always PCI_VENDOR_ID_INTEL. + */ + if (!bridge_dev) { + fprintf(stderr, "set igd-passthrough-isa-bridge failed!\n"); + return; + } + pci_config_set_device_id(bridge_dev->config, pch_dev_id); + pci_config_set_revision(bridge_dev->config, pch_rev_id); +} diff --git a/include/hw/boards.h b/include/hw/boards.h index d64b548..7b416c9 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -25,7 +25,7 @@ OBJECT_DECLARE_TYPE(MachineState, MachineClass, MACHINE) extern MachineState *current_machine; -void machine_run_board_init(MachineState *machine); +void machine_run_board_init(MachineState *machine, const char *mem_path, Error **errp); bool machine_usb(MachineState *machine); int machine_phandle_start(MachineState *machine); bool machine_dump_guest_core(MachineState *machine); @@ -339,7 +339,7 @@ struct MachineState { bool suppress_vmdesc; bool enable_graphics; ConfidentialGuestSupport *cgs; - char *ram_memdev_id; + HostMemoryBackend *memdev; /* * convenience alias to ram_memdev_id backend memory region * or to numa container memory region @@ -350,8 +350,7 @@ struct MachineState { ram_addr_t ram_size; ram_addr_t maxram_size; uint64_t ram_slots; - const char *boot_order; - const char *boot_once; + BootConfiguration boot_config; char *kernel_filename; char *kernel_cmdline; char *initrd_filename; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 637367d..aff8add 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -315,5 +315,4 @@ extern const size_t pc_compat_1_4_len; } \ type_init(pc_machine_init_##suffix) -extern void igd_passthrough_isa_bridge_create(PCIBus *bus, uint16_t gpu_dev_id); #endif diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index 031cf23..d1548d5 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -208,17 +208,19 @@ void qemu_co_queue_init(CoQueue *queue); void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock); /** - * Removes the next coroutine from the CoQueue, and wake it up. + * Removes the next coroutine from the CoQueue, and queue it to run after + * the currently-running coroutine yields. * Returns true if a coroutine was removed, false if the queue is empty. - * OK to run from coroutine and non-coroutine context. + * Used from coroutine context, use qemu_co_enter_next outside. */ -bool qemu_co_queue_next(CoQueue *queue); +bool coroutine_fn qemu_co_queue_next(CoQueue *queue); /** - * Empties the CoQueue; all coroutines are woken up. - * OK to run from coroutine and non-coroutine context. + * Empties the CoQueue and queues the coroutine to run after + * the currently-running coroutine yields. + * Used from coroutine context, use qemu_co_enter_all outside. */ -void qemu_co_queue_restart_all(CoQueue *queue); +void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue); /** * Removes the next coroutine from the CoQueue, and wake it up. Unlike @@ -234,6 +236,19 @@ void qemu_co_queue_restart_all(CoQueue *queue); bool qemu_co_enter_next_impl(CoQueue *queue, QemuLockable *lock); /** + * Empties the CoQueue, waking the waiting coroutine one at a time. Unlike + * qemu_co_queue_all, this function releases the lock during aio_co_wake + * because it is meant to be used outside coroutine context; in that case, the + * coroutine is entered immediately, before qemu_co_enter_all returns. + * + * If used in coroutine context, qemu_co_enter_all is equivalent to + * qemu_co_queue_all. + */ +#define qemu_co_enter_all(queue, lock) \ + qemu_co_enter_all_impl(queue, QEMU_MAKE_LOCKABLE(lock)) +void qemu_co_enter_all_impl(CoQueue *queue, QemuLockable *lock); + +/** * Checks if the CoQueue is empty. */ bool qemu_co_queue_empty(CoQueue *queue); diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 360a408..b4030ac 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -46,8 +46,6 @@ extern int alt_grab; extern int ctrl_grab; extern int graphic_rotate; extern int old_param; -extern int boot_menu; -extern bool boot_strict; extern uint8_t *boot_splash_filedata; extern bool enable_mlock; extern bool enable_cpu_pm; diff --git a/meson.build b/meson.build index 864e979..9b20dcd 100644 --- a/meson.build +++ b/meson.build @@ -2179,7 +2179,8 @@ config_host_data.set('CONFIG_AVX512F_OPT', get_option('avx512f') \ have_pvrdma = get_option('pvrdma') \ .require(rdma.found(), error_message: 'PVRDMA requires OpenFabrics libraries') \ - .require(cc.compiles(''' + .require(cc.compiles(gnu_source_prefix + ''' + #include <sys/mman.h> int main(void) { char buf = 0; @@ -2190,7 +2191,7 @@ have_pvrdma = get_option('pvrdma') \ }'''), error_message: 'PVRDMA requires mremap').allowed() if have_pvrdma - config_host_data.set('LEGACY_RDMA_REG_MR', not cc.compiles(''' + config_host_data.set('LEGACY_RDMA_REG_MR', not cc.links(''' #include <infiniband/verbs.h> int main(void) { @@ -2636,10 +2637,25 @@ if have_system slirp_opt = get_option('slirp') if slirp_opt in ['enabled', 'auto', 'system'] have_internal = fs.exists(meson.current_source_dir() / 'slirp/meson.build') + slirp_dep_required = (slirp_opt == 'system' or + slirp_opt == 'enabled' and not have_internal) slirp = dependency('slirp', kwargs: static_kwargs, - method: 'pkg-config', - required: slirp_opt == 'system' or - slirp_opt == 'enabled' and not have_internal) + method: 'pkg-config', version: '>=4.1.0', + required: slirp_dep_required) + # slirp <4.7 is incompatible with CFI support in QEMU. This is because + # it passes function pointers within libslirp as callbacks for timers. + # When using a system-wide shared libslirp, the type information for the + # callback is missing and the timer call produces a false positive with CFI. + # Do not use the "version" keyword argument to produce a better error. + # with control-flow integrity. + if get_option('cfi') and slirp.found() and slirp.version().version_compare('<4.7') + if slirp_dep_required + error('Control-Flow Integrity requires libslirp 4.7.') + else + warning('Control-Flow Integrity requires libslirp 4.7, not using system-wide libslirp.') + slirp = not_found + endif + endif if slirp.found() slirp_opt = 'system' elif have_internal @@ -2712,18 +2728,6 @@ if have_system endif endif -# For CFI, we need to compile slirp as a static library together with qemu. -# This is because we register slirp functions as callbacks for QEMU Timers. -# When using a system-wide shared libslirp, the type information for the -# callback is missing and the timer call produces a false positive with CFI. -# -# Now that slirp_opt has been defined, check if the selected slirp is compatible -# with control-flow integrity. -if get_option('cfi') and slirp_opt == 'system' - error('Control-Flow Integrity is not compatible with system-wide slirp.' \ - + ' Please configure with --enable-slirp=git') -endif - fdt = not_found if have_system fdt_opt = get_option('fdt') diff --git a/net/slirp.c b/net/slirp.c index bc5e9e4..8679be6 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -184,23 +184,66 @@ static int64_t net_slirp_clock_get_ns(void *opaque) return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); } +typedef struct SlirpTimer SlirpTimer; +struct SlirpTimer { + QEMUTimer timer; +#if SLIRP_CHECK_VERSION(4,7,0) + Slirp *slirp; + SlirpTimerId id; + void *cb_opaque; +#endif +}; + +#if SLIRP_CHECK_VERSION(4,7,0) +static void net_slirp_init_completed(Slirp *slirp, void *opaque) +{ + SlirpState *s = opaque; + s->slirp = slirp; +} + +static void net_slirp_timer_cb(void *opaque) +{ + SlirpTimer *t = opaque; + slirp_handle_timer(t->slirp, t->id, t->cb_opaque); +} + +static void *net_slirp_timer_new_opaque(SlirpTimerId id, + void *cb_opaque, void *opaque) +{ + SlirpState *s = opaque; + SlirpTimer *t = g_new(SlirpTimer, 1); + t->slirp = s->slirp; + t->id = id; + t->cb_opaque = cb_opaque; + timer_init_full(&t->timer, NULL, QEMU_CLOCK_VIRTUAL, + SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL, + net_slirp_timer_cb, t); + return t; +} +#else static void *net_slirp_timer_new(SlirpTimerCb cb, void *cb_opaque, void *opaque) { - return timer_new_full(NULL, QEMU_CLOCK_VIRTUAL, - SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL, - cb, cb_opaque); + SlirpTimer *t = g_new(SlirpTimer, 1); + timer_init_full(&t->timer, NULL, QEMU_CLOCK_VIRTUAL, + SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL, + cb, cb_opaque); + return t; } +#endif static void net_slirp_timer_free(void *timer, void *opaque) { - timer_free(timer); + SlirpTimer *t = timer; + timer_del(&t->timer); + g_free(t); } static void net_slirp_timer_mod(void *timer, int64_t expire_timer, void *opaque) { - timer_mod(timer, expire_timer); + SlirpTimer *t = timer; + timer_mod(&t->timer, expire_timer); } static void net_slirp_register_poll_fd(int fd, void *opaque) @@ -222,7 +265,12 @@ static const SlirpCb slirp_cb = { .send_packet = net_slirp_send_packet, .guest_error = net_slirp_guest_error, .clock_get_ns = net_slirp_clock_get_ns, +#if SLIRP_CHECK_VERSION(4,7,0) + .init_completed = net_slirp_init_completed, + .timer_new_opaque = net_slirp_timer_new_opaque, +#else .timer_new = net_slirp_timer_new, +#endif .timer_free = net_slirp_timer_free, .timer_mod = net_slirp_timer_mod, .register_poll_fd = net_slirp_register_poll_fd, @@ -380,6 +428,7 @@ static int net_slirp_init(NetClientState *peer, const char *model, #if defined(CONFIG_SMBD_COMMAND) struct in_addr smbsrv = { .s_addr = 0 }; #endif + SlirpConfig cfg = { 0 }; NetClientState *nc; SlirpState *s; char buf[20]; @@ -568,12 +617,26 @@ static int net_slirp_init(NetClientState *peer, const char *model, s = DO_UPCAST(SlirpState, nc, nc); - s->slirp = slirp_init(restricted, ipv4, net, mask, host, - ipv6, ip6_prefix, vprefix6_len, ip6_host, - vhostname, tftp_server_name, - tftp_export, bootfile, dhcp, - dns, ip6_dns, dnssearch, vdomainname, - &slirp_cb, s); + cfg.version = SLIRP_CHECK_VERSION(4,7,0) ? 4 : 1; + cfg.restricted = restricted; + cfg.in_enabled = ipv4; + cfg.vnetwork = net; + cfg.vnetmask = mask; + cfg.vhost = host; + cfg.in6_enabled = ipv6; + cfg.vprefix_addr6 = ip6_prefix; + cfg.vprefix_len = vprefix6_len; + cfg.vhost6 = ip6_host; + cfg.vhostname = vhostname; + cfg.tftp_server_name = tftp_server_name; + cfg.tftp_path = tftp_export; + cfg.bootfile = bootfile; + cfg.vdhcp_start = dhcp; + cfg.vnameserver = dns; + cfg.vnameserver6 = ip6_dns; + cfg.vdnssearch = dnssearch; + cfg.vdomainname = vdomainname; + s->slirp = slirp_new(&cfg, &slirp_cb, s); QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry); /* diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile index f1ef898..2494ad9 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -22,9 +22,11 @@ override CFLAGS += $(call cc-option, -fcf-protection=none) override CPPFLAGS += -MMD -MP -MT $@ -MF $(@D)/$(*F).d override CFLAGS += $(filter -W%, $(QEMU_CFLAGS)) -override CFLAGS += $(CFLAGS_NOPIE) -ffreestanding -I$(TOPSRC_DIR)/include +override CFLAGS += $(call cc-option, -fno-pie) +override CFLAGS += -ffreestanding -I$(TOPSRC_DIR)/include override CFLAGS += $(call cc-option, -fno-stack-protector) override CFLAGS += $(call cc-option, -m16) +override CFLAGS += $(call cc-option, -Wno-array-bounds) ifeq ($(filter -m16, $(CFLAGS)),) # Attempt to work around compilers that lack -m16 (GCC <= 4.8, clang <= ??) diff --git a/qapi/machine.json b/qapi/machine.json index 4c417e3..92480d4 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1396,6 +1396,36 @@ 'features': ['deprecated'] } ## +# @BootConfiguration: +# +# Schema for virtual machine boot configuration. +# +# @order: Boot order (a=floppy, c=hard disk, d=CD-ROM, n=network) +# +# @once: Boot order to apply on first boot +# +# @menu: Whether to show a boot menu +# +# @splash: The name of the file to be passed to the firmware as logo picture, if @menu is true. +# +# @splash-time: How long to show the logo picture, in milliseconds +# +# @reboot-timeout: Timeout before guest reboots after boot fails +# +# @strict: Whether to attempt booting from devices not included in the boot order +# +# Since: 7.1 +## +{ 'struct': 'BootConfiguration', 'data': { + '*order': 'str', + '*once': 'str', + '*menu': 'bool', + '*splash': 'str', + '*splash-time': 'int', + '*reboot-timeout': 'int', + '*strict': 'bool' } } + +## # @SMPConfiguration: # # Schema for CPU topology configuration. A missing value lets @@ -1584,3 +1614,21 @@ ## { 'enum': 'SmbiosEntryPointType', 'data': [ '32', '64' ] } + +## +# @MemorySizeConfiguration: +# +# Schema for memory size configuration. +# +# @size: memory size in bytes +# +# @max-size: maximum hotpluggable memory size in bytes +# +# @slots: number of available memory slots for hotplug +# +# Since: 7.1 +## +{ 'struct': 'MemorySizeConfiguration', 'data': { + '*size': 'size', + '*max-size': 'size', + '*slots': 'uint64' } } diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 4763d02..d900d18 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -2831,8 +2831,8 @@ sub process { } # check for pointless casting of g_malloc return - if ($line =~ /\*\s*\)\s*g_(try)?(m|re)alloc(0?)(_n)?\b/) { - if ($2 == 'm') { + if ($line =~ /\*\s*\)\s*g_(try|)(m|re)alloc(0?)(_n)?\b/) { + if ($2 eq 'm') { ERROR("unnecessary cast may hide bugs, use g_$1new$3 instead\n" . $herecurr); } else { ERROR("unnecessary cast may hide bugs, use g_$1renew$3 instead\n" . $herecurr); diff --git a/scripts/kvm/vmxcap b/scripts/kvm/vmxcap index f140040..ce27f5e 100755 --- a/scripts/kvm/vmxcap +++ b/scripts/kvm/vmxcap @@ -23,6 +23,7 @@ MSR_IA32_VMX_TRUE_PROCBASED_CTLS = 0x48E MSR_IA32_VMX_TRUE_EXIT_CTLS = 0x48F MSR_IA32_VMX_TRUE_ENTRY_CTLS = 0x490 MSR_IA32_VMX_VMFUNC = 0x491 +MSR_IA32_VMX_PROCBASED_CTLS3 = 0x492 class msr(object): def __init__(self): @@ -71,6 +72,13 @@ class Control(object): s = 'yes' print(' %-40s %s' % (self.bits[bit], s)) +# All 64 bits in the tertiary controls MSR are allowed-1 +class Allowed1Control(Control): + def read2(self, nr): + m = msr() + val = m.read(nr, 0) + return (0, val) + class Misc(object): def __init__(self, name, bits, msr): self.name = name @@ -135,6 +143,7 @@ controls = [ 12: 'RDTSC exiting', 15: 'CR3-load exiting', 16: 'CR3-store exiting', + 17: 'Activate tertiary controls', 19: 'CR8-load exiting', 20: 'CR8-store exiting', 21: 'Use TPR shadow', @@ -186,6 +195,14 @@ controls = [ cap_msr = MSR_IA32_VMX_PROCBASED_CTLS2, ), + Allowed1Control( + name = 'tertiary processor-based controls', + bits = { + 4: 'Enable IPI virtualization' + }, + cap_msr = MSR_IA32_VMX_PROCBASED_CTLS3, + ), + Control( name = 'VM-Exit controls', bits = { diff --git a/slirp b/slirp -Subproject a88d9ace234a24ce1c17189642ef9104799425e +Subproject 9d59bb775d6294c8b447a88512f7bb43f12a25a diff --git a/softmmu/bootdevice.c b/softmmu/bootdevice.c index c0713bf..2106f10 100644 --- a/softmmu/bootdevice.c +++ b/softmmu/bootdevice.c @@ -268,7 +268,8 @@ char *get_boot_devices_list(size_t *size) *size = total; - if (boot_strict && *size > 0) { + if (current_machine->boot_config.has_strict && + current_machine->boot_config.strict && *size > 0) { list[total-1] = '\n'; list = g_realloc(list, total + 5); memcpy(&list[total], "HALT", 5); diff --git a/softmmu/globals.c b/softmmu/globals.c index 98b64e0..916bc12 100644 --- a/softmmu/globals.c +++ b/softmmu/globals.c @@ -54,8 +54,6 @@ int alt_grab; int ctrl_grab; unsigned int nb_prom_envs; const char *prom_envs[MAX_PROM_ENVS]; -int boot_menu; -bool boot_strict; uint8_t *boot_splash_filedata; int only_migratable; /* turn it off unless user states otherwise */ int icount_align_option; diff --git a/softmmu/vl.c b/softmmu/vl.c index 488cc4d..817d515 100644 --- a/softmmu/vl.c +++ b/softmmu/vl.c @@ -159,11 +159,11 @@ static const char *mem_path; static const char *incoming; static const char *loadvm; static const char *accelerators; +static bool have_custom_ram_size; +static const char *ram_memdev_id; static QDict *machine_opts_dict; static QTAILQ_HEAD(, ObjectOption) object_opts = QTAILQ_HEAD_INITIALIZER(object_opts); static QTAILQ_HEAD(, DeviceOption) device_opts = QTAILQ_HEAD_INITIALIZER(device_opts); -static ram_addr_t maxram_size; -static uint64_t ram_slots; static int display_remote; static int snapshot; static bool preconfig_requested; @@ -171,7 +171,6 @@ static QemuPluginList plugin_list = QTAILQ_HEAD_INITIALIZER(plugin_list); static BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue); static bool nographic = false; static int mem_prealloc; /* force preallocation of physical target memory */ -static ram_addr_t ram_size; static const char *vga_model = NULL; static DisplayOptions dpy; static int num_serial_hds; @@ -1736,6 +1735,7 @@ static void keyval_dashify(QDict *qdict, Error **errp) static void qemu_apply_legacy_machine_options(QDict *qdict) { const char *value; + QObject *prop; keyval_dashify(qdict, &error_fatal); @@ -1768,6 +1768,26 @@ static void qemu_apply_legacy_machine_options(QDict *qdict) false); qdict_del(qdict, "kernel-irqchip"); } + + value = qdict_get_try_str(qdict, "memory-backend"); + if (value) { + if (mem_path) { + error_report("'-mem-path' can't be used together with" + "'-machine memory-backend'"); + exit(EXIT_FAILURE); + } + + /* Resolved later. */ + ram_memdev_id = g_strdup(value); + qdict_del(qdict, "memory-backend"); + } + + prop = qdict_get(qdict, "memory"); + if (prop) { + have_custom_ram_size = + qobject_type(prop) == QTYPE_QDICT && + qdict_haskey(qobject_to(QDict, prop), "size"); + } } static void object_option_foreach_add(bool (*type_opt_predicate)(const char *)) @@ -1884,38 +1904,7 @@ static bool object_create_early(const char *type) static void qemu_apply_machine_options(QDict *qdict) { - MachineClass *machine_class = MACHINE_GET_CLASS(current_machine); - const char *boot_order = NULL; - const char *boot_once = NULL; - QemuOpts *opts; - object_set_properties_from_keyval(OBJECT(current_machine), qdict, false, &error_fatal); - current_machine->ram_size = ram_size; - current_machine->maxram_size = maxram_size; - current_machine->ram_slots = ram_slots; - - opts = qemu_opts_find(qemu_find_opts("boot-opts"), NULL); - if (opts) { - boot_order = qemu_opt_get(opts, "order"); - if (boot_order) { - validate_bootdevices(boot_order, &error_fatal); - } - - boot_once = qemu_opt_get(opts, "once"); - if (boot_once) { - validate_bootdevices(boot_once, &error_fatal); - } - - boot_menu = qemu_opt_get_bool(opts, "menu", boot_menu); - boot_strict = qemu_opt_get_bool(opts, "strict", false); - } - - if (!boot_order) { - boot_order = machine_class->default_boot_order; - } - - current_machine->boot_order = boot_order; - current_machine->boot_once = boot_once; if (semihosting_enabled() && !semihosting_get_argc()) { /* fall back to the -kernel/-append */ @@ -2026,125 +2015,67 @@ static void qemu_create_late_backends(void) qemu_semihosting_console_init(); } -static bool have_custom_ram_size(void) -{ - QemuOpts *opts = qemu_find_opts_singleton("memory"); - return !!qemu_opt_get_size(opts, "size", 0); -} - static void qemu_resolve_machine_memdev(void) { - if (current_machine->ram_memdev_id) { + if (ram_memdev_id) { Object *backend; ram_addr_t backend_size; - backend = object_resolve_path_type(current_machine->ram_memdev_id, + backend = object_resolve_path_type(ram_memdev_id, TYPE_MEMORY_BACKEND, NULL); if (!backend) { - error_report("Memory backend '%s' not found", - current_machine->ram_memdev_id); - exit(EXIT_FAILURE); - } - backend_size = object_property_get_uint(backend, "size", &error_abort); - if (have_custom_ram_size() && backend_size != ram_size) { - error_report("Size specified by -m option must match size of " - "explicitly specified 'memory-backend' property"); - exit(EXIT_FAILURE); - } - if (mem_path) { - error_report("'-mem-path' can't be used together with" - "'-machine memory-backend'"); + error_report("Memory backend '%s' not found", ram_memdev_id); exit(EXIT_FAILURE); } - ram_size = backend_size; - } - - if (!xen_enabled()) { - /* On 32-bit hosts, QEMU is limited by virtual address space */ - if (ram_size > (2047 << 20) && HOST_LONG_BITS == 32) { - error_report("at most 2047 MB RAM can be simulated"); - exit(1); + if (!have_custom_ram_size) { + backend_size = object_property_get_uint(backend, "size", &error_abort); + current_machine->ram_size = backend_size; } + object_property_set_link(OBJECT(current_machine), + "memory-backend", backend, &error_fatal); } } -static void set_memory_options(MachineClass *mc) +static void parse_memory_options(const char *arg) { - uint64_t sz; + QemuOpts *opts; + QDict *dict, *prop; const char *mem_str; - const ram_addr_t default_ram_size = mc->default_ram_size; - QemuOpts *opts = qemu_find_opts_singleton("memory"); - Location loc; - loc_push_none(&loc); - qemu_opts_loc_restore(opts); + opts = qemu_opts_parse_noisily(qemu_find_opts("memory"), arg, true); + if (!opts) { + exit(EXIT_FAILURE); + } + + prop = qdict_new(); - sz = 0; - mem_str = qemu_opt_get(opts, "size"); - if (mem_str) { + if (qemu_opt_get_size(opts, "size", 0) != 0) { + mem_str = qemu_opt_get(opts, "size"); if (!*mem_str) { error_report("missing 'size' option value"); exit(EXIT_FAILURE); } - sz = qemu_opt_get_size(opts, "size", ram_size); - /* Fix up legacy suffix-less format */ if (g_ascii_isdigit(mem_str[strlen(mem_str) - 1])) { - uint64_t overflow_check = sz; - - sz *= MiB; - if (sz / MiB != overflow_check) { - error_report("too large 'size' option value"); - exit(EXIT_FAILURE); - } + g_autofree char *mib_str = g_strdup_printf("%sM", mem_str); + qdict_put_str(prop, "size", mib_str); + } else { + qdict_put_str(prop, "size", mem_str); } } - /* backward compatibility behaviour for case "-m 0" */ - if (sz == 0) { - sz = default_ram_size; - } - - sz = QEMU_ALIGN_UP(sz, 8192); - if (mc->fixup_ram_size) { - sz = mc->fixup_ram_size(sz); - } - ram_size = sz; - if (ram_size != sz) { - error_report("ram size too large"); - exit(EXIT_FAILURE); - } - - maxram_size = ram_size; - if (qemu_opt_get(opts, "maxmem")) { - uint64_t slots; - - sz = qemu_opt_get_size(opts, "maxmem", 0); - slots = qemu_opt_get_number(opts, "slots", 0); - if (sz < ram_size) { - error_report("invalid value of -m option maxmem: " - "maximum memory size (0x%" PRIx64 ") must be at least " - "the initial memory size (0x" RAM_ADDR_FMT ")", - sz, ram_size); - exit(EXIT_FAILURE); - } else if (slots && sz == ram_size) { - error_report("invalid value of -m option maxmem: " - "memory slots were specified but maximum memory size " - "(0x%" PRIx64 ") is equal to the initial memory size " - "(0x" RAM_ADDR_FMT ")", sz, ram_size); - exit(EXIT_FAILURE); - } - - maxram_size = sz; - ram_slots = slots; - } else if (qemu_opt_get(opts, "slots")) { - error_report("invalid -m option value: missing 'maxmem' option"); - exit(EXIT_FAILURE); + qdict_put_str(prop, "max-size", qemu_opt_get(opts, "maxmem")); + } + if (qemu_opt_get(opts, "slots")) { + qdict_put_str(prop, "slots", qemu_opt_get(opts, "slots")); } - loc_pop(&loc); + dict = qdict_new(); + qdict_put(dict, "memory", prop); + keyval_merge(machine_opts_dict, dict, &error_fatal); + qobject_unref(dict); } static void qemu_create_machine(QDict *qdict) @@ -2152,8 +2083,6 @@ static void qemu_create_machine(QDict *qdict) MachineClass *machine_class = select_machine(qdict, &error_fatal); object_set_machine_compat_props(machine_class->compat_props); - set_memory_options(machine_class); - current_machine = MACHINE(object_new_with_class(OBJECT_CLASS(machine_class))); object_property_add_child(object_get_root(), "machine", OBJECT(current_machine)); @@ -2212,7 +2141,9 @@ static bool is_qemuopts_group(const char *group) { if (g_str_equal(group, "object") || g_str_equal(group, "machine") || - g_str_equal(group, "smp-opts")) { + g_str_equal(group, "smp-opts") || + g_str_equal(group, "boot-opts") || + g_str_equal(group, "memory")) { return false; } return true; @@ -2234,6 +2165,10 @@ static void qemu_record_config_group(const char *group, QDict *dict, keyval_merge(machine_opts_dict, dict, errp); } else if (g_str_equal(group, "smp-opts")) { machine_merge_property("smp", dict, &error_fatal); + } else if (g_str_equal(group, "boot-opts")) { + machine_merge_property("boot", dict, &error_fatal); + } else if (g_str_equal(group, "memory")) { + machine_merge_property("memory", dict, &error_fatal); } else { abort(); } @@ -2440,27 +2375,6 @@ static void configure_accelerators(const char *progname) } } -static void create_default_memdev(MachineState *ms, const char *path) -{ - Object *obj; - MachineClass *mc = MACHINE_GET_CLASS(ms); - - obj = object_new(path ? TYPE_MEMORY_BACKEND_FILE : TYPE_MEMORY_BACKEND_RAM); - if (path) { - object_property_set_str(obj, "mem-path", path, &error_fatal); - } - object_property_set_int(obj, "size", ms->ram_size, &error_fatal); - 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 */ - object_property_set_bool(obj, "x-use-canonical-path-for-ramblock-id", - false, &error_fatal); - user_creatable_complete(USER_CREATABLE(obj), &error_fatal); - object_unref(obj); - object_property_set_str(OBJECT(ms), "memory-backend", mc->default_ram_id, - &error_fatal); -} - static void qemu_validate_options(const QDict *machine_opts) { const char *kernel_filename = qdict_get_try_str(machine_opts, "kernel"); @@ -2645,18 +2559,11 @@ static void qemu_init_displays(void) static void qemu_init_board(void) { - MachineClass *machine_class = MACHINE_GET_CLASS(current_machine); - - if (machine_class->default_ram_id && current_machine->ram_size && - numa_uses_legacy_mem() && !current_machine->ram_memdev_id) { - create_default_memdev(current_machine, mem_path); - } - /* process plugin before CPUs are created, but once -smp has been parsed */ qemu_plugin_load_list(&plugin_list, &error_fatal); /* From here on we enter MACHINE_PHASE_INITIALIZED. */ - machine_run_board_init(current_machine); + machine_run_board_init(current_machine, mem_path, &error_fatal); drive_check_orphaned(); @@ -2979,11 +2886,7 @@ void qemu_init(int argc, char **argv, char **envp) drive_add(IF_DEFAULT, 2, optarg, CDROM_OPTS); break; case QEMU_OPTION_boot: - opts = qemu_opts_parse_noisily(qemu_find_opts("boot-opts"), - optarg, true); - if (!opts) { - exit(1); - } + machine_parse_property_opt(qemu_find_opts("boot-opts"), "boot", optarg); break; case QEMU_OPTION_fda: case QEMU_OPTION_fdb: @@ -3038,11 +2941,7 @@ void qemu_init(int argc, char **argv, char **envp) exit(0); break; case QEMU_OPTION_m: - opts = qemu_opts_parse_noisily(qemu_find_opts("memory"), - optarg, true); - if (!opts) { - exit(EXIT_FAILURE); - } + parse_memory_options(optarg); break; #ifdef CONFIG_TPM case QEMU_OPTION_tpmdev: @@ -3732,7 +3631,7 @@ void qemu_init(int argc, char **argv, char **envp) machine_class = MACHINE_GET_CLASS(current_machine); if (!qtest_enabled() && machine_class->deprecation_reason) { - error_report("Machine type '%s' is deprecated: %s", + warn_report("Machine type '%s' is deprecated: %s", machine_class->name, machine_class->deprecation_reason); } diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 62c240f..c4a17c9 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5022,6 +5022,37 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w, return r; } +static void x86_cpu_get_cache_cpuid(uint32_t func, uint32_t index, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + uint32_t level, unused; + + /* Only return valid host leaves. */ + switch (func) { + case 2: + case 4: + host_cpuid(0, 0, &level, &unused, &unused, &unused); + break; + case 0x80000005: + case 0x80000006: + case 0x8000001d: + host_cpuid(0x80000000, 0, &level, &unused, &unused, &unused); + break; + default: + return; + } + + if (func > level) { + *eax = 0; + *ebx = 0; + *ecx = 0; + *edx = 0; + } else { + host_cpuid(func, index, eax, ebx, ecx, edx); + } +} + /* * Only for builtin_x86_defs models initialized with x86_register_cpudef_types. */ @@ -5280,7 +5311,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 2: /* cache info: needed for Pentium Pro compatibility */ if (cpu->cache_info_passthrough) { - host_cpuid(index, 0, eax, ebx, ecx, edx); + x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; } else if (cpu->vendor_cpuid_only && IS_AMD_CPU(env)) { *eax = *ebx = *ecx = *edx = 0; @@ -5300,7 +5331,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 4: /* cache info: needed for Core compatibility */ if (cpu->cache_info_passthrough) { - host_cpuid(index, count, eax, ebx, ecx, edx); + x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx); /* QEMU gives out its own APIC IDs, never pass down bits 31..26. */ *eax &= ~0xFC000000; if ((*eax & 31) && cs->nr_cores > 1) { @@ -5702,7 +5733,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 0x80000005: /* cache info (L1 cache) */ if (cpu->cache_info_passthrough) { - host_cpuid(index, 0, eax, ebx, ecx, edx); + x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; } *eax = (L1_DTLB_2M_ASSOC << 24) | (L1_DTLB_2M_ENTRIES << 16) | @@ -5715,7 +5746,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 0x80000006: /* cache info (L2 cache) */ if (cpu->cache_info_passthrough) { - host_cpuid(index, 0, eax, ebx, ecx, edx); + x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; } *eax = (AMD_ENC_ASSOC(L2_DTLB_2M_ASSOC) << 28) | @@ -5775,7 +5806,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, case 0x8000001D: *eax = 0; if (cpu->cache_info_passthrough) { - host_cpuid(index, count, eax, ebx, ecx, edx); + x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx); break; } switch (count) { diff --git a/tests/qtest/e1000e-test.c b/tests/qtest/e1000e-test.c index ddd6983..c98779c 100644 --- a/tests/qtest/e1000e-test.c +++ b/tests/qtest/e1000e-test.c @@ -233,6 +233,12 @@ static void test_e1000e_multiple_transfers(void *obj, void *data, static void test_e1000e_hotplug(void *obj, void *data, QGuestAllocator * alloc) { QTestState *qts = global_qtest; /* TODO: get rid of global_qtest here */ + QE1000E_PCI *dev = obj; + + if (dev->pci_dev.bus->not_hotpluggable) { + g_test_skip("pci bus does not support hotplug"); + return; + } qtest_qmp_device_add(qts, "e1000e", "e1000e_net", "{'addr': '0x06'}"); qpci_unplug_acpi_device_test(qts, "e1000e_net", 0x06); diff --git a/tests/qtest/libqos/arm-virt-machine.c b/tests/qtest/libqos/arm-virt-machine.c index 2e0beae..139eaba 100644 --- a/tests/qtest/libqos/arm-virt-machine.c +++ b/tests/qtest/libqos/arm-virt-machine.c @@ -22,6 +22,8 @@ #include "malloc.h" #include "qgraph.h" #include "virtio-mmio.h" +#include "generic-pcihost.h" +#include "hw/pci/pci_regs.h" #define ARM_PAGE_SIZE 4096 #define VIRTIO_MMIO_BASE_ADDR 0x0A003E00 @@ -35,6 +37,7 @@ struct QVirtMachine { QOSGraphObject obj; QGuestAllocator alloc; QVirtioMMIODevice virtio_mmio; + QGenericPCIHost bridge; }; static void virt_destructor(QOSGraphObject *obj) @@ -57,11 +60,13 @@ static void *virt_get_driver(void *object, const char *interface) static QOSGraphObject *virt_get_device(void *obj, const char *device) { QVirtMachine *machine = obj; - if (!g_strcmp0(device, "virtio-mmio")) { + if (!g_strcmp0(device, "generic-pcihost")) { + return &machine->bridge.obj; + } else if (!g_strcmp0(device, "virtio-mmio")) { return &machine->virtio_mmio.obj; } - fprintf(stderr, "%s not present in arm/virtio\n", device); + fprintf(stderr, "%s not present in arm/virt\n", device); g_assert_not_reached(); } @@ -76,16 +81,22 @@ static void *qos_create_machine_arm_virt(QTestState *qts) qvirtio_mmio_init_device(&machine->virtio_mmio, qts, VIRTIO_MMIO_BASE_ADDR, VIRTIO_MMIO_SIZE); + qos_create_generic_pcihost(&machine->bridge, qts, &machine->alloc); + machine->obj.get_device = virt_get_device; machine->obj.get_driver = virt_get_driver; machine->obj.destructor = virt_destructor; return machine; } -static void virtio_mmio_register_nodes(void) +static void virt_machine_register_nodes(void) { qos_node_create_machine("arm/virt", qos_create_machine_arm_virt); qos_node_contains("arm/virt", "virtio-mmio", NULL); + + qos_node_create_machine_args("aarch64/virt", qos_create_machine_arm_virt, + " -cpu max"); + qos_node_contains("aarch64/virt", "generic-pcihost", NULL); } -libqos_init(virtio_mmio_register_nodes); +libqos_init(virt_machine_register_nodes); diff --git a/tests/qtest/libqos/generic-pcihost.c b/tests/qtest/libqos/generic-pcihost.c new file mode 100644 index 0000000..3124b0e --- /dev/null +++ b/tests/qtest/libqos/generic-pcihost.c @@ -0,0 +1,231 @@ +/* + * libqos PCI bindings for generic PCI + * + * Copyright Red Hat Inc., 2022 + * + * Authors: + * Eric Auger <eric.auger@redhat.com> + * + * 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 "../libqtest.h" +#include "generic-pcihost.h" +#include "qapi/qmp/qdict.h" +#include "hw/pci/pci_regs.h" +#include "qemu/host-utils.h" + +#include "qemu/module.h" + +/* QGenericPCIHost */ + +QOSGraphObject *generic_pcihost_get_device(void *obj, const char *device) +{ + QGenericPCIHost *host = obj; + if (!g_strcmp0(device, "pci-bus-generic")) { + return &host->pci.obj; + } + fprintf(stderr, "%s not present in generic-pcihost\n", device); + g_assert_not_reached(); +} + +void qos_create_generic_pcihost(QGenericPCIHost *host, + QTestState *qts, + QGuestAllocator *alloc) +{ + host->obj.get_device = generic_pcihost_get_device; + qpci_init_generic(&host->pci, qts, alloc, false); +} + +static uint8_t qpci_generic_pio_readb(QPCIBus *bus, uint32_t addr) +{ + QGenericPCIBus *s = container_of(bus, QGenericPCIBus, bus); + + return qtest_readb(bus->qts, s->gpex_pio_base + addr); +} + +static void qpci_generic_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) +{ + QGenericPCIBus *s = container_of(bus, QGenericPCIBus, bus); + + qtest_writeb(bus->qts, s->gpex_pio_base + addr, val); +} + +static uint16_t qpci_generic_pio_readw(QPCIBus *bus, uint32_t addr) +{ + QGenericPCIBus *s = container_of(bus, QGenericPCIBus, bus); + + return qtest_readw(bus->qts, s->gpex_pio_base + addr); +} + +static void qpci_generic_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) +{ + QGenericPCIBus *s = container_of(bus, QGenericPCIBus, bus); + + qtest_writew(bus->qts, s->gpex_pio_base + addr, val); +} + +static uint32_t qpci_generic_pio_readl(QPCIBus *bus, uint32_t addr) +{ + QGenericPCIBus *s = container_of(bus, QGenericPCIBus, bus); + + return qtest_readl(bus->qts, s->gpex_pio_base + addr); +} + +static void qpci_generic_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) +{ + QGenericPCIBus *s = container_of(bus, QGenericPCIBus, bus); + + qtest_writel(bus->qts, s->gpex_pio_base + addr, val); +} + +static uint64_t qpci_generic_pio_readq(QPCIBus *bus, uint32_t addr) +{ + QGenericPCIBus *s = container_of(bus, QGenericPCIBus, bus); + + return qtest_readq(bus->qts, s->gpex_pio_base + addr); +} + +static void qpci_generic_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) +{ + QGenericPCIBus *s = container_of(bus, QGenericPCIBus, bus); + + qtest_writeq(bus->qts, s->gpex_pio_base + addr, val); +} + +static void qpci_generic_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len) +{ + qtest_memread(bus->qts, addr, buf, len); +} + +static void qpci_generic_memwrite(QPCIBus *bus, uint32_t addr, + const void *buf, size_t len) +{ + qtest_memwrite(bus->qts, addr, buf, len); +} + +static uint8_t qpci_generic_config_readb(QPCIBus *bus, int devfn, uint8_t offset) +{ + QGenericPCIBus *gbus = container_of(bus, QGenericPCIBus, bus); + uint64_t addr = gbus->ecam_alloc_ptr + ((0 << 20) | (devfn << 12) | offset); + uint8_t val; + + qtest_memread(bus->qts, addr, &val, 1); + return val; +} + +static uint16_t qpci_generic_config_readw(QPCIBus *bus, int devfn, uint8_t offset) +{ + QGenericPCIBus *gbus = container_of(bus, QGenericPCIBus, bus); + uint64_t addr = gbus->ecam_alloc_ptr + ((0 << 20) | (devfn << 12) | offset); + uint16_t val; + + qtest_memread(bus->qts, addr, &val, 2); + return le16_to_cpu(val); +} + +static uint32_t qpci_generic_config_readl(QPCIBus *bus, int devfn, uint8_t offset) +{ + QGenericPCIBus *gbus = container_of(bus, QGenericPCIBus, bus); + uint64_t addr = gbus->ecam_alloc_ptr + ((0 << 20) | (devfn << 12) | offset); + uint32_t val; + + qtest_memread(bus->qts, addr, &val, 4); + return le32_to_cpu(val); +} + +static void +qpci_generic_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value) +{ + QGenericPCIBus *gbus = container_of(bus, QGenericPCIBus, bus); + uint64_t addr = gbus->ecam_alloc_ptr + ((0 << 20) | (devfn << 12) | offset); + + qtest_memwrite(bus->qts, addr, &value, 1); +} + +static void +qpci_generic_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value) +{ + QGenericPCIBus *gbus = container_of(bus, QGenericPCIBus, bus); + uint64_t addr = gbus->ecam_alloc_ptr + ((0 << 20) | (devfn << 12) | offset); + uint16_t val = cpu_to_le16(value); + + qtest_memwrite(bus->qts, addr, &val, 2); +} + +static void +qpci_generic_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value) +{ + QGenericPCIBus *gbus = container_of(bus, QGenericPCIBus, bus); + uint64_t addr = gbus->ecam_alloc_ptr + ((0 << 20) | (devfn << 12) | offset); + uint32_t val = cpu_to_le32(value); + + qtest_memwrite(bus->qts, addr, &val, 4); +} + +static void *qpci_generic_get_driver(void *obj, const char *interface) +{ + QGenericPCIBus *qpci = obj; + if (!g_strcmp0(interface, "pci-bus")) { + return &qpci->bus; + } + fprintf(stderr, "%s not present in pci-bus-generic\n", interface); + g_assert_not_reached(); +} + +void qpci_init_generic(QGenericPCIBus *qpci, QTestState *qts, + QGuestAllocator *alloc, bool hotpluggable) +{ + assert(qts); + + qpci->gpex_pio_base = 0x3eff0000; + qpci->bus.not_hotpluggable = !hotpluggable; + qpci->bus.has_buggy_msi = false; + + qpci->bus.pio_readb = qpci_generic_pio_readb; + qpci->bus.pio_readw = qpci_generic_pio_readw; + qpci->bus.pio_readl = qpci_generic_pio_readl; + qpci->bus.pio_readq = qpci_generic_pio_readq; + + qpci->bus.pio_writeb = qpci_generic_pio_writeb; + qpci->bus.pio_writew = qpci_generic_pio_writew; + qpci->bus.pio_writel = qpci_generic_pio_writel; + qpci->bus.pio_writeq = qpci_generic_pio_writeq; + + qpci->bus.memread = qpci_generic_memread; + qpci->bus.memwrite = qpci_generic_memwrite; + + qpci->bus.config_readb = qpci_generic_config_readb; + qpci->bus.config_readw = qpci_generic_config_readw; + qpci->bus.config_readl = qpci_generic_config_readl; + + qpci->bus.config_writeb = qpci_generic_config_writeb; + qpci->bus.config_writew = qpci_generic_config_writew; + qpci->bus.config_writel = qpci_generic_config_writel; + + qpci->bus.qts = qts; + qpci->bus.pio_alloc_ptr = 0x0000; + qpci->bus.pio_limit = 0x10000; + qpci->bus.mmio_alloc_ptr = 0x10000000; + qpci->bus.mmio_limit = 0x2eff0000; + qpci->ecam_alloc_ptr = 0x4010000000; + + qpci->obj.get_driver = qpci_generic_get_driver; +} + +static void qpci_generic_register_nodes(void) +{ + qos_node_create_driver("pci-bus-generic", NULL); + qos_node_produces("pci-bus-generic", "pci-bus"); +} + +static void qpci_generic_pci_register_nodes(void) +{ + qos_node_create_driver("generic-pcihost", NULL); + qos_node_contains("generic-pcihost", "pci-bus-generic", NULL); +} + +libqos_init(qpci_generic_register_nodes); +libqos_init(qpci_generic_pci_register_nodes); diff --git a/tests/qtest/libqos/generic-pcihost.h b/tests/qtest/libqos/generic-pcihost.h new file mode 100644 index 0000000..c693c76 --- /dev/null +++ b/tests/qtest/libqos/generic-pcihost.h @@ -0,0 +1,54 @@ +/* + * libqos Generic PCI bindings and generic pci host bridge + * + * Copyright Red Hat Inc., 2022 + * + * Authors: + * Eric Auger <eric.auger@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef LIBQOS_GENERIC_PCIHOST_H +#define LIBQOS_GENERIC_PCIHOST_H + +#include "pci.h" +#include "malloc.h" +#include "qgraph.h" + +typedef struct QGenericPCIBus { + QOSGraphObject obj; + QPCIBus bus; + uint64_t gpex_pio_base; + uint64_t ecam_alloc_ptr; +} QGenericPCIBus; + +/* + * qpci_init_generic(): + * @ret: A valid QGenericPCIBus * pointer + * @qts: The %QTestState + * @alloc: A previously initialized @alloc providing memory for @qts + * @bool: devices can be hotplugged on this bus + * + * This function initializes an already allocated + * QGenericPCIBus object. + */ +void qpci_init_generic(QGenericPCIBus *ret, QTestState *qts, + QGuestAllocator *alloc, bool hotpluggable); + +/* QGenericPCIHost */ + +typedef struct QGenericPCIHost QGenericPCIHost; + +struct QGenericPCIHost { + QOSGraphObject obj; + QGenericPCIBus pci; +}; + +QOSGraphObject *generic_pcihost_get_device(void *obj, const char *device); +void qos_create_generic_pcihost(QGenericPCIHost *host, + QTestState *qts, + QGuestAllocator *alloc); + +#endif diff --git a/tests/qtest/libqos/meson.build b/tests/qtest/libqos/meson.build index 9f29233..fd5d6e5 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -45,6 +45,7 @@ libqos_srcs = files( 'virtio-scsi.c', 'virtio-serial.c', 'virtio-iommu.c', + 'generic-pcihost.c', # qgraph machines: 'aarch64-xlnx-zcu102-machine.c', diff --git a/tests/qtest/libqos/pci-pc.c b/tests/qtest/libqos/pci-pc.c index e9dd5a5..81c2c05 100644 --- a/tests/qtest/libqos/pci-pc.c +++ b/tests/qtest/libqos/pci-pc.c @@ -150,6 +150,7 @@ void qpci_init_pc(QPCIBusPC *qpci, QTestState *qts, QGuestAllocator *alloc) qpci->bus.qts = qts; qpci->bus.pio_alloc_ptr = 0xc000; + qpci->bus.pio_limit = 0x10000; qpci->bus.mmio_alloc_ptr = 0xE0000000; qpci->bus.mmio_limit = 0x100000000ULL; diff --git a/tests/qtest/libqos/pci-spapr.c b/tests/qtest/libqos/pci-spapr.c index 76bf9a8..0f1023e 100644 --- a/tests/qtest/libqos/pci-spapr.c +++ b/tests/qtest/libqos/pci-spapr.c @@ -197,6 +197,7 @@ void qpci_init_spapr(QPCIBusSPAPR *qpci, QTestState *qts, qpci->bus.qts = qts; qpci->bus.pio_alloc_ptr = 0xc000; + qpci->bus.pio_limit = 0x10000; qpci->bus.mmio_alloc_ptr = qpci->mmio32.pci_base; qpci->bus.mmio_limit = qpci->mmio32.pci_base + qpci->mmio32.size; diff --git a/tests/qtest/libqos/pci.c b/tests/qtest/libqos/pci.c index 3a9076a..b23d723 100644 --- a/tests/qtest/libqos/pci.c +++ b/tests/qtest/libqos/pci.c @@ -398,44 +398,56 @@ void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value) uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off) { - if (token.addr < QPCI_PIO_LIMIT) { - return dev->bus->pio_readb(dev->bus, token.addr + off); + QPCIBus *bus = dev->bus; + + if (token.is_io) { + return bus->pio_readb(bus, token.addr + off); } else { uint8_t val; - dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + + bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); return val; } } uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off) { - if (token.addr < QPCI_PIO_LIMIT) { - return dev->bus->pio_readw(dev->bus, token.addr + off); + QPCIBus *bus = dev->bus; + + if (token.is_io) { + return bus->pio_readw(bus, token.addr + off); } else { uint16_t val; - dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + + bus->memread(bus, token.addr + off, &val, sizeof(val)); return le16_to_cpu(val); } } uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off) { - if (token.addr < QPCI_PIO_LIMIT) { - return dev->bus->pio_readl(dev->bus, token.addr + off); + QPCIBus *bus = dev->bus; + + if (token.is_io) { + return bus->pio_readl(bus, token.addr + off); } else { uint32_t val; - dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + + bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); return le32_to_cpu(val); } } uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off) { - if (token.addr < QPCI_PIO_LIMIT) { - return dev->bus->pio_readq(dev->bus, token.addr + off); + QPCIBus *bus = dev->bus; + + if (token.is_io) { + return bus->pio_readq(bus, token.addr + off); } else { uint64_t val; - dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + + bus->memread(bus, token.addr + off, &val, sizeof(val)); return le64_to_cpu(val); } } @@ -443,57 +455,65 @@ uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off) void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off, uint8_t value) { - if (token.addr < QPCI_PIO_LIMIT) { - dev->bus->pio_writeb(dev->bus, token.addr + off, value); + QPCIBus *bus = dev->bus; + + if (token.is_io) { + bus->pio_writeb(bus, token.addr + off, value); } else { - dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + bus->memwrite(bus, token.addr + off, &value, sizeof(value)); } } void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off, uint16_t value) { - if (token.addr < QPCI_PIO_LIMIT) { - dev->bus->pio_writew(dev->bus, token.addr + off, value); + QPCIBus *bus = dev->bus; + + if (token.is_io) { + bus->pio_writew(bus, token.addr + off, value); } else { value = cpu_to_le16(value); - dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + bus->memwrite(bus, token.addr + off, &value, sizeof(value)); } } void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off, uint32_t value) { - if (token.addr < QPCI_PIO_LIMIT) { - dev->bus->pio_writel(dev->bus, token.addr + off, value); + QPCIBus *bus = dev->bus; + + if (token.is_io) { + bus->pio_writel(bus, token.addr + off, value); } else { value = cpu_to_le32(value); - dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + bus->memwrite(bus, token.addr + off, &value, sizeof(value)); } } void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off, uint64_t value) { - if (token.addr < QPCI_PIO_LIMIT) { - dev->bus->pio_writeq(dev->bus, token.addr + off, value); + QPCIBus *bus = dev->bus; + + if (token.is_io) { + bus->pio_writeq(bus, token.addr + off, value); } else { value = cpu_to_le64(value); - dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + bus->memwrite(bus, token.addr + off, &value, sizeof(value)); } } void qpci_memread(QPCIDevice *dev, QPCIBar token, uint64_t off, void *buf, size_t len) { - g_assert(token.addr >= QPCI_PIO_LIMIT); + g_assert(!token.is_io); dev->bus->memread(dev->bus, token.addr + off, buf, len); } void qpci_memwrite(QPCIDevice *dev, QPCIBar token, uint64_t off, const void *buf, size_t len) { - g_assert(token.addr >= QPCI_PIO_LIMIT); + g_assert(!token.is_io); dev->bus->memwrite(dev->bus, token.addr + off, buf, len); } @@ -534,9 +554,10 @@ QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr) loc = QEMU_ALIGN_UP(bus->pio_alloc_ptr, size); g_assert(loc >= bus->pio_alloc_ptr); - g_assert(loc + size <= QPCI_PIO_LIMIT); /* Keep PIO below 64kiB */ + g_assert(loc + size <= bus->pio_limit); bus->pio_alloc_ptr = loc + size; + bar.is_io = true; qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); } else { @@ -547,6 +568,7 @@ QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr) g_assert(loc + size <= bus->mmio_limit); bus->mmio_alloc_ptr = loc + size; + bar.is_io = false; qpci_config_writel(dev, bar_reg, loc); } @@ -562,7 +584,7 @@ void qpci_iounmap(QPCIDevice *dev, QPCIBar bar) QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr) { - QPCIBar bar = { .addr = addr }; + QPCIBar bar = { .addr = addr, .is_io = true }; return bar; } diff --git a/tests/qtest/libqos/pci.h b/tests/qtest/libqos/pci.h index e705e06..8389614 100644 --- a/tests/qtest/libqos/pci.h +++ b/tests/qtest/libqos/pci.h @@ -16,8 +16,6 @@ #include "../libqtest.h" #include "qgraph.h" -#define QPCI_PIO_LIMIT 0x10000 - #define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn)) typedef struct QPCIDevice QPCIDevice; @@ -51,14 +49,16 @@ struct QPCIBus { uint8_t offset, uint32_t value); QTestState *qts; - uint16_t pio_alloc_ptr; + uint64_t pio_alloc_ptr, pio_limit; uint64_t mmio_alloc_ptr, mmio_limit; bool has_buggy_msi; /* TRUE for spapr, FALSE for pci */ + bool not_hotpluggable; /* TRUE if devices cannot be hotplugged */ }; struct QPCIBar { uint64_t addr; + bool is_io; }; struct QPCIDevice diff --git a/tests/qtest/vhost-user-blk-test.c b/tests/qtest/vhost-user-blk-test.c index 659b505..a81c2a2 100644 --- a/tests/qtest/vhost-user-blk-test.c +++ b/tests/qtest/vhost-user-blk-test.c @@ -676,6 +676,11 @@ static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc) QVirtioPCIDevice *dev; QTestState *qts = dev1->pdev->bus->qts; + if (dev1->pdev->bus->not_hotpluggable) { + g_test_skip("pci bus does not support hotplug"); + return; + } + /* plug secondary disk */ qtest_qmp_device_add(qts, "vhost-user-blk-pci", "drv1", "{'addr': %s, 'chardev': 'char2'}", @@ -703,6 +708,11 @@ static void multiqueue(void *obj, void *data, QGuestAllocator *t_alloc) uint64_t features; uint16_t num_queues; + if (pdev1->pdev->bus->not_hotpluggable) { + g_test_skip("bus pci.0 does not support hotplug"); + return; + } + /* * The primary device has 1 queue and VIRTIO_BLK_F_MQ is not enabled. The * VIRTIO specification allows VIRTIO_BLK_F_MQ to be enabled when there is diff --git a/tests/qtest/virtio-blk-test.c b/tests/qtest/virtio-blk-test.c index f22594a..dc5eed3 100644 --- a/tests/qtest/virtio-blk-test.c +++ b/tests/qtest/virtio-blk-test.c @@ -701,6 +701,11 @@ static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc) QVirtioPCIDevice *dev; QTestState *qts = dev1->pdev->bus->qts; + if (dev1->pdev->bus->not_hotpluggable) { + g_test_skip("pci bus does not support hotplug"); + return; + } + /* plug secondary disk */ qtest_qmp_device_add(qts, "virtio-blk-pci", "drv1", "{'addr': %s, 'drive': 'drive1'}", diff --git a/tests/qtest/virtio-net-test.c b/tests/qtest/virtio-net-test.c index fc9f2b9..6ded252 100644 --- a/tests/qtest/virtio-net-test.c +++ b/tests/qtest/virtio-net-test.c @@ -173,6 +173,11 @@ static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) QTestState *qts = dev->pdev->bus->qts; const char *arch = qtest_get_arch(); + if (dev->pdev->bus->not_hotpluggable) { + g_test_skip("pci bus does not support hotplug"); + return; + } + qtest_qmp_device_add(qts, "virtio-net-pci", "net1", "{'addr': %s}", stringify(PCI_SLOT_HP)); diff --git a/tests/qtest/virtio-rng-test.c b/tests/qtest/virtio-rng-test.c index 092ba13..64e131c 100644 --- a/tests/qtest/virtio-rng-test.c +++ b/tests/qtest/virtio-rng-test.c @@ -20,6 +20,11 @@ static void rng_hotplug(void *obj, void *data, QGuestAllocator *alloc) QVirtioPCIDevice *dev = obj; QTestState *qts = dev->pdev->bus->qts; + if (dev->pdev->bus->not_hotpluggable) { + g_test_skip("pci bus does not support hotplug"); + return; + } + const char *arch = qtest_get_arch(); qtest_qmp_device_add(qts, "virtio-rng-pci", "rng1", diff --git a/ui/console.c b/ui/console.c index 15d0f6a..36c80cd 100644 --- a/ui/console.c +++ b/ui/console.c @@ -221,7 +221,7 @@ static void gui_setup_refresh(DisplayState *ds) void graphic_hw_update_done(QemuConsole *con) { if (con) { - qemu_co_queue_restart_all(&con->dump_queue); + qemu_co_enter_all(&con->dump_queue, NULL); } } diff --git a/ui/meson.build b/ui/meson.build index eba93b4..e9f48c5 100644 --- a/ui/meson.build +++ b/ui/meson.build @@ -2,6 +2,7 @@ softmmu_ss.add(pixman) specific_ss.add(when: ['CONFIG_SOFTMMU'], if_true: pixman) # for the include path specific_ss.add(when: ['CONFIG_SOFTMMU'], if_true: opengl) # for the include path +softmmu_ss.add(png) softmmu_ss.add(files( 'clipboard.c', 'console.c', @@ -40,7 +41,7 @@ vnc_ss.add(files( 'vnc-jobs.c', 'vnc-clipboard.c', )) -vnc_ss.add(zlib, png, jpeg, gnutls) +vnc_ss.add(zlib, jpeg, gnutls) vnc_ss.add(when: sasl, if_true: files('vnc-auth-sasl.c')) softmmu_ss.add_all(when: vnc, if_true: vnc_ss) softmmu_ss.add(when: vnc, if_false: files('vnc-stubs.c')) diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c index 2669403..9ad24ab 100644 --- a/util/qemu-coroutine-lock.c +++ b/util/qemu-coroutine-lock.c @@ -67,34 +67,6 @@ void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock) } } -static bool qemu_co_queue_do_restart(CoQueue *queue, bool single) -{ - Coroutine *next; - - if (QSIMPLEQ_EMPTY(&queue->entries)) { - return false; - } - - while ((next = QSIMPLEQ_FIRST(&queue->entries)) != NULL) { - QSIMPLEQ_REMOVE_HEAD(&queue->entries, co_queue_next); - aio_co_wake(next); - if (single) { - break; - } - } - return true; -} - -bool qemu_co_queue_next(CoQueue *queue) -{ - return qemu_co_queue_do_restart(queue, true); -} - -void qemu_co_queue_restart_all(CoQueue *queue) -{ - qemu_co_queue_do_restart(queue, false); -} - bool qemu_co_enter_next_impl(CoQueue *queue, QemuLockable *lock) { Coroutine *next; @@ -115,6 +87,25 @@ bool qemu_co_enter_next_impl(CoQueue *queue, QemuLockable *lock) return true; } +bool coroutine_fn qemu_co_queue_next(CoQueue *queue) +{ + /* No unlock/lock needed in coroutine context. */ + return qemu_co_enter_next_impl(queue, NULL); +} + +void qemu_co_enter_all_impl(CoQueue *queue, QemuLockable *lock) +{ + while (qemu_co_enter_next_impl(queue, lock)) { + /* just loop */ + } +} + +void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue) +{ + /* No unlock/lock needed in coroutine context. */ + qemu_co_enter_all_impl(queue, NULL); +} + bool qemu_co_queue_empty(CoQueue *queue) { return QSIMPLEQ_FIRST(&queue->entries) == NULL; |