Firmware version for upgrades (#542)

* shows and checks the firmware version

* merges metadata ranges in boards

* simplifies locations loop
This commit is contained in:
kaczmarczyck
2022-09-01 18:28:03 +02:00
committed by GitHub
parent 771ce7635b
commit 8288bb0860
7 changed files with 69 additions and 16 deletions

View File

@@ -25,6 +25,7 @@ use libtock_core::{callback, syscalls};
use persistent_store::{Storage, StorageError, StorageIndex, StorageResult};
const DRIVER_NUMBER: usize = 0x50003;
const METADATA_SIGN_OFFSET: usize = 0x800;
const UPGRADE_PUBLIC_KEY: &[u8; 65] =
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_upgrade_pubkey.bin"));
@@ -100,6 +101,10 @@ fn block_command(driver: usize, cmd: usize, arg1: usize, arg2: usize) -> Storage
}
}
unsafe fn read_slice(address: usize, length: usize) -> &'static [u8] {
core::slice::from_raw_parts(address as *const u8, length)
}
fn write_slice(ptr: usize, value: &[u8]) -> StorageResult<()> {
let code = unsafe {
syscalls::raw::allow(
@@ -228,11 +233,15 @@ pub struct TockUpgradeStorage {
page_size: usize,
partition: ModRange,
metadata: ModRange,
running_metadata: ModRange,
}
impl TockUpgradeStorage {
const METADATA_ADDRESS_A: usize = 0x4000;
const METADATA_ADDRESS_B: usize = 0x5000;
// Ideally, the kernel should tell us metadata and partitions directly.
// This code only works for one layout, refactor this into the storage driver to support more.
const METADATA_ADDRESS: usize = 0x4000;
const _PARTITION_ADDRESS_A: usize = 0x20000;
const PARTITION_ADDRESS_B: usize = 0x60000;
/// Provides access to the other upgrade partition and metadata if available.
///
@@ -244,7 +253,7 @@ impl TockUpgradeStorage {
/// Returns `CustomError` if any of the following conditions do not hold:
/// - The page size is a power of two.
/// - The storage slices are page-aligned.
/// - There are not partition or metadata slices.
/// - There are no partition or no metadata slices.
/// Returns a `NotAligned` error if partitions or metadata ranges are
/// - not exclusive or,
/// - not consecutive.
@@ -253,6 +262,7 @@ impl TockUpgradeStorage {
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
partition: ModRange::new_empty(),
metadata: ModRange::new_empty(),
running_metadata: ModRange::new_empty(),
};
if !locations.page_size.is_power_of_two() {
return Err(StorageError::CustomError);
@@ -269,7 +279,12 @@ impl TockUpgradeStorage {
}
let range = ModRange::new(storage_ptr, storage_len);
match range.start() {
Self::METADATA_ADDRESS_A | Self::METADATA_ADDRESS_B => locations.metadata = range,
Self::METADATA_ADDRESS => {
// Will be swapped if we are on B.
locations.metadata = ModRange::new(range.start(), locations.page_size);
locations.running_metadata =
ModRange::new(range.start() + locations.page_size, locations.page_size);
}
_ => {
locations.partition = locations
.partition
@@ -278,9 +293,15 @@ impl TockUpgradeStorage {
}
}
}
if locations.partition.is_empty() {
if locations.partition.is_empty()
|| locations.metadata.is_empty()
|| locations.running_metadata.is_empty()
{
return Err(StorageError::CustomError);
}
if locations.partition.start() == Self::PARTITION_ADDRESS_B {
core::mem::swap(&mut locations.metadata, &mut locations.running_metadata);
}
Ok(locations)
}
@@ -299,7 +320,7 @@ impl UpgradeStorage for TockUpgradeStorage {
.partition
.contains_range(&ModRange::new(address, length))
{
Ok(unsafe { core::slice::from_raw_parts(address as *const u8, length) })
Ok(unsafe { read_slice(address, length) })
} else {
Err(StorageError::OutOfBounds)
}
@@ -332,9 +353,7 @@ impl UpgradeStorage for TockUpgradeStorage {
}
fn read_metadata(&self) -> StorageResult<&[u8]> {
Ok(unsafe {
core::slice::from_raw_parts(self.metadata.start() as *const u8, self.metadata.length())
})
Ok(unsafe { read_slice(self.metadata.start(), self.metadata.length()) })
}
fn write_metadata(&mut self, data: &[u8]) -> StorageResult<()> {
@@ -348,6 +367,16 @@ impl UpgradeStorage for TockUpgradeStorage {
}
write_slice(self.metadata.start(), data)
}
fn running_firmware_version(&self) -> u64 {
let running_metadata = unsafe {
read_slice(
self.running_metadata.start(),
self.running_metadata.length(),
)
};
parse_metadata_version(running_metadata)
}
}
/// Parses the metadata of an upgrade, and checks its correctness.
@@ -367,11 +396,15 @@ fn parse_metadata(
metadata: &[u8],
) -> StorageResult<()> {
const METADATA_LEN: usize = 0x1000;
const METADATA_SIGN_OFFSET: usize = 0x800;
if metadata.len() != METADATA_LEN {
return Err(StorageError::CustomError);
}
let version = parse_metadata_version(metadata);
if version < upgrade_locations.running_firmware_version() {
return Err(StorageError::CustomError);
}
let metadata_address = LittleEndian::read_u32(&metadata[METADATA_SIGN_OFFSET + 8..][..4]);
if metadata_address as usize != upgrade_locations.partition_address() {
return Err(StorageError::CustomError);
@@ -396,6 +429,11 @@ fn parse_metadata(
Ok(())
}
/// Parses the metadata, returns the firmware version.
fn parse_metadata_version(data: &[u8]) -> u64 {
LittleEndian::read_u64(&data[METADATA_SIGN_OFFSET..][..8])
}
/// Verifies the signature over the given hash.
///
/// The public key is COSE encoded, and the hash is a SHA256.