diff options
| author | Joshua Bakita <jbakita@cs.unc.edu> | 2024-04-08 14:46:57 -0400 |
|---|---|---|
| committer | Joshua Bakita <jbakita@cs.unc.edu> | 2024-04-08 14:46:57 -0400 |
| commit | 6a89ea5912619af9a9682b9ab39be43f411fb984 (patch) | |
| tree | f120a12655b8301696dc7cc64e624d49e9c540d0 | |
| parent | ac60151ea0a4a1f3882fde3c486af870029b7977 (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.
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | bus.c | 130 | ||||
| -rw-r--r-- | nvdebug.h | 3 |
3 files changed, 134 insertions, 1 deletions
| @@ -1,6 +1,6 @@ | |||
| 1 | obj-m += nvdebug.o | 1 | obj-m += nvdebug.o |
| 2 | nvdebug-objs = runlist_procfs.o device_info_procfs.o runlist.o mmu.o \ | 2 | nvdebug-objs = runlist_procfs.o device_info_procfs.o runlist.o mmu.o \ |
| 3 | nvdebug_entry.o copy_topology_procfs.o | 3 | nvdebug_entry.o bus.o copy_topology_procfs.o |
| 4 | KBUILD_CFLAGS += -DGIT_HASH=\"$(shell git --git-dir=$(PWD)/.git rev-parse --short HEAD)\" | 4 | KBUILD_CFLAGS += -DGIT_HASH=\"$(shell git --git-dir=$(PWD)/.git rev-parse --short HEAD)\" |
| 5 | # -mfentry above if not building due to mcount missing | 5 | # -mfentry above if not building due to mcount missing |
| 6 | 6 | ||
| @@ -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 | */ | ||
| 35 | int 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 | ||
| 54 | relocate: | ||
| 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 | */ | ||
| 74 | int 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 | } | ||
| @@ -1141,3 +1141,6 @@ static inline void nvdebug_writeq(struct nvdebug_state *s, u32 r, u64 v) { | |||
| 1141 | writeq_relaxed(v, s->regs + r); | 1141 | writeq_relaxed(v, s->regs + r); |
| 1142 | wmb(); | 1142 | wmb(); |
| 1143 | } | 1143 | } |
| 1144 | // Defined in bus.c | ||
| 1145 | int addr_to_pramin_mut(struct nvdebug_state *g, uint64_t addr, enum INST_TARGET target); | ||
| 1146 | int get_bar2_pdb(struct nvdebug_state *g, void **pdb, bool *is_v2_pdb); | ||
