#![no_std]
#![warn(missing_docs)]
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
pub use embassy_boot::{
AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig,
FirmwareState, FirmwareUpdater, FirmwareUpdaterConfig, FirmwareUpdaterError, State,
};
pub use embedded_storage;
pub const FLASH_BASE: u32 = 0x0800_0000;
const PFIC_CFGR: *mut u32 = 0xE000_E048 as *mut u32;
const PFIC_KEY3: u32 = 0xBEEF_0000;
const PFIC_SYSRESET: u32 = 1 << 7;
unsafe extern "C" {
static __bootloader_active_start: u32;
static __bootloader_active_end: u32;
static __bootloader_dfu_start: u32;
static __bootloader_dfu_end: u32;
static __bootloader_state_start: u32;
static __bootloader_state_end: u32;
}
pub fn active_start() -> u32 {
unsafe { &__bootloader_active_start as *const u32 as u32 }
}
pub fn active_end() -> u32 {
unsafe { &__bootloader_active_end as *const u32 as u32 }
}
pub fn active_size() -> u32 {
active_end() - active_start()
}
pub fn dfu_start() -> u32 {
unsafe { &__bootloader_dfu_start as *const u32 as u32 }
}
pub fn dfu_end() -> u32 {
unsafe { &__bootloader_dfu_end as *const u32 as u32 }
}
pub fn dfu_size() -> u32 {
dfu_end() - dfu_start()
}
pub fn state_start() -> u32 {
unsafe { &__bootloader_state_start as *const u32 as u32 }
}
pub fn state_end() -> u32 {
unsafe { &__bootloader_state_end as *const u32 as u32 }
}
pub fn state_size() -> u32 {
state_end() - state_start()
}
pub fn disable_interrupts() {
#[cfg(target_arch = "riscv32")]
unsafe {
core::arch::asm!("csrw mie, zero", "csrc mstatus, 8", options(nostack));
}
}
pub fn system_reset() -> ! {
disable_interrupts();
unsafe {
core::ptr::write_volatile(PFIC_CFGR, PFIC_KEY3 | PFIC_SYSRESET);
}
loop {
core::hint::spin_loop();
}
}
pub struct CoarseFlash<F, const ERASE_SIZE: usize>(pub F);
impl<F, const ERASE_SIZE: usize> CoarseFlash<F, ERASE_SIZE> {
pub fn inner(&self) -> &F {
&self.0
}
pub fn inner_mut(&mut self) -> &mut F {
&mut self.0
}
pub fn into_inner(self) -> F {
self.0
}
}
impl<F, const ERASE_SIZE: usize> ErrorType for CoarseFlash<F, ERASE_SIZE>
where
F: ErrorType,
{
type Error = F::Error;
}
impl<F, const ERASE_SIZE: usize> ReadNorFlash for CoarseFlash<F, ERASE_SIZE>
where
F: ReadNorFlash,
{
const READ_SIZE: usize = F::READ_SIZE;
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
self.0.read(offset, bytes)
}
fn capacity(&self) -> usize {
self.0.capacity()
}
}
impl<F, const ERASE_SIZE: usize> NorFlash for CoarseFlash<F, ERASE_SIZE>
where
F: NorFlash,
{
const WRITE_SIZE: usize = F::WRITE_SIZE;
const ERASE_SIZE: usize = ERASE_SIZE;
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
const {
assert!(ERASE_SIZE > 0, "CoarseFlash erase size must not be zero");
assert!(
ERASE_SIZE.is_multiple_of(F::ERASE_SIZE),
"CoarseFlash erase size must be a multiple of the hardware erase size"
);
}
assert!(
from.is_multiple_of(ERASE_SIZE as u32) && to.is_multiple_of(ERASE_SIZE as u32),
"erase range must be aligned to the coarse erase size"
);
assert!(from < to, "erase range must not be empty");
let mut address = from;
while address < to {
self.0.erase(address, address + F::ERASE_SIZE as u32)?;
address += F::ERASE_SIZE as u32;
}
Ok(())
}
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
self.0.write(offset, bytes)
}
}
pub struct BootLoader {
pub state: State,
}
impl BootLoader {
pub fn prepare<ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash, const BUFFER_SIZE: usize>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Self {
match Self::try_prepare::<ACTIVE, DFU, STATE, BUFFER_SIZE>(config) {
Ok(loader) => loader,
Err(_) => panic!("Boot prepare error"),
}
}
pub fn try_prepare<
ACTIVE: NorFlash,
DFU: NorFlash,
STATE: NorFlash,
const BUFFER_SIZE: usize,
>(
config: BootLoaderConfig<ACTIVE, DFU, STATE>,
) -> Result<Self, BootError> {
let mut aligned_buf = AlignedBuffer([0; BUFFER_SIZE]);
let mut boot = embassy_boot::BootLoader::new(config);
let state = boot.prepare_boot(aligned_buf.as_mut())?;
Ok(Self { state })
}
pub unsafe fn load(self, start: u32) -> ! {
#[cfg(target_arch = "riscv32")]
unsafe {
let entry = FLASH_BASE + start;
core::arch::asm!(
"csrw mie, zero",
"csrc mstatus, 8",
"jr {0}",
in(reg) entry,
options(noreturn, nostack),
);
}
#[cfg(not(target_arch = "riscv32"))]
{
let _ = start;
unimplemented!("embassy-boot-ch32 can only load firmware on riscv32");
}
}
}