| 1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
| 2 | /* |
| 3 | * Support for generating ACPI tables and passing them to Guests |
| 4 | * |
| 5 | * Copyright (C) 2021 Loongson Technology Corporation Limited |
| 6 | */ |
| 7 | |
| 8 | #include "qemu/osdep.h" |
| 9 | #include "qapi/error.h" |
| 10 | #include "qemu/error-report.h" |
| 11 | #include "qemu/bitmap.h" |
| 12 | #include "hw/pci/pci.h" |
| 13 | #include "hw/core/cpu.h" |
| 14 | #include "target/loongarch/cpu.h" |
| 15 | #include "hw/acpi/acpi-defs.h" |
| 16 | #include "hw/acpi/acpi.h" |
| 17 | #include "hw/nvram/fw_cfg.h" |
| 18 | #include "hw/acpi/bios-linker-loader.h" |
| 19 | #include "migration/vmstate.h" |
| 20 | #include "hw/mem/memory-device.h" |
| 21 | #include "system/reset.h" |
| 22 | |
| 23 | /* Supported chipsets: */ |
| 24 | #include "hw/loongarch/virt.h" |
| 25 | |
| 26 | #include "hw/acpi/utils.h" |
| 27 | #include "hw/acpi/pci.h" |
| 28 | |
| 29 | #include "qom/qom-qobject.h" |
| 30 | |
| 31 | #include "hw/acpi/generic_event_device.h" |
| 32 | #include "hw/pci-host/gpex.h" |
| 33 | #include "system/system.h" |
| 34 | #include "system/tpm.h" |
| 35 | #include "hw/core/platform-bus.h" |
| 36 | #include "hw/acpi/aml-build.h" |
| 37 | #include "hw/acpi/hmat.h" |
| 38 | |
| 39 | #define ACPI_BUILD_ALIGN_SIZE 0x1000 |
| 40 | #define ACPI_BUILD_TABLE_SIZE 0x20000 |
| 41 | |
| 42 | #ifdef DEBUG_ACPI_BUILD |
| 43 | #define ACPI_BUILD_DPRINTF(fmt, ...) \ |
| 44 | do {printf("ACPI_BUILD: " fmt, ## __VA_ARGS__); } while (0) |
| 45 | #else |
| 46 | #define ACPI_BUILD_DPRINTF(fmt, ...) |
| 47 | #endif |
| 48 | |
| 49 | static void virt_madt_cpu_entry(int uid, |
| 50 | const CPUArchIdList *apic_ids, |
| 51 | GArray *entry, bool force_enabled) |
| 52 | { |
| 53 | uint32_t flags, apic_id = apic_ids->cpus[uid].arch_id; |
| 54 | |
| 55 | flags = apic_ids->cpus[uid].cpu || force_enabled ? 1 /* Enabled */ : 0; |
| 56 | |
| 57 | /* Rev 1.0b, Table 5-13 Processor Local APIC Structure */ |
| 58 | build_append_int_noprefix(entry, 0, 1); /* Type */ |
| 59 | build_append_int_noprefix(entry, 8, 1); /* Length */ |
| 60 | build_append_int_noprefix(entry, uid, 1); /* ACPI Processor ID */ |
| 61 | build_append_int_noprefix(entry, apic_id, 1); /* APIC ID */ |
| 62 | build_append_int_noprefix(entry, flags, 4); /* Flags */ |
| 63 | } |
| 64 | |
| 65 | /* build FADT */ |
| 66 | static void init_common_fadt_data(AcpiFadtData *data) |
| 67 | { |
| 68 | AcpiFadtData fadt = { |
| 69 | /* ACPI 5.0: 4.1 Hardware-Reduced ACPI */ |
| 70 | .rev = 5, |
| 71 | .flags = ((1 << ACPI_FADT_F_HW_REDUCED_ACPI) | |
| 72 | (1 << ACPI_FADT_F_RESET_REG_SUP)), |
| 73 | |
| 74 | /* ACPI 5.0: 4.8.3.7 Sleep Control and Status Registers */ |
| 75 | .sleep_ctl = { |
| 76 | .space_id = AML_AS_SYSTEM_MEMORY, |
| 77 | .bit_width = 8, |
| 78 | .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_CTL, |
| 79 | }, |
| 80 | .sleep_sts = { |
| 81 | .space_id = AML_AS_SYSTEM_MEMORY, |
| 82 | .bit_width = 8, |
| 83 | .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_SLEEP_STS, |
| 84 | }, |
| 85 | |
| 86 | /* ACPI 5.0: 4.8.3.6 Reset Register */ |
| 87 | .reset_reg = { |
| 88 | .space_id = AML_AS_SYSTEM_MEMORY, |
| 89 | .bit_width = 8, |
| 90 | .address = VIRT_GED_REG_ADDR + ACPI_GED_REG_RESET, |
| 91 | }, |
| 92 | .reset_val = ACPI_GED_RESET_VALUE, |
| 93 | }; |
| 94 | *data = fadt; |
| 95 | } |
| 96 | |
| 97 | static void acpi_align_size(GArray *blob, unsigned align) |
| 98 | { |
| 99 | /* |
| 100 | * Align size to multiple of given size. This reduces the chance |
| 101 | * we need to change size in the future (breaking cross version migration). |
| 102 | */ |
| 103 | g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); |
| 104 | } |
| 105 | |
| 106 | /* build FACS */ |
| 107 | static void |
| 108 | build_facs(GArray *table_data) |
| 109 | { |
| 110 | const char *sig = "FACS"; |
| 111 | const uint8_t reserved[40] = {}; |
| 112 | |
| 113 | g_array_append_vals(table_data, sig, 4); /* Signature */ |
| 114 | build_append_int_noprefix(table_data, 64, 4); /* Length */ |
| 115 | build_append_int_noprefix(table_data, 0, 4); /* Hardware Signature */ |
| 116 | build_append_int_noprefix(table_data, 0, 4); /* Firmware Waking Vector */ |
| 117 | build_append_int_noprefix(table_data, 0, 4); /* Global Lock */ |
| 118 | build_append_int_noprefix(table_data, 0, 4); /* Flags */ |
| 119 | g_array_append_vals(table_data, reserved, 40); /* Reserved */ |
| 120 | } |
| 121 | |
| 122 | /* build MADT */ |
| 123 | static void |
| 124 | build_madt(GArray *table_data, BIOSLinker *linker, |
| 125 | LoongArchVirtMachineState *lvms) |
| 126 | { |
| 127 | MachineState *ms = MACHINE(lvms); |
| 128 | MachineClass *mc = MACHINE_GET_CLASS(ms); |
| 129 | const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(ms); |
| 130 | int i, arch_id, flags; |
| 131 | AcpiTable table = { .sig = "APIC", .rev = 6, .oem_id = lvms->oem_id, |
| 132 | .oem_table_id = lvms->oem_table_id }; |
| 133 | |
| 134 | acpi_table_begin(&table, table_data); |
| 135 | |
| 136 | /* Local APIC Address */ |
| 137 | build_append_int_noprefix(table_data, 0, 4); |
| 138 | build_append_int_noprefix(table_data, 1 /* PCAT_COMPAT */, 4); /* Flags */ |
| 139 | |
| 140 | for (i = 0; i < arch_ids->len; i++) { |
| 141 | /* Processor Core Interrupt Controller Structure */ |
| 142 | arch_id = arch_ids->cpus[i].arch_id; |
| 143 | flags = arch_ids->cpus[i].cpu ? 1 : 0; |
| 144 | build_append_int_noprefix(table_data, 17, 1); /* Type */ |
| 145 | build_append_int_noprefix(table_data, 15, 1); /* Length */ |
| 146 | build_append_int_noprefix(table_data, 1, 1); /* Version */ |
| 147 | build_append_int_noprefix(table_data, i, 4); /* ACPI Processor ID */ |
| 148 | build_append_int_noprefix(table_data, arch_id, 4); /* Core ID */ |
| 149 | build_append_int_noprefix(table_data, flags, 4); /* Flags */ |
| 150 | } |
| 151 | |
| 152 | /* Extend I/O Interrupt Controller Structure */ |
| 153 | build_append_int_noprefix(table_data, 20, 1); /* Type */ |
| 154 | build_append_int_noprefix(table_data, 13, 1); /* Length */ |
| 155 | build_append_int_noprefix(table_data, 1, 1); /* Version */ |
| 156 | build_append_int_noprefix(table_data, 3, 1); /* Cascade */ |
| 157 | build_append_int_noprefix(table_data, 0, 1); /* Node */ |
| 158 | build_append_int_noprefix(table_data, 0xffff, 8); /* Node map */ |
| 159 | |
| 160 | /* MSI Interrupt Controller Structure */ |
| 161 | build_append_int_noprefix(table_data, 21, 1); /* Type */ |
| 162 | build_append_int_noprefix(table_data, 19, 1); /* Length */ |
| 163 | build_append_int_noprefix(table_data, 1, 1); /* Version */ |
| 164 | build_append_int_noprefix(table_data, VIRT_PCH_MSI_ADDR_LOW, 8);/* Address */ |
| 165 | build_append_int_noprefix(table_data, 0x40, 4); /* Start */ |
| 166 | build_append_int_noprefix(table_data, 0xc0, 4); /* Count */ |
| 167 | |
| 168 | /* Bridge I/O Interrupt Controller Structure */ |
| 169 | build_append_int_noprefix(table_data, 22, 1); /* Type */ |
| 170 | build_append_int_noprefix(table_data, 17, 1); /* Length */ |
| 171 | build_append_int_noprefix(table_data, 1, 1); /* Version */ |
| 172 | build_append_int_noprefix(table_data, VIRT_PCH_REG_BASE, 8);/* Address */ |
| 173 | build_append_int_noprefix(table_data, 0x1000, 2); /* Size */ |
| 174 | build_append_int_noprefix(table_data, 0, 2); /* Id */ |
| 175 | build_append_int_noprefix(table_data, 0x40, 2); /* Base */ |
| 176 | |
| 177 | acpi_table_end(linker, &table); |
| 178 | } |
| 179 | |
| 180 | /* build SRAT */ |
| 181 | static void |
| 182 | build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) |
| 183 | { |
| 184 | int i, arch_id, node_id; |
| 185 | hwaddr len, base, gap; |
| 186 | NodeInfo *numa_info; |
| 187 | int nodes, nb_numa_nodes = machine->numa_state->num_nodes; |
| 188 | LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); |
| 189 | MachineClass *mc = MACHINE_GET_CLASS(lvms); |
| 190 | const CPUArchIdList *arch_ids = mc->possible_cpu_arch_ids(machine); |
| 191 | AcpiTable table = { .sig = "SRAT", .rev = 1, .oem_id = lvms->oem_id, |
| 192 | .oem_table_id = lvms->oem_table_id }; |
| 193 | |
| 194 | acpi_table_begin(&table, table_data); |
| 195 | build_append_int_noprefix(table_data, 1, 4); /* Reserved */ |
| 196 | build_append_int_noprefix(table_data, 0, 8); /* Reserved */ |
| 197 | |
| 198 | for (i = 0; i < arch_ids->len; ++i) { |
| 199 | arch_id = arch_ids->cpus[i].arch_id; |
| 200 | node_id = arch_ids->cpus[i].props.node_id; |
| 201 | |
| 202 | /* Processor Local APIC/SAPIC Affinity Structure */ |
| 203 | build_append_int_noprefix(table_data, 0, 1); /* Type */ |
| 204 | build_append_int_noprefix(table_data, 16, 1); /* Length */ |
| 205 | /* Proximity Domain [7:0] */ |
| 206 | build_append_int_noprefix(table_data, node_id, 1); |
| 207 | build_append_int_noprefix(table_data, arch_id, 1); /* APIC ID */ |
| 208 | /* Flags, Table 5-36 */ |
| 209 | build_append_int_noprefix(table_data, 1, 4); |
| 210 | build_append_int_noprefix(table_data, 0, 1); /* Local SAPIC EID */ |
| 211 | /* Proximity Domain [31:8] */ |
| 212 | build_append_int_noprefix(table_data, 0, 3); |
| 213 | build_append_int_noprefix(table_data, 0, 4); /* Reserved */ |
| 214 | } |
| 215 | |
| 216 | base = VIRT_LOWMEM_BASE; |
| 217 | gap = VIRT_LOWMEM_SIZE; |
| 218 | numa_info = machine->numa_state->nodes; |
| 219 | nodes = nb_numa_nodes; |
| 220 | if (!nodes) { |
| 221 | nodes = 1; |
| 222 | } |
| 223 | |
| 224 | for (i = 0; i < nodes; i++) { |
| 225 | if (nb_numa_nodes) { |
| 226 | len = numa_info[i].node_mem; |
| 227 | } else { |
| 228 | len = machine->ram_size; |
| 229 | } |
| 230 | |
| 231 | /* |
| 232 | * memory for the node splited into two part |
| 233 | * lowram: [base, +gap) |
| 234 | * highram: [VIRT_HIGHMEM_BASE, +(len - gap)) |
| 235 | */ |
| 236 | if (len >= gap) { |
| 237 | build_srat_memory(table_data, base, gap, i, MEM_AFFINITY_ENABLED); |
| 238 | len -= gap; |
| 239 | base = VIRT_HIGHMEM_BASE; |
| 240 | gap = machine->ram_size - VIRT_LOWMEM_SIZE; |
| 241 | } |
| 242 | |
| 243 | if (len) { |
| 244 | build_srat_memory(table_data, base, len, i, MEM_AFFINITY_ENABLED); |
| 245 | base += len; |
| 246 | gap -= len; |
| 247 | } |
| 248 | } |
| 249 | |
| 250 | if (machine->device_memory) { |
| 251 | build_srat_memory(table_data, machine->device_memory->base, |
| 252 | memory_region_size(&machine->device_memory->mr), |
| 253 | nodes - 1, |
| 254 | MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); |
| 255 | } |
| 256 | |
| 257 | acpi_table_end(linker, &table); |
| 258 | } |
| 259 | |
| 260 | /* |
| 261 | * Serial Port Console Redirection Table (SPCR) |
| 262 | * https://learn.microsoft.com/en-us/windows-hardware/drivers/serports/serial-port-console-redirection-table |
| 263 | */ |
| 264 | static void |
| 265 | spcr_setup(GArray *table_data, BIOSLinker *linker, MachineState *machine) |
| 266 | { |
| 267 | LoongArchVirtMachineState *lvms; |
| 268 | AcpiSpcrData serial = { |
| 269 | .interface_type = 0, /* 16550 compatible */ |
| 270 | .base_addr.id = AML_AS_SYSTEM_MEMORY, |
| 271 | .base_addr.width = 32, |
| 272 | .base_addr.offset = 0, |
| 273 | .base_addr.size = 1, |
| 274 | .base_addr.addr = VIRT_UART_BASE, |
| 275 | .interrupt_type = 0, /* Interrupt not supported */ |
| 276 | .pc_interrupt = 0, |
| 277 | .interrupt = VIRT_UART_IRQ, |
| 278 | .baud_rate = 7, /* 115200 */ |
| 279 | .parity = 0, |
| 280 | .stop_bits = 1, |
| 281 | .flow_control = 0, |
| 282 | .terminal_type = 3, /* ANSI */ |
| 283 | .language = 0, /* Language */ |
| 284 | .pci_device_id = 0xffff, /* not a PCI device*/ |
| 285 | .pci_vendor_id = 0xffff, /* not a PCI device*/ |
| 286 | .pci_bus = 0, |
| 287 | .pci_device = 0, |
| 288 | .pci_function = 0, |
| 289 | .pci_flags = 0, |
| 290 | .pci_segment = 0, |
| 291 | }; |
| 292 | |
| 293 | lvms = LOONGARCH_VIRT_MACHINE(machine); |
| 294 | /* |
| 295 | * Passing NULL as the SPCR Table for Revision 2 doesn't support |
| 296 | * NameSpaceString. |
| 297 | */ |
| 298 | build_spcr(table_data, linker, &serial, 2, lvms->oem_id, |
| 299 | lvms->oem_table_id, NULL); |
| 300 | } |
| 301 | |
| 302 | typedef |
| 303 | struct AcpiBuildState { |
| 304 | /* Copy of table in RAM (for patching). */ |
| 305 | MemoryRegion *table_mr; |
| 306 | /* Is table patched? */ |
| 307 | uint8_t patched; |
| 308 | void *rsdp; |
| 309 | MemoryRegion *rsdp_mr; |
| 310 | MemoryRegion *linker_mr; |
| 311 | } AcpiBuildState; |
| 312 | |
| 313 | static void build_uart_device_aml(Aml *table, int index) |
| 314 | { |
| 315 | Aml *dev; |
| 316 | Aml *crs; |
| 317 | Aml *pkg0, *pkg1, *pkg2; |
| 318 | Aml *scope; |
| 319 | uint32_t uart_irq; |
| 320 | uint64_t base; |
| 321 | |
| 322 | uart_irq = VIRT_UART_IRQ + index; |
| 323 | base = VIRT_UART_BASE + index * VIRT_UART_SIZE; |
| 324 | scope = aml_scope("_SB"); |
| 325 | dev = aml_device("COM%d", index); |
| 326 | aml_append(dev, aml_name_decl("_HID", aml_string("PNP0501"))); |
| 327 | aml_append(dev, aml_name_decl("_UID", aml_int(index))); |
| 328 | aml_append(dev, aml_name_decl("_CCA", aml_int(1))); |
| 329 | crs = aml_resource_template(); |
| 330 | aml_append(crs, |
| 331 | aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, |
| 332 | AML_NON_CACHEABLE, AML_READ_WRITE, |
| 333 | 0, base, base + VIRT_UART_SIZE - 1, |
| 334 | 0, VIRT_UART_SIZE)); |
| 335 | aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, |
| 336 | AML_SHARED, &uart_irq, 1)); |
| 337 | aml_append(dev, aml_name_decl("_CRS", crs)); |
| 338 | pkg0 = aml_package(0x2); |
| 339 | aml_append(pkg0, aml_int(0x05F5E100)); |
| 340 | aml_append(pkg0, aml_string("clock-frenquency")); |
| 341 | pkg1 = aml_package(0x1); |
| 342 | aml_append(pkg1, pkg0); |
| 343 | pkg2 = aml_package(0x2); |
| 344 | aml_append(pkg2, aml_touuid("DAFFD814-6EBA-4D8C-8A91-BC9BBF4AA301")); |
| 345 | aml_append(pkg2, pkg1); |
| 346 | aml_append(dev, aml_name_decl("_DSD", pkg2)); |
| 347 | aml_append(scope, dev); |
| 348 | aml_append(table, scope); |
| 349 | } |
| 350 | |
| 351 | static void |
| 352 | build_la_ged_aml(Aml *dsdt, MachineState *machine) |
| 353 | { |
| 354 | uint32_t event; |
| 355 | LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); |
| 356 | CPUHotplugFeatures opts; |
| 357 | |
| 358 | build_ged_aml(dsdt, "\\_SB."GED_DEVICE, |
| 359 | HOTPLUG_HANDLER(lvms->acpi_ged), |
| 360 | VIRT_SCI_IRQ, AML_SYSTEM_MEMORY, |
| 361 | VIRT_GED_EVT_ADDR); |
| 362 | event = object_property_get_uint(OBJECT(lvms->acpi_ged), |
| 363 | "ged-event", &error_abort); |
| 364 | if (event & ACPI_GED_MEM_HOTPLUG_EVT) { |
| 365 | build_memory_hotplug_aml(dsdt, machine->ram_slots, "\\_SB", NULL, |
| 366 | AML_SYSTEM_MEMORY, |
| 367 | VIRT_GED_MEM_ADDR); |
| 368 | } |
| 369 | |
| 370 | if (event & ACPI_GED_CPU_HOTPLUG_EVT) { |
| 371 | opts.acpi_1_compatible = false; |
| 372 | opts.fw_unplugs_cpu = false; |
| 373 | opts.smi_path = NULL; |
| 374 | |
| 375 | build_cpus_aml(dsdt, machine, opts, virt_madt_cpu_entry, |
| 376 | VIRT_GED_CPUHP_ADDR, "\\_SB", |
| 377 | AML_GED_EVT_CPU_SCAN_METHOD, AML_SYSTEM_MEMORY); |
| 378 | } |
| 379 | |
| 380 | acpi_dsdt_add_power_button(dsdt); |
| 381 | } |
| 382 | |
| 383 | static void build_pci_device_aml(Aml *scope, LoongArchVirtMachineState *lvms) |
| 384 | { |
| 385 | acpi_dsdt_add_gpex(scope, &lvms->gpex); |
| 386 | } |
| 387 | |
| 388 | static void build_flash_aml(Aml *scope, LoongArchVirtMachineState *lvms) |
| 389 | { |
| 390 | Aml *dev, *crs; |
| 391 | MemoryRegion *flash_mem; |
| 392 | |
| 393 | hwaddr flash0_base; |
| 394 | hwaddr flash0_size; |
| 395 | |
| 396 | hwaddr flash1_base; |
| 397 | hwaddr flash1_size; |
| 398 | |
| 399 | flash_mem = pflash_cfi01_get_memory(lvms->flash[0]); |
| 400 | flash0_base = flash_mem->addr; |
| 401 | flash0_size = memory_region_size(flash_mem); |
| 402 | |
| 403 | flash_mem = pflash_cfi01_get_memory(lvms->flash[1]); |
| 404 | flash1_base = flash_mem->addr; |
| 405 | flash1_size = memory_region_size(flash_mem); |
| 406 | |
| 407 | dev = aml_device("FLS0"); |
| 408 | aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015"))); |
| 409 | aml_append(dev, aml_name_decl("_UID", aml_int(0))); |
| 410 | |
| 411 | crs = aml_resource_template(); |
| 412 | aml_append(crs, aml_memory32_fixed(flash0_base, flash0_size, |
| 413 | AML_READ_WRITE)); |
| 414 | aml_append(dev, aml_name_decl("_CRS", crs)); |
| 415 | aml_append(scope, dev); |
| 416 | |
| 417 | dev = aml_device("FLS1"); |
| 418 | aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015"))); |
| 419 | aml_append(dev, aml_name_decl("_UID", aml_int(1))); |
| 420 | |
| 421 | crs = aml_resource_template(); |
| 422 | aml_append(crs, aml_memory32_fixed(flash1_base, flash1_size, |
| 423 | AML_READ_WRITE)); |
| 424 | aml_append(dev, aml_name_decl("_CRS", crs)); |
| 425 | aml_append(scope, dev); |
| 426 | } |
| 427 | |
| 428 | #ifdef CONFIG_TPM |
| 429 | static void acpi_dsdt_add_tpm(Aml *scope, LoongArchVirtMachineState *vms) |
| 430 | { |
| 431 | PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); |
| 432 | hwaddr pbus_base = VIRT_PLATFORM_BUS_BASEADDRESS; |
| 433 | SysBusDevice *sbdev = SYS_BUS_DEVICE(tpm_find()); |
| 434 | MemoryRegion *sbdev_mr; |
| 435 | hwaddr tpm_base; |
| 436 | |
| 437 | if (!sbdev) { |
| 438 | return; |
| 439 | } |
| 440 | |
| 441 | tpm_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); |
| 442 | assert(tpm_base != -1); |
| 443 | |
| 444 | tpm_base += pbus_base; |
| 445 | |
| 446 | sbdev_mr = sysbus_mmio_get_region(sbdev, 0); |
| 447 | |
| 448 | Aml *dev = aml_device("TPM0"); |
| 449 | aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101"))); |
| 450 | aml_append(dev, aml_name_decl("_STR", aml_string("TPM 2.0 Device"))); |
| 451 | aml_append(dev, aml_name_decl("_UID", aml_int(0))); |
| 452 | |
| 453 | Aml *crs = aml_resource_template(); |
| 454 | aml_append(crs, |
| 455 | aml_memory32_fixed(tpm_base, |
| 456 | (uint32_t)memory_region_size(sbdev_mr), |
| 457 | AML_READ_WRITE)); |
| 458 | aml_append(dev, aml_name_decl("_CRS", crs)); |
| 459 | aml_append(scope, dev); |
| 460 | } |
| 461 | #endif |
| 462 | |
| 463 | /* build DSDT */ |
| 464 | static void |
| 465 | build_dsdt(GArray *table_data, BIOSLinker *linker, MachineState *machine) |
| 466 | { |
| 467 | int i; |
| 468 | Aml *dsdt, *scope, *pkg; |
| 469 | LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); |
| 470 | AcpiTable table = { .sig = "DSDT", .rev = 1, .oem_id = lvms->oem_id, |
| 471 | .oem_table_id = lvms->oem_table_id }; |
| 472 | |
| 473 | acpi_table_begin(&table, table_data); |
| 474 | dsdt = init_aml_allocator(); |
| 475 | for (i = 0; i < VIRT_UART_COUNT; i++) { |
| 476 | build_uart_device_aml(dsdt, i); |
| 477 | } |
| 478 | build_pci_device_aml(dsdt, lvms); |
| 479 | build_la_ged_aml(dsdt, machine); |
| 480 | build_flash_aml(dsdt, lvms); |
| 481 | #ifdef CONFIG_TPM |
| 482 | acpi_dsdt_add_tpm(dsdt, lvms); |
| 483 | #endif |
| 484 | /* System State Package */ |
| 485 | scope = aml_scope("\\"); |
| 486 | pkg = aml_package(4); |
| 487 | aml_append(pkg, aml_int(ACPI_GED_SLP_TYP_S5)); |
| 488 | aml_append(pkg, aml_int(0)); /* ignored */ |
| 489 | aml_append(pkg, aml_int(0)); /* reserved */ |
| 490 | aml_append(pkg, aml_int(0)); /* reserved */ |
| 491 | aml_append(scope, aml_name_decl("_S5", pkg)); |
| 492 | aml_append(dsdt, scope); |
| 493 | /* Copy AML table into ACPI tables blob and patch header there */ |
| 494 | g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); |
| 495 | acpi_table_end(linker, &table); |
| 496 | free_aml_allocator(); |
| 497 | } |
| 498 | |
| 499 | static void acpi_build(AcpiBuildTables *tables, MachineState *machine) |
| 500 | { |
| 501 | LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); |
| 502 | GArray *table_offsets; |
| 503 | AcpiFadtData fadt_data; |
| 504 | unsigned facs, xsdt, dsdt; |
| 505 | uint8_t *u; |
| 506 | GArray *tables_blob = tables->table_data; |
| 507 | |
| 508 | init_common_fadt_data(&fadt_data); |
| 509 | |
| 510 | table_offsets = g_array_new(false, true, sizeof(uint32_t)); |
| 511 | ACPI_BUILD_DPRINTF("init ACPI tables\n"); |
| 512 | |
| 513 | bios_linker_loader_alloc(tables->linker, |
| 514 | ACPI_BUILD_TABLE_FILE, tables_blob, |
| 515 | 64, false); |
| 516 | |
| 517 | /* |
| 518 | * FACS is pointed to by FADT. |
| 519 | * We place it first since it's the only table that has alignment |
| 520 | * requirements. |
| 521 | */ |
| 522 | facs = tables_blob->len; |
| 523 | build_facs(tables_blob); |
| 524 | |
| 525 | /* DSDT is pointed to by FADT */ |
| 526 | dsdt = tables_blob->len; |
| 527 | build_dsdt(tables_blob, tables->linker, machine); |
| 528 | |
| 529 | /* ACPI tables pointed to by RSDT */ |
| 530 | acpi_add_table(table_offsets, tables_blob); |
| 531 | fadt_data.facs_tbl_offset = &facs; |
| 532 | fadt_data.dsdt_tbl_offset = &dsdt; |
| 533 | fadt_data.xdsdt_tbl_offset = &dsdt; |
| 534 | build_fadt(tables_blob, tables->linker, &fadt_data, |
| 535 | lvms->oem_id, lvms->oem_table_id); |
| 536 | |
| 537 | acpi_add_table(table_offsets, tables_blob); |
| 538 | build_madt(tables_blob, tables->linker, lvms); |
| 539 | |
| 540 | acpi_add_table(table_offsets, tables_blob); |
| 541 | build_pptt(tables_blob, tables->linker, machine, lvms->oem_id, |
| 542 | lvms->oem_table_id, 0, NULL); |
| 543 | |
| 544 | acpi_add_table(table_offsets, tables_blob); |
| 545 | build_srat(tables_blob, tables->linker, machine); |
| 546 | acpi_add_table(table_offsets, tables_blob); |
| 547 | |
| 548 | if (machine->acpi_spcr_enabled) |
| 549 | spcr_setup(tables_blob, tables->linker, machine); |
| 550 | |
| 551 | if (machine->numa_state->num_nodes) { |
| 552 | if (machine->numa_state->have_numa_distance) { |
| 553 | acpi_add_table(table_offsets, tables_blob); |
| 554 | build_slit(tables_blob, tables->linker, machine, lvms->oem_id, |
| 555 | lvms->oem_table_id); |
| 556 | } |
| 557 | if (machine->numa_state->hmat_enabled) { |
| 558 | acpi_add_table(table_offsets, tables_blob); |
| 559 | build_hmat(tables_blob, tables->linker, machine->numa_state, |
| 560 | lvms->oem_id, lvms->oem_table_id); |
| 561 | } |
| 562 | } |
| 563 | |
| 564 | acpi_add_table(table_offsets, tables_blob); |
| 565 | { |
| 566 | AcpiMcfgInfo mcfg = { |
| 567 | .base = lvms->gpex.ecam.base, |
| 568 | .size = lvms->gpex.ecam.size, |
| 569 | }; |
| 570 | build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id, |
| 571 | lvms->oem_table_id); |
| 572 | } |
| 573 | |
| 574 | #ifdef CONFIG_TPM |
| 575 | /* TPM info */ |
| 576 | if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) { |
| 577 | acpi_add_table(table_offsets, tables_blob); |
| 578 | build_tpm2(tables_blob, tables->linker, |
| 579 | tables->tcpalog, lvms->oem_id, |
| 580 | lvms->oem_table_id); |
| 581 | } |
| 582 | #endif |
| 583 | /* Add tables supplied by user (if any) */ |
| 584 | for (u = acpi_table_first(); u; u = acpi_table_next(u)) { |
| 585 | unsigned len = acpi_table_len(u); |
| 586 | |
| 587 | acpi_add_table(table_offsets, tables_blob); |
| 588 | g_array_append_vals(tables_blob, u, len); |
| 589 | } |
| 590 | |
| 591 | /* RSDT is pointed to by RSDP */ |
| 592 | xsdt = tables_blob->len; |
| 593 | build_xsdt(tables_blob, tables->linker, table_offsets, |
| 594 | lvms->oem_id, lvms->oem_table_id); |
| 595 | |
| 596 | /* RSDP is in FSEG memory, so allocate it separately */ |
| 597 | { |
| 598 | AcpiRsdpData rsdp_data = { |
| 599 | .revision = 2, |
| 600 | .oem_id = lvms->oem_id, |
| 601 | .xsdt_tbl_offset = &xsdt, |
| 602 | .rsdt_tbl_offset = NULL, |
| 603 | }; |
| 604 | build_rsdp(tables->rsdp, tables->linker, &rsdp_data); |
| 605 | } |
| 606 | |
| 607 | /* |
| 608 | * The align size is 128, warn if 64k is not enough therefore |
| 609 | * the align size could be resized. |
| 610 | */ |
| 611 | if (tables_blob->len > ACPI_BUILD_TABLE_SIZE / 2) { |
| 612 | warn_report("ACPI table size %u exceeds %d bytes," |
| 613 | " migration may not work", |
| 614 | tables_blob->len, ACPI_BUILD_TABLE_SIZE / 2); |
| 615 | error_printf("Try removing CPUs, NUMA nodes, memory slots" |
| 616 | " or PCI bridges.\n"); |
| 617 | } |
| 618 | |
| 619 | acpi_align_size(tables->linker->cmd_blob, ACPI_BUILD_ALIGN_SIZE); |
| 620 | |
| 621 | /* Cleanup memory that's no longer used. */ |
| 622 | g_array_free(table_offsets, true); |
| 623 | } |
| 624 | |
| 625 | static void acpi_ram_update(MemoryRegion *mr, GArray *data) |
| 626 | { |
| 627 | uint32_t size = acpi_data_len(data); |
| 628 | |
| 629 | /* |
| 630 | * Make sure RAM size is correct - in case it got changed |
| 631 | * e.g. by migration |
| 632 | */ |
| 633 | memory_region_ram_resize(mr, size, &error_abort); |
| 634 | |
| 635 | memcpy(memory_region_get_ram_ptr(mr), data->data, size); |
| 636 | memory_region_set_dirty(mr, 0, size); |
| 637 | } |
| 638 | |
| 639 | static void acpi_build_update(void *build_opaque) |
| 640 | { |
| 641 | AcpiBuildState *build_state = build_opaque; |
| 642 | AcpiBuildTables tables; |
| 643 | |
| 644 | /* No state to update or already patched? Nothing to do. */ |
| 645 | if (!build_state || build_state->patched) { |
| 646 | return; |
| 647 | } |
| 648 | build_state->patched = 1; |
| 649 | |
| 650 | acpi_build_tables_init(&tables); |
| 651 | |
| 652 | acpi_build(&tables, MACHINE(qdev_get_machine())); |
| 653 | |
| 654 | acpi_ram_update(build_state->table_mr, tables.table_data); |
| 655 | acpi_ram_update(build_state->rsdp_mr, tables.rsdp); |
| 656 | acpi_ram_update(build_state->linker_mr, tables.linker->cmd_blob); |
| 657 | |
| 658 | acpi_build_tables_cleanup(&tables, true); |
| 659 | } |
| 660 | |
| 661 | static void acpi_build_reset(void *build_opaque) |
| 662 | { |
| 663 | AcpiBuildState *build_state = build_opaque; |
| 664 | build_state->patched = 0; |
| 665 | } |
| 666 | |
| 667 | static const VMStateDescription vmstate_acpi_build = { |
| 668 | .name = "acpi_build", |
| 669 | .version_id = 1, |
| 670 | .minimum_version_id = 1, |
| 671 | .fields = (const VMStateField[]) { |
| 672 | VMSTATE_UINT8(patched, AcpiBuildState), |
| 673 | VMSTATE_END_OF_LIST() |
| 674 | }, |
| 675 | }; |
| 676 | |
| 677 | static bool virt_is_acpi_enabled(LoongArchVirtMachineState *lvms) |
| 678 | { |
| 679 | if (lvms->acpi == ON_OFF_AUTO_OFF) { |
| 680 | return false; |
| 681 | } |
| 682 | return true; |
| 683 | } |
| 684 | |
| 685 | void virt_acpi_setup(LoongArchVirtMachineState *lvms) |
| 686 | { |
| 687 | AcpiBuildTables tables; |
| 688 | AcpiBuildState *build_state; |
| 689 | |
| 690 | if (!lvms->fw_cfg) { |
| 691 | ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); |
| 692 | return; |
| 693 | } |
| 694 | |
| 695 | if (!virt_is_acpi_enabled(lvms)) { |
| 696 | ACPI_BUILD_DPRINTF("ACPI disabled. Bailing out.\n"); |
| 697 | return; |
| 698 | } |
| 699 | |
| 700 | build_state = g_malloc0(sizeof *build_state); |
| 701 | |
| 702 | acpi_build_tables_init(&tables); |
| 703 | acpi_build(&tables, MACHINE(lvms)); |
| 704 | |
| 705 | /* Now expose it all to Guest */ |
| 706 | build_state->table_mr = acpi_add_rom_blob(acpi_build_update, |
| 707 | build_state, tables.table_data, |
| 708 | ACPI_BUILD_TABLE_FILE); |
| 709 | assert(build_state->table_mr != NULL); |
| 710 | |
| 711 | build_state->linker_mr = |
| 712 | acpi_add_rom_blob(acpi_build_update, build_state, |
| 713 | tables.linker->cmd_blob, ACPI_BUILD_LOADER_FILE); |
| 714 | |
| 715 | build_state->rsdp_mr = acpi_add_rom_blob(acpi_build_update, |
| 716 | build_state, tables.rsdp, |
| 717 | ACPI_BUILD_RSDP_FILE); |
| 718 | |
| 719 | fw_cfg_add_file(lvms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, |
| 720 | acpi_data_len(tables.tcpalog)); |
| 721 | |
| 722 | qemu_register_reset(acpi_build_reset, build_state); |
| 723 | acpi_build_reset(build_state); |
| 724 | vmstate_register(NULL, 0, &vmstate_acpi_build, build_state); |
| 725 | |
| 726 | /* |
| 727 | * Cleanup tables but don't free the memory: we track it |
| 728 | * in build_state. |
| 729 | */ |
| 730 | acpi_build_tables_cleanup(&tables, false); |
| 731 | } |