xref: /linux/drivers/gpu/nova-core/firmware/riscv.rs (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! Support for firmware binaries designed to run on a RISC-V core. Such firmwares files have a
4 //! dedicated header.
5 
6 use core::mem::size_of;
7 
8 use kernel::device;
9 use kernel::firmware::Firmware;
10 use kernel::prelude::*;
11 use kernel::transmute::FromBytes;
12 
13 use crate::dma::DmaObject;
14 use crate::firmware::BinFirmware;
15 
16 /// Descriptor for microcode running on a RISC-V core.
17 #[repr(C)]
18 #[derive(Debug)]
19 struct RmRiscvUCodeDesc {
20     version: u32,
21     bootloader_offset: u32,
22     bootloader_size: u32,
23     bootloader_param_offset: u32,
24     bootloader_param_size: u32,
25     riscv_elf_offset: u32,
26     riscv_elf_size: u32,
27     app_version: u32,
28     manifest_offset: u32,
29     manifest_size: u32,
30     monitor_data_offset: u32,
31     monitor_data_size: u32,
32     monitor_code_offset: u32,
33     monitor_code_size: u32,
34 }
35 
36 // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability.
37 unsafe impl FromBytes for RmRiscvUCodeDesc {}
38 
39 impl RmRiscvUCodeDesc {
40     /// Interprets the header of `bin_fw` as a [`RmRiscvUCodeDesc`] and returns it.
41     ///
42     /// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image.
43     fn new(bin_fw: &BinFirmware<'_>) -> Result<Self> {
44         let offset = bin_fw.hdr.header_offset as usize;
45 
46         bin_fw
47             .fw
48             .get(offset..offset + size_of::<Self>())
49             .and_then(Self::from_bytes_copy)
50             .ok_or(EINVAL)
51     }
52 }
53 
54 /// A parsed firmware for a RISC-V core, ready to be loaded and run.
55 #[expect(unused)]
56 pub(crate) struct RiscvFirmware {
57     /// Offset at which the code starts in the firmware image.
58     code_offset: u32,
59     /// Offset at which the data starts in the firmware image.
60     data_offset: u32,
61     /// Offset at which the manifest starts in the firmware image.
62     manifest_offset: u32,
63     /// Application version.
64     app_version: u32,
65     /// Device-mapped firmware image.
66     ucode: DmaObject,
67 }
68 
69 impl RiscvFirmware {
70     /// Parses the RISC-V firmware image contained in `fw`.
71     pub(crate) fn new(dev: &device::Device<device::Bound>, fw: &Firmware) -> Result<Self> {
72         let bin_fw = BinFirmware::new(fw)?;
73 
74         let riscv_desc = RmRiscvUCodeDesc::new(&bin_fw)?;
75 
76         let ucode = {
77             let start = bin_fw.hdr.data_offset as usize;
78             let len = bin_fw.hdr.data_size as usize;
79 
80             DmaObject::from_data(dev, fw.data().get(start..start + len).ok_or(EINVAL)?)?
81         };
82 
83         Ok(Self {
84             ucode,
85             code_offset: riscv_desc.monitor_code_offset,
86             data_offset: riscv_desc.monitor_data_offset,
87             manifest_offset: riscv_desc.manifest_offset,
88             app_version: riscv_desc.app_version,
89         })
90     }
91 }
92