xref: /linux/drivers/gpu/nova-core/gsp/fw/commands.rs (revision 23d66dbab84e8518943563df2ced14aaab28b77a)
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