1 // SPDX-License-Identifier: GPL-2.0 or MIT 2 3 //! Firmware binary parser for Mali CSF (Command Stream Frontend) GPU. 4 //! 5 //! This module implements a parser for the Mali GPU firmware binary format. The firmware 6 //! file contains a header followed by a sequence of entries, each describing how to load 7 //! firmware sections into the MCU (Microcontroller Unit) memory. The parser extracts section 8 //! metadata including: 9 //! - Virtual address ranges where sections should be mapped 10 //! - Data ranges (byte offsets) within the firmware binary 11 //! - Section flags (permissions, cache modes) 12 13 use core::{ 14 mem::size_of, 15 ops::Range, // 16 }; 17 18 use kernel::{ 19 bits::bit_u32, 20 device::Device, 21 prelude::*, 22 sizes::SZ_4K, // 23 }; 24 25 use crate::{ 26 fw::{ 27 CacheMode, 28 SectionFlags, 29 CSF_MCU_SHARED_REGION_START, // 30 }, 31 vm::{ 32 VmFlag, 33 VmMapFlags, // 34 }, // 35 }; 36 37 /// A parsed firmware section ready for loading into MCU memory. 38 /// 39 /// Represents a single firmware section extracted from the firmware binary, containing 40 /// all information needed to map the section's data into the MCU's virtual address space. 41 pub(super) struct ParsedSection { 42 /// Byte offset range within the firmware binary where this section's data resides. 43 pub(super) data_range: Range<u32>, 44 /// MCU virtual address range where this section should be mapped. 45 pub(super) va: Range<u32>, 46 /// Memory protection and caching flags for the mapping. 47 pub(super) vm_map_flags: VmMapFlags, 48 } 49 50 /// A bare-bones `std::io::Cursor<[u8]>` clone to keep track of the current position in the 51 /// firmware binary. 52 /// 53 /// Provides methods to sequentially read primitive types and byte arrays from the firmware 54 /// binary while maintaining the current read position. 55 struct Cursor<'a> { 56 dev: &'a Device, 57 data: &'a [u8], 58 pos: usize, 59 } 60 61 impl<'a> Cursor<'a> { 62 fn new(dev: &'a Device, data: &'a [u8]) -> Self { 63 Self { dev, data, pos: 0 } 64 } 65 66 fn len(&self) -> usize { 67 self.data.len() 68 } 69 70 fn pos(&self) -> usize { 71 self.pos 72 } 73 74 /// Returns a view into the cursor's data. 75 /// 76 /// This spawns a new cursor, leaving the current cursor unchanged. 77 fn view(&self, range: Range<usize>) -> Result<Cursor<'_>> { 78 if range.start < self.pos || range.end > self.data.len() { 79 dev_err!( 80 self.dev, 81 "Invalid cursor range {:?} for data of length {}", 82 range, 83 self.data.len() 84 ); 85 86 Err(EINVAL) 87 } else { 88 Ok(Self { 89 dev: self.dev, 90 data: &self.data[range], 91 pos: 0, 92 }) 93 } 94 } 95 96 /// Reads a slice of bytes from the current position and advances the cursor. 97 /// 98 /// Returns an error if the read would exceed the data bounds. 99 fn read(&mut self, nbytes: usize) -> Result<&[u8]> { 100 let start = self.pos; 101 let end = start + nbytes; 102 103 if end > self.data.len() { 104 dev_err!( 105 self.dev, 106 "Invalid firmware file: read of size {} at position {} is out of bounds", 107 nbytes, 108 start, 109 ); 110 return Err(EINVAL); 111 } 112 113 self.pos += nbytes; 114 Ok(&self.data[start..end]) 115 } 116 117 /// Reads a little-endian `u8` from the current position and advances the cursor. 118 fn read_u8(&mut self) -> Result<u8> { 119 let bytes = self.read(size_of::<u8>())?; 120 Ok(bytes[0]) 121 } 122 123 /// Reads a little-endian `u16` from the current position and advances the cursor. 124 fn read_u16(&mut self) -> Result<u16> { 125 let bytes: [u8; 2] = self 126 .read(size_of::<u16>())? 127 .try_into() 128 .map_err(|_| EINVAL)?; 129 130 Ok(u16::from_le_bytes(bytes)) 131 } 132 133 /// Reads a little-endian `u32` from the current position and advances the cursor. 134 fn read_u32(&mut self) -> Result<u32> { 135 let bytes: [u8; 4] = self 136 .read(size_of::<u32>())? 137 .try_into() 138 .map_err(|_| EINVAL)?; 139 140 Ok(u32::from_le_bytes(bytes)) 141 } 142 143 /// Advances the cursor position by the specified number of bytes. 144 /// 145 /// Returns an error if the advance would exceed the data bounds. 146 fn advance(&mut self, nbytes: usize) -> Result { 147 if self.pos + nbytes > self.data.len() { 148 dev_err!( 149 self.dev, 150 "Invalid firmware file: advance of size {} at position {} is out of bounds", 151 nbytes, 152 self.pos, 153 ); 154 return Err(EINVAL); 155 } 156 self.pos += nbytes; 157 Ok(()) 158 } 159 } 160 161 /// Parser for Mali CSF GPU firmware binaries. 162 /// 163 /// Parses the firmware binary format, extracting section metadata including virtual 164 /// address ranges, data offsets, and memory protection flags needed to load firmware 165 /// into the MCU's memory. 166 pub(super) struct FwParser<'a> { 167 cursor: Cursor<'a>, 168 } 169 170 impl<'a> FwParser<'a> { 171 /// Creates a new firmware parser for the given firmware binary data. 172 pub(super) fn new(dev: &'a Device, data: &'a [u8]) -> Self { 173 Self { 174 cursor: Cursor::new(dev, data), 175 } 176 } 177 178 /// Parses the firmware binary and returns a collection of parsed sections. 179 /// 180 /// This method validates the firmware header and iterates through all entries 181 /// in the binary, extracting section information needed for loading. 182 pub(super) fn parse(&mut self) -> Result<KVec<ParsedSection>> { 183 let fw_header = self.parse_fw_header()?; 184 let header_end = fw_header.size as usize; 185 186 let mut parsed_sections = KVec::new(); 187 while self.cursor.pos() < header_end { 188 let entry_section = self.parse_entry(header_end)?; 189 190 if let Some(inner) = entry_section.inner { 191 parsed_sections.push(inner, GFP_KERNEL)?; 192 } 193 } 194 195 if parsed_sections.is_empty() { 196 dev_err!(self.cursor.dev, "Firmware contains no loadable sections"); 197 return Err(EINVAL); 198 } 199 200 Ok(parsed_sections) 201 } 202 203 fn parse_fw_header(&mut self) -> Result<FirmwareHeader> { 204 let fw_header: FirmwareHeader = match FirmwareHeader::new(&mut self.cursor) { 205 Ok(fw_header) => fw_header, 206 Err(e) => { 207 dev_err!(self.cursor.dev, "Invalid firmware file: {}", e.to_errno()); 208 return Err(e); 209 } 210 }; 211 212 if fw_header.size as usize > self.cursor.len() { 213 dev_err!(self.cursor.dev, "Firmware image is truncated"); 214 return Err(EINVAL); 215 } 216 Ok(fw_header) 217 } 218 219 fn parse_entry(&mut self, header_end: usize) -> Result<EntrySection> { 220 let entry_start = self.cursor.pos(); 221 222 let entry_header_end = entry_start 223 .checked_add(size_of::<EntryHeader>()) 224 .ok_or(EINVAL)?; 225 226 if entry_header_end > header_end { 227 dev_err!( 228 self.cursor.dev, 229 "Firmware entry header at {:#x} exceeds header region ending at {:#x}", 230 entry_start, 231 header_end 232 ); 233 return Err(EINVAL); 234 } 235 236 let entry_section = EntrySection { 237 entry_hdr: EntryHeader(self.cursor.read_u32()?), 238 inner: None, 239 }; 240 241 let firmware_size = self.cursor.len(); 242 let entry_size = entry_section.entry_hdr.size() as usize; 243 244 if self.cursor.pos() % size_of::<u32>() != 0 245 || entry_size % size_of::<u32>() != 0 246 || entry_size < size_of::<EntryHeader>() 247 { 248 dev_err!( 249 self.cursor.dev, 250 "Firmware entry isn't 32 bit aligned, offset={:#x} size={:#x}", 251 self.cursor.pos() - size_of::<u32>(), 252 entry_size 253 ); 254 return Err(EINVAL); 255 } 256 257 let entry_end = entry_start.checked_add(entry_size).ok_or(EINVAL)?; 258 259 if entry_end > header_end { 260 dev_err!( 261 self.cursor.dev, 262 "Firmware entry at {:#x} extends beyond header region ending at {:#x}", 263 entry_start, 264 header_end 265 ); 266 return Err(EINVAL); 267 } 268 269 let section_hdr_size = entry_size - size_of::<EntryHeader>(); 270 271 let entry_section = { 272 let mut entry_cursor = self.cursor.view(self.cursor.pos()..entry_end)?; 273 274 match entry_section.entry_hdr.entry_type() { 275 Ok(EntryType::Iface) => Ok(EntrySection { 276 entry_hdr: entry_section.entry_hdr, 277 inner: Self::parse_section_entry(&mut entry_cursor, firmware_size)?, 278 }), 279 Ok( 280 EntryType::Config 281 | EntryType::FutfTest 282 | EntryType::TraceBuffer 283 | EntryType::TimelineMetadata 284 | EntryType::BuildInfoMetadata, 285 ) => Ok(entry_section), 286 287 Err(_) => { 288 if entry_section.entry_hdr.optional() { 289 Ok(entry_section) 290 } else { 291 dev_err!( 292 self.cursor.dev, 293 "Failed to handle firmware entry type: {}", 294 entry_section.entry_hdr.entry_type_raw() 295 ); 296 Err(EINVAL) 297 } 298 } 299 } 300 }; 301 302 if entry_section.is_ok() { 303 self.cursor.advance(section_hdr_size)?; 304 } 305 306 entry_section 307 } 308 309 fn parse_section_entry( 310 entry_cursor: &mut Cursor<'_>, 311 firmware_size: usize, 312 ) -> Result<Option<ParsedSection>> { 313 let section_hdr: SectionHeader = SectionHeader::new(entry_cursor)?; 314 315 if section_hdr.data.end < section_hdr.data.start { 316 dev_err!( 317 entry_cursor.dev, 318 "Firmware corrupted, data.end < data.start (0x{:x} < 0x{:x})", 319 section_hdr.data.end, 320 section_hdr.data.start 321 ); 322 return Err(EINVAL); 323 } 324 325 if section_hdr.data.end as usize > firmware_size { 326 dev_err!( 327 entry_cursor.dev, 328 "Firmware data range {:#x}..{:#x} exceeds firmware size {:#x}", 329 section_hdr.data.start, 330 section_hdr.data.end, 331 firmware_size, 332 ); 333 return Err(EINVAL); 334 } 335 336 if section_hdr.va.start as usize % SZ_4K != 0 || section_hdr.va.end as usize % SZ_4K != 0 { 337 dev_err!( 338 entry_cursor.dev, 339 "Firmware virtual address range {:#x}..{:#x} is not page aligned", 340 section_hdr.va.start, 341 section_hdr.va.end 342 ); 343 return Err(EINVAL); 344 } 345 346 if section_hdr.section_flags.prot() { 347 dev_dbg!( 348 entry_cursor.dev, 349 "Firmware protected mode entry not supported, ignoring" 350 ); 351 return Ok(None); 352 } 353 354 if section_hdr.va.start == CSF_MCU_SHARED_REGION_START 355 && !section_hdr.section_flags.shared() 356 { 357 dev_err!( 358 entry_cursor.dev, 359 "Interface at 0x{:x} must be shared", 360 CSF_MCU_SHARED_REGION_START 361 ); 362 return Err(EINVAL); 363 } 364 365 if section_hdr.va.is_empty() { 366 return Ok(None); 367 } 368 369 let mut vm_map_flags = VmMapFlags::empty(); 370 371 if !section_hdr.section_flags.write() { 372 vm_map_flags |= VmFlag::Readonly; 373 } 374 375 if !section_hdr.section_flags.exec() { 376 vm_map_flags |= VmFlag::Noexec; 377 } 378 379 // TODO: As in Panthor, map coherent firmware sections uncached until the VM 380 // supports a coherent mapping attribute. 381 if section_hdr.section_flags.cache_mode() != CacheMode::Cached { 382 vm_map_flags |= VmFlag::Uncached; 383 } 384 385 Ok(Some(ParsedSection { 386 data_range: section_hdr.data.clone(), 387 va: section_hdr.va, 388 vm_map_flags, 389 })) 390 } 391 } 392 393 /// Firmware binary header containing version and size information. 394 /// 395 /// The header is located at the beginning of the firmware binary and contains 396 /// a magic value for validation, version information, and the total size of 397 /// all structured headers that follow. 398 #[expect(dead_code)] 399 struct FirmwareHeader { 400 /// Magic value to check binary validity. 401 magic: u32, 402 403 /// Minor firmware version. 404 minor: u8, 405 406 /// Major firmware version. 407 major: u8, 408 409 /// Padding. Must be set to zero. 410 _padding1: u16, 411 412 /// Firmware version hash. 413 version_hash: u32, 414 415 /// Padding. Must be set to zero. 416 _padding2: u32, 417 418 /// Total size of all the structured data headers at beginning of firmware binary. 419 size: u32, 420 } 421 422 impl FirmwareHeader { 423 const FW_BINARY_MAGIC: u32 = 0xc3f13a6e; 424 const FW_BINARY_MAJOR_MAX: u8 = 0; 425 426 /// Reads and validates a firmware header from the cursor. 427 /// 428 /// Verifies the magic value, version compatibility, and padding fields. 429 fn new(cursor: &mut Cursor<'_>) -> Result<Self> { 430 let magic = cursor.read_u32()?; 431 if magic != Self::FW_BINARY_MAGIC { 432 dev_err!(cursor.dev, "Invalid firmware magic"); 433 return Err(EINVAL); 434 } 435 436 let minor = cursor.read_u8()?; 437 let major = cursor.read_u8()?; 438 439 if major > Self::FW_BINARY_MAJOR_MAX { 440 dev_err!( 441 cursor.dev, 442 "Unsupported firmware binary header version {}.{} (expected {}.x)", 443 major, 444 minor, 445 Self::FW_BINARY_MAJOR_MAX 446 ); 447 return Err(EINVAL); 448 } 449 450 let padding1 = cursor.read_u16()?; 451 let version_hash = cursor.read_u32()?; 452 let padding2 = cursor.read_u32()?; 453 let size = cursor.read_u32()?; 454 455 if padding1 != 0 || padding2 != 0 { 456 dev_err!( 457 cursor.dev, 458 "Invalid firmware file: header padding is not zero" 459 ); 460 return Err(EINVAL); 461 } 462 463 let fw_header = Self { 464 magic, 465 minor, 466 major, 467 _padding1: padding1, 468 version_hash, 469 _padding2: padding2, 470 size, 471 }; 472 473 Ok(fw_header) 474 } 475 } 476 477 /// Firmware section header for loading binary sections into MCU memory. 478 #[derive(Debug)] 479 struct SectionHeader { 480 section_flags: SectionFlags, 481 /// MCU virtual range to map this binary section to. 482 va: Range<u32>, 483 /// References the data in the FW binary. 484 data: Range<u32>, 485 } 486 487 impl SectionHeader { 488 /// Reads and validates a section header from the cursor. 489 /// 490 /// Parses section flags, virtual address range, and data range from the firmware binary. 491 fn new(cursor: &mut Cursor<'_>) -> Result<Self> { 492 let section_flags = SectionFlags::try_from_fw(cursor.read_u32()?)?; 493 494 let va_start = cursor.read_u32()?; 495 let va_end = cursor.read_u32()?; 496 497 let va = va_start..va_end; 498 499 if va.end < va.start { 500 dev_err!( 501 cursor.dev, 502 "Invalid firmware file: VA end precedes start at pos {}", 503 cursor.pos(), 504 ); 505 return Err(EINVAL); 506 } 507 508 let data_start = cursor.read_u32()?; 509 let data_end = cursor.read_u32()?; 510 let data = data_start..data_end; 511 512 Ok(Self { 513 section_flags, 514 va, 515 data, 516 }) 517 } 518 } 519 520 /// A firmware entry containing a header and optional parsed section data. 521 /// 522 /// Represents a single entry in the firmware binary, which may contain loadable 523 /// section data or metadata that doesn't require loading. 524 struct EntrySection { 525 entry_hdr: EntryHeader, 526 inner: Option<ParsedSection>, 527 } 528 529 /// Header for a firmware entry, packed into a single u32. 530 /// 531 /// The entry header encodes the entry type, size, and optional flag in a 532 /// 32-bit value with the following layout: 533 /// - Bits 0-7: Entry type 534 /// - Bits 8-15: Size in bytes 535 /// - Bit 31: Optional flag 536 struct EntryHeader(u32); 537 538 impl EntryHeader { 539 fn entry_type_raw(&self) -> u8 { 540 (self.0 & 0xff) as u8 541 } 542 543 fn entry_type(&self) -> Result<EntryType> { 544 let v = self.entry_type_raw(); 545 EntryType::try_from(v) 546 } 547 548 fn optional(&self) -> bool { 549 self.0 & bit_u32(31) != 0 550 } 551 552 fn size(&self) -> u32 { 553 self.0 >> 8 & 0xff 554 } 555 } 556 557 #[derive(Clone, Copy, Debug)] 558 #[repr(u8)] 559 enum EntryType { 560 /// Host <-> FW interface. 561 Iface = 0, 562 /// FW config. 563 Config = 1, 564 /// Unit tests. 565 FutfTest = 2, 566 /// Trace buffer interface. 567 TraceBuffer = 3, 568 /// Timeline metadata interface. 569 TimelineMetadata = 4, 570 /// Metadata about how the FW binary was built. 571 BuildInfoMetadata = 6, 572 } 573 574 impl TryFrom<u8> for EntryType { 575 type Error = Error; 576 577 fn try_from(value: u8) -> Result<Self, Self::Error> { 578 match value { 579 0 => Ok(EntryType::Iface), 580 1 => Ok(EntryType::Config), 581 2 => Ok(EntryType::FutfTest), 582 3 => Ok(EntryType::TraceBuffer), 583 4 => Ok(EntryType::TimelineMetadata), 584 6 => Ok(EntryType::BuildInfoMetadata), 585 _ => Err(EINVAL), 586 } 587 } 588 } 589