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