From 2f399b7d5bcfc28a808e61cf203900cfbdacf2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20=C5=BBygowski?= Date: Thu, 2 Apr 2020 19:51:37 +0200 Subject: nb/amd/pi/00730F01/northbridge.c: refactor IVRS generation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use defined structures to assemble IVRS and IVHD entries. Additionally assemble IVHD type 11h which supersedes IVHD type 10h. In order to utilize all IOMMU features firmware should also expose IVHD type 11h. The new type is already supported and parsed since Xen 1.13. IVHD type 10h should still be present for backwards compatibility. TEST=boot PC Engines apu2 and disassemble IVRS using newest IASL, boot Xen 1.13 or newer with debug enabled and see IVRS IVHD 11h parsed in xl dmesg Signed-off-by: Michał Żygowski Change-Id: I9a2c24b67adfa8ebd718caeb5eec88687dcbcc9d Reviewed-on: https://review.coreboot.org/c/coreboot/+/40042 Tested-by: build bot (Jenkins) Reviewed-by: HAOUAS Elyes Reviewed-by: Angel Pons --- src/northbridge/amd/pi/00730F01/northbridge.c | 390 ++++++++++++++++---------- 1 file changed, 239 insertions(+), 151 deletions(-) (limited to 'src/northbridge/amd/pi/00730F01') diff --git a/src/northbridge/amd/pi/00730F01/northbridge.c b/src/northbridge/amd/pi/00730F01/northbridge.c index d324689955..4a24d3550a 100644 --- a/src/northbridge/amd/pi/00730F01/northbridge.c +++ b/src/northbridge/amd/pi/00730F01/northbridge.c @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* This file is part of the coreboot project. */ +#include #include #include #include +#include #include #include #include @@ -11,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +25,7 @@ #include #include #include +#include #define MAX_NODE_NUMS MAX_NODES #define PCIE_CAP_AER BIT(5) @@ -428,59 +432,139 @@ static unsigned long acpi_fill_hest(acpi_hest_t *hest) return (unsigned long)current; } -static void add_ivhd_dev_entry(struct device *parent, struct device *dev, - unsigned long *current, uint16_t *length, - uint8_t type, uint8_t data) +unsigned long acpi_fill_ivrs_ioapic(acpi_ivrs_t *ivrs, unsigned long current) +{ + /* 8-byte IVHD structures must be aligned to the 8-byte boundary. */ + current = ALIGN_UP(current, 8); + ivrs_ivhd_special_t *ivhd_ioapic = (ivrs_ivhd_special_t *)current; + + ivhd_ioapic->type = IVHD_DEV_8_BYTE_EXT_SPECIAL_DEV; + ivhd_ioapic->reserved = 0x0000; + ivhd_ioapic->dte_setting = IVHD_DTE_LINT_1_PASS | IVHD_DTE_LINT_0_PASS | + IVHD_DTE_SYS_MGT_NO_TRANS | IVHD_DTE_NMI_PASS | + IVHD_DTE_EXT_INT_PASS | IVHD_DTE_INIT_PASS; + ivhd_ioapic->handle = CONFIG_MAX_CPUS; /* FCH IOAPIC ID */ + ivhd_ioapic->source_dev_id = PCI_DEVFN(SMBUS_DEV, SMBUS_FUNC); + ivhd_ioapic->variety = IVHD_SPECIAL_DEV_IOAPIC; + current += sizeof(ivrs_ivhd_special_t); + + ivhd_ioapic = (ivrs_ivhd_special_t *)current; + + ivhd_ioapic->type = IVHD_DEV_8_BYTE_EXT_SPECIAL_DEV; + ivhd_ioapic->reserved = 0x0000; + ivhd_ioapic->dte_setting = 0x00; + ivhd_ioapic->handle = CONFIG_MAX_CPUS + 1; /* GNB IOAPIC ID */ + ivhd_ioapic->source_dev_id = PCI_DEVFN(0, 1); + ivhd_ioapic->variety = IVHD_SPECIAL_DEV_IOAPIC; + current += sizeof(ivrs_ivhd_special_t); + + return current; +} + +static unsigned long ivhd_describe_hpet(unsigned long current) +{ + /* 8-byte IVHD structures must be aligned to the 8-byte boundary. */ + current = ALIGN_UP(current, 8); + ivrs_ivhd_special_t *ivhd_hpet = (ivrs_ivhd_special_t *)current; + + ivhd_hpet->type = IVHD_DEV_8_BYTE_EXT_SPECIAL_DEV; + ivhd_hpet->reserved = 0x0000; + ivhd_hpet->dte_setting = 0x00; + ivhd_hpet->handle = 0x00; + ivhd_hpet->source_dev_id = PCI_DEVFN(SMBUS_DEV, SMBUS_FUNC); + ivhd_hpet->variety = IVHD_SPECIAL_DEV_HPET; + current += sizeof(ivrs_ivhd_special_t); + + return current; +} + +static unsigned long ivhd_dev_range(unsigned long current, uint16_t start_devid, + uint16_t end_devid, uint8_t setting) +{ + /* 4-byte IVHD structures must be aligned to the 4-byte boundary. */ + current = ALIGN_UP(current, 4); + ivrs_ivhd_generic_t *ivhd_range = (ivrs_ivhd_generic_t *)current; + + /* Create the start range IVHD entry */ + ivhd_range->type = IVHD_DEV_4_BYTE_START_RANGE; + ivhd_range->dev_id = start_devid; + ivhd_range->dte_setting = setting; + current += sizeof(ivrs_ivhd_generic_t); + + /* Create the end range IVHD entry */ + ivhd_range = (ivrs_ivhd_generic_t *)current; + ivhd_range->type = IVHD_DEV_4_BYTE_END_RANGE; + ivhd_range->dev_id = end_devid; + ivhd_range->dte_setting = setting; + current += sizeof(ivrs_ivhd_generic_t); + + return current; +} + +static unsigned long add_ivhd_dev_entry(struct device *parent, struct device *dev, + unsigned long *current, uint8_t type, uint8_t data) { - uint8_t *p; - p = (uint8_t *) *current; - - if (type == 0x2) { - /* Entry type */ - p[0] = type; - /* Device */ - p[1] = dev->path.pci.devfn; - /* Bus */ - p[2] = dev->bus->secondary; - /* Data */ - p[3] = data; - /* [4:7] Padding */ - p[4] = 0x0; - p[5] = 0x0; - p[6] = 0x0; - p[7] = 0x0; - *length += 8; - *current += 8; - } else if (type == 0x42) { - /* Entry type */ - p[0] = type; - /* Device */ - p[1] = dev->path.pci.devfn; - /* Bus */ - p[2] = dev->bus->secondary; - /* Data */ - p[3] = 0x0; - /* Reserved */ - p[4] = 0x0; - /* Device */ - p[5] = parent->path.pci.devfn; - /* Bus */ - p[6] = parent->bus->secondary; - /* Reserved */ - p[7] = 0x0; - *length += 8; - *current += 8; + if (type == IVHD_DEV_4_BYTE_SELECT) { + /* 4-byte IVHD structures must be aligned to the 4-byte boundary. */ + *current = ALIGN_UP(*current, 4); + ivrs_ivhd_generic_t *ivhd_entry = (ivrs_ivhd_generic_t *)*current; + + ivhd_entry->type = type; + ivhd_entry->dev_id = dev->path.pci.devfn | (dev->bus->secondary << 8); + ivhd_entry->dte_setting = data; + *current += sizeof(ivrs_ivhd_generic_t); + } else if (type == IVHD_DEV_8_BYTE_ALIAS_SELECT) { + /* 8-byte IVHD structures must be aligned to the 8-byte boundary. */ + *current = ALIGN_UP(*current, 8); + ivrs_ivhd_alias_t *ivhd_entry = (ivrs_ivhd_alias_t *)*current; + + ivhd_entry->type = type; + ivhd_entry->dev_id = dev->path.pci.devfn | (dev->bus->secondary << 8); + ivhd_entry->dte_setting = data; + ivhd_entry->reserved1 = 0; + ivhd_entry->reserved2 = 0; + ivhd_entry->source_dev_id = parent->path.pci.devfn | + (parent->bus->secondary << 8); + *current += sizeof(ivrs_ivhd_alias_t); } + + return *current; } -static void add_ivrs_device_entries(struct device *parent, struct device *dev, +static void ivrs_add_device_or_bridge(struct device *parent, struct device *dev, + unsigned long *current, uint16_t *ivhd_length) +{ + unsigned int header_type, is_pcie; + unsigned long current_backup; + + header_type = dev->hdr_type & 0x7f; + is_pcie = pci_find_capability(dev, PCI_CAP_ID_PCIE); + + if (((header_type == PCI_HEADER_TYPE_NORMAL) || + (header_type == PCI_HEADER_TYPE_BRIDGE)) && is_pcie) { + /* Device or Bridge is PCIe */ + current_backup = *current; + add_ivhd_dev_entry(parent, dev, current, IVHD_DEV_4_BYTE_SELECT, 0x0); + *ivhd_length += (*current - current_backup); + } else if ((header_type == PCI_HEADER_TYPE_NORMAL) && !is_pcie) { + /* Device is legacy PCI or PCI-X */ + current_backup = *current; + add_ivhd_dev_entry(parent, dev, current, IVHD_DEV_8_BYTE_ALIAS_SELECT, 0x0); + *ivhd_length += (*current - current_backup); + } +} + +static void add_ivhd_device_entries(struct device *parent, struct device *dev, unsigned int depth, int linknum, int8_t *root_level, - unsigned long *current, uint16_t *length) + unsigned long *current, uint16_t *ivhd_length) { struct device *sibling; struct bus *link; - unsigned int header_type; - unsigned int is_pcie; + + if (!root_level) { + root_level = malloc(sizeof(int8_t)); + *root_level = -1; + } if (dev->path.type == DEVICE_PATH_PCI) { @@ -489,154 +573,158 @@ static void add_ivrs_device_entries(struct device *parent, struct device *dev, *root_level = depth; if ((*root_level != -1) && (dev->enabled)) { - if (depth == *root_level) { - if (dev->path.pci.devfn == (0x14 << 3)) { - /* SMBUS controller */ - add_ivhd_dev_entry(parent, dev, current, length, 0x2, 0x97); - } else if (dev->path.pci.devfn != 0x2 && - dev->path.pci.devfn < (0x2 << 3)) { - /* FCH control device */ - } else { - /* Other devices */ - add_ivhd_dev_entry(parent, dev, current, length, 0x2, 0x0); - } - } else { - header_type = dev->hdr_type & 0x7f; - is_pcie = pci_find_capability(dev, PCI_CAP_ID_PCIE); - if (((header_type == PCI_HEADER_TYPE_NORMAL) || - (header_type == PCI_HEADER_TYPE_BRIDGE)) - && is_pcie) { - /* Device or Bridge is PCIe */ - add_ivhd_dev_entry(parent, dev, current, length, 0x2, 0x0); - } else if ((header_type == PCI_HEADER_TYPE_NORMAL) && - !is_pcie) { - add_ivhd_dev_entry(parent, dev, current, length, 0x42, 0x0); - /* Device is legacy PCI or PCI-X */ - } - } + if (depth != *root_level) + ivrs_add_device_or_bridge(parent, dev, current, ivhd_length); } } for (link = dev->link_list; link; link = link->next) for (sibling = link->children; sibling; sibling = sibling->sibling) - add_ivrs_device_entries(dev, sibling, depth + 1, depth, - root_level, current, length); + add_ivhd_device_entries(dev, sibling, depth + 1, depth, root_level, + current, ivhd_length); + + free(root_level); } -unsigned long acpi_fill_ivrs_ioapic(acpi_ivrs_t *ivrs, unsigned long current) +#define IOMMU_MMIO32(x) (*((volatile uint32_t *)(x))) +#define EFR_SUPPORT BIT(27) + +static unsigned long acpi_fill_ivrs11(unsigned long current, acpi_ivrs_t *ivrs_agesa) { - uint8_t *p; - - uint32_t apicid_sb800; - uint32_t apicid_northbridge; - - apicid_sb800 = CONFIG_MAX_CPUS; - apicid_northbridge = CONFIG_MAX_CPUS + 1; - - /* Describe NB IOAPIC */ - p = (uint8_t *)current; - p[0] = 0x48; /* Entry type */ - p[1] = 0; /* Device */ - p[2] = 0; /* Bus */ - p[3] = 0x0; /* Data */ - p[4] = apicid_northbridge; /* IOAPIC ID */ - p[5] = 0x0; /* Device 0 Function 0 */ - p[6] = 0x0; /* Northbridge bus */ - p[7] = 0x1; /* Variety */ - current += 8; - - /* Describe SB IOAPIC */ - p = (uint8_t *)current; - p[0] = 0x48; /* Entry type */ - p[1] = 0; /* Device */ - p[2] = 0; /* Bus */ - p[3] = 0xd7; /* Data */ - p[4] = apicid_sb800; /* IOAPIC ID */ - p[5] = 0x14 << 3; /* Device 0x14 Function 0 */ - p[6] = 0x0; /* Southbridge bus */ - p[7] = 0x1; /* Variety */ - current += 8; + acpi_ivrs_ivhd11_t *ivhd_11; + unsigned long current_backup; + + /* + * These devices should be already found by previous function. + * Do not perform NULL checks. + */ + struct device *nb_dev = pcidev_on_root(0, 0); + struct device *iommu_dev = pcidev_on_root(0, 2); + + /* + * In order to utilize all features, firmware should expose type 11h + * IVHD which supersedes the type 10h. + */ + memset((void *)current, 0, sizeof(acpi_ivrs_ivhd11_t)); + ivhd_11 = (acpi_ivrs_ivhd11_t *)current; + + /* Enable EFR */ + ivhd_11->type = IVHD_BLOCK_TYPE_FULL__FIXED; + /* For type 11h bits 6 and 7 are reserved */ + ivhd_11->flags = ivrs_agesa->ivhd.flags & 0x3f; + ivhd_11->length = sizeof(struct acpi_ivrs_ivhd_11); + /* BDF :00.2 */ + ivhd_11->device_id = 0x02 | (nb_dev->bus->secondary << 8); + /* PCI Capability block 0x40 (type 0xf, "Secure device") */ + ivhd_11->capability_offset = 0x40; + ivhd_11->iommu_base_low = ivrs_agesa->ivhd.iommu_base_low; + ivhd_11->iommu_base_high = ivrs_agesa->ivhd.iommu_base_high; + ivhd_11->pci_segment_group = 0x0000; + ivhd_11->iommu_info = ivrs_agesa->ivhd.iommu_info; + ivhd_11->iommu_attributes.perf_counters = + (IOMMU_MMIO32(ivhd_11->iommu_base_low + 0x4000) >> 7) & 0xf; + ivhd_11->iommu_attributes.perf_counter_banks = + (IOMMU_MMIO32(ivhd_11->iommu_base_low + 0x4000) >> 12) & 0x3f; + ivhd_11->iommu_attributes.msi_num_ppr = + (pci_read_config32(iommu_dev, ivhd_11->capability_offset + 0x10) >> 27) & 0x1f; + + if (pci_read_config32(iommu_dev, ivhd_11->capability_offset) & EFR_SUPPORT) { + ivhd_11->efr_reg_image_low = IOMMU_MMIO32(ivhd_11->iommu_base_low + 0x30); + ivhd_11->efr_reg_image_high = IOMMU_MMIO32(ivhd_11->iommu_base_low + 0x34); + } + + current += sizeof(acpi_ivrs_ivhd11_t); + + /* Now repeat all the device entries from type 10h */ + current_backup = current; + current = ivhd_dev_range(current, PCI_DEVFN(1, 0), PCI_DEVFN(0x1f, 6), 0); + ivhd_11->length += (current - current_backup); + add_ivhd_device_entries(NULL, all_devices, 0, -1, NULL, ¤t, &ivhd_11->length); + + /* Describe HPET */ + current_backup = current; + current = ivhd_describe_hpet(current); + ivhd_11->length += (current - current_backup); + + /* Describe IOAPICs */ + current_backup = current; + current = acpi_fill_ivrs_ioapic(ivrs_agesa, current); + ivhd_11->length += (current - current_backup); return current; } static unsigned long acpi_fill_ivrs(acpi_ivrs_t *ivrs, unsigned long current) { - uint8_t *p; acpi_ivrs_t *ivrs_agesa; + unsigned long current_backup; - struct device *nb_dev = pcidev_on_root(0x0, 0); + struct device *nb_dev = pcidev_on_root(0, 0); if (!nb_dev) { - printk(BIOS_WARNING, "%s: G-series northbridge device not present!\n", __func__); printk(BIOS_WARNING, "%s: IVRS table not generated...\n", __func__); return (unsigned long)ivrs; } + struct device *iommu_dev = pcidev_on_root(0, 2); + + if (!iommu_dev) { + printk(BIOS_WARNING, "%s: IOMMU device not found\n", __func__); + + return (unsigned long)ivrs; + } - /* obtain IOMMU base address */ ivrs_agesa = agesawrapper_getlateinitptr(PICK_IVRS); if (ivrs_agesa != NULL) { - ivrs->iv_info = 0x0; - /* Maximum supported virtual address size */ - ivrs->iv_info |= (0x40 << 15); - /* Maximum supported physical address size */ - ivrs->iv_info |= (0x30 << 8); - /* Guest virtual address width */ - ivrs->iv_info |= (0x2 << 5); - - ivrs->ivhd.type = 0x10; - ivrs->ivhd.flags = 0x0e; - /* Enable ATS support */ - ivrs->ivhd.flags |= 0x10; + ivrs->iv_info = ivrs_agesa->iv_info; + ivrs->ivhd.type = IVHD_BLOCK_TYPE_LEGACY__FIXED; + ivrs->ivhd.flags = ivrs_agesa->ivhd.flags; ivrs->ivhd.length = sizeof(struct acpi_ivrs_ivhd); /* BDF :00.2 */ - ivrs->ivhd.device_id = 0x2 | (nb_dev->bus->secondary << 8); - /* Capability block 0x40 (type 0xf, "Secure device") */ + ivrs->ivhd.device_id = 0x02 | (nb_dev->bus->secondary << 8); + /* PCI Capability block 0x40 (type 0xf, "Secure device") */ ivrs->ivhd.capability_offset = 0x40; ivrs->ivhd.iommu_base_low = ivrs_agesa->ivhd.iommu_base_low; ivrs->ivhd.iommu_base_high = ivrs_agesa->ivhd.iommu_base_high; - ivrs->ivhd.pci_segment_group = 0x0; - ivrs->ivhd.iommu_info = 0x0; - ivrs->ivhd.iommu_info |= (0x13 << 8); - /* use only performance counters related bits: - * PNCounters[16:13] and - * PNBanks[22:17], - * otherwise 0 */ - ivrs->ivhd.iommu_feature_info = - ivrs_agesa->ivhd.iommu_feature_info & 0x7fe000; + ivrs->ivhd.pci_segment_group = 0x0000; + ivrs->ivhd.iommu_info = ivrs_agesa->ivhd.iommu_info; + ivrs->ivhd.iommu_feature_info = ivrs_agesa->ivhd.iommu_feature_info; + /* Enable EFR if supported */ + if (pci_read_config32(iommu_dev, ivrs->ivhd.capability_offset) & EFR_SUPPORT) + ivrs->iv_info |= IVINFO_EFR_SUPPORTED; } else { printk(BIOS_WARNING, "%s: AGESA returned NULL IVRS\n", __func__); return (unsigned long)ivrs; } + /* + * Add all possible PCI devices on bus 0 that can generate transactions + * processed by IOMMU. Start with device 00:01.0 since IOMMU does not + * translate transactions generated by itself. + */ + current_backup = current; + current = ivhd_dev_range(current, PCI_DEVFN(1, 0), PCI_DEVFN(0x1f, 6), 0); + ivrs->ivhd.length += (current - current_backup); + add_ivhd_device_entries(NULL, all_devices, 0, -1, NULL, ¤t, &ivrs->ivhd.length); + /* Describe HPET */ - p = (uint8_t *)current; - p[0] = 0x48; /* Entry type */ - p[1] = 0; /* Device */ - p[2] = 0; /* Bus */ - p[3] = 0xd7; /* Data */ - p[4] = 0x0; /* HPET number */ - p[5] = 0x14 << 3; /* HPET device */ - p[6] = nb_dev->bus->secondary; /* HPET bus */ - p[7] = 0x2; /* Variety */ - ivrs->ivhd.length += 8; - current += 8; - - /* Describe PCI devices */ - int8_t root_level = -1; - add_ivrs_device_entries(NULL, all_devices, 0, -1, &root_level, ¤t, - &ivrs->ivhd.length); + current_backup = current; + current = ivhd_describe_hpet(current); + ivrs->ivhd.length += (current - current_backup); /* Describe IOAPICs */ - unsigned long prev_current = current; - current = acpi_fill_ivrs_ioapic(ivrs, current); - ivrs->ivhd.length += (current - prev_current); + current_backup = current; + current = acpi_fill_ivrs_ioapic(ivrs_agesa, current); + ivrs->ivhd.length += (current - current_backup); - return current; + /* If EFR is not supported, IVHD type 11h is reserved */ + if (!(ivrs->iv_info & IVINFO_EFR_SUPPORTED)) + return current; + + return acpi_fill_ivrs11(current, ivrs_agesa); } static void northbridge_fill_ssdt_generator(struct device *device) -- cgit v1.2.3