1 // SPDX-License-Identifier: GPL-2.0 2 3 mod boot; 4 mod hal; 5 6 use kernel::{ 7 debugfs, 8 device, 9 dma::{ 10 Coherent, 11 CoherentBox, 12 CoherentView, 13 DmaAddress, // 14 }, 15 io::{ 16 io_project, 17 io_write, 18 Io, // 19 }, 20 pci, 21 prelude::*, // 22 }; 23 24 pub(crate) mod cmdq; 25 pub(crate) mod commands; 26 mod fw; 27 mod regs; 28 mod sequencer; 29 30 pub(crate) use fw::{ 31 GspFmcBootParams, 32 GspFwWprMeta, 33 LibosMemoryRegionInitArgument, 34 LibosParams, // 35 }; 36 pub(crate) use hal::boot_firmware_files; 37 38 use crate::{ 39 driver::Bar0, 40 falcon::{ 41 gsp::Gsp as GspFalcon, 42 sec2::Sec2 as Sec2Falcon, 43 Falcon, // 44 }, 45 fsp::Fsp, 46 gpu::Chipset, 47 gsp::{ 48 cmdq::Cmdq, 49 fw::GspArgumentsPadded, // 50 }, 51 num, 52 vgpu::VgpuManager, // 53 }; 54 55 pub(crate) const GSP_PAGE_SHIFT: usize = 12; 56 pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT; 57 58 /// Common context for the GSP boot process. 59 /// 60 /// It carries two distinct lifetimes: 61 /// 62 /// - `'gpu` is the lifetime of the bound GPU device, as captured by the GPU subdevices. 63 /// - `'ctx` is a shorter lifetime during which this context borrows those subdevices. 64 pub(crate) struct GspBootContext<'ctx, 'gpu> { 65 pub(crate) pdev: &'gpu pci::Device<device::Bound>, 66 pub(crate) bar: Bar0<'gpu>, 67 pub(crate) chipset: Chipset, 68 pub(crate) gsp_falcon: &'ctx Falcon<'gpu, GspFalcon>, 69 pub(crate) sec2_falcon: &'ctx Falcon<'gpu, Sec2Falcon>, 70 pub(crate) fsp: Option<&'ctx mut Fsp<'gpu>>, 71 pub(crate) vgpu: &'ctx VgpuManager, 72 } 73 74 impl<'ctx, 'gpu> GspBootContext<'ctx, 'gpu> { 75 pub(crate) fn dev(&self) -> &'gpu device::Device<device::Bound> { 76 self.pdev.as_ref() 77 } 78 } 79 80 /// Number of GSP pages to use in a RM log buffer. 81 const RM_LOG_BUFFER_NUM_PAGES: usize = 0x10; 82 const LOG_BUFFER_SIZE: usize = RM_LOG_BUFFER_NUM_PAGES * GSP_PAGE_SIZE; 83 84 /// Array of page table entries, as understood by the GSP bootloader. 85 #[repr(C)] 86 #[derive(FromBytes, IntoBytes)] 87 struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]); 88 89 impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> { 90 /// Initialize a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`. 91 fn init(view: CoherentView<'_, Self>, start: DmaAddress) -> Result<()> { 92 for i in 0..NUM_PAGES { 93 io_write!(view, .0[build: i], 94 start 95 .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT) 96 .ok_or(EOVERFLOW)? 97 ); 98 } 99 100 Ok(()) 101 } 102 } 103 104 /// The logging buffers are byte queues that contain encoded printf-like 105 /// messages from GSP-RM. They need to be decoded by a special application 106 /// that can parse the buffers. 107 /// 108 /// The 'loginit' buffer contains logs from early GSP-RM init and 109 /// exception dumps. The 'logrm' buffer contains the subsequent logs. Both are 110 /// written to directly by GSP-RM and can be any multiple of GSP_PAGE_SIZE. 111 /// 112 /// The physical address map for the log buffer is stored in the buffer 113 /// itself, starting with offset 1. Offset 0 contains the "put" pointer (pp). 114 /// Initially, pp is equal to 0. If the buffer has valid logging data in it, 115 /// then pp points to index into the buffer where the next logging entry will 116 /// be written. Therefore, the logging data is valid if: 117 /// 1 <= pp < sizeof(buffer)/sizeof(u64) 118 struct LogBuffer(Coherent<[u8; LOG_BUFFER_SIZE]>); 119 120 impl LogBuffer { 121 /// Creates a new `LogBuffer` mapped on `dev`. 122 fn new(dev: &device::Device<device::Bound>) -> Result<Self> { 123 let obj = Self(Coherent::zeroed(dev, GFP_KERNEL)?); 124 125 let start_addr = obj.0.dma_address(); 126 127 let pte_view = io_project!( 128 obj.0, 129 [build: size_of::<u64>()..][build: ..RM_LOG_BUFFER_NUM_PAGES * size_of::<u64>()] 130 ) 131 .try_cast::<PteArray<RM_LOG_BUFFER_NUM_PAGES>>()?; 132 PteArray::init(pte_view, start_addr)?; 133 134 Ok(obj) 135 } 136 } 137 138 struct LogBuffers { 139 /// Init log buffer. 140 loginit: LogBuffer, 141 /// Interrupts log buffer. 142 logintr: LogBuffer, 143 /// RM log buffer. 144 logrm: LogBuffer, 145 } 146 147 /// GSP runtime data. 148 #[pin_data] 149 pub(crate) struct Gsp { 150 /// Libos arguments. 151 pub(crate) libos: Coherent<[LibosMemoryRegionInitArgument]>, 152 /// Log buffers, optionally exposed via debugfs. 153 #[pin] 154 logs: debugfs::Scope<LogBuffers>, 155 /// Command queue. 156 #[pin] 157 pub(crate) cmdq: Cmdq, 158 /// RM arguments. 159 rmargs: Coherent<GspArgumentsPadded>, 160 } 161 162 impl Gsp { 163 // Creates an in-place initializer for a `Gsp` manager for `pdev`. 164 pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error> + '_ { 165 pin_init::pin_init_scope(move || { 166 let dev = pdev.as_ref(); 167 168 let loginit = LogBuffer::new(dev)?; 169 let logintr = LogBuffer::new(dev)?; 170 let logrm = LogBuffer::new(dev)?; 171 172 // Initialise the logging structures. The OpenRM equivalents are in: 173 // _kgspInitLibosLoggingStructures (allocates memory for buffers) 174 // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array) 175 Ok(try_pin_init!(Self { 176 cmdq <- Cmdq::new(dev), 177 rmargs: Coherent::init(dev, GFP_KERNEL, GspArgumentsPadded::new(&cmdq))?, 178 libos: { 179 let mut libos = CoherentBox::zeroed_slice( 180 dev, 181 GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(), 182 GFP_KERNEL, 183 )?; 184 185 libos.init_at(0, LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?; 186 libos.init_at(1, LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?; 187 libos.init_at(2, LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; 188 libos.init_at(3, LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?; 189 190 libos.into() 191 }, 192 logs <- { 193 let log_buffers = LogBuffers { 194 loginit, 195 logintr, 196 logrm, 197 }; 198 199 #[allow(static_mut_refs)] 200 // SAFETY: `DEBUGFS_ROOT` is created before driver registration and cleared 201 // after driver unregistration, so no probe() can race with its modification. 202 // 203 // PANIC: `DEBUGFS_ROOT` cannot be `None` here. It is set before driver 204 // registration and cleared after driver unregistration, so it is always 205 // `Some` for the entire lifetime that probe() can be called. 206 let log_parent: &debugfs::Dir = unsafe { crate::DEBUGFS_ROOT.as_ref() } 207 .expect("DEBUGFS_ROOT not initialized"); 208 209 log_parent.scope(log_buffers, dev.name(), |logs, dir| { 210 dir.read_binary_file(c"loginit", &logs.loginit.0); 211 dir.read_binary_file(c"logintr", &logs.logintr.0); 212 dir.read_binary_file(c"logrm", &logs.logrm.0); 213 }) 214 }, 215 })) 216 }) 217 } 218 219 /// Query the GSP for the static GPU information. 220 pub(crate) fn get_static_info(&self, bar: Bar0<'_>) -> Result<commands::GetGspStaticInfoReply> { 221 self.cmdq.send_command(bar, commands::GetGspStaticInfo) 222 } 223 } 224 225 /// Opaque bundle required to unload the GSP. Created by [`Gsp::boot`], consumed by [`Gsp::unload`]. 226 pub(crate) struct UnloadBundle(KBox<dyn hal::UnloadBundle>); 227