projects/linux·blob master · refs

978 lines · 32.2 KiB

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