summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 13:58:55 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-01 13:58:55 -0400
commitf7f8b19b200436f92f78f73b5b6ece6410f94d57 (patch)
tree60b119b90e992e87921f9d7930ac11dbfa0c0bf5 /kernel
parent4ceb08c2e4d07edc1ebed46b1ed2a2bce00f24e7 (diff)
downloadopen-iov-f7f8b19b200436f92f78f73b5b6ece6410f94d57.tar.gz
open-iov-f7f8b19b200436f92f78f73b5b6ece6410f94d57.zip
gsp-phys: program the GPU timer before the GSP kick
Diffstat (limited to 'kernel')
-rw-r--r--kernel/gsp_phys_booter.c85
1 files changed, 79 insertions, 6 deletions
diff --git a/kernel/gsp_phys_booter.c b/kernel/gsp_phys_booter.c
index 9a170c9..68b71ca 100644
--- a/kernel/gsp_phys_booter.c
+++ b/kernel/gsp_phys_booter.c
@@ -39,6 +39,7 @@
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/jiffies.h>
+#include <linux/timekeeping.h>
#include <linux/byteorder/generic.h>
#include <linux/pci_regs.h>
#include <linux/vgaarb.h>
@@ -298,6 +299,19 @@ bool gsp_phys_boot_requested(void)
#define GSP_PHYS_GFW_BOOT 0x118234
#define GSP_PHYS_BOOTER_HALT_TIMEOUT_MS 5000
+/*
+ * PTIMER system timer (BAR0 0x9000..0x9fff). TU10x bare metal programs the
+ * timer from the host wall clock before starting GSP-RM (GV100 HAL,
+ * timer/arch/volta/timer_gv100.c): TIME_1 gets the high and TIME_0 the low
+ * 32 bits, and the TIME_0 write is the load trigger. The write is guarded by
+ * TIME_PRIV_LEVEL_MASK.WRITE_PROTECTION_LEVEL0 (bit 4). Published register
+ * offsets: published/volta/gv100/dev_timer.h.
+ */
+#define GSP_PHYS_PTIMER_TIME_0 0x00009400
+#define GSP_PHYS_PTIMER_TIME_1 0x00009410
+#define GSP_PHYS_PTIMER_TIME_PRIV_LEVEL_MASK 0x00009430
+#define GSP_PHYS_PTIMER_WRITE_PROT_LEVEL0 BIT(4)
+
/* libos init argument ids: the name bytes packed big-endian into the u64. */
#define GSP_PHYS_LIBOS_ID_LOGINIT 0x4c4f47494e4954ULL /* "LOGINIT" */
#define GSP_PHYS_LIBOS_ID_LOGRM 0x4c4f474dULL /* "LOGRM" */
@@ -2895,14 +2909,18 @@ int gsp_phys_queue_tx_submit(void __iomem *bar0, u32 function,
* (osinit.c:955: primary_vga || client_managed_console; the dGPU on this
* host is a secondary device).
* - Everything else is a documented conservative zero (hypervisorType,
- * SR-IOV VF info, grid, ACPI method data, system mux, POR flags, timer
- * offset, passthru, console memory, dynamic-granularity arrays, TDR,
+ * 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).
+ * - 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.
*/
static void gsp_phys_preinit_fill_system_info(
- struct gsp_phys_gsp_system_info *info, struct pci_dev *pdev)
+ struct gsp_phys_gsp_system_info *info, struct pci_dev *pdev,
+ u64 sys_timer_offset_ns)
{
struct pci_dev *bridge;
u64 bars[3] = { 0, 0, 0 };
@@ -2978,6 +2996,55 @@ static void gsp_phys_preinit_fill_system_info(
info->b_is_primary = (vga_default_device() == pdev) ? 1 : 0;
info->b_clock_boost_supported = 1;
info->b_route_disp_intrs_to_cpu = 0; /* Linux: NV_FALSE */
+ info->sys_timer_offset_ns = cpu_to_le64(sys_timer_offset_ns);
+}
+
+/*
+ * Program the GPU system timer exactly where the reference does: after GFW
+ * boot is complete (kgspWaitForGfwBootOk_HAL) and before the GSP-RM image is
+ * started, "to avoid PLM collisions" and because the firmware consumes the
+ * updated GPU time (kernel_gsp.c:3676-3683). Bare-metal TU10x dispatches to
+ * the GV100 HAL (generated/g_objtmr_nvoc.c -> tmrSetCurrentTime_GV100,
+ * timer/arch/volta/timer_gv100.c):
+ *
+ * 1. the write is only allowed while PTIMER_TIME_PRIV_LEVEL_MASK.
+ * WRITE_PROTECTION_LEVEL0 (bit 4) is enabled; otherwise the reference
+ * asserts and returns NV_ERR_PRIV_SEC_VIOLATION, and its caller ignores
+ * the status - so a failed check is logged and the boot continues;
+ * 2. the host wall clock is read with microsecond resolution
+ * (osGetCurrentTime -> ktime_get_real_ts64; ns = (seconds * 1e6 +
+ * useconds) * 1000);
+ * 3. TIME_1 = high 32 bits, then the TIME_0 write triggers the load.
+ *
+ * The GV100/TU10x HAL computes no pTmr->sysTimerOffsetNs: that secure-timer
+ * offset is GH100-only (timer_gh100.c), where PTIMER stays a counter and
+ * GSP-RM adds the offset. Here PTIMER itself holds wall time, so the value
+ * delivered in GspSystemInfo is 0. Returns that value.
+ */
+static u64 gsp_phys_timer_set_current_time(void __iomem *bar0)
+{
+ struct timespec64 ts;
+ u32 plm, seconds, useconds;
+ u64 ns;
+
+ plm = readl(bar0 + GSP_PHYS_PTIMER_TIME_PRIV_LEVEL_MASK);
+ if (!(plm & GSP_PHYS_PTIMER_WRITE_PROT_LEVEL0)) {
+ pr_warn("gsp-phys-boot: tmrSetCurrentTime skipped: PTIMER level-0 write protection is not enabled (PLM=0x%08x)\n",
+ plm);
+ return 0;
+ }
+
+ ktime_get_real_ts64(&ts);
+ seconds = (u32)ts.tv_sec;
+ useconds = (u32)(ts.tv_nsec / NSEC_PER_USEC);
+ ns = ((u64)seconds * 1000000 + useconds) * 1000;
+
+ writel(upper_32_bits(ns), bar0 + GSP_PHYS_PTIMER_TIME_1);
+ writel(lower_32_bits(ns), bar0 + GSP_PHYS_PTIMER_TIME_0);
+
+ pr_info("gsp-phys-boot: tmrSetCurrentTime: PTIMER_TIME_1/0 = 0x%08x%08x (%llu ns wall clock, %u s + %u us); sysTimerOffsetNs=0 (GV100/TU10x HAL programs PTIMER directly)\n",
+ upper_32_bits(ns), lower_32_bits(ns), ns, seconds, useconds);
+ return 0;
}
/*
@@ -2989,17 +3056,22 @@ static void gsp_phys_preinit_fill_system_info(
* GSP-RM server's function table entries return "nothing" and no reply is
* expected. The existing TX producer already has submit-only semantics, so
* both records go through gsp_phys_queue_tx_submit with increasing seqNum.
+ * The timer step runs first, mirroring the reference order in kgspInitRm
+ * (timer, then init args, then kgspQueueAsyncInitRpcs, then the boot).
*/
static int gsp_phys_preinit_rpcs(void __iomem *bar0, struct pci_dev *pdev)
{
struct gsp_phys_gsp_system_info *info;
+ u64 sys_timer_offset_ns;
int ret;
+ sys_timer_offset_ns = gsp_phys_timer_set_current_time(bar0);
+
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
- gsp_phys_preinit_fill_system_info(info, pdev);
- pr_info("gsp-phys-boot: pre-boot GSP_SET_SYSTEM_INFO (fn%u) prepared: gpuPhys=0x%llx fb=0x%llx inst=0x%llx io=0x%llx dbf=0x%llx mirror=0x%x/0x%x ids=%04x:%04x:%02x maxUserVa=0x%llx pageSize=0x%llx flr=%u behindBridge=%u l1ss=0x%x lnkCap=0x%x primary=%u\n",
+ gsp_phys_preinit_fill_system_info(info, pdev, sys_timer_offset_ns);
+ pr_info("gsp-phys-boot: pre-boot GSP_SET_SYSTEM_INFO (fn%u) prepared: gpuPhys=0x%llx fb=0x%llx inst=0x%llx io=0x%llx dbf=0x%llx mirror=0x%x/0x%x ids=%04x:%04x:%02x maxUserVa=0x%llx pageSize=0x%llx flr=%u behindBridge=%u l1ss=0x%x lnkCap=0x%x primary=%u sysTimerOffsetNs=0x%llx\n",
GSP_PHYS_RPC_FUNC_GSP_SET_SYSTEM_INFO,
le64_to_cpu(info->gpu_phys_addr),
le64_to_cpu(info->gpu_phys_fb_addr),
@@ -3015,7 +3087,8 @@ static int gsp_phys_preinit_rpcs(void __iomem *bar0, struct pci_dev *pdev)
info->b_gpu_behind_bridge,
le32_to_cpu(info->chipset_l1ss_enable),
le32_to_cpu(info->pcie_config_reg_link_cap),
- info->b_is_primary);
+ info->b_is_primary,
+ le64_to_cpu(info->sys_timer_offset_ns));
ret = gsp_phys_queue_tx_submit(bar0,
GSP_PHYS_RPC_FUNC_GSP_SET_SYSTEM_INFO,
info, sizeof(*info));