172c194f7SMichael S. Tsirkin /* Support for generating ACPI tables and passing them to Guests 272c194f7SMichael S. Tsirkin * 372c194f7SMichael S. Tsirkin * Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> 472c194f7SMichael S. Tsirkin * Copyright (C) 2006 Fabrice Bellard 572c194f7SMichael S. Tsirkin * Copyright (C) 2013 Red Hat Inc 672c194f7SMichael S. Tsirkin * 772c194f7SMichael S. Tsirkin * Author: Michael S. Tsirkin <mst@redhat.com> 872c194f7SMichael S. Tsirkin * 972c194f7SMichael S. Tsirkin * This program is free software; you can redistribute it and/or modify 1072c194f7SMichael S. Tsirkin * it under the terms of the GNU General Public License as published by 1172c194f7SMichael S. Tsirkin * the Free Software Foundation; either version 2 of the License, or 1272c194f7SMichael S. Tsirkin * (at your option) any later version. 1372c194f7SMichael S. Tsirkin 1472c194f7SMichael S. Tsirkin * This program is distributed in the hope that it will be useful, 1572c194f7SMichael S. Tsirkin * but WITHOUT ANY WARRANTY; without even the implied warranty of 1672c194f7SMichael S. Tsirkin * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1772c194f7SMichael S. Tsirkin * GNU General Public License for more details. 1872c194f7SMichael S. Tsirkin 1972c194f7SMichael S. Tsirkin * You should have received a copy of the GNU General Public License along 2072c194f7SMichael S. Tsirkin * with this program; if not, see <http://www.gnu.org/licenses/>. 2172c194f7SMichael S. Tsirkin */ 2272c194f7SMichael S. Tsirkin 2372c194f7SMichael S. Tsirkin #include "acpi-build.h" 2472c194f7SMichael S. Tsirkin #include <stddef.h> 2572c194f7SMichael S. Tsirkin #include <glib.h> 2672c194f7SMichael S. Tsirkin #include "qemu-common.h" 2772c194f7SMichael S. Tsirkin #include "qemu/bitmap.h" 2807fb6176SPaolo Bonzini #include "qemu/osdep.h" 2972c194f7SMichael S. Tsirkin #include "qemu/range.h" 3007fb6176SPaolo Bonzini #include "qemu/error-report.h" 3172c194f7SMichael S. Tsirkin #include "hw/pci/pci.h" 3272c194f7SMichael S. Tsirkin #include "qom/cpu.h" 3372c194f7SMichael S. Tsirkin #include "hw/i386/pc.h" 3472c194f7SMichael S. Tsirkin #include "target-i386/cpu.h" 3572c194f7SMichael S. Tsirkin #include "hw/timer/hpet.h" 3672c194f7SMichael S. Tsirkin #include "hw/i386/acpi-defs.h" 3772c194f7SMichael S. Tsirkin #include "hw/acpi/acpi.h" 3872c194f7SMichael S. Tsirkin #include "hw/nvram/fw_cfg.h" 3972c194f7SMichael S. Tsirkin #include "bios-linker-loader.h" 4072c194f7SMichael S. Tsirkin #include "hw/loader.h" 4115bce1b7SGabriel L. Somlo #include "hw/isa/isa.h" 42bef3492dSIgor Mammedov #include "hw/acpi/memory_hotplug.h" 43711b20b4SStefan Berger #include "sysemu/tpm.h" 44711b20b4SStefan Berger #include "hw/acpi/tpm.h" 4572c194f7SMichael S. Tsirkin 4672c194f7SMichael S. Tsirkin /* Supported chipsets: */ 4772c194f7SMichael S. Tsirkin #include "hw/acpi/piix4.h" 4899fd437dSMichael S. Tsirkin #include "hw/acpi/pcihp.h" 4972c194f7SMichael S. Tsirkin #include "hw/i386/ich9.h" 5072c194f7SMichael S. Tsirkin #include "hw/pci/pci_bus.h" 5172c194f7SMichael S. Tsirkin #include "hw/pci-host/q35.h" 52*d4eb9119SLe Tan #include "hw/i386/intel_iommu.h" 5372c194f7SMichael S. Tsirkin 5472c194f7SMichael S. Tsirkin #include "hw/i386/q35-acpi-dsdt.hex" 5572c194f7SMichael S. Tsirkin #include "hw/i386/acpi-dsdt.hex" 5672c194f7SMichael S. Tsirkin 5772c194f7SMichael S. Tsirkin #include "qapi/qmp/qint.h" 5872c194f7SMichael S. Tsirkin #include "qom/qom-qobject.h" 5972c194f7SMichael S. Tsirkin 6007fb6176SPaolo Bonzini /* These are used to size the ACPI tables for -M pc-i440fx-1.7 and 6107fb6176SPaolo Bonzini * -M pc-i440fx-2.0. Even if the actual amount of AML generated grows 6207fb6176SPaolo Bonzini * a little bit, there should be plenty of free space since the DSDT 6307fb6176SPaolo Bonzini * shrunk by ~1.5k between QEMU 2.0 and QEMU 2.1. 6407fb6176SPaolo Bonzini */ 6507fb6176SPaolo Bonzini #define ACPI_BUILD_LEGACY_CPU_AML_SIZE 97 6607fb6176SPaolo Bonzini #define ACPI_BUILD_ALIGN_SIZE 0x1000 6707fb6176SPaolo Bonzini 68868270f2SMichael S. Tsirkin #define ACPI_BUILD_TABLE_SIZE 0x20000 6918045fb9SPaolo Bonzini 7072c194f7SMichael S. Tsirkin typedef struct AcpiCpuInfo { 71798325edSEduardo Habkost DECLARE_BITMAP(found_cpus, ACPI_CPU_HOTPLUG_ID_LIMIT); 7272c194f7SMichael S. Tsirkin } AcpiCpuInfo; 7372c194f7SMichael S. Tsirkin 7472c194f7SMichael S. Tsirkin typedef struct AcpiMcfgInfo { 7572c194f7SMichael S. Tsirkin uint64_t mcfg_base; 7672c194f7SMichael S. Tsirkin uint32_t mcfg_size; 7772c194f7SMichael S. Tsirkin } AcpiMcfgInfo; 7872c194f7SMichael S. Tsirkin 7972c194f7SMichael S. Tsirkin typedef struct AcpiPmInfo { 8072c194f7SMichael S. Tsirkin bool s3_disabled; 8172c194f7SMichael S. Tsirkin bool s4_disabled; 82133a2da4SIgor Mammedov bool pcihp_bridge_en; 8372c194f7SMichael S. Tsirkin uint8_t s4_val; 8472c194f7SMichael S. Tsirkin uint16_t sci_int; 8572c194f7SMichael S. Tsirkin uint8_t acpi_enable_cmd; 8672c194f7SMichael S. Tsirkin uint8_t acpi_disable_cmd; 8772c194f7SMichael S. Tsirkin uint32_t gpe0_blk; 8872c194f7SMichael S. Tsirkin uint32_t gpe0_blk_len; 8972c194f7SMichael S. Tsirkin uint32_t io_base; 9072c194f7SMichael S. Tsirkin } AcpiPmInfo; 9172c194f7SMichael S. Tsirkin 9272c194f7SMichael S. Tsirkin typedef struct AcpiMiscInfo { 9372c194f7SMichael S. Tsirkin bool has_hpet; 94711b20b4SStefan Berger bool has_tpm; 9572c194f7SMichael S. Tsirkin DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX); 9672c194f7SMichael S. Tsirkin const unsigned char *dsdt_code; 9772c194f7SMichael S. Tsirkin unsigned dsdt_size; 9872c194f7SMichael S. Tsirkin uint16_t pvpanic_port; 9972c194f7SMichael S. Tsirkin } AcpiMiscInfo; 10072c194f7SMichael S. Tsirkin 10199fd437dSMichael S. Tsirkin typedef struct AcpiBuildPciBusHotplugState { 10299fd437dSMichael S. Tsirkin GArray *device_table; 10399fd437dSMichael S. Tsirkin GArray *notify_table; 10499fd437dSMichael S. Tsirkin struct AcpiBuildPciBusHotplugState *parent; 105133a2da4SIgor Mammedov bool pcihp_bridge_en; 10699fd437dSMichael S. Tsirkin } AcpiBuildPciBusHotplugState; 10799fd437dSMichael S. Tsirkin 10872c194f7SMichael S. Tsirkin static void acpi_get_dsdt(AcpiMiscInfo *info) 10972c194f7SMichael S. Tsirkin { 1108977557aSGabriel L. Somlo uint16_t *applesmc_sta; 11172c194f7SMichael S. Tsirkin Object *piix = piix4_pm_find(); 11272c194f7SMichael S. Tsirkin Object *lpc = ich9_lpc_find(); 11372c194f7SMichael S. Tsirkin assert(!!piix != !!lpc); 11472c194f7SMichael S. Tsirkin 11572c194f7SMichael S. Tsirkin if (piix) { 11672c194f7SMichael S. Tsirkin info->dsdt_code = AcpiDsdtAmlCode; 11772c194f7SMichael S. Tsirkin info->dsdt_size = sizeof AcpiDsdtAmlCode; 1188977557aSGabriel L. Somlo applesmc_sta = piix_dsdt_applesmc_sta; 11972c194f7SMichael S. Tsirkin } 12072c194f7SMichael S. Tsirkin if (lpc) { 12172c194f7SMichael S. Tsirkin info->dsdt_code = Q35AcpiDsdtAmlCode; 12272c194f7SMichael S. Tsirkin info->dsdt_size = sizeof Q35AcpiDsdtAmlCode; 1238977557aSGabriel L. Somlo applesmc_sta = q35_dsdt_applesmc_sta; 12472c194f7SMichael S. Tsirkin } 12515bce1b7SGabriel L. Somlo 12615bce1b7SGabriel L. Somlo /* Patch in appropriate value for AppleSMC _STA */ 1278977557aSGabriel L. Somlo *(uint8_t *)(info->dsdt_code + *applesmc_sta) = 1288977557aSGabriel L. Somlo applesmc_find() ? 0x0b : 0x00; 12972c194f7SMichael S. Tsirkin } 13072c194f7SMichael S. Tsirkin 13172c194f7SMichael S. Tsirkin static 13272c194f7SMichael S. Tsirkin int acpi_add_cpu_info(Object *o, void *opaque) 13372c194f7SMichael S. Tsirkin { 13472c194f7SMichael S. Tsirkin AcpiCpuInfo *cpu = opaque; 13572c194f7SMichael S. Tsirkin uint64_t apic_id; 13672c194f7SMichael S. Tsirkin 13772c194f7SMichael S. Tsirkin if (object_dynamic_cast(o, TYPE_CPU)) { 13872c194f7SMichael S. Tsirkin apic_id = object_property_get_int(o, "apic-id", NULL); 139798325edSEduardo Habkost assert(apic_id < ACPI_CPU_HOTPLUG_ID_LIMIT); 14072c194f7SMichael S. Tsirkin 14172c194f7SMichael S. Tsirkin set_bit(apic_id, cpu->found_cpus); 14272c194f7SMichael S. Tsirkin } 14372c194f7SMichael S. Tsirkin 14472c194f7SMichael S. Tsirkin object_child_foreach(o, acpi_add_cpu_info, opaque); 14572c194f7SMichael S. Tsirkin return 0; 14672c194f7SMichael S. Tsirkin } 14772c194f7SMichael S. Tsirkin 14872c194f7SMichael S. Tsirkin static void acpi_get_cpu_info(AcpiCpuInfo *cpu) 14972c194f7SMichael S. Tsirkin { 15072c194f7SMichael S. Tsirkin Object *root = object_get_root(); 15172c194f7SMichael S. Tsirkin 15272c194f7SMichael S. Tsirkin memset(cpu->found_cpus, 0, sizeof cpu->found_cpus); 15372c194f7SMichael S. Tsirkin object_child_foreach(root, acpi_add_cpu_info, cpu); 15472c194f7SMichael S. Tsirkin } 15572c194f7SMichael S. Tsirkin 15672c194f7SMichael S. Tsirkin static void acpi_get_pm_info(AcpiPmInfo *pm) 15772c194f7SMichael S. Tsirkin { 15872c194f7SMichael S. Tsirkin Object *piix = piix4_pm_find(); 15972c194f7SMichael S. Tsirkin Object *lpc = ich9_lpc_find(); 16072c194f7SMichael S. Tsirkin Object *obj = NULL; 16172c194f7SMichael S. Tsirkin QObject *o; 16272c194f7SMichael S. Tsirkin 16372c194f7SMichael S. Tsirkin if (piix) { 16472c194f7SMichael S. Tsirkin obj = piix; 16572c194f7SMichael S. Tsirkin } 16672c194f7SMichael S. Tsirkin if (lpc) { 16772c194f7SMichael S. Tsirkin obj = lpc; 16872c194f7SMichael S. Tsirkin } 16972c194f7SMichael S. Tsirkin assert(obj); 17072c194f7SMichael S. Tsirkin 17172c194f7SMichael S. Tsirkin /* Fill in optional s3/s4 related properties */ 17272c194f7SMichael S. Tsirkin o = object_property_get_qobject(obj, ACPI_PM_PROP_S3_DISABLED, NULL); 17372c194f7SMichael S. Tsirkin if (o) { 17472c194f7SMichael S. Tsirkin pm->s3_disabled = qint_get_int(qobject_to_qint(o)); 17572c194f7SMichael S. Tsirkin } else { 17672c194f7SMichael S. Tsirkin pm->s3_disabled = false; 17772c194f7SMichael S. Tsirkin } 178097a97a6SKirill Batuzov qobject_decref(o); 17972c194f7SMichael S. Tsirkin o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_DISABLED, NULL); 18072c194f7SMichael S. Tsirkin if (o) { 18172c194f7SMichael S. Tsirkin pm->s4_disabled = qint_get_int(qobject_to_qint(o)); 18272c194f7SMichael S. Tsirkin } else { 18372c194f7SMichael S. Tsirkin pm->s4_disabled = false; 18472c194f7SMichael S. Tsirkin } 185097a97a6SKirill Batuzov qobject_decref(o); 18672c194f7SMichael S. Tsirkin o = object_property_get_qobject(obj, ACPI_PM_PROP_S4_VAL, NULL); 18772c194f7SMichael S. Tsirkin if (o) { 18872c194f7SMichael S. Tsirkin pm->s4_val = qint_get_int(qobject_to_qint(o)); 18972c194f7SMichael S. Tsirkin } else { 19072c194f7SMichael S. Tsirkin pm->s4_val = false; 19172c194f7SMichael S. Tsirkin } 192097a97a6SKirill Batuzov qobject_decref(o); 19372c194f7SMichael S. Tsirkin 19472c194f7SMichael S. Tsirkin /* Fill in mandatory properties */ 19572c194f7SMichael S. Tsirkin pm->sci_int = object_property_get_int(obj, ACPI_PM_PROP_SCI_INT, NULL); 19672c194f7SMichael S. Tsirkin 19772c194f7SMichael S. Tsirkin pm->acpi_enable_cmd = object_property_get_int(obj, 19872c194f7SMichael S. Tsirkin ACPI_PM_PROP_ACPI_ENABLE_CMD, 19972c194f7SMichael S. Tsirkin NULL); 20072c194f7SMichael S. Tsirkin pm->acpi_disable_cmd = object_property_get_int(obj, 20172c194f7SMichael S. Tsirkin ACPI_PM_PROP_ACPI_DISABLE_CMD, 20272c194f7SMichael S. Tsirkin NULL); 20372c194f7SMichael S. Tsirkin pm->io_base = object_property_get_int(obj, ACPI_PM_PROP_PM_IO_BASE, 20472c194f7SMichael S. Tsirkin NULL); 20572c194f7SMichael S. Tsirkin pm->gpe0_blk = object_property_get_int(obj, ACPI_PM_PROP_GPE0_BLK, 20672c194f7SMichael S. Tsirkin NULL); 20772c194f7SMichael S. Tsirkin pm->gpe0_blk_len = object_property_get_int(obj, ACPI_PM_PROP_GPE0_BLK_LEN, 20872c194f7SMichael S. Tsirkin NULL); 209133a2da4SIgor Mammedov pm->pcihp_bridge_en = 210133a2da4SIgor Mammedov object_property_get_bool(obj, "acpi-pci-hotplug-with-bridge-support", 211133a2da4SIgor Mammedov NULL); 21272c194f7SMichael S. Tsirkin } 21372c194f7SMichael S. Tsirkin 21472c194f7SMichael S. Tsirkin static void acpi_get_misc_info(AcpiMiscInfo *info) 21572c194f7SMichael S. Tsirkin { 21672c194f7SMichael S. Tsirkin info->has_hpet = hpet_find(); 217711b20b4SStefan Berger info->has_tpm = tpm_find(); 21872c194f7SMichael S. Tsirkin info->pvpanic_port = pvpanic_port(); 21972c194f7SMichael S. Tsirkin } 22072c194f7SMichael S. Tsirkin 22172c194f7SMichael S. Tsirkin static void acpi_get_pci_info(PcPciInfo *info) 22272c194f7SMichael S. Tsirkin { 22372c194f7SMichael S. Tsirkin Object *pci_host; 22472c194f7SMichael S. Tsirkin bool ambiguous; 22572c194f7SMichael S. Tsirkin 22672c194f7SMichael S. Tsirkin pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous); 22772c194f7SMichael S. Tsirkin g_assert(!ambiguous); 22872c194f7SMichael S. Tsirkin g_assert(pci_host); 22972c194f7SMichael S. Tsirkin 23072c194f7SMichael S. Tsirkin info->w32.begin = object_property_get_int(pci_host, 23172c194f7SMichael S. Tsirkin PCI_HOST_PROP_PCI_HOLE_START, 23272c194f7SMichael S. Tsirkin NULL); 23372c194f7SMichael S. Tsirkin info->w32.end = object_property_get_int(pci_host, 23472c194f7SMichael S. Tsirkin PCI_HOST_PROP_PCI_HOLE_END, 23572c194f7SMichael S. Tsirkin NULL); 23672c194f7SMichael S. Tsirkin info->w64.begin = object_property_get_int(pci_host, 23772c194f7SMichael S. Tsirkin PCI_HOST_PROP_PCI_HOLE64_START, 23872c194f7SMichael S. Tsirkin NULL); 23972c194f7SMichael S. Tsirkin info->w64.end = object_property_get_int(pci_host, 24072c194f7SMichael S. Tsirkin PCI_HOST_PROP_PCI_HOLE64_END, 24172c194f7SMichael S. Tsirkin NULL); 24272c194f7SMichael S. Tsirkin } 24372c194f7SMichael S. Tsirkin 24472c194f7SMichael S. Tsirkin #define ACPI_BUILD_APPNAME "Bochs" 24572c194f7SMichael S. Tsirkin #define ACPI_BUILD_APPNAME6 "BOCHS " 24672c194f7SMichael S. Tsirkin #define ACPI_BUILD_APPNAME4 "BXPC" 24772c194f7SMichael S. Tsirkin 24872c194f7SMichael S. Tsirkin #define ACPI_BUILD_DPRINTF(level, fmt, ...) do {} while (0) 24972c194f7SMichael S. Tsirkin 25072c194f7SMichael S. Tsirkin #define ACPI_BUILD_TABLE_FILE "etc/acpi/tables" 25172c194f7SMichael S. Tsirkin #define ACPI_BUILD_RSDP_FILE "etc/acpi/rsdp" 25272c194f7SMichael S. Tsirkin 25372c194f7SMichael S. Tsirkin static void 25472c194f7SMichael S. Tsirkin build_header(GArray *linker, GArray *table_data, 255821e3227SMichael S. Tsirkin AcpiTableHeader *h, const char *sig, int len, uint8_t rev) 25672c194f7SMichael S. Tsirkin { 257821e3227SMichael S. Tsirkin memcpy(&h->signature, sig, 4); 25872c194f7SMichael S. Tsirkin h->length = cpu_to_le32(len); 25972c194f7SMichael S. Tsirkin h->revision = rev; 26072c194f7SMichael S. Tsirkin memcpy(h->oem_id, ACPI_BUILD_APPNAME6, 6); 26172c194f7SMichael S. Tsirkin memcpy(h->oem_table_id, ACPI_BUILD_APPNAME4, 4); 262821e3227SMichael S. Tsirkin memcpy(h->oem_table_id + 4, sig, 4); 26372c194f7SMichael S. Tsirkin h->oem_revision = cpu_to_le32(1); 26472c194f7SMichael S. Tsirkin memcpy(h->asl_compiler_id, ACPI_BUILD_APPNAME4, 4); 26572c194f7SMichael S. Tsirkin h->asl_compiler_revision = cpu_to_le32(1); 26672c194f7SMichael S. Tsirkin h->checksum = 0; 26772c194f7SMichael S. Tsirkin /* Checksum to be filled in by Guest linker */ 26872c194f7SMichael S. Tsirkin bios_linker_loader_add_checksum(linker, ACPI_BUILD_TABLE_FILE, 26972c194f7SMichael S. Tsirkin table_data->data, h, len, &h->checksum); 27072c194f7SMichael S. Tsirkin } 27172c194f7SMichael S. Tsirkin 27272c194f7SMichael S. Tsirkin static inline GArray *build_alloc_array(void) 27372c194f7SMichael S. Tsirkin { 27472c194f7SMichael S. Tsirkin return g_array_new(false, true /* clear */, 1); 27572c194f7SMichael S. Tsirkin } 27672c194f7SMichael S. Tsirkin 27772c194f7SMichael S. Tsirkin static inline void build_free_array(GArray *array) 27872c194f7SMichael S. Tsirkin { 27972c194f7SMichael S. Tsirkin g_array_free(array, true); 28072c194f7SMichael S. Tsirkin } 28172c194f7SMichael S. Tsirkin 28272c194f7SMichael S. Tsirkin static inline void build_prepend_byte(GArray *array, uint8_t val) 28372c194f7SMichael S. Tsirkin { 28472c194f7SMichael S. Tsirkin g_array_prepend_val(array, val); 28572c194f7SMichael S. Tsirkin } 28672c194f7SMichael S. Tsirkin 28772c194f7SMichael S. Tsirkin static inline void build_append_byte(GArray *array, uint8_t val) 28872c194f7SMichael S. Tsirkin { 28972c194f7SMichael S. Tsirkin g_array_append_val(array, val); 29072c194f7SMichael S. Tsirkin } 29172c194f7SMichael S. Tsirkin 29272c194f7SMichael S. Tsirkin static inline void build_append_array(GArray *array, GArray *val) 29372c194f7SMichael S. Tsirkin { 29472c194f7SMichael S. Tsirkin g_array_append_vals(array, val->data, val->len); 29572c194f7SMichael S. Tsirkin } 29672c194f7SMichael S. Tsirkin 297867d898cSStefan Weil static void GCC_FMT_ATTR(2, 3) 298867d898cSStefan Weil build_append_nameseg(GArray *array, const char *format, ...) 29972c194f7SMichael S. Tsirkin { 300542da88fSMichael S. Tsirkin /* It would be nicer to use g_string_vprintf but it's only there in 2.22 */ 301542da88fSMichael S. Tsirkin char s[] = "XXXX"; 302542da88fSMichael S. Tsirkin int len; 30372c194f7SMichael S. Tsirkin va_list args; 30472c194f7SMichael S. Tsirkin 30572c194f7SMichael S. Tsirkin va_start(args, format); 306542da88fSMichael S. Tsirkin len = vsnprintf(s, sizeof s, format, args); 30772c194f7SMichael S. Tsirkin va_end(args); 30872c194f7SMichael S. Tsirkin 309542da88fSMichael S. Tsirkin assert(len == 4); 310542da88fSMichael S. Tsirkin g_array_append_vals(array, s, len); 31172c194f7SMichael S. Tsirkin } 31272c194f7SMichael S. Tsirkin 31372c194f7SMichael S. Tsirkin /* 5.4 Definition Block Encoding */ 31472c194f7SMichael S. Tsirkin enum { 31572c194f7SMichael S. Tsirkin PACKAGE_LENGTH_1BYTE_SHIFT = 6, /* Up to 63 - use extra 2 bits. */ 31672c194f7SMichael S. Tsirkin PACKAGE_LENGTH_2BYTE_SHIFT = 4, 31772c194f7SMichael S. Tsirkin PACKAGE_LENGTH_3BYTE_SHIFT = 12, 31872c194f7SMichael S. Tsirkin PACKAGE_LENGTH_4BYTE_SHIFT = 20, 31972c194f7SMichael S. Tsirkin }; 32072c194f7SMichael S. Tsirkin 32172c194f7SMichael S. Tsirkin static void build_prepend_package_length(GArray *package, unsigned min_bytes) 32272c194f7SMichael S. Tsirkin { 32372c194f7SMichael S. Tsirkin uint8_t byte; 32472c194f7SMichael S. Tsirkin unsigned length = package->len; 32572c194f7SMichael S. Tsirkin unsigned length_bytes; 32672c194f7SMichael S. Tsirkin 32772c194f7SMichael S. Tsirkin if (length + 1 < (1 << PACKAGE_LENGTH_1BYTE_SHIFT)) { 32872c194f7SMichael S. Tsirkin length_bytes = 1; 32972c194f7SMichael S. Tsirkin } else if (length + 2 < (1 << PACKAGE_LENGTH_3BYTE_SHIFT)) { 33072c194f7SMichael S. Tsirkin length_bytes = 2; 33172c194f7SMichael S. Tsirkin } else if (length + 3 < (1 << PACKAGE_LENGTH_4BYTE_SHIFT)) { 33272c194f7SMichael S. Tsirkin length_bytes = 3; 33372c194f7SMichael S. Tsirkin } else { 33472c194f7SMichael S. Tsirkin length_bytes = 4; 33572c194f7SMichael S. Tsirkin } 33672c194f7SMichael S. Tsirkin 33772c194f7SMichael S. Tsirkin /* Force length to at least min_bytes. 33872c194f7SMichael S. Tsirkin * This wastes memory but that's how bios did it. 33972c194f7SMichael S. Tsirkin */ 34072c194f7SMichael S. Tsirkin length_bytes = MAX(length_bytes, min_bytes); 34172c194f7SMichael S. Tsirkin 34272c194f7SMichael S. Tsirkin /* PkgLength is the length of the inclusive length of the data. */ 34372c194f7SMichael S. Tsirkin length += length_bytes; 34472c194f7SMichael S. Tsirkin 34572c194f7SMichael S. Tsirkin switch (length_bytes) { 34672c194f7SMichael S. Tsirkin case 1: 34772c194f7SMichael S. Tsirkin byte = length; 34872c194f7SMichael S. Tsirkin build_prepend_byte(package, byte); 34972c194f7SMichael S. Tsirkin return; 35072c194f7SMichael S. Tsirkin case 4: 35172c194f7SMichael S. Tsirkin byte = length >> PACKAGE_LENGTH_4BYTE_SHIFT; 35272c194f7SMichael S. Tsirkin build_prepend_byte(package, byte); 35372c194f7SMichael S. Tsirkin length &= (1 << PACKAGE_LENGTH_4BYTE_SHIFT) - 1; 35472c194f7SMichael S. Tsirkin /* fall through */ 35572c194f7SMichael S. Tsirkin case 3: 35672c194f7SMichael S. Tsirkin byte = length >> PACKAGE_LENGTH_3BYTE_SHIFT; 35772c194f7SMichael S. Tsirkin build_prepend_byte(package, byte); 35872c194f7SMichael S. Tsirkin length &= (1 << PACKAGE_LENGTH_3BYTE_SHIFT) - 1; 35972c194f7SMichael S. Tsirkin /* fall through */ 36072c194f7SMichael S. Tsirkin case 2: 36172c194f7SMichael S. Tsirkin byte = length >> PACKAGE_LENGTH_2BYTE_SHIFT; 36272c194f7SMichael S. Tsirkin build_prepend_byte(package, byte); 36372c194f7SMichael S. Tsirkin length &= (1 << PACKAGE_LENGTH_2BYTE_SHIFT) - 1; 36472c194f7SMichael S. Tsirkin /* fall through */ 36572c194f7SMichael S. Tsirkin } 36672c194f7SMichael S. Tsirkin /* 36772c194f7SMichael S. Tsirkin * Most significant two bits of byte zero indicate how many following bytes 36872c194f7SMichael S. Tsirkin * are in PkgLength encoding. 36972c194f7SMichael S. Tsirkin */ 37072c194f7SMichael S. Tsirkin byte = ((length_bytes - 1) << PACKAGE_LENGTH_1BYTE_SHIFT) | length; 37172c194f7SMichael S. Tsirkin build_prepend_byte(package, byte); 37272c194f7SMichael S. Tsirkin } 37372c194f7SMichael S. Tsirkin 37472c194f7SMichael S. Tsirkin static void build_package(GArray *package, uint8_t op, unsigned min_bytes) 37572c194f7SMichael S. Tsirkin { 37672c194f7SMichael S. Tsirkin build_prepend_package_length(package, min_bytes); 37772c194f7SMichael S. Tsirkin build_prepend_byte(package, op); 37872c194f7SMichael S. Tsirkin } 37972c194f7SMichael S. Tsirkin 38099fd437dSMichael S. Tsirkin static void build_extop_package(GArray *package, uint8_t op) 38199fd437dSMichael S. Tsirkin { 38299fd437dSMichael S. Tsirkin build_package(package, op, 1); 38399fd437dSMichael S. Tsirkin build_prepend_byte(package, 0x5B); /* ExtOpPrefix */ 38499fd437dSMichael S. Tsirkin } 38599fd437dSMichael S. Tsirkin 38672c194f7SMichael S. Tsirkin static void build_append_value(GArray *table, uint32_t value, int size) 38772c194f7SMichael S. Tsirkin { 38872c194f7SMichael S. Tsirkin uint8_t prefix; 38972c194f7SMichael S. Tsirkin int i; 39072c194f7SMichael S. Tsirkin 39172c194f7SMichael S. Tsirkin switch (size) { 39272c194f7SMichael S. Tsirkin case 1: 39372c194f7SMichael S. Tsirkin prefix = 0x0A; /* BytePrefix */ 39472c194f7SMichael S. Tsirkin break; 39572c194f7SMichael S. Tsirkin case 2: 39672c194f7SMichael S. Tsirkin prefix = 0x0B; /* WordPrefix */ 39772c194f7SMichael S. Tsirkin break; 39872c194f7SMichael S. Tsirkin case 4: 39972c194f7SMichael S. Tsirkin prefix = 0x0C; /* DWordPrefix */ 40072c194f7SMichael S. Tsirkin break; 40172c194f7SMichael S. Tsirkin default: 40272c194f7SMichael S. Tsirkin assert(0); 40372c194f7SMichael S. Tsirkin return; 40472c194f7SMichael S. Tsirkin } 40572c194f7SMichael S. Tsirkin build_append_byte(table, prefix); 40672c194f7SMichael S. Tsirkin for (i = 0; i < size; ++i) { 40772c194f7SMichael S. Tsirkin build_append_byte(table, value & 0xFF); 40872c194f7SMichael S. Tsirkin value = value >> 8; 40972c194f7SMichael S. Tsirkin } 41072c194f7SMichael S. Tsirkin } 41172c194f7SMichael S. Tsirkin 41299fd437dSMichael S. Tsirkin static void build_append_int(GArray *table, uint32_t value) 41399fd437dSMichael S. Tsirkin { 41499fd437dSMichael S. Tsirkin if (value == 0x00) { 41599fd437dSMichael S. Tsirkin build_append_byte(table, 0x00); /* ZeroOp */ 41699fd437dSMichael S. Tsirkin } else if (value == 0x01) { 41799fd437dSMichael S. Tsirkin build_append_byte(table, 0x01); /* OneOp */ 41899fd437dSMichael S. Tsirkin } else if (value <= 0xFF) { 41999fd437dSMichael S. Tsirkin build_append_value(table, value, 1); 420482f38b9SIgor Mammedov } else if (value <= 0xFFFF) { 42199fd437dSMichael S. Tsirkin build_append_value(table, value, 2); 42299fd437dSMichael S. Tsirkin } else { 42399fd437dSMichael S. Tsirkin build_append_value(table, value, 4); 42499fd437dSMichael S. Tsirkin } 42599fd437dSMichael S. Tsirkin } 42699fd437dSMichael S. Tsirkin 42799fd437dSMichael S. Tsirkin static GArray *build_alloc_method(const char *name, uint8_t arg_count) 42899fd437dSMichael S. Tsirkin { 42999fd437dSMichael S. Tsirkin GArray *method = build_alloc_array(); 43099fd437dSMichael S. Tsirkin 43199fd437dSMichael S. Tsirkin build_append_nameseg(method, "%s", name); 43299fd437dSMichael S. Tsirkin build_append_byte(method, arg_count); /* MethodFlags: ArgCount */ 43399fd437dSMichael S. Tsirkin 43499fd437dSMichael S. Tsirkin return method; 43599fd437dSMichael S. Tsirkin } 43699fd437dSMichael S. Tsirkin 43799fd437dSMichael S. Tsirkin static void build_append_and_cleanup_method(GArray *device, GArray *method) 43899fd437dSMichael S. Tsirkin { 43999fd437dSMichael S. Tsirkin uint8_t op = 0x14; /* MethodOp */ 44099fd437dSMichael S. Tsirkin 44199fd437dSMichael S. Tsirkin build_package(method, op, 0); 44299fd437dSMichael S. Tsirkin 44399fd437dSMichael S. Tsirkin build_append_array(device, method); 44499fd437dSMichael S. Tsirkin build_free_array(method); 44599fd437dSMichael S. Tsirkin } 44699fd437dSMichael S. Tsirkin 44799fd437dSMichael S. Tsirkin static void build_append_notify_target_ifequal(GArray *method, 44899fd437dSMichael S. Tsirkin GArray *target_name, 44972c194f7SMichael S. Tsirkin uint32_t value, int size) 45072c194f7SMichael S. Tsirkin { 45172c194f7SMichael S. Tsirkin GArray *notify = build_alloc_array(); 45272c194f7SMichael S. Tsirkin uint8_t op = 0xA0; /* IfOp */ 45372c194f7SMichael S. Tsirkin 45472c194f7SMichael S. Tsirkin build_append_byte(notify, 0x93); /* LEqualOp */ 45572c194f7SMichael S. Tsirkin build_append_byte(notify, 0x68); /* Arg0Op */ 45672c194f7SMichael S. Tsirkin build_append_value(notify, value, size); 45772c194f7SMichael S. Tsirkin build_append_byte(notify, 0x86); /* NotifyOp */ 45872c194f7SMichael S. Tsirkin build_append_array(notify, target_name); 45972c194f7SMichael S. Tsirkin build_append_byte(notify, 0x69); /* Arg1Op */ 46072c194f7SMichael S. Tsirkin 46172c194f7SMichael S. Tsirkin /* Pack it up */ 46272c194f7SMichael S. Tsirkin build_package(notify, op, 1); 46372c194f7SMichael S. Tsirkin 46472c194f7SMichael S. Tsirkin build_append_array(method, notify); 46572c194f7SMichael S. Tsirkin 46672c194f7SMichael S. Tsirkin build_free_array(notify); 46772c194f7SMichael S. Tsirkin } 46872c194f7SMichael S. Tsirkin 46999fd437dSMichael S. Tsirkin /* End here */ 47072c194f7SMichael S. Tsirkin #define ACPI_PORT_SMI_CMD 0x00b2 /* TODO: this is APM_CNT_IOPORT */ 47172c194f7SMichael S. Tsirkin 47272c194f7SMichael S. Tsirkin static inline void *acpi_data_push(GArray *table_data, unsigned size) 47372c194f7SMichael S. Tsirkin { 47472c194f7SMichael S. Tsirkin unsigned off = table_data->len; 47572c194f7SMichael S. Tsirkin g_array_set_size(table_data, off + size); 47672c194f7SMichael S. Tsirkin return table_data->data + off; 47772c194f7SMichael S. Tsirkin } 47872c194f7SMichael S. Tsirkin 47972c194f7SMichael S. Tsirkin static unsigned acpi_data_len(GArray *table) 48072c194f7SMichael S. Tsirkin { 481134d42d6SMichael S. Tsirkin #if GLIB_CHECK_VERSION(2, 22, 0) 482b15654c2SMichael S. Tsirkin assert(g_array_get_element_size(table) == 1); 483b15654c2SMichael S. Tsirkin #endif 484b15654c2SMichael S. Tsirkin return table->len; 48572c194f7SMichael S. Tsirkin } 48672c194f7SMichael S. Tsirkin 48772c194f7SMichael S. Tsirkin static void acpi_align_size(GArray *blob, unsigned align) 48872c194f7SMichael S. Tsirkin { 48972c194f7SMichael S. Tsirkin /* Align size to multiple of given size. This reduces the chance 49072c194f7SMichael S. Tsirkin * we need to change size in the future (breaking cross version migration). 49172c194f7SMichael S. Tsirkin */ 492134d42d6SMichael S. Tsirkin g_array_set_size(blob, ROUND_UP(acpi_data_len(blob), align)); 49372c194f7SMichael S. Tsirkin } 49472c194f7SMichael S. Tsirkin 495b4e5a4bfSMichael S. Tsirkin /* Set a value within table in a safe manner */ 496b4e5a4bfSMichael S. Tsirkin #define ACPI_BUILD_SET_LE(table, size, off, bits, val) \ 497b4e5a4bfSMichael S. Tsirkin do { \ 498b4e5a4bfSMichael S. Tsirkin uint64_t ACPI_BUILD_SET_LE_val = cpu_to_le64(val); \ 499b4e5a4bfSMichael S. Tsirkin memcpy(acpi_data_get_ptr(table, size, off, \ 500b4e5a4bfSMichael S. Tsirkin (bits) / BITS_PER_BYTE), \ 501b4e5a4bfSMichael S. Tsirkin &ACPI_BUILD_SET_LE_val, \ 502b4e5a4bfSMichael S. Tsirkin (bits) / BITS_PER_BYTE); \ 503b4e5a4bfSMichael S. Tsirkin } while (0) 50472c194f7SMichael S. Tsirkin 50572c194f7SMichael S. Tsirkin static inline void *acpi_data_get_ptr(uint8_t *table_data, unsigned table_size, 50672c194f7SMichael S. Tsirkin unsigned off, unsigned size) 50772c194f7SMichael S. Tsirkin { 50872c194f7SMichael S. Tsirkin assert(off + size > off); 50972c194f7SMichael S. Tsirkin assert(off + size <= table_size); 51072c194f7SMichael S. Tsirkin return table_data + off; 51172c194f7SMichael S. Tsirkin } 51272c194f7SMichael S. Tsirkin 51372c194f7SMichael S. Tsirkin static inline void acpi_add_table(GArray *table_offsets, GArray *table_data) 51472c194f7SMichael S. Tsirkin { 51572c194f7SMichael S. Tsirkin uint32_t offset = cpu_to_le32(table_data->len); 51672c194f7SMichael S. Tsirkin g_array_append_val(table_offsets, offset); 51772c194f7SMichael S. Tsirkin } 51872c194f7SMichael S. Tsirkin 51972c194f7SMichael S. Tsirkin /* FACS */ 52072c194f7SMichael S. Tsirkin static void 52172c194f7SMichael S. Tsirkin build_facs(GArray *table_data, GArray *linker, PcGuestInfo *guest_info) 52272c194f7SMichael S. Tsirkin { 52372c194f7SMichael S. Tsirkin AcpiFacsDescriptorRev1 *facs = acpi_data_push(table_data, sizeof *facs); 524821e3227SMichael S. Tsirkin memcpy(&facs->signature, "FACS", 4); 52572c194f7SMichael S. Tsirkin facs->length = cpu_to_le32(sizeof(*facs)); 52672c194f7SMichael S. Tsirkin } 52772c194f7SMichael S. Tsirkin 52872c194f7SMichael S. Tsirkin /* Load chipset information in FADT */ 52972c194f7SMichael S. Tsirkin static void fadt_setup(AcpiFadtDescriptorRev1 *fadt, AcpiPmInfo *pm) 53072c194f7SMichael S. Tsirkin { 53172c194f7SMichael S. Tsirkin fadt->model = 1; 53272c194f7SMichael S. Tsirkin fadt->reserved1 = 0; 53372c194f7SMichael S. Tsirkin fadt->sci_int = cpu_to_le16(pm->sci_int); 53472c194f7SMichael S. Tsirkin fadt->smi_cmd = cpu_to_le32(ACPI_PORT_SMI_CMD); 53572c194f7SMichael S. Tsirkin fadt->acpi_enable = pm->acpi_enable_cmd; 53672c194f7SMichael S. Tsirkin fadt->acpi_disable = pm->acpi_disable_cmd; 53772c194f7SMichael S. Tsirkin /* EVT, CNT, TMR offset matches hw/acpi/core.c */ 53872c194f7SMichael S. Tsirkin fadt->pm1a_evt_blk = cpu_to_le32(pm->io_base); 53972c194f7SMichael S. Tsirkin fadt->pm1a_cnt_blk = cpu_to_le32(pm->io_base + 0x04); 54072c194f7SMichael S. Tsirkin fadt->pm_tmr_blk = cpu_to_le32(pm->io_base + 0x08); 54172c194f7SMichael S. Tsirkin fadt->gpe0_blk = cpu_to_le32(pm->gpe0_blk); 54272c194f7SMichael S. Tsirkin /* EVT, CNT, TMR length matches hw/acpi/core.c */ 54372c194f7SMichael S. Tsirkin fadt->pm1_evt_len = 4; 54472c194f7SMichael S. Tsirkin fadt->pm1_cnt_len = 2; 54572c194f7SMichael S. Tsirkin fadt->pm_tmr_len = 4; 54672c194f7SMichael S. Tsirkin fadt->gpe0_blk_len = pm->gpe0_blk_len; 54772c194f7SMichael S. Tsirkin fadt->plvl2_lat = cpu_to_le16(0xfff); /* C2 state not supported */ 54872c194f7SMichael S. Tsirkin fadt->plvl3_lat = cpu_to_le16(0xfff); /* C3 state not supported */ 54972c194f7SMichael S. Tsirkin fadt->flags = cpu_to_le32((1 << ACPI_FADT_F_WBINVD) | 55072c194f7SMichael S. Tsirkin (1 << ACPI_FADT_F_PROC_C1) | 55172c194f7SMichael S. Tsirkin (1 << ACPI_FADT_F_SLP_BUTTON) | 55272c194f7SMichael S. Tsirkin (1 << ACPI_FADT_F_RTC_S4)); 55372c194f7SMichael S. Tsirkin fadt->flags |= cpu_to_le32(1 << ACPI_FADT_F_USE_PLATFORM_CLOCK); 55472c194f7SMichael S. Tsirkin } 55572c194f7SMichael S. Tsirkin 55672c194f7SMichael S. Tsirkin 55772c194f7SMichael S. Tsirkin /* FADT */ 55872c194f7SMichael S. Tsirkin static void 55972c194f7SMichael S. Tsirkin build_fadt(GArray *table_data, GArray *linker, AcpiPmInfo *pm, 56072c194f7SMichael S. Tsirkin unsigned facs, unsigned dsdt) 56172c194f7SMichael S. Tsirkin { 56272c194f7SMichael S. Tsirkin AcpiFadtDescriptorRev1 *fadt = acpi_data_push(table_data, sizeof(*fadt)); 56372c194f7SMichael S. Tsirkin 56472c194f7SMichael S. Tsirkin fadt->firmware_ctrl = cpu_to_le32(facs); 56572c194f7SMichael S. Tsirkin /* FACS address to be filled by Guest linker */ 56672c194f7SMichael S. Tsirkin bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, 56772c194f7SMichael S. Tsirkin ACPI_BUILD_TABLE_FILE, 56872c194f7SMichael S. Tsirkin table_data, &fadt->firmware_ctrl, 56972c194f7SMichael S. Tsirkin sizeof fadt->firmware_ctrl); 57072c194f7SMichael S. Tsirkin 57172c194f7SMichael S. Tsirkin fadt->dsdt = cpu_to_le32(dsdt); 57272c194f7SMichael S. Tsirkin /* DSDT address to be filled by Guest linker */ 57372c194f7SMichael S. Tsirkin bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, 57472c194f7SMichael S. Tsirkin ACPI_BUILD_TABLE_FILE, 57572c194f7SMichael S. Tsirkin table_data, &fadt->dsdt, 57672c194f7SMichael S. Tsirkin sizeof fadt->dsdt); 57772c194f7SMichael S. Tsirkin 57872c194f7SMichael S. Tsirkin fadt_setup(fadt, pm); 57972c194f7SMichael S. Tsirkin 58072c194f7SMichael S. Tsirkin build_header(linker, table_data, 581821e3227SMichael S. Tsirkin (void *)fadt, "FACP", sizeof(*fadt), 1); 58272c194f7SMichael S. Tsirkin } 58372c194f7SMichael S. Tsirkin 58472c194f7SMichael S. Tsirkin static void 58572c194f7SMichael S. Tsirkin build_madt(GArray *table_data, GArray *linker, AcpiCpuInfo *cpu, 58672c194f7SMichael S. Tsirkin PcGuestInfo *guest_info) 58772c194f7SMichael S. Tsirkin { 58872c194f7SMichael S. Tsirkin int madt_start = table_data->len; 58972c194f7SMichael S. Tsirkin 59072c194f7SMichael S. Tsirkin AcpiMultipleApicTable *madt; 59172c194f7SMichael S. Tsirkin AcpiMadtIoApic *io_apic; 59272c194f7SMichael S. Tsirkin AcpiMadtIntsrcovr *intsrcovr; 59372c194f7SMichael S. Tsirkin AcpiMadtLocalNmi *local_nmi; 59472c194f7SMichael S. Tsirkin int i; 59572c194f7SMichael S. Tsirkin 59672c194f7SMichael S. Tsirkin madt = acpi_data_push(table_data, sizeof *madt); 59772c194f7SMichael S. Tsirkin madt->local_apic_address = cpu_to_le32(APIC_DEFAULT_ADDRESS); 59872c194f7SMichael S. Tsirkin madt->flags = cpu_to_le32(1); 59972c194f7SMichael S. Tsirkin 60072c194f7SMichael S. Tsirkin for (i = 0; i < guest_info->apic_id_limit; i++) { 60172c194f7SMichael S. Tsirkin AcpiMadtProcessorApic *apic = acpi_data_push(table_data, sizeof *apic); 60272c194f7SMichael S. Tsirkin apic->type = ACPI_APIC_PROCESSOR; 60372c194f7SMichael S. Tsirkin apic->length = sizeof(*apic); 60472c194f7SMichael S. Tsirkin apic->processor_id = i; 60572c194f7SMichael S. Tsirkin apic->local_apic_id = i; 60672c194f7SMichael S. Tsirkin if (test_bit(i, cpu->found_cpus)) { 60772c194f7SMichael S. Tsirkin apic->flags = cpu_to_le32(1); 60872c194f7SMichael S. Tsirkin } else { 60972c194f7SMichael S. Tsirkin apic->flags = cpu_to_le32(0); 61072c194f7SMichael S. Tsirkin } 61172c194f7SMichael S. Tsirkin } 61272c194f7SMichael S. Tsirkin io_apic = acpi_data_push(table_data, sizeof *io_apic); 61372c194f7SMichael S. Tsirkin io_apic->type = ACPI_APIC_IO; 61472c194f7SMichael S. Tsirkin io_apic->length = sizeof(*io_apic); 61572c194f7SMichael S. Tsirkin #define ACPI_BUILD_IOAPIC_ID 0x0 61672c194f7SMichael S. Tsirkin io_apic->io_apic_id = ACPI_BUILD_IOAPIC_ID; 61772c194f7SMichael S. Tsirkin io_apic->address = cpu_to_le32(IO_APIC_DEFAULT_ADDRESS); 61872c194f7SMichael S. Tsirkin io_apic->interrupt = cpu_to_le32(0); 61972c194f7SMichael S. Tsirkin 62072c194f7SMichael S. Tsirkin if (guest_info->apic_xrupt_override) { 62172c194f7SMichael S. Tsirkin intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); 62272c194f7SMichael S. Tsirkin intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; 62372c194f7SMichael S. Tsirkin intsrcovr->length = sizeof(*intsrcovr); 62472c194f7SMichael S. Tsirkin intsrcovr->source = 0; 62572c194f7SMichael S. Tsirkin intsrcovr->gsi = cpu_to_le32(2); 62672c194f7SMichael S. Tsirkin intsrcovr->flags = cpu_to_le16(0); /* conforms to bus specifications */ 62772c194f7SMichael S. Tsirkin } 62872c194f7SMichael S. Tsirkin for (i = 1; i < 16; i++) { 62972c194f7SMichael S. Tsirkin #define ACPI_BUILD_PCI_IRQS ((1<<5) | (1<<9) | (1<<10) | (1<<11)) 63072c194f7SMichael S. Tsirkin if (!(ACPI_BUILD_PCI_IRQS & (1 << i))) { 63172c194f7SMichael S. Tsirkin /* No need for a INT source override structure. */ 63272c194f7SMichael S. Tsirkin continue; 63372c194f7SMichael S. Tsirkin } 63472c194f7SMichael S. Tsirkin intsrcovr = acpi_data_push(table_data, sizeof *intsrcovr); 63572c194f7SMichael S. Tsirkin intsrcovr->type = ACPI_APIC_XRUPT_OVERRIDE; 63672c194f7SMichael S. Tsirkin intsrcovr->length = sizeof(*intsrcovr); 63772c194f7SMichael S. Tsirkin intsrcovr->source = i; 63872c194f7SMichael S. Tsirkin intsrcovr->gsi = cpu_to_le32(i); 63972c194f7SMichael S. Tsirkin intsrcovr->flags = cpu_to_le16(0xd); /* active high, level triggered */ 64072c194f7SMichael S. Tsirkin } 64172c194f7SMichael S. Tsirkin 64272c194f7SMichael S. Tsirkin local_nmi = acpi_data_push(table_data, sizeof *local_nmi); 64372c194f7SMichael S. Tsirkin local_nmi->type = ACPI_APIC_LOCAL_NMI; 64472c194f7SMichael S. Tsirkin local_nmi->length = sizeof(*local_nmi); 64572c194f7SMichael S. Tsirkin local_nmi->processor_id = 0xff; /* all processors */ 64672c194f7SMichael S. Tsirkin local_nmi->flags = cpu_to_le16(0); 64772c194f7SMichael S. Tsirkin local_nmi->lint = 1; /* ACPI_LINT1 */ 64872c194f7SMichael S. Tsirkin 64972c194f7SMichael S. Tsirkin build_header(linker, table_data, 650821e3227SMichael S. Tsirkin (void *)(table_data->data + madt_start), "APIC", 65172c194f7SMichael S. Tsirkin table_data->len - madt_start, 1); 65272c194f7SMichael S. Tsirkin } 65372c194f7SMichael S. Tsirkin 65472c194f7SMichael S. Tsirkin /* Encode a hex value */ 65572c194f7SMichael S. Tsirkin static inline char acpi_get_hex(uint32_t val) 65672c194f7SMichael S. Tsirkin { 65772c194f7SMichael S. Tsirkin val &= 0x0f; 65872c194f7SMichael S. Tsirkin return (val <= 9) ? ('0' + val) : ('A' + val - 10); 65972c194f7SMichael S. Tsirkin } 66072c194f7SMichael S. Tsirkin 66172c194f7SMichael S. Tsirkin #include "hw/i386/ssdt-proc.hex" 66272c194f7SMichael S. Tsirkin 66372c194f7SMichael S. Tsirkin /* 0x5B 0x83 ProcessorOp PkgLength NameString ProcID */ 66472c194f7SMichael S. Tsirkin #define ACPI_PROC_OFFSET_CPUHEX (*ssdt_proc_name - *ssdt_proc_start + 2) 66572c194f7SMichael S. Tsirkin #define ACPI_PROC_OFFSET_CPUID1 (*ssdt_proc_name - *ssdt_proc_start + 4) 66672c194f7SMichael S. Tsirkin #define ACPI_PROC_OFFSET_CPUID2 (*ssdt_proc_id - *ssdt_proc_start) 66772c194f7SMichael S. Tsirkin #define ACPI_PROC_SIZEOF (*ssdt_proc_end - *ssdt_proc_start) 66872c194f7SMichael S. Tsirkin #define ACPI_PROC_AML (ssdp_proc_aml + *ssdt_proc_start) 66972c194f7SMichael S. Tsirkin 67072c194f7SMichael S. Tsirkin /* 0x5B 0x82 DeviceOp PkgLength NameString */ 67172c194f7SMichael S. Tsirkin #define ACPI_PCIHP_OFFSET_HEX (*ssdt_pcihp_name - *ssdt_pcihp_start + 1) 67272c194f7SMichael S. Tsirkin #define ACPI_PCIHP_OFFSET_ID (*ssdt_pcihp_id - *ssdt_pcihp_start) 67372c194f7SMichael S. Tsirkin #define ACPI_PCIHP_OFFSET_ADR (*ssdt_pcihp_adr - *ssdt_pcihp_start) 67472c194f7SMichael S. Tsirkin #define ACPI_PCIHP_OFFSET_EJ0 (*ssdt_pcihp_ej0 - *ssdt_pcihp_start) 67572c194f7SMichael S. Tsirkin #define ACPI_PCIHP_SIZEOF (*ssdt_pcihp_end - *ssdt_pcihp_start) 67672c194f7SMichael S. Tsirkin #define ACPI_PCIHP_AML (ssdp_pcihp_aml + *ssdt_pcihp_start) 67772c194f7SMichael S. Tsirkin 6788dcf525aSMichael S. Tsirkin #define ACPI_PCINOHP_OFFSET_HEX (*ssdt_pcinohp_name - *ssdt_pcinohp_start + 1) 6798dcf525aSMichael S. Tsirkin #define ACPI_PCINOHP_OFFSET_ADR (*ssdt_pcinohp_adr - *ssdt_pcinohp_start) 6808dcf525aSMichael S. Tsirkin #define ACPI_PCINOHP_SIZEOF (*ssdt_pcinohp_end - *ssdt_pcinohp_start) 6818dcf525aSMichael S. Tsirkin #define ACPI_PCINOHP_AML (ssdp_pcihp_aml + *ssdt_pcinohp_start) 6828dcf525aSMichael S. Tsirkin 6838dcf525aSMichael S. Tsirkin #define ACPI_PCIVGA_OFFSET_HEX (*ssdt_pcivga_name - *ssdt_pcivga_start + 1) 6848dcf525aSMichael S. Tsirkin #define ACPI_PCIVGA_OFFSET_ADR (*ssdt_pcivga_adr - *ssdt_pcivga_start) 6858dcf525aSMichael S. Tsirkin #define ACPI_PCIVGA_SIZEOF (*ssdt_pcivga_end - *ssdt_pcivga_start) 6868dcf525aSMichael S. Tsirkin #define ACPI_PCIVGA_AML (ssdp_pcihp_aml + *ssdt_pcivga_start) 6878dcf525aSMichael S. Tsirkin 6888dcf525aSMichael S. Tsirkin #define ACPI_PCIQXL_OFFSET_HEX (*ssdt_pciqxl_name - *ssdt_pciqxl_start + 1) 6898dcf525aSMichael S. Tsirkin #define ACPI_PCIQXL_OFFSET_ADR (*ssdt_pciqxl_adr - *ssdt_pciqxl_start) 6908dcf525aSMichael S. Tsirkin #define ACPI_PCIQXL_SIZEOF (*ssdt_pciqxl_end - *ssdt_pciqxl_start) 6918dcf525aSMichael S. Tsirkin #define ACPI_PCIQXL_AML (ssdp_pcihp_aml + *ssdt_pciqxl_start) 6928dcf525aSMichael S. Tsirkin 693bef3492dSIgor Mammedov #include "hw/i386/ssdt-mem.hex" 694bef3492dSIgor Mammedov 695bef3492dSIgor Mammedov /* 0x5B 0x82 DeviceOp PkgLength NameString DimmID */ 696bef3492dSIgor Mammedov #define ACPI_MEM_OFFSET_HEX (*ssdt_mem_name - *ssdt_mem_start + 2) 697bef3492dSIgor Mammedov #define ACPI_MEM_OFFSET_ID (*ssdt_mem_id - *ssdt_mem_start + 7) 698bef3492dSIgor Mammedov #define ACPI_MEM_SIZEOF (*ssdt_mem_end - *ssdt_mem_start) 699bef3492dSIgor Mammedov #define ACPI_MEM_AML (ssdm_mem_aml + *ssdt_mem_start) 700bef3492dSIgor Mammedov 70172c194f7SMichael S. Tsirkin #define ACPI_SSDT_SIGNATURE 0x54445353 /* SSDT */ 70272c194f7SMichael S. Tsirkin #define ACPI_SSDT_HEADER_LENGTH 36 70372c194f7SMichael S. Tsirkin 70472c194f7SMichael S. Tsirkin #include "hw/i386/ssdt-misc.hex" 70572c194f7SMichael S. Tsirkin #include "hw/i386/ssdt-pcihp.hex" 706711b20b4SStefan Berger #include "hw/i386/ssdt-tpm.hex" 70772c194f7SMichael S. Tsirkin 70872c194f7SMichael S. Tsirkin static void 70999fd437dSMichael S. Tsirkin build_append_notify_method(GArray *device, const char *name, 71099fd437dSMichael S. Tsirkin const char *format, int count) 71172c194f7SMichael S. Tsirkin { 71272c194f7SMichael S. Tsirkin int i; 71399fd437dSMichael S. Tsirkin GArray *method = build_alloc_method(name, 2); 71472c194f7SMichael S. Tsirkin 71599fd437dSMichael S. Tsirkin for (i = 0; i < count; i++) { 71672c194f7SMichael S. Tsirkin GArray *target = build_alloc_array(); 71772c194f7SMichael S. Tsirkin build_append_nameseg(target, format, i); 71872c194f7SMichael S. Tsirkin assert(i < 256); /* Fits in 1 byte */ 71999fd437dSMichael S. Tsirkin build_append_notify_target_ifequal(method, target, i, 1); 72072c194f7SMichael S. Tsirkin build_free_array(target); 72172c194f7SMichael S. Tsirkin } 72272c194f7SMichael S. Tsirkin 72399fd437dSMichael S. Tsirkin build_append_and_cleanup_method(device, method); 72472c194f7SMichael S. Tsirkin } 72572c194f7SMichael S. Tsirkin 72699fd437dSMichael S. Tsirkin static void patch_pcihp(int slot, uint8_t *ssdt_ptr) 72772c194f7SMichael S. Tsirkin { 72899fd437dSMichael S. Tsirkin unsigned devfn = PCI_DEVFN(slot, 0); 72999fd437dSMichael S. Tsirkin 73099fd437dSMichael S. Tsirkin ssdt_ptr[ACPI_PCIHP_OFFSET_HEX] = acpi_get_hex(devfn >> 4); 73199fd437dSMichael S. Tsirkin ssdt_ptr[ACPI_PCIHP_OFFSET_HEX + 1] = acpi_get_hex(devfn); 73272c194f7SMichael S. Tsirkin ssdt_ptr[ACPI_PCIHP_OFFSET_ID] = slot; 73372c194f7SMichael S. Tsirkin ssdt_ptr[ACPI_PCIHP_OFFSET_ADR + 2] = slot; 73472c194f7SMichael S. Tsirkin } 73599fd437dSMichael S. Tsirkin 7368dcf525aSMichael S. Tsirkin static void patch_pcinohp(int slot, uint8_t *ssdt_ptr) 7378dcf525aSMichael S. Tsirkin { 7388dcf525aSMichael S. Tsirkin unsigned devfn = PCI_DEVFN(slot, 0); 7398dcf525aSMichael S. Tsirkin 7408dcf525aSMichael S. Tsirkin ssdt_ptr[ACPI_PCINOHP_OFFSET_HEX] = acpi_get_hex(devfn >> 4); 7418dcf525aSMichael S. Tsirkin ssdt_ptr[ACPI_PCINOHP_OFFSET_HEX + 1] = acpi_get_hex(devfn); 7428dcf525aSMichael S. Tsirkin ssdt_ptr[ACPI_PCINOHP_OFFSET_ADR + 2] = slot; 7438dcf525aSMichael S. Tsirkin } 7448dcf525aSMichael S. Tsirkin 7458dcf525aSMichael S. Tsirkin static void patch_pcivga(int slot, uint8_t *ssdt_ptr) 7468dcf525aSMichael S. Tsirkin { 7478dcf525aSMichael S. Tsirkin unsigned devfn = PCI_DEVFN(slot, 0); 7488dcf525aSMichael S. Tsirkin 7498dcf525aSMichael S. Tsirkin ssdt_ptr[ACPI_PCIVGA_OFFSET_HEX] = acpi_get_hex(devfn >> 4); 7508dcf525aSMichael S. Tsirkin ssdt_ptr[ACPI_PCIVGA_OFFSET_HEX + 1] = acpi_get_hex(devfn); 7518dcf525aSMichael S. Tsirkin ssdt_ptr[ACPI_PCIVGA_OFFSET_ADR + 2] = slot; 7528dcf525aSMichael S. Tsirkin } 7538dcf525aSMichael S. Tsirkin 7548dcf525aSMichael S. Tsirkin static void patch_pciqxl(int slot, uint8_t *ssdt_ptr) 7558dcf525aSMichael S. Tsirkin { 7568dcf525aSMichael S. Tsirkin unsigned devfn = PCI_DEVFN(slot, 0); 7578dcf525aSMichael S. Tsirkin 7588dcf525aSMichael S. Tsirkin ssdt_ptr[ACPI_PCIQXL_OFFSET_HEX] = acpi_get_hex(devfn >> 4); 7598dcf525aSMichael S. Tsirkin ssdt_ptr[ACPI_PCIQXL_OFFSET_HEX + 1] = acpi_get_hex(devfn); 7608dcf525aSMichael S. Tsirkin ssdt_ptr[ACPI_PCIQXL_OFFSET_ADR + 2] = slot; 7618dcf525aSMichael S. Tsirkin } 7628dcf525aSMichael S. Tsirkin 76399fd437dSMichael S. Tsirkin /* Assign BSEL property to all buses. In the future, this can be changed 76499fd437dSMichael S. Tsirkin * to only assign to buses that support hotplug. 76599fd437dSMichael S. Tsirkin */ 76699fd437dSMichael S. Tsirkin static void *acpi_set_bsel(PCIBus *bus, void *opaque) 76799fd437dSMichael S. Tsirkin { 76899fd437dSMichael S. Tsirkin unsigned *bsel_alloc = opaque; 76999fd437dSMichael S. Tsirkin unsigned *bus_bsel; 77099fd437dSMichael S. Tsirkin 77199fd437dSMichael S. Tsirkin if (bus->qbus.allow_hotplug) { 77299fd437dSMichael S. Tsirkin bus_bsel = g_malloc(sizeof *bus_bsel); 77399fd437dSMichael S. Tsirkin 77499fd437dSMichael S. Tsirkin *bus_bsel = (*bsel_alloc)++; 77599fd437dSMichael S. Tsirkin object_property_add_uint32_ptr(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, 77699fd437dSMichael S. Tsirkin bus_bsel, NULL); 77799fd437dSMichael S. Tsirkin } 77899fd437dSMichael S. Tsirkin 77999fd437dSMichael S. Tsirkin return bsel_alloc; 78099fd437dSMichael S. Tsirkin } 78199fd437dSMichael S. Tsirkin 78299fd437dSMichael S. Tsirkin static void acpi_set_pci_info(void) 78399fd437dSMichael S. Tsirkin { 78499fd437dSMichael S. Tsirkin PCIBus *bus = find_i440fx(); /* TODO: Q35 support */ 78599fd437dSMichael S. Tsirkin unsigned bsel_alloc = 0; 78699fd437dSMichael S. Tsirkin 78799fd437dSMichael S. Tsirkin if (bus) { 78899fd437dSMichael S. Tsirkin /* Scan all PCI buses. Set property to enable acpi based hotplug. */ 78999fd437dSMichael S. Tsirkin pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &bsel_alloc); 79099fd437dSMichael S. Tsirkin } 79199fd437dSMichael S. Tsirkin } 79299fd437dSMichael S. Tsirkin 79399fd437dSMichael S. Tsirkin static void build_pci_bus_state_init(AcpiBuildPciBusHotplugState *state, 794133a2da4SIgor Mammedov AcpiBuildPciBusHotplugState *parent, 795133a2da4SIgor Mammedov bool pcihp_bridge_en) 79699fd437dSMichael S. Tsirkin { 79799fd437dSMichael S. Tsirkin state->parent = parent; 79899fd437dSMichael S. Tsirkin state->device_table = build_alloc_array(); 79999fd437dSMichael S. Tsirkin state->notify_table = build_alloc_array(); 800133a2da4SIgor Mammedov state->pcihp_bridge_en = pcihp_bridge_en; 80199fd437dSMichael S. Tsirkin } 80299fd437dSMichael S. Tsirkin 80399fd437dSMichael S. Tsirkin static void build_pci_bus_state_cleanup(AcpiBuildPciBusHotplugState *state) 80499fd437dSMichael S. Tsirkin { 80599fd437dSMichael S. Tsirkin build_free_array(state->device_table); 80699fd437dSMichael S. Tsirkin build_free_array(state->notify_table); 80799fd437dSMichael S. Tsirkin } 80899fd437dSMichael S. Tsirkin 80999fd437dSMichael S. Tsirkin static void *build_pci_bus_begin(PCIBus *bus, void *parent_state) 81099fd437dSMichael S. Tsirkin { 81199fd437dSMichael S. Tsirkin AcpiBuildPciBusHotplugState *parent = parent_state; 81299fd437dSMichael S. Tsirkin AcpiBuildPciBusHotplugState *child = g_malloc(sizeof *child); 81399fd437dSMichael S. Tsirkin 814133a2da4SIgor Mammedov build_pci_bus_state_init(child, parent, parent->pcihp_bridge_en); 81599fd437dSMichael S. Tsirkin 81699fd437dSMichael S. Tsirkin return child; 81799fd437dSMichael S. Tsirkin } 81899fd437dSMichael S. Tsirkin 81999fd437dSMichael S. Tsirkin static void build_pci_bus_end(PCIBus *bus, void *bus_state) 82099fd437dSMichael S. Tsirkin { 82199fd437dSMichael S. Tsirkin AcpiBuildPciBusHotplugState *child = bus_state; 82299fd437dSMichael S. Tsirkin AcpiBuildPciBusHotplugState *parent = child->parent; 82399fd437dSMichael S. Tsirkin GArray *bus_table = build_alloc_array(); 82499fd437dSMichael S. Tsirkin DECLARE_BITMAP(slot_hotplug_enable, PCI_SLOT_MAX); 8258dcf525aSMichael S. Tsirkin DECLARE_BITMAP(slot_device_present, PCI_SLOT_MAX); 8268dcf525aSMichael S. Tsirkin DECLARE_BITMAP(slot_device_system, PCI_SLOT_MAX); 8278dcf525aSMichael S. Tsirkin DECLARE_BITMAP(slot_device_vga, PCI_SLOT_MAX); 8288dcf525aSMichael S. Tsirkin DECLARE_BITMAP(slot_device_qxl, PCI_SLOT_MAX); 82999fd437dSMichael S. Tsirkin uint8_t op; 83099fd437dSMichael S. Tsirkin int i; 83199fd437dSMichael S. Tsirkin QObject *bsel; 83299fd437dSMichael S. Tsirkin GArray *method; 83399fd437dSMichael S. Tsirkin bool bus_hotplug_support = false; 83499fd437dSMichael S. Tsirkin 835133a2da4SIgor Mammedov /* 836093a35e5SMichael S. Tsirkin * Skip bridge subtree creation if bridge hotplug is disabled 837093a35e5SMichael S. Tsirkin * to make acpi tables compatible with legacy machine types. 838133a2da4SIgor Mammedov */ 839133a2da4SIgor Mammedov if (!child->pcihp_bridge_en && bus->parent_dev) { 840133a2da4SIgor Mammedov return; 841133a2da4SIgor Mammedov } 842133a2da4SIgor Mammedov 84399fd437dSMichael S. Tsirkin if (bus->parent_dev) { 84499fd437dSMichael S. Tsirkin op = 0x82; /* DeviceOp */ 84599fd437dSMichael S. Tsirkin build_append_nameseg(bus_table, "S%.02X_", 84699fd437dSMichael S. Tsirkin bus->parent_dev->devfn); 84799fd437dSMichael S. Tsirkin build_append_byte(bus_table, 0x08); /* NameOp */ 84899fd437dSMichael S. Tsirkin build_append_nameseg(bus_table, "_SUN"); 84999fd437dSMichael S. Tsirkin build_append_value(bus_table, PCI_SLOT(bus->parent_dev->devfn), 1); 85099fd437dSMichael S. Tsirkin build_append_byte(bus_table, 0x08); /* NameOp */ 85199fd437dSMichael S. Tsirkin build_append_nameseg(bus_table, "_ADR"); 85299fd437dSMichael S. Tsirkin build_append_value(bus_table, (PCI_SLOT(bus->parent_dev->devfn) << 16) | 85399fd437dSMichael S. Tsirkin PCI_FUNC(bus->parent_dev->devfn), 4); 85499fd437dSMichael S. Tsirkin } else { 85599fd437dSMichael S. Tsirkin op = 0x10; /* ScopeOp */; 85699fd437dSMichael S. Tsirkin build_append_nameseg(bus_table, "PCI0"); 85799fd437dSMichael S. Tsirkin } 85899fd437dSMichael S. Tsirkin 85999fd437dSMichael S. Tsirkin bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); 86099fd437dSMichael S. Tsirkin if (bsel) { 86199fd437dSMichael S. Tsirkin build_append_byte(bus_table, 0x08); /* NameOp */ 86299fd437dSMichael S. Tsirkin build_append_nameseg(bus_table, "BSEL"); 86399fd437dSMichael S. Tsirkin build_append_int(bus_table, qint_get_int(qobject_to_qint(bsel))); 86499fd437dSMichael S. Tsirkin memset(slot_hotplug_enable, 0xff, sizeof slot_hotplug_enable); 8658dcf525aSMichael S. Tsirkin } else { 8668dcf525aSMichael S. Tsirkin /* No bsel - no slots are hot-pluggable */ 8678dcf525aSMichael S. Tsirkin memset(slot_hotplug_enable, 0x00, sizeof slot_hotplug_enable); 8688dcf525aSMichael S. Tsirkin } 86999fd437dSMichael S. Tsirkin 8708dcf525aSMichael S. Tsirkin memset(slot_device_present, 0x00, sizeof slot_device_present); 8718dcf525aSMichael S. Tsirkin memset(slot_device_system, 0x00, sizeof slot_device_present); 8728dcf525aSMichael S. Tsirkin memset(slot_device_vga, 0x00, sizeof slot_device_vga); 8738dcf525aSMichael S. Tsirkin memset(slot_device_qxl, 0x00, sizeof slot_device_qxl); 8748dcf525aSMichael S. Tsirkin 8758dcf525aSMichael S. Tsirkin for (i = 0; i < ARRAY_SIZE(bus->devices); i += PCI_FUNC_MAX) { 8762897ae02SIgor Mammedov DeviceClass *dc; 87799fd437dSMichael S. Tsirkin PCIDeviceClass *pc; 87899fd437dSMichael S. Tsirkin PCIDevice *pdev = bus->devices[i]; 8798dcf525aSMichael S. Tsirkin int slot = PCI_SLOT(i); 880093a35e5SMichael S. Tsirkin bool bridge_in_acpi; 88199fd437dSMichael S. Tsirkin 88299fd437dSMichael S. Tsirkin if (!pdev) { 88399fd437dSMichael S. Tsirkin continue; 88499fd437dSMichael S. Tsirkin } 88599fd437dSMichael S. Tsirkin 8868dcf525aSMichael S. Tsirkin set_bit(slot, slot_device_present); 88799fd437dSMichael S. Tsirkin pc = PCI_DEVICE_GET_CLASS(pdev); 8882897ae02SIgor Mammedov dc = DEVICE_GET_CLASS(pdev); 88999fd437dSMichael S. Tsirkin 890093a35e5SMichael S. Tsirkin /* When hotplug for bridges is enabled, bridges are 891093a35e5SMichael S. Tsirkin * described in ACPI separately (see build_pci_bus_end). 892093a35e5SMichael S. Tsirkin * In this case they aren't themselves hot-pluggable. 893093a35e5SMichael S. Tsirkin */ 894093a35e5SMichael S. Tsirkin bridge_in_acpi = pc->is_bridge && child->pcihp_bridge_en; 895093a35e5SMichael S. Tsirkin 896093a35e5SMichael S. Tsirkin if (pc->class_id == PCI_CLASS_BRIDGE_ISA || bridge_in_acpi) { 8978dcf525aSMichael S. Tsirkin set_bit(slot, slot_device_system); 8988dcf525aSMichael S. Tsirkin } 89999fd437dSMichael S. Tsirkin 9008dcf525aSMichael S. Tsirkin if (pc->class_id == PCI_CLASS_DISPLAY_VGA) { 9018dcf525aSMichael S. Tsirkin set_bit(slot, slot_device_vga); 9028dcf525aSMichael S. Tsirkin 9038dcf525aSMichael S. Tsirkin if (object_dynamic_cast(OBJECT(pdev), "qxl-vga")) { 9048dcf525aSMichael S. Tsirkin set_bit(slot, slot_device_qxl); 9058dcf525aSMichael S. Tsirkin } 9068dcf525aSMichael S. Tsirkin } 9078dcf525aSMichael S. Tsirkin 908093a35e5SMichael S. Tsirkin if (!dc->hotpluggable || bridge_in_acpi) { 90999fd437dSMichael S. Tsirkin clear_bit(slot, slot_hotplug_enable); 91099fd437dSMichael S. Tsirkin } 91199fd437dSMichael S. Tsirkin } 91299fd437dSMichael S. Tsirkin 9138dcf525aSMichael S. Tsirkin /* Append Device object for each slot */ 91499fd437dSMichael S. Tsirkin for (i = 0; i < PCI_SLOT_MAX; i++) { 91599fd437dSMichael S. Tsirkin bool can_eject = test_bit(i, slot_hotplug_enable); 9168dcf525aSMichael S. Tsirkin bool present = test_bit(i, slot_device_present); 9178dcf525aSMichael S. Tsirkin bool vga = test_bit(i, slot_device_vga); 9188dcf525aSMichael S. Tsirkin bool qxl = test_bit(i, slot_device_qxl); 9198dcf525aSMichael S. Tsirkin bool system = test_bit(i, slot_device_system); 92099fd437dSMichael S. Tsirkin if (can_eject) { 92199fd437dSMichael S. Tsirkin void *pcihp = acpi_data_push(bus_table, 92299fd437dSMichael S. Tsirkin ACPI_PCIHP_SIZEOF); 92399fd437dSMichael S. Tsirkin memcpy(pcihp, ACPI_PCIHP_AML, ACPI_PCIHP_SIZEOF); 92499fd437dSMichael S. Tsirkin patch_pcihp(i, pcihp); 92599fd437dSMichael S. Tsirkin bus_hotplug_support = true; 9268dcf525aSMichael S. Tsirkin } else if (qxl) { 9278dcf525aSMichael S. Tsirkin void *pcihp = acpi_data_push(bus_table, 9288dcf525aSMichael S. Tsirkin ACPI_PCIQXL_SIZEOF); 9298dcf525aSMichael S. Tsirkin memcpy(pcihp, ACPI_PCIQXL_AML, ACPI_PCIQXL_SIZEOF); 9308dcf525aSMichael S. Tsirkin patch_pciqxl(i, pcihp); 9318dcf525aSMichael S. Tsirkin } else if (vga) { 9328dcf525aSMichael S. Tsirkin void *pcihp = acpi_data_push(bus_table, 9338dcf525aSMichael S. Tsirkin ACPI_PCIVGA_SIZEOF); 9348dcf525aSMichael S. Tsirkin memcpy(pcihp, ACPI_PCIVGA_AML, ACPI_PCIVGA_SIZEOF); 9358dcf525aSMichael S. Tsirkin patch_pcivga(i, pcihp); 9368dcf525aSMichael S. Tsirkin } else if (system) { 937b89834f4SMarcel Apfelbaum /* Nothing to do: system devices are in DSDT or in SSDT above. */ 9388dcf525aSMichael S. Tsirkin } else if (present) { 9398dcf525aSMichael S. Tsirkin void *pcihp = acpi_data_push(bus_table, 9408dcf525aSMichael S. Tsirkin ACPI_PCINOHP_SIZEOF); 9418dcf525aSMichael S. Tsirkin memcpy(pcihp, ACPI_PCINOHP_AML, ACPI_PCINOHP_SIZEOF); 9428dcf525aSMichael S. Tsirkin patch_pcinohp(i, pcihp); 94399fd437dSMichael S. Tsirkin } 94499fd437dSMichael S. Tsirkin } 94599fd437dSMichael S. Tsirkin 9468dcf525aSMichael S. Tsirkin if (bsel) { 94799fd437dSMichael S. Tsirkin method = build_alloc_method("DVNT", 2); 94899fd437dSMichael S. Tsirkin 94999fd437dSMichael S. Tsirkin for (i = 0; i < PCI_SLOT_MAX; i++) { 95099fd437dSMichael S. Tsirkin GArray *notify; 95199fd437dSMichael S. Tsirkin uint8_t op; 95299fd437dSMichael S. Tsirkin 95399fd437dSMichael S. Tsirkin if (!test_bit(i, slot_hotplug_enable)) { 95499fd437dSMichael S. Tsirkin continue; 95599fd437dSMichael S. Tsirkin } 95699fd437dSMichael S. Tsirkin 95799fd437dSMichael S. Tsirkin notify = build_alloc_array(); 95899fd437dSMichael S. Tsirkin op = 0xA0; /* IfOp */ 95999fd437dSMichael S. Tsirkin 96099fd437dSMichael S. Tsirkin build_append_byte(notify, 0x7B); /* AndOp */ 96199fd437dSMichael S. Tsirkin build_append_byte(notify, 0x68); /* Arg0Op */ 962d9631b90SPeter Maydell build_append_int(notify, 0x1U << i); 96399fd437dSMichael S. Tsirkin build_append_byte(notify, 0x00); /* NullName */ 96499fd437dSMichael S. Tsirkin build_append_byte(notify, 0x86); /* NotifyOp */ 96599fd437dSMichael S. Tsirkin build_append_nameseg(notify, "S%.02X_", PCI_DEVFN(i, 0)); 96699fd437dSMichael S. Tsirkin build_append_byte(notify, 0x69); /* Arg1Op */ 96799fd437dSMichael S. Tsirkin 96899fd437dSMichael S. Tsirkin /* Pack it up */ 96999fd437dSMichael S. Tsirkin build_package(notify, op, 0); 97099fd437dSMichael S. Tsirkin 97199fd437dSMichael S. Tsirkin build_append_array(method, notify); 97299fd437dSMichael S. Tsirkin 97399fd437dSMichael S. Tsirkin build_free_array(notify); 97499fd437dSMichael S. Tsirkin } 97599fd437dSMichael S. Tsirkin 97699fd437dSMichael S. Tsirkin build_append_and_cleanup_method(bus_table, method); 97799fd437dSMichael S. Tsirkin } 97899fd437dSMichael S. Tsirkin 97999fd437dSMichael S. Tsirkin /* Append PCNT method to notify about events on local and child buses. 98099fd437dSMichael S. Tsirkin * Add unconditionally for root since DSDT expects it. 98199fd437dSMichael S. Tsirkin */ 98299fd437dSMichael S. Tsirkin if (bus_hotplug_support || child->notify_table->len || !bus->parent_dev) { 98399fd437dSMichael S. Tsirkin method = build_alloc_method("PCNT", 0); 98499fd437dSMichael S. Tsirkin 98599fd437dSMichael S. Tsirkin /* If bus supports hotplug select it and notify about local events */ 98699fd437dSMichael S. Tsirkin if (bsel) { 98799fd437dSMichael S. Tsirkin build_append_byte(method, 0x70); /* StoreOp */ 98899fd437dSMichael S. Tsirkin build_append_int(method, qint_get_int(qobject_to_qint(bsel))); 98999fd437dSMichael S. Tsirkin build_append_nameseg(method, "BNUM"); 99099fd437dSMichael S. Tsirkin build_append_nameseg(method, "DVNT"); 99199fd437dSMichael S. Tsirkin build_append_nameseg(method, "PCIU"); 99299fd437dSMichael S. Tsirkin build_append_int(method, 1); /* Device Check */ 99399fd437dSMichael S. Tsirkin build_append_nameseg(method, "DVNT"); 99499fd437dSMichael S. Tsirkin build_append_nameseg(method, "PCID"); 99599fd437dSMichael S. Tsirkin build_append_int(method, 3); /* Eject Request */ 99699fd437dSMichael S. Tsirkin } 99799fd437dSMichael S. Tsirkin 99899fd437dSMichael S. Tsirkin /* Notify about child bus events in any case */ 99999fd437dSMichael S. Tsirkin build_append_array(method, child->notify_table); 100099fd437dSMichael S. Tsirkin 100199fd437dSMichael S. Tsirkin build_append_and_cleanup_method(bus_table, method); 100299fd437dSMichael S. Tsirkin 100399fd437dSMichael S. Tsirkin /* Append description of child buses */ 100499fd437dSMichael S. Tsirkin build_append_array(bus_table, child->device_table); 100599fd437dSMichael S. Tsirkin 100699fd437dSMichael S. Tsirkin /* Pack it up */ 100799fd437dSMichael S. Tsirkin if (bus->parent_dev) { 100899fd437dSMichael S. Tsirkin build_extop_package(bus_table, op); 100999fd437dSMichael S. Tsirkin } else { 101099fd437dSMichael S. Tsirkin build_package(bus_table, op, 0); 101199fd437dSMichael S. Tsirkin } 101299fd437dSMichael S. Tsirkin 101399fd437dSMichael S. Tsirkin /* Append our bus description to parent table */ 101499fd437dSMichael S. Tsirkin build_append_array(parent->device_table, bus_table); 101599fd437dSMichael S. Tsirkin 101699fd437dSMichael S. Tsirkin /* Also tell parent how to notify us, invoking PCNT method. 101799fd437dSMichael S. Tsirkin * At the moment this is not needed for root as we have a single root. 101899fd437dSMichael S. Tsirkin */ 101999fd437dSMichael S. Tsirkin if (bus->parent_dev) { 102099fd437dSMichael S. Tsirkin build_append_byte(parent->notify_table, '^'); /* ParentPrefixChar */ 102199fd437dSMichael S. Tsirkin build_append_byte(parent->notify_table, 0x2E); /* DualNamePrefix */ 102299fd437dSMichael S. Tsirkin build_append_nameseg(parent->notify_table, "S%.02X_", 102399fd437dSMichael S. Tsirkin bus->parent_dev->devfn); 102499fd437dSMichael S. Tsirkin build_append_nameseg(parent->notify_table, "PCNT"); 102599fd437dSMichael S. Tsirkin } 102699fd437dSMichael S. Tsirkin } 102799fd437dSMichael S. Tsirkin 1028097a97a6SKirill Batuzov qobject_decref(bsel); 102999fd437dSMichael S. Tsirkin build_free_array(bus_table); 103099fd437dSMichael S. Tsirkin build_pci_bus_state_cleanup(child); 103199fd437dSMichael S. Tsirkin g_free(child); 103272c194f7SMichael S. Tsirkin } 103372c194f7SMichael S. Tsirkin 103472c194f7SMichael S. Tsirkin static void patch_pci_windows(PcPciInfo *pci, uint8_t *start, unsigned size) 103572c194f7SMichael S. Tsirkin { 1036b4e5a4bfSMichael S. Tsirkin ACPI_BUILD_SET_LE(start, size, acpi_pci32_start[0], 32, pci->w32.begin); 103772c194f7SMichael S. Tsirkin 1038b4e5a4bfSMichael S. Tsirkin ACPI_BUILD_SET_LE(start, size, acpi_pci32_end[0], 32, pci->w32.end - 1); 103972c194f7SMichael S. Tsirkin 104072c194f7SMichael S. Tsirkin if (pci->w64.end || pci->w64.begin) { 1041b4e5a4bfSMichael S. Tsirkin ACPI_BUILD_SET_LE(start, size, acpi_pci64_valid[0], 8, 1); 1042b4e5a4bfSMichael S. Tsirkin ACPI_BUILD_SET_LE(start, size, acpi_pci64_start[0], 64, pci->w64.begin); 1043b4e5a4bfSMichael S. Tsirkin ACPI_BUILD_SET_LE(start, size, acpi_pci64_end[0], 64, pci->w64.end - 1); 1044b4e5a4bfSMichael S. Tsirkin ACPI_BUILD_SET_LE(start, size, acpi_pci64_length[0], 64, pci->w64.end - pci->w64.begin); 104572c194f7SMichael S. Tsirkin } else { 1046b4e5a4bfSMichael S. Tsirkin ACPI_BUILD_SET_LE(start, size, acpi_pci64_valid[0], 8, 0); 104772c194f7SMichael S. Tsirkin } 104872c194f7SMichael S. Tsirkin } 104972c194f7SMichael S. Tsirkin 105072c194f7SMichael S. Tsirkin static void 105172c194f7SMichael S. Tsirkin build_ssdt(GArray *table_data, GArray *linker, 105272c194f7SMichael S. Tsirkin AcpiCpuInfo *cpu, AcpiPmInfo *pm, AcpiMiscInfo *misc, 105372c194f7SMichael S. Tsirkin PcPciInfo *pci, PcGuestInfo *guest_info) 105472c194f7SMichael S. Tsirkin { 1055bef3492dSIgor Mammedov MachineState *machine = MACHINE(qdev_get_machine()); 1056bef3492dSIgor Mammedov uint32_t nr_mem = machine->ram_slots; 10572fd71f1bSLaszlo Ersek unsigned acpi_cpus = guest_info->apic_id_limit; 105872c194f7SMichael S. Tsirkin int ssdt_start = table_data->len; 105972c194f7SMichael S. Tsirkin uint8_t *ssdt_ptr; 106072c194f7SMichael S. Tsirkin int i; 106172c194f7SMichael S. Tsirkin 10622fd71f1bSLaszlo Ersek /* The current AML generator can cover the APIC ID range [0..255], 10632fd71f1bSLaszlo Ersek * inclusive, for VCPU hotplug. */ 10642fd71f1bSLaszlo Ersek QEMU_BUILD_BUG_ON(ACPI_CPU_HOTPLUG_ID_LIMIT > 256); 10652fd71f1bSLaszlo Ersek g_assert(acpi_cpus <= ACPI_CPU_HOTPLUG_ID_LIMIT); 10662fd71f1bSLaszlo Ersek 106772c194f7SMichael S. Tsirkin /* Copy header and patch values in the S3_ / S4_ / S5_ packages */ 106872c194f7SMichael S. Tsirkin ssdt_ptr = acpi_data_push(table_data, sizeof(ssdp_misc_aml)); 106972c194f7SMichael S. Tsirkin memcpy(ssdt_ptr, ssdp_misc_aml, sizeof(ssdp_misc_aml)); 107072c194f7SMichael S. Tsirkin if (pm->s3_disabled) { 107172c194f7SMichael S. Tsirkin ssdt_ptr[acpi_s3_name[0]] = 'X'; 107272c194f7SMichael S. Tsirkin } 107372c194f7SMichael S. Tsirkin if (pm->s4_disabled) { 107472c194f7SMichael S. Tsirkin ssdt_ptr[acpi_s4_name[0]] = 'X'; 107572c194f7SMichael S. Tsirkin } else { 107672c194f7SMichael S. Tsirkin ssdt_ptr[acpi_s4_pkg[0] + 1] = ssdt_ptr[acpi_s4_pkg[0] + 3] = 107772c194f7SMichael S. Tsirkin pm->s4_val; 107872c194f7SMichael S. Tsirkin } 107972c194f7SMichael S. Tsirkin 108072c194f7SMichael S. Tsirkin patch_pci_windows(pci, ssdt_ptr, sizeof(ssdp_misc_aml)); 108172c194f7SMichael S. Tsirkin 1082eee822e3SMichael S. Tsirkin ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml), 1083eee822e3SMichael S. Tsirkin ssdt_isa_pest[0], 16, misc->pvpanic_port); 108472c194f7SMichael S. Tsirkin 1085bef3492dSIgor Mammedov ACPI_BUILD_SET_LE(ssdt_ptr, sizeof(ssdp_misc_aml), 1086bef3492dSIgor Mammedov ssdt_mctrl_nr_slots[0], 32, nr_mem); 1087bef3492dSIgor Mammedov 108872c194f7SMichael S. Tsirkin { 108972c194f7SMichael S. Tsirkin GArray *sb_scope = build_alloc_array(); 109072c194f7SMichael S. Tsirkin uint8_t op = 0x10; /* ScopeOp */ 109172c194f7SMichael S. Tsirkin 109272c194f7SMichael S. Tsirkin build_append_nameseg(sb_scope, "_SB_"); 109372c194f7SMichael S. Tsirkin 109472c194f7SMichael S. Tsirkin /* build Processor object for each processor */ 109572c194f7SMichael S. Tsirkin for (i = 0; i < acpi_cpus; i++) { 109672c194f7SMichael S. Tsirkin uint8_t *proc = acpi_data_push(sb_scope, ACPI_PROC_SIZEOF); 109772c194f7SMichael S. Tsirkin memcpy(proc, ACPI_PROC_AML, ACPI_PROC_SIZEOF); 109872c194f7SMichael S. Tsirkin proc[ACPI_PROC_OFFSET_CPUHEX] = acpi_get_hex(i >> 4); 109972c194f7SMichael S. Tsirkin proc[ACPI_PROC_OFFSET_CPUHEX+1] = acpi_get_hex(i); 110072c194f7SMichael S. Tsirkin proc[ACPI_PROC_OFFSET_CPUID1] = i; 110172c194f7SMichael S. Tsirkin proc[ACPI_PROC_OFFSET_CPUID2] = i; 110272c194f7SMichael S. Tsirkin } 110372c194f7SMichael S. Tsirkin 110472c194f7SMichael S. Tsirkin /* build this code: 110572c194f7SMichael S. Tsirkin * Method(NTFY, 2) {If (LEqual(Arg0, 0x00)) {Notify(CP00, Arg1)} ...} 110672c194f7SMichael S. Tsirkin */ 110772c194f7SMichael S. Tsirkin /* Arg0 = Processor ID = APIC ID */ 110899fd437dSMichael S. Tsirkin build_append_notify_method(sb_scope, "NTFY", "CP%0.02X", acpi_cpus); 110972c194f7SMichael S. Tsirkin 111072c194f7SMichael S. Tsirkin /* build "Name(CPON, Package() { One, One, ..., Zero, Zero, ... })" */ 111172c194f7SMichael S. Tsirkin build_append_byte(sb_scope, 0x08); /* NameOp */ 111272c194f7SMichael S. Tsirkin build_append_nameseg(sb_scope, "CPON"); 111372c194f7SMichael S. Tsirkin 111472c194f7SMichael S. Tsirkin { 111572c194f7SMichael S. Tsirkin GArray *package = build_alloc_array(); 1116b4f4d548SMichael S. Tsirkin uint8_t op; 111772c194f7SMichael S. Tsirkin 1118b4f4d548SMichael S. Tsirkin /* 1119b4f4d548SMichael S. Tsirkin * Note: The ability to create variable-sized packages was first introduced in ACPI 2.0. ACPI 1.0 only 1120b4f4d548SMichael S. Tsirkin * allowed fixed-size packages with up to 255 elements. 1121b4f4d548SMichael S. Tsirkin * Windows guests up to win2k8 fail when VarPackageOp is used. 1122b4f4d548SMichael S. Tsirkin */ 1123b4f4d548SMichael S. Tsirkin if (acpi_cpus <= 255) { 1124b4f4d548SMichael S. Tsirkin op = 0x12; /* PackageOp */ 1125b4f4d548SMichael S. Tsirkin build_append_byte(package, acpi_cpus); /* NumElements */ 1126b4f4d548SMichael S. Tsirkin } else { 1127b4f4d548SMichael S. Tsirkin op = 0x13; /* VarPackageOp */ 11289bcc80cdSLaszlo Ersek build_append_int(package, acpi_cpus); /* VarNumElements */ 1129b4f4d548SMichael S. Tsirkin } 1130b4f4d548SMichael S. Tsirkin 113172c194f7SMichael S. Tsirkin for (i = 0; i < acpi_cpus; i++) { 113272c194f7SMichael S. Tsirkin uint8_t b = test_bit(i, cpu->found_cpus) ? 0x01 : 0x00; 113372c194f7SMichael S. Tsirkin build_append_byte(package, b); 113472c194f7SMichael S. Tsirkin } 113572c194f7SMichael S. Tsirkin 113672c194f7SMichael S. Tsirkin build_package(package, op, 2); 113772c194f7SMichael S. Tsirkin build_append_array(sb_scope, package); 113872c194f7SMichael S. Tsirkin build_free_array(package); 113972c194f7SMichael S. Tsirkin } 114072c194f7SMichael S. Tsirkin 1141bef3492dSIgor Mammedov if (nr_mem) { 1142bef3492dSIgor Mammedov assert(nr_mem <= ACPI_MAX_RAM_SLOTS); 1143bef3492dSIgor Mammedov /* build memory devices */ 1144bef3492dSIgor Mammedov for (i = 0; i < nr_mem; i++) { 1145bef3492dSIgor Mammedov char id[3]; 1146bef3492dSIgor Mammedov uint8_t *mem = acpi_data_push(sb_scope, ACPI_MEM_SIZEOF); 1147bef3492dSIgor Mammedov 1148bef3492dSIgor Mammedov snprintf(id, sizeof(id), "%02X", i); 1149bef3492dSIgor Mammedov memcpy(mem, ACPI_MEM_AML, ACPI_MEM_SIZEOF); 1150bef3492dSIgor Mammedov memcpy(mem + ACPI_MEM_OFFSET_HEX, id, 2); 1151bef3492dSIgor Mammedov memcpy(mem + ACPI_MEM_OFFSET_ID, id, 2); 1152bef3492dSIgor Mammedov } 1153bef3492dSIgor Mammedov 1154bef3492dSIgor Mammedov /* build Method(MEMORY_SLOT_NOTIFY_METHOD, 2) { 1155bef3492dSIgor Mammedov * If (LEqual(Arg0, 0x00)) {Notify(MP00, Arg1)} ... 1156bef3492dSIgor Mammedov */ 1157bef3492dSIgor Mammedov build_append_notify_method(sb_scope, 1158bef3492dSIgor Mammedov stringify(MEMORY_SLOT_NOTIFY_METHOD), 1159bef3492dSIgor Mammedov "MP%0.02X", nr_mem); 1160bef3492dSIgor Mammedov } 1161bef3492dSIgor Mammedov 116272c194f7SMichael S. Tsirkin { 116399fd437dSMichael S. Tsirkin AcpiBuildPciBusHotplugState hotplug_state; 11648dcf525aSMichael S. Tsirkin Object *pci_host; 11658dcf525aSMichael S. Tsirkin PCIBus *bus = NULL; 11668dcf525aSMichael S. Tsirkin bool ambiguous; 11678dcf525aSMichael S. Tsirkin 11688dcf525aSMichael S. Tsirkin pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous); 11698dcf525aSMichael S. Tsirkin if (!ambiguous && pci_host) { 11708dcf525aSMichael S. Tsirkin bus = PCI_HOST_BRIDGE(pci_host)->bus; 11718dcf525aSMichael S. Tsirkin } 117272c194f7SMichael S. Tsirkin 1173133a2da4SIgor Mammedov build_pci_bus_state_init(&hotplug_state, NULL, pm->pcihp_bridge_en); 117472c194f7SMichael S. Tsirkin 117599fd437dSMichael S. Tsirkin if (bus) { 117699fd437dSMichael S. Tsirkin /* Scan all PCI buses. Generate tables to support hotplug. */ 117799fd437dSMichael S. Tsirkin pci_for_each_bus_depth_first(bus, build_pci_bus_begin, 117899fd437dSMichael S. Tsirkin build_pci_bus_end, &hotplug_state); 117972c194f7SMichael S. Tsirkin } 118072c194f7SMichael S. Tsirkin 118199fd437dSMichael S. Tsirkin build_append_array(sb_scope, hotplug_state.device_table); 118299fd437dSMichael S. Tsirkin build_pci_bus_state_cleanup(&hotplug_state); 118372c194f7SMichael S. Tsirkin } 118472c194f7SMichael S. Tsirkin 118572c194f7SMichael S. Tsirkin build_package(sb_scope, op, 3); 118672c194f7SMichael S. Tsirkin build_append_array(table_data, sb_scope); 118772c194f7SMichael S. Tsirkin build_free_array(sb_scope); 118872c194f7SMichael S. Tsirkin } 118972c194f7SMichael S. Tsirkin 119072c194f7SMichael S. Tsirkin build_header(linker, table_data, 119172c194f7SMichael S. Tsirkin (void *)(table_data->data + ssdt_start), 1192821e3227SMichael S. Tsirkin "SSDT", table_data->len - ssdt_start, 1); 119372c194f7SMichael S. Tsirkin } 119472c194f7SMichael S. Tsirkin 119572c194f7SMichael S. Tsirkin static void 119672c194f7SMichael S. Tsirkin build_hpet(GArray *table_data, GArray *linker) 119772c194f7SMichael S. Tsirkin { 119872c194f7SMichael S. Tsirkin Acpi20Hpet *hpet; 119972c194f7SMichael S. Tsirkin 120072c194f7SMichael S. Tsirkin hpet = acpi_data_push(table_data, sizeof(*hpet)); 120172c194f7SMichael S. Tsirkin /* Note timer_block_id value must be kept in sync with value advertised by 120272c194f7SMichael S. Tsirkin * emulated hpet 120372c194f7SMichael S. Tsirkin */ 120472c194f7SMichael S. Tsirkin hpet->timer_block_id = cpu_to_le32(0x8086a201); 120572c194f7SMichael S. Tsirkin hpet->addr.address = cpu_to_le64(HPET_BASE); 120672c194f7SMichael S. Tsirkin build_header(linker, table_data, 1207821e3227SMichael S. Tsirkin (void *)hpet, "HPET", sizeof(*hpet), 1); 120872c194f7SMichael S. Tsirkin } 120972c194f7SMichael S. Tsirkin 1210711b20b4SStefan Berger static void 1211711b20b4SStefan Berger build_tpm_tcpa(GArray *table_data, GArray *linker) 1212711b20b4SStefan Berger { 1213711b20b4SStefan Berger Acpi20Tcpa *tcpa = acpi_data_push(table_data, sizeof *tcpa); 1214711b20b4SStefan Berger /* the log area will come right after the TCPA table */ 1215711b20b4SStefan Berger uint64_t log_area_start_address = acpi_data_len(table_data); 1216711b20b4SStefan Berger 1217711b20b4SStefan Berger tcpa->platform_class = cpu_to_le16(TPM_TCPA_ACPI_CLASS_CLIENT); 1218711b20b4SStefan Berger tcpa->log_area_minimum_length = cpu_to_le32(TPM_LOG_AREA_MINIMUM_SIZE); 1219711b20b4SStefan Berger tcpa->log_area_start_address = cpu_to_le64(log_area_start_address); 1220711b20b4SStefan Berger 1221711b20b4SStefan Berger /* log area start address to be filled by Guest linker */ 1222711b20b4SStefan Berger bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, 1223711b20b4SStefan Berger ACPI_BUILD_TABLE_FILE, 1224711b20b4SStefan Berger table_data, &tcpa->log_area_start_address, 1225711b20b4SStefan Berger sizeof(tcpa->log_area_start_address)); 1226711b20b4SStefan Berger 1227711b20b4SStefan Berger build_header(linker, table_data, 1228711b20b4SStefan Berger (void *)tcpa, "TCPA", sizeof(*tcpa), 2); 1229711b20b4SStefan Berger 1230711b20b4SStefan Berger /* now only get the log area and with that modify table_data */ 1231711b20b4SStefan Berger acpi_data_push(table_data, TPM_LOG_AREA_MINIMUM_SIZE); 1232711b20b4SStefan Berger } 1233711b20b4SStefan Berger 1234711b20b4SStefan Berger static void 1235711b20b4SStefan Berger build_tpm_ssdt(GArray *table_data, GArray *linker) 1236711b20b4SStefan Berger { 1237711b20b4SStefan Berger void *tpm_ptr; 1238711b20b4SStefan Berger 1239711b20b4SStefan Berger tpm_ptr = acpi_data_push(table_data, sizeof(ssdt_tpm_aml)); 1240711b20b4SStefan Berger memcpy(tpm_ptr, ssdt_tpm_aml, sizeof(ssdt_tpm_aml)); 1241711b20b4SStefan Berger } 1242711b20b4SStefan Berger 124304ed3ea8SIgor Mammedov typedef enum { 124404ed3ea8SIgor Mammedov MEM_AFFINITY_NOFLAGS = 0, 124504ed3ea8SIgor Mammedov MEM_AFFINITY_ENABLED = (1 << 0), 124604ed3ea8SIgor Mammedov MEM_AFFINITY_HOTPLUGGABLE = (1 << 1), 124704ed3ea8SIgor Mammedov MEM_AFFINITY_NON_VOLATILE = (1 << 2), 124804ed3ea8SIgor Mammedov } MemoryAffinityFlags; 124904ed3ea8SIgor Mammedov 125072c194f7SMichael S. Tsirkin static void 125104ed3ea8SIgor Mammedov acpi_build_srat_memory(AcpiSratMemoryAffinity *numamem, uint64_t base, 125204ed3ea8SIgor Mammedov uint64_t len, int node, MemoryAffinityFlags flags) 125372c194f7SMichael S. Tsirkin { 125472c194f7SMichael S. Tsirkin numamem->type = ACPI_SRAT_MEMORY; 125572c194f7SMichael S. Tsirkin numamem->length = sizeof(*numamem); 125672c194f7SMichael S. Tsirkin memset(numamem->proximity, 0, 4); 125772c194f7SMichael S. Tsirkin numamem->proximity[0] = node; 125804ed3ea8SIgor Mammedov numamem->flags = cpu_to_le32(flags); 125972c194f7SMichael S. Tsirkin numamem->base_addr = cpu_to_le64(base); 126072c194f7SMichael S. Tsirkin numamem->range_length = cpu_to_le64(len); 126172c194f7SMichael S. Tsirkin } 126272c194f7SMichael S. Tsirkin 126372c194f7SMichael S. Tsirkin static void 126472c194f7SMichael S. Tsirkin build_srat(GArray *table_data, GArray *linker, 126572c194f7SMichael S. Tsirkin AcpiCpuInfo *cpu, PcGuestInfo *guest_info) 126672c194f7SMichael S. Tsirkin { 126772c194f7SMichael S. Tsirkin AcpiSystemResourceAffinityTable *srat; 126872c194f7SMichael S. Tsirkin AcpiSratProcessorAffinity *core; 126972c194f7SMichael S. Tsirkin AcpiSratMemoryAffinity *numamem; 127072c194f7SMichael S. Tsirkin 127172c194f7SMichael S. Tsirkin int i; 127272c194f7SMichael S. Tsirkin uint64_t curnode; 127372c194f7SMichael S. Tsirkin int srat_start, numa_start, slots; 127472c194f7SMichael S. Tsirkin uint64_t mem_len, mem_base, next_base; 1275cec65193SIgor Mammedov PCMachineState *pcms = PC_MACHINE(qdev_get_machine()); 1276cec65193SIgor Mammedov ram_addr_t hotplugabble_address_space_size = 1277cec65193SIgor Mammedov object_property_get_int(OBJECT(pcms), PC_MACHINE_MEMHP_REGION_SIZE, 1278cec65193SIgor Mammedov NULL); 127972c194f7SMichael S. Tsirkin 128072c194f7SMichael S. Tsirkin srat_start = table_data->len; 128172c194f7SMichael S. Tsirkin 128272c194f7SMichael S. Tsirkin srat = acpi_data_push(table_data, sizeof *srat); 128372c194f7SMichael S. Tsirkin srat->reserved1 = cpu_to_le32(1); 128472c194f7SMichael S. Tsirkin core = (void *)(srat + 1); 128572c194f7SMichael S. Tsirkin 128672c194f7SMichael S. Tsirkin for (i = 0; i < guest_info->apic_id_limit; ++i) { 128772c194f7SMichael S. Tsirkin core = acpi_data_push(table_data, sizeof *core); 128872c194f7SMichael S. Tsirkin core->type = ACPI_SRAT_PROCESSOR; 128972c194f7SMichael S. Tsirkin core->length = sizeof(*core); 129072c194f7SMichael S. Tsirkin core->local_apic_id = i; 129172c194f7SMichael S. Tsirkin curnode = guest_info->node_cpu[i]; 129272c194f7SMichael S. Tsirkin core->proximity_lo = curnode; 129372c194f7SMichael S. Tsirkin memset(core->proximity_hi, 0, 3); 129472c194f7SMichael S. Tsirkin core->local_sapic_eid = 0; 129572c194f7SMichael S. Tsirkin if (test_bit(i, cpu->found_cpus)) { 129672c194f7SMichael S. Tsirkin core->flags = cpu_to_le32(1); 129772c194f7SMichael S. Tsirkin } else { 129872c194f7SMichael S. Tsirkin core->flags = cpu_to_le32(0); 129972c194f7SMichael S. Tsirkin } 130072c194f7SMichael S. Tsirkin } 130172c194f7SMichael S. Tsirkin 130272c194f7SMichael S. Tsirkin 130372c194f7SMichael S. Tsirkin /* the memory map is a bit tricky, it contains at least one hole 130472c194f7SMichael S. Tsirkin * from 640k-1M and possibly another one from 3.5G-4G. 130572c194f7SMichael S. Tsirkin */ 130672c194f7SMichael S. Tsirkin next_base = 0; 130772c194f7SMichael S. Tsirkin numa_start = table_data->len; 130872c194f7SMichael S. Tsirkin 130972c194f7SMichael S. Tsirkin numamem = acpi_data_push(table_data, sizeof *numamem); 131004ed3ea8SIgor Mammedov acpi_build_srat_memory(numamem, 0, 640*1024, 0, MEM_AFFINITY_ENABLED); 131172c194f7SMichael S. Tsirkin next_base = 1024 * 1024; 131272c194f7SMichael S. Tsirkin for (i = 1; i < guest_info->numa_nodes + 1; ++i) { 131372c194f7SMichael S. Tsirkin mem_base = next_base; 131472c194f7SMichael S. Tsirkin mem_len = guest_info->node_mem[i - 1]; 131572c194f7SMichael S. Tsirkin if (i == 1) { 131672c194f7SMichael S. Tsirkin mem_len -= 1024 * 1024; 131772c194f7SMichael S. Tsirkin } 131872c194f7SMichael S. Tsirkin next_base = mem_base + mem_len; 131972c194f7SMichael S. Tsirkin 132072c194f7SMichael S. Tsirkin /* Cut out the ACPI_PCI hole */ 13214c8a949bSEduardo Habkost if (mem_base <= guest_info->ram_size_below_4g && 13224c8a949bSEduardo Habkost next_base > guest_info->ram_size_below_4g) { 13234c8a949bSEduardo Habkost mem_len -= next_base - guest_info->ram_size_below_4g; 132472c194f7SMichael S. Tsirkin if (mem_len > 0) { 132572c194f7SMichael S. Tsirkin numamem = acpi_data_push(table_data, sizeof *numamem); 132604ed3ea8SIgor Mammedov acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, 132704ed3ea8SIgor Mammedov MEM_AFFINITY_ENABLED); 132872c194f7SMichael S. Tsirkin } 132972c194f7SMichael S. Tsirkin mem_base = 1ULL << 32; 13304c8a949bSEduardo Habkost mem_len = next_base - guest_info->ram_size_below_4g; 13314c8a949bSEduardo Habkost next_base += (1ULL << 32) - guest_info->ram_size_below_4g; 133272c194f7SMichael S. Tsirkin } 133372c194f7SMichael S. Tsirkin numamem = acpi_data_push(table_data, sizeof *numamem); 133404ed3ea8SIgor Mammedov acpi_build_srat_memory(numamem, mem_base, mem_len, i - 1, 133504ed3ea8SIgor Mammedov MEM_AFFINITY_ENABLED); 133672c194f7SMichael S. Tsirkin } 133772c194f7SMichael S. Tsirkin slots = (table_data->len - numa_start) / sizeof *numamem; 133872c194f7SMichael S. Tsirkin for (; slots < guest_info->numa_nodes + 2; slots++) { 133972c194f7SMichael S. Tsirkin numamem = acpi_data_push(table_data, sizeof *numamem); 134004ed3ea8SIgor Mammedov acpi_build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); 134172c194f7SMichael S. Tsirkin } 134272c194f7SMichael S. Tsirkin 1343cec65193SIgor Mammedov /* 1344cec65193SIgor Mammedov * Entry is required for Windows to enable memory hotplug in OS. 1345cec65193SIgor Mammedov * Memory devices may override proximity set by this entry, 1346cec65193SIgor Mammedov * providing _PXM method if necessary. 1347cec65193SIgor Mammedov */ 1348cec65193SIgor Mammedov if (hotplugabble_address_space_size) { 1349cec65193SIgor Mammedov numamem = acpi_data_push(table_data, sizeof *numamem); 1350cec65193SIgor Mammedov acpi_build_srat_memory(numamem, pcms->hotplug_memory_base, 1351cec65193SIgor Mammedov hotplugabble_address_space_size, 0, 1352cec65193SIgor Mammedov MEM_AFFINITY_HOTPLUGGABLE | 1353cec65193SIgor Mammedov MEM_AFFINITY_ENABLED); 1354cec65193SIgor Mammedov } 1355cec65193SIgor Mammedov 135672c194f7SMichael S. Tsirkin build_header(linker, table_data, 135772c194f7SMichael S. Tsirkin (void *)(table_data->data + srat_start), 1358821e3227SMichael S. Tsirkin "SRAT", 135972c194f7SMichael S. Tsirkin table_data->len - srat_start, 1); 136072c194f7SMichael S. Tsirkin } 136172c194f7SMichael S. Tsirkin 136272c194f7SMichael S. Tsirkin static void 136372c194f7SMichael S. Tsirkin build_mcfg_q35(GArray *table_data, GArray *linker, AcpiMcfgInfo *info) 136472c194f7SMichael S. Tsirkin { 136572c194f7SMichael S. Tsirkin AcpiTableMcfg *mcfg; 1366821e3227SMichael S. Tsirkin const char *sig; 136772c194f7SMichael S. Tsirkin int len = sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]); 136872c194f7SMichael S. Tsirkin 136972c194f7SMichael S. Tsirkin mcfg = acpi_data_push(table_data, len); 137072c194f7SMichael S. Tsirkin mcfg->allocation[0].address = cpu_to_le64(info->mcfg_base); 137172c194f7SMichael S. Tsirkin /* Only a single allocation so no need to play with segments */ 137272c194f7SMichael S. Tsirkin mcfg->allocation[0].pci_segment = cpu_to_le16(0); 137372c194f7SMichael S. Tsirkin mcfg->allocation[0].start_bus_number = 0; 137472c194f7SMichael S. Tsirkin mcfg->allocation[0].end_bus_number = PCIE_MMCFG_BUS(info->mcfg_size - 1); 137572c194f7SMichael S. Tsirkin 137672c194f7SMichael S. Tsirkin /* MCFG is used for ECAM which can be enabled or disabled by guest. 137772c194f7SMichael S. Tsirkin * To avoid table size changes (which create migration issues), 137872c194f7SMichael S. Tsirkin * always create the table even if there are no allocations, 137972c194f7SMichael S. Tsirkin * but set the signature to a reserved value in this case. 138072c194f7SMichael S. Tsirkin * ACPI spec requires OSPMs to ignore such tables. 138172c194f7SMichael S. Tsirkin */ 138272c194f7SMichael S. Tsirkin if (info->mcfg_base == PCIE_BASE_ADDR_UNMAPPED) { 1383821e3227SMichael S. Tsirkin /* Reserved signature: ignored by OSPM */ 1384821e3227SMichael S. Tsirkin sig = "QEMU"; 138572c194f7SMichael S. Tsirkin } else { 1386821e3227SMichael S. Tsirkin sig = "MCFG"; 138772c194f7SMichael S. Tsirkin } 138872c194f7SMichael S. Tsirkin build_header(linker, table_data, (void *)mcfg, sig, len, 1); 138972c194f7SMichael S. Tsirkin } 139072c194f7SMichael S. Tsirkin 139172c194f7SMichael S. Tsirkin static void 1392*d4eb9119SLe Tan build_dmar_q35(GArray *table_data, GArray *linker) 1393*d4eb9119SLe Tan { 1394*d4eb9119SLe Tan int dmar_start = table_data->len; 1395*d4eb9119SLe Tan 1396*d4eb9119SLe Tan AcpiTableDmar *dmar; 1397*d4eb9119SLe Tan AcpiDmarHardwareUnit *drhd; 1398*d4eb9119SLe Tan 1399*d4eb9119SLe Tan dmar = acpi_data_push(table_data, sizeof(*dmar)); 1400*d4eb9119SLe Tan dmar->host_address_width = VTD_HOST_ADDRESS_WIDTH - 1; 1401*d4eb9119SLe Tan dmar->flags = 0; /* No intr_remap for now */ 1402*d4eb9119SLe Tan 1403*d4eb9119SLe Tan /* DMAR Remapping Hardware Unit Definition structure */ 1404*d4eb9119SLe Tan drhd = acpi_data_push(table_data, sizeof(*drhd)); 1405*d4eb9119SLe Tan drhd->type = cpu_to_le16(ACPI_DMAR_TYPE_HARDWARE_UNIT); 1406*d4eb9119SLe Tan drhd->length = cpu_to_le16(sizeof(*drhd)); /* No device scope now */ 1407*d4eb9119SLe Tan drhd->flags = ACPI_DMAR_INCLUDE_PCI_ALL; 1408*d4eb9119SLe Tan drhd->pci_segment = cpu_to_le16(0); 1409*d4eb9119SLe Tan drhd->address = cpu_to_le64(Q35_HOST_BRIDGE_IOMMU_ADDR); 1410*d4eb9119SLe Tan 1411*d4eb9119SLe Tan build_header(linker, table_data, (void *)(table_data->data + dmar_start), 1412*d4eb9119SLe Tan "DMAR", table_data->len - dmar_start, 1); 1413*d4eb9119SLe Tan } 1414*d4eb9119SLe Tan 1415*d4eb9119SLe Tan static void 141672c194f7SMichael S. Tsirkin build_dsdt(GArray *table_data, GArray *linker, AcpiMiscInfo *misc) 141772c194f7SMichael S. Tsirkin { 141853db092aSMichael S. Tsirkin AcpiTableHeader *dsdt; 141953db092aSMichael S. Tsirkin 142072c194f7SMichael S. Tsirkin assert(misc->dsdt_code && misc->dsdt_size); 142153db092aSMichael S. Tsirkin 142272c194f7SMichael S. Tsirkin dsdt = acpi_data_push(table_data, misc->dsdt_size); 142372c194f7SMichael S. Tsirkin memcpy(dsdt, misc->dsdt_code, misc->dsdt_size); 142453db092aSMichael S. Tsirkin 142553db092aSMichael S. Tsirkin memset(dsdt, 0, sizeof *dsdt); 1426821e3227SMichael S. Tsirkin build_header(linker, table_data, dsdt, "DSDT", 142753db092aSMichael S. Tsirkin misc->dsdt_size, 1); 142872c194f7SMichael S. Tsirkin } 142972c194f7SMichael S. Tsirkin 143072c194f7SMichael S. Tsirkin /* Build final rsdt table */ 143172c194f7SMichael S. Tsirkin static void 143272c194f7SMichael S. Tsirkin build_rsdt(GArray *table_data, GArray *linker, GArray *table_offsets) 143372c194f7SMichael S. Tsirkin { 143472c194f7SMichael S. Tsirkin AcpiRsdtDescriptorRev1 *rsdt; 143572c194f7SMichael S. Tsirkin size_t rsdt_len; 143672c194f7SMichael S. Tsirkin int i; 143772c194f7SMichael S. Tsirkin 143872c194f7SMichael S. Tsirkin rsdt_len = sizeof(*rsdt) + sizeof(uint32_t) * table_offsets->len; 143972c194f7SMichael S. Tsirkin rsdt = acpi_data_push(table_data, rsdt_len); 144072c194f7SMichael S. Tsirkin memcpy(rsdt->table_offset_entry, table_offsets->data, 144172c194f7SMichael S. Tsirkin sizeof(uint32_t) * table_offsets->len); 144272c194f7SMichael S. Tsirkin for (i = 0; i < table_offsets->len; ++i) { 144372c194f7SMichael S. Tsirkin /* rsdt->table_offset_entry to be filled by Guest linker */ 144472c194f7SMichael S. Tsirkin bios_linker_loader_add_pointer(linker, 144572c194f7SMichael S. Tsirkin ACPI_BUILD_TABLE_FILE, 144672c194f7SMichael S. Tsirkin ACPI_BUILD_TABLE_FILE, 144772c194f7SMichael S. Tsirkin table_data, &rsdt->table_offset_entry[i], 144872c194f7SMichael S. Tsirkin sizeof(uint32_t)); 144972c194f7SMichael S. Tsirkin } 145072c194f7SMichael S. Tsirkin build_header(linker, table_data, 1451821e3227SMichael S. Tsirkin (void *)rsdt, "RSDT", rsdt_len, 1); 145272c194f7SMichael S. Tsirkin } 145372c194f7SMichael S. Tsirkin 145472c194f7SMichael S. Tsirkin static GArray * 145572c194f7SMichael S. Tsirkin build_rsdp(GArray *rsdp_table, GArray *linker, unsigned rsdt) 145672c194f7SMichael S. Tsirkin { 145772c194f7SMichael S. Tsirkin AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp); 145872c194f7SMichael S. Tsirkin 1459d67aadccSMichael S. Tsirkin bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, 16, 146072c194f7SMichael S. Tsirkin true /* fseg memory */); 146172c194f7SMichael S. Tsirkin 1462821e3227SMichael S. Tsirkin memcpy(&rsdp->signature, "RSD PTR ", 8); 146372c194f7SMichael S. Tsirkin memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, 6); 146472c194f7SMichael S. Tsirkin rsdp->rsdt_physical_address = cpu_to_le32(rsdt); 146572c194f7SMichael S. Tsirkin /* Address to be filled by Guest linker */ 146672c194f7SMichael S. Tsirkin bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE, 146772c194f7SMichael S. Tsirkin ACPI_BUILD_TABLE_FILE, 146872c194f7SMichael S. Tsirkin rsdp_table, &rsdp->rsdt_physical_address, 146972c194f7SMichael S. Tsirkin sizeof rsdp->rsdt_physical_address); 147072c194f7SMichael S. Tsirkin rsdp->checksum = 0; 147172c194f7SMichael S. Tsirkin /* Checksum to be filled by Guest linker */ 147272c194f7SMichael S. Tsirkin bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, 147372c194f7SMichael S. Tsirkin rsdp, rsdp, sizeof *rsdp, &rsdp->checksum); 147472c194f7SMichael S. Tsirkin 147572c194f7SMichael S. Tsirkin return rsdp_table; 147672c194f7SMichael S. Tsirkin } 147772c194f7SMichael S. Tsirkin 147872c194f7SMichael S. Tsirkin typedef 147972c194f7SMichael S. Tsirkin struct AcpiBuildTables { 148072c194f7SMichael S. Tsirkin GArray *table_data; 148172c194f7SMichael S. Tsirkin GArray *rsdp; 148272c194f7SMichael S. Tsirkin GArray *linker; 148372c194f7SMichael S. Tsirkin } AcpiBuildTables; 148472c194f7SMichael S. Tsirkin 148572c194f7SMichael S. Tsirkin static inline void acpi_build_tables_init(AcpiBuildTables *tables) 148672c194f7SMichael S. Tsirkin { 148772c194f7SMichael S. Tsirkin tables->rsdp = g_array_new(false, true /* clear */, 1); 148872c194f7SMichael S. Tsirkin tables->table_data = g_array_new(false, true /* clear */, 1); 148972c194f7SMichael S. Tsirkin tables->linker = bios_linker_loader_init(); 149072c194f7SMichael S. Tsirkin } 149172c194f7SMichael S. Tsirkin 149272c194f7SMichael S. Tsirkin static inline void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre) 149372c194f7SMichael S. Tsirkin { 149472c194f7SMichael S. Tsirkin void *linker_data = bios_linker_loader_cleanup(tables->linker); 149572c194f7SMichael S. Tsirkin if (mfre) { 149672c194f7SMichael S. Tsirkin g_free(linker_data); 149772c194f7SMichael S. Tsirkin } 149872c194f7SMichael S. Tsirkin g_array_free(tables->rsdp, mfre); 149972c194f7SMichael S. Tsirkin g_array_free(tables->table_data, mfre); 150072c194f7SMichael S. Tsirkin } 150172c194f7SMichael S. Tsirkin 150272c194f7SMichael S. Tsirkin typedef 150372c194f7SMichael S. Tsirkin struct AcpiBuildState { 150472c194f7SMichael S. Tsirkin /* Copy of table in RAM (for patching). */ 150572c194f7SMichael S. Tsirkin uint8_t *table_ram; 150672c194f7SMichael S. Tsirkin uint32_t table_size; 150772c194f7SMichael S. Tsirkin /* Is table patched? */ 150872c194f7SMichael S. Tsirkin uint8_t patched; 150972c194f7SMichael S. Tsirkin PcGuestInfo *guest_info; 151072c194f7SMichael S. Tsirkin } AcpiBuildState; 151172c194f7SMichael S. Tsirkin 151272c194f7SMichael S. Tsirkin static bool acpi_get_mcfg(AcpiMcfgInfo *mcfg) 151372c194f7SMichael S. Tsirkin { 151472c194f7SMichael S. Tsirkin Object *pci_host; 151572c194f7SMichael S. Tsirkin QObject *o; 151672c194f7SMichael S. Tsirkin bool ambiguous; 151772c194f7SMichael S. Tsirkin 151872c194f7SMichael S. Tsirkin pci_host = object_resolve_path_type("", TYPE_PCI_HOST_BRIDGE, &ambiguous); 151972c194f7SMichael S. Tsirkin g_assert(!ambiguous); 152072c194f7SMichael S. Tsirkin g_assert(pci_host); 152172c194f7SMichael S. Tsirkin 152272c194f7SMichael S. Tsirkin o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_BASE, NULL); 152372c194f7SMichael S. Tsirkin if (!o) { 152472c194f7SMichael S. Tsirkin return false; 152572c194f7SMichael S. Tsirkin } 152672c194f7SMichael S. Tsirkin mcfg->mcfg_base = qint_get_int(qobject_to_qint(o)); 1527097a97a6SKirill Batuzov qobject_decref(o); 152872c194f7SMichael S. Tsirkin 152972c194f7SMichael S. Tsirkin o = object_property_get_qobject(pci_host, PCIE_HOST_MCFG_SIZE, NULL); 153072c194f7SMichael S. Tsirkin assert(o); 153172c194f7SMichael S. Tsirkin mcfg->mcfg_size = qint_get_int(qobject_to_qint(o)); 1532097a97a6SKirill Batuzov qobject_decref(o); 153372c194f7SMichael S. Tsirkin return true; 153472c194f7SMichael S. Tsirkin } 153572c194f7SMichael S. Tsirkin 1536*d4eb9119SLe Tan static bool acpi_has_iommu(void) 1537*d4eb9119SLe Tan { 1538*d4eb9119SLe Tan bool ambiguous; 1539*d4eb9119SLe Tan Object *intel_iommu; 1540*d4eb9119SLe Tan 1541*d4eb9119SLe Tan intel_iommu = object_resolve_path_type("", TYPE_INTEL_IOMMU_DEVICE, 1542*d4eb9119SLe Tan &ambiguous); 1543*d4eb9119SLe Tan return intel_iommu && !ambiguous; 1544*d4eb9119SLe Tan } 1545*d4eb9119SLe Tan 154672c194f7SMichael S. Tsirkin static 154772c194f7SMichael S. Tsirkin void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) 154872c194f7SMichael S. Tsirkin { 154972c194f7SMichael S. Tsirkin GArray *table_offsets; 155007fb6176SPaolo Bonzini unsigned facs, ssdt, dsdt, rsdt; 155172c194f7SMichael S. Tsirkin AcpiCpuInfo cpu; 155272c194f7SMichael S. Tsirkin AcpiPmInfo pm; 155372c194f7SMichael S. Tsirkin AcpiMiscInfo misc; 155472c194f7SMichael S. Tsirkin AcpiMcfgInfo mcfg; 155572c194f7SMichael S. Tsirkin PcPciInfo pci; 155672c194f7SMichael S. Tsirkin uint8_t *u; 155707fb6176SPaolo Bonzini size_t aml_len = 0; 155872c194f7SMichael S. Tsirkin 155972c194f7SMichael S. Tsirkin acpi_get_cpu_info(&cpu); 156072c194f7SMichael S. Tsirkin acpi_get_pm_info(&pm); 156172c194f7SMichael S. Tsirkin acpi_get_dsdt(&misc); 156272c194f7SMichael S. Tsirkin acpi_get_misc_info(&misc); 156372c194f7SMichael S. Tsirkin acpi_get_pci_info(&pci); 156472c194f7SMichael S. Tsirkin 156572c194f7SMichael S. Tsirkin table_offsets = g_array_new(false, true /* clear */, 156672c194f7SMichael S. Tsirkin sizeof(uint32_t)); 156772c194f7SMichael S. Tsirkin ACPI_BUILD_DPRINTF(3, "init ACPI tables\n"); 156872c194f7SMichael S. Tsirkin 156972c194f7SMichael S. Tsirkin bios_linker_loader_alloc(tables->linker, ACPI_BUILD_TABLE_FILE, 157072c194f7SMichael S. Tsirkin 64 /* Ensure FACS is aligned */, 157172c194f7SMichael S. Tsirkin false /* high memory */); 157272c194f7SMichael S. Tsirkin 157372c194f7SMichael S. Tsirkin /* 157472c194f7SMichael S. Tsirkin * FACS is pointed to by FADT. 157572c194f7SMichael S. Tsirkin * We place it first since it's the only table that has alignment 157672c194f7SMichael S. Tsirkin * requirements. 157772c194f7SMichael S. Tsirkin */ 157872c194f7SMichael S. Tsirkin facs = tables->table_data->len; 157972c194f7SMichael S. Tsirkin build_facs(tables->table_data, tables->linker, guest_info); 158072c194f7SMichael S. Tsirkin 158172c194f7SMichael S. Tsirkin /* DSDT is pointed to by FADT */ 158272c194f7SMichael S. Tsirkin dsdt = tables->table_data->len; 158372c194f7SMichael S. Tsirkin build_dsdt(tables->table_data, tables->linker, &misc); 158472c194f7SMichael S. Tsirkin 158507fb6176SPaolo Bonzini /* Count the size of the DSDT and SSDT, we will need it for legacy 158607fb6176SPaolo Bonzini * sizing of ACPI tables. 158707fb6176SPaolo Bonzini */ 158807fb6176SPaolo Bonzini aml_len += tables->table_data->len - dsdt; 158907fb6176SPaolo Bonzini 159072c194f7SMichael S. Tsirkin /* ACPI tables pointed to by RSDT */ 159172c194f7SMichael S. Tsirkin acpi_add_table(table_offsets, tables->table_data); 159272c194f7SMichael S. Tsirkin build_fadt(tables->table_data, tables->linker, &pm, facs, dsdt); 159372c194f7SMichael S. Tsirkin 159407fb6176SPaolo Bonzini ssdt = tables->table_data->len; 15959ac1c4c0SMichael S. Tsirkin acpi_add_table(table_offsets, tables->table_data); 159672c194f7SMichael S. Tsirkin build_ssdt(tables->table_data, tables->linker, &cpu, &pm, &misc, &pci, 159772c194f7SMichael S. Tsirkin guest_info); 159807fb6176SPaolo Bonzini aml_len += tables->table_data->len - ssdt; 159972c194f7SMichael S. Tsirkin 160072c194f7SMichael S. Tsirkin acpi_add_table(table_offsets, tables->table_data); 16019ac1c4c0SMichael S. Tsirkin build_madt(tables->table_data, tables->linker, &cpu, guest_info); 16029ac1c4c0SMichael S. Tsirkin 160372c194f7SMichael S. Tsirkin if (misc.has_hpet) { 16049ac1c4c0SMichael S. Tsirkin acpi_add_table(table_offsets, tables->table_data); 160572c194f7SMichael S. Tsirkin build_hpet(tables->table_data, tables->linker); 160672c194f7SMichael S. Tsirkin } 1607711b20b4SStefan Berger if (misc.has_tpm) { 1608711b20b4SStefan Berger acpi_add_table(table_offsets, tables->table_data); 1609711b20b4SStefan Berger build_tpm_tcpa(tables->table_data, tables->linker); 1610711b20b4SStefan Berger 1611711b20b4SStefan Berger acpi_add_table(table_offsets, tables->table_data); 1612711b20b4SStefan Berger build_tpm_ssdt(tables->table_data, tables->linker); 1613711b20b4SStefan Berger } 161472c194f7SMichael S. Tsirkin if (guest_info->numa_nodes) { 161572c194f7SMichael S. Tsirkin acpi_add_table(table_offsets, tables->table_data); 161672c194f7SMichael S. Tsirkin build_srat(tables->table_data, tables->linker, &cpu, guest_info); 161772c194f7SMichael S. Tsirkin } 161872c194f7SMichael S. Tsirkin if (acpi_get_mcfg(&mcfg)) { 161972c194f7SMichael S. Tsirkin acpi_add_table(table_offsets, tables->table_data); 162072c194f7SMichael S. Tsirkin build_mcfg_q35(tables->table_data, tables->linker, &mcfg); 162172c194f7SMichael S. Tsirkin } 1622*d4eb9119SLe Tan if (acpi_has_iommu()) { 1623*d4eb9119SLe Tan acpi_add_table(table_offsets, tables->table_data); 1624*d4eb9119SLe Tan build_dmar_q35(tables->table_data, tables->linker); 1625*d4eb9119SLe Tan } 162672c194f7SMichael S. Tsirkin 162772c194f7SMichael S. Tsirkin /* Add tables supplied by user (if any) */ 162872c194f7SMichael S. Tsirkin for (u = acpi_table_first(); u; u = acpi_table_next(u)) { 162972c194f7SMichael S. Tsirkin unsigned len = acpi_table_len(u); 163072c194f7SMichael S. Tsirkin 163172c194f7SMichael S. Tsirkin acpi_add_table(table_offsets, tables->table_data); 163272c194f7SMichael S. Tsirkin g_array_append_vals(tables->table_data, u, len); 163372c194f7SMichael S. Tsirkin } 163472c194f7SMichael S. Tsirkin 163572c194f7SMichael S. Tsirkin /* RSDT is pointed to by RSDP */ 163672c194f7SMichael S. Tsirkin rsdt = tables->table_data->len; 163772c194f7SMichael S. Tsirkin build_rsdt(tables->table_data, tables->linker, table_offsets); 163872c194f7SMichael S. Tsirkin 163972c194f7SMichael S. Tsirkin /* RSDP is in FSEG memory, so allocate it separately */ 164072c194f7SMichael S. Tsirkin build_rsdp(tables->rsdp, tables->linker, rsdt); 164172c194f7SMichael S. Tsirkin 164207fb6176SPaolo Bonzini /* We'll expose it all to Guest so we want to reduce 164372c194f7SMichael S. Tsirkin * chance of size changes. 164472c194f7SMichael S. Tsirkin * RSDP is small so it's easy to keep it immutable, no need to 164572c194f7SMichael S. Tsirkin * bother with alignment. 164607fb6176SPaolo Bonzini * 164707fb6176SPaolo Bonzini * We used to align the tables to 4k, but of course this would 164807fb6176SPaolo Bonzini * too simple to be enough. 4k turned out to be too small an 164907fb6176SPaolo Bonzini * alignment very soon, and in fact it is almost impossible to 165007fb6176SPaolo Bonzini * keep the table size stable for all (max_cpus, max_memory_slots) 165107fb6176SPaolo Bonzini * combinations. So the table size is always 64k for pc-i440fx-2.1 165207fb6176SPaolo Bonzini * and we give an error if the table grows beyond that limit. 165307fb6176SPaolo Bonzini * 165407fb6176SPaolo Bonzini * We still have the problem of migrating from "-M pc-i440fx-2.0". For 165507fb6176SPaolo Bonzini * that, we exploit the fact that QEMU 2.1 generates _smaller_ tables 165607fb6176SPaolo Bonzini * than 2.0 and we can always pad the smaller tables with zeros. We can 165707fb6176SPaolo Bonzini * then use the exact size of the 2.0 tables. 165807fb6176SPaolo Bonzini * 165907fb6176SPaolo Bonzini * All this is for PIIX4, since QEMU 2.0 didn't support Q35 migration. 166072c194f7SMichael S. Tsirkin */ 166107fb6176SPaolo Bonzini if (guest_info->legacy_acpi_table_size) { 166207fb6176SPaolo Bonzini /* Subtracting aml_len gives the size of fixed tables. Then add the 166307fb6176SPaolo Bonzini * size of the PIIX4 DSDT/SSDT in QEMU 2.0. 166407fb6176SPaolo Bonzini */ 166507fb6176SPaolo Bonzini int legacy_aml_len = 166607fb6176SPaolo Bonzini guest_info->legacy_acpi_table_size + 166707fb6176SPaolo Bonzini ACPI_BUILD_LEGACY_CPU_AML_SIZE * max_cpus; 166807fb6176SPaolo Bonzini int legacy_table_size = 166907fb6176SPaolo Bonzini ROUND_UP(tables->table_data->len - aml_len + legacy_aml_len, 167007fb6176SPaolo Bonzini ACPI_BUILD_ALIGN_SIZE); 167107fb6176SPaolo Bonzini if (tables->table_data->len > legacy_table_size) { 167207fb6176SPaolo Bonzini /* Should happen only with PCI bridges and -M pc-i440fx-2.0. */ 1673868270f2SMichael S. Tsirkin error_report("Warning: migration may not work."); 167407fb6176SPaolo Bonzini } 167507fb6176SPaolo Bonzini g_array_set_size(tables->table_data, legacy_table_size); 167607fb6176SPaolo Bonzini } else { 1677868270f2SMichael S. Tsirkin /* Make sure we have a buffer in case we need to resize the tables. */ 1678868270f2SMichael S. Tsirkin if (tables->table_data->len > ACPI_BUILD_TABLE_SIZE / 2) { 167918045fb9SPaolo Bonzini /* As of QEMU 2.1, this fires with 160 VCPUs and 255 memory slots. */ 1680868270f2SMichael S. Tsirkin error_report("Warning: ACPI tables are larger than 64k."); 1681868270f2SMichael S. Tsirkin error_report("Warning: migration may not work."); 1682868270f2SMichael S. Tsirkin error_report("Warning: please remove CPUs, NUMA nodes, " 1683868270f2SMichael S. Tsirkin "memory slots or PCI bridges."); 168418045fb9SPaolo Bonzini } 1685868270f2SMichael S. Tsirkin acpi_align_size(tables->table_data, ACPI_BUILD_TABLE_SIZE); 168607fb6176SPaolo Bonzini } 168772c194f7SMichael S. Tsirkin 168807fb6176SPaolo Bonzini acpi_align_size(tables->linker, ACPI_BUILD_ALIGN_SIZE); 168972c194f7SMichael S. Tsirkin 169072c194f7SMichael S. Tsirkin /* Cleanup memory that's no longer used. */ 169172c194f7SMichael S. Tsirkin g_array_free(table_offsets, true); 169272c194f7SMichael S. Tsirkin } 169372c194f7SMichael S. Tsirkin 169472c194f7SMichael S. Tsirkin static void acpi_build_update(void *build_opaque, uint32_t offset) 169572c194f7SMichael S. Tsirkin { 169672c194f7SMichael S. Tsirkin AcpiBuildState *build_state = build_opaque; 169772c194f7SMichael S. Tsirkin AcpiBuildTables tables; 169872c194f7SMichael S. Tsirkin 169972c194f7SMichael S. Tsirkin /* No state to update or already patched? Nothing to do. */ 170072c194f7SMichael S. Tsirkin if (!build_state || build_state->patched) { 170172c194f7SMichael S. Tsirkin return; 170272c194f7SMichael S. Tsirkin } 170372c194f7SMichael S. Tsirkin build_state->patched = 1; 170472c194f7SMichael S. Tsirkin 170572c194f7SMichael S. Tsirkin acpi_build_tables_init(&tables); 170672c194f7SMichael S. Tsirkin 170772c194f7SMichael S. Tsirkin acpi_build(build_state->guest_info, &tables); 170872c194f7SMichael S. Tsirkin 170972c194f7SMichael S. Tsirkin assert(acpi_data_len(tables.table_data) == build_state->table_size); 171072c194f7SMichael S. Tsirkin memcpy(build_state->table_ram, tables.table_data->data, 171172c194f7SMichael S. Tsirkin build_state->table_size); 171272c194f7SMichael S. Tsirkin 171372c194f7SMichael S. Tsirkin acpi_build_tables_cleanup(&tables, true); 171472c194f7SMichael S. Tsirkin } 171572c194f7SMichael S. Tsirkin 171672c194f7SMichael S. Tsirkin static void acpi_build_reset(void *build_opaque) 171772c194f7SMichael S. Tsirkin { 171872c194f7SMichael S. Tsirkin AcpiBuildState *build_state = build_opaque; 171972c194f7SMichael S. Tsirkin build_state->patched = 0; 172072c194f7SMichael S. Tsirkin } 172172c194f7SMichael S. Tsirkin 172272c194f7SMichael S. Tsirkin static void *acpi_add_rom_blob(AcpiBuildState *build_state, GArray *blob, 172372c194f7SMichael S. Tsirkin const char *name) 172472c194f7SMichael S. Tsirkin { 172572c194f7SMichael S. Tsirkin return rom_add_blob(name, blob->data, acpi_data_len(blob), -1, name, 172672c194f7SMichael S. Tsirkin acpi_build_update, build_state); 172772c194f7SMichael S. Tsirkin } 172872c194f7SMichael S. Tsirkin 172972c194f7SMichael S. Tsirkin static const VMStateDescription vmstate_acpi_build = { 173072c194f7SMichael S. Tsirkin .name = "acpi_build", 173172c194f7SMichael S. Tsirkin .version_id = 1, 173272c194f7SMichael S. Tsirkin .minimum_version_id = 1, 173372c194f7SMichael S. Tsirkin .fields = (VMStateField[]) { 173472c194f7SMichael S. Tsirkin VMSTATE_UINT8(patched, AcpiBuildState), 173572c194f7SMichael S. Tsirkin VMSTATE_END_OF_LIST() 173672c194f7SMichael S. Tsirkin }, 173772c194f7SMichael S. Tsirkin }; 173872c194f7SMichael S. Tsirkin 173972c194f7SMichael S. Tsirkin void acpi_setup(PcGuestInfo *guest_info) 174072c194f7SMichael S. Tsirkin { 174172c194f7SMichael S. Tsirkin AcpiBuildTables tables; 174272c194f7SMichael S. Tsirkin AcpiBuildState *build_state; 174372c194f7SMichael S. Tsirkin 174472c194f7SMichael S. Tsirkin if (!guest_info->fw_cfg) { 174572c194f7SMichael S. Tsirkin ACPI_BUILD_DPRINTF(3, "No fw cfg. Bailing out.\n"); 174672c194f7SMichael S. Tsirkin return; 174772c194f7SMichael S. Tsirkin } 174872c194f7SMichael S. Tsirkin 174972c194f7SMichael S. Tsirkin if (!guest_info->has_acpi_build) { 175072c194f7SMichael S. Tsirkin ACPI_BUILD_DPRINTF(3, "ACPI build disabled. Bailing out.\n"); 175172c194f7SMichael S. Tsirkin return; 175272c194f7SMichael S. Tsirkin } 175372c194f7SMichael S. Tsirkin 175481adc513SMichael S. Tsirkin if (!acpi_enabled) { 175581adc513SMichael S. Tsirkin ACPI_BUILD_DPRINTF(3, "ACPI disabled. Bailing out.\n"); 175681adc513SMichael S. Tsirkin return; 175781adc513SMichael S. Tsirkin } 175881adc513SMichael S. Tsirkin 175972c194f7SMichael S. Tsirkin build_state = g_malloc0(sizeof *build_state); 176072c194f7SMichael S. Tsirkin 176172c194f7SMichael S. Tsirkin build_state->guest_info = guest_info; 176272c194f7SMichael S. Tsirkin 176399fd437dSMichael S. Tsirkin acpi_set_pci_info(); 176499fd437dSMichael S. Tsirkin 176572c194f7SMichael S. Tsirkin acpi_build_tables_init(&tables); 176672c194f7SMichael S. Tsirkin acpi_build(build_state->guest_info, &tables); 176772c194f7SMichael S. Tsirkin 176872c194f7SMichael S. Tsirkin /* Now expose it all to Guest */ 176972c194f7SMichael S. Tsirkin build_state->table_ram = acpi_add_rom_blob(build_state, tables.table_data, 177072c194f7SMichael S. Tsirkin ACPI_BUILD_TABLE_FILE); 177172c194f7SMichael S. Tsirkin build_state->table_size = acpi_data_len(tables.table_data); 177272c194f7SMichael S. Tsirkin 177372c194f7SMichael S. Tsirkin acpi_add_rom_blob(NULL, tables.linker, "etc/table-loader"); 177472c194f7SMichael S. Tsirkin 177572c194f7SMichael S. Tsirkin /* 177672c194f7SMichael S. Tsirkin * RSDP is small so it's easy to keep it immutable, no need to 177772c194f7SMichael S. Tsirkin * bother with ROM blobs. 177872c194f7SMichael S. Tsirkin */ 177972c194f7SMichael S. Tsirkin fw_cfg_add_file(guest_info->fw_cfg, ACPI_BUILD_RSDP_FILE, 178072c194f7SMichael S. Tsirkin tables.rsdp->data, acpi_data_len(tables.rsdp)); 178172c194f7SMichael S. Tsirkin 178272c194f7SMichael S. Tsirkin qemu_register_reset(acpi_build_reset, build_state); 178372c194f7SMichael S. Tsirkin acpi_build_reset(build_state); 178472c194f7SMichael S. Tsirkin vmstate_register(NULL, 0, &vmstate_acpi_build, build_state); 178572c194f7SMichael S. Tsirkin 178672c194f7SMichael S. Tsirkin /* Cleanup tables but don't free the memory: we track it 178772c194f7SMichael S. Tsirkin * in build_state. 178872c194f7SMichael S. Tsirkin */ 178972c194f7SMichael S. Tsirkin acpi_build_tables_cleanup(&tables, false); 179072c194f7SMichael S. Tsirkin } 1791