Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions services/flash/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0

load("@rules_rust//rust:defs.bzl", "rust_library")

package(default_visibility = ["//visibility:public"])

rust_library(
name = "opcode",
srcs = [
"opcode.rs",
],
crate_name = "services_flash_opcode",
edition = "2024",
deps = [
"//hal/blocking/flash",
"//util/types",
"@rust_crates//:zerocopy",
],
)

rust_library(
name = "client",
srcs = [
"client.rs",
],
crate_name = "services_flash_client",
edition = "2024",
deps = [
":opcode",
"//hal/blocking/flash",
"//util/error",
"//util/ipc",
"//util/types",
"@pigweed//pw_kernel/userspace",
"@pigweed//pw_log/rust:pw_log",
"@rust_crates//:zerocopy",
],
)

rust_library(
name = "server",
srcs = [
"server.rs",
],
crate_name = "services_flash_server",
edition = "2024",
deps = [
":opcode",
"//hal/blocking/flash",
"//util/error",
"//util/ipc",
"//util/types",
"@pigweed//pw_kernel/userspace",
"@pigweed//pw_log/rust:pw_log",
"@rust_crates//:zerocopy",
],
)
88 changes: 88 additions & 0 deletions services/flash/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Flash Service

The Flash Service provides a centralized interface for userspace applications to interact with on-chip and external flash memory. This is achieved via an IPC-based client-server architecture.

## Overview

Applications interact with flash through the `Flash` trait, typically using the `FlashIpcClient` implementation. All operations are blocking from the perspective of the caller.

### Key Features
- **Partition Support**: Access to both primary Data partitions and auxiliary Info partitions.
- **Flexible Erase**: Support for multiple erase granularities (e.g., page vs. block) as reported by the hardware.
- **Unified Addressing**: A logical `FlashAddress` system that abstracts hardware-specific bank and page layouts.

## Usage

To use the flash service, initialize a `FlashIpcClient` with a handle to the flash service:

```rust
use hal_flash::{Flash, FlashAddress};
use services_flash_client::FlashIpcClient;
use util_ipc::IpcHandle;

// 1. Connect to the flash service
let mut flash = FlashIpcClient::new(IpcHandle::new(FLASH_SERVICE_HANDLE))?;

// 2. Retrieve device geometry
let (total_size, page_size, erasable_bitmap) = flash.geometry();

// 3. Erase a block (using the default page size)
let addr = FlashAddress::new(0x1000);
flash.erase(addr, page_size)?;

// 4. Program data
flash.program(addr, b"Hello, Flash!")?;

// 5. Read data back
let mut buf = [0u8; 13];
flash.read(addr, &mut buf)?;
```

## The `Flash` Trait

The primary interface for flash operations:

- `geometry() -> (NonZero<usize>, PowerOf2Usize, u32)`: Returns the total capacity, the default/smallest page size, and a bitmap of all supported erase block sizes.
- `read(addr, buf)`: Reads data from the specified address.
- `erase(addr, size)`: Erases a block of the specified size. The size must be one of the values supported in the `erasable_bitmap`.
- `program(addr, data)`: Writes data to the specified address. Flash must be erased before programming.

### Understanding `erasable_bitmap`
The `erasable_bitmap` is a `u32` where each set bit `i` indicates that an erase block size of `2^i` bytes is supported.
- Bit 11 set (`0x800`) -> 2048-byte erase supported.
- Bit 16 set (`0x10000`) -> 64KB erase supported.

## Addressing

Flash memory is addressed using the `FlashAddress` type, which wraps a single 32-bit `offset`.

On platforms like Earlgrey, the most significant bit (MSB) of this offset is used to distinguish between different partitions:
- **DATA partition**: MSB is 0 (offset < 0x80000000).
- **INFO partition**: MSB is 1 (offset >= 0x80000000).

The `EarlgreyFlashAddress` trait (from `earlgrey_util`) provides helper methods to construct and inspect addresses:
- `FlashAddress::data(offset)`: Accesses the main data partition.
- `FlashAddress::info(bank, page, offset)`: Accesses specific info pages.

## Implementation Details

The service is built on several layers of abstraction:

### IPC Layer
- **`FlashIpcServer`**: Wraps a hardware-backed `Flash` implementation and dispatches IPC requests.
- **`FlashIpcClient`**: Implements the `Flash` trait by proxying calls to the server.

### Hardware Abstraction
- **`FlashDriver` Trait**: Defines the low-level, often asynchronous, interface for hardware drivers.
- **`BlockingFlash`**: A wrapper that converts a `FlashDriver` into a synchronous `Flash` implementation using a provided blocking mechanism.

### Component Diagram

```mermaid
graph TD
Client[Userspace Application] -- "Flash Trait" --> IPC_Client[FlashIpcClient]
IPC_Client -- "IPC" --> IPC_Server[FlashIpcServer]
IPC_Server -- "Flash Trait" --> BlockingFlash[BlockingFlash]
BlockingFlash -- "FlashDriver Trait" --> HardwareDriver[e.g., EmbeddedFlash]
HardwareDriver --> HW[Flash Controller]
```
109 changes: 109 additions & 0 deletions services/flash/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

//! Flash IPC client implementation.

#![no_std]
use core::num::NonZero;

