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