1 // SPDX-License-Identifier: GPL-2.0 2 // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 4 use kernel::{ 5 bits, 6 device, 7 dma::Coherent, 8 io::poll::read_poll_timeout, 9 pci, 10 prelude::*, 11 time::Delta, 12 types::ScopeGuard, // 13 }; 14 15 use crate::{ 16 driver::Bar0, 17 falcon::{ 18 gsp::Gsp, 19 sec2::Sec2, 20 Falcon, // 21 }, 22 fb::FbLayout, 23 firmware::{ 24 gsp::GspFirmware, 25 FIRMWARE_VERSION, // 26 }, 27 gpu::Chipset, 28 gsp::{ 29 cmdq::Cmdq, 30 commands, 31 GspFwWprMeta, // 32 }, 33 }; 34 35 /// Arguments required to call [`Gsp::unload`](super::Gsp::unload). 36 /// 37 /// Stored as their own type to avoid repeating a long and tedious list in [`BootUnloadGuard`]. 38 pub(super) struct BootUnloadArgs<'a> { 39 gsp: &'a super::Gsp, 40 dev: &'a device::Device<device::Bound>, 41 bar: &'a Bar0, 42 gsp_falcon: &'a Falcon<Gsp>, 43 sec2_falcon: &'a Falcon<Sec2>, 44 unload_bundle: Option<super::UnloadBundle>, 45 } 46 47 /// Guard that calls [`Gsp::unload`](super::Gsp::unload) with a 48 /// [`UnloadBundle`](super::UnloadBundle) when dropped. 49 /// 50 /// Used to ensure the `UnloadBundle` is run during failure paths. 51 pub(super) struct BootUnloadGuard<'a> { 52 guard: ScopeGuard<BootUnloadArgs<'a>, fn(BootUnloadArgs<'a>)>, 53 } 54 55 impl<'a> BootUnloadGuard<'a> { 56 /// Wraps `unload_bundle` into a guard that executes it when dropped. 57 pub(super) fn new( 58 gsp: &'a super::Gsp, 59 dev: &'a device::Device<device::Bound>, 60 bar: &'a Bar0, 61 gsp_falcon: &'a Falcon<Gsp>, 62 sec2_falcon: &'a Falcon<Sec2>, 63 unload_bundle: Option<super::UnloadBundle>, 64 ) -> Self { 65 Self { 66 guard: ScopeGuard::new_with_data( 67 BootUnloadArgs { 68 gsp, 69 dev, 70 bar, 71 gsp_falcon, 72 sec2_falcon, 73 unload_bundle, 74 }, 75 |args| { 76 let _ = super::Gsp::unload( 77 args.gsp, 78 args.dev, 79 args.bar, 80 args.gsp_falcon, 81 args.sec2_falcon, 82 args.unload_bundle, 83 ); 84 }, 85 ), 86 } 87 } 88 89 /// Disarms the guard and returns the [`UnloadBundle`](super::UnloadBundle) it contains. 90 pub(super) fn dismiss(self) -> Option<super::UnloadBundle> { 91 self.guard.dismiss().unload_bundle 92 } 93 } 94 95 impl super::Gsp { 96 /// Attempt to boot the GSP. 97 /// 98 /// This is a GPU-dependent and complex procedure that involves loading firmware files from 99 /// user-space, patching them with signatures, and building firmware-specific intricate data 100 /// structures that the GSP will use at runtime. 101 /// 102 /// Upon return, the GSP is up and running, and its unload bundle (to be given as argument to 103 /// [`Self::unload`]) returned. 104 pub(crate) fn boot( 105 self: Pin<&mut Self>, 106 pdev: &pci::Device<device::Bound>, 107 bar: &Bar0, 108 chipset: Chipset, 109 gsp_falcon: &Falcon<Gsp>, 110 sec2_falcon: &Falcon<Sec2>, 111 ) -> Result<Option<super::UnloadBundle>> { 112 let dev = pdev.as_ref(); 113 let hal = super::hal::gsp_hal(chipset); 114 115 let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?; 116 117 let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?; 118 dev_dbg!(dev, "{:#x?}\n", fb_layout); 119 120 let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?; 121 122 // Perform the chipset-specific boot sequence, and retrieve the unload bundle. 123 let unload_guard = hal.boot( 124 &self, 125 dev, 126 bar, 127 chipset, 128 &fb_layout, 129 &wpr_meta, 130 gsp_falcon, 131 sec2_falcon, 132 )?; 133 134 gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version); 135 136 // Poll for RISC-V to become active before continuing. 137 read_poll_timeout( 138 || Ok(gsp_falcon.is_riscv_active(bar)), 139 |val: &bool| *val, 140 Delta::from_millis(10), 141 Delta::from_secs(5), 142 )?; 143 144 dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(bar),); 145 146 self.cmdq 147 .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev))?; 148 self.cmdq 149 .send_command_no_wait(bar, commands::SetRegistry::new())?; 150 151 hal.post_boot(&self, dev, bar, &gsp_fw, gsp_falcon, sec2_falcon)?; 152 153 // Wait until GSP is fully initialized. 154 commands::wait_gsp_init_done(&self.cmdq)?; 155 156 // Obtain and display basic GPU information. 157 let info = self.cmdq.send_command(bar, commands::GetGspStaticInfo)?; 158 match info.gpu_name() { 159 Ok(name) => dev_info!(pdev, "GPU name: {}\n", name), 160 Err(e) => dev_warn!(pdev, "GPU name unavailable: {:?}\n", e), 161 } 162 163 Ok(unload_guard.dismiss()) 164 } 165 166 /// Shut down the GSP and wait until it is offline. 167 fn shutdown_gsp( 168 cmdq: &Cmdq, 169 bar: &Bar0, 170 gsp_falcon: &Falcon<Gsp>, 171 mode: commands::PowerStateLevel, 172 ) -> Result { 173 // Command to shut the GSP down. 174 cmdq.send_command(bar, commands::UnloadingGuestDriver::new(mode))?; 175 176 // Wait until GSP signals it is suspended. 177 const LIBOS_INTERRUPT_PROCESSOR_SUSPENDED: u32 = bits::bit_u32(31); 178 read_poll_timeout( 179 || Ok(gsp_falcon.read_mailbox0(bar)), 180 |&mb0| mb0 & LIBOS_INTERRUPT_PROCESSOR_SUSPENDED != 0, 181 Delta::from_millis(10), 182 Delta::from_secs(5), 183 ) 184 .map(|_| ()) 185 } 186 187 /// Attempts to unload the GSP firmware. 188 /// 189 /// This stops all activity on the GSP. 190 pub(crate) fn unload( 191 &self, 192 dev: &device::Device<device::Bound>, 193 bar: &Bar0, 194 gsp_falcon: &Falcon<Gsp>, 195 sec2_falcon: &Falcon<Sec2>, 196 unload_bundle: Option<super::UnloadBundle>, 197 ) -> Result { 198 // Shut down the GSP. Keep going even in case of error. 199 let mut res = Self::shutdown_gsp( 200 &self.cmdq, 201 bar, 202 gsp_falcon, 203 commands::PowerStateLevel::Level0, 204 ) 205 .inspect_err(|e| dev_err!(dev, "GSP shutdown failed: {:?}\n", e)); 206 207 // Run the unload bundle to reset the GSP so it can be booted again. 208 if let Some(unload_bundle) = unload_bundle { 209 res = res.and( 210 unload_bundle 211 .0 212 .run(dev, bar, gsp_falcon, sec2_falcon) 213 .inspect_err(|e| dev_err!(dev, "Unload bundle failed: {:?}\n", e)), 214 ); 215 } else { 216 dev_warn!( 217 dev, 218 "Unload bundle is missing, GSP won't be properly reset.\n" 219 ); 220 221 res = Err(EAGAIN); 222 } 223 224 res.inspect(|()| dev_info!(dev, "GSP successfully unloaded\n")) 225 } 226 } 227