Add cpu hotplug support in ACPI. Signed-off-by: Shannon Zhao <zhaoshengl...@huawei.com> --- hw/acpi/aml-build.c | 135 ++++++++++++++++++++++++++++ hw/acpi/virt-hotplug.c | 11 +++ hw/arm/virt-acpi-build.c | 179 +++++++++++++++++++++++++++++++++++++- hw/arm/virt.c | 1 + include/hw/acpi/aml-build.h | 13 +++ include/hw/acpi/virt-hotplug.h | 2 +- include/hw/arm/virt-acpi-build.h | 1 + 7 files changed, 337 insertions(+), 5 deletions(-)
diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 5e0c40b..023e055 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -468,6 +468,21 @@ Aml *aml_arg(int pos) return var; } +Aml *aml_index(Aml *val, Aml *index) +{ + Aml *var = aml_opcode(0x88 /* IndexOp */); + aml_append(var, val); + aml_append(var, index); + return var; +} + +Aml *aml_derefof(Aml *val) +{ + Aml *var = aml_opcode(0x83 /* DerefofOp */); + aml_append(var, val); + return var; +} + /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefStore */ Aml *aml_store(Aml *val, Aml *target) { @@ -496,6 +511,14 @@ Aml *aml_notify(Aml *arg1, Aml *arg2) return var; } +/* helper to call method with none argument */ +Aml *aml_call0(const char *method) +{ + Aml *var = aml_alloc(); + build_append_namestring(var->buf, "%s", method); + return var; +} + /* helper to call method with 1 argument */ Aml *aml_call1(const char *method, Aml *arg1) { @@ -539,6 +562,48 @@ Aml *aml_call4(const char *method, Aml *arg1, Aml *arg2, Aml *arg3, Aml *arg4) } /* + * ACPI 5.0 19.5.53 GpioInt (GPIO Interrupt Connection Resource Descriptor Macro) + */ +Aml *aml_gpio_int(uint8_t edge, uint8_t polarity, uint8_t share, uint8_t wake, + int8_t pin_cfg, int32_t pin_num, const char *name) +{ + Aml *var = aml_alloc(); + uint8_t flags = edge & (polarity >> 1) & (share >> 3) & (wake >> 4) & 0x1F; + uint16_t length = 0x18 + sizeof(name); + + build_append_byte(var->buf, 0x8C); /* GpioInt Resource Descriptor */ + build_append_byte(var->buf, length & 0xff); /* Length, bits[7:0] */ + build_append_byte(var->buf, (length >> 8) & 0xff); /* Length, bits[15:8] */ + build_append_byte(var->buf, 1); /* Revision ID */ + build_append_byte(var->buf, 0); /* GPIO Connection Type 0x00 = Interrupt Connection */ + build_append_byte(var->buf, 0); /* General Flags, bits[7:0] */ + build_append_byte(var->buf, 0); /* General Flags, bits[15:8] */ + build_append_byte(var->buf, flags); /* Interrupt and IO Flags, bits[7:0] */ + build_append_byte(var->buf, 0); /* Interrupt and IO Flags, bits[15:8] */ + build_append_byte(var->buf, pin_cfg); /* Pin Configuration 0 = Default 1 = Pull-up + * 2 = Pull-down 3 = No Pull */ + build_append_byte(var->buf, 0); /* Output Drive Strength, bits[7:0] */ + build_append_byte(var->buf, 0); /* Output Drive Strength, bits[15:8] */ + build_append_byte(var->buf, 0); /* Debounce timeout, bits[7:0] */ + build_append_byte(var->buf, 0); /* Debounce timeout, bits[15:8] */ + + build_append_byte(var->buf, 0x16); /* Pin Table Offset, bits[7:0] */ + build_append_byte(var->buf, 0); /* Pin Table Offset, bits[15:8] */ + build_append_byte(var->buf, 0); /* Resource Source Index */ + build_append_byte(var->buf, 0x18); /* Resource Source Name, bits[7:0] */ + build_append_byte(var->buf, 0); /* Resource Source Name, bits[15:8] */ + build_append_byte(var->buf, length & 0xff); /* Vendor Data Offset, bits[7:0] */ + build_append_byte(var->buf, (length >> 8) & 0xff); /* Vendor Data Offset, bits[15:8] */ + build_append_byte(var->buf, 0); /* Vendor Data Length, bits[7:0] */ + build_append_byte(var->buf, 0); /* Vendor Data Length, bits[15:8] */ + build_append_byte(var->buf, pin_num & 0xff); /* Pin Number, bits[7:0] */ + build_append_byte(var->buf, (pin_num >> 8) & 0xff); /* Pin Number, bits[15:8] */ + build_append_namestring(var->buf, "%s", name); /* Resource Source */ + + return var; +} + +/* * ACPI 1.0: 6.4.3.4 Memory32Fixed (Memory Resource Descriptor Macro) */ Aml *aml_memory32_fixed(uint64_t addr, uint64_t size, uint8_t rw_flag) @@ -615,6 +680,43 @@ Aml *aml_irq_no_flags(uint8_t irq) return var; } +/* ACPI 1.0: 16.2.3 Operators: DefIncrement */ +Aml *aml_increment(Aml *arg) +{ + Aml *var = aml_opcode(0x75 /* IncrementOp */); + aml_append(var, arg); + build_append_int(var->buf, 0x00); /* NullNameOp */ + return var; +} + +/* ACPI 1.0: 16.2.3 Operators: DefShiftRight */ +Aml *aml_shiftright(Aml *arg1, Aml *arg2, Aml *arg3) +{ + Aml *var = aml_opcode(0x7A /* ShiftRightOp */); + aml_append(var, arg1); + aml_append(var, arg2); + aml_append(var, arg3); + build_append_int(var->buf, 0x00); /* NullNameOp */ + return var; +} + +/* ACPI 1.0: 16.2.3 Operators: DefSizeof */ +Aml *aml_sizeof(Aml *arg) +{ + Aml *var = aml_opcode(0x87 /* SizeofOp */); + aml_append(var, arg); + build_append_int(var->buf, 0x00); /* NullNameOp */ + return var; +} + +/* ACPI 1.0: 16.2.3 Operators: DefLNot */ +Aml *aml_not(Aml *arg) +{ + Aml *var = aml_opcode(0x92 /* LNotOp */); + aml_append(var, arg); + build_append_int(var->buf, 0x00); /* NullNameOp */ + return var; +} /* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLEqual */ Aml *aml_equal(Aml *arg1, Aml *arg2) { @@ -625,6 +727,16 @@ Aml *aml_equal(Aml *arg1, Aml *arg2) return var; } +/* ACPI 1.0: 16.2.3 Operators: DefLLess */ +Aml *aml_less(Aml *arg1, Aml *arg2) +{ + Aml *var = aml_opcode(0x95 /* LLessOp */); + aml_append(var, arg1); + aml_append(var, arg2); + build_append_int(var->buf, 0x00); /* NullNameOp */ + return var; +} + /* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefIfElse */ Aml *aml_if(Aml *predicate) { @@ -633,6 +745,29 @@ Aml *aml_if(Aml *predicate) return var; } +/* ACPI 1.0: 16.2.3 Operators: DefElse */ +Aml *aml_else(void) +{ + Aml *var = aml_bundle(0xA1 /* ElseOp */, AML_PACKAGE); + return var; +} + +/* ACPI 1.0: 16.2.3 Operators: DefWhile */ +Aml *aml_while(Aml *predicate) +{ + Aml *var = aml_bundle(0xA2 /* DefWhile */, AML_PACKAGE); + aml_append(var, predicate); + return var; +} + +/* ACPI 1.0: 16.2.3 Operators: DefSleep */ +Aml *aml_sleep(const uint64_t msectime) +{ + Aml *var = aml_bundle(0x22 /* SleepOp */, AML_EXT_PACKAGE); + build_append_int(var->buf, msectime); + return var; +} + /* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMethod */ Aml *aml_method(const char *name, int arg_count) { diff --git a/hw/acpi/virt-hotplug.c b/hw/acpi/virt-hotplug.c index 47da078..c9a837d 100644 --- a/hw/acpi/virt-hotplug.c +++ b/hw/acpi/virt-hotplug.c @@ -85,6 +85,17 @@ static int virt_hotplug_initfn(SysBusDevice *sbd) return 0; } +Object *virt_hotplug_find(void) +{ + bool ambig; + Object *o = object_resolve_path_type("", TYPE_VIRT_HOTPLUG, &ambig); + + if (ambig || !o) { + return NULL; + } + return o; +} + void virt_hotplug_init(DeviceState **virt_hotplug) { DeviceState *dev; diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 4f26551..a719487 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -38,6 +38,7 @@ #include "target-arm/cpu.h" #include "hw/acpi/acpi-defs.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/virt-hotplug.h" #include "hw/nvram/fw_cfg.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/loader.h" @@ -179,7 +180,7 @@ static void acpi_dsdt_add_virtio(Aml *scope, const hwaddr *mmio_addrs, static void acpi_dsdt_add_gpio(Aml *scope, const hwaddr *gpio_addrs, const int *gpio_irq) { - Aml *dev, *crs; + Aml *dev, *crs, *aei, *method; dev = aml_device("GPO0"); aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0009"))); @@ -193,9 +194,172 @@ static void acpi_dsdt_add_gpio(Aml *scope, const hwaddr *gpio_addrs, aml_interrupt(0x01, *gpio_irq + 32)); aml_append(dev, aml_name_decl("_CRS", crs)); + aei = aml_resource_template(); + aml_append(aei, aml_gpio_int(1, 2, 0, 0, 1, 2, "\\_SB.GPO0")); + aml_append(dev, aml_name_decl("_AEI", aei)); + /* _E02 for cpu hotplug */ + method = aml_method("_E02", 0); + aml_append(method, aml_call0("\\_SB.PRSC")); + aml_append(dev, method); + aml_append(scope, dev); } +static void acpi_dsdt_add_cpu_hotplug(Aml *scope, int max_cpus, + const hwaddr *cpu_hotplug_addr, VirtAcpiCpuInfo *cpuinfo) +{ + Aml *dev, *crs, *field, *method, *ifctx, *elsectx, *ifctx1, *whilectx; + Aml *local0, *local1, *local2, *local3, *local4, *pkg, *buffer; + int i; + + /* create PRES device and its _CRS to reserve CPU hotplug MMIO */ + dev = aml_device("PRES"); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06"))); + aml_append(dev, aml_name_decl("_UID", aml_string("CPU Hotplug Resources"))); + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); + crs = aml_resource_template(); + aml_append(crs, + aml_memory32_fixed(cpu_hotplug_addr[0], cpu_hotplug_addr[1], 0x01)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); + + /* declare CPU hotplug MMIO region and PRS field to access it */ + aml_append(scope, aml_operation_region( + "PRST", aml_system_memory, cpu_hotplug_addr[0], cpu_hotplug_addr[1])); + field = aml_field("PRST", aml_byte_acc); + aml_append(field, aml_named_field("PRS", 256)); + aml_append(scope, field); + + /* build Processor object for each processor */ + for (i = 0; i < max_cpus; i++) { + dev = aml_processor(i, 0, 0, "CP%.02X", i); + + method = aml_method("_MAT", 0); + aml_append(method, aml_return(aml_call1("CPMA", aml_int(i)))); + aml_append(dev, method); + + method = aml_method("_STA", 0); + aml_append(method, aml_return(aml_call1("CPST", aml_int(i)))); + aml_append(dev, method); + + method = aml_method("_EJ0", 1); + aml_append(method, + aml_return(aml_call2("CPEJ", aml_int(i), aml_arg(0)))); + aml_append(dev, method); + + aml_append(scope, dev); + } + + /* build this code: + * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...} + */ + /* Arg0 = Processor ID = APIC ID */ + method = aml_method("NTFY", 2); + for (i = 0; i < max_cpus; i++) { + ifctx = aml_if(aml_equal(aml_arg(0), aml_int(i))); + aml_append(ifctx, + aml_notify(aml_name("CP%.02X", i), aml_arg(1)) + ); + aml_append(method, ifctx); + } + aml_append(scope, method); + + /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" + * + * Note: The ability to create variable-sized packages was first + * ntroduced in ACPI 2.0. ACPI 1.0 only allowed fixed-size packages + * ith up to 255 elements. Windows guests up to win2k8 fail when + * VarPackageOp is used. + */ + pkg = max_cpus <= 255 ? aml_package(max_cpus) : + aml_varpackage(max_cpus); + + for (i = 0; i < max_cpus; i++) { + uint8_t b = test_bit(i, cpuinfo->found_cpus) ? 0x01 : 0x00; + aml_append(pkg, aml_int(b)); + } + aml_append(scope, aml_name_decl("CPON", pkg)); + + /* Add CPMA method */ + method = aml_method("CPMA", 1); + local0 = aml_local(0); + aml_append(method, + aml_store(aml_derefof(aml_index(pkg, aml_arg(0))), local0)); + buffer = aml_buffer(); + aml_append(buffer, aml_int(0x0)); + aml_append(buffer, aml_int(0x08)); + aml_append(buffer, aml_int(0x0)); + aml_append(buffer, aml_int(0x0)); + aml_append(buffer, aml_int(0x0)); + aml_append(buffer, aml_int(0x0)); + aml_append(buffer, aml_int(0x0)); + aml_append(buffer, aml_int(0x0)); + local1 = aml_local(1); + aml_append(method, aml_store(buffer, local1)); + aml_append(method, aml_store(aml_arg(0), aml_index(local1, aml_int(2)))); + aml_append(method, aml_store(aml_arg(0), aml_index(local1, aml_int(3)))); + aml_append(method, aml_store(local0, aml_index(local1, aml_int(4)))); + aml_append(method, aml_return(local1)); + aml_append(scope, method); + + /* Add CPST method */ + method = aml_method("CPST", 1); + local0 = aml_local(0); + aml_append(method, + aml_store(aml_derefof(aml_index(pkg, aml_arg(0))), local0)); + ifctx = aml_if(local0); + aml_append(ifctx, aml_return(aml_int(0xF))); + aml_append(method, ifctx); + elsectx = aml_else(); + aml_append(elsectx, aml_return(aml_int(0x0))); + aml_append(method, elsectx); + aml_append(scope, method); + + /* Add CPEJ method */ + method = aml_method("CPEJ", 2); + aml_append(method, aml_sleep(200)); + aml_append(scope, method); + + /* Add PRSC method */ + method = aml_method("PRSC", 0); + local4 = aml_local(4); + aml_append(method, aml_store(field, local4)); + local2 = aml_local(2); + aml_append(method, aml_store(aml_int(0), local2)); + local0 = aml_local(0); + aml_append(method, aml_store(aml_int(0), local0)); + + whilectx = aml_while(aml_less(local0, aml_sizeof(pkg))); + local1 = aml_local(1); + aml_append(whilectx, + aml_store(aml_derefof(aml_index(pkg, local0)), local1)); + ifctx = aml_if(aml_and(local0, aml_int(0x07))); + aml_append(ifctx, aml_shiftright(local2, aml_int(0x1), local2)); + aml_append(whilectx, ifctx); + elsectx = aml_else(); + aml_append(elsectx, + aml_store(aml_derefof(aml_index(local4, + aml_shiftright(local0, aml_int(0x3), local0))), local0) + ); + aml_append(whilectx, elsectx); + local3 = aml_local(3); + aml_append(whilectx, aml_store(aml_and(local2, aml_int(0x1)), local3)); + ifctx = aml_if(aml_not(aml_equal(local1, local3))); + aml_append(ifctx, aml_store(local3, aml_index(pkg, local0))); + ifctx1 = aml_if(aml_equal(local3, aml_int(0x1))); + aml_append(ifctx1, aml_call2("NTFY", local0, aml_int(0x1))); + aml_append(ifctx, ifctx1); + elsectx = aml_else(); + aml_append(elsectx, aml_call2("NTFY", local0, aml_int(0x3))); + aml_append(ifctx, elsectx); + aml_append(whilectx, ifctx); + + aml_append(whilectx, aml_increment(local0)); + + aml_append(method, whilectx); + aml_append(scope, method); +} + /* RSDP */ static GArray * build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) @@ -348,22 +512,29 @@ build_fadt(GArray *table_data, GArray *linker, uint64_t dsdt) /* DSDT */ static void -build_dsdt(Aml *table_data, GArray *linker, VirtGuestInfo *guest_info) +build_dsdt(Aml *table_data, GArray *linker, VirtGuestInfo *guest_info, + VirtAcpiCpuInfo *cpuinfo) { Aml *scope, *dsdt; const struct acpi_dsdt_info *info = guest_info->dsdt_info; + Object *virt_hotplug = virt_hotplug_find(); dsdt = aml_def_block("DSDT", 1, ACPI_BUILD_APPNAME6, ACPI_BUILD_APPNAME4, 1, ACPI_BUILD_APPNAME4_HEX, 1); scope = aml_scope("\\_SB"); - acpi_dsdt_add_cpus(scope, guest_info->max_cpus); acpi_dsdt_add_uart(scope, info->uart_addr, info->uart_irq); acpi_dsdt_add_rtc(scope, info->rtc_addr, info->rtc_irq); acpi_dsdt_add_flash(scope, info->flash_addr); acpi_dsdt_add_virtio(scope, info->virtio_mmio_addr, info->virtio_mmio_irq, info->virtio_mmio_num); acpi_dsdt_add_gpio(scope, info->gpio_addr, info->gpio_irq); + if (virt_hotplug) { + acpi_dsdt_add_cpu_hotplug(scope, guest_info->max_cpus, + info->cpu_hotplug_addr, cpuinfo); + } else { + acpi_dsdt_add_cpus(scope, guest_info->max_cpus); + } aml_append(dsdt, scope); aml_append(table_data, dsdt); @@ -410,7 +581,7 @@ void virt_acpi_build(VirtGuestInfo *guest_info, AcpiBuildTables *tables) /* DSDT is pointed to by FADT */ dsdt = tables_blob->len; - build_dsdt(tables->table_data, tables->linker, guest_info); + build_dsdt(tables->table_data, tables->linker, guest_info, &cpuinfo); /* FADT MADT GTDT pointed to by XSDT */ acpi_add_table(table_offsets, tables_blob); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 760afbb..4add9f5 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -168,6 +168,7 @@ static const struct acpi_dsdt_info dsdt_info = { .virtio_mmio_num = NUM_VIRTIO_TRANSPORTS, .gpio_addr = &a15memmap[VIRT_GPIO].base, .gpio_irq = &a15irqmap[VIRT_GPIO], + .cpu_hotplug_addr = &a15memmap[VIRT_CPU_HOTPLUG].base, .rtc_addr = &a15memmap[VIRT_RTC].base, .rtc_irq = &a15irqmap[VIRT_RTC], .flash_addr = &a15memmap[VIRT_FLASH].base, diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index eb9ba0d..5e43557 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -155,17 +155,27 @@ Aml *aml_name_decl(const char *name, Aml *val); Aml *aml_return(Aml *val); Aml *aml_int(const uint64_t val); Aml *aml_arg(int pos); +Aml *aml_index(Aml *val, Aml *index); +Aml *aml_derefof(Aml *val); Aml *aml_store(Aml *val, Aml *target); Aml *aml_and(Aml *arg1, Aml *arg2); Aml *aml_notify(Aml *arg1, Aml *arg2); +Aml *aml_call0(const char *method); 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_gpio_int(uint8_t edge, uint8_t polarity, uint8_t share, uint8_t wake, + int8_t pin_cfg, int32_t pin_num, const char *name); Aml *aml_memory32_fixed(uint64_t addr, uint64_t size, uint8_t rw_flag); Aml *aml_interrupt(uint8_t irq_flags, int irq); Aml *aml_io(AmlIODecode dec, uint16_t min_base, uint16_t max_base, uint8_t aln, uint8_t len); +Aml *aml_increment(Aml *arg); +Aml *aml_shiftright(Aml *arg1, Aml *arg2, Aml *arg3); +Aml *aml_sizeof(Aml *arg); +Aml *aml_not(Aml *arg); +Aml *aml_less(Aml *arg1, Aml *arg2); Aml *aml_operation_region(const char *name, AmlRegionSpace rs, uint32_t offset, uint32_t len); Aml *aml_irq_no_flags(uint8_t irq); @@ -208,6 +218,9 @@ Aml *aml_scope(const char *name_format, ...) GCC_FMT_ATTR(1, 2); Aml *aml_device(const char *name_format, ...) GCC_FMT_ATTR(1, 2); Aml *aml_method(const char *name, int arg_count); Aml *aml_if(Aml *predicate); +Aml *aml_else(void); +Aml *aml_while(Aml *predicate); +Aml *aml_sleep(const uint64_t msectime); Aml *aml_package(uint8_t num_elements); Aml *aml_buffer(void); Aml *aml_resource_template(void); diff --git a/include/hw/acpi/virt-hotplug.h b/include/hw/acpi/virt-hotplug.h index 8f94235..aad3381 100644 --- a/include/hw/acpi/virt-hotplug.h +++ b/include/hw/acpi/virt-hotplug.h @@ -7,5 +7,5 @@ #define VIRT_CPU_HOTPLUG_MMIO_BASE 0x09600000 void virt_hotplug_init(DeviceState **virt_hotplug); - +Object *virt_hotplug_find(void); #endif diff --git a/include/hw/arm/virt-acpi-build.h b/include/hw/arm/virt-acpi-build.h index de8d4c6..b183b42 100644 --- a/include/hw/arm/virt-acpi-build.h +++ b/include/hw/arm/virt-acpi-build.h @@ -49,6 +49,7 @@ struct acpi_dsdt_info { int virtio_mmio_num; const hwaddr *gpio_addr; const int *gpio_irq; + const hwaddr *cpu_hotplug_addr; const hwaddr *rtc_addr; const int *rtc_irq; const hwaddr *flash_addr; -- 1.7.1