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