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