blob: 296bbbe8b83c8f222c5dc8ae030cf9289afae77a [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! Flash IPC server implementation.
#![no_std]
use hal_flash::{Flash, FlashAddress};
use services_flash_opcode::*;
use util_error::{self as error, ErrorCode};
use util_ipc::{IpcChannel, IpcHandle};
use util_types::{Opcode, PowerOf2Usize};
use zerocopy::{FromBytes, IntoBytes};
/// A flash server that handles flash IPC requests.
///
/// This struct wraps an object implementing the `Flash` trait and provides
/// an IPC interface to it.
pub struct FlashIpcServer<TFlash: Flash> {
flash: TFlash,
}
impl<TFlash: Flash<Error = ErrorCode>> FlashIpcServer<TFlash> {
/// Creates a new `FlashIpcServer` wrapping the given flash implementation.
pub fn new(flash: TFlash) -> Self {
Self { flash }
}
/// Handles the `IPC_OP_FLASH_GET_INFO` request.
///
/// Writes the flash geometry into the provided buffer and returns it.
fn handle_geometry<'a>(
&mut self,
data: &'a mut [u8],
reqsz: usize,
) -> Result<&'a [u8], ErrorCode> {
if reqsz != 0 {
return Err(error::IPC_ERROR_BAD_REQ_LEN);
}
let (info, _rest) =
FlashInfo::mut_from_prefix(data).map_err(|_| error::IPC_ERROR_BAD_REQ_LEN)?;
let (total_size, page_size, erasable_sizes_bitmap) = self.flash.geometry()?;
info.page_size = page_size.get() as u32;
info.total_size = total_size.get() as u32;
info.erasable_sizes_bitmap = erasable_sizes_bitmap;
Ok(info.as_bytes())
}
/// Handles the `IPC_OP_FLASH_ERASE` request.
///
/// Parses the `EraseOp` from the input data and erases the specified block.
fn handle_erase<'a>(
&mut self,
data: &'a mut [u8],
reqsz: usize,
) -> Result<&'a [u8], ErrorCode> {
let req_data = data.get(..reqsz).ok_or(error::IPC_ERROR_BAD_REQ_LEN)?;
let op = EraseOp::read_from_bytes(req_data).map_err(|_| error::IPC_ERROR_BAD_REQ_LEN)?;
let Some(size) = PowerOf2Usize::new(op.size as usize) else {
return Err(error::FLASH_GENERIC_ERASE_INVALID_SIZE);
};
self.flash.erase(op.address, size)?;
Ok(&data[0..0])
}
/// Handles the `IPC_OP_FLASH_PROGRAM` request.
///
/// Parses the start address and data from the input, then programs it.
fn handle_program<'a>(
&mut self,
data: &'a mut [u8],
reqsz: usize,
) -> Result<&'a [u8], ErrorCode> {
let req_data = data.get(..reqsz).ok_or(error::IPC_ERROR_BAD_REQ_LEN)?;
let (addr, program_data) =
FlashAddress::read_from_prefix(req_data).map_err(|_| error::IPC_ERROR_BAD_REQ_LEN)?;
self.flash.program(addr, program_data)?;
Ok(&data[0..0])
}
/// Handles the `IPC_OP_FLASH_READ` request.
///
/// Parses the `ReadOp` from the input, reads the data from flash into the
/// buffer, and returns the read slice.
fn handle_read<'a>(&mut self, data: &'a mut [u8], reqsz: usize) -> Result<&'a [u8], ErrorCode> {
let req_data = data.get(..reqsz).ok_or(error::IPC_ERROR_BAD_REQ_LEN)?;
let op = ReadOp::read_from_bytes(req_data).map_err(|_| error::IPC_ERROR_BAD_REQ_LEN)?;
let length = op.length as usize;
if length > data.len() {
return Err(error::FLASH_GENERIC_INVALID_SIZE);
}
self.flash.read(op.address, &mut data[..length])?;
Ok(&data[..length])
}
fn handle_op<'a>(
&mut self,
opcode: Opcode,
data: &'a mut [u8],
reqsz: usize,
) -> Result<&'a [u8], ErrorCode> {
match opcode {
IPC_OP_FLASH_GET_INFO => self.handle_geometry(data, reqsz),
IPC_OP_FLASH_ERASE => self.handle_erase(data, reqsz),
IPC_OP_FLASH_PROGRAM => self.handle_program(data, reqsz),
IPC_OP_FLASH_READ => self.handle_read(data, reqsz),
_ => Err(error::IPC_ERROR_UNKNOWN_OP),
}
}
/// Handles a single IPC request.
///
/// This method performs a non-blocking read on the IPC handle. The caller
/// must ensure the handle is readable (e.g., by calling `syscall::object_wait`)
/// before calling this method.
pub fn handle_one(&mut self, ipc: &IpcHandle, data: &mut [u8]) -> Result<(), ErrorCode> {
let len = ipc.read(0, data).map_err(ErrorCode::kernel_error)?;
let (opcode, reqrsp) = data.split_at_mut(core::mem::size_of::<Opcode>());
let opcode = Opcode::read_from_bytes(opcode).map_err(|_| error::IPC_ERROR_BAD_REQ_LEN)?;
let len = len.saturating_sub(core::mem::size_of::<Opcode>());
let mut status = 0u32;
let result = match self.handle_op(opcode, reqrsp, len) {
Ok(result) => result,
Err(e) => {
status = e.0.get();
&[]
}
};
ipc.respond(&[status.as_bytes(), result])
.map_err(ErrorCode::kernel_error)?;
Ok(())
}
}