diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-01 15:35:35 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-01 15:35:35 -0400 |
| commit | fe3b01b72cba79d4b14875e6940a87987ad196a9 (patch) | |
| tree | 59fa3342e0f3b32772c3d64be96514a0c6119ba2 /kernel/gsp_phys_booter.c | |
| parent | 69f0c2dbc4cd5f61302ae8d70002e1a1eb96da52 (diff) | |
| download | open-iov-fe3b01b72cba79d4b14875e6940a87987ad196a9.tar.gz open-iov-fe3b01b72cba79d4b14875e6940a87987ad196a9.zip | |
gsp-phys: populate the chipset/CL block of the pre-boot system info
Fill the GspSystemInfo chipset fields the production client syncs before
the GSP boot instead of leaving them zero:
- clPdbProperties (bitmask in the client's clSyncWithGsp order): bit 0
(config space accessible, from the OS MCFG/ECAM availability), bit 7
and bit 21 (Intel Skylake-family upstream-port setup: ASPM POR list,
root-port nosnoop WAR), bit 13 (upstream port ASPM L1 capable), bit 27
(LTR supported+enabled up to the root port) and bit 32 (IO coherent).
PDB_PROP_CL_UNSUPPORTED_CHIPSET is never assigned by the client, so the
faithful unmatched-chipset value is 0.
- Chipset = 0 (CS_UNKNOWN) for a host bridge with no chipsetInfo[] entry,
the reference's no-match fallback.
- FHBBusInfo/chipsetIDInfo: 10-byte BUSINFO blobs of the first host
bridge of the GPU's domain (device, vendor, subdevice, subvendor,
revision), with the subsystem-capability fallback on chipsetIDInfo.
acpiMethodData is intentionally sent all-zero (bValid=0): no ACPI/AML
calls are made and every consumer tolerates the no-data encoding. All
device lookups use refcounted pci_get_device()/pci_dev_put().
Diffstat (limited to 'kernel/gsp_phys_booter.c')
| -rw-r--r-- | kernel/gsp_phys_booter.c | 270 |
1 files changed, 265 insertions, 5 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c index fb72cdc..8757a8e 100644 --- a/kernel/gsp_phys_booter.c +++ b/kernel/gsp_phys_booter.c @@ -43,6 +43,7 @@ #include <linux/byteorder/generic.h> #include <linux/pci_regs.h> #include <linux/vgaarb.h> +#include <linux/acpi.h> #include <asm/barrier.h> #include <asm/unaligned.h> @@ -2909,21 +2910,240 @@ int gsp_phys_queue_tx_submit(void __iomem *bar0, u32 function, * size = PAGE_SIZE, bIsPrimary from the VGA arbiter default device * (osinit.c:955: primary_vga || client_managed_console; the dGPU on this * host is a secondary device). + * - clPdbProperties/Chipset/FHBBusInfo/chipsetIDInfo: the chipset block, + * mirroring clSyncWithGsp_IMPL (chipset.c:779-834, 570.144 order) after + * the CPU-RM init that ran before this RPC (clInitPcie -> + * clUpdatePcieConfig -> objClInitPcieChipset, chipset_pcie.c:928-1081): + * * clPdbProperties bit indices are the clSyncWithGsp order; only the + * bits with a host-observable condition are set here (see + * GSP_PHYS_CL_PDB_* below). PDB_PROP_CL_IS_CHIPSET_IO_COHERENT is + * forced TRUE by clConstruct (chipset.c:54-58); + * PDB_PROP_CL_PCIE_CONFIG_ACCESSIBLE is set when the OS exposes the + * MCFG/ECAM table (chipset_pcie.c:349-356); the Intel Skylake-family + * upstream-port setup funcs set IS_CHIPSET_IN_ASPM_POR_LIST and + * ROOTPORT_NEEDS_NOSNOOP_WAR (chipset_info.c:92-94, + * chipset_pcie.c:3327-3370, 3373-3403); the LTR bit follows + * clCheckUpstreamLtrSupport (chipset_pcie.c:485-517, 532-609). + * * Chipset = CS_UNKNOWN = 0: the live host bridge 8086:9b44 is not in + * chipsetInfo[] (nvcst.h:105-298; the terminator at :298 carries + * CS_UNKNOWN == 0, nvdevid.h:542), which is exactly the + * clFindFHBAndGetChipsetInfoIndex no-match fallback + * (chipset.c:394-399, 406-466) - no chipset setup function runs. + * * FHBBusInfo/chipsetIDInfo: BUSINFO is {deviceID, vendorID, + * subdeviceID, subvendorID, revisionID} (g_chipset_nvoc.h:213-220, + * 10 bytes); both are copied from the first host bridge found + * (chipset.c:420-450); chipsetIDInfo additionally falls back to the + * Subsystem-ID PCI capability when the config subsystem vendor is 0 + * (chipset.c:442-450, 214-255). + * - acpiMethodData stays all-zero (bValid=0). The stock client copies + * pGpu->acpiMethodData, filled by RmInitAcpiMethods from live ACPI + * (_DOD plus the _DSM JT/MXDM/MXID/MXDS/caps queries, acpi_common.c: + * 1106-1170). This pass deliberately makes no ACPI/AML calls, so the + * "no method data" encoding is sent; every consumer of the field in the + * client tree tolerates bValid=0. * - Everything else is a documented conservative zero (hypervisorType, - * SR-IOV VF info, grid, ACPI method data, system mux, POR flags, - * passthru, console memory, dynamic-granularity arrays, TDR, - * stretch-vblank, route display interrupts; Windows/display/optional - * paths that are inactive in a bare-metal Linux headless boot). - * bClockBoostSupported is 1 (the task-specified value for this board). + * SR-IOV VF info, grid, system mux, POR flags, passthru, console memory, + * dynamic-granularity arrays, TDR, stretch-vblank, route display + * interrupts; Windows/display/optional paths that are inactive in a + * bare-metal Linux headless boot). bClockBoostSupported is 1 (the + * task-specified value for this board). * - sysTimerOffsetNs comes from the timer step that runs just before this * fill (gsp_phys_timer_set_current_time); TU10x/GV100 programs PTIMER * with wall time itself and leaves the offset 0. */ +/* + * clPdbProperties bit indices, in the exact order the 570.144 client packs + * them (clSyncWithGsp_IMPL, chipset.c:779-834) and the GSP-RM server unpacks + * them (clSyncPropertiesWithGsp_IMPL, the same order). Only the properties + * with a host-observable condition are named: + * bit 0 PDB_PROP_CL_PCIE_CONFIG_ACCESSIBLE + * bit 7 PDB_PROP_CL_IS_CHIPSET_IN_ASPM_POR_LIST + * bit 13 PDB_PROP_CL_ASPM_L1_UPSTREAM_PORT_SUPPORTED + * bit 21 PDB_PROP_CL_ROOTPORT_NEEDS_NOSNOOP_WAR + * bit 27 PDB_PROP_CL_UPSTREAM_LTR_SUPPORTED + * bit 32 PDB_PROP_CL_IS_CHIPSET_IO_COHERENT + * PDB_PROP_CL_UNSUPPORTED_CHIPSET (bit 31) is never assigned anywhere in the + * 570.144 client tree (only read by kbifStateInitLocked_IMPL, + * kernel_bif.c:124, where it makes BIF init fail), so the faithful value for + * an unmatched chipset is 0, not 1. + */ +#define GSP_PHYS_CL_PDB_PCIE_CONFIG_ACCESSIBLE 0 +#define GSP_PHYS_CL_PDB_IS_CHIPSET_IN_ASPM_POR_LIST 7 +#define GSP_PHYS_CL_PDB_ASPM_L1_UPSTREAM_PORT_SUPPORTED 13 +#define GSP_PHYS_CL_PDB_ROOTPORT_NEEDS_NOSNOOP_WAR 21 +#define GSP_PHYS_CL_PDB_UPSTREAM_LTR_SUPPORTED 27 +#define GSP_PHYS_CL_PDB_IS_CHIPSET_IO_COHERENT 32 + +/* + * First host bridge of the GPU's domain, the way + * clFindFHBAndGetChipsetInfoIndex (chipset.c:406-466) falls back to the + * first class 0x0600 (host bridge) entry in the cached bus topology after no + * chipsetInfo[] match. The topology is cached in bus/device/function order, + * so the lowest-BDF host bridge of the domain is the same device. + * pci_get_device() refcounts what it returns; the caller must + * pci_dev_put() the returned device. + */ +static struct pci_dev *gsp_phys_find_host_bridge(struct pci_dev *gpu) +{ + unsigned int domain = pci_domain_nr(gpu->bus); + struct pci_dev *dev, *found = NULL; + u16 best = 0xffff; + + for_each_pci_dev(dev) { + if (pci_domain_nr(dev->bus) != domain) + continue; + if ((dev->class >> 8) != PCI_CLASS_BRIDGE_HOST) + continue; + if (dev->devfn < best) { + if (found) + pci_dev_put(found); + found = pci_dev_get(dev); + best = dev->devfn; + } + } + + return found; +} + +/* + * Pack one BUSINFO (deviceID, vendorID, subdeviceID, subvendorID, + * revisionID; g_chipset_nvoc.h:213-220) into the 10-byte GspSystemInfo blob. + * The reference copies the values from the cached topology entry + * (chipset.c:420-440). The chipsetIDInfo copy has one extra fallback the + * FHBBusInfo copy does not: when the config subsystem vendor is 0 it reads + * the Subsystem-ID PCI capability instead (chipset.c:442-450, + * getSubsystemFromPCIECapabilities at :214-255). + */ +static void gsp_phys_pack_bus_info(u8 out[10], struct pci_dev *dev, + bool subsystem_cap_fallback) +{ + u16 subvendor = dev->subsystem_vendor; + u16 subdevice = dev->subsystem_device; + + if (subsystem_cap_fallback && subvendor == 0) { + int cap = pci_find_capability(dev, PCI_CAP_ID_SSVID); + + if (cap) { + u16 v = 0, d = 0; + + if (!pci_read_config_word(dev, cap + 4, &v) && + !pci_read_config_word(dev, cap + 6, &d)) { + subvendor = v; + subdevice = d; + } + } + } + + put_unaligned_le16(dev->device, out + 0); + put_unaligned_le16(dev->vendor, out + 2); + put_unaligned_le16(subdevice, out + 4); + put_unaligned_le16(subvendor, out + 6); + out[8] = dev->revision; + out[9] = 0; +} + +/* + * Host-side port of the CL PDB properties the CPU-RM would have set by the + * time it sends GSP_SET_SYSTEM_INFO (clConstruct, clInitPropertiesFromRegistry + * and clInitPcie -> clUpdatePcieConfig ran during RmInitAdapter before + * kgspInitRm; osinit.c:1808, 1835, 1855). Bits whose value comes from the + * registry are left 0: the default host profile does not enable the + * registry-only properties (DISABLE_BR03_FLOW_CONTROL, PCIE_FORCE_GEN2_ENABLE, + * DISABLE_IOMAP_WC, CL_ASLM_CFG; chipset.c:64-90, chipset_pcie.c:110-165). + */ +static u64 gsp_phys_cl_pdb_properties(struct pci_dev *pdev) +{ + struct pci_dev *bridge = pci_upstream_bridge(pdev); + struct pci_dev *port; + u64 props = 0; + u32 cap; + unsigned int depth; + + /* clConstruct_IMPL: PDB_PROP_CL_IS_CHIPSET_IO_COHERENT = TRUE. */ + props |= BIT_ULL(GSP_PHYS_CL_PDB_IS_CHIPSET_IO_COHERENT); + + /* + * objClInitPcieChipset sets PDB_PROP_CL_PCIE_CONFIG_ACCESSIBLE when + * the OS provides the MCFG table (chipset_pcie.c:349-356, + * clStorePcieConfigSpaceBaseFromMcfg at :4087). On Linux/x86 that is + * the ACPI MCFG table, present whenever ACPI is enabled. + */ + if (!acpi_disabled) + props |= BIT_ULL(GSP_PHYS_CL_PDB_PCIE_CONFIG_ACCESSIBLE); + + if (!bridge) + return props; + + /* + * upstreamPortInfo[] setup functions (chipset_info.c:90-104): + * 0x1901 is the Intel Skylake/Kabylake/Comet Lake CPU root port + * (Intel_Skylake_setupFunc, chipset_pcie.c:3327-3370); 0x9D18 is + * the Skylake-U PCH and 0xA117/0xA118 the Skylake-H PCH + * (chipset_pcie.c:3373-3403). All four set + * IS_CHIPSET_IN_ASPM_POR_LIST and ROOTPORT_NEEDS_NOSNOOP_WAR + * (0x1901 does not need the FHB device-ID switch for those two; + * the switch only adds ASPM-disabled bits for other host bridges). + */ + if (bridge->vendor == PCI_VENDOR_ID_INTEL) { + switch (bridge->device) { + case 0x1901: /* Skylake/Kabylake/Comet Lake CPU root port */ + case 0x9d18: /* Skylake-U PCH */ + case 0xa117: /* Skylake-H PCH */ + case 0xa118: /* Skylake-H PCH */ + props |= BIT_ULL(GSP_PHYS_CL_PDB_IS_CHIPSET_IN_ASPM_POR_LIST); + props |= BIT_ULL(GSP_PHYS_CL_PDB_ROOTPORT_NEEDS_NOSNOOP_WAR); + break; + default: + break; + } + } + + /* + * clUpdatePcieConfig -> clIsL1SupportedForUpstreamPort + * (chipset_pcie.c:581-616, called at :1011-1019): the upstream + * port's PCIe Link Capabilities ASPM-L1 bit. + */ + cap = 0; + if (!pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &cap) && + (cap & PCI_EXP_LNKCAP_ASPM_L1)) + props |= BIT_ULL(GSP_PHYS_CL_PDB_ASPM_L1_UPSTREAM_PORT_SUPPORTED); + + /* + * objClInitPcieChipset's LTR check (chipset_pcie.c:485-517) plus + * clCheckUpstreamLtrSupport (:532-609): every P2P bridge from the + * GPU up to and including the root port must advertise LTR in + * DevCap2 and have it enabled in DevCtrl2. The walk is bounded + * (the reference loops until it finds a root port; a malformed + * topology must not spin here). + */ + for (port = bridge, depth = 0; port && depth < 16; + depth++, port = pci_upstream_bridge(port)) { + u32 devcap2 = 0, devctl2 = 0; + u16 flags = 0; + + if (pcie_capability_read_dword(port, PCI_EXP_DEVCAP2, &devcap2) || + pcie_capability_read_dword(port, PCI_EXP_DEVCTL2, &devctl2) || + !(devcap2 & PCI_EXP_DEVCAP2_LTR) || + !(devctl2 & PCI_EXP_DEVCTL2_LTR_EN)) + break; + + if (!pcie_capability_read_word(port, PCI_EXP_FLAGS, &flags) && + ((flags & PCI_EXP_FLAGS_TYPE) >> 4) == + PCI_EXP_TYPE_ROOT_PORT) { + props |= BIT_ULL(GSP_PHYS_CL_PDB_UPSTREAM_LTR_SUPPORTED); + break; + } + } + + return props; +} + static void gsp_phys_preinit_fill_system_info( struct gsp_phys_gsp_system_info *info, struct pci_dev *pdev, u64 sys_timer_offset_ns) { struct pci_dev *bridge; + struct pci_dev *fhb; u64 bars[3] = { 0, 0, 0 }; u32 cap = 0; unsigned int i, nbars = 0; @@ -2992,6 +3212,32 @@ static void gsp_phys_preinit_fill_system_info( info->chipset_l1ss_enable = cpu_to_le32(cap); } + /* + * Chipset/CL block. FHBBusInfo and chipsetIDInfo are copied from the + * first host bridge (the reference's no-chipsets-match fallback, + * chipset.c:406-466); Chipset is CS_UNKNOWN because the live host + * bridge is not in chipsetInfo[] (nvcst.h terminator at :298); the + * CL PDB mask is the host-observable subset in the 570.144 + * clSyncWithGsp bit order. + */ + fhb = gsp_phys_find_host_bridge(pdev); + if (fhb) { + gsp_phys_pack_bus_info(info->fhb_bus_info, fhb, false); + gsp_phys_pack_bus_info(info->chipset_id_info, fhb, true); + pci_dev_put(fhb); + } + info->chipset = cpu_to_le32(0); /* CS_UNKNOWN (nvdevid.h:542) */ + info->cl_pdb_properties = cpu_to_le64(gsp_phys_cl_pdb_properties(pdev)); + + /* + * acpiMethodData (676 bytes) intentionally stays all-zero: bValid=0 + * is the "no ACPI method data" encoding. The stock client copies + * pGpu->acpiMethodData, which RmInitAcpiMethods fills from live ACPI + * (_DOD and the _DSM queries). This boot path makes no ACPI/AML + * calls (they can block); every consumer of the field in the client + * tree tolerates bValid=0. + */ + info->max_user_va = cpu_to_le64((u64)TASK_SIZE); info->host_page_size = cpu_to_le64((u64)PAGE_SIZE); info->b_is_primary = (vga_default_device() == pdev) ? 1 : 0; @@ -3090,6 +3336,20 @@ static int gsp_phys_preinit_rpcs(void __iomem *bar0, struct pci_dev *pdev) le32_to_cpu(info->pcie_config_reg_link_cap), info->b_is_primary, le64_to_cpu(info->sys_timer_offset_ns)); + pr_info("gsp-phys-boot: pre-boot GSP_SET_SYSTEM_INFO (fn%u) chipset block: clPdbProperties=0x%llx chipset=%u (CS_UNKNOWN) FHBBusInfo=%04x:%04x sub=%04x:%04x rev=%02x chipsetIDInfo=%04x:%04x sub=%04x:%04x rev=%02x acpiMethodData bValid=0 (676-byte body zeroed; host ACPI not read)\n", + GSP_PHYS_RPC_FUNC_GSP_SET_SYSTEM_INFO, + le64_to_cpu(info->cl_pdb_properties), + le32_to_cpu(info->chipset), + get_unaligned_le16(info->fhb_bus_info + 0), + get_unaligned_le16(info->fhb_bus_info + 2), + get_unaligned_le16(info->fhb_bus_info + 4), + get_unaligned_le16(info->fhb_bus_info + 6), + info->fhb_bus_info[8], + get_unaligned_le16(info->chipset_id_info + 0), + get_unaligned_le16(info->chipset_id_info + 2), + get_unaligned_le16(info->chipset_id_info + 4), + get_unaligned_le16(info->chipset_id_info + 6), + info->chipset_id_info[8]); ret = gsp_phys_queue_tx_submit(bar0, GSP_PHYS_RPC_FUNC_GSP_SET_SYSTEM_INFO, info, sizeof(*info)); |
