1 // SPDX-License-Identifier: GPL-2.0 2 // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 4 use core::ops::Range; 5 6 use kernel::{ 7 device, 8 pci, 9 prelude::*, 10 transmute::{ 11 AsBytes, 12 FromBytes, // 13 }, // 14 }; 15 16 use crate::{ 17 gpu::Chipset, 18 gsp::GSP_PAGE_SIZE, 19 num::IntoSafeCast, // 20 }; 21 22 use super::bindings; 23 24 /// Payload of the `GspSetSystemInfo` command. 25 #[repr(transparent)] 26 pub(crate) struct GspSetSystemInfo { 27 inner: bindings::GspSystemInfo, 28 } 29 static_assert!(size_of::<GspSetSystemInfo>() < GSP_PAGE_SIZE); 30 31 impl GspSetSystemInfo { 32 /// Returns an in-place initializer for the `GspSetSystemInfo` command. 33 #[allow(non_snake_case)] 34 pub(crate) fn init<'a>( 35 dev: &'a pci::Device<device::Bound>, 36 chipset: Chipset, 37 ) -> impl Init<Self, Error> + 'a { 38 type InnerGspSystemInfo = bindings::GspSystemInfo; 39 let pci_config_mirror_range = chipset.pci_config_mirror_range(); 40 let init_inner = try_init!(InnerGspSystemInfo { 41 gpuPhysAddr: dev.resource_start(0)?, 42 gpuPhysFbAddr: dev.resource_start(1)?, 43 gpuPhysInstAddr: dev.resource_start(3)?, 44 nvDomainBusDeviceFunc: u64::from(dev.dev_id()), 45 46 // Using TASK_SIZE in r535_gsp_rpc_set_system_info() seems wrong because 47 // TASK_SIZE is per-task. That's probably a design issue in GSP-RM though. 48 maxUserVa: (1 << 47) - 4096, 49 pciConfigMirrorBase: pci_config_mirror_range.start, 50 pciConfigMirrorSize: pci_config_mirror_range.end - pci_config_mirror_range.start, 51 52 PCIDeviceID: (u32::from(dev.device_id()) << 16) | u32::from(dev.vendor_id().as_raw()), 53 PCISubDeviceID: (u32::from(dev.subsystem_device_id()) << 16) 54 | u32::from(dev.subsystem_vendor_id()), 55 PCIRevisionID: u32::from(dev.revision_id()), 56 bIsPrimary: 0, 57 bPreserveVideoMemoryAllocations: 0, 58 ..Zeroable::init_zeroed() 59 }); 60 61 try_init!(GspSetSystemInfo { 62 inner <- init_inner, 63 }) 64 } 65 } 66 67 // SAFETY: These structs don't meet the no-padding requirements of AsBytes but 68 // that is not a problem because they are not used outside the kernel. 69 unsafe impl AsBytes for GspSetSystemInfo {} 70 71 // SAFETY: These structs don't meet the no-padding requirements of FromBytes but 72 // that is not a problem because they are not used outside the kernel. 73 unsafe impl FromBytes for GspSetSystemInfo {} 74 75 #[repr(transparent)] 76 pub(crate) struct PackedRegistryEntry(bindings::PACKED_REGISTRY_ENTRY); 77 78 impl PackedRegistryEntry { 79 pub(crate) fn new(offset: u32, value: u32) -> Self { 80 Self({ 81 bindings::PACKED_REGISTRY_ENTRY { 82 nameOffset: offset, 83 84 // We only support DWORD types for now. Support for other types 85 // will come later if required. 86 type_: bindings::REGISTRY_TABLE_ENTRY_TYPE_DWORD as u8, 87 __bindgen_padding_0: Default::default(), 88 data: value, 89 length: 0, 90 } 91 }) 92 } 93 } 94 95 // SAFETY: Padding is explicit and will not contain uninitialized data. 96 unsafe impl AsBytes for PackedRegistryEntry {} 97 98 /// Payload of the `SetRegistry` command. 99 #[repr(transparent)] 100 pub(crate) struct PackedRegistryTable { 101 inner: bindings::PACKED_REGISTRY_TABLE, 102 } 103 104 impl PackedRegistryTable { 105 #[allow(non_snake_case)] 106 pub(crate) fn init(num_entries: u32, size: u32) -> impl Init<Self> { 107 type InnerPackedRegistryTable = bindings::PACKED_REGISTRY_TABLE; 108 let init_inner = init!(InnerPackedRegistryTable { 109 numEntries: num_entries, 110 size, 111 entries: Default::default() 112 }); 113 114 init!(PackedRegistryTable { inner <- init_inner }) 115 } 116 } 117 118 // SAFETY: Padding is explicit and will not contain uninitialized data. 119 unsafe impl AsBytes for PackedRegistryTable {} 120 121 // SAFETY: This struct only contains integer types for which all bit patterns 122 // are valid. 123 unsafe impl FromBytes for PackedRegistryTable {} 124 125 /// Payload of the `GetGspStaticInfo` command and message. 126 #[repr(transparent)] 127 #[derive(Zeroable)] 128 pub(crate) struct GspStaticConfigInfo(bindings::GspStaticConfigInfo_t); 129 130 impl GspStaticConfigInfo { 131 /// Returns a bytes array containing the (hopefully) zero-terminated name of this GPU. 132 pub(crate) fn gpu_name_str(&self) -> [u8; 64] { 133 self.0.gpuNameString 134 } 135 136 /// Returns an iterator over valid FB regions from GSP firmware data. 137 fn fb_regions( 138 &self, 139 ) -> impl Iterator<Item = &bindings::NV2080_CTRL_CMD_FB_GET_FB_REGION_FB_REGION_INFO> { 140 let fb_info = &self.0.fbRegionInfoParams; 141 fb_info 142 .fbRegion 143 .iter() 144 .take(fb_info.numFBRegions.into_safe_cast()) 145 .filter(|reg| reg.limit >= reg.base) 146 } 147 148 /// Iterates over usable FB regions from GSP firmware data. 149 /// 150 /// Each yielded region is a [`Range<u64>`] suitable for driver memory allocation. 151 /// Usable regions are those that satisfy all the following properties: 152 /// - Are not reserved for firmware internal use. 153 /// - Are not protected (hardware-enforced access restrictions). 154 /// - Support compression (can use GPU memory compression for bandwidth). 155 /// - Support ISO (isochronous memory for display requiring guaranteed bandwidth). 156 pub(crate) fn usable_fb_regions(&self) -> impl Iterator<Item = Range<u64>> + '_ { 157 self.fb_regions().filter_map(|reg| { 158 // Filter: not reserved, not protected, supports compression and ISO. 159 if reg.reserved == 0 160 && reg.bProtected == 0 161 && reg.supportCompressed != 0 162 && reg.supportISO != 0 163 { 164 reg.limit.checked_add(1).map(|end| reg.base..end) 165 } else { 166 None 167 } 168 }) 169 } 170 } 171 172 // SAFETY: Padding is explicit and will not contain uninitialized data. 173 unsafe impl AsBytes for GspStaticConfigInfo {} 174 175 // SAFETY: This struct only contains integer types for which all bit patterns 176 // are valid. 177 unsafe impl FromBytes for GspStaticConfigInfo {} 178 179 /// Power level requested to the [`UnloadingGuestDriver`] command. 180 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 181 #[repr(u32)] 182 #[expect(unused)] 183 pub(crate) enum PowerStateLevel { 184 /// Full unload. 185 Level0 = bindings::NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_0, 186 /// S3 (suspend to RAM). 187 Level3 = bindings::NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_3, 188 /// Hibernate (suspend to disk). 189 Level7 = bindings::NV2080_CTRL_GPU_SET_POWER_STATE_GPU_LEVEL_7, 190 } 191 192 impl PowerStateLevel { 193 /// Returns `true` if this state represents a power management transition, i.e. some GPU state 194 /// must survive it (as opposed to a full unload). 195 pub(crate) fn is_power_transition(self) -> bool { 196 self != PowerStateLevel::Level0 197 } 198 } 199 200 /// Payload of the `UnloadingGuestDriver` command and message. 201 #[repr(transparent)] 202 #[derive(Clone, Copy, Debug, Zeroable)] 203 pub(crate) struct UnloadingGuestDriver(bindings::rpc_unloading_guest_driver_v1F_07); 204 205 impl UnloadingGuestDriver { 206 pub(crate) fn new(level: PowerStateLevel) -> Self { 207 Self(bindings::rpc_unloading_guest_driver_v1F_07 { 208 bInPMTransition: u8::from(level.is_power_transition()), 209 bGc6Entering: 0, 210 newLevel: level as u32, 211 ..Zeroable::zeroed() 212 }) 213 } 214 } 215 216 // SAFETY: Padding is explicit and will not contain uninitialized data. 217 unsafe impl AsBytes for UnloadingGuestDriver {} 218 219 // SAFETY: This struct only contains integer types for which all bit patterns 220 // are valid. 221 unsafe impl FromBytes for UnloadingGuestDriver {} 222