vbios.rs 35 KB

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