Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/nova-core/falcon.rs
26426 views
1
// SPDX-License-Identifier: GPL-2.0
2
3
//! Falcon microprocessor base support
4
5
use core::ops::Deref;
6
use hal::FalconHal;
7
use kernel::bindings;
8
use kernel::device;
9
use kernel::prelude::*;
10
use kernel::time::Delta;
11
use kernel::types::ARef;
12
13
use crate::dma::DmaObject;
14
use crate::driver::Bar0;
15
use crate::gpu::Chipset;
16
use crate::regs;
17
use crate::util;
18
19
pub(crate) mod gsp;
20
mod hal;
21
pub(crate) mod sec2;
22
23
// TODO[FPRI]: Replace with `ToPrimitive`.
24
macro_rules! impl_from_enum_to_u32 {
25
($enum_type:ty) => {
26
impl From<$enum_type> for u32 {
27
fn from(value: $enum_type) -> Self {
28
value as u32
29
}
30
}
31
};
32
}
33
34
/// Revision number of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
35
/// register.
36
#[repr(u8)]
37
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
38
pub(crate) enum FalconCoreRev {
39
#[default]
40
Rev1 = 1,
41
Rev2 = 2,
42
Rev3 = 3,
43
Rev4 = 4,
44
Rev5 = 5,
45
Rev6 = 6,
46
Rev7 = 7,
47
}
48
impl_from_enum_to_u32!(FalconCoreRev);
49
50
// TODO[FPRI]: replace with `FromPrimitive`.
51
impl TryFrom<u8> for FalconCoreRev {
52
type Error = Error;
53
54
fn try_from(value: u8) -> Result<Self> {
55
use FalconCoreRev::*;
56
57
let rev = match value {
58
1 => Rev1,
59
2 => Rev2,
60
3 => Rev3,
61
4 => Rev4,
62
5 => Rev5,
63
6 => Rev6,
64
7 => Rev7,
65
_ => return Err(EINVAL),
66
};
67
68
Ok(rev)
69
}
70
}
71
72
/// Revision subversion number of a falcon core, used in the
73
/// [`crate::regs::NV_PFALCON_FALCON_HWCFG1`] register.
74
#[repr(u8)]
75
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
76
pub(crate) enum FalconCoreRevSubversion {
77
#[default]
78
Subversion0 = 0,
79
Subversion1 = 1,
80
Subversion2 = 2,
81
Subversion3 = 3,
82
}
83
impl_from_enum_to_u32!(FalconCoreRevSubversion);
84
85
// TODO[FPRI]: replace with `FromPrimitive`.
86
impl TryFrom<u8> for FalconCoreRevSubversion {
87
type Error = Error;
88
89
fn try_from(value: u8) -> Result<Self> {
90
use FalconCoreRevSubversion::*;
91
92
let sub_version = match value & 0b11 {
93
0 => Subversion0,
94
1 => Subversion1,
95
2 => Subversion2,
96
3 => Subversion3,
97
_ => return Err(EINVAL),
98
};
99
100
Ok(sub_version)
101
}
102
}
103
104
/// Security model of a falcon core, used in the [`crate::regs::NV_PFALCON_FALCON_HWCFG1`]
105
/// register.
106
#[repr(u8)]
107
#[derive(Debug, Default, Copy, Clone)]
108
/// Security mode of the Falcon microprocessor.
109
///
110
/// See `falcon.rst` for more details.
111
pub(crate) enum FalconSecurityModel {
112
/// Non-Secure: runs unsigned code without privileges.
113
#[default]
114
None = 0,
115
/// Light-Secured (LS): Runs signed code with some privileges.
116
/// Entry into this mode is only possible from 'Heavy-secure' mode, which verifies the code's
117
/// signature.
118
///
119
/// Also known as Low-Secure, Privilege Level 2 or PL2.
120
Light = 2,
121
/// Heavy-Secured (HS): Runs signed code with full privileges.
122
/// The code's signature is verified by the Falcon Boot ROM (BROM).
123
///
124
/// Also known as High-Secure, Privilege Level 3 or PL3.
125
Heavy = 3,
126
}
127
impl_from_enum_to_u32!(FalconSecurityModel);
128
129
// TODO[FPRI]: replace with `FromPrimitive`.
130
impl TryFrom<u8> for FalconSecurityModel {
131
type Error = Error;
132
133
fn try_from(value: u8) -> Result<Self> {
134
use FalconSecurityModel::*;
135
136
let sec_model = match value {
137
0 => None,
138
2 => Light,
139
3 => Heavy,
140
_ => return Err(EINVAL),
141
};
142
143
Ok(sec_model)
144
}
145
}
146
147
/// Signing algorithm for a given firmware, used in the [`crate::regs::NV_PFALCON2_FALCON_MOD_SEL`]
148
/// register. It is passed to the Falcon Boot ROM (BROM) as a parameter.
149
#[repr(u8)]
150
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
151
pub(crate) enum FalconModSelAlgo {
152
/// AES.
153
#[expect(dead_code)]
154
Aes = 0,
155
/// RSA3K.
156
#[default]
157
Rsa3k = 1,
158
}
159
impl_from_enum_to_u32!(FalconModSelAlgo);
160
161
// TODO[FPRI]: replace with `FromPrimitive`.
162
impl TryFrom<u8> for FalconModSelAlgo {
163
type Error = Error;
164
165
fn try_from(value: u8) -> Result<Self> {
166
match value {
167
1 => Ok(FalconModSelAlgo::Rsa3k),
168
_ => Err(EINVAL),
169
}
170
}
171
}
172
173
/// Valid values for the `size` field of the [`crate::regs::NV_PFALCON_FALCON_DMATRFCMD`] register.
174
#[repr(u8)]
175
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
176
pub(crate) enum DmaTrfCmdSize {
177
/// 256 bytes transfer.
178
#[default]
179
Size256B = 0x6,
180
}
181
impl_from_enum_to_u32!(DmaTrfCmdSize);
182
183
// TODO[FPRI]: replace with `FromPrimitive`.
184
impl TryFrom<u8> for DmaTrfCmdSize {
185
type Error = Error;
186
187
fn try_from(value: u8) -> Result<Self> {
188
match value {
189
0x6 => Ok(Self::Size256B),
190
_ => Err(EINVAL),
191
}
192
}
193
}
194
195
/// Currently active core on a dual falcon/riscv (Peregrine) controller.
196
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
197
pub(crate) enum PeregrineCoreSelect {
198
/// Falcon core is active.
199
#[default]
200
Falcon = 0,
201
/// RISC-V core is active.
202
Riscv = 1,
203
}
204
impl_from_enum_to_u32!(PeregrineCoreSelect);
205
206
impl From<bool> for PeregrineCoreSelect {
207
fn from(value: bool) -> Self {
208
match value {
209
false => PeregrineCoreSelect::Falcon,
210
true => PeregrineCoreSelect::Riscv,
211
}
212
}
213
}
214
215
/// Different types of memory present in a falcon core.
216
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
217
pub(crate) enum FalconMem {
218
/// Instruction Memory.
219
Imem,
220
/// Data Memory.
221
Dmem,
222
}
223
224
/// Defines the Framebuffer Interface (FBIF) aperture type.
225
/// This determines the memory type for external memory access during a DMA transfer, which is
226
/// performed by the Falcon's Framebuffer DMA (FBDMA) engine. See falcon.rst for more details.
227
#[derive(Debug, Clone, Default)]
228
pub(crate) enum FalconFbifTarget {
229
/// VRAM.
230
#[default]
231
/// Local Framebuffer (GPU's VRAM memory).
232
LocalFb = 0,
233
/// Coherent system memory (System DRAM).
234
CoherentSysmem = 1,
235
/// Non-coherent system memory (System DRAM).
236
NoncoherentSysmem = 2,
237
}
238
impl_from_enum_to_u32!(FalconFbifTarget);
239
240
// TODO[FPRI]: replace with `FromPrimitive`.
241
impl TryFrom<u8> for FalconFbifTarget {
242
type Error = Error;
243
244
fn try_from(value: u8) -> Result<Self> {
245
let res = match value {
246
0 => Self::LocalFb,
247
1 => Self::CoherentSysmem,
248
2 => Self::NoncoherentSysmem,
249
_ => return Err(EINVAL),
250
};
251
252
Ok(res)
253
}
254
}
255
256
/// Type of memory addresses to use.
257
#[derive(Debug, Clone, Default)]
258
pub(crate) enum FalconFbifMemType {
259
/// Virtual memory addresses.
260
#[default]
261
Virtual = 0,
262
/// Physical memory addresses.
263
Physical = 1,
264
}
265
impl_from_enum_to_u32!(FalconFbifMemType);
266
267
/// Conversion from a single-bit register field.
268
impl From<bool> for FalconFbifMemType {
269
fn from(value: bool) -> Self {
270
match value {
271
false => Self::Virtual,
272
true => Self::Physical,
273
}
274
}
275
}
276
277
/// Trait defining the parameters of a given Falcon instance.
278
pub(crate) trait FalconEngine: Sync {
279
/// Base I/O address for the falcon, relative from which its registers are accessed.
280
const BASE: usize;
281
}
282
283
/// Represents a portion of the firmware to be loaded into a particular memory (e.g. IMEM or DMEM).
284
#[derive(Debug)]
285
pub(crate) struct FalconLoadTarget {
286
/// Offset from the start of the source object to copy from.
287
pub(crate) src_start: u32,
288
/// Offset from the start of the destination memory to copy into.
289
pub(crate) dst_start: u32,
290
/// Number of bytes to copy.
291
pub(crate) len: u32,
292
}
293
294
/// Parameters for the falcon boot ROM.
295
#[derive(Debug)]
296
pub(crate) struct FalconBromParams {
297
/// Offset in `DMEM`` of the firmware's signature.
298
pub(crate) pkc_data_offset: u32,
299
/// Mask of engines valid for this firmware.
300
pub(crate) engine_id_mask: u16,
301
/// ID of the ucode used to infer a fuse register to validate the signature.
302
pub(crate) ucode_id: u8,
303
}
304
305
/// Trait for providing load parameters of falcon firmwares.
306
pub(crate) trait FalconLoadParams {
307
/// Returns the load parameters for `IMEM`.
308
fn imem_load_params(&self) -> FalconLoadTarget;
309
310
/// Returns the load parameters for `DMEM`.
311
fn dmem_load_params(&self) -> FalconLoadTarget;
312
313
/// Returns the parameters to write into the BROM registers.
314
fn brom_params(&self) -> FalconBromParams;
315
316
/// Returns the start address of the firmware.
317
fn boot_addr(&self) -> u32;
318
}
319
320
/// Trait for a falcon firmware.
321
///
322
/// A falcon firmware can be loaded on a given engine, and is presented in the form of a DMA
323
/// object.
324
pub(crate) trait FalconFirmware: FalconLoadParams + Deref<Target = DmaObject> {
325
/// Engine on which this firmware is to be loaded.
326
type Target: FalconEngine;
327
}
328
329
/// Contains the base parameters common to all Falcon instances.
330
pub(crate) struct Falcon<E: FalconEngine> {
331
hal: KBox<dyn FalconHal<E>>,
332
dev: ARef<device::Device>,
333
}
334
335
impl<E: FalconEngine + 'static> Falcon<E> {
336
/// Create a new falcon instance.
337
///
338
/// `need_riscv` is set to `true` if the caller expects the falcon to be a dual falcon/riscv
339
/// controller.
340
pub(crate) fn new(
341
dev: &device::Device,
342
chipset: Chipset,
343
bar: &Bar0,
344
need_riscv: bool,
345
) -> Result<Self> {
346
let hwcfg1 = regs::NV_PFALCON_FALCON_HWCFG1::read(bar, E::BASE);
347
// Check that the revision and security model contain valid values.
348
let _ = hwcfg1.core_rev()?;
349
let _ = hwcfg1.security_model()?;
350
351
if need_riscv {
352
let hwcfg2 = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
353
if !hwcfg2.riscv() {
354
dev_err!(
355
dev,
356
"riscv support requested on a controller that does not support it\n"
357
);
358
return Err(EINVAL);
359
}
360
}
361
362
Ok(Self {
363
hal: hal::falcon_hal(chipset)?,
364
dev: dev.into(),
365
})
366
}
367
368
/// Wait for memory scrubbing to complete.
369
fn reset_wait_mem_scrubbing(&self, bar: &Bar0) -> Result {
370
// TIMEOUT: memory scrubbing should complete in less than 20ms.
371
util::wait_on(Delta::from_millis(20), || {
372
if regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE).mem_scrubbing_done() {
373
Some(())
374
} else {
375
None
376
}
377
})
378
}
379
380
/// Reset the falcon engine.
381
fn reset_eng(&self, bar: &Bar0) -> Result {
382
let _ = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
383
384
// According to OpenRM's `kflcnPreResetWait_GA102` documentation, HW sometimes does not set
385
// RESET_READY so a non-failing timeout is used.
386
let _ = util::wait_on(Delta::from_micros(150), || {
387
let r = regs::NV_PFALCON_FALCON_HWCFG2::read(bar, E::BASE);
388
if r.reset_ready() {
389
Some(())
390
} else {
391
None
392
}
393
});
394
395
regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v| v.set_reset(true));
396
397
// TODO[DLAY]: replace with udelay() or equivalent once available.
398
// TIMEOUT: falcon engine should not take more than 10us to reset.
399
let _: Result = util::wait_on(Delta::from_micros(10), || None);
400
401
regs::NV_PFALCON_FALCON_ENGINE::alter(bar, E::BASE, |v| v.set_reset(false));
402
403
self.reset_wait_mem_scrubbing(bar)?;
404
405
Ok(())
406
}
407
408
/// Reset the controller, select the falcon core, and wait for memory scrubbing to complete.
409
pub(crate) fn reset(&self, bar: &Bar0) -> Result {
410
self.reset_eng(bar)?;
411
self.hal.select_core(self, bar)?;
412
self.reset_wait_mem_scrubbing(bar)?;
413
414
regs::NV_PFALCON_FALCON_RM::default()
415
.set_value(regs::NV_PMC_BOOT_0::read(bar).into())
416
.write(bar, E::BASE);
417
418
Ok(())
419
}
420
421
/// Perform a DMA write according to `load_offsets` from `dma_handle` into the falcon's
422
/// `target_mem`.
423
///
424
/// `sec` is set if the loaded firmware is expected to run in secure mode.
425
fn dma_wr<F: FalconFirmware<Target = E>>(
426
&self,
427
bar: &Bar0,
428
fw: &F,
429
target_mem: FalconMem,
430
load_offsets: FalconLoadTarget,
431
sec: bool,
432
) -> Result {
433
const DMA_LEN: u32 = 256;
434
435
// For IMEM, we want to use the start offset as a virtual address tag for each page, since
436
// code addresses in the firmware (and the boot vector) are virtual.
437
//
438
// For DMEM we can fold the start offset into the DMA handle.
439
let (src_start, dma_start) = match target_mem {
440
FalconMem::Imem => (load_offsets.src_start, fw.dma_handle()),
441
FalconMem::Dmem => (
442
0,
443
fw.dma_handle_with_offset(load_offsets.src_start as usize)?,
444
),
445
};
446
if dma_start % bindings::dma_addr_t::from(DMA_LEN) > 0 {
447
dev_err!(
448
self.dev,
449
"DMA transfer start addresses must be a multiple of {}",
450
DMA_LEN
451
);
452
return Err(EINVAL);
453
}
454
if load_offsets.len % DMA_LEN > 0 {
455
dev_err!(
456
self.dev,
457
"DMA transfer length must be a multiple of {}",
458
DMA_LEN
459
);
460
return Err(EINVAL);
461
}
462
463
// Set up the base source DMA address.
464
465
regs::NV_PFALCON_FALCON_DMATRFBASE::default()
466
.set_base((dma_start >> 8) as u32)
467
.write(bar, E::BASE);
468
regs::NV_PFALCON_FALCON_DMATRFBASE1::default()
469
.set_base((dma_start >> 40) as u16)
470
.write(bar, E::BASE);
471
472
let cmd = regs::NV_PFALCON_FALCON_DMATRFCMD::default()
473
.set_size(DmaTrfCmdSize::Size256B)
474
.set_imem(target_mem == FalconMem::Imem)
475
.set_sec(if sec { 1 } else { 0 });
476
477
for pos in (0..load_offsets.len).step_by(DMA_LEN as usize) {
478
// Perform a transfer of size `DMA_LEN`.
479
regs::NV_PFALCON_FALCON_DMATRFMOFFS::default()
480
.set_offs(load_offsets.dst_start + pos)
481
.write(bar, E::BASE);
482
regs::NV_PFALCON_FALCON_DMATRFFBOFFS::default()
483
.set_offs(src_start + pos)
484
.write(bar, E::BASE);
485
cmd.write(bar, E::BASE);
486
487
// Wait for the transfer to complete.
488
// TIMEOUT: arbitrarily large value, no DMA transfer to the falcon's small memories
489
// should ever take that long.
490
util::wait_on(Delta::from_secs(2), || {
491
let r = regs::NV_PFALCON_FALCON_DMATRFCMD::read(bar, E::BASE);
492
if r.idle() {
493
Some(())
494
} else {
495
None
496
}
497
})?;
498
}
499
500
Ok(())
501
}
502
503
/// Perform a DMA load into `IMEM` and `DMEM` of `fw`, and prepare the falcon to run it.
504
pub(crate) fn dma_load<F: FalconFirmware<Target = E>>(&self, bar: &Bar0, fw: &F) -> Result {
505
regs::NV_PFALCON_FBIF_CTL::alter(bar, E::BASE, |v| v.set_allow_phys_no_ctx(true));
506
regs::NV_PFALCON_FALCON_DMACTL::default().write(bar, E::BASE);
507
regs::NV_PFALCON_FBIF_TRANSCFG::alter(bar, E::BASE, |v| {
508
v.set_target(FalconFbifTarget::CoherentSysmem)
509
.set_mem_type(FalconFbifMemType::Physical)
510
});
511
512
self.dma_wr(bar, fw, FalconMem::Imem, fw.imem_load_params(), true)?;
513
self.dma_wr(bar, fw, FalconMem::Dmem, fw.dmem_load_params(), true)?;
514
515
self.hal.program_brom(self, bar, &fw.brom_params())?;
516
517
// Set `BootVec` to start of non-secure code.
518
regs::NV_PFALCON_FALCON_BOOTVEC::default()
519
.set_value(fw.boot_addr())
520
.write(bar, E::BASE);
521
522
Ok(())
523
}
524
525
/// Runs the loaded firmware and waits for its completion.
526
///
527
/// `mbox0` and `mbox1` are optional parameters to write into the `MBOX0` and `MBOX1` registers
528
/// prior to running.
529
///
530
/// Wait up to two seconds for the firmware to complete, and return its exit status read from
531
/// the `MBOX0` and `MBOX1` registers.
532
pub(crate) fn boot(
533
&self,
534
bar: &Bar0,
535
mbox0: Option<u32>,
536
mbox1: Option<u32>,
537
) -> Result<(u32, u32)> {
538
if let Some(mbox0) = mbox0 {
539
regs::NV_PFALCON_FALCON_MAILBOX0::default()
540
.set_value(mbox0)
541
.write(bar, E::BASE);
542
}
543
544
if let Some(mbox1) = mbox1 {
545
regs::NV_PFALCON_FALCON_MAILBOX1::default()
546
.set_value(mbox1)
547
.write(bar, E::BASE);
548
}
549
550
match regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE).alias_en() {
551
true => regs::NV_PFALCON_FALCON_CPUCTL_ALIAS::default()
552
.set_startcpu(true)
553
.write(bar, E::BASE),
554
false => regs::NV_PFALCON_FALCON_CPUCTL::default()
555
.set_startcpu(true)
556
.write(bar, E::BASE),
557
}
558
559
// TIMEOUT: arbitrarily large value, firmwares should complete in less than 2 seconds.
560
util::wait_on(Delta::from_secs(2), || {
561
let r = regs::NV_PFALCON_FALCON_CPUCTL::read(bar, E::BASE);
562
if r.halted() {
563
Some(())
564
} else {
565
None
566
}
567
})?;
568
569
let (mbox0, mbox1) = (
570
regs::NV_PFALCON_FALCON_MAILBOX0::read(bar, E::BASE).value(),
571
regs::NV_PFALCON_FALCON_MAILBOX1::read(bar, E::BASE).value(),
572
);
573
574
Ok((mbox0, mbox1))
575
}
576
577
/// Returns the fused version of the signature to use in order to run a HS firmware on this
578
/// falcon instance. `engine_id_mask` and `ucode_id` are obtained from the firmware header.
579
pub(crate) fn signature_reg_fuse_version(
580
&self,
581
bar: &Bar0,
582
engine_id_mask: u16,
583
ucode_id: u8,
584
) -> Result<u32> {
585
self.hal
586
.signature_reg_fuse_version(self, bar, engine_id_mask, ucode_id)
587
}
588
}
589
590