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