1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
|
// SPDX-License-Identifier: GPL-2.0
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
use kernel::{
bits,
io::poll::read_poll_timeout,
prelude::*,
time::Delta,
types::ScopeGuard, //
};
use crate::{
driver::Bar0,
falcon::{
gsp::Gsp,
Falcon, //
},
firmware::gsp::GspFirmware,
gsp::{
cmdq::Cmdq,
commands, //
},
};
impl super::Gsp {
/// Attempt to boot the GSP.
///
/// This is a GPU-dependent and complex procedure that involves loading firmware files from
/// user-space, patching them with signatures, and building firmware-specific intricate data
/// structures that the GSP will use at runtime.
///
/// Upon return, the GSP is up and running, and its unload bundle (to be given as argument to
/// [`Self::unload`]) returned.
pub(crate) fn boot(
self: Pin<&mut Self>,
mut ctx: super::GspBootContext<'_, '_>,
) -> Result<Option<super::UnloadBundle>> {
let pdev = ctx.pdev;
let bar = ctx.bar;
let chipset = ctx.chipset;
let gsp_falcon = ctx.gsp_falcon;
let dev = pdev.as_ref();
let hal = super::hal::gsp_hal(chipset);
let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset), GFP_KERNEL)?;
// Perform the chipset-specific boot sequence, and retrieve the unload bundle.
let unload_bundle = hal.boot(&self, &mut ctx, &gsp_fw)?.or_else(|| {
dev_warn!(dev, "The GSP won't be able to unload properly on unbind.\n");
dev_warn!(
dev,
"The GPU will need to be reset before the driver can bind again.\n"
);
None
});
let mut unload_guard =
ScopeGuard::new_with_data((ctx, unload_bundle), |(ctx, unload_bundle)| {
let _ = self.unload(ctx, unload_bundle);
});
let ctx = &mut unload_guard.0;
gsp_falcon.write_os_version(gsp_fw.bootloader.app_version);
// Poll for RISC-V to become active before continuing.
read_poll_timeout(
|| Ok(gsp_falcon.is_riscv_active()),
|val: &bool| *val,
Delta::from_millis(10),
Delta::from_secs(5),
)?;
dev_dbg!(pdev, "RISC-V active? {}\n", gsp_falcon.is_riscv_active(),);
self.cmdq
.send_command_no_wait(bar, commands::SetSystemInfo::new(pdev, chipset))?;
self.cmdq
.send_command_no_wait(bar, commands::SetRegistry::new(ctx.vgpu.state())?)?;
hal.post_boot(&self, ctx, &gsp_fw)?;
// Wait until GSP is fully initialized.
commands::wait_gsp_init_done(&self.cmdq)?;
Ok(unload_guard.dismiss().1)
}
/// Shut down the GSP and wait until it is offline.
fn shutdown_gsp(
cmdq: &Cmdq,
bar: Bar0<'_>,
gsp_falcon: &Falcon<'_, Gsp>,
mode: commands::PowerStateLevel,
) -> Result {
// Command to shut the GSP down.
cmdq.send_command(bar, commands::UnloadingGuestDriver::new(mode))?;
// Wait until GSP signals it is suspended.
const LIBOS_INTERRUPT_PROCESSOR_SUSPENDED: u32 = bits::bit_u32(31);
read_poll_timeout(
|| Ok(gsp_falcon.read_mailbox0()),
|&mb0| mb0 & LIBOS_INTERRUPT_PROCESSOR_SUSPENDED != 0,
Delta::from_millis(10),
Delta::from_secs(5),
)
.map(|_| ())
}
/// Attempts to unload the GSP firmware.
///
/// This stops all activity on the GSP.
pub(crate) fn unload(
&self,
mut ctx: super::GspBootContext<'_, '_>,
unload_bundle: Option<super::UnloadBundle>,
) -> Result {
let dev = ctx.dev();
// Shut down the GSP. Keep going even in case of error.
let mut res = Self::shutdown_gsp(
&self.cmdq,
ctx.bar,
ctx.gsp_falcon,
commands::PowerStateLevel::Level0,
)
.inspect_err(|e| dev_err!(dev, "GSP shutdown failed: {:?}\n", e));
// Run the unload bundle to reset the GSP so it can be booted again.
if let Some(unload_bundle) = unload_bundle {
res = res.and(
unload_bundle
.0
.run(&mut ctx)
.inspect_err(|e| dev_err!(dev, "Unload bundle failed: {:?}\n", e)),
);
} else {
dev_warn!(
dev,
"Unload bundle is missing, GSP won't be properly reset.\n"
);
res = Err(EAGAIN);
}
res.inspect(|()| dev_info!(dev, "GSP successfully unloaded\n"))
}
}
|