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