xref: /linux/drivers/gpu/nova-core/vbios.rs (revision 6fda04e7f0cd57989066fa3b7975af08d779c8fd)
1*6fda04e7SJoel Fernandes // SPDX-License-Identifier: GPL-2.0
2*6fda04e7SJoel Fernandes 
3*6fda04e7SJoel Fernandes //! VBIOS extraction and parsing.
4*6fda04e7SJoel Fernandes 
5*6fda04e7SJoel Fernandes // To be removed when all code is used.
6*6fda04e7SJoel Fernandes #![expect(dead_code)]
7*6fda04e7SJoel Fernandes 
8*6fda04e7SJoel Fernandes use crate::driver::Bar0;
9*6fda04e7SJoel Fernandes use core::convert::TryFrom;
10*6fda04e7SJoel Fernandes use kernel::error::Result;
11*6fda04e7SJoel Fernandes use kernel::pci;
12*6fda04e7SJoel Fernandes use kernel::prelude::*;
13*6fda04e7SJoel Fernandes 
14*6fda04e7SJoel Fernandes /// The offset of the VBIOS ROM in the BAR0 space.
15*6fda04e7SJoel Fernandes const ROM_OFFSET: usize = 0x300000;
16*6fda04e7SJoel Fernandes /// The maximum length of the VBIOS ROM to scan into.
17*6fda04e7SJoel Fernandes const BIOS_MAX_SCAN_LEN: usize = 0x100000;
18*6fda04e7SJoel Fernandes /// The size to read ahead when parsing initial BIOS image headers.
19*6fda04e7SJoel Fernandes const BIOS_READ_AHEAD_SIZE: usize = 1024;
20*6fda04e7SJoel Fernandes /// The bit in the last image indicator byte for the PCI Data Structure that
21*6fda04e7SJoel Fernandes /// indicates the last image. Bit 0-6 are reserved, bit 7 is last image bit.
22*6fda04e7SJoel Fernandes const LAST_IMAGE_BIT_MASK: u8 = 0x80;
23*6fda04e7SJoel Fernandes 
24*6fda04e7SJoel Fernandes // PMU lookup table entry types. Used to locate PMU table entries
25*6fda04e7SJoel Fernandes // in the Fwsec image, corresponding to falcon ucodes.
26*6fda04e7SJoel Fernandes #[expect(dead_code)]
27*6fda04e7SJoel Fernandes const FALCON_UCODE_ENTRY_APPID_FIRMWARE_SEC_LIC: u8 = 0x05;
28*6fda04e7SJoel Fernandes #[expect(dead_code)]
29*6fda04e7SJoel Fernandes const FALCON_UCODE_ENTRY_APPID_FWSEC_DBG: u8 = 0x45;
30*6fda04e7SJoel Fernandes const FALCON_UCODE_ENTRY_APPID_FWSEC_PROD: u8 = 0x85;
31*6fda04e7SJoel Fernandes 
32*6fda04e7SJoel Fernandes /// Vbios Reader for constructing the VBIOS data.
33*6fda04e7SJoel Fernandes struct VbiosIterator<'a> {
34*6fda04e7SJoel Fernandes     pdev: &'a pci::Device,
35*6fda04e7SJoel Fernandes     bar0: &'a Bar0,
36*6fda04e7SJoel Fernandes     /// VBIOS data vector: As BIOS images are scanned, they are added to this vector for reference
37*6fda04e7SJoel Fernandes     /// or copying into other data structures. It is the entire scanned contents of the VBIOS which
38*6fda04e7SJoel Fernandes     /// progressively extends. It is used so that we do not re-read any contents that are already
39*6fda04e7SJoel Fernandes     /// read as we use the cumulative length read so far, and re-read any gaps as we extend the
40*6fda04e7SJoel Fernandes     /// length.
41*6fda04e7SJoel Fernandes     data: KVec<u8>,
42*6fda04e7SJoel Fernandes     /// Current offset of the [`Iterator`].
43*6fda04e7SJoel Fernandes     current_offset: usize,
44*6fda04e7SJoel Fernandes     /// Indicate whether the last image has been found.
45*6fda04e7SJoel Fernandes     last_found: bool,
46*6fda04e7SJoel Fernandes }
47*6fda04e7SJoel Fernandes 
48*6fda04e7SJoel Fernandes impl<'a> VbiosIterator<'a> {
49*6fda04e7SJoel Fernandes     fn new(pdev: &'a pci::Device, bar0: &'a Bar0) -> Result<Self> {
50*6fda04e7SJoel Fernandes         Ok(Self {
51*6fda04e7SJoel Fernandes             pdev,
52*6fda04e7SJoel Fernandes             bar0,
53*6fda04e7SJoel Fernandes             data: KVec::new(),
54*6fda04e7SJoel Fernandes             current_offset: 0,
55*6fda04e7SJoel Fernandes             last_found: false,
56*6fda04e7SJoel Fernandes         })
57*6fda04e7SJoel Fernandes     }
58*6fda04e7SJoel Fernandes 
59*6fda04e7SJoel Fernandes     /// Read bytes from the ROM at the current end of the data vector.
60*6fda04e7SJoel Fernandes     fn read_more(&mut self, len: usize) -> Result {
61*6fda04e7SJoel Fernandes         let current_len = self.data.len();
62*6fda04e7SJoel Fernandes         let start = ROM_OFFSET + current_len;
63*6fda04e7SJoel Fernandes 
64*6fda04e7SJoel Fernandes         // Ensure length is a multiple of 4 for 32-bit reads
65*6fda04e7SJoel Fernandes         if len % core::mem::size_of::<u32>() != 0 {
66*6fda04e7SJoel Fernandes             dev_err!(
67*6fda04e7SJoel Fernandes                 self.pdev.as_ref(),
68*6fda04e7SJoel Fernandes                 "VBIOS read length {} is not a multiple of 4\n",
69*6fda04e7SJoel Fernandes                 len
70*6fda04e7SJoel Fernandes             );
71*6fda04e7SJoel Fernandes             return Err(EINVAL);
72*6fda04e7SJoel Fernandes         }
73*6fda04e7SJoel Fernandes 
74*6fda04e7SJoel Fernandes         self.data.reserve(len, GFP_KERNEL)?;
75*6fda04e7SJoel Fernandes         // Read ROM data bytes and push directly to `data`.
76*6fda04e7SJoel Fernandes         for addr in (start..start + len).step_by(core::mem::size_of::<u32>()) {
77*6fda04e7SJoel Fernandes             // Read 32-bit word from the VBIOS ROM
78*6fda04e7SJoel Fernandes             let word = self.bar0.try_read32(addr)?;
79*6fda04e7SJoel Fernandes 
80*6fda04e7SJoel Fernandes             // Convert the `u32` to a 4 byte array and push each byte.
81*6fda04e7SJoel Fernandes             word.to_ne_bytes()
82*6fda04e7SJoel Fernandes                 .iter()
83*6fda04e7SJoel Fernandes                 .try_for_each(|&b| self.data.push(b, GFP_KERNEL))?;
84*6fda04e7SJoel Fernandes         }
85*6fda04e7SJoel Fernandes 
86*6fda04e7SJoel Fernandes         Ok(())
87*6fda04e7SJoel Fernandes     }
88*6fda04e7SJoel Fernandes 
89*6fda04e7SJoel Fernandes     /// Read bytes at a specific offset, filling any gap.
90*6fda04e7SJoel Fernandes     fn read_more_at_offset(&mut self, offset: usize, len: usize) -> Result {
91*6fda04e7SJoel Fernandes         if offset > BIOS_MAX_SCAN_LEN {
92*6fda04e7SJoel Fernandes             dev_err!(self.pdev.as_ref(), "Error: exceeded BIOS scan limit.\n");
93*6fda04e7SJoel Fernandes             return Err(EINVAL);
94*6fda04e7SJoel Fernandes         }
95*6fda04e7SJoel Fernandes 
96*6fda04e7SJoel Fernandes         // If `offset` is beyond current data size, fill the gap first.
97*6fda04e7SJoel Fernandes         let current_len = self.data.len();
98*6fda04e7SJoel Fernandes         let gap_bytes = offset.saturating_sub(current_len);
99*6fda04e7SJoel Fernandes 
100*6fda04e7SJoel Fernandes         // Now read the requested bytes at the offset.
101*6fda04e7SJoel Fernandes         self.read_more(gap_bytes + len)
102*6fda04e7SJoel Fernandes     }
103*6fda04e7SJoel Fernandes 
104*6fda04e7SJoel Fernandes     /// Read a BIOS image at a specific offset and create a [`BiosImage`] from it.
105*6fda04e7SJoel Fernandes     ///
106*6fda04e7SJoel Fernandes     /// `self.data` is extended as needed and a new [`BiosImage`] is returned.
107*6fda04e7SJoel Fernandes     /// `context` is a string describing the operation for error reporting.
108*6fda04e7SJoel Fernandes     fn read_bios_image_at_offset(
109*6fda04e7SJoel Fernandes         &mut self,
110*6fda04e7SJoel Fernandes         offset: usize,
111*6fda04e7SJoel Fernandes         len: usize,
112*6fda04e7SJoel Fernandes         context: &str,
113*6fda04e7SJoel Fernandes     ) -> Result<BiosImage> {
114*6fda04e7SJoel Fernandes         let data_len = self.data.len();
115*6fda04e7SJoel Fernandes         if offset + len > data_len {
116*6fda04e7SJoel Fernandes             self.read_more_at_offset(offset, len).inspect_err(|e| {
117*6fda04e7SJoel Fernandes                 dev_err!(
118*6fda04e7SJoel Fernandes                     self.pdev.as_ref(),
119*6fda04e7SJoel Fernandes                     "Failed to read more at offset {:#x}: {:?}\n",
120*6fda04e7SJoel Fernandes                     offset,
121*6fda04e7SJoel Fernandes                     e
122*6fda04e7SJoel Fernandes                 )
123*6fda04e7SJoel Fernandes             })?;
124*6fda04e7SJoel Fernandes         }
125*6fda04e7SJoel Fernandes 
126*6fda04e7SJoel Fernandes         BiosImage::new(self.pdev, &self.data[offset..offset + len]).inspect_err(|err| {
127*6fda04e7SJoel Fernandes             dev_err!(
128*6fda04e7SJoel Fernandes                 self.pdev.as_ref(),
129*6fda04e7SJoel Fernandes                 "Failed to {} at offset {:#x}: {:?}\n",
130*6fda04e7SJoel Fernandes                 context,
131*6fda04e7SJoel Fernandes                 offset,
132*6fda04e7SJoel Fernandes                 err
133*6fda04e7SJoel Fernandes             )
134*6fda04e7SJoel Fernandes         })
135*6fda04e7SJoel Fernandes     }
136*6fda04e7SJoel Fernandes }
137*6fda04e7SJoel Fernandes 
138*6fda04e7SJoel Fernandes impl<'a> Iterator for VbiosIterator<'a> {
139*6fda04e7SJoel Fernandes     type Item = Result<BiosImage>;
140*6fda04e7SJoel Fernandes 
141*6fda04e7SJoel Fernandes     /// Iterate over all VBIOS images until the last image is detected or offset
142*6fda04e7SJoel Fernandes     /// exceeds scan limit.
143*6fda04e7SJoel Fernandes     fn next(&mut self) -> Option<Self::Item> {
144*6fda04e7SJoel Fernandes         if self.last_found {
145*6fda04e7SJoel Fernandes             return None;
146*6fda04e7SJoel Fernandes         }
147*6fda04e7SJoel Fernandes 
148*6fda04e7SJoel Fernandes         if self.current_offset > BIOS_MAX_SCAN_LEN {
149*6fda04e7SJoel Fernandes             dev_err!(
150*6fda04e7SJoel Fernandes                 self.pdev.as_ref(),
151*6fda04e7SJoel Fernandes                 "Error: exceeded BIOS scan limit, stopping scan\n"
152*6fda04e7SJoel Fernandes             );
153*6fda04e7SJoel Fernandes             return None;
154*6fda04e7SJoel Fernandes         }
155*6fda04e7SJoel Fernandes 
156*6fda04e7SJoel Fernandes         // Parse image headers first to get image size.
157*6fda04e7SJoel Fernandes         let image_size = match self.read_bios_image_at_offset(
158*6fda04e7SJoel Fernandes             self.current_offset,
159*6fda04e7SJoel Fernandes             BIOS_READ_AHEAD_SIZE,
160*6fda04e7SJoel Fernandes             "parse initial BIOS image headers",
161*6fda04e7SJoel Fernandes         ) {
162*6fda04e7SJoel Fernandes             Ok(image) => image.image_size_bytes(),
163*6fda04e7SJoel Fernandes             Err(e) => return Some(Err(e)),
164*6fda04e7SJoel Fernandes         };
165*6fda04e7SJoel Fernandes 
166*6fda04e7SJoel Fernandes         // Now create a new `BiosImage` with the full image data.
167*6fda04e7SJoel Fernandes         let full_image = match self.read_bios_image_at_offset(
168*6fda04e7SJoel Fernandes             self.current_offset,
169*6fda04e7SJoel Fernandes             image_size,
170*6fda04e7SJoel Fernandes             "parse full BIOS image",
171*6fda04e7SJoel Fernandes         ) {
172*6fda04e7SJoel Fernandes             Ok(image) => image,
173*6fda04e7SJoel Fernandes             Err(e) => return Some(Err(e)),
174*6fda04e7SJoel Fernandes         };
175*6fda04e7SJoel Fernandes 
176*6fda04e7SJoel Fernandes         self.last_found = full_image.is_last();
177*6fda04e7SJoel Fernandes 
178*6fda04e7SJoel Fernandes         // Advance to next image (aligned to 512 bytes).
179*6fda04e7SJoel Fernandes         self.current_offset += image_size;
180*6fda04e7SJoel Fernandes         // TODO: replace with `align_up` once it lands.
181*6fda04e7SJoel Fernandes         self.current_offset = self.current_offset.next_multiple_of(512);
182*6fda04e7SJoel Fernandes 
183*6fda04e7SJoel Fernandes         Some(Ok(full_image))
184*6fda04e7SJoel Fernandes     }
185*6fda04e7SJoel Fernandes }
186*6fda04e7SJoel Fernandes 
187*6fda04e7SJoel Fernandes pub(crate) struct Vbios {
188*6fda04e7SJoel Fernandes     fwsec_image: FwSecBiosImage,
189*6fda04e7SJoel Fernandes }
190*6fda04e7SJoel Fernandes 
191*6fda04e7SJoel Fernandes impl Vbios {
192*6fda04e7SJoel Fernandes     /// Probe for VBIOS extraction.
193*6fda04e7SJoel Fernandes     ///
194*6fda04e7SJoel Fernandes     /// Once the VBIOS object is built, `bar0` is not read for [`Vbios`] purposes anymore.
195*6fda04e7SJoel Fernandes     pub(crate) fn new(pdev: &pci::Device, bar0: &Bar0) -> Result<Vbios> {
196*6fda04e7SJoel Fernandes         // Images to extract from iteration
197*6fda04e7SJoel Fernandes         let mut pci_at_image: Option<PciAtBiosImage> = None;
198*6fda04e7SJoel Fernandes         let mut first_fwsec_image: Option<FwSecBiosImage> = None;
199*6fda04e7SJoel Fernandes         let mut second_fwsec_image: Option<FwSecBiosImage> = None;
200*6fda04e7SJoel Fernandes 
201*6fda04e7SJoel Fernandes         // Parse all VBIOS images in the ROM
202*6fda04e7SJoel Fernandes         for image_result in VbiosIterator::new(pdev, bar0)? {
203*6fda04e7SJoel Fernandes             let full_image = image_result?;
204*6fda04e7SJoel Fernandes 
205*6fda04e7SJoel Fernandes             dev_dbg!(
206*6fda04e7SJoel Fernandes                 pdev.as_ref(),
207*6fda04e7SJoel Fernandes                 "Found BIOS image: size: {:#x}, type: {}, last: {}\n",
208*6fda04e7SJoel Fernandes                 full_image.image_size_bytes(),
209*6fda04e7SJoel Fernandes                 full_image.image_type_str(),
210*6fda04e7SJoel Fernandes                 full_image.is_last()
211*6fda04e7SJoel Fernandes             );
212*6fda04e7SJoel Fernandes 
213*6fda04e7SJoel Fernandes             // Get references to images we will need after the loop, in order to
214*6fda04e7SJoel Fernandes             // setup the falcon data offset.
215*6fda04e7SJoel Fernandes             match full_image {
216*6fda04e7SJoel Fernandes                 BiosImage::PciAt(image) => {
217*6fda04e7SJoel Fernandes                     pci_at_image = Some(image);
218*6fda04e7SJoel Fernandes                 }
219*6fda04e7SJoel Fernandes                 BiosImage::FwSec(image) => {
220*6fda04e7SJoel Fernandes                     if first_fwsec_image.is_none() {
221*6fda04e7SJoel Fernandes                         first_fwsec_image = Some(image);
222*6fda04e7SJoel Fernandes                     } else {
223*6fda04e7SJoel Fernandes                         second_fwsec_image = Some(image);
224*6fda04e7SJoel Fernandes                     }
225*6fda04e7SJoel Fernandes                 }
226*6fda04e7SJoel Fernandes                 // For now we don't need to handle these
227*6fda04e7SJoel Fernandes                 BiosImage::Efi(_image) => {}
228*6fda04e7SJoel Fernandes                 BiosImage::Nbsi(_image) => {}
229*6fda04e7SJoel Fernandes             }
230*6fda04e7SJoel Fernandes         }
231*6fda04e7SJoel Fernandes 
232*6fda04e7SJoel Fernandes         // Using all the images, setup the falcon data pointer in Fwsec.
233*6fda04e7SJoel Fernandes         // These are temporarily unused images and will be used in later patches.
234*6fda04e7SJoel Fernandes         if let (Some(second), Some(_first), Some(_pci_at)) =
235*6fda04e7SJoel Fernandes             (second_fwsec_image, first_fwsec_image, pci_at_image)
236*6fda04e7SJoel Fernandes         {
237*6fda04e7SJoel Fernandes             Ok(Vbios {
238*6fda04e7SJoel Fernandes                 fwsec_image: second,
239*6fda04e7SJoel Fernandes             })
240*6fda04e7SJoel Fernandes         } else {
241*6fda04e7SJoel Fernandes             dev_err!(
242*6fda04e7SJoel Fernandes                 pdev.as_ref(),
243*6fda04e7SJoel Fernandes                 "Missing required images for falcon data setup, skipping\n"
244*6fda04e7SJoel Fernandes             );
245*6fda04e7SJoel Fernandes             Err(EINVAL)
246*6fda04e7SJoel Fernandes         }
247*6fda04e7SJoel Fernandes     }
248*6fda04e7SJoel Fernandes }
249*6fda04e7SJoel Fernandes 
250*6fda04e7SJoel Fernandes /// PCI Data Structure as defined in PCI Firmware Specification
251*6fda04e7SJoel Fernandes #[derive(Debug, Clone)]
252*6fda04e7SJoel Fernandes #[repr(C)]
253*6fda04e7SJoel Fernandes struct PcirStruct {
254*6fda04e7SJoel Fernandes     /// PCI Data Structure signature ("PCIR" or "NPDS")
255*6fda04e7SJoel Fernandes     signature: [u8; 4],
256*6fda04e7SJoel Fernandes     /// PCI Vendor ID (e.g., 0x10DE for NVIDIA)
257*6fda04e7SJoel Fernandes     vendor_id: u16,
258*6fda04e7SJoel Fernandes     /// PCI Device ID
259*6fda04e7SJoel Fernandes     device_id: u16,
260*6fda04e7SJoel Fernandes     /// Device List Pointer
261*6fda04e7SJoel Fernandes     device_list_ptr: u16,
262*6fda04e7SJoel Fernandes     /// PCI Data Structure Length
263*6fda04e7SJoel Fernandes     pci_data_struct_len: u16,
264*6fda04e7SJoel Fernandes     /// PCI Data Structure Revision
265*6fda04e7SJoel Fernandes     pci_data_struct_rev: u8,
266*6fda04e7SJoel Fernandes     /// Class code (3 bytes, 0x03 for display controller)
267*6fda04e7SJoel Fernandes     class_code: [u8; 3],
268*6fda04e7SJoel Fernandes     /// Size of this image in 512-byte blocks
269*6fda04e7SJoel Fernandes     image_len: u16,
270*6fda04e7SJoel Fernandes     /// Revision Level of the Vendor's ROM
271*6fda04e7SJoel Fernandes     vendor_rom_rev: u16,
272*6fda04e7SJoel Fernandes     /// ROM image type (0x00 = PC-AT compatible, 0x03 = EFI, 0x70 = NBSI)
273*6fda04e7SJoel Fernandes     code_type: u8,
274*6fda04e7SJoel Fernandes     /// Last image indicator (0x00 = Not last image, 0x80 = Last image)
275*6fda04e7SJoel Fernandes     last_image: u8,
276*6fda04e7SJoel Fernandes     /// Maximum Run-time Image Length (units of 512 bytes)
277*6fda04e7SJoel Fernandes     max_runtime_image_len: u16,
278*6fda04e7SJoel Fernandes }
279*6fda04e7SJoel Fernandes 
280*6fda04e7SJoel Fernandes impl PcirStruct {
281*6fda04e7SJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
282*6fda04e7SJoel Fernandes         if data.len() < core::mem::size_of::<PcirStruct>() {
283*6fda04e7SJoel Fernandes             dev_err!(pdev.as_ref(), "Not enough data for PcirStruct\n");
284*6fda04e7SJoel Fernandes             return Err(EINVAL);
285*6fda04e7SJoel Fernandes         }
286*6fda04e7SJoel Fernandes 
287*6fda04e7SJoel Fernandes         let mut signature = [0u8; 4];
288*6fda04e7SJoel Fernandes         signature.copy_from_slice(&data[0..4]);
289*6fda04e7SJoel Fernandes 
290*6fda04e7SJoel Fernandes         // Signature should be "PCIR" (0x52494350) or "NPDS" (0x5344504e).
291*6fda04e7SJoel Fernandes         if &signature != b"PCIR" && &signature != b"NPDS" {
292*6fda04e7SJoel Fernandes             dev_err!(
293*6fda04e7SJoel Fernandes                 pdev.as_ref(),
294*6fda04e7SJoel Fernandes                 "Invalid signature for PcirStruct: {:?}\n",
295*6fda04e7SJoel Fernandes                 signature
296*6fda04e7SJoel Fernandes             );
297*6fda04e7SJoel Fernandes             return Err(EINVAL);
298*6fda04e7SJoel Fernandes         }
299*6fda04e7SJoel Fernandes 
300*6fda04e7SJoel Fernandes         let mut class_code = [0u8; 3];
301*6fda04e7SJoel Fernandes         class_code.copy_from_slice(&data[13..16]);
302*6fda04e7SJoel Fernandes 
303*6fda04e7SJoel Fernandes         let image_len = u16::from_le_bytes([data[16], data[17]]);
304*6fda04e7SJoel Fernandes         if image_len == 0 {
305*6fda04e7SJoel Fernandes             dev_err!(pdev.as_ref(), "Invalid image length: 0\n");
306*6fda04e7SJoel Fernandes             return Err(EINVAL);
307*6fda04e7SJoel Fernandes         }
308*6fda04e7SJoel Fernandes 
309*6fda04e7SJoel Fernandes         Ok(PcirStruct {
310*6fda04e7SJoel Fernandes             signature,
311*6fda04e7SJoel Fernandes             vendor_id: u16::from_le_bytes([data[4], data[5]]),
312*6fda04e7SJoel Fernandes             device_id: u16::from_le_bytes([data[6], data[7]]),
313*6fda04e7SJoel Fernandes             device_list_ptr: u16::from_le_bytes([data[8], data[9]]),
314*6fda04e7SJoel Fernandes             pci_data_struct_len: u16::from_le_bytes([data[10], data[11]]),
315*6fda04e7SJoel Fernandes             pci_data_struct_rev: data[12],
316*6fda04e7SJoel Fernandes             class_code,
317*6fda04e7SJoel Fernandes             image_len,
318*6fda04e7SJoel Fernandes             vendor_rom_rev: u16::from_le_bytes([data[18], data[19]]),
319*6fda04e7SJoel Fernandes             code_type: data[20],
320*6fda04e7SJoel Fernandes             last_image: data[21],
321*6fda04e7SJoel Fernandes             max_runtime_image_len: u16::from_le_bytes([data[22], data[23]]),
322*6fda04e7SJoel Fernandes         })
323*6fda04e7SJoel Fernandes     }
324*6fda04e7SJoel Fernandes 
325*6fda04e7SJoel Fernandes     /// Check if this is the last image in the ROM.
326*6fda04e7SJoel Fernandes     fn is_last(&self) -> bool {
327*6fda04e7SJoel Fernandes         self.last_image & LAST_IMAGE_BIT_MASK != 0
328*6fda04e7SJoel Fernandes     }
329*6fda04e7SJoel Fernandes 
330*6fda04e7SJoel Fernandes     /// Calculate image size in bytes from 512-byte blocks.
331*6fda04e7SJoel Fernandes     fn image_size_bytes(&self) -> usize {
332*6fda04e7SJoel Fernandes         self.image_len as usize * 512
333*6fda04e7SJoel Fernandes     }
334*6fda04e7SJoel Fernandes }
335*6fda04e7SJoel Fernandes 
336*6fda04e7SJoel Fernandes /// PCI ROM Expansion Header as defined in PCI Firmware Specification.
337*6fda04e7SJoel Fernandes ///
338*6fda04e7SJoel Fernandes /// This is header is at the beginning of every image in the set of images in the ROM. It contains
339*6fda04e7SJoel Fernandes /// a pointer to the PCI Data Structure which describes the image. For "NBSI" images (NoteBook
340*6fda04e7SJoel Fernandes /// System Information), the ROM header deviates from the standard and contains an offset to the
341*6fda04e7SJoel Fernandes /// NBSI image however we do not yet parse that in this module and keep it for future reference.
342*6fda04e7SJoel Fernandes #[derive(Debug, Clone, Copy)]
343*6fda04e7SJoel Fernandes #[expect(dead_code)]
344*6fda04e7SJoel Fernandes struct PciRomHeader {
345*6fda04e7SJoel Fernandes     /// 00h: Signature (0xAA55)
346*6fda04e7SJoel Fernandes     signature: u16,
347*6fda04e7SJoel Fernandes     /// 02h: Reserved bytes for processor architecture unique data (20 bytes)
348*6fda04e7SJoel Fernandes     reserved: [u8; 20],
349*6fda04e7SJoel Fernandes     /// 16h: NBSI Data Offset (NBSI-specific, offset from header to NBSI image)
350*6fda04e7SJoel Fernandes     nbsi_data_offset: Option<u16>,
351*6fda04e7SJoel Fernandes     /// 18h: Pointer to PCI Data Structure (offset from start of ROM image)
352*6fda04e7SJoel Fernandes     pci_data_struct_offset: u16,
353*6fda04e7SJoel Fernandes     /// 1Ah: Size of block (this is NBSI-specific)
354*6fda04e7SJoel Fernandes     size_of_block: Option<u32>,
355*6fda04e7SJoel Fernandes }
356*6fda04e7SJoel Fernandes 
357*6fda04e7SJoel Fernandes impl PciRomHeader {
358*6fda04e7SJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
359*6fda04e7SJoel Fernandes         if data.len() < 26 {
360*6fda04e7SJoel Fernandes             // Need at least 26 bytes to read pciDataStrucPtr and sizeOfBlock.
361*6fda04e7SJoel Fernandes             return Err(EINVAL);
362*6fda04e7SJoel Fernandes         }
363*6fda04e7SJoel Fernandes 
364*6fda04e7SJoel Fernandes         let signature = u16::from_le_bytes([data[0], data[1]]);
365*6fda04e7SJoel Fernandes 
366*6fda04e7SJoel Fernandes         // Check for valid ROM signatures.
367*6fda04e7SJoel Fernandes         match signature {
368*6fda04e7SJoel Fernandes             0xAA55 | 0xBB77 | 0x4E56 => {}
369*6fda04e7SJoel Fernandes             _ => {
370*6fda04e7SJoel Fernandes                 dev_err!(pdev.as_ref(), "ROM signature unknown {:#x}\n", signature);
371*6fda04e7SJoel Fernandes                 return Err(EINVAL);
372*6fda04e7SJoel Fernandes             }
373*6fda04e7SJoel Fernandes         }
374*6fda04e7SJoel Fernandes 
375*6fda04e7SJoel Fernandes         // Read the pointer to the PCI Data Structure at offset 0x18.
376*6fda04e7SJoel Fernandes         let pci_data_struct_ptr = u16::from_le_bytes([data[24], data[25]]);
377*6fda04e7SJoel Fernandes 
378*6fda04e7SJoel Fernandes         // Try to read optional fields if enough data.
379*6fda04e7SJoel Fernandes         let mut size_of_block = None;
380*6fda04e7SJoel Fernandes         let mut nbsi_data_offset = None;
381*6fda04e7SJoel Fernandes 
382*6fda04e7SJoel Fernandes         if data.len() >= 30 {
383*6fda04e7SJoel Fernandes             // Read size_of_block at offset 0x1A.
384*6fda04e7SJoel Fernandes             size_of_block = Some(
385*6fda04e7SJoel Fernandes                 (data[29] as u32) << 24
386*6fda04e7SJoel Fernandes                     | (data[28] as u32) << 16
387*6fda04e7SJoel Fernandes                     | (data[27] as u32) << 8
388*6fda04e7SJoel Fernandes                     | (data[26] as u32),
389*6fda04e7SJoel Fernandes             );
390*6fda04e7SJoel Fernandes         }
391*6fda04e7SJoel Fernandes 
392*6fda04e7SJoel Fernandes         // For NBSI images, try to read the nbsiDataOffset at offset 0x16.
393*6fda04e7SJoel Fernandes         if data.len() >= 24 {
394*6fda04e7SJoel Fernandes             nbsi_data_offset = Some(u16::from_le_bytes([data[22], data[23]]));
395*6fda04e7SJoel Fernandes         }
396*6fda04e7SJoel Fernandes 
397*6fda04e7SJoel Fernandes         Ok(PciRomHeader {
398*6fda04e7SJoel Fernandes             signature,
399*6fda04e7SJoel Fernandes             reserved: [0u8; 20],
400*6fda04e7SJoel Fernandes             pci_data_struct_offset: pci_data_struct_ptr,
401*6fda04e7SJoel Fernandes             size_of_block,
402*6fda04e7SJoel Fernandes             nbsi_data_offset,
403*6fda04e7SJoel Fernandes         })
404*6fda04e7SJoel Fernandes     }
405*6fda04e7SJoel Fernandes }
406*6fda04e7SJoel Fernandes 
407*6fda04e7SJoel Fernandes /// NVIDIA PCI Data Extension Structure.
408*6fda04e7SJoel Fernandes ///
409*6fda04e7SJoel Fernandes /// This is similar to the PCI Data Structure, but is Nvidia-specific and is placed right after the
410*6fda04e7SJoel Fernandes /// PCI Data Structure. It contains some fields that are redundant with the PCI Data Structure, but
411*6fda04e7SJoel Fernandes /// are needed for traversing the BIOS images. It is expected to be present in all BIOS images
412*6fda04e7SJoel Fernandes /// except for NBSI images.
413*6fda04e7SJoel Fernandes #[derive(Debug, Clone)]
414*6fda04e7SJoel Fernandes #[repr(C)]
415*6fda04e7SJoel Fernandes struct NpdeStruct {
416*6fda04e7SJoel Fernandes     /// 00h: Signature ("NPDE")
417*6fda04e7SJoel Fernandes     signature: [u8; 4],
418*6fda04e7SJoel Fernandes     /// 04h: NVIDIA PCI Data Extension Revision
419*6fda04e7SJoel Fernandes     npci_data_ext_rev: u16,
420*6fda04e7SJoel Fernandes     /// 06h: NVIDIA PCI Data Extension Length
421*6fda04e7SJoel Fernandes     npci_data_ext_len: u16,
422*6fda04e7SJoel Fernandes     /// 08h: Sub-image Length (in 512-byte units)
423*6fda04e7SJoel Fernandes     subimage_len: u16,
424*6fda04e7SJoel Fernandes     /// 0Ah: Last image indicator flag
425*6fda04e7SJoel Fernandes     last_image: u8,
426*6fda04e7SJoel Fernandes }
427*6fda04e7SJoel Fernandes 
428*6fda04e7SJoel Fernandes impl NpdeStruct {
429*6fda04e7SJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Option<Self> {
430*6fda04e7SJoel Fernandes         if data.len() < core::mem::size_of::<Self>() {
431*6fda04e7SJoel Fernandes             dev_dbg!(pdev.as_ref(), "Not enough data for NpdeStruct\n");
432*6fda04e7SJoel Fernandes             return None;
433*6fda04e7SJoel Fernandes         }
434*6fda04e7SJoel Fernandes 
435*6fda04e7SJoel Fernandes         let mut signature = [0u8; 4];
436*6fda04e7SJoel Fernandes         signature.copy_from_slice(&data[0..4]);
437*6fda04e7SJoel Fernandes 
438*6fda04e7SJoel Fernandes         // Signature should be "NPDE" (0x4544504E).
439*6fda04e7SJoel Fernandes         if &signature != b"NPDE" {
440*6fda04e7SJoel Fernandes             dev_dbg!(
441*6fda04e7SJoel Fernandes                 pdev.as_ref(),
442*6fda04e7SJoel Fernandes                 "Invalid signature for NpdeStruct: {:?}\n",
443*6fda04e7SJoel Fernandes                 signature
444*6fda04e7SJoel Fernandes             );
445*6fda04e7SJoel Fernandes             return None;
446*6fda04e7SJoel Fernandes         }
447*6fda04e7SJoel Fernandes 
448*6fda04e7SJoel Fernandes         let subimage_len = u16::from_le_bytes([data[8], data[9]]);
449*6fda04e7SJoel Fernandes         if subimage_len == 0 {
450*6fda04e7SJoel Fernandes             dev_dbg!(pdev.as_ref(), "Invalid subimage length: 0\n");
451*6fda04e7SJoel Fernandes             return None;
452*6fda04e7SJoel Fernandes         }
453*6fda04e7SJoel Fernandes 
454*6fda04e7SJoel Fernandes         Some(NpdeStruct {
455*6fda04e7SJoel Fernandes             signature,
456*6fda04e7SJoel Fernandes             npci_data_ext_rev: u16::from_le_bytes([data[4], data[5]]),
457*6fda04e7SJoel Fernandes             npci_data_ext_len: u16::from_le_bytes([data[6], data[7]]),
458*6fda04e7SJoel Fernandes             subimage_len,
459*6fda04e7SJoel Fernandes             last_image: data[10],
460*6fda04e7SJoel Fernandes         })
461*6fda04e7SJoel Fernandes     }
462*6fda04e7SJoel Fernandes 
463*6fda04e7SJoel Fernandes     /// Check if this is the last image in the ROM.
464*6fda04e7SJoel Fernandes     fn is_last(&self) -> bool {
465*6fda04e7SJoel Fernandes         self.last_image & LAST_IMAGE_BIT_MASK != 0
466*6fda04e7SJoel Fernandes     }
467*6fda04e7SJoel Fernandes 
468*6fda04e7SJoel Fernandes     /// Calculate image size in bytes from 512-byte blocks.
469*6fda04e7SJoel Fernandes     fn image_size_bytes(&self) -> usize {
470*6fda04e7SJoel Fernandes         self.subimage_len as usize * 512
471*6fda04e7SJoel Fernandes     }
472*6fda04e7SJoel Fernandes 
473*6fda04e7SJoel Fernandes     /// Try to find NPDE in the data, the NPDE is right after the PCIR.
474*6fda04e7SJoel Fernandes     fn find_in_data(
475*6fda04e7SJoel Fernandes         pdev: &pci::Device,
476*6fda04e7SJoel Fernandes         data: &[u8],
477*6fda04e7SJoel Fernandes         rom_header: &PciRomHeader,
478*6fda04e7SJoel Fernandes         pcir: &PcirStruct,
479*6fda04e7SJoel Fernandes     ) -> Option<Self> {
480*6fda04e7SJoel Fernandes         // Calculate the offset where NPDE might be located
481*6fda04e7SJoel Fernandes         // NPDE should be right after the PCIR structure, aligned to 16 bytes
482*6fda04e7SJoel Fernandes         let pcir_offset = rom_header.pci_data_struct_offset as usize;
483*6fda04e7SJoel Fernandes         let npde_start = (pcir_offset + pcir.pci_data_struct_len as usize + 0x0F) & !0x0F;
484*6fda04e7SJoel Fernandes 
485*6fda04e7SJoel Fernandes         // Check if we have enough data
486*6fda04e7SJoel Fernandes         if npde_start + core::mem::size_of::<Self>() > data.len() {
487*6fda04e7SJoel Fernandes             dev_dbg!(pdev.as_ref(), "Not enough data for NPDE\n");
488*6fda04e7SJoel Fernandes             return None;
489*6fda04e7SJoel Fernandes         }
490*6fda04e7SJoel Fernandes 
491*6fda04e7SJoel Fernandes         // Try to create NPDE from the data
492*6fda04e7SJoel Fernandes         NpdeStruct::new(pdev, &data[npde_start..])
493*6fda04e7SJoel Fernandes     }
494*6fda04e7SJoel Fernandes }
495*6fda04e7SJoel Fernandes 
496*6fda04e7SJoel Fernandes // Use a macro to implement BiosImage enum and methods. This avoids having to
497*6fda04e7SJoel Fernandes // repeat each enum type when implementing functions like base() in BiosImage.
498*6fda04e7SJoel Fernandes macro_rules! bios_image {
499*6fda04e7SJoel Fernandes     (
500*6fda04e7SJoel Fernandes         $($variant:ident: $class:ident),* $(,)?
501*6fda04e7SJoel Fernandes     ) => {
502*6fda04e7SJoel Fernandes         // BiosImage enum with variants for each image type
503*6fda04e7SJoel Fernandes         enum BiosImage {
504*6fda04e7SJoel Fernandes             $($variant($class)),*
505*6fda04e7SJoel Fernandes         }
506*6fda04e7SJoel Fernandes 
507*6fda04e7SJoel Fernandes         impl BiosImage {
508*6fda04e7SJoel Fernandes             /// Get a reference to the common BIOS image data regardless of type
509*6fda04e7SJoel Fernandes             fn base(&self) -> &BiosImageBase {
510*6fda04e7SJoel Fernandes                 match self {
511*6fda04e7SJoel Fernandes                     $(Self::$variant(img) => &img.base),*
512*6fda04e7SJoel Fernandes                 }
513*6fda04e7SJoel Fernandes             }
514*6fda04e7SJoel Fernandes 
515*6fda04e7SJoel Fernandes             /// Returns a string representing the type of BIOS image
516*6fda04e7SJoel Fernandes             fn image_type_str(&self) -> &'static str {
517*6fda04e7SJoel Fernandes                 match self {
518*6fda04e7SJoel Fernandes                     $(Self::$variant(_) => stringify!($variant)),*
519*6fda04e7SJoel Fernandes                 }
520*6fda04e7SJoel Fernandes             }
521*6fda04e7SJoel Fernandes         }
522*6fda04e7SJoel Fernandes     }
523*6fda04e7SJoel Fernandes }
524*6fda04e7SJoel Fernandes 
525*6fda04e7SJoel Fernandes impl BiosImage {
526*6fda04e7SJoel Fernandes     /// Check if this is the last image.
527*6fda04e7SJoel Fernandes     fn is_last(&self) -> bool {
528*6fda04e7SJoel Fernandes         let base = self.base();
529*6fda04e7SJoel Fernandes 
530*6fda04e7SJoel Fernandes         // For NBSI images (type == 0x70), return true as they're
531*6fda04e7SJoel Fernandes         // considered the last image
532*6fda04e7SJoel Fernandes         if matches!(self, Self::Nbsi(_)) {
533*6fda04e7SJoel Fernandes             return true;
534*6fda04e7SJoel Fernandes         }
535*6fda04e7SJoel Fernandes 
536*6fda04e7SJoel Fernandes         // For other image types, check the NPDE first if available
537*6fda04e7SJoel Fernandes         if let Some(ref npde) = base.npde {
538*6fda04e7SJoel Fernandes             return npde.is_last();
539*6fda04e7SJoel Fernandes         }
540*6fda04e7SJoel Fernandes 
541*6fda04e7SJoel Fernandes         // Otherwise, fall back to checking the PCIR last_image flag
542*6fda04e7SJoel Fernandes         base.pcir.is_last()
543*6fda04e7SJoel Fernandes     }
544*6fda04e7SJoel Fernandes 
545*6fda04e7SJoel Fernandes     /// Get the image size in bytes.
546*6fda04e7SJoel Fernandes     fn image_size_bytes(&self) -> usize {
547*6fda04e7SJoel Fernandes         let base = self.base();
548*6fda04e7SJoel Fernandes 
549*6fda04e7SJoel Fernandes         // Prefer NPDE image size if available
550*6fda04e7SJoel Fernandes         if let Some(ref npde) = base.npde {
551*6fda04e7SJoel Fernandes             return npde.image_size_bytes();
552*6fda04e7SJoel Fernandes         }
553*6fda04e7SJoel Fernandes 
554*6fda04e7SJoel Fernandes         // Otherwise, fall back to the PCIR image size
555*6fda04e7SJoel Fernandes         base.pcir.image_size_bytes()
556*6fda04e7SJoel Fernandes     }
557*6fda04e7SJoel Fernandes 
558*6fda04e7SJoel Fernandes     /// Create a [`BiosImageBase`] from a byte slice and convert it to a [`BiosImage`] which
559*6fda04e7SJoel Fernandes     /// triggers the constructor of the specific BiosImage enum variant.
560*6fda04e7SJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
561*6fda04e7SJoel Fernandes         let base = BiosImageBase::new(pdev, data)?;
562*6fda04e7SJoel Fernandes         let image = base.into_image().inspect_err(|e| {
563*6fda04e7SJoel Fernandes             dev_err!(pdev.as_ref(), "Failed to create BiosImage: {:?}\n", e);
564*6fda04e7SJoel Fernandes         })?;
565*6fda04e7SJoel Fernandes 
566*6fda04e7SJoel Fernandes         Ok(image)
567*6fda04e7SJoel Fernandes     }
568*6fda04e7SJoel Fernandes }
569*6fda04e7SJoel Fernandes 
570*6fda04e7SJoel Fernandes bios_image! {
571*6fda04e7SJoel Fernandes     PciAt: PciAtBiosImage,   // PCI-AT compatible BIOS image
572*6fda04e7SJoel Fernandes     Efi: EfiBiosImage,       // EFI (Extensible Firmware Interface)
573*6fda04e7SJoel Fernandes     Nbsi: NbsiBiosImage,     // NBSI (Nvidia Bios System Interface)
574*6fda04e7SJoel Fernandes     FwSec: FwSecBiosImage,   // FWSEC (Firmware Security)
575*6fda04e7SJoel Fernandes }
576*6fda04e7SJoel Fernandes 
577*6fda04e7SJoel Fernandes struct PciAtBiosImage {
578*6fda04e7SJoel Fernandes     base: BiosImageBase,
579*6fda04e7SJoel Fernandes     // PCI-AT-specific fields can be added here in the future.
580*6fda04e7SJoel Fernandes }
581*6fda04e7SJoel Fernandes 
582*6fda04e7SJoel Fernandes struct EfiBiosImage {
583*6fda04e7SJoel Fernandes     base: BiosImageBase,
584*6fda04e7SJoel Fernandes     // EFI-specific fields can be added here in the future.
585*6fda04e7SJoel Fernandes }
586*6fda04e7SJoel Fernandes 
587*6fda04e7SJoel Fernandes struct NbsiBiosImage {
588*6fda04e7SJoel Fernandes     base: BiosImageBase,
589*6fda04e7SJoel Fernandes     // NBSI-specific fields can be added here in the future.
590*6fda04e7SJoel Fernandes }
591*6fda04e7SJoel Fernandes 
592*6fda04e7SJoel Fernandes struct FwSecBiosImage {
593*6fda04e7SJoel Fernandes     base: BiosImageBase,
594*6fda04e7SJoel Fernandes     // FWSEC-specific fields can be added here in the future.
595*6fda04e7SJoel Fernandes }
596*6fda04e7SJoel Fernandes 
597*6fda04e7SJoel Fernandes // Convert from BiosImageBase to BiosImage
598*6fda04e7SJoel Fernandes impl TryFrom<BiosImageBase> for BiosImage {
599*6fda04e7SJoel Fernandes     type Error = Error;
600*6fda04e7SJoel Fernandes 
601*6fda04e7SJoel Fernandes     fn try_from(base: BiosImageBase) -> Result<Self> {
602*6fda04e7SJoel Fernandes         match base.pcir.code_type {
603*6fda04e7SJoel Fernandes             0x00 => Ok(BiosImage::PciAt(PciAtBiosImage { base })),
604*6fda04e7SJoel Fernandes             0x03 => Ok(BiosImage::Efi(EfiBiosImage { base })),
605*6fda04e7SJoel Fernandes             0x70 => Ok(BiosImage::Nbsi(NbsiBiosImage { base })),
606*6fda04e7SJoel Fernandes             0xE0 => Ok(BiosImage::FwSec(FwSecBiosImage { base })),
607*6fda04e7SJoel Fernandes             _ => Err(EINVAL),
608*6fda04e7SJoel Fernandes         }
609*6fda04e7SJoel Fernandes     }
610*6fda04e7SJoel Fernandes }
611*6fda04e7SJoel Fernandes 
612*6fda04e7SJoel Fernandes /// BIOS Image structure containing various headers and reference fields to all BIOS images.
613*6fda04e7SJoel Fernandes ///
614*6fda04e7SJoel Fernandes /// Each BiosImage type has a BiosImageBase type along with other image-specific fields. Note that
615*6fda04e7SJoel Fernandes /// Rust favors composition of types over inheritance.
616*6fda04e7SJoel Fernandes #[derive(Debug)]
617*6fda04e7SJoel Fernandes #[expect(dead_code)]
618*6fda04e7SJoel Fernandes struct BiosImageBase {
619*6fda04e7SJoel Fernandes     /// PCI ROM Expansion Header
620*6fda04e7SJoel Fernandes     rom_header: PciRomHeader,
621*6fda04e7SJoel Fernandes     /// PCI Data Structure
622*6fda04e7SJoel Fernandes     pcir: PcirStruct,
623*6fda04e7SJoel Fernandes     /// NVIDIA PCI Data Extension (optional)
624*6fda04e7SJoel Fernandes     npde: Option<NpdeStruct>,
625*6fda04e7SJoel Fernandes     /// Image data (includes ROM header and PCIR)
626*6fda04e7SJoel Fernandes     data: KVec<u8>,
627*6fda04e7SJoel Fernandes }
628*6fda04e7SJoel Fernandes 
629*6fda04e7SJoel Fernandes impl BiosImageBase {
630*6fda04e7SJoel Fernandes     fn into_image(self) -> Result<BiosImage> {
631*6fda04e7SJoel Fernandes         BiosImage::try_from(self)
632*6fda04e7SJoel Fernandes     }
633*6fda04e7SJoel Fernandes 
634*6fda04e7SJoel Fernandes     /// Creates a new BiosImageBase from raw byte data.
635*6fda04e7SJoel Fernandes     fn new(pdev: &pci::Device, data: &[u8]) -> Result<Self> {
636*6fda04e7SJoel Fernandes         // Ensure we have enough data for the ROM header.
637*6fda04e7SJoel Fernandes         if data.len() < 26 {
638*6fda04e7SJoel Fernandes             dev_err!(pdev.as_ref(), "Not enough data for ROM header\n");
639*6fda04e7SJoel Fernandes             return Err(EINVAL);
640*6fda04e7SJoel Fernandes         }
641*6fda04e7SJoel Fernandes 
642*6fda04e7SJoel Fernandes         // Parse the ROM header.
643*6fda04e7SJoel Fernandes         let rom_header = PciRomHeader::new(pdev, &data[0..26])
644*6fda04e7SJoel Fernandes             .inspect_err(|e| dev_err!(pdev.as_ref(), "Failed to create PciRomHeader: {:?}\n", e))?;
645*6fda04e7SJoel Fernandes 
646*6fda04e7SJoel Fernandes         // Get the PCI Data Structure using the pointer from the ROM header.
647*6fda04e7SJoel Fernandes         let pcir_offset = rom_header.pci_data_struct_offset as usize;
648*6fda04e7SJoel Fernandes         let pcir_data = data
649*6fda04e7SJoel Fernandes             .get(pcir_offset..pcir_offset + core::mem::size_of::<PcirStruct>())
650*6fda04e7SJoel Fernandes             .ok_or(EINVAL)
651*6fda04e7SJoel Fernandes             .inspect_err(|_| {
652*6fda04e7SJoel Fernandes                 dev_err!(
653*6fda04e7SJoel Fernandes                     pdev.as_ref(),
654*6fda04e7SJoel Fernandes                     "PCIR offset {:#x} out of bounds (data length: {})\n",
655*6fda04e7SJoel Fernandes                     pcir_offset,
656*6fda04e7SJoel Fernandes                     data.len()
657*6fda04e7SJoel Fernandes                 );
658*6fda04e7SJoel Fernandes                 dev_err!(
659*6fda04e7SJoel Fernandes                     pdev.as_ref(),
660*6fda04e7SJoel Fernandes                     "Consider reading more data for construction of BiosImage\n"
661*6fda04e7SJoel Fernandes                 );
662*6fda04e7SJoel Fernandes             })?;
663*6fda04e7SJoel Fernandes 
664*6fda04e7SJoel Fernandes         let pcir = PcirStruct::new(pdev, pcir_data)
665*6fda04e7SJoel Fernandes             .inspect_err(|e| dev_err!(pdev.as_ref(), "Failed to create PcirStruct: {:?}\n", e))?;
666*6fda04e7SJoel Fernandes 
667*6fda04e7SJoel Fernandes         // Look for NPDE structure if this is not an NBSI image (type != 0x70).
668*6fda04e7SJoel Fernandes         let npde = NpdeStruct::find_in_data(pdev, data, &rom_header, &pcir);
669*6fda04e7SJoel Fernandes 
670*6fda04e7SJoel Fernandes         // Create a copy of the data.
671*6fda04e7SJoel Fernandes         let mut data_copy = KVec::new();
672*6fda04e7SJoel Fernandes         data_copy.extend_from_slice(data, GFP_KERNEL)?;
673*6fda04e7SJoel Fernandes 
674*6fda04e7SJoel Fernandes         Ok(BiosImageBase {
675*6fda04e7SJoel Fernandes             rom_header,
676*6fda04e7SJoel Fernandes             pcir,
677*6fda04e7SJoel Fernandes             npde,
678*6fda04e7SJoel Fernandes             data: data_copy,
679*6fda04e7SJoel Fernandes         })
680*6fda04e7SJoel Fernandes     }
681*6fda04e7SJoel Fernandes }
682