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