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