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