// SPDX-License-Identifier: GPL-2.0 //! Support for firmware binaries designed to run on a RISC-V core. Such firmwares files have a //! dedicated header. use core::mem::size_of; use kernel::{ device, firmware::Firmware, prelude::*, transmute::FromBytes, // }; use crate::{ dma::DmaObject, firmware::BinFirmware, num::FromSafeCast, // }; /// Descriptor for microcode running on a RISC-V core. #[repr(C)] #[derive(Debug)] struct RmRiscvUCodeDesc { version: u32, bootloader_offset: u32, bootloader_size: u32, bootloader_param_offset: u32, bootloader_param_size: u32, riscv_elf_offset: u32, riscv_elf_size: u32, app_version: u32, manifest_offset: u32, manifest_size: u32, monitor_data_offset: u32, monitor_data_size: u32, monitor_code_offset: u32, monitor_code_size: u32, } // SAFETY: all bit patterns are valid for this type, and it doesn't use interior mutability. unsafe impl FromBytes for RmRiscvUCodeDesc {} impl RmRiscvUCodeDesc { /// Interprets the header of `bin_fw` as a [`RmRiscvUCodeDesc`] and returns it. /// /// Fails if the header pointed at by `bin_fw` is not within the bounds of the firmware image. fn new(bin_fw: &BinFirmware<'_>) -> Result { let offset = usize::from_safe_cast(bin_fw.hdr.header_offset); bin_fw .fw .get(offset..offset + size_of::()) .and_then(Self::from_bytes_copy) .ok_or(EINVAL) } } /// A parsed firmware for a RISC-V core, ready to be loaded and run. pub(crate) struct RiscvFirmware { /// Offset at which the code starts in the firmware image. pub(crate) code_offset: u32, /// Offset at which the data starts in the firmware image. pub(crate) data_offset: u32, /// Offset at which the manifest starts in the firmware image. pub(crate) manifest_offset: u32, /// Application version. pub(crate) app_version: u32, /// Device-mapped firmware image. pub(crate) ucode: DmaObject, } impl RiscvFirmware { /// Parses the RISC-V firmware image contained in `fw`. pub(crate) fn new(dev: &device::Device, fw: &Firmware) -> Result { let bin_fw = BinFirmware::new(fw)?; let riscv_desc = RmRiscvUCodeDesc::new(&bin_fw)?; let ucode = { let start = usize::from_safe_cast(bin_fw.hdr.data_offset); let len = usize::from_safe_cast(bin_fw.hdr.data_size); DmaObject::from_data(dev, fw.data().get(start..start + len).ok_or(EINVAL)?)? }; Ok(Self { ucode, code_offset: riscv_desc.monitor_code_offset, data_offset: riscv_desc.monitor_data_offset, manifest_offset: riscv_desc.manifest_offset, app_version: riscv_desc.app_version, }) } }