// SPDX-License-Identifier: GPL-2.0
//! VBIOS extraction and parsing.
use core::convert::TryFrom;
use kernel::{
device,
prelude::*,
ptr::{
Alignable,
Alignment, //
},
transmute::FromBytes,
types::ARef,
};
use crate::{
driver::Bar0,
firmware::{
fwsec::Bcrt30Rsa3kSignature,
FalconUCodeDescV3, //
},
num::FromSafeCast,
};
/// The offset of the VBIOS ROM in the BAR0 space.
const ROM_OFFSET: usize = 0x300000;
/// The maximum length of the VBIOS ROM to scan into.
const BIOS_MAX_SCAN_LEN: usize = 0x100000;
/// The size to read ahead when parsing initial BIOS image headers.
const BIOS_READ_AHEAD_SIZE: usize = 1024;
/// The bit in the last image indicator byte for the PCI Data Structure that
/// indicates the last image. Bit 0-6 are reserved, bit 7 is last image bit.
const LAST_IMAGE_BIT_MASK: u8 = 0x80;
/// BIOS Image Type from PCI Data Structure code_type field.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum BiosImageType {
/// PC-AT compatible BIOS image (x86 legacy)
PciAt = 0x00,
/// EFI (Extensible Firmware Interface) BIOS image
Efi = 0x03,
/// NBSI (Notebook System Information) BIOS image
Nbsi = 0x70,
/// FwSec (Firmware Security) BIOS image
FwSec = 0xE0,
}
impl TryFrom<u8> for BiosImageType {
type Error = Error;
fn try_from(code: u8) -> Result<Self> {
match code {
0x00 => Ok(Self::PciAt),
0x03 => Ok(Self::Efi),
0x70 => Ok(Self::Nbsi),
0xE0 => Ok(Self::FwSec),
_ => Err(EINVAL),
}
}
}
// PMU lookup table entry types. Used to locate PMU table entries
// in the Fwsec image, corresponding to falcon ucodes.
#[expect(dead_code)]
const FALCON_UCODE_ENTRY_APPID_FIRMWARE_SEC_LIC: u8 = 0x05;
#[expect(dead_code)]
const FALCON_UCODE_ENTRY_APPID_FWSEC_DBG: u8 = 0x45;
const FALCON_UCODE_ENTRY_APPID_FWSEC_PROD: u8 = 0x85;
/// Vbios Reader for constructing the VBIOS data.
struct VbiosIterator<'a> {
dev: &'a device::Device,
bar0: &'a Bar0,
/// VBIOS data vector: As BIOS images are scanned, they are added to this vector for reference
/// or copying into other data structures. It is the entire scanned contents of the VBIOS which
/// progressively extends. It is used so that we do not re-read any contents that are already
/// read as we use the cumulative length read so far, and re-read any gaps as we extend the
/// length.
data: KVec<u8>,
/// Current offset of the [`Iterator`].
current_offset: usize,
/// Indicate whether the last image has been found.
last_found: bool,
}
impl<'a> VbiosIterator<'a> {
fn new(dev: &'a device::Device, bar0: &'a Bar0) -> Result<Self> {
Ok(Self {
dev,
bar0,
data: KVec::new(),
current_offset: 0,
last_found: false,
})
}
/// Read bytes from the ROM at the current end of the data vector.
fn read_more(&mut self, len: usize) -> Result {
let current_len = self.data.len();
let start = ROM_OFFSET + current_len;
// Ensure length is a multiple of 4 for 32-bit reads
if len % core::mem::size_of::<u32>() != 0 {
dev_err!(
self.dev,
"VBIOS read length {} is not a multiple of 4\n",
len
);
return Err(EINVAL);
}
self.data.reserve(len, GFP_KERNEL)?;
// Read ROM data bytes and push directly to `data`.
for addr in (start..start + len).step_by(core::mem::size_of::<u32>()) {
// Read 32-bit word from the VBIOS ROM
let word = self.bar0.try_read32(addr)?;
// Convert the `u32` to a 4 byte array and push each byte.
word.to_ne_bytes()
.