Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/nova-core/vbios.rs
49750 views
1
// SPDX-License-Identifier: GPL-2.0
2
3
//! VBIOS extraction and parsing.
4
5
use core::convert::TryFrom;
6
7
use kernel::{
8
device,
9
prelude::*,
10
ptr::{
11
Alignable,
12
Alignment, //
13
},
14
transmute::FromBytes,
15
types::ARef,
16
};
17
18
use crate::{
19
driver::Bar0,
20
firmware::{
21
fwsec::Bcrt30Rsa3kSignature,
22
FalconUCodeDescV3, //
23
},
24
num::FromSafeCast,
25
};
26
27
/// The offset of the VBIOS ROM in the BAR0 space.
28
const ROM_OFFSET: usize = 0x300000;
29
/// The maximum length of the VBIOS ROM to scan into.
30
const BIOS_MAX_SCAN_LEN: usize = 0x100000;
31
/// The size to read ahead when parsing initial BIOS image headers.
32
const BIOS_READ_AHEAD_SIZE: usize = 1024;
33
/// The bit in the last image indicator byte for the PCI Data Structure that
34
/// indicates the last image. Bit 0-6 are reserved, bit 7 is last image bit.
35
const LAST_IMAGE_BIT_MASK: u8 = 0x80;
36
37
/// BIOS Image Type from PCI Data Structure code_type field.
38
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39
#[repr(u8)]
40
enum BiosImageType {
41
/// PC-AT compatible BIOS image (x86 legacy)
42
PciAt = 0x00,
43
/// EFI (Extensible Firmware Interface) BIOS image
44
Efi = 0x03,
45
/// NBSI (Notebook System Information) BIOS image
46
Nbsi = 0x70,
47
/// FwSec (Firmware Security) BIOS image
48
FwSec = 0xE0,
49
}
50
51
impl TryFrom<u8> for BiosImageType {
52
type Error = Error;
53
54
fn try_from(code: u8) -> Result<Self> {
55
match code {
56
0x00 => Ok(Self::PciAt),
57
0x03 => Ok(Self::Efi),
58
0x70 => Ok(Self::Nbsi),
59
0xE0 => Ok(Self::FwSec),
60
_ => Err(EINVAL),
61
}
62
}
63
}
64
65
// PMU lookup table entry types. Used to locate PMU table entries
66
// in the Fwsec image, corresponding to falcon ucodes.
67
#[expect(dead_code)]
68
const FALCON_UCODE_ENTRY_APPID_FIRMWARE_SEC_LIC: u8 = 0x05;
69
#[expect(dead_code)]
70
const FALCON_UCODE_ENTRY_APPID_FWSEC_DBG: u8 = 0x45;
71
const FALCON_UCODE_ENTRY_APPID_FWSEC_PROD: u8 = 0x85;
72
73
/// Vbios Reader for constructing the VBIOS data.
74
struct VbiosIterator<'a> {
75
dev: &'a device::Device,
76
bar0: &'a Bar0,
77
/// VBIOS data vector: As BIOS images are scanned, they are added to this vector for reference
78
/// or copying into other data structures. It is the entire scanned contents of the VBIOS which
79
/// progressively extends. It is used so that we do not re-read any contents that are already
80
/// read as we use the cumulative length read so far, and re-read any gaps as we extend the
81
/// length.
82
data: KVec<u8>,
83
/// Current offset of the [`Iterator`].
84
current_offset: usize,
85
/// Indicate whether the last image has been found.
86
last_found: bool,
87
}
88
89
impl<'a> VbiosIterator<'a> {
90
fn new(dev: &'a device::Device, bar0: &'a Bar0) -> Result<Self> {
91
Ok(Self {
92
dev,
93
bar0,
94
data: KVec::new(),
95
current_offset: 0,
96
last_found: false,
97
})
98
}
99
100
/// Read bytes from the ROM at the current end of the data vector.
101
fn read_more(&mut self, len: usize) -> Result {
102
let current_len = self.data.len();
103
let start = ROM_OFFSET + current_len;
104
105
// Ensure length is a multiple of 4 for 32-bit reads
106
if len % core::mem::size_of::<u32>() != 0 {
107
dev_err!(
108
self.dev,
109
"VBIOS read length {} is not a multiple of 4\n",
110
len
111
);
112
return Err(EINVAL);
113
}
114
115
self.data.reserve(len, GFP_KERNEL)?;
116
// Read ROM data bytes and push directly to `data`.
117
for addr in (start..start + len).step_by(core::mem::size_of::<u32>()) {
118
// Read 32-bit word from the VBIOS ROM
119
let word = self.bar0.try_read32(addr)?;
120
121
// Convert the `u32` to a 4 byte array and push each byte.
122
word.to_ne_bytes()
123
.iter()
124
.try_for_each(|&b| self.data.push(b, GFP_KERNEL))?;
125
}
126
127
Ok(())
128
}
129
130
/// Read bytes at a specific offset, filling any gap.
131
fn read_more_at_offset(&mut self, offset: usize, len: usize) -> Result {
132
if offset > BIOS_MAX_SCAN_LEN {
133
dev_err!(self.dev, "Error: exceeded BIOS scan limit.\n");
134
return Err(EINVAL);
135
}
136
137
// If `offset` is beyond current data size, fill the gap first.
138
let current_len = self.data.len();
139
let gap_bytes = offset.saturating_sub(current_len);
140
141
// Now read the requested bytes at the offset.
142
self.read_more(gap_bytes + len)
143
}
144
145
/// Read a BIOS image at a specific offset and create a [`BiosImage`] from it.
146
///
147
/// `self.data` is extended as needed and a new [`BiosImage`] is returned.
148
/// `context` is a string describing the operation for error reporting.
149
fn read_bios_image_at_offset(
150
&mut self,
151
offset: usize,
152
len: usize,
153
context: &str,
154
) -> Result<BiosImage> {
155
let data_len = self.data.len();
156
if offset + len > data_len {
157
self.read_more_at_offset(offset, len).inspect_err(|e| {
158
dev_err!(
159
self.dev,
160
"Failed to read more at offset {:#x}: {:?}\n",
161
offset,
162
e
163
)
164
})?;
165
}
166
167
BiosImage::new(self.dev, &self.data[offset..offset + len]).inspect_err(|err| {
168
dev_err!(
169
self.dev,
170
"Failed to {} at offset {:#x}: {:?}\n",
171
context,
172
offset,
173
err
174
)
175
})
176
}
177
}
178
179
impl<'a> Iterator for VbiosIterator<'a> {
180
type Item = Result<BiosImage>;
181
182
/// Iterate over all VBIOS images until the last image is detected or offset
183
/// exceeds scan limit.
184
fn next(&mut self) -> Option<Self::Item> {
185
if self.last_found {
186
return None;
187
}
188
189
if self.current_offset > BIOS_MAX_SCAN_LEN {
190
dev_err!(self.dev, "Error: exceeded BIOS scan limit, stopping scan\n");
191
return None;
192
}
193
194
// Parse image headers first to get image size.
195
let image_size = match self.read_bios_image_at_offset(
196
self.current_offset,
197
BIOS_READ_AHEAD_SIZE,
198
"parse initial BIOS image headers",
199
) {
200
Ok(image) => image.image_size_bytes(),
201
Err(e) => return Some(Err(e)),
202
};
203
204
// Now create a new `BiosImage` with the full image data.
205
let full_image = match self.read_bios_image_at_offset(
206
self.current_offset,
207
image_size,
208
"parse full BIOS image",
209
) {
210
Ok(image) => image,
211
Err(e) => return Some(Err(e)),
212
};
213
214
self.last_found = full_image.is_last();
215
216
// Advance to next image (aligned to 512 bytes).
217
self.current_offset += image_size;
218
self.current_offset = self.current_offset.align_up(Alignment::new::<512>())?;
219
220
Some(Ok(full_image))
221
}
222
}
223
224
pub(crate) struct Vbios {
225
fwsec_image: FwSecBiosImage,
226
}
227
228
impl Vbios {
229
/// Probe for VBIOS extraction.
230
///
231
/// Once the VBIOS object is built, `bar0` is not read for [`Vbios`] purposes anymore.
232
pub(crate) fn new(dev: &device::Device, bar0: &Bar0) -> Result<Vbios> {
233
// Images to extract from iteration
234
let mut pci_at_image: Option<PciAtBiosImage> = None;
235
let mut first_fwsec_image: Option<FwSecBiosBuilder> = None;
236
let mut second_fwsec_image: Option<FwSecBiosBuilder> = None;
237
238
// Parse all VBIOS images in the ROM
239
for image_result in VbiosIterator::new(dev, bar0)? {
240
let image = image_result?;
241
242
dev_dbg!(
243
dev,
244
"Found BIOS image: size: {:#x}, type: {:?}, last: {}\n",
245
image.image_size_bytes(),
246
image.image_type(),
247
image.is_last()
248
);
249
250
// Convert to a specific image type
251
match BiosImageType::try_from(image.pcir.code_type) {
252
Ok(BiosImageType::PciAt) => {
253
pci_at_image = Some(PciAtBiosImage::try_from(image)?);
254
}
255
Ok(BiosImageType::FwSec) => {
256
let fwsec = FwSecBiosBuilder {
257
base: image,
258
falcon_data_offset: None,
259
pmu_lookup_table: None,
260
falcon_ucode_offset: None,
261
};
262
if first_fwsec_image.is_none() {
263
first_fwsec_image = Some(fwsec);
264
} else {
265
second_fwsec_image = Some(fwsec);
266
}
267
}
268
_ => {
269
// Ignore other image types or unknown types
270
}
271
}
272
}
273
274
// Using all the images, setup the falcon data pointer in Fwsec.
275
if let (Some(mut second), Some(first), Some(pci_at)) =
276
(second_fwsec_image, first_fwsec_image, pci_at_image)
277
{
278
second
279
.setup_falcon_data(&pci_at, &first)
280
.inspect_err(|e| dev_err!(dev, "Falcon data setup failed: {:?}\n", e))?;
281
Ok(Vbios {
282
fwsec_image: second.build()?,
283
})
284
} else {
285
dev_err!(
286
dev,
287
"Missing required images for falcon data setup, skipping\n"
288
);
289
Err(EINVAL)
290
}
291
}
292
293
pub(crate) fn fwsec_image(&self) -> &FwSecBiosImage {
294
&self.fwsec_image
295
}
296
}
297
298
/// PCI Data Structure as defined in PCI Firmware Specification
299
#[derive(Debug, Clone)]
300
#[repr(C)]
301
struct PcirStruct {
302
/// PCI Data Structure signature ("PCIR" or "NPDS")
303
signature: [u8; 4],
304
/// PCI Vendor ID (e.g., 0x10DE for NVIDIA)
305
vendor_id: u16,
306
/// PCI Device ID
307
device_id: u16,
308
/// Device List Pointer
309
device_list_ptr: u16,
310
/// PCI Data Structure Length
311
pci_data_struct_len: u16,
312
/// PCI Data Structure Revision
313
pci_data_struct_rev: u8,
314
/// Class code (3 bytes, 0x03 for display controller)
315
class_code: [u8; 3],
316
/// Size of this image in 512-byte blocks
317
image_len: u16,
318
/// Revision Level of the Vendor's ROM
319
vendor_rom_rev: u16,
320
/// ROM image type (0x00 = PC-AT compatible, 0x03 = EFI, 0x70 = NBSI)
321
code_type: u8,
322
/// Last image indicator (0x00 = Not last image, 0x80 = Last image)
323
last_image: u8,
324
/// Maximum Run-time Image Length (units of 512 bytes)
325
max_runtime_image_len: u16,
326
}
327
328
// SAFETY: all bit patterns are valid for `PcirStruct`.
329
unsafe impl FromBytes for PcirStruct {}
330
331
impl PcirStruct {
332
fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
333
let (pcir, _) = PcirStruct::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
334
335
// Signature should be "PCIR" (0x52494350) or "NPDS" (0x5344504e).
336
if &pcir.signature != b"PCIR" && &pcir.signature != b"NPDS" {
337
dev_err!(
338
dev,
339
"Invalid signature for PcirStruct: {:?}\n",
340
pcir.signature
341
);
342
return Err(EINVAL);
343
}
344
345
if pcir.image_len == 0 {
346
dev_err!(dev, "Invalid image length: 0\n");
347
return Err(EINVAL);
348
}
349
350
Ok(pcir)
351
}
352
353
/// Check if this is the last image in the ROM.
354
fn is_last(&self) -> bool {
355
self.last_image & LAST_IMAGE_BIT_MASK != 0
356
}
357
358
/// Calculate image size in bytes from 512-byte blocks.
359
fn image_size_bytes(&self) -> usize {
360
usize::from(self.image_len) * 512
361
}
362
}
363
364
/// BIOS Information Table (BIT) Header.
365
///
366
/// This is the head of the BIT table, that is used to locate the Falcon data. The BIT table (with
367
/// its header) is in the [`PciAtBiosImage`] and the falcon data it is pointing to is in the
368
/// [`FwSecBiosImage`].
369
#[derive(Debug, Clone, Copy)]
370
#[repr(C)]
371
struct BitHeader {
372
/// 0h: BIT Header Identifier (BMP=0x7FFF/BIT=0xB8FF)
373
id: u16,
374
/// 2h: BIT Header Signature ("BIT\0")
375
signature: [u8; 4],
376
/// 6h: Binary Coded Decimal Version, ex: 0x0100 is 1.00.
377
bcd_version: u16,
378
/// 8h: Size of BIT Header (in bytes)
379
header_size: u8,
380
/// 9h: Size of BIT Tokens (in bytes)
381
token_size: u8,
382
/// 10h: Number of token entries that follow
383
token_entries: u8,
384
/// 11h: BIT Header Checksum
385
checksum: u8,
386
}
387
388
// SAFETY: all bit patterns are valid for `BitHeader`.
389
unsafe impl FromBytes for BitHeader {}
390
391
impl BitHeader {
392
fn new(data: &[u8]) -> Result<Self> {
393
let (header, _) = BitHeader::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
394
395
// Check header ID and signature
396
if header.id != 0xB8FF || &header.signature != b"BIT\0" {
397
return Err(EINVAL);
398
}
399
400
Ok(header)
401
}
402
}
403
404
/// BIT Token Entry: Records in the BIT table followed by the BIT header.
405
#[derive(Debug, Clone, Copy)]
406
#[expect(dead_code)]
407
struct BitToken {
408
/// 00h: Token identifier
409
id: u8,
410
/// 01h: Version of the token data
411
data_version: u8,
412
/// 02h: Size of token data in bytes
413
data_size: u16,
414
/// 04h: Offset to the token data
415
data_offset: u16,
416
}
417
418
// Define the token ID for the Falcon data
419
const BIT_TOKEN_ID_FALCON_DATA: u8 = 0x70;
420
421
impl BitToken {
422
/// Find a BIT token entry by BIT ID in a PciAtBiosImage
423
fn from_id(image: &PciAtBiosImage, token_id: u8) -> Result<Self> {
424
let header = &image.bit_header;
425
426
// Offset to the first token entry
427
let tokens_start = image.bit_offset + usize::from(header.header_size);
428
429
for i in 0..usize::from(header.token_entries) {
430
let entry_offset = tokens_start + (i * usize::from(header.token_size));
431
432
// Make sure we don't go out of bounds
433
if entry_offset + usize::from(header.token_size) > image.base.data.len() {
434
return Err(EINVAL);
435
}
436
437
// Check if this token has the requested ID
438
if image.base.data[entry_offset] == token_id {
439
return Ok(BitToken {
440
id: image.base.data[entry_offset],
441
data_version: image.base.data[entry_offset + 1],
442
data_size: u16::from_le_bytes([
443
image.base.data[entry_offset + 2],
444
image.base.data[entry_offset + 3],
445
]),
446
data_offset: u16::from_le_bytes([
447
image.base.data[entry_offset + 4],
448
image.base.data[entry_offset + 5],
449
]),
450
});
451
}
452
}
453
454
// Token not found
455
Err(ENOENT)
456
}
457
}
458
459
/// PCI ROM Expansion Header as defined in PCI Firmware Specification.
460
///
461
/// This is header is at the beginning of every image in the set of images in the ROM. It contains
462
/// a pointer to the PCI Data Structure which describes the image. For "NBSI" images (NoteBook
463
/// System Information), the ROM header deviates from the standard and contains an offset to the
464
/// NBSI image however we do not yet parse that in this module and keep it for future reference.
465
#[derive(Debug, Clone, Copy)]
466
#[expect(dead_code)]
467
struct PciRomHeader {
468
/// 00h: Signature (0xAA55)
469
signature: u16,
470
/// 02h: Reserved bytes for processor architecture unique data (20 bytes)
471
reserved: [u8; 20],
472
/// 16h: NBSI Data Offset (NBSI-specific, offset from header to NBSI image)
473
nbsi_data_offset: Option<u16>,
474
/// 18h: Pointer to PCI Data Structure (offset from start of ROM image)
475
pci_data_struct_offset: u16,
476
/// 1Ah: Size of block (this is NBSI-specific)
477
size_of_block: Option<u32>,
478
}
479
480
impl PciRomHeader {
481
fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
482
if data.len() < 26 {
483
// Need at least 26 bytes to read pciDataStrucPtr and sizeOfBlock.
484
return Err(EINVAL);
485
}
486
487
let signature = u16::from_le_bytes([data[0], data[1]]);
488
489
// Check for valid ROM signatures.
490
match signature {
491
0xAA55 | 0xBB77 | 0x4E56 => {}
492
_ => {
493
dev_err!(dev, "ROM signature unknown {:#x}\n", signature);
494
return Err(EINVAL);
495
}
496
}
497
498
// Read the pointer to the PCI Data Structure at offset 0x18.
499
let pci_data_struct_ptr = u16::from_le_bytes([data[24], data[25]]);
500
501
// Try to read optional fields if enough data.
502
let mut size_of_block = None;
503
let mut nbsi_data_offset = None;
504
505
if data.len() >= 30 {
506
// Read size_of_block at offset 0x1A.
507
size_of_block = Some(
508
u32::from(data[29]) << 24
509
| u32::from(data[28]) << 16
510
| u32::from(data[27]) << 8
511
| u32::from(data[26]),
512
);
513
}
514
515
// For NBSI images, try to read the nbsiDataOffset at offset 0x16.
516
if data.len() >= 24 {
517
nbsi_data_offset = Some(u16::from_le_bytes([data[22], data[23]]));
518
}
519
520
Ok(PciRomHeader {
521
signature,
522
reserved: [0u8; 20],
523
pci_data_struct_offset: pci_data_struct_ptr,
524
size_of_block,
525
nbsi_data_offset,
526
})
527
}
528
}
529
530
/// NVIDIA PCI Data Extension Structure.
531
///
532
/// This is similar to the PCI Data Structure, but is Nvidia-specific and is placed right after the
533
/// PCI Data Structure. It contains some fields that are redundant with the PCI Data Structure, but
534
/// are needed for traversing the BIOS images. It is expected to be present in all BIOS images
535
/// except for NBSI images.
536
#[derive(Debug, Clone)]
537
#[repr(C)]
538
struct NpdeStruct {
539
/// 00h: Signature ("NPDE")
540
signature: [u8; 4],
541
/// 04h: NVIDIA PCI Data Extension Revision
542
npci_data_ext_rev: u16,
543
/// 06h: NVIDIA PCI Data Extension Length
544
npci_data_ext_len: u16,
545
/// 08h: Sub-image Length (in 512-byte units)
546
subimage_len: u16,
547
/// 0Ah: Last image indicator flag
548
last_image: u8,
549
}
550
551
// SAFETY: all bit patterns are valid for `NpdeStruct`.
552
unsafe impl FromBytes for NpdeStruct {}
553
554
impl NpdeStruct {
555
fn new(dev: &device::Device, data: &[u8]) -> Option<Self> {
556
let (npde, _) = NpdeStruct::from_bytes_copy_prefix(data)?;
557
558
// Signature should be "NPDE" (0x4544504E).
559
if &npde.signature != b"NPDE" {
560
dev_dbg!(
561
dev,
562
"Invalid signature for NpdeStruct: {:?}\n",
563
npde.signature
564
);
565
return None;
566
}
567
568
if npde.subimage_len == 0 {
569
dev_dbg!(dev, "Invalid subimage length: 0\n");
570
return None;
571
}
572
573
Some(npde)
574
}
575
576
/// Check if this is the last image in the ROM.
577
fn is_last(&self) -> bool {
578
self.last_image & LAST_IMAGE_BIT_MASK != 0
579
}
580
581
/// Calculate image size in bytes from 512-byte blocks.
582
fn image_size_bytes(&self) -> usize {
583
usize::from(self.subimage_len) * 512
584
}
585
586
/// Try to find NPDE in the data, the NPDE is right after the PCIR.
587
fn find_in_data(
588
dev: &device::Device,
589
data: &[u8],
590
rom_header: &PciRomHeader,
591
pcir: &PcirStruct,
592
) -> Option<Self> {
593
// Calculate the offset where NPDE might be located
594
// NPDE should be right after the PCIR structure, aligned to 16 bytes
595
let pcir_offset = usize::from(rom_header.pci_data_struct_offset);
596
let npde_start = (pcir_offset + usize::from(pcir.pci_data_struct_len) + 0x0F) & !0x0F;
597
598
// Check if we have enough data
599
if npde_start + core::mem::size_of::<Self>() > data.len() {
600
dev_dbg!(dev, "Not enough data for NPDE\n");
601
return None;
602
}
603
604
// Try to create NPDE from the data
605
NpdeStruct::new(dev, &data[npde_start..])
606
}
607
}
608
609
/// The PciAt BIOS image is typically the first BIOS image type found in the BIOS image chain.
610
///
611
/// It contains the BIT header and the BIT tokens.
612
struct PciAtBiosImage {
613
base: BiosImage,
614
bit_header: BitHeader,
615
bit_offset: usize,
616
}
617
618
#[expect(dead_code)]
619
struct EfiBiosImage {
620
base: BiosImage,
621
// EFI-specific fields can be added here in the future.
622
}
623
624
#[expect(dead_code)]
625
struct NbsiBiosImage {
626
base: BiosImage,
627
// NBSI-specific fields can be added here in the future.
628
}
629
630
struct FwSecBiosBuilder {
631
base: BiosImage,
632
/// These are temporary fields that are used during the construction of the
633
/// [`FwSecBiosBuilder`].
634
///
635
/// Once FwSecBiosBuilder is constructed, the `falcon_ucode_offset` will be copied into a new
636
/// [`FwSecBiosImage`].
637
///
638
/// The offset of the Falcon data from the start of Fwsec image.
639
falcon_data_offset: Option<usize>,
640
/// The [`PmuLookupTable`] starts at the offset of the falcon data pointer.
641
pmu_lookup_table: Option<PmuLookupTable>,
642
/// The offset of the Falcon ucode.
643
falcon_ucode_offset: Option<usize>,
644
}
645
646
/// The [`FwSecBiosImage`] structure contains the PMU table and the Falcon Ucode.
647
///
648
/// The PMU table contains voltage/frequency tables as well as a pointer to the Falcon Ucode.
649
pub(crate) struct FwSecBiosImage {
650
base: BiosImage,
651
/// The offset of the Falcon ucode.
652
falcon_ucode_offset: usize,
653
}
654
655
/// BIOS Image structure containing various headers and reference fields to all BIOS images.
656
///
657
/// A BiosImage struct is embedded into all image types and implements common operations.
658
#[expect(dead_code)]
659
struct BiosImage {
660
/// Used for logging.
661
dev: ARef<device::Device>,
662
/// PCI ROM Expansion Header
663
rom_header: PciRomHeader,
664
/// PCI Data Structure
665
pcir: PcirStruct,
666
/// NVIDIA PCI Data Extension (optional)
667
npde: Option<NpdeStruct>,
668
/// Image data (includes ROM header and PCIR)
669
data: KVec<u8>,
670
}
671
672
impl BiosImage {
673
/// Get the image size in bytes.
674
fn image_size_bytes(&self) -> usize {
675
// Prefer NPDE image size if available
676
if let Some(ref npde) = self.npde {
677
npde.image_size_bytes()
678
} else {
679
// Otherwise, fall back to the PCIR image size
680
self.pcir.image_size_bytes()
681
}
682
}
683
684
/// Get the BIOS image type.
685
fn image_type(&self) -> Result<BiosImageType> {
686
BiosImageType::try_from(self.pcir.code_type)
687
}
688
689
/// Check if this is the last image.
690
fn is_last(&self) -> bool {
691
// For NBSI images, return true as they're considered the last image.
692
if self.image_type() == Ok(BiosImageType::Nbsi) {
693
return true;
694
}
695
696
// For other image types, check the NPDE first if available
697
if let Some(ref npde) = self.npde {
698
return npde.is_last();
699
}
700
701
// Otherwise, fall back to checking the PCIR last_image flag
702
self.pcir.is_last()
703
}
704
705
/// Creates a new BiosImage from raw byte data.
706
fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
707
// Ensure we have enough data for the ROM header.
708
if data.len() < 26 {
709
dev_err!(dev, "Not enough data for ROM header\n");
710
return Err(EINVAL);
711
}
712
713
// Parse the ROM header.
714
let rom_header = PciRomHeader::new(dev, &data[0..26])
715
.inspect_err(|e| dev_err!(dev, "Failed to create PciRomHeader: {:?}\n", e))?;
716
717
// Get the PCI Data Structure using the pointer from the ROM header.
718
let pcir_offset = usize::from(rom_header.pci_data_struct_offset);
719
let pcir_data = data
720
.get(pcir_offset..pcir_offset + core::mem::size_of::<PcirStruct>())
721
.ok_or(EINVAL)
722
.inspect_err(|_| {
723
dev_err!(
724
dev,
725
"PCIR offset {:#x} out of bounds (data length: {})\n",
726
pcir_offset,
727
data.len()
728
);
729
dev_err!(
730
dev,
731
"Consider reading more data for construction of BiosImage\n"
732
);
733
})?;
734
735
let pcir = PcirStruct::new(dev, pcir_data)
736
.inspect_err(|e| dev_err!(dev, "Failed to create PcirStruct: {:?}\n", e))?;
737
738
// Look for NPDE structure if this is not an NBSI image (type != 0x70).
739
let npde = NpdeStruct::find_in_data(dev, data, &rom_header, &pcir);
740
741
// Create a copy of the data.
742
let mut data_copy = KVec::new();
743
data_copy.extend_from_slice(data, GFP_KERNEL)?;
744
745
Ok(BiosImage {
746
dev: dev.into(),
747
rom_header,
748
pcir,
749
npde,
750
data: data_copy,
751
})
752
}
753
}
754
755
impl PciAtBiosImage {
756
/// Find a byte pattern in a slice.
757
fn find_byte_pattern(haystack: &[u8], needle: &[u8]) -> Result<usize> {
758
haystack
759
.windows(needle.len())
760
.position(|window| window == needle)
761
.ok_or(EINVAL)
762
}
763
764
/// Find the BIT header in the [`PciAtBiosImage`].
765
fn find_bit_header(data: &[u8]) -> Result<(BitHeader, usize)> {
766
let bit_pattern = [0xff, 0xb8, b'B', b'I', b'T', 0x00];
767
let bit_offset = Self::find_byte_pattern(data, &bit_pattern)?;
768
let bit_header = BitHeader::new(&data[bit_offset..])?;
769
770
Ok((bit_header, bit_offset))
771
}
772
773
/// Get a BIT token entry from the BIT table in the [`PciAtBiosImage`]
774
fn get_bit_token(&self, token_id: u8) -> Result<BitToken> {
775
BitToken::from_id(self, token_id)
776
}
777
778
/// Find the Falcon data pointer structure in the [`PciAtBiosImage`].
779
///
780
/// This is just a 4 byte structure that contains a pointer to the Falcon data in the FWSEC
781
/// image.
782
fn falcon_data_ptr(&self) -> Result<u32> {
783
let token = self.get_bit_token(BIT_TOKEN_ID_FALCON_DATA)?;
784
785
// Make sure we don't go out of bounds
786
if usize::from(token.data_offset) + 4 > self.base.data.len() {
787
return Err(EINVAL);
788
}
789
790
// read the 4 bytes at the offset specified in the token
791
let offset = usize::from(token.data_offset);
792
let bytes: [u8; 4] = self.base.data[offset..offset + 4].try_into().map_err(|_| {
793
dev_err!(self.base.dev, "Failed to convert data slice to array");
794
EINVAL
795
})?;
796
797
let data_ptr = u32::from_le_bytes(bytes);
798
799
if (usize::from_safe_cast(data_ptr)) < self.base.data.len() {
800
dev_err!(self.base.dev, "Falcon data pointer out of bounds\n");
801
return Err(EINVAL);
802
}
803
804
Ok(data_ptr)
805
}
806
}
807
808
impl TryFrom<BiosImage> for PciAtBiosImage {
809
type Error = Error;
810
811
fn try_from(base: BiosImage) -> Result<Self> {
812
let data_slice = &base.data;
813
let (bit_header, bit_offset) = PciAtBiosImage::find_bit_header(data_slice)?;
814
815
Ok(PciAtBiosImage {
816
base,
817
bit_header,
818
bit_offset,
819
})
820
}
821
}
822
823
/// The [`PmuLookupTableEntry`] structure is a single entry in the [`PmuLookupTable`].
824
///
825
/// See the [`PmuLookupTable`] description for more information.
826
#[repr(C, packed)]
827
struct PmuLookupTableEntry {
828
application_id: u8,
829
target_id: u8,
830
data: u32,
831
}
832
833
impl PmuLookupTableEntry {
834
fn new(data: &[u8]) -> Result<Self> {
835
if data.len() < core::mem::size_of::<Self>() {
836
return Err(EINVAL);
837
}
838
839
Ok(PmuLookupTableEntry {
840
application_id: data[0],
841
target_id: data[1],
842
data: u32::from_le_bytes(data[2..6].try_into().map_err(|_| EINVAL)?),
843
})
844
}
845
}
846
847
#[repr(C)]
848
struct PmuLookupTableHeader {
849
version: u8,
850
header_len: u8,
851
entry_len: u8,
852
entry_count: u8,
853
}
854
855
// SAFETY: all bit patterns are valid for `PmuLookupTableHeader`.
856
unsafe impl FromBytes for PmuLookupTableHeader {}
857
858
/// The [`PmuLookupTableEntry`] structure is used to find the [`PmuLookupTableEntry`] for a given
859
/// application ID.
860
///
861
/// The table of entries is pointed to by the falcon data pointer in the BIT table, and is used to
862
/// locate the Falcon Ucode.
863
struct PmuLookupTable {
864
header: PmuLookupTableHeader,
865
table_data: KVec<u8>,
866
}
867
868
impl PmuLookupTable {
869
fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
870
let (header, _) = PmuLookupTableHeader::from_bytes_copy_prefix(data).ok_or(EINVAL)?;
871
872
let header_len = usize::from(header.header_len);
873
let entry_len = usize::from(header.entry_len);
874
let entry_count = usize::from(header.entry_count);
875
876
let required_bytes = header_len + (entry_count * entry_len);
877
878
if data.len() < required_bytes {
879
dev_err!(dev, "PmuLookupTable data length less than required\n");
880
return Err(EINVAL);
881
}
882
883
// Create a copy of only the table data
884
let table_data = {
885
let mut ret = KVec::new();
886
ret.extend_from_slice(&data[header_len..required_bytes], GFP_KERNEL)?;
887
ret
888
};
889
890
// Debug logging of entries (dumps the table data to dmesg)
891
for i in (header_len..required_bytes).step_by(entry_len) {
892
dev_dbg!(dev, "PMU entry: {:02x?}\n", &data[i..][..entry_len]);
893
}
894
895
Ok(PmuLookupTable { header, table_data })
896
}
897
898
fn lookup_index(&self, idx: u8) -> Result<PmuLookupTableEntry> {
899
if idx >= self.header.entry_count {
900
return Err(EINVAL);
901
}
902
903
let index = (usize::from(idx)) * usize::from(self.header.entry_len);
904
PmuLookupTableEntry::new(&self.table_data[index..])
905
}
906
907
// find entry by type value
908
fn find_entry_by_type(&self, entry_type: u8) -> Result<PmuLookupTableEntry> {
909
for i in 0..self.header.entry_count {
910
let entry = self.lookup_index(i)?;
911
if entry.application_id == entry_type {
912
return Ok(entry);
913
}
914
}
915
916
Err(EINVAL)
917
}
918
}
919
920
impl FwSecBiosBuilder {
921
fn setup_falcon_data(
922
&mut self,
923
pci_at_image: &PciAtBiosImage,
924
first_fwsec: &FwSecBiosBuilder,
925
) -> Result {
926
let mut offset = usize::from_safe_cast(pci_at_image.falcon_data_ptr()?);
927
let mut pmu_in_first_fwsec = false;
928
929
// The falcon data pointer assumes that the PciAt and FWSEC images
930
// are contiguous in memory. However, testing shows the EFI image sits in
931
// between them. So calculate the offset from the end of the PciAt image
932
// rather than the start of it. Compensate.
933
offset -= pci_at_image.base.data.len();
934
935
// The offset is now from the start of the first Fwsec image, however
936
// the offset points to a location in the second Fwsec image. Since
937
// the fwsec images are contiguous, subtract the length of the first Fwsec
938
// image from the offset to get the offset to the start of the second
939
// Fwsec image.
940
if offset < first_fwsec.base.data.len() {
941
pmu_in_first_fwsec = true;
942
} else {
943
offset -= first_fwsec.base.data.len();
944
}
945
946
self.falcon_data_offset = Some(offset);
947
948
if pmu_in_first_fwsec {
949
self.pmu_lookup_table = Some(PmuLookupTable::new(
950
&self.base.dev,
951
&first_fwsec.base.data[offset..],
952
)?);
953
} else {
954
self.pmu_lookup_table = Some(PmuLookupTable::new(
955
&self.base.dev,
956
&self.base.data[offset..],
957
)?);
958
}
959
960
match self
961
.pmu_lookup_table
962
.as_ref()
963
.ok_or(EINVAL)?
964
.find_entry_by_type(FALCON_UCODE_ENTRY_APPID_FWSEC_PROD)
965
{
966
Ok(entry) => {
967
let mut ucode_offset = usize::from_safe_cast(entry.data);
968
ucode_offset -= pci_at_image.base.data.len();
969
if ucode_offset < first_fwsec.base.data.len() {
970
dev_err!(self.base.dev, "Falcon Ucode offset not in second Fwsec.\n");
971
return Err(EINVAL);
972
}
973
ucode_offset -= first_fwsec.base.data.len();
974
self.falcon_ucode_offset = Some(ucode_offset);
975
}
976
Err(e) => {
977
dev_err!(
978
self.base.dev,
979
"PmuLookupTableEntry not found, error: {:?}\n",
980
e
981
);
982
return Err(EINVAL);
983
}
984
}
985
Ok(())
986
}
987
988
/// Build the final FwSecBiosImage from this builder
989
fn build(self) -> Result<FwSecBiosImage> {
990
let ret = FwSecBiosImage {
991
base: self.base,
992
falcon_ucode_offset: self.falcon_ucode_offset.ok_or(EINVAL)?,
993
};
994
995
if cfg!(debug_assertions) {
996
// Print the desc header for debugging
997
let desc = ret.header()?;
998
dev_dbg!(ret.base.dev, "PmuLookupTableEntry desc: {:#?}\n", desc);
999
}
1000
1001
Ok(ret)
1002
}
1003
}
1004
1005
impl FwSecBiosImage {
1006
/// Get the FwSec header ([`FalconUCodeDescV3`]).
1007
pub(crate) fn header(&self) -> Result<&FalconUCodeDescV3> {
1008
// Get the falcon ucode offset that was found in setup_falcon_data.
1009
let falcon_ucode_offset = self.falcon_ucode_offset;
1010
1011
// Make sure the offset is within the data bounds.
1012
if falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>() > self.base.data.len() {
1013
dev_err!(
1014
self.base.dev,
1015
"fwsec-frts header not contained within BIOS bounds\n"
1016
);
1017
return Err(ERANGE);
1018
}
1019
1020
// Read the first 4 bytes to get the version.
1021
let hdr_bytes: [u8; 4] = self.base.data[falcon_ucode_offset..falcon_ucode_offset + 4]
1022
.try_into()
1023
.map_err(|_| EINVAL)?;
1024
let hdr = u32::from_le_bytes(hdr_bytes);
1025
let ver = (hdr & 0xff00) >> 8;
1026
1027
if ver != 3 {
1028
dev_err!(self.base.dev, "invalid fwsec firmware version: {:?}\n", ver);
1029
return Err(EINVAL);
1030
}
1031
1032
// Return a reference to the FalconUCodeDescV3 structure.
1033
//
1034
// SAFETY: We have checked that `falcon_ucode_offset + size_of::<FalconUCodeDescV3>` is
1035
// within the bounds of `data`. Also, this data vector is from ROM, and the `data` field
1036
// in `BiosImageBase` is immutable after construction.
1037
Ok(unsafe {
1038
&*(self
1039
.base
1040
.data
1041
.as_ptr()
1042
.add(falcon_ucode_offset)
1043
.cast::<FalconUCodeDescV3>())
1044
})
1045
}
1046
1047
/// Get the ucode data as a byte slice
1048
pub(crate) fn ucode(&self, desc: &FalconUCodeDescV3) -> Result<&[u8]> {
1049
let falcon_ucode_offset = self.falcon_ucode_offset;
1050
1051
// The ucode data follows the descriptor.
1052
let ucode_data_offset = falcon_ucode_offset + desc.size();
1053
let size = usize::from_safe_cast(desc.imem_load_size + desc.dmem_load_size);
1054
1055
// Get the data slice, checking bounds in a single operation.
1056
self.base
1057
.data
1058
.get(ucode_data_offset..ucode_data_offset + size)
1059
.ok_or(ERANGE)
1060
.inspect_err(|_| {
1061
dev_err!(
1062
self.base.dev,
1063
"fwsec ucode data not contained within BIOS bounds\n"
1064
)
1065
})
1066
}
1067
1068
/// Get the signatures as a byte slice
1069
pub(crate) fn sigs(&self, desc: &FalconUCodeDescV3) -> Result<&[Bcrt30Rsa3kSignature]> {
1070
// The signatures data follows the descriptor.
1071
let sigs_data_offset = self.falcon_ucode_offset + core::mem::size_of::<FalconUCodeDescV3>();
1072
let sigs_count = usize::from(desc.signature_count);
1073
let sigs_size = sigs_count * core::mem::size_of::<Bcrt30Rsa3kSignature>();
1074
1075
// Make sure the data is within bounds.
1076
if sigs_data_offset + sigs_size > self.base.data.len() {
1077
dev_err!(
1078
self.base.dev,
1079
"fwsec signatures data not contained within BIOS bounds\n"
1080
);
1081
return Err(ERANGE);
1082
}
1083
1084
// SAFETY: we checked that `data + sigs_data_offset + (signature_count *
1085
// sizeof::<Bcrt30Rsa3kSignature>()` is within the bounds of `data`.
1086
Ok(unsafe {
1087
core::slice::from_raw_parts(
1088
self.base
1089
.data
1090
.as_ptr()
1091
.add(sigs_data_offset)
1092
.cast::<Bcrt30Rsa3kSignature>(),
1093
sigs_count,
1094
)
1095
})
1096
}
1097
}
1098
1099