xref: /linux/drivers/gpu/drm/tyr/fw/parser.rs (revision 570f7e331f5febb30f1384817463c7e42b65ca7d)
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