Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0
2
3//! VBIOS extraction and parsing.
4
5use core::convert::TryFrom;
6
7use kernel::{
8 device,
9 prelude::*,
10 ptr::{
11 Alignable,
12 Alignment, //
13 },
14 transmute::FromBytes,
15 types::ARef,
16};
17
18use 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.
28const ROM_OFFSET: usize = 0x300000;
29/// The maximum length of the VBIOS ROM to scan into.
30const BIOS_MAX_SCAN_LEN: usize = 0x100000;
31/// The size to read ahead when parsing initial BIOS image headers.
32const 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.
35const 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)]
40enum 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
51impl 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)]
68const FALCON_UCODE_ENTRY_APPID_FIRMWARE_SEC_LIC: u8 = 0x05;
69#[expect(dead_code)]
70const FALCON_UCODE_ENTRY_APPID_FWSEC_DBG: u8 = 0x45;
71const FALCON_UCODE_ENTRY_APPID_FWSEC_PROD: u8 = 0x85;
72
73/// Vbios Reader for constructing the VBIOS data.
74struct 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
89impl<'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
179impl<'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
224pub(crate) struct Vbios {
225 fwsec_image: FwSecBiosImage,
226}
227
228impl 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)]
301struct 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`.
329unsafe impl FromBytes for PcirStruct {}
330
331impl 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)]
371struct 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`.
389unsafe impl FromBytes for BitHeader {}
390
391impl 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)]
407struct 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
419const BIT_TOKEN_ID_FALCON_DATA: u8 = 0x70;
420
421impl 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)]
467struct 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
480impl 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)]
538struct 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`.
552unsafe impl FromBytes for NpdeStruct {}
553
554impl 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.
612struct PciAtBiosImage {
613 base: BiosImage,
614 bit_header: BitHeader,
615 bit_offset: usize,
616}
617
618#[expect(dead_code)]
619struct EfiBiosImage {
620 base: BiosImage,
621 // EFI-specific fields can be added here in the future.
622}
623
624#[expect(dead_code)]
625struct NbsiBiosImage {
626 base: BiosImage,
627 // NBSI-specific fields can be added here in the future.
628}
629
630struct 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.
649pub(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)]
659struct 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
672impl 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
755impl 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
808impl 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)]
827struct PmuLookupTableEntry {
828 application_id: u8,
829 target_id: u8,
830 data: u32,
831}
832
833impl 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)]
848struct 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`.
856unsafe 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.
863struct PmuLookupTable {
864 header: PmuLookupTableHeader,
865 table_data: KVec<u8>,
866}
867
868impl 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
920impl 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
1005impl 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}