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