xref: /linux/drivers/gpu/nova-core/gsp/fw.rs (revision f77be04d00d8ce403ecaf547f1515a844bbde060)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 pub(crate) mod commands;
4 mod r570_144;
5 
6 // Alias to avoid repeating the version number with every use.
7 use r570_144 as bindings;
8 
9 use core::{
10     fmt,
11     ops::Range, //
12 };
13 
14 use kernel::{
15     dma::CoherentAllocation,
16     prelude::*,
17     ptr::{
18         Alignable,
19         Alignment, //
20     },
21     sizes::{
22         SZ_128K,
23         SZ_1M, //
24     },
25     transmute::{
26         AsBytes,
27         FromBytes, //
28     },
29 };
30 
31 use crate::{
32     fb::FbLayout,
33     firmware::gsp::GspFirmware,
34     gpu::Chipset,
35     gsp::{
36         cmdq::Cmdq, //
37         GSP_PAGE_SIZE,
38     },
39     num::{
40         self,
41         FromSafeCast, //
42     },
43 };
44 
45 /// Empty type to group methods related to heap parameters for running the GSP firmware.
46 enum GspFwHeapParams {}
47 
48 /// Minimum required alignment for the GSP heap.
49 const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new::<{ 1 << 20 }>();
50 
51 impl GspFwHeapParams {
52     /// Returns the amount of GSP-RM heap memory used during GSP-RM boot and initialization (up to
53     /// and including the first client subdevice allocation).
54     fn base_rm_size(_chipset: Chipset) -> u64 {
55         // TODO: this needs to be updated to return the correct value for Hopper+ once support for
56         // them is added:
57         // u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100)
58         u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X)
59     }
60 
61     /// Returns the amount of heap memory required to support a single channel allocation.
62     fn client_alloc_size() -> u64 {
63         u64::from(bindings::GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE)
64             .align_up(GSP_HEAP_ALIGNMENT)
65             .unwrap_or(u64::MAX)
66     }
67 
68     /// Returns the amount of memory to reserve for management purposes for a framebuffer of size
69     /// `fb_size`.
70     fn management_overhead(fb_size: u64) -> u64 {
71         let fb_size_gb = fb_size.div_ceil(u64::from_safe_cast(kernel::sizes::SZ_1G));
72 
73         u64::from(bindings::GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB)
74             .saturating_mul(fb_size_gb)
75             .align_up(GSP_HEAP_ALIGNMENT)
76             .unwrap_or(u64::MAX)
77     }
78 }
79 
80 /// Heap memory requirements and constraints for a given version of the GSP LIBOS.
81 pub(crate) struct LibosParams {
82     /// The base amount of heap required by the GSP operating system, in bytes.
83     carveout_size: u64,
84     /// The minimum and maximum sizes allowed for the GSP FW heap, in bytes.
85     allowed_heap_size: Range<u64>,
86 }
87 
88 impl LibosParams {
89     /// Version 2 of the GSP LIBOS (Turing and GA100)
90     const LIBOS2: LibosParams = LibosParams {
91         carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2),
92         allowed_heap_size: num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB)
93             * num::usize_as_u64(SZ_1M)
94             ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB)
95                 * num::usize_as_u64(SZ_1M),
96     };
97 
98     /// Version 3 of the GSP LIBOS (GA102+)
99     const LIBOS3: LibosParams = LibosParams {
100         carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL),
101         allowed_heap_size: num::u32_as_u64(
102             bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB,
103         ) * num::usize_as_u64(SZ_1M)
104             ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB)
105                 * num::usize_as_u64(SZ_1M),
106     };
107 
108     /// Returns the libos parameters corresponding to `chipset`.
109     pub(crate) fn from_chipset(chipset: Chipset) -> &'static LibosParams {
110         if chipset < Chipset::GA102 {
111             &Self::LIBOS2
112         } else {
113             &Self::LIBOS3
114         }
115     }
116 
117     /// Returns the amount of memory (in bytes) to allocate for the WPR heap for a framebuffer size
118     /// of `fb_size` (in bytes) for `chipset`.
119     pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> u64 {
120         // The WPR heap will contain the following:
121         // LIBOS carveout,
122         self.carveout_size
123             // RM boot working memory,
124             .saturating_add(GspFwHeapParams::base_rm_size(chipset))
125             // One RM client,
126             .saturating_add(GspFwHeapParams::client_alloc_size())
127             // Overhead for memory management.
128             .saturating_add(GspFwHeapParams::management_overhead(fb_size))
129             // Clamp to the supported heap sizes.
130             .clamp(self.allowed_heap_size.start, self.allowed_heap_size.end - 1)
131     }
132 }
133 
134 /// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA
135 /// addresses of the GSP bootloader and firmware.
136 #[repr(transparent)]
137 pub(crate) struct GspFwWprMeta(bindings::GspFwWprMeta);
138 
139 // SAFETY: Padding is explicit and does not contain uninitialized data.
140 unsafe impl AsBytes for GspFwWprMeta {}
141 
142 // SAFETY: This struct only contains integer types for which all bit patterns
143 // are valid.
144 unsafe impl FromBytes for GspFwWprMeta {}
145 
146 type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1;
147 type GspFwWprMetaBootInfo = r570_144::GspFwWprMeta__bindgen_ty_1__bindgen_ty_1;
148 
149 impl GspFwWprMeta {
150     /// Fill in and return a `GspFwWprMeta` suitable for booting `gsp_firmware` using the
151     /// `fb_layout` layout.
152     pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self {
153         Self(bindings::GspFwWprMeta {
154             // CAST: we want to store the bits of `GSP_FW_WPR_META_MAGIC` unmodified.
155             magic: r570_144::GSP_FW_WPR_META_MAGIC as u64,
156             revision: u64::from(r570_144::GSP_FW_WPR_META_REVISION),
157             sysmemAddrOfRadix3Elf: gsp_firmware.radix3_dma_handle(),
158             sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size),
159             sysmemAddrOfBootloader: gsp_firmware.bootloader.ucode.dma_handle(),
160             sizeOfBootloader: u64::from_safe_cast(gsp_firmware.bootloader.ucode.size()),
161             bootloaderCodeOffset: u64::from(gsp_firmware.bootloader.code_offset),
162             bootloaderDataOffset: u64::from(gsp_firmware.bootloader.data_offset),
163             bootloaderManifestOffset: u64::from(gsp_firmware.bootloader.manifest_offset),
164             __bindgen_anon_1: GspFwWprMetaBootResumeInfo {
165                 __bindgen_anon_1: GspFwWprMetaBootInfo {
166                     sysmemAddrOfSignature: gsp_firmware.signatures.dma_handle(),
167                     sizeOfSignature: u64::from_safe_cast(gsp_firmware.signatures.size()),
168                 },
169             },
170             gspFwRsvdStart: fb_layout.heap.start,
171             nonWprHeapOffset: fb_layout.heap.start,
172             nonWprHeapSize: fb_layout.heap.end - fb_layout.heap.start,
173             gspFwWprStart: fb_layout.wpr2.start,
174             gspFwHeapOffset: fb_layout.wpr2_heap.start,
175             gspFwHeapSize: fb_layout.wpr2_heap.end - fb_layout.wpr2_heap.start,
176             gspFwOffset: fb_layout.elf.start,
177             bootBinOffset: fb_layout.boot.start,
178             frtsOffset: fb_layout.frts.start,
179             frtsSize: fb_layout.frts.end - fb_layout.frts.start,
180             gspFwWprEnd: fb_layout
181                 .vga_workspace
182                 .start
183                 .align_down(Alignment::new::<SZ_128K>()),
184             gspFwHeapVfPartitionCount: fb_layout.vf_partition_count,
185             fbSize: fb_layout.fb.end - fb_layout.fb.start,
186             vgaWorkspaceOffset: fb_layout.vga_workspace.start,
187             vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start,
188             ..Default::default()
189         })
190     }
191 }
192 
193 #[derive(Copy, Clone, Debug, PartialEq)]
194 #[repr(u32)]
195 pub(crate) enum MsgFunction {
196     // Common function codes
197     Nop = bindings::NV_VGPU_MSG_FUNCTION_NOP,
198     SetGuestSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO,
199     AllocRoot = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT,
200     AllocDevice = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE,
201     AllocMemory = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY,
202     AllocCtxDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA,
203     AllocChannelDma = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA,
204     MapMemory = bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY,
205     BindCtxDma = bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA,
206     AllocObject = bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT,
207     Free = bindings::NV_VGPU_MSG_FUNCTION_FREE,
208     Log = bindings::NV_VGPU_MSG_FUNCTION_LOG,
209     GetGspStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO,
210     SetRegistry = bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY,
211     GspSetSystemInfo = bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO,
212     GspInitPostObjGpu = bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU,
213     GspRmControl = bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL,
214     GetStaticInfo = bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO,
215 
216     // Event codes
217     GspInitDone = bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE,
218     GspRunCpuSequencer = bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER,
219     PostEvent = bindings::NV_VGPU_MSG_EVENT_POST_EVENT,
220     RcTriggered = bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED,
221     MmuFaultQueued = bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED,
222     OsErrorLog = bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG,
223     GspPostNoCat = bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD,
224     GspLockdownNotice = bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE,
225     UcodeLibOsPrint = bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT,
226 }
227 
228 impl fmt::Display for MsgFunction {
229     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230         match self {
231             // Common function codes
232             MsgFunction::Nop => write!(f, "NOP"),
233             MsgFunction::SetGuestSystemInfo => write!(f, "SET_GUEST_SYSTEM_INFO"),
234             MsgFunction::AllocRoot => write!(f, "ALLOC_ROOT"),
235             MsgFunction::AllocDevice => write!(f, "ALLOC_DEVICE"),
236             MsgFunction::AllocMemory => write!(f, "ALLOC_MEMORY"),
237             MsgFunction::AllocCtxDma => write!(f, "ALLOC_CTX_DMA"),
238             MsgFunction::AllocChannelDma => write!(f, "ALLOC_CHANNEL_DMA"),
239             MsgFunction::MapMemory => write!(f, "MAP_MEMORY"),
240             MsgFunction::BindCtxDma => write!(f, "BIND_CTX_DMA"),
241             MsgFunction::AllocObject => write!(f, "ALLOC_OBJECT"),
242             MsgFunction::Free => write!(f, "FREE"),
243             MsgFunction::Log => write!(f, "LOG"),
244             MsgFunction::GetGspStaticInfo => write!(f, "GET_GSP_STATIC_INFO"),
245             MsgFunction::SetRegistry => write!(f, "SET_REGISTRY"),
246             MsgFunction::GspSetSystemInfo => write!(f, "GSP_SET_SYSTEM_INFO"),
247             MsgFunction::GspInitPostObjGpu => write!(f, "GSP_INIT_POST_OBJGPU"),
248             MsgFunction::GspRmControl => write!(f, "GSP_RM_CONTROL"),
249             MsgFunction::GetStaticInfo => write!(f, "GET_STATIC_INFO"),
250 
251             // Event codes
252             MsgFunction::GspInitDone => write!(f, "INIT_DONE"),
253             MsgFunction::GspRunCpuSequencer => write!(f, "RUN_CPU_SEQUENCER"),
254             MsgFunction::PostEvent => write!(f, "POST_EVENT"),
255             MsgFunction::RcTriggered => write!(f, "RC_TRIGGERED"),
256             MsgFunction::MmuFaultQueued => write!(f, "MMU_FAULT_QUEUED"),
257             MsgFunction::OsErrorLog => write!(f, "OS_ERROR_LOG"),
258             MsgFunction::GspPostNoCat => write!(f, "NOCAT"),
259             MsgFunction::GspLockdownNotice => write!(f, "LOCKDOWN_NOTICE"),
260             MsgFunction::UcodeLibOsPrint => write!(f, "LIBOS_PRINT"),
261         }
262     }
263 }
264 
265 impl TryFrom<u32> for MsgFunction {
266     type Error = kernel::error::Error;
267 
268     fn try_from(value: u32) -> Result<MsgFunction> {
269         match value {
270             bindings::NV_VGPU_MSG_FUNCTION_NOP => Ok(MsgFunction::Nop),
271             bindings::NV_VGPU_MSG_FUNCTION_SET_GUEST_SYSTEM_INFO => {
272                 Ok(MsgFunction::SetGuestSystemInfo)
273             }
274             bindings::NV_VGPU_MSG_FUNCTION_ALLOC_ROOT => Ok(MsgFunction::AllocRoot),
275             bindings::NV_VGPU_MSG_FUNCTION_ALLOC_DEVICE => Ok(MsgFunction::AllocDevice),
276             bindings::NV_VGPU_MSG_FUNCTION_ALLOC_MEMORY => Ok(MsgFunction::AllocMemory),
277             bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CTX_DMA => Ok(MsgFunction::AllocCtxDma),
278             bindings::NV_VGPU_MSG_FUNCTION_ALLOC_CHANNEL_DMA => Ok(MsgFunction::AllocChannelDma),
279             bindings::NV_VGPU_MSG_FUNCTION_MAP_MEMORY => Ok(MsgFunction::MapMemory),
280             bindings::NV_VGPU_MSG_FUNCTION_BIND_CTX_DMA => Ok(MsgFunction::BindCtxDma),
281             bindings::NV_VGPU_MSG_FUNCTION_ALLOC_OBJECT => Ok(MsgFunction::AllocObject),
282             bindings::NV_VGPU_MSG_FUNCTION_FREE => Ok(MsgFunction::Free),
283             bindings::NV_VGPU_MSG_FUNCTION_LOG => Ok(MsgFunction::Log),
284             bindings::NV_VGPU_MSG_FUNCTION_GET_GSP_STATIC_INFO => Ok(MsgFunction::GetGspStaticInfo),
285             bindings::NV_VGPU_MSG_FUNCTION_SET_REGISTRY => Ok(MsgFunction::SetRegistry),
286             bindings::NV_VGPU_MSG_FUNCTION_GSP_SET_SYSTEM_INFO => Ok(MsgFunction::GspSetSystemInfo),
287             bindings::NV_VGPU_MSG_FUNCTION_GSP_INIT_POST_OBJGPU => {
288                 Ok(MsgFunction::GspInitPostObjGpu)
289             }
290             bindings::NV_VGPU_MSG_FUNCTION_GSP_RM_CONTROL => Ok(MsgFunction::GspRmControl),
291             bindings::NV_VGPU_MSG_FUNCTION_GET_STATIC_INFO => Ok(MsgFunction::GetStaticInfo),
292             bindings::NV_VGPU_MSG_EVENT_GSP_INIT_DONE => Ok(MsgFunction::GspInitDone),
293             bindings::NV_VGPU_MSG_EVENT_GSP_RUN_CPU_SEQUENCER => {
294                 Ok(MsgFunction::GspRunCpuSequencer)
295             }
296             bindings::NV_VGPU_MSG_EVENT_POST_EVENT => Ok(MsgFunction::PostEvent),
297             bindings::NV_VGPU_MSG_EVENT_RC_TRIGGERED => Ok(MsgFunction::RcTriggered),
298             bindings::NV_VGPU_MSG_EVENT_MMU_FAULT_QUEUED => Ok(MsgFunction::MmuFaultQueued),
299             bindings::NV_VGPU_MSG_EVENT_OS_ERROR_LOG => Ok(MsgFunction::OsErrorLog),
300             bindings::NV_VGPU_MSG_EVENT_GSP_POST_NOCAT_RECORD => Ok(MsgFunction::GspPostNoCat),
301             bindings::NV_VGPU_MSG_EVENT_GSP_LOCKDOWN_NOTICE => Ok(MsgFunction::GspLockdownNotice),
302             bindings::NV_VGPU_MSG_EVENT_UCODE_LIBOS_PRINT => Ok(MsgFunction::UcodeLibOsPrint),
303             _ => Err(EINVAL),
304         }
305     }
306 }
307 
308 impl From<MsgFunction> for u32 {
309     fn from(value: MsgFunction) -> Self {
310         // CAST: `MsgFunction` is `repr(u32)` and can thus be cast losslessly.
311         value as u32
312     }
313 }
314 
315 /// Struct containing the arguments required to pass a memory buffer to the GSP
316 /// for use during initialisation.
317 ///
318 /// The GSP only understands 4K pages (GSP_PAGE_SIZE), so even if the kernel is
319 /// configured for a larger page size (e.g. 64K pages), we need to give
320 /// the GSP an array of 4K pages. Since we only create physically contiguous
321 /// buffers the math to calculate the addresses is simple.
322 ///
323 /// The buffers must be a multiple of GSP_PAGE_SIZE.  GSP-RM also currently
324 /// ignores the @kind field for LOGINIT, LOGINTR, and LOGRM, but expects the
325 /// buffers to be physically contiguous anyway.
326 ///
327 /// The memory allocated for the arguments must remain until the GSP sends the
328 /// init_done RPC.
329 #[repr(transparent)]
330 pub(crate) struct LibosMemoryRegionInitArgument(bindings::LibosMemoryRegionInitArgument);
331 
332 // SAFETY: Padding is explicit and does not contain uninitialized data.
333 unsafe impl AsBytes for LibosMemoryRegionInitArgument {}
334 
335 // SAFETY: This struct only contains integer types for which all bit patterns
336 // are valid.
337 unsafe impl FromBytes for LibosMemoryRegionInitArgument {}
338 
339 impl LibosMemoryRegionInitArgument {
340     pub(crate) fn new<A: AsBytes + FromBytes>(
341         name: &'static str,
342         obj: &CoherentAllocation<A>,
343     ) -> Self {
344         /// Generates the `ID8` identifier required for some GSP objects.
345         fn id8(name: &str) -> u64 {
346             let mut bytes = [0u8; core::mem::size_of::<u64>()];
347 
348             for (c, b) in name.bytes().rev().zip(&mut bytes) {
349                 *b = c;
350             }
351 
352             u64::from_ne_bytes(bytes)
353         }
354 
355         Self(bindings::LibosMemoryRegionInitArgument {
356             id8: id8(name),
357             pa: obj.dma_handle(),
358             size: num::usize_as_u64(obj.size()),
359             kind: num::u32_into_u8::<
360                 { bindings::LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS },
361             >(),
362             loc: num::u32_into_u8::<
363                 { bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM },
364             >(),
365             ..Default::default()
366         })
367     }
368 }
369 
370 /// TX header for setting up a message queue with the GSP.
371 #[repr(transparent)]
372 pub(crate) struct MsgqTxHeader(bindings::msgqTxHeader);
373 
374 impl MsgqTxHeader {
375     /// Create a new TX queue header.
376     ///
377     /// # Arguments
378     ///
379     /// * `msgq_size` - Total size of the message queue structure, in bytes.
380     /// * `rx_hdr_offset` - Offset, in bytes, of the start of the RX header in the message queue
381     ///   structure.
382     /// * `msg_count` - Number of messages that can be sent, i.e. the number of memory pages
383     ///   allocated for the message queue in the message queue structure.
384     pub(crate) fn new(msgq_size: u32, rx_hdr_offset: u32, msg_count: u32) -> Self {
385         Self(bindings::msgqTxHeader {
386             version: 0,
387             size: msgq_size,
388             msgSize: num::usize_into_u32::<GSP_PAGE_SIZE>(),
389             msgCount: msg_count,
390             writePtr: 0,
391             flags: 1,
392             rxHdrOff: rx_hdr_offset,
393             entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(),
394         })
395     }
396 
397     /// Returns the value of the write pointer for this queue.
398     pub(crate) fn write_ptr(&self) -> u32 {
399         let ptr = core::ptr::from_ref(&self.0.writePtr);
400 
401         // SAFETY: `ptr` is a valid pointer to a `u32`.
402         unsafe { ptr.read_volatile() }
403     }
404 
405     /// Sets the value of the write pointer for this queue.
406     pub(crate) fn set_write_ptr(&mut self, val: u32) {
407         let ptr = core::ptr::from_mut(&mut self.0.writePtr);
408 
409         // SAFETY: `ptr` is a valid pointer to a `u32`.
410         unsafe { ptr.write_volatile(val) }
411     }
412 }
413 
414 // SAFETY: Padding is explicit and does not contain uninitialized data.
415 unsafe impl AsBytes for MsgqTxHeader {}
416 
417 /// RX header for setting up a message queue with the GSP.
418 #[repr(transparent)]
419 pub(crate) struct MsgqRxHeader(bindings::msgqRxHeader);
420 
421 /// Header for the message RX queue.
422 impl MsgqRxHeader {
423     /// Creates a new RX queue header.
424     pub(crate) fn new() -> Self {
425         Self(Default::default())
426     }
427 
428     /// Returns the value of the read pointer for this queue.
429     pub(crate) fn read_ptr(&self) -> u32 {
430         let ptr = core::ptr::from_ref(&self.0.readPtr);
431 
432         // SAFETY: `ptr` is a valid pointer to a `u32`.
433         unsafe { ptr.read_volatile() }
434     }
435 
436     /// Sets the value of the read pointer for this queue.
437     pub(crate) fn set_read_ptr(&mut self, val: u32) {
438         let ptr = core::ptr::from_mut(&mut self.0.readPtr);
439 
440         // SAFETY: `ptr` is a valid pointer to a `u32`.
441         unsafe { ptr.write_volatile(val) }
442     }
443 }
444 
445 // SAFETY: Padding is explicit and does not contain uninitialized data.
446 unsafe impl AsBytes for MsgqRxHeader {}
447 
448 bitfield! {
449     struct MsgHeaderVersion(u32) {
450         31:24 major as u8;
451         23:16 minor as u8;
452     }
453 }
454 
455 impl MsgHeaderVersion {
456     const MAJOR_TOT: u8 = 3;
457     const MINOR_TOT: u8 = 0;
458 
459     fn new() -> Self {
460         Self::default()
461             .set_major(Self::MAJOR_TOT)
462             .set_minor(Self::MINOR_TOT)
463     }
464 }
465 
466 impl bindings::rpc_message_header_v {
467     fn init(cmd_size: usize, function: MsgFunction) -> impl Init<Self, Error> {
468         type RpcMessageHeader = bindings::rpc_message_header_v;
469 
470         try_init!(RpcMessageHeader {
471             header_version: MsgHeaderVersion::new().into(),
472             signature: bindings::NV_VGPU_MSG_SIGNATURE_VALID,
473             function: function.into(),
474             length: size_of::<Self>()
475                 .checked_add(cmd_size)
476                 .ok_or(EOVERFLOW)
477                 .and_then(|v| v.try_into().map_err(|_| EINVAL))?,
478             rpc_result: 0xffffffff,
479             rpc_result_private: 0xffffffff,
480             ..Zeroable::init_zeroed()
481         })
482     }
483 }
484 
485 // SAFETY: We can't derive the Zeroable trait for this binding because the
486 // procedural macro doesn't support the syntax used by bindgen to create the
487 // __IncompleteArrayField types. So instead we implement it here, which is safe
488 // because these are explicitly padded structures only containing types for
489 // which any bit pattern, including all zeros, is valid.
490 unsafe impl Zeroable for bindings::rpc_message_header_v {}
491 
492 /// GSP Message Element.
493 ///
494 /// This is essentially a message header expected to be followed by the message data.
495 #[repr(transparent)]
496 pub(crate) struct GspMsgElement {
497     inner: bindings::GSP_MSG_QUEUE_ELEMENT,
498 }
499 
500 impl GspMsgElement {
501     /// Creates a new message element.
502     ///
503     /// # Arguments
504     ///
505     /// * `sequence` - Sequence number of the message.
506     /// * `cmd_size` - Size of the command (not including the message element), in bytes.
507     /// * `function` - Function of the message.
508     #[allow(non_snake_case)]
509     pub(crate) fn init(
510         sequence: u32,
511         cmd_size: usize,
512         function: MsgFunction,
513     ) -> impl Init<Self, Error> {
514         type RpcMessageHeader = bindings::rpc_message_header_v;
515         type InnerGspMsgElement = bindings::GSP_MSG_QUEUE_ELEMENT;
516         let init_inner = try_init!(InnerGspMsgElement {
517             seqNum: sequence,
518             elemCount: size_of::<Self>()
519                 .checked_add(cmd_size)
520                 .ok_or(EOVERFLOW)?
521                 .div_ceil(GSP_PAGE_SIZE)
522                 .try_into()
523                 .map_err(|_| EOVERFLOW)?,
524             rpc <- RpcMessageHeader::init(cmd_size, function),
525             ..Zeroable::init_zeroed()
526         });
527 
528         try_init!(GspMsgElement {
529             inner <- init_inner,
530         })
531     }
532 
533     /// Sets the checksum of this message.
534     ///
535     /// Since the header is also part of the checksum, this is usually called after the whole
536     /// message has been written to the shared memory area.
537     pub(crate) fn set_checksum(&mut self, checksum: u32) {
538         self.inner.checkSum = checksum;
539     }
540 
541     /// Returns the total length of the message.
542     pub(crate) fn length(&self) -> usize {
543         // `rpc.length` includes the length of the GspRpcHeader but not the message header.
544         size_of::<Self>() - size_of::<bindings::rpc_message_header_v>()
545             + num::u32_as_usize(self.inner.rpc.length)
546     }
547 
548     // Returns the sequence number of the message.
549     pub(crate) fn sequence(&self) -> u32 {
550         self.inner.rpc.sequence
551     }
552 
553     // Returns the function of the message, if it is valid, or the invalid function number as an
554     // error.
555     pub(crate) fn function(&self) -> Result<MsgFunction, u32> {
556         self.inner
557             .rpc
558             .function
559             .try_into()
560             .map_err(|_| self.inner.rpc.function)
561     }
562 
563     // Returns the number of elements (i.e. memory pages) used by this message.
564     pub(crate) fn element_count(&self) -> u32 {
565         self.inner.elemCount
566     }
567 }
568 
569 // SAFETY: Padding is explicit and does not contain uninitialized data.
570 unsafe impl AsBytes for GspMsgElement {}
571 
572 // SAFETY: This struct only contains integer types for which all bit patterns
573 // are valid.
574 unsafe impl FromBytes for GspMsgElement {}
575 
576 /// Arguments for GSP startup.
577 #[repr(transparent)]
578 pub(crate) struct GspArgumentsCached(bindings::GSP_ARGUMENTS_CACHED);
579 
580 impl GspArgumentsCached {
581     /// Creates the arguments for starting the GSP up using `cmdq` as its command queue.
582     pub(crate) fn new(cmdq: &Cmdq) -> Self {
583         Self(bindings::GSP_ARGUMENTS_CACHED {
584             messageQueueInitArguments: MessageQueueInitArguments::new(cmdq).0,
585             bDmemStack: 1,
586             ..Default::default()
587         })
588     }
589 }
590 
591 // SAFETY: Padding is explicit and will not contain uninitialized data.
592 unsafe impl AsBytes for GspArgumentsCached {}
593 
594 // SAFETY: This struct only contains integer types for which all bit patterns
595 // are valid.
596 unsafe impl FromBytes for GspArgumentsCached {}
597 
598 /// Init arguments for the message queue.
599 #[repr(transparent)]
600 struct MessageQueueInitArguments(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS);
601 
602 impl MessageQueueInitArguments {
603     /// Creates a new init arguments structure for `cmdq`.
604     fn new(cmdq: &Cmdq) -> Self {
605         Self(bindings::MESSAGE_QUEUE_INIT_ARGUMENTS {
606             sharedMemPhysAddr: cmdq.dma_handle(),
607             pageTableEntryCount: num::usize_into_u32::<{ Cmdq::NUM_PTES }>(),
608             cmdQueueOffset: num::usize_as_u64(Cmdq::CMDQ_OFFSET),
609             statQueueOffset: num::usize_as_u64(Cmdq::STATQ_OFFSET),
610             ..Default::default()
611         })
612     }
613 }
614