diff --git a/src/manifest/manager.rs b/src/manifest/manager.rs new file mode 100644 index 00000000..f980f1b8 --- /dev/null +++ b/src/manifest/manager.rs @@ -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, + >::Parsed, + Arena, + >, + guarded: Manifest::Guarded, + _ph: PhantomData 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<&>::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. + >::validate( + &new_parsed, + ValidationTime::Activation, + &self.guarded, + )?; + + // Finally copy it into its new home and replace `self.manifest` with + // it. + >::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) + } +} diff --git a/src/manifest/mod.rs b/src/manifest/mod.rs index b8963322..228200d0 100644 --- a/src/manifest/mod.rs +++ b/src/manifest/mod.rs @@ -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; @@ -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. @@ -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. diff --git a/src/manifest/pfm.rs b/src/manifest/pfm.rs index 01cfaaef..81ea7be2 100644 --- a/src/manifest/pfm.rs +++ b/src/manifest/pfm.rs @@ -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 { @@ -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, diff --git a/src/mem/arena.rs b/src/mem/arena.rs index 19312e15..aabc0f46 100644 --- a/src/mem/arena.rs +++ b/src/mem/arena.rs @@ -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`. + pub fn static_mut() -> &'static mut Self { + // SAFETY: See the note below about zero-length slices. + unsafe { &mut *(mem::align_of::() as *mut Self) } + } +} + unsafe impl Arena for OutOfMemory { fn alloc_aligned( &self, diff --git a/src/mem/mod.rs b/src/mem/mod.rs index 61bed163..efbeca62 100644 --- a/src/mem/mod.rs +++ b/src/mem/mod.rs @@ -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. diff --git a/src/mem/self_ref.rs b/src/mem/self_ref.rs new file mode 100644 index 00000000..bad65b81 --- /dev/null +++ b/src/mem/self_ref.rs @@ -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, + arena: NonNull, + derived: Option, + _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::(|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(&mut self, f: F) -> Result<&mut D, E> + where + F: FnOnce(&'a mut B, &'a mut A) -> Result, + { + // 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 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. + } +}