Skip to content

[manifest] Add a Manager type for managing manifest updates #67

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
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
177 changes: 177 additions & 0 deletions src/manifest/manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

use core::marker::PhantomData;

use crate::crypto::rsa;
use crate::crypto::sha256;
use crate::hardware::flash::Flash;
use crate::manifest::provenance;
use crate::manifest::Container;
use crate::manifest::Error;
use crate::manifest::Parse;
use crate::manifest::ValidationTime;
use crate::mem::Arena;
use crate::mem::MapMut;

/// A manifest manager, responsible for handling loading and updates of a
/// [`Manifest`] in flash.
///
/// A `Manager` tracks the flash location that is the long-term storage for a
/// manifest, as well as the resource that the Manager guards, if any. For
/// example, this would be the remote flash storage (containing platform
/// firmware) that a PFM describes.
pub struct Manager<'f, Manifest, Flash, Arena, Provenance = provenance::Signed>
where
Manifest: Parse<'f, Flash, Provenance>,
Arena: crate::mem::Arena,
{
manifest: MapMut<
'f,
Flash,
<Manifest as Parse<'f, Flash, Provenance>>::Parsed,
Arena,
>,
guarded: Manifest::Guarded,
_ph: PhantomData<fn() -> Provenance>,
}

impl<'f, M, F, A, P> Manager<'f, M, F, A, P>
where
A: Arena,
M: Parse<'f, F, P>,
{
/// Creates a new `Manager` to track a manifest at `flash`.
///
/// `arena` is used as auxiliary storage for parsing the manifest out of
/// `flash`, as necessary; `guarded` is the resource that the [`Manifest`]
/// type guards.
pub fn new(
flash: &'f mut F,
arena: &'f mut A,
guarded: M::Guarded,
) -> Self {
Self {
manifest: MapMut::with_arena(flash, arena),
guarded,
_ph: PhantomData,
}
}

/// Returns a reference to the cached parsed manifest value.
///
/// This function will return `None` until either [`Manifest::init()`]
/// or [`Manifest::activate()`] has been called.
pub fn manifest(&self) -> Option<&M::Parsed> {
self.manifest.mapped().ok()
}

/// Explicitly runs the managed manifest's validation routine, as if it were
/// being run at `when` time.
///
/// This function returns `Ok(())` without doing anything if not manifest
/// has been parsed yet.
pub fn validate(&self, when: ValidationTime) -> Result<(), Error> {
let manifest = match self.manifest() {
Some(m) => m,
None => return Ok(()),
};
M::validate(manifest, when, &self.guarded)
}
}

impl<'f, M, F, A> Manager<'f, M, F, A, provenance::Signed>
where
M: Parse<'f, F, provenance::Signed>,
F: Flash,
A: Arena,
{
/// Triggers startup initialization for this `Manager`.
///
/// In effect, this function parses and verifies the manifest in the flash
/// region that backs this `Manager`, and then runs [`Parse::validate()`]
/// on the resulting structure.
///
/// The `sha`, `rsa`, and `verify_arena` arguments are as those in
/// [`Container::parse_and_verify()`].
pub fn startup(
&mut self,
sha: &impl sha256::Builder,
rsa: &mut impl rsa::Engine,
verify_arena: &impl Arena,
) -> Result<&M::Parsed, Error> {
let manifest = self.manifest.try_map(|flash, arena| {
let container = Container::parse_and_verify(
flash,
sha,
rsa,
arena,
verify_arena,
)?;
M::parse(container)
})?;
M::validate(manifest, ValidationTime::Startup, &self.guarded)?;
Ok(manifest)
}

/// Activates a pending manifest located at `new_manifest`.
///
/// This function parses a manifest out of `new_manifest`, runs
/// [`Parse::validate()`] on it, and then writes it to the backing
/// flash for this `Manager`. It then re-parses and re-verifies the
/// newly activated manifest.
///
/// This function can also be used to provision a new manifest without
/// looking at the existing manifest in flash; it is sufficient that the
/// new manifest be appropriately signed and valid (per
/// [`Parse::validate()`]).
///
/// The `sha`, `rsa`, `toc_arena` and `verify_arena` arguments are as those
/// in [`Container::parse_and_verify()`].
pub fn activate<'f2, F2: Flash>(
&mut self,
new_manifest: &'f2 F2,
sha: &impl sha256::Builder,
rsa: &mut impl rsa::Engine,
toc_arena: &'f2 impl Arena,
verify_arena: &impl Arena,
) -> Result<&<M as Parse<'f, F, provenance::Signed>>::Parsed, Error>
where
M: Parse<'f2, F2, provenance::Signed>,
{
// First, parse and verify the signature of the new manifest.
let container = Container::parse_and_verify(
new_manifest,
sha,
rsa,
toc_arena,
verify_arena,
)?;
let new_parsed = M::parse(container)?;

// Then, validate that it the guarded resource is consistent with this
// manifest type.
<M as Parse<'f2, F2, _>>::validate(
&new_parsed,
ValidationTime::Activation,
&self.guarded,
)?;

