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