| 1 | |
| 2 |
|
| 3 | |
| 4 |
|
| 5 | use 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 |
|
| 18 | use crate::{
|
| 19 | driver::Bar0,
|
| 20 | firmware::{
|
| 21 | fwsec::Bcrt30Rsa3kSignature,
|
| 22 | FalconUCodeDesc,
|
| 23 | FalconUCodeDescV2,
|
| 24 | FalconUCodeDescV3, |
| 25 | },
|
| 26 | num::FromSafeCast,
|
| 27 | };
|
| 28 |
|
| 29 | |
| 30 | #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
| 31 | #[repr(u8)]
|
| 32 | enum BiosImageType {
|
| 33 | |
| 34 | PciAt = 0x00,
|
| 35 | |
| 36 | Efi = 0x03,
|
| 37 | |
| 38 | Nbsi = 0x70,
|
| 39 | |
| 40 | FwSec = 0xE0,
|
| 41 | }
|
| 42 |
|
| 43 | impl 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 | |
| 58 | struct VbiosIterator<'a> {
|
| 59 | dev: &'a device::Device,
|
| 60 | bar0: Bar0<'a>,
|
| 61 | |
| 62 | |
| 63 | |
| 64 | |
| 65 | |
| 66 | data: KVVec<u8>,
|
| 67 | |
| 68 | current_offset: usize,
|
| 69 | |
| 70 | last_found: bool,
|
| 71 | }
|
| 72 |
|
| 73 | impl<'a> VbiosIterator<'a> {
|
| 74 | |
| 75 | const ROM_OFFSET: usize = 0x300000;
|
| 76 | |
| 77 | const BIOS_MAX_SCAN_LEN: usize = 0x100000;
|
| 78 | |
| 79 | const BIOS_READ_AHEAD_SIZE: usize = 1024;
|
| 80 |
|
| 81 | |
| 82 | |
| 83 | |
| 84 | |
| 85 | |
| 86 | |
| 87 | |
| 88 | |
| 89 | |
| 90 | |
| 91 | |
| 92 | fn rom_offset(dev: &device::Device, bar0: Bar0<'_>) -> Result<usize> {
|
| 93 | |
| 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 | |
| 114 | const NV_PBUS_IFR_FMT_FIXED0_SIGNATURE_VALUE: u32 = u32::from_le_bytes(*b"NVGI");
|
| 115 | |
| 116 | const NV_ROM_DIRECTORY_IDENTIFIER: u32 = u32::from_le_bytes(*b"RFRD");
|
| 117 | |
| 118 | const IFR_SW_EXT_ROM_ADDR_OFFSET: usize = 4;
|
| 119 | |
| 120 | const RFW_FLASH_STATUS_SIZE: usize = SZ_4K;
|
| 121 | |
| 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 | |
| 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 | |
| 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 | |
| 192 | for addr in (start..end).step_by(core::mem::size_of::<u32>()) {
|
| 193 | |
| 194 | let word = self.bar0.try_read32(Self::ROM_OFFSET + addr)?;
|
| 195 |
|
| 196 | |
| 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 | |
| 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 | |
| 213 | |
| 214 | |
| 215 | |
| 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 |
|
| 246 | impl<'a> Iterator for VbiosIterator<'a> {
|
| 247 | type Item = Result<BiosImage>;
|
| 248 |
|
| 249 | |
| 250 | |
| 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 | |
| 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 | |
| 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 | |
| 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 |
|
| 291 | pub(crate) struct Vbios {
|
| 292 | fwsec_image: FwSecBiosImage,
|
| 293 | }
|
| 294 |
|
| 295 | impl Vbios {
|
| 296 | |
| 297 | |
| 298 | |
| 299 | pub(crate) fn new(dev: &device::Device, bar0: Bar0<'_>) -> Result<Vbios> {
|
| 300 | |
| 301 | let mut pci_at_image: Option<PciAtBiosImage> = None;
|
| 302 | let mut fwsec_section: Option<KVVec<u8>> = None;
|
| 303 |
|
| 304 | |
| 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 | |
| 317 | |
| 318 | if let Some(data) = fwsec_section.as_mut() {
|
| 319 | data.extend_from_slice(&image.data, GFP_KERNEL)?;
|
| 320 | continue;
|
| 321 | }
|
| 322 |
|
| 323 | |
| 324 | match BiosImageType::try_from(image.pcir.code_type) {
|
| 325 | Ok(BiosImageType::PciAt) => {
|
| 326 | |
| 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 | |
| 334 | }
|
| 335 | }
|
| 336 | }
|
| 337 |
|
| 338 | |
| 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 | |
| 359 | #[derive(Debug, Clone, FromBytes)]
|
| 360 | #[repr(C)]
|
| 361 | struct PcirStruct {
|
| 362 | |
| 363 | signature: [u8; 4],
|
| 364 | |
| 365 | vendor_id: u16,
|
| 366 | |
| 367 | device_id: u16,
|
| 368 | |
| 369 | device_list_ptr: u16,
|
| 370 | |
| 371 | pci_data_struct_len: u16,
|
| 372 | |
| 373 | pci_data_struct_rev: u8,
|
| 374 | |
| 375 | class_code: [u8; 3],
|
| 376 | |
| 377 | image_len: u16,
|
| 378 | |
| 379 | vendor_rom_rev: u16,
|
| 380 | |
| 381 | code_type: u8,
|
| 382 | |
| 383 | last_image: u8,
|
| 384 | |
| 385 | max_runtime_image_len: u16,
|
| 386 | }
|
| 387 |
|
| 388 | impl PcirStruct {
|
| 389 | |
| 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 | |
| 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 | |
| 414 | fn is_last(&self) -> bool {
|
| 415 | self.last_image & Self::LAST_IMAGE_BIT_MASK != 0
|
| 416 | }
|
| 417 |
|
| 418 | |
| 419 | fn image_size_bytes(&self) -> usize {
|
| 420 | usize::from(self.image_len) * 512
|
| 421 | }
|
| 422 | }
|
| 423 |
|
| 424 | |
| 425 | |
| 426 | |
| 427 | |
| 428 | |
| 429 | #[derive(Debug, Clone, Copy, FromBytes)]
|
| 430 | #[repr(C)]
|
| 431 | struct BitHeader {
|
| 432 | |
| 433 | id: u16,
|
| 434 | |
| 435 | signature: [u8; 4],
|
| 436 | |
| 437 | bcd_version: u16,
|
| 438 | |
| 439 | header_size: u8,
|
| 440 | |
| 441 | token_size: u8,
|
| 442 | |
| 443 | token_entries: u8,
|
| 444 | |
| 445 | checksum: u8,
|
| 446 | }
|
| 447 |
|
| 448 | impl BitHeader {
|
| 449 | fn new(data: &[u8]) -> Result<Self> {
|
| 450 | let (header, _) = BitHeader::read_from_prefix(data).map_err(|_| EINVAL)?;
|
| 451 |
|
| 452 | |
| 453 | if header.id != 0xB8FF || &header.signature != b"BIT\0" {
|
| 454 | return Err(EINVAL);
|
| 455 | }
|
| 456 |
|
| 457 | Ok(header)
|
| 458 | }
|
| 459 | }
|
| 460 |
|
| 461 | |
| 462 | #[derive(Debug, Clone, Copy, FromBytes)]
|
| 463 | #[repr(C)]
|
| 464 | struct BitToken {
|
| 465 | |
| 466 | id: u8,
|
| 467 | |
| 468 | data_version: u8,
|
| 469 | |
| 470 | data_size: u16,
|
| 471 | |
| 472 | data_offset: u16,
|
| 473 | }
|
| 474 |
|
| 475 | impl BitToken {
|
| 476 | |
| 477 | const ID_FALCON_DATA: u8 = 0x70;
|
| 478 |
|
| 479 | |
| 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 | |
| 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 | |
| 502 | if token.id == token_id {
|
| 503 | return Ok(token);
|
| 504 | }
|
| 505 | }
|
| 506 |
|
| 507 | |
| 508 | Err(ENOENT)
|
| 509 | }
|
| 510 | }
|
| 511 |
|
| 512 | |
| 513 | |
| 514 | |
| 515 | |
| 516 | #[derive(Debug, Clone, Copy, FromBytes)]
|
| 517 | #[repr(C)]
|
| 518 | struct PciRomHeader {
|
| 519 | |
| 520 | signature: u16,
|
| 521 | |
| 522 | reserved: [u8; 22],
|
| 523 | |
| 524 | pci_data_struct_offset: u16,
|
| 525 | }
|
| 526 |
|
| 527 | impl 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 | |
| 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 | |
| 547 | |
| 548 | |
| 549 | |
| 550 | |
| 551 | |
| 552 | #[derive(Debug, Clone, FromBytes)]
|
| 553 | #[repr(C)]
|
| 554 | struct NpdeStruct {
|
| 555 | |
| 556 | signature: [u8; 4],
|
| 557 | |
| 558 | npci_data_ext_rev: u16,
|
| 559 | |
| 560 | npci_data_ext_len: u16,
|
| 561 | |
| 562 | subimage_len: u16,
|
| 563 | |
| 564 | last_image: u8,
|
| 565 | }
|
| 566 |
|
| 567 | impl NpdeStruct {
|
| 568 | |
| 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 | |
| 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 | |
| 593 | fn is_last(&self) -> bool {
|
| 594 | self.last_image & Self::LAST_IMAGE_BIT_MASK != 0
|
| 595 | }
|
| 596 |
|
| 597 | |
| 598 | fn image_size_bytes(&self) -> usize {
|
| 599 | usize::from(self.subimage_len) * 512
|
| 600 | }
|
| 601 |
|
| 602 | |
| 603 | fn find_in_data(
|
| 604 | dev: &device::Device,
|
| 605 | data: &[u8],
|
| 606 | rom_header: &PciRomHeader,
|
| 607 | pcir: &PcirStruct,
|
| 608 | ) -> Option<Self> {
|
| 609 | |
| 610 | |
| 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 | |
| 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 | |
| 621 | NpdeStruct::new(dev, &data[npde_start..])
|
| 622 | }
|
| 623 | }
|
| 624 |
|
| 625 | |
| 626 | |
| 627 | |
| 628 | struct PciAtBiosImage {
|
| 629 | base: BiosImage,
|
| 630 | bit_header: BitHeader,
|
| 631 | bit_offset: usize,
|
| 632 | }
|
| 633 |
|
| 634 | |
| 635 | |
| 636 | |
| 637 | pub(crate) struct FwSecBiosImage {
|
| 638 | |
| 639 | dev: ARef<device::Device>,
|
| 640 | |
| 641 | data: KVVec<u8>,
|
| 642 | |
| 643 | falcon_ucode_offset: usize,
|
| 644 | }
|
| 645 |
|
| 646 | |
| 647 | |
| 648 | |
| 649 | struct BiosImage {
|
| 650 | |
| 651 | pcir: PcirStruct,
|
| 652 | |
| 653 | npde: Option<NpdeStruct>,
|
| 654 | |
| 655 | data: KVVec<u8>,
|
| 656 | }
|
| 657 |
|
| 658 | impl BiosImage {
|
| 659 | |
| 660 | fn image_size_bytes(&self) -> usize {
|
| 661 | |
| 662 | if let Some(ref npde) = self.npde {
|
| 663 | npde.image_size_bytes()
|
| 664 | } else {
|
| 665 | |
| 666 | self.pcir.image_size_bytes()
|
| 667 | }
|
| 668 | }
|
| 669 |
|
| 670 | |
| 671 | fn image_type(&self) -> Result<BiosImageType> {
|
| 672 | BiosImageType::try_from(self.pcir.code_type)
|
| 673 | }
|
| 674 |
|
| 675 | |
| 676 | fn is_last(&self) -> bool {
|
| 677 | |
| 678 | if self.image_type() == Ok(BiosImageType::Nbsi) {
|
| 679 | return true;
|
| 680 | }
|
| 681 |
|
| 682 | |
| 683 | if let Some(ref npde) = self.npde {
|
| 684 | return npde.is_last();
|
| 685 | }
|
| 686 |
|
| 687 | |
| 688 | self.pcir.is_last()
|
| 689 | }
|
| 690 |
|
| 691 | |
| 692 | fn new(dev: &device::Device, data: &[u8]) -> Result<Self> {
|
| 693 | |
| 694 | let rom_header = PciRomHeader::new(dev, data)?;
|
| 695 |
|
| 696 | |
| 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 | |
| 718 | let npde = NpdeStruct::find_in_data(dev, data, &rom_header, &pcir);
|
| 719 |
|
| 720 | |
| 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 |
|
| 732 | impl PciAtBiosImage {
|
| 733 | |
| 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 | |
| 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 | |
| 751 | fn get_bit_token(&self, token_id: u8) -> Result<BitToken> {
|
| 752 | BitToken::from_id(self, token_id)
|
| 753 | }
|
| 754 |
|
| 755 | |
| 756 | |
| 757 | |
| 758 | |
| 759 | |
| 760 | |
| 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 | |
| 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 |
|
| 781 | impl 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 | |
| 797 | |
| 798 | |
| 799 | #[derive(FromBytes)]
|
| 800 | #[repr(C, packed)]
|
| 801 | struct PmuLookupTableEntry {
|
| 802 | application_id: u8,
|
| 803 | target_id: u8,
|
| 804 | data: u32,
|
| 805 | }
|
| 806 |
|
| 807 | impl PmuLookupTableEntry {
|
| 808 | |
| 809 | #[expect(dead_code)]
|
| 810 | const APPID_FIRMWARE_SEC_LIC: u8 = 0x05;
|
| 811 | |
| 812 | #[expect(dead_code)]
|
| 813 | const APPID_FWSEC_DBG: u8 = 0x45;
|
| 814 | |
| 815 | const APPID_FWSEC_PROD: u8 = 0x85;
|
| 816 | }
|
| 817 |
|
| 818 | #[repr(C)]
|
| 819 | #[derive(FromBytes)]
|
| 820 | struct PmuLookupTableHeader {
|
| 821 | version: u8,
|
| 822 | header_len: u8,
|
| 823 | entry_len: u8,
|
| 824 | entry_count: u8,
|
| 825 | }
|
| 826 |
|
| 827 | |
| 828 | |
| 829 | |
| 830 | |
| 831 | |
| 832 | struct PmuLookupTable {
|
| 833 | entries: KVVec<PmuLookupTableEntry>,
|
| 834 | }
|
| 835 |
|
| 836 | impl 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 | |
| 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 |
|
| 870 | impl FwSecBiosImage {
|
| 871 | |
| 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 | |
| 903 | pub(crate) fn header(&self) -> Result<FalconUCodeDesc> {
|
| 904 | let data = self.data.get(self.falcon_ucode_offset..).ok_or(EINVAL)?;
|
| 905 |
|
| 906 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 967 | |
| 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 | }
|