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::{ 17 SZ_128K, 18 SZ_1M, // 19 }, 20 transmute::{ 21 AsBytes, 22 FromBytes, // 23 }, 24 }; 25 26 use crate::{ 27 fb::FbLayout, 28 firmware::gsp::GspFirmware, 29 gpu::Chipset, 30 num::{ 31 self, 32 FromSafeCast, // 33 }, 34 }; 35 36 /// Empty type to group methods related to heap parameters for running the GSP firmware. 37 enum GspFwHeapParams {} 38 39 /// Minimum required alignment for the GSP heap. 40 const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new::<{ 1 << 20 }>(); 41 42 impl GspFwHeapParams { 43 /// Returns the amount of GSP-RM heap memory used during GSP-RM boot and initialization (up to 44 /// and including the first client subdevice allocation). 45 fn base_rm_size(_chipset: Chipset) -> u64 { 46 // TODO: this needs to be updated to return the correct value for Hopper+ once support for 47 // them is added: 48 // u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_GH100) 49 u64::from(bindings::GSP_FW_HEAP_PARAM_BASE_RM_SIZE_TU10X) 50 } 51 52 /// Returns the amount of heap memory required to support a single channel allocation. 53 fn client_alloc_size() -> u64 { 54 u64::from(bindings::GSP_FW_HEAP_PARAM_CLIENT_ALLOC_SIZE) 55 .align_up(GSP_HEAP_ALIGNMENT) 56 .unwrap_or(u64::MAX) 57 } 58 59 /// Returns the amount of memory to reserve for management purposes for a framebuffer of size 60 /// `fb_size`. 61 fn management_overhead(fb_size: u64) -> u64 { 62 let fb_size_gb = fb_size.div_ceil(u64::from_safe_cast(kernel::sizes::SZ_1G)); 63 64 u64::from(bindings::GSP_FW_HEAP_PARAM_SIZE_PER_GB_FB) 65 .saturating_mul(fb_size_gb) 66 .align_up(GSP_HEAP_ALIGNMENT) 67 .unwrap_or(u64::MAX) 68 } 69 } 70 71 /// Heap memory requirements and constraints for a given version of the GSP LIBOS. 72 pub(crate) struct LibosParams { 73 /// The base amount of heap required by the GSP operating system, in bytes. 74 carveout_size: u64, 75 /// The minimum and maximum sizes allowed for the GSP FW heap, in bytes. 76 allowed_heap_size: Range<u64>, 77 } 78 79 impl LibosParams { 80 /// Version 2 of the GSP LIBOS (Turing and GA100) 81 const LIBOS2: LibosParams = LibosParams { 82 carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS2), 83 allowed_heap_size: num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MIN_MB) 84 * num::usize_as_u64(SZ_1M) 85 ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS2_MAX_MB) 86 * num::usize_as_u64(SZ_1M), 87 }; 88 89 /// Version 3 of the GSP LIBOS (GA102+) 90 const LIBOS3: LibosParams = LibosParams { 91 carveout_size: num::u32_as_u64(bindings::GSP_FW_HEAP_PARAM_OS_SIZE_LIBOS3_BAREMETAL), 92 allowed_heap_size: num::u32_as_u64( 93 bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MIN_MB, 94 ) * num::usize_as_u64(SZ_1M) 95 ..num::u32_as_u64(bindings::GSP_FW_HEAP_SIZE_OVERRIDE_LIBOS3_BAREMETAL_MAX_MB) 96 * num::usize_as_u64(SZ_1M), 97 }; 98 99 /// Returns the libos parameters corresponding to `chipset`. 100 pub(crate) fn from_chipset(chipset: Chipset) -> &'static LibosParams { 101 if chipset < Chipset::GA102 { 102 &Self::LIBOS2 103 } else { 104 &Self::LIBOS3 105 } 106 } 107 108 /// Returns the amount of memory (in bytes) to allocate for the WPR heap for a framebuffer size 109 /// of `fb_size` (in bytes) for `chipset`. 110 pub(crate) fn wpr_heap_size(&self, chipset: Chipset, fb_size: u64) -> u64 { 111 // The WPR heap will contain the following: 112 // LIBOS carveout, 113 self.carveout_size 114 // RM boot working memory, 115 .saturating_add(GspFwHeapParams::base_rm_size(chipset)) 116 // One RM client, 117 .saturating_add(GspFwHeapParams::client_alloc_size()) 118 // Overhead for memory management. 119 .saturating_add(GspFwHeapParams::management_overhead(fb_size)) 120 // Clamp to the supported heap sizes. 121 .clamp(self.allowed_heap_size.start, self.allowed_heap_size.end - 1) 122 } 123 } 124 125 /// Structure passed to the GSP bootloader, containing the framebuffer layout as well as the DMA 126 /// addresses of the GSP bootloader and firmware. 127 #[repr(transparent)] 128 pub(crate) struct GspFwWprMeta(bindings::GspFwWprMeta); 129 130 // SAFETY: Padding is explicit and does not contain uninitialized data. 131 unsafe impl AsBytes for GspFwWprMeta {} 132 133 // SAFETY: This struct only contains integer types for which all bit patterns 134 // are valid. 135 unsafe impl FromBytes for GspFwWprMeta {} 136 137 type GspFwWprMetaBootResumeInfo = r570_144::GspFwWprMeta__bindgen_ty_1; 138 type GspFwWprMetaBootInfo = r570_144::GspFwWprMeta__bindgen_ty_1__bindgen_ty_1; 139 140 impl GspFwWprMeta { 141 /// Fill in and return a `GspFwWprMeta` suitable for booting `gsp_firmware` using the 142 /// `fb_layout` layout. 143 pub(crate) fn new(gsp_firmware: &GspFirmware, fb_layout: &FbLayout) -> Self { 144 Self(bindings::GspFwWprMeta { 145 // CAST: we want to store the bits of `GSP_FW_WPR_META_MAGIC` unmodified. 146 magic: r570_144::GSP_FW_WPR_META_MAGIC as u64, 147 revision: u64::from(r570_144::GSP_FW_WPR_META_REVISION), 148 sysmemAddrOfRadix3Elf: gsp_firmware.radix3_dma_handle(), 149 sizeOfRadix3Elf: u64::from_safe_cast(gsp_firmware.size), 150 sysmemAddrOfBootloader: gsp_firmware.bootloader.ucode.dma_handle(), 151 sizeOfBootloader: u64::from_safe_cast(gsp_firmware.bootloader.ucode.size()), 152 bootloaderCodeOffset: u64::from(gsp_firmware.bootloader.code_offset), 153 bootloaderDataOffset: u64::from(gsp_firmware.bootloader.data_offset), 154 bootloaderManifestOffset: u64::from(gsp_firmware.bootloader.manifest_offset), 155 __bindgen_anon_1: GspFwWprMetaBootResumeInfo { 156 __bindgen_anon_1: GspFwWprMetaBootInfo { 157 sysmemAddrOfSignature: gsp_firmware.signatures.dma_handle(), 158 sizeOfSignature: u64::from_safe_cast(gsp_firmware.signatures.size()), 159 }, 160 }, 161 gspFwRsvdStart: fb_layout.heap.start, 162 nonWprHeapOffset: fb_layout.heap.start, 163 nonWprHeapSize: fb_layout.heap.end - fb_layout.heap.start, 164 gspFwWprStart: fb_layout.wpr2.start, 165 gspFwHeapOffset: fb_layout.wpr2_heap.start, 166 gspFwHeapSize: fb_layout.wpr2_heap.end - fb_layout.wpr2_heap.start, 167 gspFwOffset: fb_layout.elf.start, 168 bootBinOffset: fb_layout.boot.start, 169 frtsOffset: fb_layout.frts.start, 170 frtsSize: fb_layout.frts.end - fb_layout.frts.start, 171 gspFwWprEnd: fb_layout 172 .vga_workspace 173 .start 174 .align_down(Alignment::new::<SZ_128K>()), 175 gspFwHeapVfPartitionCount: fb_layout.vf_partition_count, 176 fbSize: fb_layout.fb.end - fb_layout.fb.start, 177 vgaWorkspaceOffset: fb_layout.vga_workspace.start, 178 vgaWorkspaceSize: fb_layout.vga_workspace.end - fb_layout.vga_workspace.start, 179 ..Default::default() 180 }) 181 } 182 } 183 184 /// Struct containing the arguments required to pass a memory buffer to the GSP 185 /// for use during initialisation. 186 /// 187 /// The GSP only understands 4K pages (GSP_PAGE_SIZE), so even if the kernel is 188 /// configured for a larger page size (e.g. 64K pages), we need to give 189 /// the GSP an array of 4K pages. Since we only create physically contiguous 190 /// buffers the math to calculate the addresses is simple. 191 /// 192 /// The buffers must be a multiple of GSP_PAGE_SIZE. GSP-RM also currently 193 /// ignores the @kind field for LOGINIT, LOGINTR, and LOGRM, but expects the 194 /// buffers to be physically contiguous anyway. 195 /// 196 /// The memory allocated for the arguments must remain until the GSP sends the 197 /// init_done RPC. 198 #[repr(transparent)] 199 pub(crate) struct LibosMemoryRegionInitArgument(bindings::LibosMemoryRegionInitArgument); 200 201 // SAFETY: Padding is explicit and does not contain uninitialized data. 202 unsafe impl AsBytes for LibosMemoryRegionInitArgument {} 203 204 // SAFETY: This struct only contains integer types for which all bit patterns 205 // are valid. 206 unsafe impl FromBytes for LibosMemoryRegionInitArgument {} 207 208 impl LibosMemoryRegionInitArgument { 209 pub(crate) fn new<A: AsBytes + FromBytes>( 210 name: &'static str, 211 obj: &CoherentAllocation<A>, 212 ) -> Self { 213 /// Generates the `ID8` identifier required for some GSP objects. 214 fn id8(name: &str) -> u64 { 215 let mut bytes = [0u8; core::mem::size_of::<u64>()]; 216 217 for (c, b) in name.bytes().rev().zip(&mut bytes) { 218 *b = c; 219 } 220 221 u64::from_ne_bytes(bytes) 222 } 223 224 Self(bindings::LibosMemoryRegionInitArgument { 225 id8: id8(name), 226 pa: obj.dma_handle(), 227 size: num::usize_as_u64(obj.size()), 228 kind: num::u32_into_u8::< 229 { bindings::LibosMemoryRegionKind_LIBOS_MEMORY_REGION_CONTIGUOUS }, 230 >(), 231 loc: num::u32_into_u8::< 232 { bindings::LibosMemoryRegionLoc_LIBOS_MEMORY_REGION_LOC_SYSMEM }, 233 >(), 234 ..Default::default() 235 }) 236 } 237 } 238