summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDov Shlachter <dovs@google.com>2024-01-30 23:17:16 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-01-30 23:17:16 +0000
commit496dc41ad6b6a0a57710c7051c89a629ecf552ba (patch)
tree570ca40be9ec068b7d54ee948ad027073e013a13
parent638afc1a9e05d9e2cf0be5500e35f48e8b7e42d6 (diff)
parentf7db52939848fbaf0edeacfc1c60fcc7dba233ad (diff)
downloadlibbootloader-496dc41ad6b6a0a57710c7051c89a629ecf552ba.tar.gz
Merge "Integrate GBL A/B boot slot API with rest of GBL" into main
-rw-r--r--gbl/libgbl/Android.bp68
-rw-r--r--gbl/libgbl/src/error.rs3
-rw-r--r--gbl/libgbl/src/lib.rs170
-rw-r--r--gbl/libgbl/src/ops.rs12
-rw-r--r--gbl/libgbl/src/slots.rs13
-rw-r--r--gbl/libgbl/src/slots/fuchsia.rs (renamed from gbl/libgbl/src/fuchsia.rs)166
-rw-r--r--gbl/libgbl/tests/nostd.rs10
7 files changed, 259 insertions, 183 deletions
diff --git a/gbl/libgbl/Android.bp b/gbl/libgbl/Android.bp
index f0bb406..1f1491f 100644
--- a/gbl/libgbl/Android.bp
+++ b/gbl/libgbl/Android.bp
@@ -20,28 +20,40 @@ rust_defaults {
name: "libgbl_defaults",
srcs: ["src/lib.rs"],
rustlibs: [
+ "libavb_rs",
"libbitflags",
+ "libcrc32fast",
+ "liblazy_static",
+ "libring",
+ "libspin",
"libstatic_assertions",
+ "libzbi",
+ "libzerocopy",
// "sw_digest",
],
}
+rust_defaults {
+ name: "libgbl_defaults_nostd",
+ srcs: ["src/lib.rs"],
+ rlibs: [
+ "libavb_rs_nostd",
+ "libcrc32fast",
+ "liblazy_static_nostd",
+ "libring_nostd",
+ "libspin_nostd",
+ "libzbi_nostd",
+ "libzerocopy_nostd",
+ ],
+ no_stdlibs: true,
+}
+
// GBL Rust library.
rust_library {
name: "libgbl",
host_supported: true,
crate_name: "gbl",
defaults: ["libgbl_defaults"],
- rustlibs: [
- "libzbi",
- "libavb_rs",
- "libring",
- "libspin",
- "libzbi",
- "libzerocopy",
- "libcrc32fast",
- "liblazy_static",
- ],
}
// Defining rlib here because we want to build with [no_std], but
@@ -51,18 +63,7 @@ rust_library {
rust_library_rlib {
name: "libgbl_nostd",
crate_name: "gbl",
- defaults: ["libgbl_defaults"],
- rlibs: [
- "libzbi_nostd",
- "libavb_rs_nostd",
- "libring_nostd",
- "libspin_nostd",
- "libzbi_nostd",
- "libzerocopy_nostd",
- "libcrc32fast",
- "liblazy_static_nostd",
- ],
- no_stdlibs: true,
+ defaults: ["libgbl_defaults_nostd"],
prefer_rlib: true,
}
@@ -71,14 +72,18 @@ rust_defaults {
name: "libgbl_test_defaults",
defaults: ["libgbl_defaults"],
rustlibs: [
- "libzbi",
- "libavb_rs",
+ "libhex",
"libitertools",
+ "libavb_rs",
+ "libbitflags",
+ "libcrc32fast",
+ "liblazy_static",
"libring",
- "libhex",
"libspin",
"libstatic_assertions",
- "liblazy_static",
+ "libzbi",
+ "libzerocopy",
+
],
features: ["alloc"],
}
@@ -90,13 +95,8 @@ rust_test {
}
rust_test_host {
- name: "libgbl_slot_test",
- srcs: ["src/fuchsia.rs"],
- rustlibs: [
- "libzbi",
- "libcrc32fast",
- "libzerocopy",
- ],
+ name: "libgbl_unit_tests",
+ defaults: ["libgbl_test_defaults"],
}
// Test building with libgbl in a [no_std] environment.
@@ -108,8 +108,8 @@ rust_binary {
name: "libgbl_test_nostd",
srcs: ["tests/nostd.rs"],
rustlibs: [
- "libgbl_nostd",
"libbuddy_system_allocator",
+ "libgbl_nostd",
"liblazy_static_nostd",
],
// panic=abort to satisfy the eh_personality compile requirement.
diff --git a/gbl/libgbl/src/error.rs b/gbl/libgbl/src/error.rs
index ef023d3..227bf0a 100644
--- a/gbl/libgbl/src/error.rs
+++ b/gbl/libgbl/src/error.rs
@@ -28,6 +28,8 @@ pub enum Error {
MissingImage,
/// Functionality is not implemented
NotImplemented,
+ /// Some combination of parameters and global state prohibits the operation
+ OperationProhibited,
}
// Unfortunately thiserror is not available in `no_std` world.
@@ -38,6 +40,7 @@ impl Display for Error {
Error::Error => "Generic error",
Error::MissingImage => "Missing image required to boot system",
Error::NotImplemented => "Functionality is not implemented",
+ Error::OperationProhibited => "Operation is prohibited",
};
write!(f, "{str}")
}
diff --git a/gbl/libgbl/src/lib.rs b/gbl/libgbl/src/lib.rs
index 774bb6f..5d0d15b 100644
--- a/gbl/libgbl/src/lib.rs
+++ b/gbl/libgbl/src/lib.rs
@@ -33,9 +33,9 @@ extern crate lazy_static;
extern crate spin;
extern crate zbi;
-use core::fmt::{Debug, Display, Formatter};
+use core::fmt::Debug;
use lazy_static::lazy_static;
-use spin::Mutex;
+use spin::{Mutex, MutexGuard};
pub mod boot_mode;
pub mod boot_reason;
@@ -47,6 +47,8 @@ pub mod ops;
/// querying and modifying slotted boot behavior.
pub mod slots;
+use slots::{BootTarget, BootToken, Cursor, OneShot};
+
#[cfg(feature = "sw_digest")]
pub mod sw_digest;
@@ -164,27 +166,6 @@ pub fn get_images<'a: 'b, 'b: 'c, 'c>(
(boot_image, init_boot_image, vendor_boot_image, partitions_ram_map)
}
-// TODO: b/312607649 - Boot slot placeholder
-#[derive(Debug, PartialEq)]
-#[repr(u32)]
-/// Boot slots used by ABR model
-pub enum BootSlot {
- /// '_a'
- A = 0,
- /// '_b'
- B = 1,
-}
-
-impl Display for BootSlot {
- fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
- let str = match self {
- BootSlot::A => "a",
- BootSlot::B => "b",
- };
- write!(f, "{str}")
- }
-}
-
// TODO: b/312608785 - helper function that would track slices don't overlap
// [core::slice::from_raw_parts_mut] is not stable for `const` so we have to use `lazy_static` for
// initialization of constants.
@@ -225,12 +206,13 @@ lazy_static! {
};
}
+static BOOT_TOKEN: Mutex<Option<BootToken>> = Mutex::new(Some(BootToken(())));
+
#[derive(Debug)]
/// GBL object that provides implementation of helpers for boot process.
pub struct Gbl<'a, D, C> {
ops: &'a mut dyn GblOps<D, C>,
image_verification: bool,
- boot_token: Option<slots::BootToken>,
}
impl<'a, D, C> Gbl<'a, D, C>
@@ -242,14 +224,14 @@ where
where
'b: 'a,
{
- Gbl { ops, image_verification: true, boot_token: Some(slots::BootToken(())) }
+ Gbl { ops, image_verification: true }
}
fn new_no_verification<'b>(ops: &'b mut impl GblOps<D, C>) -> Gbl<'a, D, C>
where
'b: 'a,
{
- Gbl { ops, image_verification: false, boot_token: Some(slots::BootToken(())) }
+ Gbl { ops, image_verification: false }
}
}
@@ -258,36 +240,6 @@ where
D: Digest,
C: Context<D>,
{
- /// Get Boot Mode
- ///
- /// # Returns
- ///
- /// * `Ok(())` - on success
- /// * `Err(Error)` - on failure
- pub fn get_boot_mode(&self) -> Result<BootMode> {
- unimplemented!();
- }
-
- /// Reset Boot Mode (strictly reset generic android struct - not vendor specific)
- ///
- /// # Returns
- ///
- /// * `Ok(())` - on success
- /// * `Err(Error)` - on failure
- pub fn reset_boot_mode(&mut self) -> Result<()> {
- unimplemented!();
- }
-
- /// Get Boot Slot
- ///
- /// # Returns
- ///
- /// * `Ok(&str)` - Slot Suffix as [BootSolt] enum
- /// * `Err(Error)` - on failure
- pub fn get_boot_slot(&self) -> Result<BootSlot> {
- unimplemented!();
- }
-
/// Verify + Load Image Into memory
///
/// Load from disk, validate with AVB
@@ -295,7 +247,7 @@ where
/// # Arguments
/// * `partitions_ram_map` - Partitions to verify with optional address to load image to.
/// * `avb_verification_flags` - AVB verification flags/options
- /// * `boot_slot` - Optional Boot Slot
+ /// * `boot_target` - [Optional] Boot Target
///
/// # Returns
///
@@ -306,7 +258,7 @@ where
&self,
partitions_ram_map: &mut [PartitionRamMap],
avb_verification_flags: AvbVerificationFlags,
- boot_slot: Option<&BootSlot>,
+ boot_target: Option<BootTarget>,
) -> Result<&'b mut [AvbDescriptor]>
where
'a: 'b,
@@ -319,6 +271,20 @@ where
unimplemented!("partition loading and verification");
}
+ /// Load Slot Manager Interface
+ ///
+ /// The default implementation loads from the `durable_boot` partition
+ /// and writes changes back on the destruction of the cursor.
+ ///
+ /// # Returns
+ ///
+ /// * `Ok(Cursor)` - Cursor object that manages a Manager
+ /// * `Err(Error)` - on failure
+ pub fn load_slot_interface(&mut self) -> Result<Cursor> {
+ let boot_token = BOOT_TOKEN.lock().take().ok_or(Error::OperationProhibited)?;
+ self.ops.load_slot_interface(boot_token).map_err(|_| Error::OperationProhibited)
+ }
+
/// Info Load
///
/// Unpack boot image in RAM
@@ -326,7 +292,7 @@ where
/// # Arguments
/// * `boot_image_buffer` - Buffer that contains (Optionally Verified) Boot Image
/// * `boot_mode` - Boot Mode
- /// * `boot_slot` - [Optional] Boot Slot
+ /// * `boot_target` - [Optional] Boot Target
///
/// # Returns
///
@@ -336,8 +302,7 @@ where
pub fn unpack_boot_image(
&self,
boot_image_buffer: &BootImage,
- boot_mode: &BootMode,
- boot_slot: Option<&BootSlot>,
+ boot_target: Option<BootTarget>,
) -> Result<InfoStruct> {
unimplemented!();
}
@@ -388,7 +353,7 @@ where
info: &InfoStruct,
vendor_boot_image: &VendorBootImage,
init_boot_image: &InitBootImage,
- ramdisk_load_buffer: &mut [u8],
+ ramdisk: &mut Ramdisk,
bootconfig_load_buffer: &mut [u8],
) -> Result<&'static str> {
unimplemented!();
@@ -422,6 +387,7 @@ where
/// * `ramdisk_bootconfi_load_buffer` - Concatenated Ramdisk, (Bootconfig if present) Load
/// buffer
/// * `dtb_load_buffer` - DTB Load buffer
+ /// * `boot_token` - Consumable boot token
///
/// # Returns
///
@@ -433,6 +399,7 @@ where
kernel_load_buffer: KernelImage,
ramdisk_load_buffer: Ramdisk,
dtb_load_buffer: Dtb,
+ boot_token: BootToken,
) -> Result<()> {
unimplemented!();
}
@@ -445,6 +412,7 @@ where
/// # Arguments
/// * `partitions_ram_map` - Partitions to verify and optional address for them to be loaded.
/// * `avb_verification_flags` - AVB verification flags/options
+ /// * `slot_cursor` - Cursor object that manages interactions with boot slot management
///
/// # Returns
///
@@ -455,30 +423,60 @@ where
&mut self,
partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>],
avb_verification_flags: AvbVerificationFlags,
+ slot_cursor: Cursor,
) -> Result<()> {
- let mut boot_mode = self.get_boot_mode()?;
- self.reset_boot_mode()?;
-
- // Handle fastboot mode
- if boot_mode == BootMode::Bootloader {
- let res = self.ops.do_fastboot();
- match res {
- // After `fastboot continue` we need to requery mode and continue usual process.
- Ok(_) => {
- boot_mode = self.get_boot_mode()?;
- }
- // Fastboot is not available so we try continue
+ // TODO(dovs):
+ // * Change the receiver of ops.load_slot_interface to be &mut self
+ // * Add partition write capabilites to slot manager
+ let mut dtb_load_buffer = DTB.lock();
+ let dtb = Dtb(&mut dtb_load_buffer);
+ let mut ramdisk_load_buffer = RAMDISK.lock();
+ let mut kernel_load_buffer = KERNEL_IMAGE.lock();
+ let mut ramdisk = Ramdisk(&mut ramdisk_load_buffer);
+
+ // Call the inner method which consumes the cursor
+ // in order to properly manager cursor lifetime
+ // and cleanup.
+ let (kernel_image, token) = self.lvb_inner(
+ &mut ramdisk,
+ &mut kernel_load_buffer,
+ partitions_ram_map,
+ avb_verification_flags,
+ slot_cursor,
+ )?;
+
+ self.kernel_jump(kernel_image, ramdisk, dtb, token)
+ }
+
+ fn lvb_inner<'b: 'c, 'c, 'd: 'b>(
+ &self,
+ ramdisk: &mut Ramdisk,
+ kernel_load_buffer: &mut MutexGuard<&'static mut [u8]>,
+ partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>],
+ avb_verification_flags: AvbVerificationFlags,
+ slot_cursor: Cursor,
+ ) -> Result<(KernelImage, BootToken)> {
+ let mut oneshot_status = slot_cursor.ctx.get_oneshot_status();
+ slot_cursor.ctx.clear_oneshot_status();
+
+ if oneshot_status == Some(OneShot::Bootloader) {
+ match self.ops.do_fastboot(&slot_cursor) {
+ Ok(_) => oneshot_status = slot_cursor.ctx.get_oneshot_status(),
Err(Error::NotImplemented) => (),
- // Fail on any other error
Err(e) => return Err(e),
}
}
- let boot_slot = self.get_boot_slot()?;
+ let boot_target = match oneshot_status {
+ None | Some(OneShot::Bootloader) => slot_cursor.ctx.get_boot_target(),
+ Some(OneShot::Continue(target)) => target,
+ };
+
+ // TODO b/312608785: handle the failure by marking a failed boot attempt
let avb_descriptors = self.load_and_verify_image(
partitions_ram_map,
AvbVerificationFlags(0),
- Some(&boot_slot),
+ Some(boot_target),
)?;
let (boot_image, init_boot_image, vendor_boot_image, _) =
@@ -487,27 +485,27 @@ where
let vendor_boot_image = vendor_boot_image.ok_or(Error::MissingImage)?;
let init_boot_image = init_boot_image.ok_or(Error::MissingImage)?;
- let info_struct = self.unpack_boot_image(&boot_image, &boot_mode, Some(&boot_slot))?;
+ let info_struct = self.unpack_boot_image(&boot_image, Some(boot_target))?;
- let mut kernel_load_buffer = KERNEL_IMAGE.lock();
- let kernel_image = self.kernel_load(&info_struct, boot_image, &mut kernel_load_buffer)?;
+ let kernel_image = self.kernel_load(&info_struct, boot_image, kernel_load_buffer)?;
- let mut ramdisk_load_buffer = RAMDISK.lock();
let mut bootconfig_load_buffer = BOOTCONFIG.lock();
let cmd_line = self.ramdisk_bootconfig_load(
&info_struct,
&vendor_boot_image,
&init_boot_image,
- &mut ramdisk_load_buffer,
+ ramdisk,
&mut bootconfig_load_buffer,
)?;
self.dtb_update_and_load(&info_struct, vendor_boot_image)?;
- let mut dtb_load_buffer = DTB.lock();
- let dtb = Dtb(&mut dtb_load_buffer);
- let ramdisk = Ramdisk(&mut ramdisk_load_buffer);
- self.kernel_jump(kernel_image, ramdisk, dtb)
+ let token = slot_cursor
+ .ctx
+ .mark_boot_attempt(boot_target)
+ .map_err(|_| Error::OperationProhibited)?;
+
+ Ok((kernel_image, token))
}
}
diff --git a/gbl/libgbl/src/ops.rs b/gbl/libgbl/src/ops.rs
index d7c847c..0dd924b 100644
--- a/gbl/libgbl/src/ops.rs
+++ b/gbl/libgbl/src/ops.rs
@@ -26,6 +26,8 @@ use alloc::ffi::CString;
use avb::{IoError, Ops, PublicKeyForPartitionInfo};
use core::{ffi::CStr, fmt::Debug, ptr::NonNull};
+use super::slots;
+
/// TODO: b/312607649 - Placeholder. Use result type from `avb` when it is stable and public
pub type AvbResult<T> = core::result::Result<T, IoError>;
@@ -55,15 +57,19 @@ pub trait GblOps<D: Digest, C: Context<D>>: Ops + Debug {
ctx.update(data);
ctx.finish()
}
+
/// Callback for when fastboot mode is requested.
// Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812
- fn do_fastboot(&self) -> Result<()> {
+ fn do_fastboot(&self, cursor: &slots::Cursor) -> Result<()> {
Err(Error::NotImplemented)
}
/// TODO: b/312607649 - placeholder interface for Gbl specific callbacks that uses alloc.
#[cfg(feature = "alloc")]
fn gbl_alloc_extra_action(&mut self, s: &str) -> Result<()>;
+
+ /// Load and initialize a slot manager and return a cursor over the manager on success.
+ fn load_slot_interface(&mut self, boot_token: slots::BootToken) -> Result<slots::Cursor>;
}
/// Default [GblOps] implementation that returns errors and does nothing.
@@ -112,6 +118,10 @@ where
D: Digest,
C: Context<D>,
{
+ fn load_slot_interface(&mut self, boot_token: slots::BootToken) -> Result<slots::Cursor> {
+ Err(Error::OperationProhibited)
+ }
+
#[cfg(feature = "alloc")]
fn gbl_alloc_extra_action(&mut self, s: &str) -> Result<()> {
let _c_string = CString::new(s);
diff --git a/gbl/libgbl/src/slots.rs b/gbl/libgbl/src/slots.rs
index cdf40ae..2456b38 100644
--- a/gbl/libgbl/src/slots.rs
+++ b/gbl/libgbl/src/slots.rs
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+/// Export the default implementation
+pub mod fuchsia;
+
/// A type safe container for describing the number of retries a slot has left
/// before it becomes unbootable.
/// Slot tries can only be compared to, assigned to, or assigned from other
@@ -246,7 +249,7 @@ impl<'a> Iterator for SlotIterator<'a> {
///
/// if let Some(Continue(oneshot_target)) = manager.get_oneshot_status() {
/// let regular_target = manager.get_boot_target();
-/// if regular_target == oneshot_target() {
+/// if regular_target == oneshot_target {
/// println!("The oneshot and regular boot targets are equal");
/// } else {
/// println!("The oneshot and regular boot targets are NOT equal, which is VALID");
@@ -285,7 +288,7 @@ pub trait Manager: private::SlotGet {
/// the slot last set active cannot be Recovery.
fn get_slot_last_set_active(&self) -> Slot;
- /// Given a slot suffix, updates internal metadata (usually the retry count)
+ /// Given a boot target, updates internal metadata (usually the retry count)
/// indicating that the system will have tried to boot the slot.
/// Returns Ok(BootToken) on success to verify that boot attempt metadata has been updated.
/// The token must be consumed by `kernel_jump`.
@@ -297,7 +300,11 @@ pub trait Manager: private::SlotGet {
/// Note: mark_boot_attempt is NOT idempotent.
/// It is intended to be called EXACTLY once,
/// right before jumping into the kernel.
- fn mark_boot_attempt(&mut self, slot_suffix: Suffix) -> Result<BootToken, Error>;
+ ///
+ /// Note: mark_boot_attempt takes a BootTarget to facilitate generating
+ /// the boot token when booting to recovery. If the boot target is recovery,
+ /// then implementations SHOULD NOT update internal metadata.
+ fn mark_boot_attempt(&mut self, boot_target: BootTarget) -> Result<BootToken, Error>;
/// Attempts to set the active slot.
///
diff --git a/gbl/libgbl/src/fuchsia.rs b/gbl/libgbl/src/slots/fuchsia.rs
index 8a046ee..0ada37e 100644
--- a/gbl/libgbl/src/fuchsia.rs
+++ b/gbl/libgbl/src/slots/fuchsia.rs
@@ -12,47 +12,49 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#![cfg_attr(not(test), no_std)]
-pub mod slots;
extern crate bitflags;
extern crate crc32fast;
extern crate zerocopy;
-use bitflags::bitflags;
-use core::mem::size_of;
-use crc32fast::Hasher;
-use slots::{
+use super::{
BootTarget, BootToken, Bootability, Error, Manager, OneShot, Slot, SlotIterator, Suffix,
UnbootableReason,
};
+use bitflags::bitflags;
+use core::mem::size_of;
+use crc32fast::Hasher;
use zerocopy::byteorder::big_endian::U32 as BigEndianU32;
use zerocopy::{AsBytes, ByteSlice, FromBytes, FromZeroes, Ref};
/// Custom error type
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum AbrSlotError {
+ /// The magic number field was corrupted
BadMagic,
+ /// The major version of the structure is unsupported
BadVersion,
+ /// The struct crc check failed
BadCrc,
+ /// The deserialization buffer is too small
BufferTooSmall,
}
#[derive(Debug, PartialEq, Eq)]
-pub enum CacheStatus {
+enum CacheStatus {
Clean,
Dirty,
}
-pub const DEFAULT_PRIORITY: u8 = 15;
-pub const DEFAULT_RETRIES: u8 = 7;
+const DEFAULT_PRIORITY: u8 = 15;
+const DEFAULT_RETRIES: u8 = 7;
#[repr(C, packed)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)]
-pub(crate) struct AbrSlotData {
- pub priority: u8,
- pub tries: u8,
- pub successful: u8,
- pub unbootable_reason: u8,
+struct AbrSlotData {
+ priority: u8,
+ tries: u8,
+ successful: u8,
+ unbootable_reason: u8,
}
impl Default for AbrSlotData {
@@ -68,17 +70,23 @@ impl Default for AbrSlotData {
#[repr(C, packed)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)]
-pub struct OneShotFlags(pub u8);
+struct OneShotFlags(u8);
bitflags! {
impl OneShotFlags: u8 {
+ /// No oneshot specified
const NONE = 0;
+ /// Oneshot boot to recovery mode
const RECOVERY = 1 << 0;
+ /// Oneshot boot to fastboot
const BOOTLOADER = 1 << 1;
+ /// Slot mask
const SLOT = 1 << 2;
+ /// Oneshot boot to slot A
const SLOT_A = (1 << 4) | Self::SLOT.bits();
+ /// Oneshot boot to slot B
const SLOT_B = (1 << 5) | Self::SLOT.bits();
}
}
@@ -129,15 +137,15 @@ const ABR_VERSION_MINOR: u8 = 3;
#[repr(C, packed)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, AsBytes, FromBytes, FromZeroes)]
-pub(crate) struct AbrData {
- pub magic: [u8; 4],
- pub version_major: u8,
- pub version_minor: u8,
- pub reserved: [u8; 2],
- pub slot_data: [AbrSlotData; 2],
- pub oneshot_flag: OneShotFlags,
- pub reserved2: [u8; 11],
- pub crc32: BigEndianU32,
+struct AbrData {
+ magic: [u8; 4],
+ version_major: u8,
+ version_minor: u8,
+ reserved: [u8; 2],
+ slot_data: [AbrSlotData; 2],
+ oneshot_flag: OneShotFlags,
+ reserved2: [u8; 11],
+ crc32: BigEndianU32,
}
impl AbrData {
@@ -168,22 +176,32 @@ impl Default for AbrData {
}
#[derive(Debug, PartialEq, Eq)]
-pub(crate) struct SlotBlock {
+/// Default implementation for Manager.
+/// Represents a partition-backed slot control block with two slots, A and B,
+/// a special recovery partition, R, and support for oneshot boot.
+/// Includes an Option<BootToken> to support `mark_boot_attempt`
+/// and a cache status to support lazy write-back on destruction.
+pub struct SlotBlock {
cache_status: CacheStatus,
boot_token: Option<BootToken>,
abr_data: AbrData,
}
impl SlotBlock {
- pub fn get_mut_data(&mut self) -> &mut AbrData {
+ fn get_mut_data(&mut self) -> &mut AbrData {
self.cache_status = CacheStatus::Dirty;
&mut self.abr_data
}
- pub fn get_data(&self) -> &AbrData {
+ fn get_data(&self) -> &AbrData {
&self.abr_data
}
+ /// Attempt to deserialize a slot control block
+ ///
+ /// # Returns
+ /// * `Ok(SlotBlock)` - on success returns a SlotBlock that wraps a copy of the serialized data
+ /// * `Err(AbrSlotError)` - on failure
pub fn deserialize<B: ByteSlice>(
buffer: B,
boot_token: BootToken,
@@ -209,7 +227,7 @@ impl SlotBlock {
})
}
- pub fn new(boot_token: BootToken) -> Self {
+ pub(crate) fn new(boot_token: BootToken) -> Self {
Self { boot_token: Some(boot_token), ..Default::default() }
}
}
@@ -226,7 +244,7 @@ impl Default for SlotBlock {
}
}
-impl slots::private::SlotGet for SlotBlock {
+impl super::private::SlotGet for SlotBlock {
fn get_slot_by_number(&self, number: usize) -> Result<Slot, Error> {
let abr_slot = self.abr_data.slot_data.get(number).ok_or(Error::Other)?;
@@ -293,25 +311,34 @@ impl Manager for SlotBlock {
Ok(())
}
- fn mark_boot_attempt(&mut self, slot_suffix: Suffix) -> Result<BootToken, Error> {
- let (idx, slot) = self.get_index_and_slot_with_suffix(slot_suffix)?;
- let token = self.boot_token.take().ok_or(Error::OperationProhibited)?;
-
- match slot.bootability {
- Bootability::Unbootable(_) => {
- self.boot_token = Some(token);
- Err(Error::OperationProhibited)
+ fn mark_boot_attempt(&mut self, boot_target: BootTarget) -> Result<BootToken, Error> {
+ let target_slot = match boot_target {
+ BootTarget::NormalBoot(slot) => slot,
+ BootTarget::Recovery(Some(_)) => Err(Error::OperationProhibited)?,
+ BootTarget::Recovery(None) => {
+ // Even though boot to recovery does not cause a metadata update,
+ // we still need to gate access to the boot token.
+ return self.boot_token.take().ok_or(Error::OperationProhibited);
}
+ };
+ let (idx, slot) = self.get_index_and_slot_with_suffix(target_slot.suffix)?;
+
+ match slot.bootability {
+ Bootability::Unbootable(_) => Err(Error::OperationProhibited),
Bootability::Retriable(_) => {
let abr_slot = &mut self.get_mut_data().slot_data[idx];
abr_slot.tries -= 1;
if abr_slot.tries == 0 {
abr_slot.unbootable_reason = UnbootableReason::NoMoreTries.into();
}
+ let token = self.boot_token.take().ok_or(Error::OperationProhibited)?;
+ Ok(token)
+ }
+ Bootability::Successful => {
+ let token = self.boot_token.take().ok_or(Error::OperationProhibited)?;
Ok(token)
}
- Bootability::Successful => Ok(token),
}
}
@@ -367,6 +394,8 @@ impl Manager for SlotBlock {
let data = self.get_mut_data();
data.version_minor = ABR_VERSION_MINOR;
data.crc32 = data.calculate_crc32().into();
+
+ // TODO(dovs): write data back to partition
}
}
@@ -467,31 +496,34 @@ mod test {
#[test]
fn test_slot_mark_boot_attempt() {
let mut sb: SlotBlock = Default::default();
- let suffix: Suffix = 'a'.into();
- assert_eq!(sb.mark_boot_attempt(suffix), Ok(BootToken(())));
+ let slot = Slot { suffix: 'a'.into(), ..Default::default() };
+ assert_eq!(sb.mark_boot_attempt(BootTarget::NormalBoot(slot)), Ok(BootToken(())));
assert_eq!(
sb.slots_iter().next().unwrap(),
Slot {
- suffix,
+ suffix: slot.suffix,
priority: DEFAULT_PRIORITY.into(),
bootability: Bootability::Retriable((DEFAULT_RETRIES - 1).into())
}
);
// Make sure we can call exactly once
- assert_eq!(sb.mark_boot_attempt(suffix), Err(Error::OperationProhibited));
+ assert_eq!(
+ sb.mark_boot_attempt(BootTarget::NormalBoot(slot)),
+ Err(Error::OperationProhibited)
+ );
}
#[test]
fn test_slot_mark_boot_attempt_no_more_tries() {
let mut sb: SlotBlock = Default::default();
sb.get_mut_data().slot_data[0].tries = 1;
- let suffix: Suffix = 'a'.into();
- assert_eq!(sb.mark_boot_attempt(suffix), Ok(BootToken(())));
+ let slot = Slot { suffix: 'a'.into(), ..Default::default() };
+ assert_eq!(sb.mark_boot_attempt(BootTarget::NormalBoot(slot)), Ok(BootToken(())));
assert_eq!(
sb.slots_iter().next().unwrap(),
Slot {
- suffix,
+ suffix: slot.suffix,
priority: DEFAULT_PRIORITY.into(),
bootability: Bootability::Unbootable(UnbootableReason::NoMoreTries)
}
@@ -502,28 +534,54 @@ mod test {
fn test_slot_mark_boot_attempt_successful() {
let mut sb: SlotBlock = Default::default();
sb.get_mut_data().slot_data[0].successful = 1;
- let slot = Slot {
+ let target = BootTarget::NormalBoot(Slot {
suffix: 'a'.into(),
priority: DEFAULT_PRIORITY.into(),
bootability: Bootability::Successful,
- };
- assert_eq!(sb.mark_boot_attempt(slot.suffix), Ok(BootToken(())));
- assert_eq!(sb.get_boot_target(), BootTarget::NormalBoot(slot));
+ });
+ assert_eq!(sb.mark_boot_attempt(target), Ok(BootToken(())));
+ assert_eq!(sb.get_boot_target(), target);
}
#[test]
fn test_slot_mark_tried_no_such_slot() {
let mut sb: SlotBlock = Default::default();
- let suffix: Suffix = '$'.into();
- assert_eq!(sb.mark_boot_attempt(suffix), Err(Error::NoSuchSlot(suffix)));
+ let slot = Slot { suffix: '$'.into(), ..Default::default() };
+ assert_eq!(
+ sb.mark_boot_attempt(BootTarget::NormalBoot(slot)),
+ Err(Error::NoSuchSlot(slot.suffix))
+ );
+ }
+
+ #[test]
+ fn test_slot_mark_tried_recovery() {
+ let mut sb: SlotBlock = Default::default();
+ let recovery_tgt = BootTarget::Recovery(None);
+ assert_eq!(sb.mark_boot_attempt(recovery_tgt), Ok(BootToken(())));
+
+ // Make sure a second attempt fails due to the moved boot token
+ assert_eq!(sb.mark_boot_attempt(recovery_tgt), Err(Error::OperationProhibited));
+ }
+
+ #[test]
+ fn test_mark_slot_tried_slotted_recovery() {
+ let mut sb: SlotBlock = Default::default();
+ let slot: Slot = Default::default();
+ assert_eq!(
+ sb.mark_boot_attempt(BootTarget::Recovery(Some(slot))),
+ Err(Error::OperationProhibited)
+ );
}
#[test]
fn test_slot_mark_tried_unbootable() {
let mut sb: SlotBlock = Default::default();
- let suffix: Suffix = 'b'.into();
- assert_eq!(sb.set_slot_unbootable(suffix, UnbootableReason::UserRequested), Ok(()));
- assert_eq!(sb.mark_boot_attempt(suffix), Err(Error::OperationProhibited));
+ let slot = Slot { suffix: 'b'.into(), ..Default::default() };
+ assert_eq!(sb.set_slot_unbootable(slot.suffix, UnbootableReason::UserRequested), Ok(()));
+ assert_eq!(
+ sb.mark_boot_attempt(BootTarget::NormalBoot(slot)),
+ Err(Error::OperationProhibited)
+ );
}
macro_rules! set_unbootable_tests {
diff --git a/gbl/libgbl/tests/nostd.rs b/gbl/libgbl/tests/nostd.rs
index fe3a99f..157799b 100644
--- a/gbl/libgbl/tests/nostd.rs
+++ b/gbl/libgbl/tests/nostd.rs
@@ -21,7 +21,7 @@
#![no_main]
#![no_std]
-use core::panic::PanicInfo;
+// use core::panic::PanicInfo;
use gbl as _;
@@ -33,10 +33,10 @@ static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new();
static mut HEAP: [u8; 65536] = [0; 65536];
-#[panic_handler]
-fn panic(_: &PanicInfo) -> ! {
- loop {}
-}
+// #[panic_handler]
+// fn panic(_: &PanicInfo) -> ! {
+// loop {}
+// }
/// main() entry point replacement required by [no_std].
#[no_mangle]