aboutsummaryrefslogtreecommitdiffstats
path: root/bus.c
diff options
context:
space:
mode:
authorJoshua Bakita <jbakita@cs.unc.edu>2024-04-08 14:46:57 -0400
committerJoshua Bakita <jbakita@cs.unc.edu>2024-04-08 14:46:57 -0400
commit6a89ea5912619af9a9682b9ab39be43f411fb984 (patch)
treef120a12655b8301696dc7cc64e624d49e9c540d0 /bus.c
parentac60151ea0a4a1f3882fde3c486af870029b7977 (diff)
Put PRAMIN-pointer and BAR2-page-table-PRAMIN-pointer logic into bus.c
Derived from logic in `runlist.c` and `mmu.c`. The new functions are not directly used in this commit.
Diffstat (limited to 'bus.c')
-rw-r--r--bus.c130
1 files changed, 130 insertions, 0 deletions
diff --git a/bus.c b/bus.c
new file mode 100644
index 0000000..802b6df
--- /dev/null
+++ b/bus.c
@@ -0,0 +1,130 @@
1/* Copyright 2024 Joshua Bakita
2 * Helpers for dealing with the PCIe and platform bus
3 *
4 * = Design Notes =
5 * We have to use PRAMIN to access the BAR2 page table. While it's typically also
6 * mapped into BAR2, we have no way to know where without the table. If the table
7 * changes, the new sections will have new mappings in BAR2, repeating the
8 * problem, and making caching insufficient.
9 *
10 * = Terms =
11 * VRAM/VID_MEM: Video RAM; Addresses to physical frames in the on-GPU RAM.
12 * SYS_MEM: System Memory; "Bus addresses"; Addresses which can be presented to
13 * the PCIe Host for resolution. On x86_64 without an IOMMU, these are
14 * just physical addresses, but may be I/O Virtual Addresses (IOVAs)
15 * or translated via an I/O MMU on other platforms; DMA Addresses.
16 * PEER: Addresses to RAM on another GPU.
17 */
18#include <linux/printk.h> // For printk()
19#include <asm/errno.h> // For error defines
20#include <asm/io.h> // For readl()
21
22#include "nvdebug.h"
23
24/* Obtain the PRAMIN offset at which `addr` can be accessed
25 @param addr Address to find
26 @param target Which address space to use (VRAM, SYS_MEM, PEER(?))
27 @return positive offset or -EINVAL on invalid arguments
28
29 Note: Will move the PRAMIN window to accomodate the request. Only guarantees
30 that the surrounding 64KiB window will be accessible.
31 Note: Moving the PRAMIN window will cause problems if it races with driver
32 code that tries to do the same, or expects the window not to move.
33 Bugs: Untested on PEER.
34*/
35int addr_to_pramin_mut(struct nvdebug_state *g,
36 uint64_t addr, enum INST_TARGET target) {
37 bar0_window_t window;
38 uint64_t pramin_base;
39 // For us, accuracy and robustness is more important than speed
40 // Check that the address is valid (49 bits are addressable on-GPU, but
41 // PRAMIN only supports up to 40 bits).
42 if (addr & ~0x000000ffffffffff) {
43 printk(KERN_ERR "[nvdebug] Invalid address %llx passed to %s!\n",
44 addr, __func__);
45 return -EINVAL;
46 }
47 window.raw = nvdebug_readl(g, NV_PBUS_BAR0_WINDOW);
48 if (window.target != target)
49 goto relocate;
50 pramin_base = ((uint64_t)window.base) << 16;
51 if (addr < pramin_base || addr > pramin_base + NV_PRAMIN_LEN)
52 goto relocate;
53 return addr - pramin_base; // Guaranteed to be < 1MiB, so safe for int
54relocate:
55 printk(KERN_INFO "[nvdebug] Moving PRAMIN win from base %llx to %llx to accomodate %#018llx\n", pramin_base, (addr >> 16) << 16, addr);
56 // Move PRAMIN window to a 64KiB-aligned address
57 window.base = (u32)(addr >> 16); // Safe, due to above range check
58 window.target = target;
59 nvdebug_writel(g, NV_PBUS_BAR0_WINDOW, window.raw);
60 return (int)(addr & 0xffffull);
61}
62
63
64/* Get a persistent pointer to the page directory base
65 @param pdb Dereferencable pointer to the zeroeth entry of top-level page
66 directory (PD3) for the BAR2 register region.
67 Note: The returned pointer will be into the PRAMIN space. If the PRAMIN
68 window is moved to a region that does not cover the BAR2 page table,
69 this ***will move the window***.
70 Note: Even if the page table is located in SYS_MEM, we route reads/writes via
71 PRAMIN. This ensures that we always see what the GPU sees, and that
72 includes any passes through I/O MMUs or IOVA spaces.
73*/
74int get_bar2_pdb(struct nvdebug_state *g, void **pdb, bool *is_v2_pdb) {
75 static void* cached_pdb = NULL;
76 static bool cached_is_v2_pdb = false;
77 static long pd_hash = 0;
78 int ret;
79 bar_config_block_t bar2_block;
80 page_dir_config_t pd_config;
81 uint64_t pdb_vram;
82
83 // Use cached base as long as it's still pointing to the same thing
84 if (cached_pdb && readl(cached_pdb) == pd_hash) {
85 *pdb = cached_pdb;
86 *is_v2_pdb = cached_is_v2_pdb;
87 return 0;
88 }
89
90 if (!g->bar2)
91 return -ENXIO;
92
93 // BAR2 has its own instance block (typically in VRAM) which contains the
94 // Page Directory Base (PDB), a pointer to a page directory/table
95 // hierarchy used to translate BAR2 offsets to VRAM or SYS_MEM addresses.
96
97 // Determine location of BAR2 instance block
98 if ((bar2_block.raw = nvdebug_readl(g, NV_PBUS_BAR2_BLOCK)) == -1) {
99 printk(KERN_ERR "[nvdebug] Unable to read BAR2/3 configuration! BAR2/3 inaccessible.\n");
100 return -ENOTSUPP;
101 }
102 printk(KERN_INFO "[nvdebug] BAR2 inst block @ %llx in %s's %s address space.\n", ((u64)bar2_block.ptr) << 12, target_to_text(bar2_block.target), bar2_block.is_virtual ? "virtual" : "physical");
103 // Setup PRAMIN to point at the BAR2 instance block
104 if ((ret = addr_to_pramin_mut(g, (uint64_t)bar2_block.ptr << 12, bar2_block.target)) < 0) {
105 printk(KERN_ERR "[nvdebug] Invalid BAR2/3 Instance Block configuration! BAR2/3 inaccessible.\n");
106 return ret;
107 }
108 printk(KERN_INFO "[nvdebug] BAR2 inst block at off %x in PRAMIN\n", ret);
109 // Pull the page directory base configuration from the instance block
110 if ((pd_config.raw = nvdebug_readq(g, NV_PRAMIN + ret + NV_PRAMIN_PDB_CONFIG_OFF)) == -1) {
111 printk(KERN_ERR "[nvdebug] Unable to read BAR2/3 PDB configuration! BAR2/3 inaccessible.\n");
112 return -ENOTSUPP;
113 }
114 pdb_vram = pd_config.page_dir_hi;
115 pdb_vram <<= 20;
116 pdb_vram |= pd_config.page_dir_lo;
117 pdb_vram <<= 12;
118 printk(KERN_INFO "[nvdebug] BAR2 PDB @ %llx (config raw: %llx)\n", pdb_vram, pd_config.raw);
119 // Setup PRAMIN to point at the page directory
120 if ((ret = addr_to_pramin_mut(g, pdb_vram, pd_config.target)) < 0) {
121 printk(KERN_ERR "[nvdebug] Invalid BAR2/3 PDB configuration! BAR2/3 inaccessible.\n");
122 return ret;
123 }
124
125 *pdb = cached_pdb = g->regs + NV_PRAMIN + ret;
126 pd_hash = readl(cached_pdb);
127 *is_v2_pdb = cached_is_v2_pdb = pd_config.is_ver2;
128
129 return 0;
130}