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