summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 15:35:35 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 15:35:35 -0400
commitd12bb9e4d078bad57424f729b25f8b9fafd35af5 (patch)
treec517070d4433fc415613b2a66efeca0e618515ba /kernel
parentb6516f7c31cb360c394814422c2fd48522007c98 (diff)
downloadopen-iov-d12bb9e4d078bad57424f729b25f8b9fafd35af5.tar.gz
open-iov-d12bb9e4d078bad57424f729b25f8b9fafd35af5.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')
-rw-r--r--kernel/gsp_phys_booter.c270
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));