Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move away from ROM APIs for flash writing #1504

Merged
merged 1 commit into from
Aug 31, 2023
Merged
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

65 changes: 65 additions & 0 deletions drv/lpc55-flash/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ pub const BYTES_PER_FLASH_WORD: usize = 16;
/// Number of bytes per flash page.
pub const BYTES_PER_FLASH_PAGE: usize = 512;

/// Flash words per flash page
pub const WORDS_PER_FLASH_PAGE: usize =
BYTES_PER_FLASH_PAGE / BYTES_PER_FLASH_WORD;

/// Flash driver handle. Wraps a pointer to the flash register set and provides
/// encapsulated operations.
///
Expand Down Expand Up @@ -336,6 +340,67 @@ impl<'a> Flash<'a> {
w
});
}

pub fn write_page(
&mut self,
addr: u32,
flash_page: &[u8; BYTES_PER_FLASH_PAGE],
wait: fn() -> (),
) -> Result<(), FlashTimeout> {
let start_word = addr / BYTES_PER_FLASH_WORD as u32;
self.start_erase_range(
start_word..=start_word + ((WORDS_PER_FLASH_PAGE as u32) - 1),
);
self.wait_for_erase_or_program(wait)?;

for (i, row) in
flash_page.chunks_exact(BYTES_PER_FLASH_WORD).enumerate()
{
let row: &[u8; BYTES_PER_FLASH_WORD] = row.try_into().unwrap();

self.start_write_row(i as u32, row);
while !self.poll_write_result() {}
}

self.start_program(start_word);
self.wait_for_erase_or_program(wait)?;
Ok(())
}

fn wait_for_erase_or_program(
&mut self,
wait: fn() -> (),
) -> Result<(), FlashTimeout> {
loop {
if let Some(result) = self.poll_erase_or_program_result() {
return result;
}

self.enable_interrupt_sources();
wait();
self.disable_interrupt_sources();
}
}

pub fn is_page_range_programmed(&mut self, addr: u32, len: u32) -> bool {
for i in (addr..addr + len).step_by(BYTES_PER_FLASH_PAGE) {
let word = i / (BYTES_PER_FLASH_WORD as u32);
// the blank check is inclusive so we need to end before
// the last word
let end_word = (word + WORDS_PER_FLASH_PAGE as u32) - 1;
self.start_blank_check(word..=end_word);
loop {
if let Some(s) = self.poll_blank_check_result() {
match s {
ProgramState::Blank => return false,
_ => break,
}
}
}
}

true
}
}

/// Raw status bits from the flash controller.
Expand Down
96 changes: 16 additions & 80 deletions drv/lpc55-update-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

use core::convert::Infallible;
use core::mem::MaybeUninit;
use drv_lpc55_flash::{BYTES_PER_FLASH_PAGE, BYTES_PER_FLASH_WORD};
use drv_lpc55_flash::BYTES_PER_FLASH_PAGE;
use drv_lpc55_update_api::{
RawCabooseError, RotBootInfo, SlotId, SwitchDuration, UpdateTarget,
};
Expand Down Expand Up @@ -403,11 +403,13 @@ impl idl::InOrderUpdateImpl for ServerImpl<'_> {
// Note that the page write machinery uses page numbers. This
// should probably change. But, for now, we must divide our word
// number by 32.
do_raw_page_write(
&mut self.flash,
CFPA_SCRATCH_FLASH_WORD / 32,
&cfpa_bytes,
)?;
self.flash
.write_page(
CFPA_SCRATCH_FLASH_WORD,
&cfpa_bytes,
wait_for_flash_interrupt,
)
.map_err(|_| UpdateError::FlashError)?;
}
}

Expand Down Expand Up @@ -713,82 +715,16 @@ fn do_block_write(
None => return Err(UpdateError::OutOfBounds),
};

// write_addr is a byte address; convert it back to a page number, but this
// time, an absolute page number in the flash device.
let page_num = write_addr / BYTES_PER_FLASH_PAGE as u32;

do_raw_page_write(flash, page_num, flash_page)
}

