Skip to content
Open
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
4 changes: 2 additions & 2 deletions protocol/usb/dfu/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,9 @@ where
return;
}
if let Some(data) = self.buffer.get(self.transfer_offset..self.transfer_total) {
let n = driver.transfer_in_unaligned(0, data, true);
let zlp = self.transfer_total < self.config.transfer_size as usize;
let n = driver.transfer_in_unaligned(0, data, zlp);
self.transfer_offset += n;

if self.transfer_offset == self.transfer_total {
if self.transfer_total < self.config.transfer_size as usize {
self.state = DfuState::DfuIdle;
Expand Down
6 changes: 6 additions & 0 deletions target/earlgrey/firmware/transport/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ rust_process(
"//target/earlgrey/util",
"//util/error",
"//util/ipc",
"//util/types",
"//util/zfmt",
"@pigweed//pw_kernel/userspace",
"@pigweed//pw_status/rust:pw_status",
Expand All @@ -85,17 +86,22 @@ rust_process(
tags = ["kernel"],
visibility = ["//visibility:public"],
deps = [
"//drivers/flash:spi_flash",
"//hal/blocking/flash",
"//hal/blocking/flash:driver",
"//services/flash:server",
"//target/earlgrey/drivers:eflash_driver",
"//target/earlgrey/drivers:spi_host",
"//target/earlgrey/registers:flash_ctrl_core",
"//target/earlgrey/registers:spi_host",
"//target/earlgrey/util",
"//util/error",
"//util/ipc",
"//util/types",
"//util/zfmt",
"@pigweed//pw_kernel/userspace",
"@pigweed//pw_status/rust:pw_status",
"@rust_crates//:embedded-hal",
"@zfmt//zfmt",
],
)
Expand Down
65 changes: 62 additions & 3 deletions target/earlgrey/firmware/transport/dfu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use hal_flash::{Flash, FlashAddress};
use services_flash_client::FlashIpcClient;
use util_error::ErrorCode;
use util_ipc::IpcChannel;
use util_types::PowerOf2Usize;
use zerocopy::FromBytes;

use protocol_usb_dfu::{DfuHandler, DfuStatus};
Expand Down Expand Up @@ -111,6 +112,12 @@ pub const DFU_CDI0_CERT: hal_usb::StringDescriptorRef =
pub const DFU_CDI1_CERT: hal_usb::StringDescriptorRef =
hal_usb::string_descriptor!("CDI1 Certificate").as_ref();

pub const DFU_ALT_FIRMWARE: u8 = 0;
pub const DFU_ALT_UDS_CERT: u8 = 1;
pub const DFU_ALT_CDI0_CERT: u8 = 2;
pub const DFU_ALT_CDI1_CERT: u8 = 3;
pub const DFU_ALT_SPI_EEPROM0: u8 = 4;

/// Retrieves a certificate from the info partition in flash.
///
/// # Arguments
Expand Down Expand Up @@ -247,21 +254,31 @@ impl FwUpdate {
/// DFU handler for Earlgrey, managing firmware updates and certificate uploads.
pub struct EarlgreyDfuHandler<IPC: IpcChannel> {
flash: FlashIpcClient,
spi_flash: FlashIpcClient,
spi_flash_total_size: u32,
spi_flash_page_size: PowerOf2Usize,
sysmgr: SysmgrClient<IPC>,
update: FwUpdate,
alt_setting: Option<u8>,
}

impl<IPC: IpcChannel> EarlgreyDfuHandler<IPC> {
/// Creates a new DFU handler.
pub fn new(
flash: FlashIpcClient,
mut spi_flash: FlashIpcClient,
sysmgr: SysmgrClient<IPC>,
info: &BootInfo,
) -> Result<Self, ErrorCode> {
let (spi_flash_total_size, spi_flash_page_size, _) = spi_flash.geometry()?;
Ok(EarlgreyDfuHandler {
flash,
spi_flash,
spi_flash_total_size: spi_flash_total_size.get() as u32,
spi_flash_page_size,
sysmgr,
update: FwUpdate::new(info)?,
alt_setting: None,
})
}

Expand Down Expand Up @@ -394,8 +411,40 @@ impl<IPC: IpcChannel> EarlgreyDfuHandler<IPC> {
}
Ok(())
}
}

fn flash_spi_eeprom0_block(&mut self, block_num: u32, data: &[u8]) -> Result<(), DfuStatus> {
let address = block_num * FLASH_BLOCK_SIZE as u32;
if address >= self.spi_flash_total_size {
return Err(DfuStatus::ErrAddress);
}
if (address as usize) % self.spi_flash_page_size.get() == 0 {
self.spi_flash
.erase(FlashAddress::new(address), self.spi_flash_page_size)
.map_err(|_| DfuStatus::ErrErase)?;
}
self.spi_flash
.program(FlashAddress::new(address), data)
.map_err(|_| DfuStatus::ErrProg)?;
Ok(())
}

fn read_spi_eeprom0_block(
&mut self,
block_num: u32,
data: &mut [u8],
) -> Result<usize, DfuStatus> {
let address = block_num * FLASH_BLOCK_SIZE as u32;
let total_bytes = self.spi_flash_total_size;
if address >= total_bytes {
return Ok(0);
}
let read_len = core::cmp::min(data.len(), (total_bytes - address) as usize);
self.spi_flash
.read(FlashAddress::new(address), &mut data[..read_len])
.map_err(|_| DfuStatus::ErrUnknown)?;
Ok(read_len)
}
}
impl<IPC: IpcChannel> DfuHandler for EarlgreyDfuHandler<IPC> {
/// Handles a DFU download (DNLOAD) request.
///
Expand All @@ -407,8 +456,11 @@ impl<IPC: IpcChannel> DfuHandler for EarlgreyDfuHandler<IPC> {
block: block_num,
len: data.len() as u32,
});
if alt == 0 {
self.alt_setting = Some(alt);
if alt == DFU_ALT_FIRMWARE {
self.flash_fw_block(block_num as u32, data)
} else if alt == DFU_ALT_SPI_EEPROM0 {
self.flash_spi_eeprom0_block(block_num as u32, data)
} else {
Err(DfuStatus::ErrFile)
}
Expand All @@ -424,8 +476,12 @@ impl<IPC: IpcChannel> DfuHandler for EarlgreyDfuHandler<IPC> {
block: block_num,
len: data.len() as u32,
});
self.alt_setting = Some(alt);
match alt {
1 | 2 | 3 => get_certificate(&mut self.flash, alt - 1, data),
DFU_ALT_UDS_CERT | DFU_ALT_CDI0_CERT | DFU_ALT_CDI1_CERT => {
get_certificate(&mut self.flash, alt - DFU_ALT_UDS_CERT, data)
}
DFU_ALT_SPI_EEPROM0 => self.read_spi_eeprom0_block(block_num as u32, data),
_ => Err(DfuStatus::ErrFile),
}
}
Expand All @@ -436,6 +492,9 @@ impl<IPC: IpcChannel> DfuHandler for EarlgreyDfuHandler<IPC> {
/// slot and requests a reboot.
fn manifest(&mut self) -> Result<(), DfuStatus> {
util_zfmt::info!(DfuManifest);
if self.alt_setting == Some(DFU_ALT_SPI_EEPROM0) {
return Ok(());
}
if self.update.state == FwUpdateState::Done
|| self.update.state == FwUpdateState::Application
|| self.update.state == FwUpdateState::RomExt
Expand Down
79 changes: 68 additions & 11 deletions target/earlgrey/firmware/transport/flash_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,29 @@ use userspace::time::Instant;
use userspace::{process_entry, syscall};
use util_error::{AsStatus, ErrorCode};
use util_zfmt::messages::{ProcessExit, ProcessStart};
use zfmt::Zfmt;

use earlgrey_util::EarlgreyFlashAddress;
use eflash_driver::{EmbeddedFlash, Permission};
use hal_flash::{BlockingFlash, FlashAddress};
use services_flash_server::FlashIpcServer;
use spi_flash::SpiFlash;
use spi_host::SpiHost0;
use util_ipc::IpcHandle;
use util_types::Blocking;

#[derive(Zfmt)]
#[zfmt(format = "SPI Host init failed: {code:08x}")]
struct SpiHostInitFailed {
code: u32,
}

#[derive(Zfmt)]
#[zfmt(format = "SPI Flash init failed: {code:08x}")]
struct SpiFlashInitFailed {
code: u32,
}

struct FlashCtrlInterrupt;

impl Blocking for FlashCtrlInterrupt {
Expand All @@ -38,28 +53,70 @@ impl Blocking for FlashCtrlInterrupt {
}

fn flash_server() -> Result<(), ErrorCode> {
let mut driver =
let mut eflash_driver =
EmbeddedFlash::new_with_interrupts(unsafe { flash_ctrl_core::FlashCtrl::new() });
driver.set_default_permission(Permission::FULL_ACCESS);
eflash_driver.set_default_permission(Permission::FULL_ACCESS);
for i in 5..9 {
driver.set_info_permission(FlashAddress::info(0, i, 0), Permission::FULL_ACCESS)?;
driver.set_info_permission(FlashAddress::info(1, i, 0), Permission::FULL_ACCESS)?;
eflash_driver.set_info_permission(FlashAddress::info(0, i, 0), Permission::FULL_ACCESS)?;
eflash_driver.set_info_permission(FlashAddress::info(1, i, 0), Permission::FULL_ACCESS)?;
}
let flash = BlockingFlash {
driver,
let eflash = BlockingFlash {
driver: eflash_driver,
blocking: FlashCtrlInterrupt,
};
let mut flash_server = FlashIpcServer::new(flash);
let mut eflash_server = FlashIpcServer::new(eflash);

let mut spi_host = unsafe {
// SAFETY: we have exclusive access to the spi_host0 peripheral.
earlgrey_spi_host::SpiHost::new(spi_host::RegisterBlock::new(SpiHost0::PTR))
};
if let Err(e) = spi_host.init(&earlgrey_spi_host::SpiConfig::DEFAULT_SPI0) {
let code = u32::from(ErrorCode::from(e));
util_zfmt::error!(SpiHostInitFailed { code });
return Err(ErrorCode::from(e));
}

let mut spi_flash = SpiFlash::new(spi_host);
if let Err(e) = spi_flash.init() {
util_zfmt::error!(SpiFlashInitFailed { code: u32::from(e) });
return Err(e);
}
let mut spi_flash_server = FlashIpcServer::new(spi_flash);

syscall::wait_group_add(
handle::FLASH_WAIT_GROUP,
handle::EFLASH_SERVICE,
syscall::Signals::READABLE,
handle::EFLASH_SERVICE as usize,
)
.map_err(ErrorCode::kernel_error)?;

syscall::wait_group_add(
handle::FLASH_WAIT_GROUP,
handle::SPI_FLASH_SERVICE,
syscall::Signals::READABLE,
handle::SPI_FLASH_SERVICE as usize,
)
.map_err(ErrorCode::kernel_error)?;

let mut buf = [0u8; 2064];
let ipc = IpcHandle::new(handle::FLASH_SERVICE);
let eflash_ipc = IpcHandle::new(handle::EFLASH_SERVICE);
let spi_flash_ipc = IpcHandle::new(handle::SPI_FLASH_SERVICE);

loop {
syscall::object_wait(
handle::FLASH_SERVICE,
let wait_result = syscall::object_wait(
handle::FLASH_WAIT_GROUP,
syscall::Signals::READABLE,
Instant::MAX,
)
.map_err(ErrorCode::kernel_error)?;
flash_server.handle_one(&ipc, &mut buf)?;

let channel = wait_result.user_data as u32;
if channel == handle::EFLASH_SERVICE {
eflash_server.handle_one(&eflash_ipc, &mut buf)?;
} else if channel == handle::SPI_FLASH_SERVICE {
spi_flash_server.handle_one(&spi_flash_ipc, &mut buf)?;
}
}
}

Expand Down
24 changes: 22 additions & 2 deletions target/earlgrey/firmware/transport/system.json5
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,17 @@
handler_object_name: "logger_flash"
},
{
name: "flash_service",
name: "eflash_service",
type: "channel_handler"
},
{
name: "spi_flash_service",
type: "channel_handler"
},
{
name: "flash_wait_group",
type: "wait_group"
},
{
name: "flash_interrupts",
type: "interrupt",
Expand All @@ -169,6 +177,12 @@
type: "device",
start_address: 0x41000000,
size_bytes: 0x200
},
{
name: "spi_host0",
type: "device",
start_address: 0x40300000,
size_bytes: 0x1000
}
]
},
Expand Down Expand Up @@ -215,7 +229,13 @@
name: "flash_usb",
type: "channel_initiator",
handler_process: "flash_server",
handler_object_name: "flash_service"
handler_object_name: "eflash_service"
},
{
name: "spi_flash_usb",
type: "channel_initiator",
handler_process: "flash_server",
handler_object_name: "spi_flash_service"
},
{
name: "sysmgr_usb",
Expand Down
56 changes: 56 additions & 0 deletions target/earlgrey/firmware/transport/tests/dfu/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,59 @@ opentitan_test(
test_cmd = "--logging=info --expect-reboot --expect-app --firmware=target/earlgrey/firmware/transport/tests/dfu/bootinfo_simple.app_prod_0.signed.bin",
test_harness = ":host_usb_dfu_owner_transfer",
)

opentitan_rust_binary(
name = "host_usb_dfu_spi_flash",
srcs = ["host_usb_dfu_spi_flash.rs"],
edition = "2024",
rustc_flags = [
"-C",
"link-arg=-Wl,--allow-shlib-undefined",
],
deps = [
"//target/earlgrey/testutil",
"//third_party/lowrisc_opentitan:opentitanlib",
"//third_party/lowrisc_opentitan:usb_test_helper",
"@ot_crate_index//:anyhow",
"@ot_crate_index//:clap",
"@ot_crate_index//:log",
],
)

opentitan_test(
name = "dfu_spi_flash_hyper310_test",
timeout = "eternal",
clear_bitstream = True,
ecdsa_key = FPGA_ECDSA_KEY,
environment = "//target/earlgrey/env:hyper310",
interface = "hyper310",
tags = [
"hardware",
"hyper310",
],
target = "//target/earlgrey/firmware/transport:transport_firmware",
target_data = [
":bootinfo_signed_simple",
],
test_cmd = "--logging=info --firmware=target/earlgrey/firmware/transport/tests/dfu/bootinfo_simple.app_prod_0.signed.bin",
test_harness = ":host_usb_dfu_spi_flash",
)

opentitan_test(
name = "dfu_spi_flash_hyper340_test",
timeout = "eternal",
clear_bitstream = True,
ecdsa_key = FPGA_ECDSA_KEY,
environment = "//target/earlgrey/env:hyper340",
interface = "hyper340",
tags = [
"hardware",
"hyper340",
],
target = "//target/earlgrey/firmware/transport:transport_firmware",
target_data = [
":bootinfo_signed_simple",
],
test_cmd = "--logging=info --firmware=target/earlgrey/firmware/transport/tests/dfu/bootinfo_simple.app_prod_0.signed.bin",
test_harness = ":host_usb_dfu_spi_flash",
)
Loading