Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpu: nova-core: Create initial Gsp

The GSP requires several areas of memory to operate. Each of these have
their own simple embedded page tables. Set these up and map them for DMA
to/from GSP using CoherentAllocation's. Return the DMA handle describing
where each of these regions are for future use when booting GSP.

Signed-off-by: Alistair Popple <apopple@nvidia.com>
Co-developed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Message-ID: <20251110-gsp_boot-v9-4-8ae4058e3c0e@nvidia.com>

authored by

Alistair Popple and committed by
Alexandre Courbot
f38b4f10 89605daa

+197 -7
+1 -1
drivers/gpu/nova-core/gpu.rs
··· 231 231 232 232 sec2_falcon: Falcon::new(pdev.as_ref(), spec.chipset)?, 233 233 234 - gsp <- Gsp::new(), 234 + gsp <- Gsp::new(pdev)?, 235 235 236 236 _: { gsp.boot(pdev, bar, spec.chipset, gsp_falcon, sec2_falcon)? }, 237 237
+117 -6
drivers/gpu/nova-core/gsp.rs
··· 2 2 3 3 mod boot; 4 4 5 - use kernel::prelude::*; 5 + use kernel::{ 6 + device, 7 + dma::{ 8 + CoherentAllocation, 9 + DmaAddress, // 10 + }, 11 + dma_write, 12 + pci, 13 + prelude::*, 14 + transmute::AsBytes, // 15 + }; 6 16 7 17 mod fw; 8 18 ··· 21 11 LibosParams, // 22 12 }; 23 13 14 + use crate::{ 15 + gsp::fw::LibosMemoryRegionInitArgument, 16 + num, // 17 + }; 18 + 24 19 pub(crate) const GSP_PAGE_SHIFT: usize = 12; 25 20 pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT; 26 21 27 - /// GSP runtime data. 22 + /// Number of GSP pages to use in a RM log buffer. 23 + const RM_LOG_BUFFER_NUM_PAGES: usize = 0x10; 24 + 25 + /// Array of page table entries, as understood by the GSP bootloader. 26 + #[repr(C)] 27 + struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]); 28 + 29 + /// SAFETY: arrays of `u64` implement `AsBytes` and we are but a wrapper around one. 30 + unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {} 31 + 32 + impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> { 33 + /// Creates a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`. 34 + fn new(start: DmaAddress) -> Result<Self> { 35 + let mut ptes = [0u64; NUM_PAGES]; 36 + for (i, pte) in ptes.iter_mut().enumerate() { 37 + *pte = start 38 + .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT) 39 + .ok_or(EOVERFLOW)?; 40 + } 41 + 42 + Ok(Self(ptes)) 43 + } 44 + } 45 + 46 + /// The logging buffers are byte queues that contain encoded printf-like 47 + /// messages from GSP-RM. They need to be decoded by a special application 48 + /// that can parse the buffers. 28 49 /// 29 - /// This is an empty pinned placeholder for now. 50 + /// The 'loginit' buffer contains logs from early GSP-RM init and 51 + /// exception dumps. The 'logrm' buffer contains the subsequent logs. Both are 52 + /// written to directly by GSP-RM and can be any multiple of GSP_PAGE_SIZE. 53 + /// 54 + /// The physical address map for the log buffer is stored in the buffer 55 + /// itself, starting with offset 1. Offset 0 contains the "put" pointer (pp). 56 + /// Initially, pp is equal to 0. If the buffer has valid logging data in it, 57 + /// then pp points to index into the buffer where the next logging entry will 58 + /// be written. Therefore, the logging data is valid if: 59 + /// 1 <= pp < sizeof(buffer)/sizeof(u64) 60 + struct LogBuffer(CoherentAllocation<u8>); 61 + 62 + impl LogBuffer { 63 + /// Creates a new `LogBuffer` mapped on `dev`. 64 + fn new(dev: &device::Device<device::Bound>) -> Result<Self> { 65 + const NUM_PAGES: usize = RM_LOG_BUFFER_NUM_PAGES; 66 + 67 + let mut obj = Self(CoherentAllocation::<u8>::alloc_coherent( 68 + dev, 69 + NUM_PAGES * GSP_PAGE_SIZE, 70 + GFP_KERNEL | __GFP_ZERO, 71 + )?); 72 + let ptes = PteArray::<NUM_PAGES>::new(obj.0.dma_handle())?; 73 + 74 + // SAFETY: `obj` has just been created and we are its sole user. 75 + unsafe { 76 + // Copy the self-mapping PTE at the expected location. 77 + obj.0 78 + .as_slice_mut(size_of::<u64>(), size_of_val(&ptes))? 79 + .copy_from_slice(ptes.as_bytes()) 80 + }; 81 + 82 + Ok(obj) 83 + } 84 + } 85 + 86 + /// GSP runtime data. 30 87 #[pin_data] 31 - pub(crate) struct Gsp {} 88 + pub(crate) struct Gsp { 89 + /// Libos arguments. 90 + pub(crate) libos: CoherentAllocation<LibosMemoryRegionInitArgument>, 91 + /// Init log buffer. 92 + loginit: LogBuffer, 93 + /// Interrupts log buffer. 94 + logintr: LogBuffer, 95 + /// RM log buffer. 96 + logrm: LogBuffer, 97 + } 32 98 33 99 impl Gsp { 34 - pub(crate) fn new() -> impl PinInit<Self> { 35 - pin_init!(Self {}) 100 + // Creates an in-place initializer for a `Gsp` manager for `pdev`. 101 + pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> Result<impl PinInit<Self, Error>> { 102 + let dev = pdev.as_ref(); 103 + let libos = CoherentAllocation::<LibosMemoryRegionInitArgument>::alloc_coherent( 104 + dev, 105 + GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(), 106 + GFP_KERNEL | __GFP_ZERO, 107 + )?; 108 + 109 + // Initialise the logging structures. The OpenRM equivalents are in: 110 + // _kgspInitLibosLoggingStructures (allocates memory for buffers) 111 + // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array) 112 + let loginit = LogBuffer::new(dev)?; 113 + dma_write!(libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?; 114 + 115 + let logintr = LogBuffer::new(dev)?; 116 + dma_write!(libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?; 117 + 118 + let logrm = LogBuffer::new(dev)?; 119 + dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; 120 + 121 + Ok(try_pin_init!(Self { 122 + libos, 123 + loginit, 124 + logintr, 125 + logrm, 126 + })) 36 127 } 37 128 }
+60
drivers/gpu/nova-core/gsp/fw.rs
··· 8 8 use core::ops::Range; 9 9 10 10 use kernel::{ 11 + dma::CoherentAllocation, 11 12 ptr::{ 12 13 Alignable, 13 14 Alignment, // 14 15 }, 15 16 sizes::SZ_1M, 17 + transmute::{ 18 + AsBytes, 19 + FromBytes, // 20 + }, 16 21 }; 17 22 18 23 use crate::{ ··· 121 116 /// addresses of the GSP bootloader and firmware. 122 117 #[repr(transparent)] 123 118 pub(crate) struct GspFwWprMeta(bindings::GspFwWprMeta); 119 + 120 + /// Struct containing the arguments required to pass a memory buffer to the GSP 121 + /// for use during initialisation. 122 + /// 123 + /// The GSP only understands 4K pages (GSP_PAGE_SIZE), so even if the kernel is 124 + /// configured for a larger page size (e.g. 64K pages), we need to give 125 + /// the GSP an array of 4K pages. Since we only create physically contiguous 126 + /// buffers the math to calculate the addresses is simple. 127 + /// 128 + /// The buffers must be a multiple of GSP_PAGE_SIZE. GSP-RM also currently 129 + /// ignores the @kind field for LOGINIT, LOGINTR, and LOGRM, but expects the 130 + /// buffers to be physically contiguous anyway. 131 + /// 132 + /// The memory allocated for the arguments must remain until the GSP sends the 133 + /// init_done RPC. 134 + #[repr(transparent)] 135 + pub(crate) struct LibosMemoryRegionInitArgument(bindings::LibosMemoryRegionInitArgument); 136 + 137 + // SAFETY: Padding is explicit and does not contain uninitialized data. 138 + unsafe impl AsBytes for LibosMemoryRegionInitArgument {} 139 + 140 + // SAFETY: This struct only contains integer types for which all bit patterns 141 + // are valid. 142 + unsafe impl FromBytes for LibosMemoryRegionInitArgument {} 143 + 144 + impl LibosMemoryRegionInitArgument { 145 + pub(crate) fn new<A: AsBytes + FromBytes>( 146 + name: &'static str, 147 + obj: &CoherentAllocation<A>, 148 + ) -> Self { 149 + /// Generates the `ID8` identifier required for some GSP objects. 150 + fn id8(name: &str) -> u64 { 151 + let mut bytes = [0u8; core::mem::size_of::<u64>()]; 152 + 153 + for (c, b) in name.bytes().rev().zip(&mut bytes) { 154 + *b = c; 155 + } 156 + 157 + u64::from_ne_bytes(bytes) 158 + } 159 + 160 + Self(bindings::LibosMemoryRegionInitArgument { 161 + id8: id8(name), 162 + pa: obj.dma_handle(), 163 + size: num::usize_as_u64(obj.size()), 164 + kind: num::u32_into_u8::< 165 + { bindings::LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS }, 166 + >(), 167 + loc: num::u32_into_u8::< 168 + { bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM }, 169 + >(), 170 + ..Default::default() 171 + }) 172 + } 173 + }
+19
drivers/gpu/nova-core/gsp/fw/r570_144/bindings.rs
··· 124 124 } 125 125 } 126 126 } 127 + pub type LibosAddress = u64_; 128 + pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_NONE: LibosMemoryRegionKind = 0; 129 + pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS: LibosMemoryRegionKind = 1; 130 + pub const LibosMemoryRegionKind_LIBOS_MEMORY_REGION_RADIX3: LibosMemoryRegionKind = 2; 131 + pub type LibosMemoryRegionKind = ffi::c_uint; 132 + pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_NONE: LibosMemoryRegionLoc = 0; 133 + pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM: LibosMemoryRegionLoc = 1; 134 + pub const LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_FB: LibosMemoryRegionLoc = 2; 135 + pub type LibosMemoryRegionLoc = ffi::c_uint; 136 + #[repr(C)] 137 + #[derive(Debug, Default, Copy, Clone)] 138 + pub struct LibosMemoryRegionInitArgument { 139 + pub id8: LibosAddress, 140 + pub pa: LibosAddress, 141 + pub size: LibosAddress, 142 + pub kind: u8_, 143 + pub loc: u8_, 144 + pub __bindgen_padding_0: [u8; 6usize], 145 + }