From 35658f6e0c3dacfdb22198ee3917c0c28914d81d Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Fri, 10 Jun 2016 04:15:41 -0500 Subject: ipmi: Add SMBIOS table entry Add an IPMI table entry to the SMBIOS. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/smbios/ipmi.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 include/hw/smbios/ipmi.h (limited to 'include') diff --git a/include/hw/smbios/ipmi.h b/include/hw/smbios/ipmi.h new file mode 100644 index 0000000000..1c9aae38f2 --- /dev/null +++ b/include/hw/smbios/ipmi.h @@ -0,0 +1,15 @@ +/* + * IPMI SMBIOS firmware handling + * + * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC + * + * 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 QEMU_SMBIOS_IPMI_H +#define QEMU_SMBIOS_IPMI_H + +void smbios_build_type_38_table(void); + +#endif /* QEMU_SMBIOS_IPMI_H */ -- cgit v1.2.3-55-g7522 From 86e91dd713414dda40bb16aabe2f1fc9ba95a3a7 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Fri, 10 Jun 2016 04:15:42 -0500 Subject: acpi: Add IPMI table entries Use the ACPI table construction tools to create an ACPI entry for IPMI. This adds a function called build_acpi_ipmi_devices to add an DSDT entry for IPMI if IPMI is compiled in and an IPMI device exists. It also adds a dummy function if IPMI is not compiled in. This conforms to section "C3-2 Locating IPMI System Interfaces in ACPI Name Space" in the IPMI 2.0 specification. Signed-off-by: Corey Minyard Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/Makefile.objs | 1 + hw/acpi/ipmi.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/i386/acpi-build.c | 12 ++++++ include/hw/acpi/ipmi.h | 22 +++++++++++ stubs/Makefile.objs | 1 + stubs/ipmi.c | 14 +++++++ 6 files changed, 155 insertions(+) create mode 100644 hw/acpi/ipmi.c create mode 100644 include/hw/acpi/ipmi.h create mode 100644 stubs/ipmi.c (limited to 'include') diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 66bd72702b..1a48f61676 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -6,3 +6,4 @@ obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o common-obj-$(CONFIG_ACPI) += acpi_interface.o common-obj-$(CONFIG_ACPI) += bios-linker-loader.o common-obj-$(CONFIG_ACPI) += aml-build.o +common-obj-$(call land,$(CONFIG_ACPI),$(CONFIG_IPMI)) += ipmi.o diff --git a/hw/acpi/ipmi.c b/hw/acpi/ipmi.c new file mode 100644 index 0000000000..7e74ce4460 --- /dev/null +++ b/hw/acpi/ipmi.c @@ -0,0 +1,105 @@ +/* + * IPMI ACPI firmware handling + * + * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC + * + * 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/ipmi/ipmi.h" +#include "hw/acpi/aml-build.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/ipmi.h" + +static Aml *aml_ipmi_crs(IPMIFwInfo *info) +{ + Aml *crs = aml_resource_template(); + + /* + * The base address is fixed and cannot change. That may be different + * if someone does PCI, but we aren't there yet. + */ + switch (info->memspace) { + case IPMI_MEMSPACE_IO: + aml_append(crs, aml_io(AML_DECODE16, info->base_address, + info->base_address + info->register_length - 1, + info->register_spacing, info->register_length)); + break; + case IPMI_MEMSPACE_MEM32: + aml_append(crs, + aml_dword_memory(AML_POS_DECODE, + AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, + 0xffffffff, + info->base_address, + info->base_address + info->register_length - 1, + info->register_spacing, info->register_length)); + break; + case IPMI_MEMSPACE_MEM64: + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, + AML_MIN_FIXED, AML_MAX_FIXED, + AML_NON_CACHEABLE, AML_READ_WRITE, + 0xffffffffffffffffULL, + info->base_address, + info->base_address + info->register_length - 1, + info->register_spacing, info->register_length)); + break; + case IPMI_MEMSPACE_SMBUS: + aml_append(crs, aml_return(aml_int(info->base_address))); + break; + default: + abort(); + } + + if (info->interrupt_number) { + aml_append(crs, aml_irq_no_flags(info->interrupt_number)); + } + + return crs; +} + +static Aml *aml_ipmi_device(IPMIFwInfo *info) +{ + Aml *dev; + uint16_t version = ((info->ipmi_spec_major_revision << 8) + | (info->ipmi_spec_minor_revision << 4)); + + assert(info->ipmi_spec_minor_revision <= 15); + + dev = aml_device("MI%d", info->uuid); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("IPI0001"))); + aml_append(dev, aml_name_decl("_STR", aml_string("ipmi_%s", + info->interface_name))); + aml_append(dev, aml_name_decl("_UID", aml_int(info->uuid))); + aml_append(dev, aml_name_decl("_CRS", aml_ipmi_crs(info))); + aml_append(dev, aml_name_decl("_IFT", aml_int(info->interface_type))); + aml_append(dev, aml_name_decl("_SRV", aml_int(version))); + + return dev; +} + +void build_acpi_ipmi_devices(Aml *scope, BusState *bus) +{ + + BusChild *kid; + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + IPMIInterface *ii; + IPMIInterfaceClass *iic; + IPMIFwInfo info; + Object *obj = object_dynamic_cast(OBJECT(kid->child), + TYPE_IPMI_INTERFACE); + + if (!obj) { + continue; + } + + ii = IPMI_INTERFACE(obj); + iic = IPMI_INTERFACE_GET_CLASS(obj); + iic->get_fwinfo(ii, &info); + aml_append(scope, aml_ipmi_device(&info)); + } +} diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 8ca203211a..b3dc1df25a 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -58,6 +58,8 @@ #include "qapi/qmp/qint.h" #include "qom/qom-qobject.h" +#include "hw/acpi/ipmi.h" + /* These are used to size the ACPI tables for -M pc-i440fx-1.7 and * -M pc-i440fx-2.0. Even if the actual amount of AML generated grows * a little bit, there should be plenty of free space since the DSDT @@ -1334,8 +1336,10 @@ static Aml *build_com_device_aml(uint8_t uid) static void build_isa_devices_aml(Aml *table) { ISADevice *fdc = pc_find_fdc0(); + bool ambiguous; Aml *scope = aml_scope("_SB.PCI0.ISA"); + Object *obj = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous); aml_append(scope, build_rtc_device_aml()); aml_append(scope, build_kbd_device_aml()); @@ -1347,6 +1351,14 @@ static void build_isa_devices_aml(Aml *table) aml_append(scope, build_com_device_aml(1)); aml_append(scope, build_com_device_aml(2)); + if (ambiguous) { + error_report("Multiple ISA busses, unable to define IPMI ACPI data"); + } else if (!obj) { + error_report("No ISA bus, unable to define IPMI ACPI data"); + } else { + build_acpi_ipmi_devices(scope, BUS(obj)); + } + aml_append(table, scope); } diff --git a/include/hw/acpi/ipmi.h b/include/hw/acpi/ipmi.h new file mode 100644 index 0000000000..ab2bb29048 --- /dev/null +++ b/include/hw/acpi/ipmi.h @@ -0,0 +1,22 @@ +/* + * QEMU IPMI ACPI handling + * + * Copyright (c) 2015,2016 Corey Minyard + * + * 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 HW_ACPI_IPMI_H +#define HW_ACPI_IPMI_H + +#include "qemu/osdep.h" +#include "hw/acpi/aml-build.h" + +/* + * Add ACPI IPMI entries for all registered IPMI devices whose parent + * bus matches the given bus. The resource is the ACPI resource that + * contains the IPMI device, this is required for the I2C CRS. + */ +void build_acpi_ipmi_devices(Aml *table, BusState *bus); + +#endif /* HW_ACPI_IPMI_H */ diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index b21cd5f77d..b829ee6e1a 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -42,3 +42,4 @@ stub-obj-y += target-get-monitor-def.o stub-obj-y += vhost.o stub-obj-y += iohandler.o stub-obj-y += smbios_type_38.o +stub-obj-y += ipmi.o diff --git a/stubs/ipmi.c b/stubs/ipmi.c new file mode 100644 index 0000000000..98b6dcee0d --- /dev/null +++ b/stubs/ipmi.c @@ -0,0 +1,14 @@ +/* + * IPMI ACPI firmware handling + * + * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC + * + * 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 "hw/acpi/ipmi.h" + +void build_acpi_ipmi_devices(Aml *table, BusState *bus) +{ +} -- cgit v1.2.3-55-g7522 From 8df1426e44176512be1b6456e90d100d1af907e1 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Tue, 7 Jun 2016 20:21:57 +0800 Subject: pc-dimm: introduce get_vmstate_memory_region callback This callback returns the MemoryRegion that is the memory of dimm should be kept during live migration nvdimm device is different with pc-dimm as its memory includes not only the MemoryRegion directly mapping to guest's address space but also the memory used as label data Signed-off-by: Xiao Guangrong Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/pc-dimm.c | 14 ++++++++++++-- include/hw/mem/pc-dimm.h | 5 ++++- 2 files changed, 16 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 6de2275986..249193a543 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -40,6 +40,8 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, int slot; MachineState *machine = MACHINE(qdev_get_machine()); PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); Error *local_err = NULL; uint64_t existing_dimms_capacity = 0; uint64_t addr; @@ -105,7 +107,7 @@ void pc_dimm_memory_plug(DeviceState *dev, MemoryHotplugState *hpms, } memory_region_add_subregion(&hpms->mr, addr - hpms->base, mr); - vmstate_register_ram(mr, dev); + vmstate_register_ram(vmstate_mr, dev); numa_set_mem_node_id(addr, memory_region_size(mr), dimm->node); out: @@ -116,10 +118,12 @@ void pc_dimm_memory_unplug(DeviceState *dev, MemoryHotplugState *hpms, MemoryRegion *mr) { PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm); numa_unset_mem_node_id(dimm->addr, memory_region_size(mr), dimm->node); memory_region_del_subregion(&hpms->mr, mr); - vmstate_unregister_ram(mr, dev); + vmstate_unregister_ram(vmstate_mr, dev); } static int pc_existing_dimms_capacity_internal(Object *obj, void *opaque) @@ -424,6 +428,11 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm) return host_memory_backend_get_memory(dimm->hostmem, &error_abort); } +static MemoryRegion *pc_dimm_get_vmstate_memory_region(PCDIMMDevice *dimm) +{ + return host_memory_backend_get_memory(dimm->hostmem, &error_abort); +} + static void pc_dimm_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -434,6 +443,7 @@ static void pc_dimm_class_init(ObjectClass *oc, void *data) dc->desc = "DIMM memory module"; ddc->get_memory_region = pc_dimm_get_memory_region; + ddc->get_vmstate_memory_region = pc_dimm_get_vmstate_memory_region; } static TypeInfo pc_dimm_info = { diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 67e92d8f7b..1e483f2670 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -61,7 +61,9 @@ typedef struct PCDIMMDevice { * @realize: called after common dimm is realized so that the dimm based * devices get the chance to do specified operations. * @get_memory_region: returns #MemoryRegion associated with @dimm which - * is directly mapped into the physical address space of guest + * is directly mapped into the physical address space of guest. + * @get_vmstate_memory_region: returns #MemoryRegion which indicates the + * memory of @dimm should be kept during live migration. */ typedef struct PCDIMMDeviceClass { /* private */ @@ -70,6 +72,7 @@ typedef struct PCDIMMDeviceClass { /* public */ void (*realize)(PCDIMMDevice *dimm, Error **errp); MemoryRegion *(*get_memory_region)(PCDIMMDevice *dimm); + MemoryRegion *(*get_vmstate_memory_region)(PCDIMMDevice *dimm); } PCDIMMDeviceClass; /** -- cgit v1.2.3-55-g7522 From d6fb213a628c66b391d0ce704982bab7c14e559b Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Tue, 7 Jun 2016 20:21:58 +0800 Subject: nvdimm: support nvdimm label Introduce a parameter, 'label-size', which is the size of nvdimm label data area which is reserved at the end of backend memory. It is required at least 128k Two callbacks, read_label_data() and write_label_data(), are used to operate the label area Reviewed-by: Stefan Hajnoczi Signed-off-by: Xiao Guangrong Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/nvdimm.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/mem/nvdimm.h | 55 +++++++++++++++++++- 2 files changed, 186 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 0a602f28ba..81896c0e84 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -23,20 +23,152 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/visitor.h" #include "hw/mem/nvdimm.h" +static void nvdimm_get_label_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + NVDIMMDevice *nvdimm = NVDIMM(obj); + uint64_t value = nvdimm->label_size; + + visit_type_size(v, name, &value, errp); +} + +static void nvdimm_set_label_size(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + NVDIMMDevice *nvdimm = NVDIMM(obj); + Error *local_err = NULL; + uint64_t value; + + if (memory_region_size(&nvdimm->nvdimm_mr)) { + error_setg(&local_err, "cannot change property value"); + goto out; + } + + visit_type_size(v, name, &value, &local_err); + if (local_err) { + goto out; + } + if (value < MIN_NAMESPACE_LABEL_SIZE) { + error_setg(&local_err, "Property '%s.%s' (0x%" PRIx64 ") is required" + " at least 0x%lx", object_get_typename(obj), + name, value, MIN_NAMESPACE_LABEL_SIZE); + goto out; + } + + nvdimm->label_size = value; +out: + error_propagate(errp, local_err); +} + +static void nvdimm_init(Object *obj) +{ + object_property_add(obj, "label-size", "int", + nvdimm_get_label_size, nvdimm_set_label_size, NULL, + NULL, NULL); +} + +static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm) +{ + NVDIMMDevice *nvdimm = NVDIMM(dimm); + + return &nvdimm->nvdimm_mr; +} + +static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp) +{ + MemoryRegion *mr = host_memory_backend_get_memory(dimm->hostmem, errp); + NVDIMMDevice *nvdimm = NVDIMM(dimm); + uint64_t align, pmem_size, size = memory_region_size(mr); + + align = memory_region_get_alignment(mr); + + pmem_size = size - nvdimm->label_size; + nvdimm->label_data = memory_region_get_ram_ptr(mr) + pmem_size; + pmem_size = QEMU_ALIGN_DOWN(pmem_size, align); + + if (size <= nvdimm->label_size || !pmem_size) { + HostMemoryBackend *hostmem = dimm->hostmem; + char *path = object_get_canonical_path_component(OBJECT(hostmem)); + + error_setg(errp, "the size of memdev %s (0x%" PRIx64 ") is too " + "small to contain nvdimm label (0x%" PRIx64 ") and " + "aligned PMEM (0x%" PRIx64 ")", + path, memory_region_size(mr), nvdimm->label_size, align); + return; + } + + memory_region_init_alias(&nvdimm->nvdimm_mr, OBJECT(dimm), + "nvdimm-memory", mr, 0, pmem_size); + nvdimm->nvdimm_mr.align = align; +} + +/* + * the caller should check the input parameters before calling + * label read/write functions. + */ +static void nvdimm_validate_rw_label_data(NVDIMMDevice *nvdimm, uint64_t size, + uint64_t offset) +{ + assert((nvdimm->label_size >= size + offset) && (offset + size > offset)); +} + +static void nvdimm_read_label_data(NVDIMMDevice *nvdimm, void *buf, + uint64_t size, uint64_t offset) +{ + nvdimm_validate_rw_label_data(nvdimm, size, offset); + + memcpy(buf, nvdimm->label_data + offset, size); +} + +static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, + uint64_t size, uint64_t offset) +{ + MemoryRegion *mr; + PCDIMMDevice *dimm = PC_DIMM(nvdimm); + uint64_t backend_offset; + + nvdimm_validate_rw_label_data(nvdimm, size, offset); + + memcpy(nvdimm->label_data + offset, buf, size); + + mr = host_memory_backend_get_memory(dimm->hostmem, &error_abort); + backend_offset = memory_region_size(mr) - nvdimm->label_size + offset; + memory_region_set_dirty(mr, backend_offset, size); +} + +static MemoryRegion *nvdimm_get_vmstate_memory_region(PCDIMMDevice *dimm) +{ + return host_memory_backend_get_memory(dimm->hostmem, &error_abort); +} + static void nvdimm_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); + NVDIMMClass *nvc = NVDIMM_CLASS(oc); /* nvdimm hotplug has not been supported yet. */ dc->hotpluggable = false; + + ddc->realize = nvdimm_realize; + ddc->get_memory_region = nvdimm_get_memory_region; + ddc->get_vmstate_memory_region = nvdimm_get_vmstate_memory_region; + + nvc->read_label_data = nvdimm_read_label_data; + nvc->write_label_data = nvdimm_write_label_data; } static TypeInfo nvdimm_info = { .name = TYPE_NVDIMM, .parent = TYPE_PC_DIMM, + .class_size = sizeof(NVDIMMClass), .class_init = nvdimm_class_init, + .instance_size = sizeof(NVDIMMDevice), + .instance_init = nvdimm_init, }; static void nvdimm_register_types(void) diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index 60ee92b85a..1cfe9e01c4 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -34,7 +34,60 @@ } \ } while (0) -#define TYPE_NVDIMM "nvdimm" +/* + * The minimum label data size is required by NVDIMM Namespace + * specification, see the chapter 2 Namespaces: + * "NVDIMMs following the NVDIMM Block Mode Specification use an area + * at least 128KB in size, which holds around 1000 labels." + */ +#define MIN_NAMESPACE_LABEL_SIZE (128UL << 10) + +#define TYPE_NVDIMM "nvdimm" +#define NVDIMM(obj) OBJECT_CHECK(NVDIMMDevice, (obj), TYPE_NVDIMM) +#define NVDIMM_CLASS(oc) OBJECT_CLASS_CHECK(NVDIMMClass, (oc), TYPE_NVDIMM) +#define NVDIMM_GET_CLASS(obj) OBJECT_GET_CLASS(NVDIMMClass, (obj), \ + TYPE_NVDIMM) +struct NVDIMMDevice { + /* private */ + PCDIMMDevice parent_obj; + + /* public */ + + /* + * the size of label data in NVDIMM device which is presented to + * guest via __DSM "Get Namespace Label Size" function. + */ + uint64_t label_size; + + /* + * the address of label data which is read by __DSM "Get Namespace + * Label Data" function and written by __DSM "Set Namespace Label + * Data" function. + */ + void *label_data; + + /* + * it's the PMEM region in NVDIMM device, which is presented to + * guest via ACPI NFIT and _FIT method if NVDIMM hotplug is supported. + */ + MemoryRegion nvdimm_mr; +}; +typedef struct NVDIMMDevice NVDIMMDevice; + +struct NVDIMMClass { + /* private */ + PCDIMMDeviceClass parent_class; + + /* public */ + + /* read @size bytes from NVDIMM label data at @offset into @buf. */ + void (*read_label_data)(NVDIMMDevice *nvdimm, void *buf, + uint64_t size, uint64_t offset); + /* write @size bytes from @buf to NVDIMM label data at @offset. */ + void (*write_label_data)(NVDIMMDevice *nvdimm, const void *buf, + uint64_t size, uint64_t offset); +}; +typedef struct NVDIMMClass NVDIMMClass; #define NVDIMM_DSM_MEM_FILE "etc/acpi/nvdimm-mem" -- cgit v1.2.3-55-g7522 From b265f27c5a31c0b2d5017e99f52db45aa0559909 Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Tue, 7 Jun 2016 20:21:59 +0800 Subject: acpi: add aml_object_type Implement ObjectType which is used by NVDIMM _DSM method in later patch Signed-off-by: Xiao Guangrong Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 8 ++++++++ include/hw/acpi/aml-build.h | 1 + 2 files changed, 9 insertions(+) (limited to 'include') diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 874e473cac..c71fd16136 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1481,6 +1481,14 @@ Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target) target); } +/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefObjectType */ +Aml *aml_object_type(Aml *object) +{ + Aml *var = aml_opcode(0x8E /* ObjectTypeOp */); + aml_append(var, object); + return var; +} + void build_header(BIOSLinker *linker, GArray *table_data, AcpiTableHeader *h, const char *sig, int len, uint8_t rev, diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 10c09ca29f..7a548e15f5 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -363,6 +363,7 @@ Aml *aml_refof(Aml *arg); Aml *aml_derefof(Aml *arg); Aml *aml_sizeof(Aml *arg); Aml *aml_concatenate(Aml *source1, Aml *source2, Aml *target); +Aml *aml_object_type(Aml *object); void build_header(BIOSLinker *linker, GArray *table_data, -- cgit v1.2.3-55-g7522 From 052889b8e96d24625043cd5901ab91dd38aca49f Mon Sep 17 00:00:00 2001 From: Xiao Guangrong Date: Tue, 7 Jun 2016 20:22:00 +0800 Subject: acpi: add aml_call5 It will be used by NVDIMM ACPI Signed-off-by: Xiao Guangrong Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 14 ++++++++++++++ include/hw/acpi/aml-build.h | 2 ++ 2 files changed, 16 insertions(+) (limited to 'include') diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index c71fd16136..db3e914fb4 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -660,6 +660,20 @@ Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4) return var; } +/* helper to call method with 5 arguments */ +Aml *aml_call5(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4, + Aml *arg5) +{ + Aml *var = aml_alloc(); + build_append_namestring(var->buf, "%s", method); + aml_append(var, arg1); + aml_append(var, arg2); + aml_append(var, arg3); + aml_append(var, arg4); + aml_append(var, arg5); + return var; +} + /* * ACPI 5.0: 6.4.3.8.1 GPIO Connection Descriptor * Type 1, Large Item Name 0xC diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 7a548e15f5..e7a1a4cefd 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -277,6 +277,8 @@ Aml *aml_call1(const char *method, Aml *arg1); Aml *aml_call2(const char *method, Aml *arg1, Aml *arg2); Aml *aml_call3(const char *method, Aml *arg1, Aml *arg2, Aml *arg3); Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4); +Aml *aml_call5(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4, + Aml *arg5); Aml *aml_gpio_int(AmlConsumerAndProducer con_and_pro, AmlLevelAndEdge edge_level, AmlActiveHighAndLow active_level, AmlShared shared, -- cgit v1.2.3-55-g7522 From 16bcab97eb9fc2d5f15e5c9ff6369f9d1d120b49 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Mon, 11 Apr 2016 17:25:54 +0200 Subject: pc: piix4/ich9: add 'cpu-hotplug-legacy' property It will be used to select which hotplug call-back is called and for switching from legacy mode into new one. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/ich9.c | 23 ++++++++++++++++++++++- hw/acpi/piix4.c | 24 +++++++++++++++++++++++- include/hw/acpi/ich9.h | 1 + 3 files changed, 46 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 853c9c4eb7..ed16940601 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -306,6 +306,21 @@ static void ich9_pm_set_memory_hotplug_support(Object *obj, bool value, s->pm.acpi_memory_hotplug.is_enabled = value; } +static bool ich9_pm_get_cpu_hotplug_legacy(Object *obj, Error **errp) +{ + ICH9LPCState *s = ICH9_LPC_DEVICE(obj); + + return s->pm.cpu_hotplug_legacy; +} + +static void ich9_pm_set_cpu_hotplug_legacy(Object *obj, bool value, + Error **errp) +{ + ICH9LPCState *s = ICH9_LPC_DEVICE(obj); + + s->pm.cpu_hotplug_legacy = value; +} + static void ich9_pm_get_disable_s3(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -397,6 +412,7 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) { static const uint32_t gpe0_len = ICH9_PMIO_GPE0_LEN; pm->acpi_memory_hotplug.is_enabled = true; + pm->cpu_hotplug_legacy = true; pm->disable_s3 = 0; pm->disable_s4 = 0; pm->s4_val = 2; @@ -412,6 +428,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) ich9_pm_get_memory_hotplug_support, ich9_pm_set_memory_hotplug_support, NULL); + object_property_add_bool(obj, "cpu-hotplug-legacy", + ich9_pm_get_cpu_hotplug_legacy, + ich9_pm_set_cpu_hotplug_legacy, + NULL); object_property_add(obj, ACPI_PM_PROP_S3_DISABLED, "uint8", ich9_pm_get_disable_s3, ich9_pm_set_disable_s3, @@ -439,7 +459,8 @@ void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_plug_cb(hotplug_dev, &lpc->pm.acpi_memory_hotplug, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + } else if (lpc->pm.cpu_hotplug_legacy && + object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { legacy_acpi_cpu_plug_cb(hotplug_dev, &lpc->pm.gpe_cpu, dev, errp); } else { error_setg(errp, "acpi: device plug request for not supported device" diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index c48cb1b91a..9ae3964099 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -86,6 +86,7 @@ typedef struct PIIX4PMState { uint8_t disable_s4; uint8_t s4_val; + bool cpu_hotplug_legacy; AcpiCpuHotplug gpe_cpu; MemHotplugState acpi_memory_hotplug; @@ -351,7 +352,8 @@ static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, acpi_memory_plug_cb(hotplug_dev, &s->acpi_memory_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { acpi_pcihp_device_plug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, errp); - } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + } else if (s->cpu_hotplug_legacy && + object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { legacy_acpi_cpu_plug_cb(hotplug_dev, &s->gpe_cpu, dev, errp); } else { error_setg(errp, "acpi: device plug request for not supported device" @@ -560,6 +562,21 @@ static const MemoryRegionOps piix4_gpe_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; + +static bool piix4_get_cpu_hotplug_legacy(Object *obj, Error **errp) +{ + PIIX4PMState *s = PIIX4_PM(obj); + + return s->cpu_hotplug_legacy; +} + +static void piix4_set_cpu_hotplug_legacy(Object *obj, bool value, Error **errp) +{ + PIIX4PMState *s = PIIX4_PM(obj); + + s->cpu_hotplug_legacy = value; +} + static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, PCIBus *bus, PIIX4PMState *s) { @@ -570,6 +587,11 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent, s->use_acpi_pci_hotplug); + s->cpu_hotplug_legacy = true; + object_property_add_bool(OBJECT(s), "cpu-hotplug-legacy", + piix4_get_cpu_hotplug_legacy, + piix4_set_cpu_hotplug_legacy, + NULL); legacy_acpi_cpu_hotplug_init(parent, OBJECT(s), &s->gpe_cpu, PIIX4_CPU_HOTPLUG_IO_BASE); diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index bbd657c59b..e29a8566d8 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -48,6 +48,7 @@ typedef struct ICH9LPCPMRegs { uint32_t pm_io_base; Notifier powerdown_notifier; + bool cpu_hotplug_legacy; AcpiCpuHotplug gpe_cpu; MemHotplugState acpi_memory_hotplug; -- cgit v1.2.3-55-g7522 From 5e1b5d93887b52eede156f846b6c4c5c8bbcfcdb Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Tue, 14 Jun 2016 16:02:06 +0200 Subject: acpi: cpuhp: add CPU devices AML with _STA method it adds CPU objects to DSDT with _STA method and QEMU side of CPU hotplug interface initialization with registers sufficient to handle _STA requests, including necessary hotplug callbacks in piix4,ich9 code. Hot-(un)plug hw/acpi parts will be added by corresponding follow up patches. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/Makefile.objs | 1 + hw/acpi/cpu.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/acpi/ich9.c | 9 +- hw/acpi/piix4.c | 11 ++- hw/acpi/trace-events | 5 ++ include/hw/acpi/cpu.h | 51 +++++++++++ include/hw/acpi/ich9.h | 2 + 7 files changed, 313 insertions(+), 6 deletions(-) create mode 100644 hw/acpi/cpu.c create mode 100644 include/hw/acpi/cpu.h (limited to 'include') diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 1a48f61676..4b7da6639f 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -2,6 +2,7 @@ common-obj-$(CONFIG_ACPI_X86) += core.o piix4.o pcihp.o common-obj-$(CONFIG_ACPI_X86_ICH) += ich9.o tco.o common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu_hotplug.o common-obj-$(CONFIG_ACPI_MEMORY_HOTPLUG) += memory_hotplug.o memory_hotplug_acpi_table.o +common-obj-$(CONFIG_ACPI_CPU_HOTPLUG) += cpu.o obj-$(CONFIG_ACPI_NVDIMM) += nvdimm.o common-obj-$(CONFIG_ACPI) += acpi_interface.o common-obj-$(CONFIG_ACPI) += bios-linker-loader.o diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c new file mode 100644 index 0000000000..d99002c0ef --- /dev/null +++ b/hw/acpi/cpu.c @@ -0,0 +1,240 @@ +#include "qemu/osdep.h" +#include "hw/boards.h" +#include "hw/acpi/cpu.h" +#include "qapi/error.h" +#include "trace.h" + +#define ACPI_CPU_HOTPLUG_REG_LEN 12 +#define ACPI_CPU_SELECTOR_OFFSET_WR 0 +#define ACPI_CPU_FLAGS_OFFSET_RW 4 + +static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t val = 0; + CPUHotplugState *cpu_st = opaque; + AcpiCpuStatus *cdev; + + if (cpu_st->selector >= cpu_st->dev_count) { + return val; + } + + cdev = &cpu_st->devs[cpu_st->selector]; + switch (addr) { + case ACPI_CPU_FLAGS_OFFSET_RW: /* pack and return is_* fields */ + val |= cdev->cpu ? 1 : 0; + trace_cpuhp_acpi_read_flags(cpu_st->selector, val); + break; + default: + break; + } + return val; +} + +static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + CPUHotplugState *cpu_st = opaque; + + assert(cpu_st->dev_count); + + if (addr) { + if (cpu_st->selector >= cpu_st->dev_count) { + trace_cpuhp_acpi_invalid_idx_selected(cpu_st->selector); + return; + } + } + + switch (addr) { + case ACPI_CPU_SELECTOR_OFFSET_WR: /* current CPU selector */ + cpu_st->selector = data; + trace_cpuhp_acpi_write_idx(cpu_st->selector); + break; + default: + break; + } +} + +static const MemoryRegionOps cpu_hotplug_ops = { + .read = cpu_hotplug_rd, + .write = cpu_hotplug_wr, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, + CPUHotplugState *state, hwaddr base_addr) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(machine); + CPUArchIdList *id_list; + int i; + + assert(mc->possible_cpu_arch_ids); + id_list = mc->possible_cpu_arch_ids(machine); + state->dev_count = id_list->len; + state->devs = g_new0(typeof(*state->devs), state->dev_count); + for (i = 0; i < id_list->len; i++) { + state->devs[i].cpu = id_list->cpus[i].cpu; + state->devs[i].arch_id = id_list->cpus[i].arch_id; + } + g_free(id_list); + memory_region_init_io(&state->ctrl_reg, owner, &cpu_hotplug_ops, state, + "acpi-mem-hotplug", ACPI_CPU_HOTPLUG_REG_LEN); + memory_region_add_subregion(as, base_addr, &state->ctrl_reg); +} + +static AcpiCpuStatus *get_cpu_status(CPUHotplugState *cpu_st, DeviceState *dev) +{ + CPUClass *k = CPU_GET_CLASS(dev); + uint64_t cpu_arch_id = k->get_arch_id(CPU(dev)); + int i; + + for (i = 0; i < cpu_st->dev_count; i++) { + if (cpu_arch_id == cpu_st->devs[i].arch_id) { + return &cpu_st->devs[i]; + } + } + return NULL; +} + +void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, + CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) +{ + AcpiCpuStatus *cdev; + + cdev = get_cpu_status(cpu_st, dev); + if (!cdev) { + return; + } + + cdev->cpu = CPU(dev); +} + +const VMStateDescription vmstate_cpu_hotplug = { + .name = "CPU hotplug state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(selector, CPUHotplugState), + VMSTATE_END_OF_LIST() + } +}; + +#define CPU_NAME_FMT "C%.03X" +#define CPUHP_RES_DEVICE "PRES" +#define CPU_LOCK "CPLK" +#define CPU_STS_METHOD "CSTA" + +#define CPU_ENABLED "CPEN" +#define CPU_SELECTOR "CSEL" + +void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, + hwaddr io_base, + const char *res_root) +{ + Aml *ifctx; + Aml *field; + Aml *method; + Aml *cpu_ctrl_dev; + Aml *cpus_dev; + Aml *zero = aml_int(0); + Aml *one = aml_int(1); + Aml *sb_scope = aml_scope("_SB"); + MachineClass *mc = MACHINE_GET_CLASS(machine); + CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); + char *cphp_res_path = g_strdup_printf("%s." CPUHP_RES_DEVICE, res_root); + + cpu_ctrl_dev = aml_device("%s", cphp_res_path); + { + Aml *crs; + + aml_append(cpu_ctrl_dev, + aml_name_decl("_HID", aml_eisaid("PNP0A06"))); + aml_append(cpu_ctrl_dev, + aml_name_decl("_UID", aml_string("CPU Hotplug resources"))); + aml_append(cpu_ctrl_dev, aml_mutex(CPU_LOCK, 0)); + + crs = aml_resource_template(); + aml_append(crs, aml_io(AML_DECODE16, io_base, io_base, 1, + ACPI_CPU_HOTPLUG_REG_LEN)); + aml_append(cpu_ctrl_dev, aml_name_decl("_CRS", crs)); + + /* declare CPU hotplug MMIO region with related access fields */ + aml_append(cpu_ctrl_dev, + aml_operation_region("PRST", AML_SYSTEM_IO, aml_int(io_base), + ACPI_CPU_HOTPLUG_REG_LEN)); + + field = aml_field("PRST", AML_BYTE_ACC, AML_NOLOCK, + AML_WRITE_AS_ZEROS); + aml_append(field, aml_reserved_field(ACPI_CPU_FLAGS_OFFSET_RW * 8)); + /* 1 if enabled, read only */ + aml_append(field, aml_named_field(CPU_ENABLED, 1)); + aml_append(cpu_ctrl_dev, field); + + field = aml_field("PRST", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE); + /* CPU selector, write only */ + aml_append(field, aml_named_field(CPU_SELECTOR, 32)); + aml_append(cpu_ctrl_dev, field); + + } + aml_append(sb_scope, cpu_ctrl_dev); + + cpus_dev = aml_device("\\_SB.CPUS"); + { + int i; + Aml *ctrl_lock = aml_name("%s.%s", cphp_res_path, CPU_LOCK); + Aml *cpu_selector = aml_name("%s.%s", cphp_res_path, CPU_SELECTOR); + Aml *is_enabled = aml_name("%s.%s", cphp_res_path, CPU_ENABLED); + + aml_append(cpus_dev, aml_name_decl("_HID", aml_string("ACPI0010"))); + aml_append(cpus_dev, aml_name_decl("_CID", aml_eisaid("PNP0A05"))); + + method = aml_method(CPU_STS_METHOD, 1, AML_SERIALIZED); + { + Aml *idx = aml_arg(0); + Aml *sta = aml_local(0); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(idx, cpu_selector)); + aml_append(method, aml_store(zero, sta)); + ifctx = aml_if(aml_equal(is_enabled, one)); + { + aml_append(ifctx, aml_store(aml_int(0xF), sta)); + } + aml_append(method, ifctx); + aml_append(method, aml_release(ctrl_lock)); + aml_append(method, aml_return(sta)); + } + aml_append(cpus_dev, method); + + /* build Processor object for each processor */ + for (i = 0; i < arch_ids->len; i++) { + Aml *dev; + Aml *uid = aml_int(i); + int arch_id = arch_ids->cpus[i].arch_id; + + if (opts.apci_1_compatible && arch_id < 255) { + dev = aml_processor(i, 0, 0, CPU_NAME_FMT, i); + } else { + dev = aml_device(CPU_NAME_FMT, i); + aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0007"))); + aml_append(dev, aml_name_decl("_UID", uid)); + } + + method = aml_method("_STA", 0, AML_SERIALIZED); + aml_append(method, aml_return(aml_call1(CPU_STS_METHOD, uid))); + aml_append(dev, method); + + aml_append(cpus_dev, dev); + } + } + aml_append(sb_scope, cpus_dev); + aml_append(table, sb_scope); + + g_free(cphp_res_path); + g_free(arch_ids); +} diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index ed16940601..9a81da82d3 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -459,9 +459,12 @@ void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_plug_cb(hotplug_dev, &lpc->pm.acpi_memory_hotplug, dev, errp); - } else if (lpc->pm.cpu_hotplug_legacy && - object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { - legacy_acpi_cpu_plug_cb(hotplug_dev, &lpc->pm.gpe_cpu, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + if (lpc->pm.cpu_hotplug_legacy) { + legacy_acpi_cpu_plug_cb(hotplug_dev, &lpc->pm.gpe_cpu, dev, errp); + } else { + acpi_cpu_plug_cb(hotplug_dev, &lpc->pm.cpuhp_state, dev, errp); + } } else { error_setg(errp, "acpi: device plug request for not supported device" " type: %s", object_get_typename(OBJECT(dev))); diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 9ae3964099..6351d2ebb2 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -34,6 +34,7 @@ #include "hw/acpi/piix4.h" #include "hw/acpi/pcihp.h" #include "hw/acpi/cpu_hotplug.h" +#include "hw/acpi/cpu.h" #include "hw/hotplug.h" #include "hw/mem/pc-dimm.h" #include "hw/acpi/memory_hotplug.h" @@ -88,6 +89,7 @@ typedef struct PIIX4PMState { bool cpu_hotplug_legacy; AcpiCpuHotplug gpe_cpu; + CPUHotplugState cpuhp_state; MemHotplugState acpi_memory_hotplug; } PIIX4PMState; @@ -352,9 +354,12 @@ static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, acpi_memory_plug_cb(hotplug_dev, &s->acpi_memory_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { acpi_pcihp_device_plug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, errp); - } else if (s->cpu_hotplug_legacy && - object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { - legacy_acpi_cpu_plug_cb(hotplug_dev, &s->gpe_cpu, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + if (s->cpu_hotplug_legacy) { + legacy_acpi_cpu_plug_cb(hotplug_dev, &s->gpe_cpu, dev, errp); + } else { + acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp); + } } else { error_setg(errp, "acpi: device plug request for not supported device" " type: %s", object_get_typename(OBJECT(dev))); diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index e95b2183ac..940467ba14 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -16,3 +16,8 @@ mhp_acpi_clear_insert_evt(uint32_t slot) "slot[0x%"PRIx32"] clear insert event" mhp_acpi_clear_remove_evt(uint32_t slot) "slot[0x%"PRIx32"] clear remove event" mhp_acpi_pc_dimm_deleted(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm deleted" mhp_acpi_pc_dimm_delete_failed(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm delete failed" + +# hw/acpi/cpu.c +cpuhp_acpi_invalid_idx_selected(uint32_t idx) "0x%"PRIx32 +cpuhp_acpi_read_flags(uint32_t idx, uint8_t flags) "idx[0x%"PRIx32"] flags: 0x%"PRIx8 +cpuhp_acpi_write_idx(uint32_t idx) "set active cpu idx: 0x%"PRIx32 diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h new file mode 100644 index 0000000000..f345447af3 --- /dev/null +++ b/include/hw/acpi/cpu.h @@ -0,0 +1,51 @@ +/* + * QEMU ACPI hotplug utilities + * + * Copyright (C) 2016 Red Hat Inc + * + * Authors: + * Igor Mammedov + * + * 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 ACPI_CPU_H +#define ACPI_CPU_H + +#include "hw/qdev-core.h" +#include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" +#include "hw/hotplug.h" + +typedef struct AcpiCpuStatus { + struct CPUState *cpu; + uint64_t arch_id; +} AcpiCpuStatus; + +typedef struct CPUHotplugState { + MemoryRegion ctrl_reg; + uint32_t selector; + uint32_t dev_count; + AcpiCpuStatus *devs; +} CPUHotplugState; + +void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, + CPUHotplugState *cpu_st, DeviceState *dev, Error **errp); + +void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, + CPUHotplugState *state, hwaddr base_addr); + +typedef struct CPUHotplugFeatures { + bool apci_1_compatible; +} CPUHotplugFeatures; + +void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, + hwaddr io_base, + const char *res_root); + +extern const VMStateDescription vmstate_cpu_hotplug; +#define VMSTATE_CPU_HOTPLUG(cpuhp, state) \ + VMSTATE_STRUCT(cpuhp, state, 1, \ + vmstate_cpu_hotplug, CPUHotplugState) + +#endif diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index e29a8566d8..a352c94fde 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -23,6 +23,7 @@ #include "hw/acpi/acpi.h" #include "hw/acpi/cpu_hotplug.h" +#include "hw/acpi/cpu.h" #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/acpi_dev_interface.h" #include "hw/acpi/tco.h" @@ -50,6 +51,7 @@ typedef struct ICH9LPCPMRegs { bool cpu_hotplug_legacy; AcpiCpuHotplug gpe_cpu; + CPUHotplugState cpuhp_state; MemHotplugState acpi_memory_hotplug; -- cgit v1.2.3-55-g7522 From ac35f13ba8f80533b21016ced01aa55891952251 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 20 Apr 2016 11:28:57 +0200 Subject: pc: acpi: introduce AcpiDeviceIfClass.madt_cpu hook Add madt_cpu callback to AcpiDeviceIfClass and use it for generating LAPIC MADT entries for CPUs. Later it will be used for generating x2APIC entries in case of more than 255 CPUs and also would be reused by ARM target when ACPI CPU hotplug is introduced there. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/piix4.c | 1 + hw/i386/acpi-build.c | 45 +++++++++++++++++++++--------------- hw/isa/lpc_ich9.c | 1 + include/hw/acpi/acpi_dev_interface.h | 7 ++++++ include/hw/i386/pc.h | 5 ++++ stubs/Makefile.objs | 1 + stubs/pc_madt_cpu_entry.c | 7 ++++++ 7 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 stubs/pc_madt_cpu_entry.c (limited to 'include') diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 6351d2ebb2..6d24cb535b 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -658,6 +658,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) hc->unplug = piix4_device_unplug_cb; adevc->ospm_status = piix4_ospm_status; adevc->send_event = piix4_send_gpe; + adevc->madt_cpu = pc_madt_cpu_entry; } static const TypeInfo piix4_pm_info = { diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index b3dc1df25a..e35a446a9d 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -329,12 +329,38 @@ build_fadt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm, (void *)fadt, "FACP", sizeof(*fadt), 1, oem_id, oem_table_id); } +void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, + CPUArchIdList *apic_ids, GArray *entry) +{ + int apic_id; + AcpiMadtProcessorApic *apic = acpi_data_push(entry, sizeof *apic); + + apic_id = apic_ids->cpus[uid].arch_id; + apic->type = ACPI_APIC_PROCESSOR; + apic->length = sizeof(*apic); + apic->processor_id = uid; + apic->local_apic_id = apic_id; + if (apic_ids->cpus[uid].cpu != NULL) { + apic->flags = cpu_to_le32(1); + } else { + /* ACPI spec says that LAPIC entry for non present + * CPU may be omitted from MADT or it must be marked + * as disabled. However omitting non present CPU from + * MADT breaks hotplug on linux. So possible CPUs + * should be put in MADT but kept disabled. + */ + apic->flags = cpu_to_le32(0); + } +} + static void build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms) { MachineClass *mc = MACHINE_GET_CLASS(pcms); CPUArchIdList *apic_ids = mc->possible_cpu_arch_ids(MACHINE(pcms)); int madt_start = table_data->len; + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(pcms->acpi_dev); + AcpiDeviceIf *adev = ACPI_DEVICE_IF(pcms->acpi_dev); AcpiMultipleApicTable *madt; AcpiMadtIoApic *io_apic; @@ -347,24 +373,7 @@ build_madt(GArray *table_data, BIOSLinker *linker, PCMachineState *pcms) madt->flags = cpu_to_le32(1); for (i = 0; i < apic_ids->len; i++) { - AcpiMadtProcessorApic *apic = acpi_data_push(table_data, sizeof *apic); - int apic_id = apic_ids->cpus[i].arch_id; - - apic->type = ACPI_APIC_PROCESSOR; - apic->length = sizeof(*apic); - apic->processor_id = i; - apic->local_apic_id = apic_id; - if (apic_ids->cpus[i].cpu != NULL) { - apic->flags = cpu_to_le32(1); - } else { - /* ACPI spec says that LAPIC entry for non present - * CPU may be omitted from MADT or it must be marked - * as disabled. However omitting non present CPU from - * MADT breaks hotplug on linux. So possible CPUs - * should be put in MADT but kept disabled. - */ - apic->flags = cpu_to_le32(0); - } + adevc->madt_cpu(adev, i, apic_ids, table_data); } g_free(apic_ids); diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index 213741bc21..c1a4f1b34c 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -714,6 +714,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) hc->unplug = ich9_pm_device_unplug_cb; adevc->ospm_status = ich9_pm_ospm_status; adevc->send_event = ich9_send_gpe; + adevc->madt_cpu = pc_madt_cpu_entry; } static const TypeInfo ich9_lpc_info = { diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index a0c4a336f2..da4ef7fbd3 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -3,6 +3,7 @@ #include "qom/object.h" #include "qapi-types.h" +#include "hw/boards.h" /* These values are part of guest ABI, and can not be changed */ typedef enum { @@ -37,6 +38,10 @@ void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event); * ospm_status: returns status of ACPI device objects, reported * via _OST method if device supports it. * send_event: inject a specified event into guest + * madt_cpu: fills @entry with Interrupt Controller Structure + * for CPU indexed by @uid in @apic_ids array, + * returned structure types are: + * 0 - Local APIC, 9 - Local x2APIC, 0xB - GICC * * Interface is designed for providing unified interface * to generic ACPI functionality that could be used without @@ -50,5 +55,7 @@ typedef struct AcpiDeviceIfClass { /* */ void (*ospm_status)(AcpiDeviceIf *adev, ACPIOSTInfoList ***list); void (*send_event)(AcpiDeviceIf *adev, AcpiEventStatusBits ev); + void (*madt_cpu)(AcpiDeviceIf *adev, int uid, + CPUArchIdList *apic_ids, GArray *entry); } AcpiDeviceIfClass; #endif diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 49566c89d4..9e239297e8 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -17,6 +17,7 @@ #include "hw/compat.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" +#include "hw/acpi/acpi_dev_interface.h" #define HPET_INTCAP "hpet-intcap" @@ -345,6 +346,10 @@ void pc_system_firmware_init(MemoryRegion *rom_memory, /* pvpanic.c */ uint16_t pvpanic_port(void); +/* acpi-build.c */ +void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, + CPUArchIdList *apic_ids, GArray *entry); + /* e820 types */ #define E820_RAM 1 #define E820_RESERVED 2 diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index b829ee6e1a..7cdcad4fdb 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -43,3 +43,4 @@ stub-obj-y += vhost.o stub-obj-y += iohandler.o stub-obj-y += smbios_type_38.o stub-obj-y += ipmi.o +stub-obj-y += pc_madt_cpu_entry.o diff --git a/stubs/pc_madt_cpu_entry.c b/stubs/pc_madt_cpu_entry.c new file mode 100644 index 0000000000..427e772868 --- /dev/null +++ b/stubs/pc_madt_cpu_entry.c @@ -0,0 +1,7 @@ +#include "qemu/osdep.h" +#include "hw/i386/pc.h" + +void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, + CPUArchIdList *apic_ids, GArray *entry) +{ +} -- cgit v1.2.3-55-g7522 From d2238cb6781d7bcbbf8ddf4a1f8486838b80c7bb Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Tue, 14 Jun 2016 16:13:32 +0200 Subject: acpi: cpuhp: implement hot-add parts of CPU hotplug interface it adds hw registers needed for handling CPU hot-add and corresponding AML methods to handle hot-add events on guest side. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/cpu.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++++- hw/acpi/trace-events | 4 ++ include/hw/acpi/cpu.h | 5 +- 3 files changed, 157 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index d99002c0ef..811be8a2d3 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -7,6 +7,13 @@ #define ACPI_CPU_HOTPLUG_REG_LEN 12 #define ACPI_CPU_SELECTOR_OFFSET_WR 0 #define ACPI_CPU_FLAGS_OFFSET_RW 4 +#define ACPI_CPU_CMD_OFFSET_WR 5 +#define ACPI_CPU_CMD_DATA_OFFSET_RW 8 + +enum { + CPHP_GET_NEXT_CPU_WITH_EVENT_CMD = 0, + CPHP_CMD_MAX +}; static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size) { @@ -22,8 +29,19 @@ static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size) switch (addr) { case ACPI_CPU_FLAGS_OFFSET_RW: /* pack and return is_* fields */ val |= cdev->cpu ? 1 : 0; + val |= cdev->is_inserting ? 2 : 0; trace_cpuhp_acpi_read_flags(cpu_st->selector, val); break; + case ACPI_CPU_CMD_DATA_OFFSET_RW: + switch (cpu_st->command) { + case CPHP_GET_NEXT_CPU_WITH_EVENT_CMD: + val = cpu_st->selector; + break; + default: + break; + } + trace_cpuhp_acpi_read_cmd_data(cpu_st->selector, val); + break; default: break; } @@ -34,6 +52,7 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { CPUHotplugState *cpu_st = opaque; + AcpiCpuStatus *cdev; assert(cpu_st->dev_count); @@ -49,6 +68,33 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, cpu_st->selector = data; trace_cpuhp_acpi_write_idx(cpu_st->selector); break; + case ACPI_CPU_FLAGS_OFFSET_RW: /* set is_* fields */ + cdev = &cpu_st->devs[cpu_st->selector]; + if (data & 2) { /* clear insert event */ + cdev->is_inserting = false; + trace_cpuhp_acpi_clear_inserting_evt(cpu_st->selector); + } + break; + case ACPI_CPU_CMD_OFFSET_WR: + trace_cpuhp_acpi_write_cmd(cpu_st->selector, data); + if (data < CPHP_CMD_MAX) { + cpu_st->command = data; + if (cpu_st->command == CPHP_GET_NEXT_CPU_WITH_EVENT_CMD) { + uint32_t iter = cpu_st->selector; + + do { + cdev = &cpu_st->devs[iter]; + if (cdev->is_inserting) { + cpu_st->selector = iter; + trace_cpuhp_acpi_cpu_has_events(cpu_st->selector, + cdev->is_inserting); + break; + } + iter = iter + 1 < cpu_st->dev_count ? iter + 1 : 0; + } while (iter != cpu_st->selector); + } + } + break; default: break; } @@ -111,8 +157,23 @@ void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, } cdev->cpu = CPU(dev); + if (dev->hotplugged) { + cdev->is_inserting = true; + acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS); + } } +static const VMStateDescription vmstate_cpuhp_sts = { + .name = "CPU hotplug device state", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(is_inserting, AcpiCpuStatus), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_cpu_hotplug = { .name = "CPU hotplug state", .version_id = 1, @@ -120,6 +181,9 @@ const VMStateDescription vmstate_cpu_hotplug = { .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT32(selector, CPUHotplugState), + VMSTATE_UINT8(command, CPUHotplugState), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(devs, CPUHotplugState, dev_count, + vmstate_cpuhp_sts, AcpiCpuStatus), VMSTATE_END_OF_LIST() } }; @@ -128,13 +192,19 @@ const VMStateDescription vmstate_cpu_hotplug = { #define CPUHP_RES_DEVICE "PRES" #define CPU_LOCK "CPLK" #define CPU_STS_METHOD "CSTA" +#define CPU_SCAN_METHOD "CSCN" +#define CPU_NOTIFY_METHOD "CTFY" #define CPU_ENABLED "CPEN" #define CPU_SELECTOR "CSEL" +#define CPU_COMMAND "CCMD" +#define CPU_DATA "CDAT" +#define CPU_INSERT_EVENT "CINS" void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, hwaddr io_base, - const char *res_root) + const char *res_root, + const char *event_handler_method) { Aml *ifctx; Aml *field; @@ -147,6 +217,9 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, MachineClass *mc = MACHINE_GET_CLASS(machine); CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); char *cphp_res_path = g_strdup_printf("%s." CPUHP_RES_DEVICE, res_root); + Object *obj = object_resolve_path_type("", TYPE_ACPI_DEVICE_IF, NULL); + AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_GET_CLASS(obj); + AcpiDeviceIf *adev = ACPI_DEVICE_IF(obj); cpu_ctrl_dev = aml_device("%s", cphp_res_path); { @@ -173,11 +246,18 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(field, aml_reserved_field(ACPI_CPU_FLAGS_OFFSET_RW * 8)); /* 1 if enabled, read only */ aml_append(field, aml_named_field(CPU_ENABLED, 1)); + /* (read) 1 if has a insert event. (write) 1 to clear event */ + aml_append(field, aml_named_field(CPU_INSERT_EVENT, 1)); + aml_append(field, aml_reserved_field(6)); + aml_append(field, aml_named_field(CPU_COMMAND, 8)); aml_append(cpu_ctrl_dev, field); field = aml_field("PRST", AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE); /* CPU selector, write only */ aml_append(field, aml_named_field(CPU_SELECTOR, 32)); + /* flags + cmd + 2byte align */ + aml_append(field, aml_reserved_field(4 * 8)); + aml_append(field, aml_named_field(CPU_DATA, 32)); aml_append(cpu_ctrl_dev, field); } @@ -189,10 +269,27 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, Aml *ctrl_lock = aml_name("%s.%s", cphp_res_path, CPU_LOCK); Aml *cpu_selector = aml_name("%s.%s", cphp_res_path, CPU_SELECTOR); Aml *is_enabled = aml_name("%s.%s", cphp_res_path, CPU_ENABLED); + Aml *cpu_cmd = aml_name("%s.%s", cphp_res_path, CPU_COMMAND); + Aml *cpu_data = aml_name("%s.%s", cphp_res_path, CPU_DATA); + Aml *ins_evt = aml_name("%s.%s", cphp_res_path, CPU_INSERT_EVENT); aml_append(cpus_dev, aml_name_decl("_HID", aml_string("ACPI0010"))); aml_append(cpus_dev, aml_name_decl("_CID", aml_eisaid("PNP0A05"))); + method = aml_method(CPU_NOTIFY_METHOD, 2, AML_NOTSERIALIZED); + for (i = 0; i < arch_ids->len; i++) { + Aml *cpu = aml_name(CPU_NAME_FMT, i); + Aml *uid = aml_arg(0); + Aml *event = aml_arg(1); + + ifctx = aml_if(aml_equal(uid, aml_int(i))); + { + aml_append(ifctx, aml_notify(cpu, event)); + } + aml_append(method, ifctx); + } + aml_append(cpus_dev, method); + method = aml_method(CPU_STS_METHOD, 1, AML_SERIALIZED); { Aml *idx = aml_arg(0); @@ -211,10 +308,41 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, } aml_append(cpus_dev, method); + method = aml_method(CPU_SCAN_METHOD, 0, AML_SERIALIZED); + { + Aml *while_ctx; + Aml *has_event = aml_local(0); + Aml *dev_chk = aml_int(1); + Aml *next_cpu_cmd = aml_int(CPHP_GET_NEXT_CPU_WITH_EVENT_CMD); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(one, has_event)); + while_ctx = aml_while(aml_equal(has_event, one)); + { + /* clear loop exit condition, ins_evt check + * will set it to 1 while next_cpu_cmd returns a CPU + * with events */ + aml_append(while_ctx, aml_store(zero, has_event)); + aml_append(while_ctx, aml_store(next_cpu_cmd, cpu_cmd)); + ifctx = aml_if(aml_equal(ins_evt, one)); + { + aml_append(ifctx, + aml_call2(CPU_NOTIFY_METHOD, cpu_data, dev_chk)); + aml_append(ifctx, aml_store(one, ins_evt)); + aml_append(ifctx, aml_store(one, has_event)); + } + aml_append(while_ctx, ifctx); + } + aml_append(method, while_ctx); + aml_append(method, aml_release(ctrl_lock)); + } + aml_append(cpus_dev, method); + /* build Processor object for each processor */ for (i = 0; i < arch_ids->len; i++) { Aml *dev; Aml *uid = aml_int(i); + GArray *madt_buf = g_array_new(0, 1, 1); int arch_id = arch_ids->cpus[i].arch_id; if (opts.apci_1_compatible && arch_id < 255) { @@ -229,12 +357,32 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(method, aml_return(aml_call1(CPU_STS_METHOD, uid))); aml_append(dev, method); + /* build _MAT object */ + assert(adevc && adevc->madt_cpu); + adevc->madt_cpu(adev, i, arch_ids, madt_buf); + switch (madt_buf->data[0]) { + case ACPI_APIC_PROCESSOR: { + AcpiMadtProcessorApic *apic = (void *)madt_buf->data; + apic->flags = cpu_to_le32(1); + break; + } + default: + assert(0); + } + aml_append(dev, aml_name_decl("_MAT", + aml_buffer(madt_buf->len, (uint8_t *)madt_buf->data))); + g_array_free(madt_buf, true); + aml_append(cpus_dev, dev); } } aml_append(sb_scope, cpus_dev); aml_append(table, sb_scope); + method = aml_method(event_handler_method, 0, AML_NOTSERIALIZED); + aml_append(method, aml_call0("\\_SB.CPUS." CPU_SCAN_METHOD)); + aml_append(table, method); + g_free(cphp_res_path); g_free(arch_ids); } diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index 940467ba14..3043769044 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -21,3 +21,7 @@ mhp_acpi_pc_dimm_delete_failed(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm delete cpuhp_acpi_invalid_idx_selected(uint32_t idx) "0x%"PRIx32 cpuhp_acpi_read_flags(uint32_t idx, uint8_t flags) "idx[0x%"PRIx32"] flags: 0x%"PRIx8 cpuhp_acpi_write_idx(uint32_t idx) "set active cpu idx: 0x%"PRIx32 +cpuhp_acpi_write_cmd(uint32_t idx, uint8_t cmd) "idx[0x%"PRIx32"] cmd: 0x%"PRIx8 +cpuhp_acpi_read_cmd_data(uint32_t idx, uint32_t data) "idx[0x%"PRIx32"] data: 0x%"PRIx32 +cpuhp_acpi_cpu_has_events(uint32_t idx, bool ins) "idx[0x%"PRIx32"] inserting: %d" +cpuhp_acpi_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]" diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index f345447af3..55c3166c0f 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -20,11 +20,13 @@ typedef struct AcpiCpuStatus { struct CPUState *cpu; uint64_t arch_id; + bool is_inserting; } AcpiCpuStatus; typedef struct CPUHotplugState { MemoryRegion ctrl_reg; uint32_t selector; + uint8_t command; uint32_t dev_count; AcpiCpuStatus *devs; } CPUHotplugState; @@ -41,7 +43,8 @@ typedef struct CPUHotplugFeatures { void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, hwaddr io_base, - const char *res_root); + const char *res_root, + const char *event_handler_method); extern const VMStateDescription vmstate_cpu_hotplug; #define VMSTATE_CPU_HOTPLUG(cpuhp, state) \ -- cgit v1.2.3-55-g7522 From 8872c25a26ccc2c2f62903b023212bbc0a4f5d39 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Tue, 14 Jun 2016 16:14:02 +0200 Subject: acpi: cpuhp: implement hot-remove parts of CPU hotplug interface it adds hw registers needed for handling CPU hot-remove and corresponding AML methods to request and eject a CPU with necessary hotplug callbacks in pc,piix4,ich9 code. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/cpu.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++++--- hw/acpi/ich9.c | 7 ++++ hw/acpi/piix4.c | 6 ++++ hw/acpi/trace-events | 5 ++- hw/i386/pc.c | 47 +++++++++++++++++++++++++++ include/hw/acpi/cpu.h | 8 +++++ 6 files changed, 158 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 811be8a2d3..483b80810e 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -30,6 +30,7 @@ static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size) case ACPI_CPU_FLAGS_OFFSET_RW: /* pack and return is_* fields */ val |= cdev->cpu ? 1 : 0; val |= cdev->is_inserting ? 2 : 0; + val |= cdev->is_removing ? 4 : 0; trace_cpuhp_acpi_read_flags(cpu_st->selector, val); break; case ACPI_CPU_CMD_DATA_OFFSET_RW: @@ -73,6 +74,22 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, if (data & 2) { /* clear insert event */ cdev->is_inserting = false; trace_cpuhp_acpi_clear_inserting_evt(cpu_st->selector); + } else if (data & 4) { /* clear remove event */ + cdev->is_removing = false; + trace_cpuhp_acpi_clear_remove_evt(cpu_st->selector); + } else if (data & 8) { + DeviceState *dev = NULL; + HotplugHandler *hotplug_ctrl = NULL; + + if (!cdev->cpu) { + trace_cpuhp_acpi_ejecting_invalid_cpu(cpu_st->selector); + break; + } + + trace_cpuhp_acpi_ejecting_cpu(cpu_st->selector); + dev = DEVICE(cdev->cpu); + hotplug_ctrl = qdev_get_hotplug_handler(dev); + hotplug_handler_unplug(hotplug_ctrl, dev, NULL); } break; case ACPI_CPU_CMD_OFFSET_WR: @@ -84,10 +101,10 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, do { cdev = &cpu_st->devs[iter]; - if (cdev->is_inserting) { + if (cdev->is_inserting || cdev->is_removing) { cpu_st->selector = iter; trace_cpuhp_acpi_cpu_has_events(cpu_st->selector, - cdev->is_inserting); + cdev->is_inserting, cdev->is_removing); break; } iter = iter + 1 < cpu_st->dev_count ? iter + 1 : 0; @@ -163,6 +180,34 @@ void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, } } +void acpi_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, + CPUHotplugState *cpu_st, + DeviceState *dev, Error **errp) +{ + AcpiCpuStatus *cdev; + + cdev = get_cpu_status(cpu_st, dev); + if (!cdev) { + return; + } + + cdev->is_removing = true; + acpi_send_event(DEVICE(hotplug_dev), ACPI_CPU_HOTPLUG_STATUS); +} + +void acpi_cpu_unplug_cb(CPUHotplugState *cpu_st, + DeviceState *dev, Error **errp) +{ + AcpiCpuStatus *cdev; + + cdev = get_cpu_status(cpu_st, dev); + if (!cdev) { + return; + } + + cdev->cpu = NULL; +} + static const VMStateDescription vmstate_cpuhp_sts = { .name = "CPU hotplug device state", .version_id = 1, @@ -170,6 +215,7 @@ static const VMStateDescription vmstate_cpuhp_sts = { .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_BOOL(is_inserting, AcpiCpuStatus), + VMSTATE_BOOL(is_removing, AcpiCpuStatus), VMSTATE_END_OF_LIST() } }; @@ -194,12 +240,15 @@ const VMStateDescription vmstate_cpu_hotplug = { #define CPU_STS_METHOD "CSTA" #define CPU_SCAN_METHOD "CSCN" #define CPU_NOTIFY_METHOD "CTFY" +#define CPU_EJECT_METHOD "CEJ0" #define CPU_ENABLED "CPEN" #define CPU_SELECTOR "CSEL" #define CPU_COMMAND "CCMD" #define CPU_DATA "CDAT" #define CPU_INSERT_EVENT "CINS" +#define CPU_REMOVE_EVENT "CRMV" +#define CPU_EJECT_EVENT "CEJ0" void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, hwaddr io_base, @@ -248,7 +297,11 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(field, aml_named_field(CPU_ENABLED, 1)); /* (read) 1 if has a insert event. (write) 1 to clear event */ aml_append(field, aml_named_field(CPU_INSERT_EVENT, 1)); - aml_append(field, aml_reserved_field(6)); + /* (read) 1 if has a remove event. (write) 1 to clear event */ + aml_append(field, aml_named_field(CPU_REMOVE_EVENT, 1)); + /* initiates device eject, write only */ + aml_append(field, aml_named_field(CPU_EJECT_EVENT, 1)); + aml_append(field, aml_reserved_field(4)); aml_append(field, aml_named_field(CPU_COMMAND, 8)); aml_append(cpu_ctrl_dev, field); @@ -272,6 +325,8 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, Aml *cpu_cmd = aml_name("%s.%s", cphp_res_path, CPU_COMMAND); Aml *cpu_data = aml_name("%s.%s", cphp_res_path, CPU_DATA); Aml *ins_evt = aml_name("%s.%s", cphp_res_path, CPU_INSERT_EVENT); + Aml *rm_evt = aml_name("%s.%s", cphp_res_path, CPU_REMOVE_EVENT); + Aml *ej_evt = aml_name("%s.%s", cphp_res_path, CPU_EJECT_EVENT); aml_append(cpus_dev, aml_name_decl("_HID", aml_string("ACPI0010"))); aml_append(cpus_dev, aml_name_decl("_CID", aml_eisaid("PNP0A05"))); @@ -308,18 +363,31 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, } aml_append(cpus_dev, method); + method = aml_method(CPU_EJECT_METHOD, 1, AML_SERIALIZED); + { + Aml *idx = aml_arg(0); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(idx, cpu_selector)); + aml_append(method, aml_store(one, ej_evt)); + aml_append(method, aml_release(ctrl_lock)); + } + aml_append(cpus_dev, method); + method = aml_method(CPU_SCAN_METHOD, 0, AML_SERIALIZED); { + Aml *else_ctx; Aml *while_ctx; Aml *has_event = aml_local(0); Aml *dev_chk = aml_int(1); + Aml *eject_req = aml_int(3); Aml *next_cpu_cmd = aml_int(CPHP_GET_NEXT_CPU_WITH_EVENT_CMD); aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); aml_append(method, aml_store(one, has_event)); while_ctx = aml_while(aml_equal(has_event, one)); { - /* clear loop exit condition, ins_evt check + /* clear loop exit condition, ins_evt/rm_evt checks * will set it to 1 while next_cpu_cmd returns a CPU * with events */ aml_append(while_ctx, aml_store(zero, has_event)); @@ -332,6 +400,16 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(ifctx, aml_store(one, has_event)); } aml_append(while_ctx, ifctx); + else_ctx = aml_else(); + ifctx = aml_if(aml_equal(rm_evt, one)); + { + aml_append(ifctx, + aml_call2(CPU_NOTIFY_METHOD, cpu_data, eject_req)); + aml_append(ifctx, aml_store(one, rm_evt)); + aml_append(ifctx, aml_store(one, has_event)); + } + aml_append(else_ctx, ifctx); + aml_append(while_ctx, else_ctx); } aml_append(method, while_ctx); aml_append(method, aml_release(ctrl_lock)); @@ -373,6 +451,10 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_buffer(madt_buf->len, (uint8_t *)madt_buf->data))); g_array_free(madt_buf, true); + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); + aml_append(method, aml_call1(CPU_EJECT_METHOD, uid)); + aml_append(dev, method); + aml_append(cpus_dev, dev); } } diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 9a81da82d3..0abe2ce5ee 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -481,6 +481,10 @@ void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev, acpi_memory_unplug_request_cb(hotplug_dev, &lpc->pm.acpi_memory_hotplug, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) && + !lpc->pm.cpu_hotplug_legacy) { + acpi_cpu_unplug_request_cb(hotplug_dev, &lpc->pm.cpuhp_state, + dev, errp); } else { error_setg(errp, "acpi: device unplug request for not supported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -495,6 +499,9 @@ void ich9_pm_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, if (lpc->pm.acpi_memory_hotplug.is_enabled && object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_unplug_cb(&lpc->pm.acpi_memory_hotplug, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) && + !lpc->pm.cpu_hotplug_legacy) { + acpi_cpu_unplug_cb(&lpc->pm.cpuhp_state, dev, errp); } else { error_setg(errp, "acpi: device unplug for not supported device" " type: %s", object_get_typename(OBJECT(dev))); diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 6d24cb535b..8cdc1da9ac 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -378,6 +378,9 @@ static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev, } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { acpi_pcihp_device_unplug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) && + !s->cpu_hotplug_legacy) { + acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp); } else { error_setg(errp, "acpi: device unplug request for not supported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -392,6 +395,9 @@ static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev, if (s->acpi_memory_hotplug.is_enabled && object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_unplug_cb(&s->acpi_memory_hotplug, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) && + !s->cpu_hotplug_legacy) { + acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp); } else { error_setg(errp, "acpi: device unplug for not supported device" " type: %s", object_get_typename(OBJECT(dev))); diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index 3043769044..c1279b1547 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -23,5 +23,8 @@ cpuhp_acpi_read_flags(uint32_t idx, uint8_t flags) "idx[0x%"PRIx32"] flags: 0x%" cpuhp_acpi_write_idx(uint32_t idx) "set active cpu idx: 0x%"PRIx32 cpuhp_acpi_write_cmd(uint32_t idx, uint8_t cmd) "idx[0x%"PRIx32"] cmd: 0x%"PRIx8 cpuhp_acpi_read_cmd_data(uint32_t idx, uint32_t data) "idx[0x%"PRIx32"] data: 0x%"PRIx32 -cpuhp_acpi_cpu_has_events(uint32_t idx, bool ins) "idx[0x%"PRIx32"] inserting: %d" +cpuhp_acpi_cpu_has_events(uint32_t idx, bool ins, bool rm) "idx[0x%"PRIx32"] inserting: %d, removing: %d" cpuhp_acpi_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]" +cpuhp_acpi_clear_remove_evt(uint32_t idx) "idx[0x%"PRIx32"]" +cpuhp_acpi_ejecting_invalid_cpu(uint32_t idx) "0x%"PRIx32 +cpuhp_acpi_ejecting_cpu(uint32_t idx) "0x%"PRIx32 diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 7198ed533c..dbfba5cad1 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1707,6 +1707,49 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev, out: error_propagate(errp, local_err); } +static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + HotplugHandlerClass *hhc; + Error *local_err = NULL; + PCMachineState *pcms = PC_MACHINE(hotplug_dev); + + hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); + hhc->unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + + if (local_err) { + goto out; + } + + out: + error_propagate(errp, local_err); + +} + +static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + HotplugHandlerClass *hhc; + Error *local_err = NULL; + PCMachineState *pcms = PC_MACHINE(hotplug_dev); + + hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); + hhc->unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + + if (local_err) { + goto out; + } + + /* + * TODO: enable unplug once generic CPU remove bits land + * for now guest will be able to eject CPU ACPI wise but + * it will come back again on machine reset. + */ + /* object_unparent(OBJECT(dev)); */ + + out: + error_propagate(errp, local_err); +} static void pc_machine_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) @@ -1723,6 +1766,8 @@ static void pc_machine_device_unplug_request_cb(HotplugHandler *hotplug_dev, { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { pc_dimm_unplug_request(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + pc_cpu_unplug_request_cb(hotplug_dev, dev, errp); } else { error_setg(errp, "acpi: device unplug request for not supported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -1734,6 +1779,8 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev, { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { pc_dimm_unplug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + pc_cpu_unplug_cb(hotplug_dev, dev, errp); } else { error_setg(errp, "acpi: device unplug for not supported device" " type: %s", object_get_typename(OBJECT(dev))); diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index 55c3166c0f..f334221d85 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -21,6 +21,7 @@ typedef struct AcpiCpuStatus { struct CPUState *cpu; uint64_t arch_id; bool is_inserting; + bool is_removing; } AcpiCpuStatus; typedef struct CPUHotplugState { @@ -34,6 +35,13 @@ typedef struct CPUHotplugState { void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp); +void acpi_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, + CPUHotplugState *cpu_st, + DeviceState *dev, Error **errp); + +void acpi_cpu_unplug_cb(CPUHotplugState *cpu_st, + DeviceState *dev, Error **errp); + void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, CPUHotplugState *state, hwaddr base_addr); -- cgit v1.2.3-55-g7522 From 76623d00ae578335f6c9f31cd13fc5bf1931dbc3 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 22 Apr 2016 19:06:36 +0200 Subject: acpi: cpuhp: add cpu._OST handling it adds HW and AML parts for CPU_Device._OST method handling to allow OSPM reports status of hot-(un)plug operation. And extends QMP command query-acpi-ospm-status to report CPU's OST info along with already reported PC-DIMM devices. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/cpu.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++ hw/acpi/ich9.c | 3 ++ hw/acpi/piix4.c | 3 ++ hw/acpi/trace-events | 2 ++ include/hw/acpi/cpu.h | 4 +++ qapi-schema.json | 3 +- 6 files changed, 96 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 483b80810e..401ac0dab2 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -2,6 +2,7 @@ #include "hw/boards.h" #include "hw/acpi/cpu.h" #include "qapi/error.h" +#include "qapi-event.h" #include "trace.h" #define ACPI_CPU_HOTPLUG_REG_LEN 12 @@ -12,9 +13,42 @@ enum { CPHP_GET_NEXT_CPU_WITH_EVENT_CMD = 0, + CPHP_OST_EVENT_CMD = 1, + CPHP_OST_STATUS_CMD = 2, CPHP_CMD_MAX }; +static ACPIOSTInfo *acpi_cpu_device_status(int idx, AcpiCpuStatus *cdev) +{ + ACPIOSTInfo *info = g_new0(ACPIOSTInfo, 1); + + info->slot_type = ACPI_SLOT_TYPE_CPU; + info->slot = g_strdup_printf("%d", idx); + info->source = cdev->ost_event; + info->status = cdev->ost_status; + if (cdev->cpu) { + DeviceState *dev = DEVICE(cdev->cpu); + if (dev->id) { + info->device = g_strdup(dev->id); + info->has_device = true; + } + } + return info; +} + +void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list) +{ + int i; + + for (i = 0; i < cpu_st->dev_count; i++) { + ACPIOSTInfoList *elem = g_new0(ACPIOSTInfoList, 1); + elem->value = acpi_cpu_device_status(i, &cpu_st->devs[i]); + elem->next = NULL; + **list = elem; + *list = &elem->next; + } +} + static uint64_t cpu_hotplug_rd(void *opaque, hwaddr addr, unsigned size) { uint64_t val = 0; @@ -54,6 +88,7 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, { CPUHotplugState *cpu_st = opaque; AcpiCpuStatus *cdev; + ACPIOSTInfo *info; assert(cpu_st->dev_count); @@ -112,6 +147,28 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, } } break; + case ACPI_CPU_CMD_DATA_OFFSET_RW: + switch (cpu_st->command) { + case CPHP_OST_EVENT_CMD: { + cdev = &cpu_st->devs[cpu_st->selector]; + cdev->ost_event = data; + trace_cpuhp_acpi_write_ost_ev(cpu_st->selector, cdev->ost_event); + break; + } + case CPHP_OST_STATUS_CMD: { + cdev = &cpu_st->devs[cpu_st->selector]; + cdev->ost_status = data; + info = acpi_cpu_device_status(cpu_st->selector, cdev); + qapi_event_send_acpi_device_ost(info, &error_abort); + qapi_free_ACPIOSTInfo(info); + trace_cpuhp_acpi_write_ost_status(cpu_st->selector, + cdev->ost_status); + break; + } + default: + break; + } + break; default: break; } @@ -216,6 +273,8 @@ static const VMStateDescription vmstate_cpuhp_sts = { .fields = (VMStateField[]) { VMSTATE_BOOL(is_inserting, AcpiCpuStatus), VMSTATE_BOOL(is_removing, AcpiCpuStatus), + VMSTATE_UINT32(ost_event, AcpiCpuStatus), + VMSTATE_UINT32(ost_status, AcpiCpuStatus), VMSTATE_END_OF_LIST() } }; @@ -241,6 +300,7 @@ const VMStateDescription vmstate_cpu_hotplug = { #define CPU_SCAN_METHOD "CSCN" #define CPU_NOTIFY_METHOD "CTFY" #define CPU_EJECT_METHOD "CEJ0" +#define CPU_OST_METHOD "COST" #define CPU_ENABLED "CPEN" #define CPU_SELECTOR "CSEL" @@ -416,6 +476,22 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, } aml_append(cpus_dev, method); + method = aml_method(CPU_OST_METHOD, 4, AML_SERIALIZED); + { + Aml *uid = aml_arg(0); + Aml *ev_cmd = aml_int(CPHP_OST_EVENT_CMD); + Aml *st_cmd = aml_int(CPHP_OST_STATUS_CMD); + + aml_append(method, aml_acquire(ctrl_lock, 0xFFFF)); + aml_append(method, aml_store(uid, cpu_selector)); + aml_append(method, aml_store(ev_cmd, cpu_cmd)); + aml_append(method, aml_store(aml_arg(1), cpu_data)); + aml_append(method, aml_store(st_cmd, cpu_cmd)); + aml_append(method, aml_store(aml_arg(2), cpu_data)); + aml_append(method, aml_release(ctrl_lock)); + } + aml_append(cpus_dev, method); + /* build Processor object for each processor */ for (i = 0; i < arch_ids->len; i++) { Aml *dev; @@ -455,6 +531,12 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(method, aml_call1(CPU_EJECT_METHOD, uid)); aml_append(dev, method); + method = aml_method("_OST", 3, AML_SERIALIZED); + aml_append(method, + aml_call4(CPU_OST_METHOD, uid, aml_arg(0), + aml_arg(1), aml_arg(2)) + ); + aml_append(dev, method); aml_append(cpus_dev, dev); } } diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 0abe2ce5ee..d12cc62381 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -513,4 +513,7 @@ void ich9_pm_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) ICH9LPCState *s = ICH9_LPC_DEVICE(adev); acpi_memory_ospm_status(&s->pm.acpi_memory_hotplug, list); + if (!s->pm.cpu_hotplug_legacy) { + acpi_cpu_ospm_status(&s->pm.cpuhp_state, list); + } } diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 8cdc1da9ac..858244a661 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -616,6 +616,9 @@ static void piix4_ospm_status(AcpiDeviceIf *adev, ACPIOSTInfoList ***list) PIIX4PMState *s = PIIX4_PM(adev); acpi_memory_ospm_status(&s->acpi_memory_hotplug, list); + if (!s->cpu_hotplug_legacy) { + acpi_cpu_ospm_status(&s->cpuhp_state, list); + } } static void piix4_send_gpe(AcpiDeviceIf *adev, AcpiEventStatusBits ev) diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index c1279b1547..5aa3ba67c8 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -28,3 +28,5 @@ cpuhp_acpi_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]" cpuhp_acpi_clear_remove_evt(uint32_t idx) "idx[0x%"PRIx32"]" cpuhp_acpi_ejecting_invalid_cpu(uint32_t idx) "0x%"PRIx32 cpuhp_acpi_ejecting_cpu(uint32_t idx) "0x%"PRIx32 +cpuhp_acpi_write_ost_ev(uint32_t slot, uint32_t ev) "idx[0x%"PRIx32"] OST EVENT: 0x%"PRIx32 +cpuhp_acpi_write_ost_status(uint32_t slot, uint32_t st) "idx[0x%"PRIx32"] OST STATUS: 0x%"PRIx32 diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index f334221d85..980a83c4b8 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -22,6 +22,8 @@ typedef struct AcpiCpuStatus { uint64_t arch_id; bool is_inserting; bool is_removing; + uint32_t ost_event; + uint32_t ost_status; } AcpiCpuStatus; typedef struct CPUHotplugState { @@ -54,6 +56,8 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, const char *res_root, const char *event_handler_method); +void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list); + extern const VMStateDescription vmstate_cpu_hotplug; #define VMSTATE_CPU_HOTPLUG(cpuhp, state) \ VMSTATE_STRUCT(cpuhp, state, 1, \ diff --git a/qapi-schema.json b/qapi-schema.json index 0964eece6d..84b6708125 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -4079,8 +4079,9 @@ ## @ACPISlotType # # @DIMM: memory slot +# @CPU: logical CPU slot (since 2.7) # -{ 'enum': 'ACPISlotType', 'data': [ 'DIMM' ] } +{ 'enum': 'ACPISlotType', 'data': [ 'DIMM', 'CPU' ] } ## @ACPIOSTInfo # -- cgit v1.2.3-55-g7522 From 679dd1a957df418453efdd3ed2914dba5cd73773 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Wed, 15 Jun 2016 11:25:23 +0200 Subject: pc: use new CPU hotplug interface since 2.7 machine type For compatibility reasons PC/Q35 will start with legacy CPU hotplug interface by default but with new CPU hotplug AML code since 2.7 machine type. That way legacy firmware that doesn't use QEMU generated ACPI tables will be able to continue using legacy CPU hotplug interface. While new machine type, with firmware supporting QEMU provided ACPI tables, will generate new CPU hotplug AML, which will switch to new CPU hotplug interface when guest OS executes its _INI method on ACPI tables loading. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/cpu.c | 9 +++++++++ hw/acpi/cpu_hotplug.c | 21 ++++++++++++++++++++- hw/acpi/ich9.c | 33 +++++++++++++++++++++++++++++++++ hw/acpi/piix4.c | 32 ++++++++++++++++++++++++++++++++ hw/i386/acpi-build.c | 12 +++++++++++- hw/i386/pc_piix.c | 2 ++ hw/i386/pc_q35.c | 2 ++ include/hw/acpi/cpu.h | 1 + include/hw/acpi/cpu_hotplug.h | 6 ++++++ include/hw/i386/pc.h | 2 ++ 10 files changed, 118 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 401ac0dab2..c13b65c2c9 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -373,6 +373,15 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_append(field, aml_named_field(CPU_DATA, 32)); aml_append(cpu_ctrl_dev, field); + if (opts.has_legacy_cphp) { + method = aml_method("_INI", 0, AML_SERIALIZED); + /* switch off legacy CPU hotplug HW and use new one, + * on reboot system is in new mode and writing 0 + * in CPU_SELECTOR selects BSP, which is NOP at + * the time _INI is called */ + aml_append(method, aml_store(zero, aml_name(CPU_SELECTOR))); + aml_append(cpu_ctrl_dev, method); + } } aml_append(sb_scope, cpu_ctrl_dev); diff --git a/hw/acpi/cpu_hotplug.c b/hw/acpi/cpu_hotplug.c index fe75bd9ac9..e19d902063 100644 --- a/hw/acpi/cpu_hotplug.c +++ b/hw/acpi/cpu_hotplug.c @@ -34,7 +34,15 @@ static uint64_t cpu_status_read(void *opaque, hwaddr addr, unsigned int size) static void cpu_status_write(void *opaque, hwaddr addr, uint64_t data, unsigned int size) { - /* TODO: implement VCPU removal on guest signal that CPU can be removed */ + /* firmware never used to write in CPU present bitmap so use + this fact as means to switch QEMU into modern CPU hotplug + mode by writing 0 at the beginning of legacy CPU bitmap + */ + if (addr == 0 && data == 0) { + AcpiCpuHotplug *cpus = opaque; + object_property_set_bool(cpus->device, false, "cpu-hotplug-legacy", + &error_abort); + } } static const MemoryRegionOps AcpiCpuHotplug_ops = { @@ -83,6 +91,17 @@ void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, memory_region_init_io(&gpe_cpu->io, owner, &AcpiCpuHotplug_ops, gpe_cpu, "acpi-cpu-hotplug", ACPI_GPE_PROC_LEN); memory_region_add_subregion(parent, base, &gpe_cpu->io); + gpe_cpu->device = owner; +} + +void acpi_switch_to_modern_cphp(AcpiCpuHotplug *gpe_cpu, + CPUHotplugState *cpuhp_state, + uint16_t io_port) +{ + MemoryRegion *parent = pci_address_space_io(PCI_DEVICE(gpe_cpu->device)); + + memory_region_del_subregion(parent, &gpe_cpu->io); + cpu_hotplug_hw_init(parent, gpe_cpu->device, cpuhp_state, io_port); } void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index d12cc62381..e5a3c18e52 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -189,6 +189,33 @@ static const VMStateDescription vmstate_tco_io_state = { } }; +static bool vmstate_test_use_cpuhp(void *opaque) +{ + ICH9LPCPMRegs *s = opaque; + return !s->cpu_hotplug_legacy; +} + +static int vmstate_cpuhp_pre_load(void *opaque) +{ + ICH9LPCPMRegs *s = opaque; + Object *obj = OBJECT(s->gpe_cpu.device); + object_property_set_bool(obj, false, "cpu-hotplug-legacy", &error_abort); + return 0; +} + +static const VMStateDescription vmstate_cpuhp_state = { + .name = "ich9_pm/cpuhp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .needed = vmstate_test_use_cpuhp, + .pre_load = vmstate_cpuhp_pre_load, + .fields = (VMStateField[]) { + VMSTATE_CPU_HOTPLUG(cpuhp_state, ICH9LPCPMRegs), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_ich9_pm = { .name = "ich9_pm", .version_id = 1, @@ -209,6 +236,7 @@ const VMStateDescription vmstate_ich9_pm = { .subsections = (const VMStateDescription*[]) { &vmstate_memhp_state, &vmstate_tco_io_state, + &vmstate_cpuhp_state, NULL } }; @@ -318,6 +346,11 @@ static void ich9_pm_set_cpu_hotplug_legacy(Object *obj, bool value, { ICH9LPCState *s = ICH9_LPC_DEVICE(obj); + assert(!value); + if (s->pm.cpu_hotplug_legacy && value == false) { + acpi_switch_to_modern_cphp(&s->pm.gpe_cpu, &s->pm.cpuhp_state, + ICH9_CPU_HOTPLUG_IO_BASE); + } s->pm.cpu_hotplug_legacy = value; } diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 858244a661..2adc246b00 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -276,6 +276,32 @@ static const VMStateDescription vmstate_memhp_state = { } }; +static bool vmstate_test_use_cpuhp(void *opaque) +{ + PIIX4PMState *s = opaque; + return !s->cpu_hotplug_legacy; +} + +static int vmstate_cpuhp_pre_load(void *opaque) +{ + Object *obj = OBJECT(opaque); + object_property_set_bool(obj, false, "cpu-hotplug-legacy", &error_abort); + return 0; +} + +static const VMStateDescription vmstate_cpuhp_state = { + .name = "piix4_pm/cpuhp", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .needed = vmstate_test_use_cpuhp, + .pre_load = vmstate_cpuhp_pre_load, + .fields = (VMStateField[]) { + VMSTATE_CPU_HOTPLUG(cpuhp_state, PIIX4PMState), + VMSTATE_END_OF_LIST() + } +}; + /* qemu-kvm 1.2 uses version 3 but advertised as 2 * To support incoming qemu-kvm 1.2 migration, change version_id * and minimum_version_id to 2 below (which breaks migration from @@ -310,6 +336,7 @@ static const VMStateDescription vmstate_acpi = { }, .subsections = (const VMStateDescription*[]) { &vmstate_memhp_state, + &vmstate_cpuhp_state, NULL } }; @@ -585,6 +612,11 @@ static void piix4_set_cpu_hotplug_legacy(Object *obj, bool value, Error **errp) { PIIX4PMState *s = PIIX4_PM(obj); + assert(!value); + if (s->cpu_hotplug_legacy && value == false) { + acpi_switch_to_modern_cphp(&s->gpe_cpu, &s->cpuhp_state, + PIIX4_CPU_HOTPLUG_IO_BASE); + } s->cpu_hotplug_legacy = value; } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index e35a446a9d..20e5b4948e 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -33,6 +33,7 @@ #include "hw/timer/hpet.h" #include "hw/acpi/acpi-defs.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/cpu.h" #include "hw/nvram/fw_cfg.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/loader.h" @@ -1895,6 +1896,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, GPtrArray *mem_ranges = g_ptr_array_new_with_free_func(crs_range_free); GPtrArray *io_ranges = g_ptr_array_new_with_free_func(crs_range_free); PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(machine); uint32_t nr_mem = machine->ram_slots; int root_bus_limit = 0xFF; PCIBus *bus = NULL; @@ -1950,7 +1952,15 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, build_q35_pci0_int(dsdt); } - build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base); + if (pcmc->legacy_cpu_hotplug) { + build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base); + } else { + CPUHotplugFeatures opts = { + .apci_1_compatible = true, .has_legacy_cphp = true + }; + build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base, + "\\_SB.PCI0", "\\_GPE._E02"); + } build_memory_hotplug_aml(dsdt, nr_mem, pm->mem_hp_io_base, pm->mem_hp_io_len); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 53bc968bd0..c7d70af253 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -445,9 +445,11 @@ DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", NULL, static void pc_i440fx_2_6_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_7_machine_options(m); m->is_default = 0; m->alias = NULL; + pcmc->legacy_cpu_hotplug = true; SET_MACHINE_COMPAT(m, PC_COMPAT_2_6); } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index e4b541f7b2..97a8835eea 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -294,8 +294,10 @@ DEFINE_Q35_MACHINE(v2_7, "pc-q35-2.7", NULL, static void pc_q35_2_6_machine_options(MachineClass *m) { + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_2_7_machine_options(m); m->alias = NULL; + pcmc->legacy_cpu_hotplug = true; SET_MACHINE_COMPAT(m, PC_COMPAT_2_6); } diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index 980a83c4b8..89ce172941 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -49,6 +49,7 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, typedef struct CPUHotplugFeatures { bool apci_1_compatible; + bool has_legacy_cphp; } CPUHotplugFeatures; void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, diff --git a/include/hw/acpi/cpu_hotplug.h b/include/hw/acpi/cpu_hotplug.h index 6fef67ec14..b995ef2ebd 100644 --- a/include/hw/acpi/cpu_hotplug.h +++ b/include/hw/acpi/cpu_hotplug.h @@ -16,8 +16,10 @@ #include "hw/acpi/pc-hotplug.h" #include "hw/acpi/aml-build.h" #include "hw/hotplug.h" +#include "hw/acpi/cpu.h" typedef struct AcpiCpuHotplug { + Object *device; MemoryRegion io; uint8_t sts[ACPI_GPE_PROC_LEN]; } AcpiCpuHotplug; @@ -28,6 +30,10 @@ void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, AcpiCpuHotplug *gpe_cpu, uint16_t base); +void acpi_switch_to_modern_cphp(AcpiCpuHotplug *gpe_cpu, + CPUHotplugState *cpuhp_state, + uint16_t io_port); + void build_legacy_cpu_hotplug_aml(Aml *ctx, MachineState *machine, uint16_t io_base); #endif diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 9e239297e8..884224ed19 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -137,6 +137,8 @@ struct PCMachineClass { /* TSC rate migration: */ bool save_tsc_khz; + /* generate legacy CPU hotplug AML */ + bool legacy_cpu_hotplug; }; #define TYPE_PC_MACHINE "generic-pc-machine" -- cgit v1.2.3-55-g7522 From 1f3aba377d2a531453f018c70de2580a142c74c9 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 16 Jun 2016 14:23:48 +0200 Subject: pc: acpi: drop intermediate PCMachineState.node_cpu PCMachineState.node_cpu was used for mapping APIC ID to numa node id as CPU entries in SRAT used to be built on sparse APIC ID bitmap (up to apic_id_limit). However since commit 5803fce pc: acpi: SRAT: create only valid processor lapic entries CPU entries in SRAT aren't build using apic bitmap but using 0..maxcpus index instead which is also used for creating numa_info[x].node_cpu map. So instead of doing useless intermediate conversion from 1. node by cpu index -> node by apic id i.e. numa_info[x].node_cpu -> PCMachineState.node_cpu 2. apic id -> srat entry PMX PCMachineState.node_cpu[apic id] -> PMX value use numa_info[x].node_cpu map directly like ARM does and do 1. numa_info[x].node_cpu -> PMX value using index in range 0..maxcpus and drop not necessary PCMachineState.node_cpu and related code. That also removes the last (not counting legacy hotplug) dependency of ACPI code on apic_id_limit and need to allocate huge sparse PCMachineState.node_cpu array in case of 32-bit APIC IDs. Signed-off-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 11 ++++++++--- hw/i386/pc.c | 16 +--------------- include/hw/i386/pc.h | 1 - 3 files changed, 9 insertions(+), 19 deletions(-) (limited to 'include') diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 20e5b4948e..5a594be8ee 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -44,6 +44,7 @@ #include "hw/acpi/tpm.h" #include "sysemu/tpm_backend.h" #include "hw/timer/mc146818rtc_regs.h" +#include "sysemu/numa.h" /* Supported chipsets: */ #include "hw/acpi/piix4.h" @@ -2328,7 +2329,6 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) AcpiSratMemoryAffinity *numamem; int i; - uint64_t curnode; int srat_start, numa_start, slots; uint64_t mem_len, mem_base, next_base; MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -2344,14 +2344,19 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) srat->reserved1 = cpu_to_le32(1); for (i = 0; i < apic_ids->len; i++) { + int j; int apic_id = apic_ids->cpus[i].arch_id; core = acpi_data_push(table_data, sizeof *core); core->type = ACPI_SRAT_PROCESSOR_APIC; core->length = sizeof(*core); core->local_apic_id = apic_id; - curnode = pcms->node_cpu[apic_id]; - core->proximity_lo = curnode; + for (j = 0; j < nb_numa_nodes; j++) { + if (test_bit(i, numa_info[j].node_cpu)) { + core->proximity_lo = j; + break; + } + } memset(core->proximity_hi, 0, 3); core->local_sapic_eid = 0; core->flags = cpu_to_le32(1); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index dbfba5cad1..b8fead37e2 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1179,7 +1179,7 @@ void pc_machine_done(Notifier *notifier, void *data) void pc_guest_info_init(PCMachineState *pcms) { - int i, j; + int i; pcms->apic_xrupt_override = kvm_allows_irq0_override(); pcms->numa_nodes = nb_numa_nodes; @@ -1189,20 +1189,6 @@ void pc_guest_info_init(PCMachineState *pcms) pcms->node_mem[i] = numa_info[i].node_mem; } - pcms->node_cpu = g_malloc0(pcms->apic_id_limit * - sizeof *pcms->node_cpu); - - for (i = 0; i < max_cpus; i++) { - unsigned int apic_id = x86_cpu_apic_id_from_index(i); - assert(apic_id < pcms->apic_id_limit); - for (j = 0; j < nb_numa_nodes; j++) { - if (test_bit(i, numa_info[j].node_cpu)) { - pcms->node_cpu[apic_id] = j; - break; - } - } - } - pcms->machine_done.notify = pc_machine_done; qemu_add_machine_init_done_notifier(&pcms->machine_done); } diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 884224ed19..948ed0c277 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -72,7 +72,6 @@ struct PCMachineState { /* NUMA information: */ uint64_t numa_nodes; uint64_t *node_mem; - uint64_t *node_cpu; }; #define PC_MACHINE_ACPI_DEVICE_PROP "acpi-device" -- cgit v1.2.3-55-g7522 From 6798e245a3c7e6f34f66a548a108cfa8eba79b7d Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 10 Jun 2016 11:04:09 +0200 Subject: virtio-bus: common ioeventfd infrastructure Introduce a set of ioeventfd callbacks on the virtio-bus level that can be implemented by the individual transports. At the virtio-bus level, do common handling for host notifiers (which is actually most of it). Two things of note: - When setting the host notifier, we only switch from/to the generic ioeventfd handler. This fixes a latent bug where we had no ioeventfd assigned for a certain window. - We always iterate over all possible virtio queues, even though ccw (currently) has a lower limit. It does not really matter here. Signed-off-by: Cornelia Huck Reviewed-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-bus.c | 132 +++++++++++++++++++++++++++++++++++++++++ include/hw/virtio/virtio-bus.h | 30 ++++++++++ 2 files changed, 162 insertions(+) (limited to 'include') diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index 574f0e23f8..131376027b 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -146,6 +146,138 @@ void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config) } } +/* + * This function handles both assigning the ioeventfd handler and + * registering it with the kernel. + * assign: register/deregister ioeventfd with the kernel + * set_handler: use the generic ioeventfd handler + */ +static int set_host_notifier_internal(DeviceState *proxy, VirtioBusState *bus, + int n, bool assign, bool set_handler) +{ + VirtIODevice *vdev = virtio_bus_get_device(bus); + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); + VirtQueue *vq = virtio_get_queue(vdev, n); + EventNotifier *notifier = virtio_queue_get_host_notifier(vq); + int r = 0; + + if (assign) { + r = event_notifier_init(notifier, 1); + if (r < 0) { + error_report("%s: unable to init event notifier: %d", __func__, r); + return r; + } + virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler); + r = k->ioeventfd_assign(proxy, notifier, n, assign); + if (r < 0) { + error_report("%s: unable to assign ioeventfd: %d", __func__, r); + virtio_queue_set_host_notifier_fd_handler(vq, false, false); + event_notifier_cleanup(notifier); + return r; + } + } else { + virtio_queue_set_host_notifier_fd_handler(vq, false, false); + k->ioeventfd_assign(proxy, notifier, n, assign); + event_notifier_cleanup(notifier); + } + return r; +} + +void virtio_bus_start_ioeventfd(VirtioBusState *bus) +{ + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); + DeviceState *proxy = DEVICE(BUS(bus)->parent); + VirtIODevice *vdev; + int n, r; + + if (!k->ioeventfd_started || k->ioeventfd_started(proxy)) { + return; + } + if (k->ioeventfd_disabled(proxy)) { + return; + } + vdev = virtio_bus_get_device(bus); + for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { + if (!virtio_queue_get_num(vdev, n)) { + continue; + } + r = set_host_notifier_internal(proxy, bus, n, true, true); + if (r < 0) { + goto assign_error; + } + } + k->ioeventfd_set_started(proxy, true, false); + return; + +assign_error: + while (--n >= 0) { + if (!virtio_queue_get_num(vdev, n)) { + continue; + } + + r = set_host_notifier_internal(proxy, bus, n, false, false); + assert(r >= 0); + } + k->ioeventfd_set_started(proxy, false, true); + error_report("%s: failed. Fallback to userspace (slower).", __func__); +} + +void virtio_bus_stop_ioeventfd(VirtioBusState *bus) +{ + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); + DeviceState *proxy = DEVICE(BUS(bus)->parent); + VirtIODevice *vdev; + int n, r; + + if (!k->ioeventfd_started || !k->ioeventfd_started(proxy)) { + return; + } + vdev = virtio_bus_get_device(bus); + for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { + if (!virtio_queue_get_num(vdev, n)) { + continue; + } + r = set_host_notifier_internal(proxy, bus, n, false, false); + assert(r >= 0); + } + k->ioeventfd_set_started(proxy, false, false); +} + +/* + * This function switches from/to the generic ioeventfd handler. + * assign==false means 'use generic ioeventfd handler'. + */ +int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign) +{ + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); + DeviceState *proxy = DEVICE(BUS(bus)->parent); + VirtIODevice *vdev = virtio_bus_get_device(bus); + VirtQueue *vq = virtio_get_queue(vdev, n); + + if (!k->ioeventfd_started) { + return -ENOSYS; + } + if (assign) { + /* + * Stop using the generic ioeventfd, we are doing eventfd handling + * ourselves below + */ + k->ioeventfd_set_disabled(proxy, true); + } + /* + * Just switch the handler, don't deassign the ioeventfd. + * Otherwise, there's a window where we don't have an + * ioeventfd and we may end up with a notification where + * we don't expect one. + */ + virtio_queue_set_host_notifier_fd_handler(vq, assign, !assign); + if (!assign) { + /* Use generic ioeventfd handler again. */ + k->ioeventfd_set_disabled(proxy, false); + } + return 0; +} + static char *virtio_bus_get_dev_path(DeviceState *dev) { BusState *bus = qdev_get_parent_bus(dev); diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index 3f2c1363d0..9637f80f4d 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -70,6 +70,29 @@ typedef struct VirtioBusClass { */ void (*device_unplugged)(DeviceState *d); int (*query_nvectors)(DeviceState *d); + /* + * ioeventfd handling: if the transport implements ioeventfd_started, + * it must implement the other ioeventfd callbacks as well + */ + /* Returns true if the ioeventfd has been started for the device. */ + bool (*ioeventfd_started)(DeviceState *d); + /* + * Sets the 'ioeventfd started' state after the ioeventfd has been + * started/stopped for the device. err signifies whether an error + * had occurred. + */ + void (*ioeventfd_set_started)(DeviceState *d, bool started, bool err); + /* Returns true if the ioeventfd has been disabled for the device. */ + bool (*ioeventfd_disabled)(DeviceState *d); + /* Sets the 'ioeventfd disabled' state for the device. */ + void (*ioeventfd_set_disabled)(DeviceState *d, bool disabled); + /* + * Assigns/deassigns the ioeventfd backing for the transport on + * the device for queue number n. Returns an error value on + * failure. + */ + int (*ioeventfd_assign)(DeviceState *d, EventNotifier *notifier, + int n, bool assign); /* * Does the transport have variable vring alignment? * (ie can it ever call virtio_queue_set_align()?) @@ -111,4 +134,11 @@ static inline VirtIODevice *virtio_bus_get_device(VirtioBusState *bus) return (VirtIODevice *)qdev; } +/* Start the ioeventfd. */ +void virtio_bus_start_ioeventfd(VirtioBusState *bus); +/* Stop the ioeventfd. */ +void virtio_bus_stop_ioeventfd(VirtioBusState *bus); +/* Switch from/to the generic ioeventfd handler */ +int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign); + #endif /* VIRTIO_BUS_H */ -- cgit v1.2.3-55-g7522 From 21a4d96243e60a4c8eeb124a023b8a3bd9120e18 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 10 Jun 2016 11:04:14 +0200 Subject: virtio-bus: remove old set_host_notifier callback All users have been converted to the new ioevent callbacks. Signed-off-by: Cornelia Huck Reviewed-by: Fam Zheng Reviewed-by: Stefan Hajnoczi Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/dataplane/virtio-blk.c | 12 ++---------- hw/scsi/virtio-scsi-dataplane.c | 19 ++++--------------- hw/virtio/vhost.c | 13 +------------ include/hw/virtio/virtio-bus.h | 1 - 4 files changed, 7 insertions(+), 38 deletions(-) (limited to 'include') diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index fdf5fd1190..2041b0400b 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -79,8 +79,7 @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, } /* Don't try if transport does not support notifiers. */ - if (!k->set_guest_notifiers || - (!k->set_host_notifier && !k->ioeventfd_started)) { + if (!k->set_guest_notifiers || !k->ioeventfd_started) { error_setg(errp, "device is incompatible with dataplane " "(transport does not support notifiers)"); @@ -159,9 +158,6 @@ void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) /* Set up virtqueue notify */ r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), 0, true); - if (r == -ENOSYS) { - r = k->set_host_notifier(qbus->parent, 0, true); - } if (r != 0) { fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); goto fail_host_notifier; @@ -197,7 +193,6 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); - int r; if (!vblk->dataplane_started || s->stopping) { return; @@ -222,10 +217,7 @@ void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) aio_context_release(s->ctx); - r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), 0, false); - if (r == -ENOSYS) { - k->set_host_notifier(qbus->parent, 0, false); - } + virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), 0, false); /* Clean up guest notifier (irq) */ k->set_guest_notifiers(qbus->parent, 1, false); diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index b9a5716501..18ced31493 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -31,8 +31,7 @@ void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread) s->ctx = iothread_get_aio_context(vs->conf.iothread); /* Don't try if transport does not support notifiers. */ - if (!k->set_guest_notifiers || - (!k->set_host_notifier && !k->ioeventfd_started)) { + if (!k->set_guest_notifiers || !k->ioeventfd_started) { fprintf(stderr, "virtio-scsi: Failed to set iothread " "(transport does not support notifiers)"); exit(1); @@ -70,14 +69,10 @@ static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, void (*fn)(VirtIODevice *vdev, VirtQueue *vq)) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); int rc; /* Set up virtqueue notify */ rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true); - if (rc == -ENOSYS) { - rc = k->set_host_notifier(qbus->parent, n, true); - } if (rc != 0) { fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", rc); @@ -163,10 +158,7 @@ fail_vrings: virtio_scsi_clear_aio(s); aio_context_release(s->ctx); for (i = 0; i < vs->conf.num_queues + 2; i++) { - rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); - if (rc == -ENOSYS) { - k->set_host_notifier(qbus->parent, i, false); - } + virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); fail_guest_notifiers: @@ -181,7 +173,7 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s) BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - int i, rc; + int i; if (!s->dataplane_started || s->dataplane_stopping) { return; @@ -205,10 +197,7 @@ void virtio_scsi_dataplane_stop(VirtIOSCSI *s) aio_context_release(s->ctx); for (i = 0; i < vs->conf.num_queues + 2; i++) { - rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); - if (rc == -ENOSYS) { - k->set_host_notifier(qbus->parent, i, false); - } + virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); } /* Clean up guest notifier (irq) */ diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index bce1b6e313..a01394d5ac 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1110,7 +1110,7 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) VirtioBusState *vbus = VIRTIO_BUS(qbus); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); int i, r, e; - if (!k->set_host_notifier || !k->ioeventfd_started) { + if (!k->ioeventfd_started) { fprintf(stderr, "binding does not support host notifiers\n"); r = -ENOSYS; goto fail; @@ -1119,9 +1119,6 @@ int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) for (i = 0; i < hdev->nvqs; ++i) { r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i, true); - if (r == -ENOSYS) { - r = k->set_host_notifier(qbus->parent, hdev->vq_index + i, true); - } if (r < 0) { fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r); goto fail_vq; @@ -1133,9 +1130,6 @@ fail_vq: while (--i >= 0) { e = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i, false); - if (e == -ENOSYS) { - e = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false); - } if (e < 0) { fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r); fflush(stderr); @@ -1154,16 +1148,11 @@ fail: void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); - VirtioBusState *vbus = VIRTIO_BUS(qbus); - VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); int i, r; for (i = 0; i < hdev->nvqs; ++i) { r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), hdev->vq_index + i, false); - if (r == -ENOSYS) { - r = k->set_host_notifier(qbus->parent, hdev->vq_index + i, false); - } if (r < 0) { fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r); fflush(stderr); diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index 9637f80f4d..f3e5ef3f5b 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -52,7 +52,6 @@ typedef struct VirtioBusClass { bool (*has_extra_state)(DeviceState *d); bool (*query_guest_notifiers)(DeviceState *d); int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assign); - int (*set_host_notifier)(DeviceState *d, int n, bool assigned); void (*vmstate_change)(DeviceState *d, bool running); /* * transport independent init function. -- cgit v1.2.3-55-g7522