// Finally copy it into its new home and replace `self.manifest` with
// it.
<M as Parse<'f2, F2, _>>::copy_to(&new_parsed, self.manifest.unmap())?;
let manifest = self.manifest.try_map(|flash, arena| {
let container = Container::parse_and_verify(
flash,
sha,
rsa,
arena,
verify_arena,
)?;
M::parse(container)
})?;

Ok(manifest)
}
}
14 changes: 12 additions & 2 deletions src/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ pub use container::Metadata;
pub use container::Toc;
pub use container::TocEntry;

mod manager;
pub use manager::Manager;

#[cfg(feature = "std")]
pub mod owned;
pub mod pfm;
Expand Down Expand Up @@ -245,6 +248,15 @@ pub trait Manifest: Sized {
/// Manticore. All manifest elements must be future-compatible, so knowing
/// a "maximum version" is not necessary.
fn min_version(ty: Self::ElementType) -> u8;

/// The type of data this manifest guards.
///
/// For example, this might be the region of flash that a PFM describes.
///
/// See [`Parse::validate()`]. This type is here, rather than in [`Parse`],
/// because it cannot depend on any parse-time parameters. This allows
/// different parsed manifest to validate against the same guarded value.
type Guarded;
}

/// A moment in which a [`Parse`]able manifest is validated.
Expand Down Expand Up @@ -288,8 +300,6 @@ pub trait Parse<'f, Flash, Provenance>: Manifest {
dest: &mut F,
) -> Result<(), Error>;

/// The type of data this manifest guards.
type Guarded;
/// Validates that `manifest` is "valid"; that is, whatever state of
/// the system this manifest protects is consistent with the manifest's
/// expectation.
Expand Down
3 changes: 2 additions & 1 deletion src/manifest/pfm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ impl Manifest for Pfm {
fn min_version(_: ElementType) -> u8 {
0
}

type Guarded = ();
}

impl<'f, F: 'f + Flash, P> Parse<'f, F, P> for Pfm {
Expand Down Expand Up @@ -123,7 +125,6 @@ impl<'f, F: 'f + Flash, P> Parse<'f, F, P> for Pfm {
Ok(())
}

type Guarded = ();
fn validate(
_manifest: &Self::Parsed,
_when: ValidationTime,
Expand Down
12 changes: 12 additions & 0 deletions src/mem/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ use crate::mem::align_to;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OutOfMemory;

impl OutOfMemory {
/// Returns a static, mutable reference to an `OutOfMemory`.
///
/// This function is the moral equivalent of `&mut []`; `OutOfMemory`
/// holds no data, so any non-null pointer is a suitable reference to
/// `OutOfMemory`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is technically incorrect (the pointer also needs to be aligned, and the alignment isn't guaranteed to be 1). It can probably be shortened to "This function is similar to &mut [], as OutOfMemory holds no data.".

pub fn static_mut() -> &'static mut Self {
// SAFETY: See the note below about zero-length slices.
unsafe { &mut *(mem::align_of::<Self>() as *mut Self) }
}
}

