xref: /linux/drivers/gpu/nova-core/gsp/fw.rs (revision f38b4f105cfc19598bdb512c08f5d27be774f0de)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 mod r570_144;
4 
5 // Alias to avoid repeating the version number with every use.
6 use r570_144 as bindings;
7 
8 use core::ops::Range;
9 
10 use kernel::{
11     dma::CoherentAllocation,
12     ptr::{
13         Alignable,
14         Alignment, //
15     },
16     sizes::SZ_1M,
17     transmute::{
18         AsBytes,
19         FromBytes, //
20     },
21 };
22 
23 use crate::{
24     gpu::Chipset,
25     num::{
26         self,
27         FromSafeCast, //
28     },
29 };
30 
31 /// Empty type to group methods related to heap parameters for running the GSP firmware.
32 enum GspFwHeapParams {}
33 
34 /// Minimum required alignment for the GSP heap.
35 const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new::<{ 1 << 20 }>();
36 
37 impl GspFwHeapParams {
38     /// Returns the amount of GSP-RM heap memory used during GSP-RM boot and initialization (up to
39     /// and including the first client subdevice allocation).
40     fn base_rm_size(_chipset: Chipset) -> u64 {
41         // TODO: this needs to be updated to return the correct value for Hopper+ once support for
42         // them is added:
43         // u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100)
44         u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X)
45     }
46 
47     /// Returns the amount of heap memory required to support a single channel allocation.
48     fn client_alloc_size() -> u64 {
49         u64::from(bindings::GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE)
50             .align_up(GSP_HEAP_ALIGNMENT)
51             .unwrap_or(u64::MAX)
52     }
53 
54     /// Returns the amount of memory to reserve for management purposes for a framebuffer of size
55     /// `fb_size`.
56     fn management_overhead(fb_size: u64) -> u64 {
57         let fb_size_gb = fb_size.div_ceil(u64::from_safe_cast(kernel::sizes::SZ_1G));
58 
59         u64::from(bindings::GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB)
60             .saturating_mul(fb_size_gb)
61             .align_up(GSP_HEAP_ALIGNMENT)
62             .unwrap_or(u64::MAX)
63     }
64 }
65 
66 /// Heap memory requirements and constraints for a given version of the GSP LIBOS.
67 pub(crate) struct LibosParams {
68     /// The base amount of heap required by the GSP operating system, in bytes.
69     carveout_size: u64,
70     /// The minimum and maximum sizes allowed for the GSP FW heap, in bytes.
71     allowed_heap_size: Range<u64>,
72 }
73 
74 impl LibosParams {
75     /// Version 2 of the GSP LIBOS (Turing and GA100)
76     const LIBOS2: LibosParams = LibosParams {
77         carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2),
78         allowed_heap_size: num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB)
79             * num::usize_as_u64(SZ_1M)
80             ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB)
81                 * num::usize_as_u64(SZ_1M),
82     };
83 
84     /// Version 3 of the GSP LIBOS (GA102+)
85     const LIBOS3: LibosParams = LibosParams {
86         carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL),
87         allowed_heap_size: num::u32_as_u64(
88             bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB,
89         ) * num::usize_as_u64(SZ_1M)
90             ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB)
91                 * num::usize_as_u64(SZ_1M),
92     };
93 
94     /// Returns the libos parameters corresponding to `chipset`.
95     pub(crate) fn from_chipset(chipset: Chipset) -> &'static LibosParams {
96         if chipset < Chipset::GA102 {
97             &Self::LIBOS2
98         } else {
99             &Self::LIBOS3
100         }
101     }
102 
103     /// Returns the amount of memory (in bytes) to allocate for the WPR heap for a framebuffer size
104     /// of `fb_size` (in bytes) for `chipset`.
105     pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> u64 {
106         // The WPR heap will contain the following:
107         // LIBOS carveout,
108         self.carveout_size
109             // RM boot working memory,
110             .saturating_add(GspFwHeapParams::base_rm_size(chipset))
111             // One RM client,
112             .saturating_add(GspFwHeapParams::client_alloc_size())
113             // Overhead for memory management.
114             .saturating_add(GspFwHeapParams::management_overhead(fb_size))
115             // Clamp to the supported heap sizes.
116             .clamp(self.allowed_heap_size.start, self.allowed_heap_size.end - 1)
117     }
118 }
119 
120 /// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA
121 /// addresses of the GSP bootloader and firmware.
122 #[repr(transparent)]
123 pub(crate) struct GspFwWprMeta(bindings::GspFwWprMeta);
124 
125 /// Struct containing the arguments required to pass a memory buffer to the GSP
126 /// for use during initialisation.
127 ///
128 /// The GSP only understands 4K pages (GSP_PAGE_SIZE), so even if the kernel is
129 /// configured for a larger page size (e.g. 64K pages), we need to give
130 /// the GSP an array of 4K pages. Since we only create physically contiguous
131 /// buffers the math to calculate the addresses is simple.
132 ///
133 /// The buffers must be a multiple of GSP_PAGE_SIZE.  GSP-RM also currently
134 /// ignores the @kind field for LOGINIT, LOGINTR, and LOGRM, but expects the
135 /// buffers to be physically contiguous anyway.
136 ///
137 /// The memory allocated for the arguments must remain until the GSP sends the
138 /// init_done RPC.
139 #[repr(transparent)]
140 pub(crate) struct LibosMemoryRegionInitArgument(bindings::LibosMemoryRegionInitArgument);
141 
142 // SAFETY: Padding is explicit and does not contain uninitialized data.
143 unsafe impl AsBytes for LibosMemoryRegionInitArgument {}
144 
145 // SAFETY: This struct only contains integer types for which all bit patterns
146 // are valid.
147 unsafe impl FromBytes for LibosMemoryRegionInitArgument {}
148 
149 impl LibosMemoryRegionInitArgument {
150     pub(crate) fn new<A: AsBytes + FromBytes>(
151         name: &'static str,
152         obj: &CoherentAllocation<A>,
153     ) -> Self {
154         /// Generates the `ID8` identifier required for some GSP objects.
155         fn id8(name: &str) -> u64 {
156             let mut bytes = [0u8; core::mem::size_of::<u64>()];
157 
158             for (c, b) in name.bytes().rev().zip(&mut bytes) {
159                 *b = c;
160             }
161 
162             u64::from_ne_bytes(bytes)
163         }
164 
165         Self(bindings::LibosMemoryRegionInitArgument {
166             id8: id8(name),
167             pa: obj.dma_handle(),
168             size: num::usize_as_u64(obj.size()),
169             kind: num::u32_into_u8::<
170                 { bindings::LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS },
171             >(),
172             loc: num::u32_into_u8::<
173                 { bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM },
174             >(),
175             ..Default::default()
176         })
177     }
178 }
179