use hal_flash::{Flash, FlashAddress};
use services_flash_opcode::*;
use userspace::time::Instant;
use util_error::{self as error, ErrorCode};
use util_ipc::{IpcChannel, IpcHandle};
use util_types::PowerOf2Usize;
use zerocopy::{FromZeros, IntoBytes};

/// An IPC-based client for the flash service.
///
/// This struct implements the `Flash` trait by proxying requests to a remote
/// flash server via an IPC handle.
pub struct FlashIpcClient {
ipc: IpcHandle,
page_size: PowerOf2Usize,
total_size: NonZero<usize>,
erasable_sizes_bitmap: u32,
}

impl FlashIpcClient {
/// Creates a new `FlashIpcClient` using the provided IPC handle.
///
/// This constructor will perform an IPC transaction to retrieve flash
/// geometry and capabilities from the server.
pub fn new(ipc: IpcHandle) -> Result<Self, ErrorCode> {
let mut info = FlashInfo::new_zeroed();
let mut result = 0u32;

ipc.transact(
&[IPC_OP_FLASH_GET_INFO.as_bytes()],
&mut [result.as_mut_bytes(), info.as_mut_bytes()],
Instant::MAX,
)
.map_err(ErrorCode::kernel_error)?;
ErrorCode::check_status(result)?;

let Some(page_size) = PowerOf2Usize::new(info.page_size as usize) else {
return Err(error::FLASH_GENERIC_INVALID_PAGE_SIZE);
};
let Some(total_size) = NonZero::new(info.total_size as usize) else {
return Err(error::FLASH_GENERIC_INVALID_SIZE);
};
Ok(Self {
ipc,
page_size,
total_size,
erasable_sizes_bitmap: info.erasable_sizes_bitmap,
})
}
}

impl Flash for FlashIpcClient {
type Error = ErrorCode;
fn geometry(&mut self) -> Result<(NonZero<usize>, PowerOf2Usize, u32), ErrorCode> {
Ok((self.total_size, self.page_size, self.erasable_sizes_bitmap))
}

fn erase(&mut self, start_addr: FlashAddress, size: PowerOf2Usize) -> Result<(), ErrorCode> {
let mut result = 0u32;
let op = EraseOp {
address: start_addr,
size: size.get() as u32,
};
self.ipc
.transact(
&[IPC_OP_FLASH_ERASE.as_bytes(), op.as_bytes()],
&mut [result.as_mut_bytes()],
Instant::MAX,
)
.map_err(ErrorCode::kernel_error)?;
ErrorCode::check_status(result)
}

fn program(&mut self, start_addr: FlashAddress, data: &[u8]) -> Result<(), ErrorCode> {
let mut result = 0u32;
self.ipc
.transact(
&[IPC_OP_FLASH_PROGRAM.as_bytes(), start_addr.as_bytes(), data],
&mut [result.as_mut_bytes()],
Instant::MAX,
)
.map_err(ErrorCode::kernel_error)?;
ErrorCode::check_status(result)
}

fn read(&mut self, start_addr: FlashAddress, buf: &mut [u8]) -> Result<(), ErrorCode> {
let mut result = 0u32;
let op = ReadOp {
address: start_addr,
length: buf.len() as u32,
};
self.ipc
.transact(
&[IPC_OP_FLASH_READ.as_bytes(), op.as_bytes()],
&mut [result.as_mut_bytes(), buf],
Instant::MAX,
)
.map_err(ErrorCode::kernel_error)?;
ErrorCode::check_status(result)
}
}
51 changes: 51 additions & 0 deletions services/flash/opcode.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

//! Shared flash IPC opcodes and data structures.

#![no_std]

use hal_flash::FlashAddress;
use util_types::Opcode;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};

/// IPC opcode for erasing a flash block.
pub const IPC_OP_FLASH_ERASE: Opcode = Opcode::new(*b"FLET");
/// IPC opcode for programming flash.
pub const IPC_OP_FLASH_PROGRAM: Opcode = Opcode::new(*b"FLWR");
/// IPC opcode for reading from flash.
pub const IPC_OP_FLASH_READ: Opcode = Opcode::new(*b"FLRD");
/// IPC opcode for retrieving flash information.
pub const IPC_OP_FLASH_GET_INFO: Opcode = Opcode::new(*b"FLIN");

/// Information about the flash device.
#[derive(FromBytes, Immutable, IntoBytes, KnownLayout)]
#[repr(C)]
pub struct FlashInfo {
/// The size of a single flash page in bytes.
pub page_size: u32,
/// The total size of the flash in bytes.
pub total_size: u32,
/// A bitmap of supported erase block sizes.
pub erasable_sizes_bitmap: u32,
}

/// Arguments for the `IPC_OP_FLASH_ERASE` request.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct EraseOp {
/// The start address of the block to erase.
pub address: FlashAddress,
/// The size of the block to erase in bytes.
pub size: u32,
}

/// Arguments for the `IPC_OP_FLASH_READ` request.
#[derive(FromBytes, IntoBytes, KnownLayout, Immutable)]
#[repr(C)]
pub struct ReadOp {
/// The start address to read from.
pub address: FlashAddress,
/// The number of bytes to read.
pub length: u32,
}
Loading