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