xref: /linux/drivers/gpu/nova-core/gsp/hal/gh100.rs (revision 5c9deba5578db5a3ea05be8a3e4a59ecb08ee76f)
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