Message ID | 1463496205-251412-17-git-send-email-imammedo@redhat.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On 05/17/2016 05:43 PM, Igor Mammedov wrote: > Signed-off-by: Igor Mammedov <imammedo@redhat.com> Maybe it worth adding some kind of explanation of the functionality added here. Thanks, Marcel > --- > v1: > - drop CPUHotplugState.is_enabled field > --- > hw/acpi/Makefile.objs | 1 + > hw/acpi/cpu.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++ > include/hw/acpi/cpu.h | 52 +++++++++++++ > trace-events | 9 +++ > 4 files changed, 268 insertions(+) > create mode 100644 hw/acpi/cpu.c > create mode 100644 include/hw/acpi/cpu.h > > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs > index 66bd727..f200419 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 0000000..171a5f5 > --- /dev/null > +++ b/hw/acpi/cpu.c > @@ -0,0 +1,206 @@ > +#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 = 0; > + val |= cdev->is_enabled ? 1 : 0; > + val |= cdev->is_inserting ? 2 : 0; > + val |= cdev->is_removing ? 4 : 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; > + AcpiCpuStatus *cdev; > + Error *local_err = NULL; > + > + 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; > + 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); > + } 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->is_enabled) { > + 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, &local_err); > + if (local_err) { > + break; > + } > + } > + break; > + default: > + break; > + } > + error_free(local_err); > +} > + > +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; > + > + 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; > + state->devs[i].is_enabled = id_list->cpus[i].cpu ? true : false; > + } > + 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); > + cdev->is_enabled = true; > + if (dev->hotplugged) { > + cdev->is_inserting = true; > + ACPI_SEND_EVENT(hotplug_dev, ACPI_CPU_HOTPLUG_STATUS); > + } > +} > + > +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(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; > + cdev->is_enabled = false; > +} > + > +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_enabled, AcpiCpuStatus), > + VMSTATE_BOOL(is_inserting, AcpiCpuStatus), > + VMSTATE_BOOL(is_removing, AcpiCpuStatus), > + VMSTATE_END_OF_LIST() > + } > +}; > + > +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_STRUCT_VARRAY_POINTER_UINT32(devs, CPUHotplugState, dev_count, > + vmstate_cpuhp_sts, AcpiCpuStatus), > + VMSTATE_END_OF_LIST() > + } > +}; > diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h > new file mode 100644 > index 0000000..3fdf4eb > --- /dev/null > +++ b/include/hw/acpi/cpu.h > @@ -0,0 +1,52 @@ > +/* > + * QEMU ACPI hotplug utilities > + * > + * Copyright (C) 2016 Red Hat Inc > + * > + * Authors: > + * Igor Mammedov <imammedo@redhat.com> > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > + * See the COPYING file in the top-level directory. > + */ > +#ifndef ACPI_CPU_H > +#define ACPI_CPU_H > + > +#include "hw/qdev-core.h" > +#include "hw/acpi/acpi.h" > +#include "hw/hotplug.h" > + > +typedef struct AcpiCpuStatus { > + struct CPUState *cpu; > + uint64_t arch_id; > + bool is_enabled; > + bool is_inserting; > + bool is_removing; > +} 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 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); > + > +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/trace-events b/trace-events > index 4fce005..7101ba9 100644 > --- a/trace-events > +++ b/trace-events > @@ -1908,3 +1908,12 @@ aspeed_vic_update_fiq(int flags) "Raising FIQ: %d" > aspeed_vic_update_irq(int flags) "Raising IRQ: %d" > aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 > aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 > + > +# 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_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]" > +cpuhp_acpi_clear_remove_evt(uint32_t idx) "idx[0x%"PRIx32"]" > +cpuhp_acpi_write_idx(uint32_t idx) "set active cpu idx: 0x%"PRIx32 > +cpuhp_acpi_ejecting_invalid_cpu(uint32_t idx) "0x%"PRIx32 > +cpuhp_acpi_ejecting_cpu(uint32_t idx) "0x%"PRIx32 >
On Mon, 30 May 2016 21:50:08 +0300 Marcel Apfelbaum <marcel@redhat.com> wrote: > On 05/17/2016 05:43 PM, Igor Mammedov wrote: > > Signed-off-by: Igor Mammedov <imammedo@redhat.com> > > Maybe it worth adding some kind of explanation > of the functionality added here. I'll add a bit more detailed explanation here > Thanks, > Marcel > > > --- > > v1: > > - drop CPUHotplugState.is_enabled field > > --- > > hw/acpi/Makefile.objs | 1 + > > hw/acpi/cpu.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++ > > include/hw/acpi/cpu.h | 52 +++++++++++++ > > trace-events | 9 +++ > > 4 files changed, 268 insertions(+) > > create mode 100644 hw/acpi/cpu.c > > create mode 100644 include/hw/acpi/cpu.h > > > > diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs > > index 66bd727..f200419 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 0000000..171a5f5 > > --- /dev/null > > +++ b/hw/acpi/cpu.c > > @@ -0,0 +1,206 @@ > > +#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 = 0; > > + val |= cdev->is_enabled ? 1 : 0; > > + val |= cdev->is_inserting ? 2 : 0; > > + val |= cdev->is_removing ? 4 : 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; > > + AcpiCpuStatus *cdev; > > + Error *local_err = NULL; > > + > > + 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; > > + 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); > > + } 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->is_enabled) { > > + 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, &local_err); > > + if (local_err) { > > + break; > > + } > > + } > > + break; > > + default: > > + break; > > + } > > + error_free(local_err); > > +} > > + > > +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; > > + > > + 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; > > + state->devs[i].is_enabled = id_list->cpus[i].cpu ? true : false; > > + } > > + 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); > > + cdev->is_enabled = true; > > + if (dev->hotplugged) { > > + cdev->is_inserting = true; > > + ACPI_SEND_EVENT(hotplug_dev, ACPI_CPU_HOTPLUG_STATUS); > > + } > > +} > > + > > +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(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; > > + cdev->is_enabled = false; > > +} > > + > > +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_enabled, AcpiCpuStatus), > > + VMSTATE_BOOL(is_inserting, AcpiCpuStatus), > > + VMSTATE_BOOL(is_removing, AcpiCpuStatus), > > + VMSTATE_END_OF_LIST() > > + } > > +}; > > + > > +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_STRUCT_VARRAY_POINTER_UINT32(devs, CPUHotplugState, dev_count, > > + vmstate_cpuhp_sts, AcpiCpuStatus), > > + VMSTATE_END_OF_LIST() > > + } > > +}; > > diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h > > new file mode 100644 > > index 0000000..3fdf4eb > > --- /dev/null > > +++ b/include/hw/acpi/cpu.h > > @@ -0,0 +1,52 @@ > > +/* > > + * QEMU ACPI hotplug utilities > > + * > > + * Copyright (C) 2016 Red Hat Inc > > + * > > + * Authors: > > + * Igor Mammedov <imammedo@redhat.com> > > + * > > + * This work is licensed under the terms of the GNU GPL, version 2 or later. > > + * See the COPYING file in the top-level directory. > > + */ > > +#ifndef ACPI_CPU_H > > +#define ACPI_CPU_H > > + > > +#include "hw/qdev-core.h" > > +#include "hw/acpi/acpi.h" > > +#include "hw/hotplug.h" > > + > > +typedef struct AcpiCpuStatus { > > + struct CPUState *cpu; > > + uint64_t arch_id; > > + bool is_enabled; > > + bool is_inserting; > > + bool is_removing; > > +} 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 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); > > + > > +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/trace-events b/trace-events > > index 4fce005..7101ba9 100644 > > --- a/trace-events > > +++ b/trace-events > > @@ -1908,3 +1908,12 @@ aspeed_vic_update_fiq(int flags) "Raising FIQ: %d" > > aspeed_vic_update_irq(int flags) "Raising IRQ: %d" > > aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 > > aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 > > + > > +# 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_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]" > > +cpuhp_acpi_clear_remove_evt(uint32_t idx) "idx[0x%"PRIx32"]" > > +cpuhp_acpi_write_idx(uint32_t idx) "set active cpu 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/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 66bd727..f200419 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 0000000..171a5f5 --- /dev/null +++ b/hw/acpi/cpu.c @@ -0,0 +1,206 @@ +#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 = 0; + val |= cdev->is_enabled ? 1 : 0; + val |= cdev->is_inserting ? 2 : 0; + val |= cdev->is_removing ? 4 : 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; + AcpiCpuStatus *cdev; + Error *local_err = NULL; + + 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; + 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); + } 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->is_enabled) { + 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, &local_err); + if (local_err) { + break; + } + } + break; + default: + break; + } + error_free(local_err); +} + +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; + + 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; + state->devs[i].is_enabled = id_list->cpus[i].cpu ? true : false; + } + 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); + cdev->is_enabled = true; + if (dev->hotplugged) { + cdev->is_inserting = true; + ACPI_SEND_EVENT(hotplug_dev, ACPI_CPU_HOTPLUG_STATUS); + } +} + +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(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; + cdev->is_enabled = false; +} + +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_enabled, AcpiCpuStatus), + VMSTATE_BOOL(is_inserting, AcpiCpuStatus), + VMSTATE_BOOL(is_removing, AcpiCpuStatus), + VMSTATE_END_OF_LIST() + } +}; + +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_STRUCT_VARRAY_POINTER_UINT32(devs, CPUHotplugState, dev_count, + vmstate_cpuhp_sts, AcpiCpuStatus), + VMSTATE_END_OF_LIST() + } +}; diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h new file mode 100644 index 0000000..3fdf4eb --- /dev/null +++ b/include/hw/acpi/cpu.h @@ -0,0 +1,52 @@ +/* + * QEMU ACPI hotplug utilities + * + * Copyright (C) 2016 Red Hat Inc + * + * Authors: + * Igor Mammedov <imammedo@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef ACPI_CPU_H +#define ACPI_CPU_H + +#include "hw/qdev-core.h" +#include "hw/acpi/acpi.h" +#include "hw/hotplug.h" + +typedef struct AcpiCpuStatus { + struct CPUState *cpu; + uint64_t arch_id; + bool is_enabled; + bool is_inserting; + bool is_removing; +} 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 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); + +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/trace-events b/trace-events index 4fce005..7101ba9 100644 --- a/trace-events +++ b/trace-events @@ -1908,3 +1908,12 @@ aspeed_vic_update_fiq(int flags) "Raising FIQ: %d" aspeed_vic_update_irq(int flags) "Raising IRQ: %d" aspeed_vic_read(uint64_t offset, unsigned size, uint32_t value) "From 0x%" PRIx64 " of size %u: 0x%" PRIx32 aspeed_vic_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 + +# 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_clear_inserting_evt(uint32_t idx) "idx[0x%"PRIx32"]" +cpuhp_acpi_clear_remove_evt(uint32_t idx) "idx[0x%"PRIx32"]" +cpuhp_acpi_write_idx(uint32_t idx) "set active cpu idx: 0x%"PRIx32 +cpuhp_acpi_ejecting_invalid_cpu(uint32_t idx) "0x%"PRIx32 +cpuhp_acpi_ejecting_cpu(uint32_t idx) "0x%"PRIx32
Signed-off-by: Igor Mammedov <imammedo@redhat.com> --- v1: - drop CPUHotplugState.is_enabled field --- hw/acpi/Makefile.objs | 1 + hw/acpi/cpu.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/hw/acpi/cpu.h | 52 +++++++++++++ trace-events | 9 +++ 4 files changed, 268 insertions(+) create mode 100644 hw/acpi/cpu.c create mode 100644 include/hw/acpi/cpu.h