unsafe impl Arena for OutOfMemory {
fn alloc_aligned(
&self,
Expand Down
3 changes: 3 additions & 0 deletions src/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
mod arena;
pub use arena::*;

mod self_ref;
pub use self_ref::MapMut;

pub mod cow;

/// Aligns the given address to the alignment for the given type.
Expand Down
132 changes: 132 additions & 0 deletions src/mem/self_ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

//! Self-referential helpers.

#![allow(unsafe_code)]

use core::marker::PhantomData;
use core::ptr::NonNull;

use crate::mem::Arena;
use crate::mem::OutOfMemory;

/// A mutable reference that can be "reversibly mapped".
///
/// A `MapMut` holds a mutable reference to a value of type `Base`; this
/// reference can be consumed to create a value of type `Derived`.
///
/// An arena may also be included; this arena can be used to construct
/// the mapped value, and it will be reset whenever the value is
/// "unmapped".
#[derive(Debug)]
pub struct MapMut<'a, Base, Derived, A: Arena = OutOfMemory> {
// Invariant: if `derived` is None, `base` can safely be dereferenced
// without violating aliasing.
base: NonNull<Base>,
arena: NonNull<A>,
derived: Option<Derived>,
_ph: PhantomData<&'a mut (Base, A)>,
}

impl<'a, B, D> MapMut<'a, B, D> {
/// Create a new `MapMut` wrapping `base`.
pub fn new(base: &'a mut B) -> Self {
Self::with_arena(base, OutOfMemory::static_mut())
}
}

impl<'a, B, D, A: Arena> MapMut<'a, B, D, A> {
/// Create a new `MapMut` wrapping `base` with the given `arena`.
pub fn with_arena(base: &'a mut B, arena: &'a mut A) -> Self {
Self {
base: NonNull::from(base),
arena: NonNull::from(arena),
derived: None,
_ph: PhantomData,
}
}

/// Returns a reference to the mapped value.
///
/// If the value has not currently been mapped, a reference to the base
/// type is returned in an `Err`, instead.
pub fn mapped(&self) -> Result<&D, &B> {
match &self.derived {
Some(d) => Ok(d),
// SAFETY: when `derived` is `None`, this reference is unique.
None => unsafe { Err(self.base.as_ref()) },
}
}

/// Returns a mutable reference to the mapped value.
///
/// If the value has not currently been mapped, a reference to the base
/// type is returned in an `Err`, instead.
pub fn mapped_mut(&mut self) -> Result<&mut D, &mut B> {
match &mut self.derived {
Some(d) => Ok(d),
// SAFETY: when `derived` is `None`, this reference is unique.
None => unsafe { Err(self.base.as_mut()) },
}
}

/// Maps the wrapped base value using `f`, returning a reference to the
/// newly mapped value.
///
/// Unlike other functions on this type, `f` receives a reference by
/// the absolute lifetime `'a`, allowing the type `D` to depend on it.
pub fn map(&mut self, f: impl FnOnce(&'a mut B, &'a mut A) -> D) -> &mut D {
// We could do .unwrap(), but that would probably emit an extra bounds
// check...
match self.try_map::<core::convert::Infallible, _>(|b, a| Ok(f(b, a))) {
Ok(x) => x,
Err(e) => match e {}, // My kingdom for a never-type.
}
}

/// Maps the wrapped base value using `f`, returning a reference to the
/// newly mapped value. The mapping function is permitted to fail.
///
/// Unlike other functions on this type, `f` receives a reference by
/// the absolute lifetime `'a`, allowing the type `D` to depend on it.
pub fn try_map<E, F>(&mut self, f: F) -> Result<&mut D, E>
where
F: FnOnce(&'a mut B, &'a mut A) -> Result<D, E>,
{
// SAFETY: Ensure that any references to `self.base` are destroyed
// before we convert it to a reference.
self.unmap();
self.derived = Some(f(
// NOTE: We cannot use .as_mut(), since that would use the
// lifetime of &mut self; we want to manually dereference it
// to make the lifetime "unbound"; this is fine, because the
// lifetime it will pick is `'a`.
unsafe { &mut *self.base.as_ptr() },
unsafe { &mut *self.arena.as_ptr() },
)?);
Ok(self.derived.as_mut().unwrap())
}

/// Destroys the derived value, if it exists, and returns a reference to
/// the base value.
pub fn unmap(&mut self) -> &mut B {
// SAFETY: Ensure that any references to `self.base` are destroyed
// before we convert it to a reference.
self.derived = None;
unsafe {
self.arena.as_mut().reset();
self.base.as_mut()
}
}
}

impl<B, D, A: Arena> Drop for MapMut<'_, B, D, A> {
fn drop(&mut self) {
// Trigger an arena reset.
self.unmap();
// No need to destroy the base or the arena, since we don't actually
// own them.
}
}