/// Performs an erase-write sequence to a single page within the raw flash
/// device. This function is capable of writing outside of any image slot, which
/// is important for doing CFPA updates. If you're writing to an image slot, use
/// `do_block_write`.
fn do_raw_page_write(
flash: &mut drv_lpc55_flash::Flash<'_>,
page_num: u32,
flash_page: &[u8; BYTES_PER_FLASH_PAGE],
) -> Result<(), UpdateError> {
// We regularly need the number of flash words per flash page below, and
// specifically as a u32, so:
static_assertions::const_assert_eq!(
BYTES_PER_FLASH_PAGE % BYTES_PER_FLASH_WORD,
0
);
const WORDS_PER_PAGE: u32 =
(BYTES_PER_FLASH_PAGE / BYTES_PER_FLASH_WORD) as u32;

// The hardware operates in terms of word numbers, never page numbers.
// Convert the page number to the number of the first word in that page.
// (This is equivalent to multiplying by 32 but named constants are nice.)
let word_num = page_num * WORDS_PER_PAGE;

// Step one: erase the page. Note that this range is INCLUSIVE. The hardware
// will happily erase multiple pages if you let it. We don't want that here.
flash.start_erase_range(word_num..=word_num + (WORDS_PER_PAGE - 1));
wait_for_erase_or_program(flash)?;

// Step two: Transfer each 16-byte flash word (page row) into the write
// registers in the flash controller.
for (i, row) in flash_page.chunks_exact(BYTES_PER_FLASH_WORD).enumerate() {
// TODO: this will be unnecessary if array_chunks stabilizes
let row: &[u8; BYTES_PER_FLASH_WORD] = row.try_into().unwrap_lite();

flash.start_write_row(i as u32, row);
while !flash.poll_write_result() {
// spin - supposed to be very quick in hardware.
}
}

// Step three: program the whole page into non-volatile storage by naming
// the first word in the target page. (Any word in the page will do,
// actually, but we've conveniently got the first word available.)
flash.start_program(word_num);
wait_for_erase_or_program(flash)?;

Ok(())
flash
.write_page(write_addr, flash_page, wait_for_flash_interrupt)
.map_err(|_| UpdateError::FlashError)
}

/// Utility function that does an interrupt-driven poll and sleep while the
/// flash controller finishes a write or erase.
fn wait_for_erase_or_program(
flash: &mut drv_lpc55_flash::Flash<'_>,
) -> Result<(), UpdateError> {
loop {
if let Some(result) = flash.poll_erase_or_program_result() {
return result.map_err(|_| UpdateError::FlashError);
}

flash.enable_interrupt_sources();
sys_irq_control(notifications::FLASH_IRQ_MASK, true);
// RECV from the kernel cannot produce an error, so ignore it.
let _ = sys_recv_closed(
&mut [],
notifications::FLASH_IRQ_MASK,
TaskId::KERNEL,
);
flash.disable_interrupt_sources();
}
fn wait_for_flash_interrupt() {
sys_irq_control(notifications::FLASH_IRQ_MASK, true);
// RECV from the kernel cannot produce an error, so ignore it.
let _ =
sys_recv_closed(&mut [], notifications::FLASH_IRQ_MASK, TaskId::KERNEL);
}

fn same_image(which: UpdateTarget) -> bool {
Expand Down
10 changes: 5 additions & 5 deletions drv/update-api/hiffy-rot-flash.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@

# NOTE: Make sure you are not runnning this on an ancient version of bash on Mac OSX

set -x
set -ex

image=$1
image_type=$2

block_size=512
binsize=`ls -la $image | cut -w -f 5`
binsize=`ls -la $image | cut -d ' ' -f 5`
numblocks=$(("${binsize}" / "${block_size}"))
total_before_last_block=$((numblocks * "${block_size}"))
lastblocksize=$(("${binsize}" - "${total_before_last_block}"))
Expand Down Expand Up @@ -63,10 +63,10 @@ write_blocks() {
humility -a "${ARCHIVE}" hiffy -c Update.prep_image_update -a image_type="${image_type}"

# Begin by invalidating the header which resides at offset 0x130 (in block zero).
write_blocks 0 0 /dev/zero
write_blocks 1 "${loopend}" "${image}"
#write_blocks 0 0 /dev/zero
write_blocks 0 "${loopend}" "${image}"
humility -a "${ARCHIVE}" hiffy -c Update.write_one_block -a block_num="${numblocks}" -i <(dd if="${image}" bs=1 count="${lastblocksize}" skip="${total_before_last_block}")
# Lastly, write the correct block zero.
write_blocks 0 0 "${image}"
#write_blocks 0 0 "${image}"

humility -a "${ARCHIVE}" hiffy -c Update.finish_image_update
Loading