From nobody Thu Nov 6 10:33:18 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=linux.intel.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1540233800992154.56578056509363; Mon, 22 Oct 2018 11:43:20 -0700 (PDT) Received: from localhost ([::1]:36622 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gEfAY-0004Qw-Hu for importer@patchew.org; Mon, 22 Oct 2018 14:43:14 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:39152) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gEf5Z-0008HY-6X for qemu-devel@nongnu.org; Mon, 22 Oct 2018 14:38:08 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gEf5V-00047N-2Z for qemu-devel@nongnu.org; Mon, 22 Oct 2018 14:38:05 -0400 Received: from mga01.intel.com ([192.55.52.88]:7827) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1gEf5U-0003wU-FJ for qemu-devel@nongnu.org; Mon, 22 Oct 2018 14:38:00 -0400 Received: from fmsmga003.fm.intel.com ([10.253.24.29]) by fmsmga101.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 22 Oct 2018 11:38:00 -0700 Received: from omccarth-mobl.ger.corp.intel.com (HELO localhost.localdomain) ([10.252.18.99]) by FMSMGA003.fm.intel.com with ESMTP; 22 Oct 2018 11:37:55 -0700 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.54,413,1534834800"; d="scan'208";a="90377841" From: Samuel Ortiz To: qemu-devel@nongnu.org Date: Mon, 22 Oct 2018 20:36:35 +0200 Message-Id: <20181022183656.4902-6-sameo@linux.intel.com> X-Mailer: git-send-email 2.17.2 In-Reply-To: <20181022183656.4902-1-sameo@linux.intel.com> References: <20181022183656.4902-1-sameo@linux.intel.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 192.55.52.88 Subject: [Qemu-devel] [PATCH 05/26] hw: acpi: Generalize AML build routines X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Yang Zhong , Eduardo Habkost , "Michael S. Tsirkin" , Paolo Bonzini , Igor Mammedov , Richard Henderson Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RDMRC_1 RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: Yang Zhong Most of the AML build routines under acpi-build are not even architecture specific. They can be moved to the more generic hw/acpi folder where they could be shared across machine types and architectures. Cc: "Michael S. Tsirkin" Cc: Igor Mammedov Cc: Paolo Bonzini Cc: Richard Henderson Cc: Eduardo Habkost Cc: Marcel Apfelbaum Signed-off-by: Yang Zhong --- hw/acpi/aml-build.c | 498 ++++++++++++++++++++++++++++++++++ hw/i386/acpi-build.c | 516 +----------------------------------- include/hw/acpi/aml-build.h | 25 ++ 3 files changed, 526 insertions(+), 513 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 8f02af0659..6daa0f6df2 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -25,6 +25,10 @@ #include "qemu/bswap.h" #include "qemu/bitops.h" #include "sysemu/numa.h" +#include "hw/pci/pci.h" +#include "hw/pci/pci_bus.h" +#include "qemu/range.h" +#include "hw/pci/pci_bridge.h" =20 static GArray *build_alloc_array(void) { @@ -1597,6 +1601,500 @@ void acpi_build_tables_cleanup(AcpiBuildTables *tab= les, bool mfre) g_array_free(tables->vmgenid, mfre); } =20 +static void crs_range_insert(GPtrArray *ranges, uint64_t base, uint64_t li= mit) +{ + CrsRangeEntry *entry; + + entry =3D g_malloc(sizeof(*entry)); + entry->base =3D base; + entry->limit =3D limit; + + g_ptr_array_add(ranges, entry); +} + +static void crs_range_free(gpointer data) +{ + CrsRangeEntry *entry =3D (CrsRangeEntry *)data; + g_free(entry); +} + +void crs_range_set_init(CrsRangeSet *range_set) +{ + range_set->io_ranges =3D g_ptr_array_new_with_free_func(crs_range_free= ); + range_set->mem_ranges =3D g_ptr_array_new_with_free_func(crs_range_fre= e); + range_set->mem_64bit_ranges =3D + g_ptr_array_new_with_free_func(crs_range_free); +} + +void crs_range_set_free(CrsRangeSet *range_set) +{ + g_ptr_array_free(range_set->io_ranges, true); + g_ptr_array_free(range_set->mem_ranges, true); + g_ptr_array_free(range_set->mem_64bit_ranges, true); +} + +static gint crs_range_compare(gconstpointer a, gconstpointer b) +{ + CrsRangeEntry *entry_a =3D *(CrsRangeEntry **)a; + CrsRangeEntry *entry_b =3D *(CrsRangeEntry **)b; + + return (int64_t)entry_a->base - (int64_t)entry_b->base; +} + +/* + * crs_replace_with_free_ranges - given the 'used' ranges within [start - = end] + * interval, computes the 'free' ranges from the same interval. + * Example: If the input array is { [a1 - a2],[b1 - b2] }, the function + * will return { [base - a1], [a2 - b1], [b2 - limit] }. + */ +void crs_replace_with_free_ranges(GPtrArray *ranges, + uint64_t start, uint64_t end) +{ + GPtrArray *free_ranges =3D g_ptr_array_new(); + uint64_t free_base =3D start; + int i; + + g_ptr_array_sort(ranges, crs_range_compare); + for (i =3D 0; i < ranges->len; i++) { + CrsRangeEntry *used =3D g_ptr_array_index(ranges, i); + + if (free_base < used->base) { + crs_range_insert(free_ranges, free_base, used->base - 1); + } + + free_base =3D used->limit + 1; + } + + if (free_base < end) { + crs_range_insert(free_ranges, free_base, end); + } + + g_ptr_array_set_size(ranges, 0); + for (i =3D 0; i < free_ranges->len; i++) { + g_ptr_array_add(ranges, g_ptr_array_index(free_ranges, i)); + } + + g_ptr_array_free(free_ranges, true); +} + +/* + * crs_range_merge - merges adjacent ranges in the given array. + * Array elements are deleted and replaced with the merged ranges. + */ +static void crs_range_merge(GPtrArray *range) +{ + GPtrArray *tmp =3D g_ptr_array_new_with_free_func(crs_range_free); + CrsRangeEntry *entry; + uint64_t range_base, range_limit; + int i; + + if (!range->len) { + return; + } + + g_ptr_array_sort(range, crs_range_compare); + + entry =3D g_ptr_array_index(range, 0); + range_base =3D entry->base; + range_limit =3D entry->limit; + for (i =3D 1; i < range->len; i++) { + entry =3D g_ptr_array_index(range, i); + if (entry->base - 1 =3D=3D range_limit) { + range_limit =3D entry->limit; + } else { + crs_range_insert(tmp, range_base, range_limit); + range_base =3D entry->base; + range_limit =3D entry->limit; + } + } + crs_range_insert(tmp, range_base, range_limit); + + g_ptr_array_set_size(range, 0); + for (i =3D 0; i < tmp->len; i++) { + entry =3D g_ptr_array_index(tmp, i); + crs_range_insert(range, entry->base, entry->limit); + } + g_ptr_array_free(tmp, true); +} + +Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set) +{ + Aml *crs =3D aml_resource_template(); + CrsRangeSet temp_range_set; + CrsRangeEntry *entry; + uint8_t max_bus =3D pci_bus_num(host->bus); + uint8_t type; + int devfn; + int i; + + crs_range_set_init(&temp_range_set); + for (devfn =3D 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) { + uint64_t range_base, range_limit; + PCIDevice *dev =3D host->bus->devices[devfn]; + + if (!dev) { + continue; + } + + for (i =3D 0; i < PCI_NUM_REGIONS; i++) { + PCIIORegion *r =3D &dev->io_regions[i]; + + range_base =3D r->addr; + range_limit =3D r->addr + r->size - 1; + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (!range_base || range_base > range_limit) { + continue; + } + + if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { + crs_range_insert(temp_range_set.io_ranges, + range_base, range_limit); + } else { /* "memory" */ + crs_range_insert(temp_range_set.mem_ranges, + range_base, range_limit); + } + } + + type =3D dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUN= CTION; + if (type =3D=3D PCI_HEADER_TYPE_BRIDGE) { + uint8_t subordinate =3D dev->config[PCI_SUBORDINATE_BUS]; + if (subordinate > max_bus) { + max_bus =3D subordinate; + } + + range_base =3D pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE= _IO); + range_limit =3D pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPA= CE_IO); + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (range_base && range_base <=3D range_limit) { + crs_range_insert(temp_range_set.io_ranges, + range_base, range_limit); + } + + range_base =3D + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + range_limit =3D + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (range_base && range_base <=3D range_limit) { + uint64_t length =3D range_limit - range_base + 1; + if (range_limit <=3D UINT32_MAX && length <=3D UINT32_MAX)= { + crs_range_insert(temp_range_set.mem_ranges, + range_base, range_limit); + } else { + crs_range_insert(temp_range_set.mem_64bit_ranges, + range_base, range_limit); + } + } + + range_base =3D + pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + range_limit =3D + pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); + + /* + * Work-around for old bioses + * that do not support multiple root buses + */ + if (range_base && range_base <=3D range_limit) { + uint64_t length =3D range_limit - range_base + 1; + if (range_limit <=3D UINT32_MAX && length <=3D UINT32_MAX)= { + crs_range_insert(temp_range_set.mem_ranges, + range_base, range_limit); + } else { + crs_range_insert(temp_range_set.mem_64bit_ranges, + range_base, range_limit); + } + } + } + } + + crs_range_merge(temp_range_set.io_ranges); + for (i =3D 0; i < temp_range_set.io_ranges->len; i++) { + entry =3D g_ptr_array_index(temp_range_set.io_ranges, i); + aml_append(crs, + aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, + AML_POS_DECODE, AML_ENTIRE_RANGE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(range_set->io_ranges, entry->base, entry->limit); + } + + crs_range_merge(temp_range_set.mem_ranges); + for (i =3D 0; i < temp_range_set.mem_ranges->len; i++) { + entry =3D g_ptr_array_index(temp_range_set.mem_ranges, i); + aml_append(crs, + aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(range_set->mem_ranges, entry->base, entry->limit); + } + + crs_range_merge(temp_range_set.mem_64bit_ranges); + for (i =3D 0; i < temp_range_set.mem_64bit_ranges->len; i++) { + entry =3D g_ptr_array_index(temp_range_set.mem_64bit_ranges, i); + aml_append(crs, + aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, + AML_MAX_FIXED, AML_NON_CACHEABLE, + AML_READ_WRITE, + 0, entry->base, entry->limit, 0, + entry->limit - entry->base + 1)); + crs_range_insert(range_set->mem_64bit_ranges, + entry->base, entry->limit); + } + + crs_range_set_free(&temp_range_set); + + aml_append(crs, + aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, + 0, + pci_bus_num(host->bus), + max_bus, + 0, + max_bus - pci_bus_num(host->bus) + 1)); + + return crs; +} + +Aml *build_osc_method(void) +{ + Aml *if_ctx; + Aml *if_ctx2; + Aml *else_ctx; + Aml *method; + Aml *a_cwd1 =3D aml_name("CDW1"); + Aml *a_ctrl =3D aml_local(0); + + method =3D aml_method("_OSC", 4, AML_NOTSERIALIZED); + aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW= 1")); + + if_ctx =3D aml_if(aml_equal( + aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"))); + aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW= 2")); + aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW= 3")); + + aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl)); + + /* + * Always allow native PME, AER (no dependencies) + * Allow SHPC (PCI bridges can have SHPC controller) + */ + aml_append(if_ctx, aml_and(a_ctrl, aml_int(0x1F), a_ctrl)); + + if_ctx2 =3D aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1)))); + /* Unknown revision */ + aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1)); + aml_append(if_ctx, if_ctx2); + + if_ctx2 =3D aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl))); + /* Capabilities bits were masked */ + aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1)); + aml_append(if_ctx, if_ctx2); + + /* Update DWORD3 in the buffer */ + aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3"))); + aml_append(method, if_ctx); + + else_ctx =3D aml_else(); + /* Unrecognized UUID */ + aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1)); + aml_append(method, else_ctx); + + aml_append(method, aml_return(aml_arg(3))); + return method; +} + +void +build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info) +{ + AcpiTableMcfg *mcfg; + const char *sig; + int len =3D sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]); + + mcfg =3D acpi_data_push(table_data, len); + mcfg->allocation[0].address =3D cpu_to_le64(info->mcfg_base); + /* Only a single allocation so no need to play with segments */ + mcfg->allocation[0].pci_segment =3D cpu_to_le16(0); + mcfg->allocation[0].start_bus_number =3D 0; + mcfg->allocation[0].end_bus_number =3D PCIE_MMCFG_BUS(info->mcfg_size = - 1); + + /* MCFG is used for ECAM which can be enabled or disabled by guest. + * To avoid table size changes (which create migration issues), + * always create the table even if there are no allocations, + * but set the signature to a reserved value in this case. + * ACPI spec requires OSPMs to ignore such tables. + */ + if (info->mcfg_base =3D=3D PCIE_BASE_ADDR_UNMAPPED) { + /* Reserved signature: ignored by OSPM */ + sig =3D "QEMU"; + } else { + sig =3D "MCFG"; + } + build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL, NULL= ); +} + +Aml *build_gsi_link_dev(const char *name, uint8_t uid, uint8_t gsi) +{ + Aml *dev; + Aml *crs; + Aml *method; + uint32_t irqs; + + dev =3D aml_device("%s", name); + aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C0F"))); + aml_append(dev, aml_name_decl("_UID", aml_int(uid))); + + crs =3D aml_resource_template(); + irqs =3D gsi; + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, + AML_SHARED, &irqs, 1)); + aml_append(dev, aml_name_decl("_PRS", crs)); + + aml_append(dev, aml_name_decl("_CRS", crs)); + + /* + * _DIS can be no-op because the interrupt cannot be disabled. + */ + method =3D aml_method("_DIS", 0, AML_NOTSERIALIZED); + aml_append(dev, method); + + method =3D aml_method("_SRS", 1, AML_NOTSERIALIZED); + aml_append(dev, method); + + return dev; +} + +/** + * build_prt_entry: + * @link_name: link name for PCI route entry + * + * build AML package containing a PCI route entry for @link_name + */ +static Aml *build_prt_entry(const char *link_name) +{ + Aml *a_zero =3D aml_int(0); + Aml *pkg =3D aml_package(4); + aml_append(pkg, a_zero); + aml_append(pkg, a_zero); + aml_append(pkg, aml_name("%s", link_name)); + aml_append(pkg, a_zero); + return pkg; +} + +/* + * initialize_route - Initialize the interrupt routing rule + * through a specific LINK: + * if (lnk_idx =3D=3D idx) + * route using link 'link_name' + */ +static Aml *initialize_route(Aml *route, const char *link_name, + Aml *lnk_idx, int idx) +{ + Aml *if_ctx =3D aml_if(aml_equal(lnk_idx, aml_int(idx))); + Aml *pkg =3D build_prt_entry(link_name); + + aml_append(if_ctx, aml_store(pkg, route)); + + return if_ctx; +} + +/* + * build_prt - Define interrupt rounting rules + * + * Returns an array of 128 routes, one for each device, + * based on device location. + * The main goal is to equaly distribute the interrupts + * over the 4 existing ACPI links (works only for i440fx). + * The hash function is (slot + pin) & 3 -> "LNK[D|A|B|C]". + * + */ +Aml *build_prt(bool is_pci0_prt) +{ + Aml *method, *while_ctx, *pin, *res; + + method =3D aml_method("_PRT", 0, AML_NOTSERIALIZED); + res =3D aml_local(0); + pin =3D aml_local(1); + aml_append(method, aml_store(aml_package(128), res)); + aml_append(method, aml_store(aml_int(0), pin)); + + /* while (pin < 128) */ + while_ctx =3D aml_while(aml_lless(pin, aml_int(128))); + { + Aml *slot =3D aml_local(2); + Aml *lnk_idx =3D aml_local(3); + Aml *route =3D aml_local(4); + + /* slot =3D pin >> 2 */ + aml_append(while_ctx, + aml_store(aml_shiftright(pin, aml_int(2), NULL), slot)); + /* lnk_idx =3D (slot + pin) & 3 */ + aml_append(while_ctx, + aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3), NULL), + lnk_idx)); + + /* route[2] =3D "LNK[D|A|B|C]", selection based on pin % 3 */ + aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0)); + if (is_pci0_prt) { + Aml *if_device_1, *if_pin_4, *else_pin_4; + + /* device 1 is the power-management device, needs SCI */ + if_device_1 =3D aml_if(aml_equal(lnk_idx, aml_int(1))); + { + if_pin_4 =3D aml_if(aml_equal(pin, aml_int(4))); + { + aml_append(if_pin_4, + aml_store(build_prt_entry("LNKS"), route)); + } + aml_append(if_device_1, if_pin_4); + else_pin_4 =3D aml_else(); + { + aml_append(else_pin_4, + aml_store(build_prt_entry("LNKA"), route)); + } + aml_append(if_device_1, else_pin_4); + } + aml_append(while_ctx, if_device_1); + } else { + aml_append(while_ctx, initialize_route(route, "LNKA", lnk_idx,= 1)); + } + aml_append(while_ctx, initialize_route(route, "LNKB", lnk_idx, 2)); + aml_append(while_ctx, initialize_route(route, "LNKC", lnk_idx, 3)); + + /* route[0] =3D 0x[slot]FFFF */ + aml_append(while_ctx, + aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xF= FFF), + NULL), + aml_index(route, aml_int(0)))); + /* route[1] =3D pin & 3 */ + aml_append(while_ctx, + aml_store(aml_and(pin, aml_int(3), NULL), + aml_index(route, aml_int(1)))); + /* res[pin] =3D route */ + aml_append(while_ctx, aml_store(route, aml_index(res, pin))); + /* pin++ */ + aml_append(while_ctx, aml_increment(pin)); + } + aml_append(method, while_ctx); + /* return res*/ + aml_append(method, aml_return(res)); + + return method; +} + /* Build rsdt table */ void build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index eee2eb3ed2..3ab68fd24d 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -86,11 +86,6 @@ /* Default IOAPIC ID */ #define ACPI_BUILD_IOAPIC_ID 0x0 =20 -typedef struct AcpiMcfgInfo { - uint64_t mcfg_base; - uint32_t mcfg_size; -} AcpiMcfgInfo; - typedef struct AcpiPmInfo { bool s3_disabled; bool s4_disabled; @@ -567,403 +562,6 @@ static void build_append_pci_bus_devices(Aml *parent_= scope, PCIBus *bus, qobject_unref(bsel); } =20 -/** - * build_prt_entry: - * @link_name: link name for PCI route entry - * - * build AML package containing a PCI route entry for @link_name - */ -static Aml *build_prt_entry(const char *link_name) -{ - Aml *a_zero =3D aml_int(0); - Aml *pkg =3D aml_package(4); - aml_append(pkg, a_zero); - aml_append(pkg, a_zero); - aml_append(pkg, aml_name("%s", link_name)); - aml_append(pkg, a_zero); - return pkg; -} - -/* - * initialize_route - Initialize the interrupt routing rule - * through a specific LINK: - * if (lnk_idx =3D=3D idx) - * route using link 'link_name' - */ -static Aml *initialize_route(Aml *route, const char *link_name, - Aml *lnk_idx, int idx) -{ - Aml *if_ctx =3D aml_if(aml_equal(lnk_idx, aml_int(idx))); - Aml *pkg =3D build_prt_entry(link_name); - - aml_append(if_ctx, aml_store(pkg, route)); - - return if_ctx; -} - -/* - * build_prt - Define interrupt rounting rules - * - * Returns an array of 128 routes, one for each device, - * based on device location. - * The main goal is to equaly distribute the interrupts - * over the 4 existing ACPI links (works only for i440fx). - * The hash function is (slot + pin) & 3 -> "LNK[D|A|B|C]". - * - */ -static Aml *build_prt(bool is_pci0_prt) -{ - Aml *method, *while_ctx, *pin, *res; - - method =3D aml_method("_PRT", 0, AML_NOTSERIALIZED); - res =3D aml_local(0); - pin =3D aml_local(1); - aml_append(method, aml_store(aml_package(128), res)); - aml_append(method, aml_store(aml_int(0), pin)); - - /* while (pin < 128) */ - while_ctx =3D aml_while(aml_lless(pin, aml_int(128))); - { - Aml *slot =3D aml_local(2); - Aml *lnk_idx =3D aml_local(3); - Aml *route =3D aml_local(4); - - /* slot =3D pin >> 2 */ - aml_append(while_ctx, - aml_store(aml_shiftright(pin, aml_int(2), NULL), slot)); - /* lnk_idx =3D (slot + pin) & 3 */ - aml_append(while_ctx, - aml_store(aml_and(aml_add(pin, slot, NULL), aml_int(3), NULL), - lnk_idx)); - - /* route[2] =3D "LNK[D|A|B|C]", selection based on pin % 3 */ - aml_append(while_ctx, initialize_route(route, "LNKD", lnk_idx, 0)); - if (is_pci0_prt) { - Aml *if_device_1, *if_pin_4, *else_pin_4; - - /* device 1 is the power-management device, needs SCI */ - if_device_1 =3D aml_if(aml_equal(lnk_idx, aml_int(1))); - { - if_pin_4 =3D aml_if(aml_equal(pin, aml_int(4))); - { - aml_append(if_pin_4, - aml_store(build_prt_entry("LNKS"), route)); - } - aml_append(if_device_1, if_pin_4); - else_pin_4 =3D aml_else(); - { - aml_append(else_pin_4, - aml_store(build_prt_entry("LNKA"), route)); - } - aml_append(if_device_1, else_pin_4); - } - aml_append(while_ctx, if_device_1); - } else { - aml_append(while_ctx, initialize_route(route, "LNKA", lnk_idx,= 1)); - } - aml_append(while_ctx, initialize_route(route, "LNKB", lnk_idx, 2)); - aml_append(while_ctx, initialize_route(route, "LNKC", lnk_idx, 3)); - - /* route[0] =3D 0x[slot]FFFF */ - aml_append(while_ctx, - aml_store(aml_or(aml_shiftleft(slot, aml_int(16)), aml_int(0xF= FFF), - NULL), - aml_index(route, aml_int(0)))); - /* route[1] =3D pin & 3 */ - aml_append(while_ctx, - aml_store(aml_and(pin, aml_int(3), NULL), - aml_index(route, aml_int(1)))); - /* res[pin] =3D route */ - aml_append(while_ctx, aml_store(route, aml_index(res, pin))); - /* pin++ */ - aml_append(while_ctx, aml_increment(pin)); - } - aml_append(method, while_ctx); - /* return res*/ - aml_append(method, aml_return(res)); - - return method; -} - -typedef struct CrsRangeEntry { - uint64_t base; - uint64_t limit; -} CrsRangeEntry; - -static void crs_range_insert(GPtrArray *ranges, uint64_t base, uint64_t li= mit) -{ - CrsRangeEntry *entry; - - entry =3D g_malloc(sizeof(*entry)); - entry->base =3D base; - entry->limit =3D limit; - - g_ptr_array_add(ranges, entry); -} - -static void crs_range_free(gpointer data) -{ - CrsRangeEntry *entry =3D (CrsRangeEntry *)data; - g_free(entry); -} - -typedef struct CrsRangeSet { - GPtrArray *io_ranges; - GPtrArray *mem_ranges; - GPtrArray *mem_64bit_ranges; - } CrsRangeSet; - -static void crs_range_set_init(CrsRangeSet *range_set) -{ - range_set->io_ranges =3D g_ptr_array_new_with_free_func(crs_range_free= ); - range_set->mem_ranges =3D g_ptr_array_new_with_free_func(crs_range_fre= e); - range_set->mem_64bit_ranges =3D - g_ptr_array_new_with_free_func(crs_range_free); -} - -static void crs_range_set_free(CrsRangeSet *range_set) -{ - g_ptr_array_free(range_set->io_ranges, true); - g_ptr_array_free(range_set->mem_ranges, true); - g_ptr_array_free(range_set->mem_64bit_ranges, true); -} - -static gint crs_range_compare(gconstpointer a, gconstpointer b) -{ - CrsRangeEntry *entry_a =3D *(CrsRangeEntry **)a; - CrsRangeEntry *entry_b =3D *(CrsRangeEntry **)b; - - return (int64_t)entry_a->base - (int64_t)entry_b->base; -} - -/* - * crs_replace_with_free_ranges - given the 'used' ranges within [start - = end] - * interval, computes the 'free' ranges from the same interval. - * Example: If the input array is { [a1 - a2],[b1 - b2] }, the function - * will return { [base - a1], [a2 - b1], [b2 - limit] }. - */ -static void crs_replace_with_free_ranges(GPtrArray *ranges, - uint64_t start, uint64_t end) -{ - GPtrArray *free_ranges =3D g_ptr_array_new(); - uint64_t free_base =3D start; - int i; - - g_ptr_array_sort(ranges, crs_range_compare); - for (i =3D 0; i < ranges->len; i++) { - CrsRangeEntry *used =3D g_ptr_array_index(ranges, i); - - if (free_base < used->base) { - crs_range_insert(free_ranges, free_base, used->base - 1); - } - - free_base =3D used->limit + 1; - } - - if (free_base < end) { - crs_range_insert(free_ranges, free_base, end); - } - - g_ptr_array_set_size(ranges, 0); - for (i =3D 0; i < free_ranges->len; i++) { - g_ptr_array_add(ranges, g_ptr_array_index(free_ranges, i)); - } - - g_ptr_array_free(free_ranges, true); -} - -/* - * crs_range_merge - merges adjacent ranges in the given array. - * Array elements are deleted and replaced with the merged ranges. - */ -static void crs_range_merge(GPtrArray *range) -{ - GPtrArray *tmp =3D g_ptr_array_new_with_free_func(crs_range_free); - CrsRangeEntry *entry; - uint64_t range_base, range_limit; - int i; - - if (!range->len) { - return; - } - - g_ptr_array_sort(range, crs_range_compare); - - entry =3D g_ptr_array_index(range, 0); - range_base =3D entry->base; - range_limit =3D entry->limit; - for (i =3D 1; i < range->len; i++) { - entry =3D g_ptr_array_index(range, i); - if (entry->base - 1 =3D=3D range_limit) { - range_limit =3D entry->limit; - } else { - crs_range_insert(tmp, range_base, range_limit); - range_base =3D entry->base; - range_limit =3D entry->limit; - } - } - crs_range_insert(tmp, range_base, range_limit); - - g_ptr_array_set_size(range, 0); - for (i =3D 0; i < tmp->len; i++) { - entry =3D g_ptr_array_index(tmp, i); - crs_range_insert(range, entry->base, entry->limit); - } - g_ptr_array_free(tmp, true); -} - -static Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set) -{ - Aml *crs =3D aml_resource_template(); - CrsRangeSet temp_range_set; - CrsRangeEntry *entry; - uint8_t max_bus =3D pci_bus_num(host->bus); - uint8_t type; - int devfn; - int i; - - crs_range_set_init(&temp_range_set); - for (devfn =3D 0; devfn < ARRAY_SIZE(host->bus->devices); devfn++) { - uint64_t range_base, range_limit; - PCIDevice *dev =3D host->bus->devices[devfn]; - - if (!dev) { - continue; - } - - for (i =3D 0; i < PCI_NUM_REGIONS; i++) { - PCIIORegion *r =3D &dev->io_regions[i]; - - range_base =3D r->addr; - range_limit =3D r->addr + r->size - 1; - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (!range_base || range_base > range_limit) { - continue; - } - - if (r->type & PCI_BASE_ADDRESS_SPACE_IO) { - crs_range_insert(temp_range_set.io_ranges, - range_base, range_limit); - } else { /* "memory" */ - crs_range_insert(temp_range_set.mem_ranges, - range_base, range_limit); - } - } - - type =3D dev->config[PCI_HEADER_TYPE] & ~PCI_HEADER_TYPE_MULTI_FUN= CTION; - if (type =3D=3D PCI_HEADER_TYPE_BRIDGE) { - uint8_t subordinate =3D dev->config[PCI_SUBORDINATE_BUS]; - if (subordinate > max_bus) { - max_bus =3D subordinate; - } - - range_base =3D pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE= _IO); - range_limit =3D pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPA= CE_IO); - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (range_base && range_base <=3D range_limit) { - crs_range_insert(temp_range_set.io_ranges, - range_base, range_limit); - } - - range_base =3D - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - range_limit =3D - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_SPACE_MEMORY); - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (range_base && range_base <=3D range_limit) { - uint64_t length =3D range_limit - range_base + 1; - if (range_limit <=3D UINT32_MAX && length <=3D UINT32_MAX)= { - crs_range_insert(temp_range_set.mem_ranges, - range_base, range_limit); - } else { - crs_range_insert(temp_range_set.mem_64bit_ranges, - range_base, range_limit); - } - } - - range_base =3D - pci_bridge_get_base(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - range_limit =3D - pci_bridge_get_limit(dev, PCI_BASE_ADDRESS_MEM_PREFETCH); - - /* - * Work-around for old bioses - * that do not support multiple root buses - */ - if (range_base && range_base <=3D range_limit) { - uint64_t length =3D range_limit - range_base + 1; - if (range_limit <=3D UINT32_MAX && length <=3D UINT32_MAX)= { - crs_range_insert(temp_range_set.mem_ranges, - range_base, range_limit); - } else { - crs_range_insert(temp_range_set.mem_64bit_ranges, - range_base, range_limit); - } - } - } - } - - crs_range_merge(temp_range_set.io_ranges); - for (i =3D 0; i < temp_range_set.io_ranges->len; i++) { - entry =3D g_ptr_array_index(temp_range_set.io_ranges, i); - aml_append(crs, - aml_word_io(AML_MIN_FIXED, AML_MAX_FIXED, - AML_POS_DECODE, AML_ENTIRE_RANGE, - 0, entry->base, entry->limit, 0, - entry->limit - entry->base + 1)); - crs_range_insert(range_set->io_ranges, entry->base, entry->limit); - } - - crs_range_merge(temp_range_set.mem_ranges); - for (i =3D 0; i < temp_range_set.mem_ranges->len; i++) { - entry =3D g_ptr_array_index(temp_range_set.mem_ranges, i); - aml_append(crs, - aml_dword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_NON_CACHEABLE, - AML_READ_WRITE, - 0, entry->base, entry->limit, 0, - entry->limit - entry->base + 1)); - crs_range_insert(range_set->mem_ranges, entry->base, entry->limit); - } - - crs_range_merge(temp_range_set.mem_64bit_ranges); - for (i =3D 0; i < temp_range_set.mem_64bit_ranges->len; i++) { - entry =3D g_ptr_array_index(temp_range_set.mem_64bit_ranges, i); - aml_append(crs, - aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, - AML_MAX_FIXED, AML_NON_CACHEABLE, - AML_READ_WRITE, - 0, entry->base, entry->limit, 0, - entry->limit - entry->base + 1)); - crs_range_insert(range_set->mem_64bit_ranges, - entry->base, entry->limit); - } - - crs_range_set_free(&temp_range_set); - - aml_append(crs, - aml_word_bus_number(AML_MIN_FIXED, AML_MAX_FIXED, AML_POS_DECODE, - 0, - pci_bus_num(host->bus), - max_bus, - 0, - max_bus - pci_bus_num(host->bus) + 1)); - - return crs; -} - static void build_hpet_aml(Aml *table) { Aml *crs; @@ -1334,37 +932,6 @@ static Aml *build_link_dev(const char *name, uint8_t = uid, Aml *reg) return dev; } =20 -static Aml *build_gsi_link_dev(const char *name, uint8_t uid, uint8_t gsi) -{ - Aml *dev; - Aml *crs; - Aml *method; - uint32_t irqs; - - dev =3D aml_device("%s", name); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C0F"))); - aml_append(dev, aml_name_decl("_UID", aml_int(uid))); - - crs =3D aml_resource_template(); - irqs =3D gsi; - aml_append(crs, aml_interrupt(AML_CONSUMER, AML_LEVEL, AML_ACTIVE_HIGH, - AML_SHARED, &irqs, 1)); - aml_append(dev, aml_name_decl("_PRS", crs)); - - aml_append(dev, aml_name_decl("_CRS", crs)); - - /* - * _DIS can be no-op because the interrupt cannot be disabled. - */ - method =3D aml_method("_DIS", 0, AML_NOTSERIALIZED); - aml_append(dev, method); - - method =3D aml_method("_SRS", 1, AML_NOTSERIALIZED); - aml_append(dev, method); - - return dev; -} - /* _CRS method - get current settings */ static Aml *build_iqcr_method(bool is_piix4) { @@ -1728,54 +1295,6 @@ static void build_piix4_pci_hotplug(Aml *table) aml_append(table, scope); } =20 -static Aml *build_q35_osc_method(void) -{ - Aml *if_ctx; - Aml *if_ctx2; - Aml *else_ctx; - Aml *method; - Aml *a_cwd1 =3D aml_name("CDW1"); - Aml *a_ctrl =3D aml_local(0); - - method =3D aml_method("_OSC", 4, AML_NOTSERIALIZED); - aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW= 1")); - - if_ctx =3D aml_if(aml_equal( - aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"))); - aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW= 2")); - aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW= 3")); - - aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl)); - - /* - * Always allow native PME, AER (no dependencies) - * Allow SHPC (PCI bridges can have SHPC controller) - */ - aml_append(if_ctx, aml_and(a_ctrl, aml_int(0x1F), a_ctrl)); - - if_ctx2 =3D aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1)))); - /* Unknown revision */ - aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1)); - aml_append(if_ctx, if_ctx2); - - if_ctx2 =3D aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl))); - /* Capabilities bits were masked */ - aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1)); - aml_append(if_ctx, if_ctx2); - - /* Update DWORD3 in the buffer */ - aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3"))); - aml_append(method, if_ctx); - - else_ctx =3D aml_else(); - /* Unrecognized UUID */ - aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1)); - aml_append(method, else_ctx); - - aml_append(method, aml_return(aml_arg(3))); - return method; -} - static void build_dsdt(GArray *table_data, BIOSLinker *linker, AcpiPmInfo *pm, AcpiMiscInfo *misc, @@ -1818,7 +1337,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03"))); aml_append(dev, aml_name_decl("_ADR", aml_int(0))); aml_append(dev, aml_name_decl("_UID", aml_int(1))); - aml_append(dev, build_q35_osc_method()); + aml_append(dev, build_osc_method()); aml_append(sb_scope, dev); aml_append(dsdt, sb_scope); =20 @@ -1883,7 +1402,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03"))); aml_append(dev, aml_name_decl("_BBN", aml_int(bus_num))); if (pci_bus_is_express(bus)) { - aml_append(dev, build_q35_osc_method()); + aml_append(dev, build_osc_method()); } =20 if (numa_node !=3D NUMA_NODE_UNASSIGNED) { @@ -2369,35 +1888,6 @@ build_srat(GArray *table_data, BIOSLinker *linker, table_data->len - srat_start, 1, NULL, NULL); } =20 -static void -build_mcfg_q35(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info) -{ - AcpiTableMcfg *mcfg; - const char *sig; - int len =3D sizeof(*mcfg) + 1 * sizeof(mcfg->allocation[0]); - - mcfg =3D acpi_data_push(table_data, len); - mcfg->allocation[0].address =3D cpu_to_le64(info->mcfg_base); - /* Only a single allocation so no need to play with segments */ - mcfg->allocation[0].pci_segment =3D cpu_to_le16(0); - mcfg->allocation[0].start_bus_number =3D 0; - mcfg->allocation[0].end_bus_number =3D PCIE_MMCFG_BUS(info->mcfg_size = - 1); - - /* MCFG is used for ECAM which can be enabled or disabled by guest. - * To avoid table size changes (which create migration issues), - * always create the table even if there are no allocations, - * but set the signature to a reserved value in this case. - * ACPI spec requires OSPMs to ignore such tables. - */ - if (info->mcfg_base =3D=3D PCIE_BASE_ADDR_UNMAPPED) { - /* Reserved signature: ignored by OSPM */ - sig =3D "QEMU"; - } else { - sig =3D "MCFG"; - } - build_header(linker, table_data, (void *)mcfg, sig, len, 1, NULL, NULL= ); -} - /* * VT-d spec 8.1 DMA Remapping Reporting Structure * (version Oct. 2014 or later) @@ -2625,7 +2115,7 @@ void acpi_build(AcpiBuildTables *tables, } if (acpi_get_mcfg(&mcfg)) { acpi_add_table(table_offsets, tables_blob); - build_mcfg_q35(tables_blob, tables->linker, &mcfg); + build_mcfg(tables_blob, tables->linker, &mcfg); } if (x86_iommu_get_default()) { IommuType IOMMUType =3D x86_iommu_get_type(); diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index b4345c95bd..d6dbb399a0 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -3,6 +3,7 @@ =20 #include "hw/acpi/acpi-defs.h" #include "hw/acpi/bios-linker-loader.h" +#include "hw/pci/pcie_host.h" =20 /* Reserve RAM space for tables: add another order of magnitude. */ #define ACPI_BUILD_TABLE_MAX_SIZE 0x200000 @@ -224,6 +225,21 @@ struct AcpiBuildTables { BIOSLinker *linker; } AcpiBuildTables; =20 +typedef struct AcpiMcfgInfo { + uint64_t mcfg_base; + uint32_t mcfg_size; +} AcpiMcfgInfo; + +typedef struct CrsRangeEntry { + uint64_t base; + uint64_t limit; +} CrsRangeEntry; + +typedef struct CrsRangeSet { + GPtrArray *io_ranges; + GPtrArray *mem_ranges; + GPtrArray *mem_64bit_ranges; +} CrsRangeSet; /** * init_aml_allocator: * @@ -389,6 +405,15 @@ void acpi_align_size(GArray *blob, unsigned align); void acpi_add_table(GArray *table_offsets, GArray *table_data); void acpi_build_tables_init(AcpiBuildTables *tables); void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre); +Aml *build_osc_method(void); +void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info= ); +Aml *build_gsi_link_dev(const char *name, uint8_t uid, uint8_t gsi); +Aml *build_prt(bool is_pci0_prt); +void crs_range_set_init(CrsRangeSet *range_set); +Aml *build_crs(PCIHostState *host, CrsRangeSet *range_set); +void crs_replace_with_free_ranges(GPtrArray *ranges, + uint64_t start, uint64_t end); +void crs_range_set_free(CrsRangeSet *range_set); GArray *build_rsdp_rsdt(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset); GArray *build_rsdp(GArray *rsdp_table, --=20 2.17.2