1 // SPDX-License-Identifier: GPL-2.0 2 // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 3 4 use kernel::prelude::*; 5 6 use kernel::{ 7 device, 8 dma::Coherent, 9 io::poll::read_poll_timeout, 10 time::Delta, 11 types::ScopeGuard, // 12 }; 13 14 use crate::{ 15 falcon::{ 16 gsp::Gsp as GspEngine, 17 Falcon, // 18 }, 19 fb::FbLayout, 20 fsp::FmcBootArgs, 21 gsp::{ 22 hal::{ 23 GspHal, 24 UnloadBundle, // 25 }, 26 Gsp, 27 GspBootContext, 28 GspFmcBootParams, 29 GspFwWprMeta, // 30 }, 31 }; 32 33 /// GSP falcon mailbox state, used to track lockdown release status. 34 struct GspMbox { 35 mbox0: u32, 36 mbox1: u32, 37 } 38 39 impl GspMbox { 40 /// Reads both mailboxes from the GSP falcon. 41 fn read(gsp_falcon: &Falcon<'_, GspEngine>) -> Self { 42 Self { 43 mbox0: gsp_falcon.read_mailbox0(), 44 mbox1: gsp_falcon.read_mailbox1(), 45 } 46 } 47 48 /// Combines mailbox0 and mailbox1 into a 64-bit address. 49 fn combined_addr(&self) -> u64 { 50 (u64::from(self.mbox1) << 32) | u64::from(self.mbox0) 51 } 52 53 /// Returns `true` if GSP lockdown has been released or a GSP-FMC error happened. 54 /// 55 /// Returns `true` both on successful lockdown release and on GSP-FMC-reported errors, since 56 /// either condition should stop the poll loop. 57 fn lockdown_released_or_error( 58 &self, 59 gsp_falcon: &Falcon<'_, GspEngine>, 60 fmc_boot_params: &Coherent<GspFmcBootParams>, 61 ) -> bool { 62 // GSP-FMC normally clears the boot parameters address from the mailboxes early during 63 // boot. If the address is still there, keep polling rather than treating it as an error. 64 // Any other non-zero mailbox0 value is a GSP-FMC error code. 65 if self.mbox0 != 0 { 66 return self.combined_addr() != fmc_boot_params.dma_handle(); 67 } 68 69 !gsp_falcon.riscv_branch_privilege_lockdown() 70 } 71 } 72 73 /// Waits for GSP lockdown to be released after FSP Chain of Trust. 74 fn wait_for_gsp_lockdown_release( 75 dev: &device::Device<device::Bound>, 76 gsp_falcon: &Falcon<'_, GspEngine>, 77 fmc_boot_params: &Coherent<GspFmcBootParams>, 78 ) -> Result { 79 dev_dbg!(dev, "Waiting for GSP lockdown release\n"); 80 81 let mbox = read_poll_timeout( 82 || { 83 // While the PRIV target mask is still locked to FSP, GSP register and mailbox reads 84 // are not meaningful. Wait until HWCFG2 says the CPU can read them. 85 Ok(match gsp_falcon.priv_target_mask_released() { 86 false => None, 87 true => Some(GspMbox::read(gsp_falcon)), 88 }) 89 }, 90 |mbox| match mbox { 91 None => false, 92 Some(mbox) => mbox.lockdown_released_or_error(gsp_falcon, fmc_boot_params), 93 }, 94 Delta::from_millis(10), 95 Delta::from_secs(30), 96 ) 97 .inspect_err(|_| { 98 dev_err!(dev, "GSP lockdown release timeout\n"); 99 })? 100 .ok_or(EIO)?; 101 102 // If polling stopped with a non-zero mailbox0, it was not the boot parameters address 103 // anymore and therefore represents a GSP-FMC error code. 104 if mbox.mbox0 != 0 { 105 dev_err!(dev, "GSP-FMC boot failed (mbox: {:#x})\n", mbox.mbox0); 106 return Err(EIO); 107 } 108 109 dev_dbg!(dev, "GSP lockdown released\n"); 110 Ok(()) 111 } 112 113 struct FspUnloadBundle; 114 115 impl UnloadBundle for FspUnloadBundle { 116 fn run(&self, ctx: &mut GspBootContext<'_, '_>) -> Result { 117 // GSP falcon does most of the work of resetting, so just wait for it to finish. 118 read_poll_timeout( 119 || { 120 // GSP register reads are not meaningful until the PRIV target mask is released. 121 if !ctx.gsp_falcon.priv_target_mask_released() { 122 return Ok(false); 123 } 124 125 ctx.gsp_falcon.is_riscv_halted() 126 }, 127 |&halted| halted, 128 Delta::from_millis(10), 129 Delta::from_secs(5), 130 ) 131 .map(|_| ()) 132 .inspect_err(|_| dev_err!(ctx.dev(), "GSP falcon failed to halt\n")) 133 } 134 } 135 136 struct Gh100; 137 138 impl GspHal for Gh100 { 139 /// Boot GSP via FSP Chain of Trust (Hopper/Blackwell+ path). 140 /// 141 /// This path uses FSP to establish a chain of trust and boot GSP-FMC. FSP handles 142 /// the GSP boot internally - no manual GSP reset/boot is needed. 143 fn boot( 144 &self, 145 gsp: &Gsp, 146 ctx: &mut GspBootContext<'_, '_>, 147 fb_layout: &FbLayout, 148 wpr_meta: &Coherent<GspFwWprMeta>, 149 ) -> Result<Option<crate::gsp::UnloadBundle>> { 150 let dev = ctx.dev(); 151 let chipset = ctx.chipset; 152 let gsp_falcon = ctx.gsp_falcon; 153 154 let unload_bundle = crate::gsp::UnloadBundle( 155 KBox::new(FspUnloadBundle, GFP_KERNEL)? as KBox<dyn UnloadBundle> 156 ); 157 158 let args = FmcBootArgs::new(dev, chipset, wpr_meta, &gsp.libos, false)?; 159 160 // Wait for the GSP RISC-V core to halt in case of error. We create this guard after `args` 161 // to make sure that boot args are kept alive until halt, in case they are still being 162 // accessed. 163 let mut unload_guard = 164 ScopeGuard::new_with_data((unload_bundle, ctx), |(unload_bundle, ctx)| { 165 let _ = unload_bundle.0.run(ctx); 166 }); 167 168 let fsp = unload_guard.1.fsp.as_mut().ok_or(ENODEV)?; 169 170 fsp.boot_fmc(dev, fb_layout, &args)?; 171 172 // Wait for GSP-FMC to release the GSP lockdown, indicating that `args` is not accessed 173 // anymore. 174 wait_for_gsp_lockdown_release(dev, gsp_falcon, args.boot_params())?; 175 176 Ok(Some(unload_guard.dismiss().0)) 177 } 178 } 179 180 const GH100: Gh100 = Gh100; 181 pub(super) const GH100_HAL: &dyn GspHal = &GH100; 182