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 sequencer; 28 29 pub(crate) use fw::{ 30 GspFmcBootParams, 31 GspFwWprMeta, 32 LibosParams, // 33 }; 34 35 use crate::{ 36 gsp::cmdq::Cmdq, 37 gsp::fw::{ 38 GspArgumentsPadded, 39 LibosMemoryRegionInitArgument, // 40 }, 41 num, 42 }; 43 44 pub(crate) const GSP_PAGE_SHIFT: usize = 12; 45 pub(crate) const GSP_PAGE_SIZE: usize = 1 << GSP_PAGE_SHIFT; 46 47 /// Number of GSP pages to use in a RM log buffer. 48 const RM_LOG_BUFFER_NUM_PAGES: usize = 0x10; 49 const LOG_BUFFER_SIZE: usize = RM_LOG_BUFFER_NUM_PAGES * GSP_PAGE_SIZE; 50 51 /// Array of page table entries, as understood by the GSP bootloader. 52 #[repr(C)] 53 #[derive(FromBytes, IntoBytes)] 54 struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]); 55 56 impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> { 57 /// Initialize a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`. 58 fn init(view: CoherentView<'_, Self>, start: DmaAddress) -> Result<()> { 59 for i in 0..NUM_PAGES { 60 io_write!(view, .0[build: i], 61 start 62 .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT) 63 .ok_or(EOVERFLOW)? 64 ); 65 } 66 67 Ok(()) 68 } 69 } 70 71 /// The logging buffers are byte queues that contain encoded printf-like 72 /// messages from GSP-RM. They need to be decoded by a special application 73 /// that can parse the buffers. 74 /// 75 /// The 'loginit' buffer contains logs from early GSP-RM init and 76 /// exception dumps. The 'logrm' buffer contains the subsequent logs. Both are 77 /// written to directly by GSP-RM and can be any multiple of GSP_PAGE_SIZE. 78 /// 79 /// The physical address map for the log buffer is stored in the buffer 80 /// itself, starting with offset 1. Offset 0 contains the "put" pointer (pp). 81 /// Initially, pp is equal to 0. If the buffer has valid logging data in it, 82 /// then pp points to index into the buffer where the next logging entry will 83 /// be written. Therefore, the logging data is valid if: 84 /// 1 <= pp < sizeof(buffer)/sizeof(u64) 85 struct LogBuffer(Coherent<[u8; LOG_BUFFER_SIZE]>); 86 87 impl LogBuffer { 88 /// Creates a new `LogBuffer` mapped on `dev`. 89 fn new(dev: &device::Device<device::Bound>) -> Result<Self> { 90 let obj = Self(Coherent::zeroed(dev, GFP_KERNEL)?); 91 92 let start_addr = obj.0.dma_handle(); 93 94 let pte_view = io_project!( 95 obj.0, 96 [build: size_of::<u64>()..][build: ..RM_LOG_BUFFER_NUM_PAGES * size_of::<u64>()] 97 ) 98 .try_cast::<PteArray<RM_LOG_BUFFER_NUM_PAGES>>()?; 99 PteArray::init(pte_view, start_addr)?; 100 101 Ok(obj) 102 } 103 } 104 105 struct LogBuffers { 106 /// Init log buffer. 107 loginit: LogBuffer, 108 /// Interrupts log buffer. 109 logintr: LogBuffer, 110 /// RM log buffer. 111 logrm: LogBuffer, 112 } 113 114 /// GSP runtime data. 115 #[pin_data] 116 pub(crate) struct Gsp { 117 /// Libos arguments. 118 pub(crate) libos: Coherent<[LibosMemoryRegionInitArgument]>, 119 /// Log buffers, optionally exposed via debugfs. 120 #[pin] 121 logs: debugfs::Scope<LogBuffers>, 122 /// Command queue. 123 #[pin] 124 pub(crate) cmdq: Cmdq, 125 /// RM arguments. 126 rmargs: Coherent<GspArgumentsPadded>, 127 } 128 129 impl Gsp { 130 // Creates an in-place initializer for a `Gsp` manager for `pdev`. 131 pub(crate) fn new(pdev: &pci::Device<device::Bound>) -> impl PinInit<Self, Error> + '_ { 132 pin_init::pin_init_scope(move || { 133 let dev = pdev.as_ref(); 134 135 let loginit = LogBuffer::new(dev)?; 136 let logintr = LogBuffer::new(dev)?; 137 let logrm = LogBuffer::new(dev)?; 138 139 // Initialise the logging structures. The OpenRM equivalents are in: 140 // _kgspInitLibosLoggingStructures (allocates memory for buffers) 141 // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array) 142 Ok(try_pin_init!(Self { 143 cmdq <- Cmdq::new(dev), 144 rmargs: Coherent::init(dev, GFP_KERNEL, GspArgumentsPadded::new(&cmdq))?, 145 libos: { 146 let mut libos = CoherentBox::zeroed_slice( 147 dev, 148 GSP_PAGE_SIZE / size_of::<LibosMemoryRegionInitArgument>(), 149 GFP_KERNEL, 150 )?; 151 152 libos.init_at(0, LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0))?; 153 libos.init_at(1, LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0))?; 154 libos.init_at(2, LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; 155 libos.init_at(3, LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?; 156 157 libos.into() 158 }, 159 logs <- { 160 let log_buffers = LogBuffers { 161 loginit, 162 logintr, 163 logrm, 164 }; 165 166 #[allow(static_mut_refs)] 167 // SAFETY: `DEBUGFS_ROOT` is created before driver registration and cleared 168 // after driver unregistration, so no probe() can race with its modification. 169 // 170 // PANIC: `DEBUGFS_ROOT` cannot be `None` here. It is set before driver 171 // registration and cleared after driver unregistration, so it is always 172 // `Some` for the entire lifetime that probe() can be called. 173 let log_parent: &debugfs::Dir = unsafe { crate::DEBUGFS_ROOT.as_ref() } 174 .expect("DEBUGFS_ROOT not initialized"); 175 176 log_parent.scope(log_buffers, dev.name(), |logs, dir| { 177 dir.read_binary_file(c"loginit", &logs.loginit.0); 178 dir.read_binary_file(c"logintr", &logs.logintr.0); 179 dir.read_binary_file(c"logrm", &logs.logrm.0); 180 }) 181 }, 182 })) 183 }) 184 } 185 } 186 187 /// Opaque bundle required to unload the GSP. Created by [`Gsp::boot`], consumed by [`Gsp::unload`]. 188 pub(crate) struct UnloadBundle(KBox<dyn hal::UnloadBundle>); 189