xref: /linux/drivers/gpu/nova-core/gsp/boot.rs (revision 75d59327367dc6e2141cf4e11cdf57c55851b5c2)
1 // SPDX-License-Identifier: GPL-2.0
2 // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3 
4 use kernel::{
5     bits,
6     device,
7     dma::Coherent,
8     io::poll::read_poll_timeout,
9     pci,
10     prelude::*,
11     time::Delta,
12     types::ScopeGuard, //
13 };
14 
15 use crate::{
16     driver::Bar0,
17     falcon::{
18         gsp::Gsp,
19         sec2::Sec2,
20         Falcon, //
21     },
22     fb::FbLayout,
23     firmware::{
24         gsp::GspFirmware,
25         FIRMWARE_VERSION, //
26     },
27     gpu::Chipset,
28     gsp::{
29         cmdq::Cmdq,
30         commands,
31         GspFwWprMeta, //
32     },
33 };
34 
35 /// Arguments required to call [`Gsp::unload`](super::Gsp::unload).
36 ///
37 /// Stored as their own type to avoid repeating a long and tedious list in [`BootUnloadGuard`].
38 pub(super) struct BootUnloadArgs<'a> {
39     gsp: &'a super::Gsp,
40     dev: &'a device::Device<device::Bound>,
41     bar: &'a Bar0,
42     gsp_falcon: &'a Falcon<Gsp>,
43     sec2_falcon: &'a Falcon<Sec2>,
44     unload_bundle: Option<super::UnloadBundle>,
45 }
46 
47 /// Guard that calls [`Gsp::unload`](super::Gsp::unload) with a
48 /// [`UnloadBundle`](super::UnloadBundle) when dropped.
49 ///
50 /// Used to ensure the `UnloadBundle` is run during failure paths.
51 pub(super) struct BootUnloadGuard<'a> {
52     guard: ScopeGuard<BootUnloadArgs<'a>, fn(BootUnloadArgs<'a>)>,
53 }
54 
55 impl<'a> BootUnloadGuard<'a> {
56     /// Wraps `unload_bundle` into a guard that executes it when dropped.
57     pub(super) fn new(
58         gsp: &'a super::Gsp,
59         dev: &'a device::Device<device::Bound>,
60         bar: &'a Bar0,
61         gsp_falcon: &'a Falcon<Gsp>,
62         sec2_falcon: &'a Falcon<Sec2>,
63         unload_bundle: Option<super::UnloadBundle>,
64     ) -> Self {
65         Self {
66             guard: ScopeGuard::new_with_data(
67                 BootUnloadArgs {
68                     gsp,
69                     dev,
70                     bar,
71                     gsp_falcon,
72                     sec2_falcon,
73                     unload_bundle,
74                 },
75                 |args| {
76                     let _ = super::Gsp::unload(
77                         args.gsp,
78                         args.dev,
79                         args.bar,
80                         args.gsp_falcon,
81                         args.sec2_falcon,
82                         args.unload_bundle,
83                     );
84                 },
85             ),
86         }
87     }
88 
89     /// Disarms the guard and returns the [`UnloadBundle`](super::UnloadBundle) it contains.
90     pub(super) fn dismiss(self) -> Option<super::UnloadBundle> {
91         self.guard.dismiss().unload_bundle
92     }
93 }
94 
95 impl super::Gsp {
96     /// Attempt to boot the GSP.
97     ///
98     /// This is a GPU-dependent and complex procedure that involves loading firmware files from
99     /// user-space, patching them with signatures, and building firmware-specific intricate data
100     /// structures that the GSP will use at runtime.
101     ///
102     /// Upon return, the GSP is up and running, and its unload bundle (to be given as argument to
103     /// [`Self::unload`]) returned.
104     pub(crate) fn boot(
105         self: Pin<&mut Self>,
106         pdev: &pci::Device<device::Bound>,
107         bar: &Bar0,
108         chipset: Chipset,
109         gsp_falcon: &Falcon<Gsp>,
110         sec2_falcon: &Falcon<Sec2>,
111     ) -> Result<Option<super::UnloadBundle>> {
112         let dev = pdev.as_ref();
113         let hal = super::hal::gsp_hal(chipset);
114 
115         let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset, FIRMWARE_VERSION), GFP_KERNEL)?;
116 
117         let fb_layout = FbLayout::new(chipset, bar, &gsp_fw)?;
118         dev_dbg!(dev, "{:#x?}\n", fb_layout);
119 
120         let wpr_meta = Coherent::init(dev, GFP_KERNEL, GspFwWprMeta::new(&gsp_fw, &fb_layout))?;
121 
122         // Perform the chipset-specific boot sequence, and retrieve the unload bundle.
123         let unload_guard = hal.boot(
124             &self,
125             dev,
126             bar,
127             chipset,
128             &fb_layout,
129             &wpr_meta,
130             gsp_falcon,
131             sec2_falcon,
132         )?;
133 
134         gsp_falcon.write_os_version(bar, gsp_fw.bootloader.app_version);
135 
136         // Poll for RISC-V to become active before continuing.
137         read_poll_timeout(
138             || Ok(gsp_falcon.is_riscv_active(bar)),
139             |val: &bool| *val,
140             Delta::from_millis(10),
141             Delta::from_secs(5),
142         )?;
143 
144         dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(bar),);
145 
146         self.cmdq
147             .send_command_no_wait(bar, commands::SetSystemInfo::new(pdev))?;
148         self.cmdq
149             .send_command_no_wait(bar, commands::SetRegistry::new())?;
150 
151         hal.post_boot(&self, dev, bar, &gsp_fw, gsp_falcon, sec2_falcon)?;
152 
153         // Wait until GSP is fully initialized.
154         commands::wait_gsp_init_done(&self.cmdq)?;
155 
156         // Obtain and display basic GPU information.
157         let info = self.cmdq.send_command(bar, commands::GetGspStaticInfo)?;
158         match info.gpu_name() {
159             Ok(name) => dev_info!(pdev, "GPU name: {}\n", name),
160             Err(e) => dev_warn!(pdev, "GPU name unavailable: {:?}\n", e),
161         }
162 
163         Ok(unload_guard.dismiss())
164     }
165 
166     /// Shut down the GSP and wait until it is offline.
167     fn shutdown_gsp(
168         cmdq: &Cmdq,
169         bar: &Bar0,
170         gsp_falcon: &Falcon<Gsp>,
171         mode: commands::PowerStateLevel,
172     ) -> Result {
173         // Command to shut the GSP down.
174         cmdq.send_command(bar, commands::UnloadingGuestDriver::new(mode))?;
175 
176         // Wait until GSP signals it is suspended.
177         const LIBOS_INTERRUPT_PROCESSOR_SUSPENDED: u32 = bits::bit_u32(31);
178         read_poll_timeout(
179             || Ok(gsp_falcon.read_mailbox0(bar)),
180             |&mb0| mb0 & LIBOS_INTERRUPT_PROCESSOR_SUSPENDED != 0,
181             Delta::from_millis(10),
182             Delta::from_secs(5),
183         )
184         .map(|_| ())
185     }
186 
187     /// Attempts to unload the GSP firmware.
188     ///
189     /// This stops all activity on the GSP.
190     pub(crate) fn unload(
191         &self,
192         dev: &device::Device<device::Bound>,
193         bar: &Bar0,
194         gsp_falcon: &Falcon<Gsp>,
195         sec2_falcon: &Falcon<Sec2>,
196         unload_bundle: Option<super::UnloadBundle>,
197     ) -> Result {
198         // Shut down the GSP. Keep going even in case of error.
199         let mut res = Self::shutdown_gsp(
200             &self.cmdq,
201             bar,
202             gsp_falcon,
203             commands::PowerStateLevel::Level0,
204         )
205         .inspect_err(|e| dev_err!(dev, "GSP shutdown failed: {:?}\n", e));
206 
207         // Run the unload bundle to reset the GSP so it can be booted again.
208         if let Some(unload_bundle) = unload_bundle {
209             res = res.and(
210                 unload_bundle
211                     .0
212                     .run(dev, bar, gsp_falcon, sec2_falcon)
213                     .inspect_err(|e| dev_err!(dev, "Unload bundle failed: {:?}\n", e)),
214             );
215         } else {
216             dev_warn!(
217                 dev,
218                 "Unload bundle is missing, GSP won't be properly reset.\n"
219             );
220 
221             res = Err(EAGAIN);
222         }
223 
224         res.inspect(|()| dev_info!(dev, "GSP successfully unloaded\n"))
225     }
226 }
227