diff options
author | Sergii Parubochyi <sergiip@google.com> | 2023-10-03 17:32:37 +1100 |
---|---|---|
committer | Sergii Parubochyi <sergiip@google.com> | 2023-11-27 17:21:17 +1100 |
commit | f84f7d261a08bc32246ab6e2f8d7edb63f2f3fe7 (patch) | |
tree | 119fa67b7584b720a394df222abcc400a57c044c | |
parent | 62f2a221b836d6c1f6e2842a0bc892ac6b3b12ca (diff) | |
download | libbootloader-f84f7d261a08bc32246ab6e2f8d7edb63f2f3fe7.tar.gz |
Add top level GBL API
Adding API approximation based on go/libgbl-api
Test: a test
Bug: 294059574
Change-Id: I325cae2d11b98d51cc0db708bbe2965b0a3f5fb6
-rw-r--r-- | gbl/libgbl/Android.bp | 54 | ||||
-rw-r--r-- | gbl/libgbl/src/boot_mode.rs | 165 | ||||
-rw-r--r-- | gbl/libgbl/src/boot_reason.rs | 69 | ||||
-rw-r--r-- | gbl/libgbl/src/digest.rs | 142 | ||||
-rw-r--r-- | gbl/libgbl/src/error.rs | 44 | ||||
-rw-r--r-- | gbl/libgbl/src/lib.rs | 526 | ||||
-rw-r--r-- | gbl/libgbl/src/ops.rs | 129 | ||||
-rw-r--r-- | gbl/libgbl/tests/nostd.rs | 14 | ||||
-rw-r--r-- | gbl/third_party/libzbi/src/lib.rs | 2 |
9 files changed, 1118 insertions, 27 deletions
diff --git a/gbl/libgbl/Android.bp b/gbl/libgbl/Android.bp index f7d8911..50ee1cc 100644 --- a/gbl/libgbl/Android.bp +++ b/gbl/libgbl/Android.bp @@ -19,6 +19,7 @@ package { rust_defaults { name: "libgbl_defaults", srcs: ["src/lib.rs"], + rustlibs: ["libstatic_assertions"], } // GBL Rust library. @@ -26,8 +27,14 @@ rust_library { name: "libgbl", host_supported: true, crate_name: "gbl", - defaults: [ "libgbl_defaults" ], - rustlibs: ["libzbi"], + defaults: ["libgbl_defaults"], + rustlibs: [ + "libzbi", + "libavb_rs", + "libring", + "libspin", + "liblazy_static", + ], } // Defining rlib here because we want to build with [no_std], but @@ -37,24 +44,43 @@ rust_library { rust_library_rlib { name: "libgbl_nostd", crate_name: "gbl", - defaults: [ "libgbl_defaults" ], - rlibs: ["libzbi_nostd"], + defaults: ["libgbl_defaults"], + rlibs: [ + "libzbi_nostd", + "libavb_rs_nostd", + "libring_nostd", + "libspin_nostd", + "liblazy_static_nostd", + ], no_stdlibs: true, prefer_rlib: true, } // libgbl unit tests. +rust_defaults { + name: "libgbl_test_defaults", + defaults: ["libgbl_defaults"], + rustlibs: [ + "libzbi", + "libavb_rs", + "libitertools", + "libring", + "libhex", + "libspin", + "liblazy_static", + ], + features: ["alloc"], +} + rust_test { name: "libgbl_device_test", - defaults: [ "libgbl_defaults" ], - rustlibs: ["libzbi"], + defaults: ["libgbl_test_defaults"], test_suites: ["general-tests"], } rust_test_host { name: "libgbl_host_test", - defaults: [ "libgbl_defaults" ], - rustlibs: ["libzbi"], + defaults: ["libgbl_test_defaults"], } // Test building with libgbl in a [no_std] environment. @@ -64,10 +90,16 @@ rust_test_host { // so actual logic unittests go in libgbl tests instead. rust_binary { name: "libgbl_test_nostd", - srcs: [ "tests/nostd.rs" ], - rustlibs: [ "libgbl_nostd" ], + srcs: ["tests/nostd.rs"], + rustlibs: [ + "libgbl_nostd", + "libbuddy_system_allocator", + ], // panic=abort to satisfy the eh_personality compile requirement. - flags: [ "-C", "panic=abort" ], + flags: [ + "-C", + "panic=abort", + ], no_stdlibs: true, prefer_rlib: true, } diff --git a/gbl/libgbl/src/boot_mode.rs b/gbl/libgbl/src/boot_mode.rs new file mode 100644 index 0000000..4890b86 --- /dev/null +++ b/gbl/libgbl/src/boot_mode.rs @@ -0,0 +1,165 @@ +// Copyright 2023, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Possible boot modes. + +// TODO: b/312605899 - find full list of supported boot modes +// Looks like we need only compliant items from map: system/core/bootstat/bootstat.cpp +// kBootReasonMap +// It might be required to assemble this type in format: <reason>,<sub_reason>,<detil>,... +// Bootloaders must provide a kernel set or a blunt set reason, and are strongly encouraged to +// provide a subreason if it can be determined. For example, a power key long press that may or may +// not have ramoops backup would have the boot reason "reboot,longkey". +/* good reasons from kBootReasonMap +{"reboot,[empty]", kEmptyBootReason}, +{"recovery", 3}, +{"reboot", 4}, +{"kernel_panic", 7}, +{"watchdog", 40}, +{"shutdown,", 45}, // Trailing comma is intentional. Do NOT use. +{"shutdown,userrequested", 46}, +{"reboot,bootloader", 47}, +{"reboot,cold", 48}, +{"reboot,recovery", 49}, +{"kernel_panic,sysrq", 52}, +{"kernel_panic,null", 53}, +{"kernel_panic,bug", 54}, +{"bootloader", 55}, +{"cold", 56}, +{"hard", 57}, +{"warm", 58}, +{"reboot,kernel_power_off_charging__reboot_system", 59}, // Can not happen +{"shutdown,thermal", 61}, +{"shutdown,battery", 62}, +{"reboot,ota", 63}, +{"reboot,factory_reset", 64}, +{"reboot,", 65}, +{"reboot,shell", 66}, +{"reboot,adb", 67}, +{"reboot,userrequested", 68}, +{"shutdown,container", 69}, // Host OS asking Android Container to shutdown +{"cold,powerkey", 70}, +{"warm,s3_wakeup", 71}, +{"hard,hw_reset", 72}, +{"shutdown,suspend", 73}, // Suspend to RAM +{"shutdown,hibernate", 74}, // Suspend to DISK +{"reboot,by_key", 84}, +{"reboot,longkey", 85}, +{"reboot,2sec", 86}, // Deprecate in two years, replaced with cold,rtc,2sec +{"shutdown,thermal,battery", 87}, +{"reboot,its_just_so_hard", 88}, // produced by boot_reason_test +{"reboot,rescueparty", 90}, +{"reboot,powerloss", 119}, +{"reboot,undervoltage", 120}, +{"cold,charger", 148}, +{"cold,rtc", 149}, +{"cold,rtc,2sec", 150}, // Mediatek +{"reboot,tool", 151}, // Mediatek +{"reboot,wdt", 152}, // Mediatek +{"reboot,unknown", 153}, // Mediatek +{"kernel_panic,audit", 154}, +{"kernel_panic,atomic", 155}, +{"kernel_panic,hung", 156}, +{"kernel_panic,hung,rcu", 157}, +{"kernel_panic,init", 158}, +{"kernel_panic,oom", 159}, +{"kernel_panic,stack", 160}, +{"kernel_panic,sysrq,livelock,alarm", 161}, // llkd +{"kernel_panic,sysrq,livelock,driver", 162}, // llkd +{"kernel_panic,sysrq,livelock,zombie", 163}, // llkd +{"kernel_panic,modem", 164}, +{"kernel_panic,adsp", 165}, +{"kernel_panic,dsps", 166}, +{"kernel_panic,wcnss", 167}, +{"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout", 168}, +{"recovery,quiescent", 169}, +{"reboot,quiescent", 170}, +{"reboot,rtc", 171}, +{"reboot,dm-verity_device_corrupted", 172}, +{"reboot,dm-verity_enforcing", 173}, +{"reboot,keys_clear", 174}, +{"reboot,pmic_off_fault,.*", 175}, +{"reboot,pmic_off_s3rst,.*", 176}, +{"reboot,pmic_off_other,.*", 177}, +{"reboot,userrequested,fastboot", 178}, +{"reboot,userrequested,recovery", 179}, +{"reboot,userrequested,recovery,ui", 180}, +{"shutdown,userrequested,fastboot", 181}, +{"shutdown,userrequested,recovery", 182}, +{"reboot,unknown[0-9]*", 183}, +{"reboot,longkey,.*", 184}, +{"reboot,boringssl-self-check-failed", 185}, +{"reboot,userspace_failed,shutdown_aborted", 186}, +{"reboot,userspace_failed,watchdog_triggered", 187}, +{"reboot,userspace_failed,watchdog_fork", 188}, +{"reboot,userspace_failed,*", 189}, +{"reboot,mount_userdata_failed", 190}, +{"reboot,forcedsilent", 191}, +{"reboot,forcednonsilent", 192}, +{"reboot,thermal,tj", 193}, +{"reboot,emergency", 194}, +{"reboot,factory", 195}, +{"reboot,fastboot", 196}, +{"reboot,gsa,hard", 197}, +{"reboot,gsa,soft", 198}, +{"reboot,master_dc,fault_n", 199}, +{"reboot,master_dc,reset", 200}, +{"reboot,ocp", 201}, +{"reboot,pin", 202}, +{"reboot,rom_recovery", 203}, +{"reboot,uvlo", 204}, +{"reboot,uvlo,pmic,if", 205}, +{"reboot,uvlo,pmic,main", 206}, +{"reboot,uvlo,pmic,sub", 207}, +{"reboot,warm", 208}, +{"watchdog,aoc", 209}, +{"watchdog,apc", 210}, +{"watchdog,apc,bl,debug,early", 211}, +{"watchdog,apc,bl,early", 212}, +{"watchdog,apc,early", 213}, +{"watchdog,apm", 214}, +{"watchdog,gsa,hard", 215}, +{"watchdog,gsa,soft", 216}, +{"watchdog,pmucal", 217}, +{"reboot,early,bl", 218}, +{"watchdog,apc,gsa,crashed", 219}, +{"watchdog,apc,bl31,crashed", 220}, +{"watchdog,apc,pbl,crashed", 221}, +{"reboot,memory_protect,hyp", 222}, +{"reboot,tsd,pmic,main", 223}, +{"reboot,tsd,pmic,sub", 224}, +{"reboot,ocp,pmic,main", 225}, +{"reboot,ocp,pmic,sub", 226}, +{"reboot,sys_ldo_ok,pmic,main", 227}, +{"reboot,sys_ldo_ok,pmic,sub", 228}, +{"reboot,smpl_timeout,pmic,main", 229}, +{"reboot,ota,.*", 230}, +{"reboot,periodic,.*", 231}, +*/ + +#[derive(Debug, PartialEq, Clone)] +/// Boot mode +/// +/// This is subset of compliant tems from map: system/core/bootstat/bootstat.cpp kBootReasonMap +// Underlying format is <reason>,<sub_reason>,<detil>,... +pub enum BootMode { + /// Normal system start + Normal, + /// Recovery mode + Recovery, + /// Request to boot into bootloader mode staying in CMD-line or fastboot mode. + Bootloader, + // TODO: b/312605899 - need full list of supported modes + // Quiescent, +} diff --git a/gbl/libgbl/src/boot_reason.rs b/gbl/libgbl/src/boot_reason.rs new file mode 100644 index 0000000..e4559e6 --- /dev/null +++ b/gbl/libgbl/src/boot_reason.rs @@ -0,0 +1,69 @@ +// Copyright 2023, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Possible boot reasons. + +use core::fmt::{Debug, Display, Formatter}; + +#[derive(Debug, PartialEq, Clone)] +/// Boot reasons that could be used in [BootMode] +pub enum KnownBootReason { + // kernel + /// Watchdog + Watchdog, + /// Kerner panic + KernelPanic, + // strong + /// Recovery + Recovery, + /// Bootloader + Bootloader, + // blunt + /// Generally indicates a full reset of all devices, including memory + Cold, + /// Generally indicates the hardware has its state reset and ramoops should retain persistent + /// content + Hard, + /// Generally indicates the memory and the devices retain some state, and the ramoops (see + /// pstore driver in kernel) backing store contains persistent content + Warm, + // super blunt + /// Shutdown + Shutdown, + /// Generally means the ramoops state is unknown and the hardware state is unknown. This value + /// is a catchall as the cold, hard, and warm values provide clues as to the depth of the reset + /// for the device + Reboot, +} + +impl Display for KnownBootReason { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let str = match self { + // kernel + KnownBootReason::Watchdog => "watchdog", + KnownBootReason::KernelPanic => "kernel_panic", + // strong + KnownBootReason::Recovery => "recovery", + KnownBootReason::Bootloader => "bootloader", + // blunt + KnownBootReason::Cold => "cold", + KnownBootReason::Hard => "hard", + KnownBootReason::Warm => "warm", + // super blunt + KnownBootReason::Shutdown => "shutdown", + KnownBootReason::Reboot => "reboot", + }; + write!(f, "{str}") + } +} diff --git a/gbl/libgbl/src/digest.rs b/gbl/libgbl/src/digest.rs new file mode 100644 index 0000000..fff1c1f --- /dev/null +++ b/gbl/libgbl/src/digest.rs @@ -0,0 +1,142 @@ +// Copyright 2023, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! GBL Digest trait that defines interface for hash computation. +//! + +// TODO: b/312606477 - SW implementation should be behind feature + +extern crate ring; +pub use ring::digest::Algorithm; + +/// Digest output trait that return algorithm and ref to the value +pub trait Digest: AsRef<[u8]> { + /// Get digest algorithm + fn algorithm(&self) -> &'static Algorithm; +} + +/// Context trait that implements digesting. +/// Sha256 or Sha512. +pub trait Context<D: Digest> { + /// Create [Context] object that can calculate digest with requested algorithm. + /// + /// # Arguments + /// + /// * algorithm - requested algorithm + fn new(algorithm: &'static Algorithm) -> Self; + + /// Process next portion of data for the digest. + /// + /// # Arguments + /// + /// * input - block of data to be processed + fn update(&mut self, input: &[u8]); + + /// Finalise digest computation. + /// + /// Object is consumed to prevent reusing. + fn finish(self) -> D; + + /// The algorithm that this context is using. + fn algorithm(&self) -> &'static Algorithm; +} + +/// Software implementation for digest Context +pub struct SwContext { + ring_context: ring::digest::Context, +} +impl Context<SwDigest> for SwContext { + fn new(algorithm: &'static Algorithm) -> Self + where + Self: Sized, + { + Self { ring_context: ring::digest::Context::new(algorithm) } + } + + fn update(&mut self, input: &[u8]) { + self.ring_context.update(input) + } + + fn finish(self) -> SwDigest { + SwDigest { ring_digest: self.ring_context.finish() } + } + + fn algorithm(&self) -> &'static Algorithm { + self.ring_context.algorithm() + } +} + +/// Software implementation of Digest. +pub struct SwDigest { + ring_digest: ring::digest::Digest, +} +impl AsRef<[u8]> for SwDigest { + fn as_ref(&self) -> &[u8] { + self.ring_digest.as_ref() + } +} +impl Digest for SwDigest { + fn algorithm(&self) -> &'static Algorithm { + self.ring_digest.algorithm() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + // Compute digest on provided input using algorithm + fn digest(algorithm: &'static Algorithm, input: &[u8]) -> SwDigest { + let mut ctx = SwContext::new(algorithm); + ctx.update(input); + ctx.finish() + } + + #[test] + fn test_swdigest_sha256() { + let input = b"abc"; + let expected = + hex::decode("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD") + .unwrap(); + assert_eq!(digest(&ring::digest::SHA256, input).as_ref(), expected); + } + + #[test] + fn test_swdigest_sha512() { + assert_eq!( + digest(&ring::digest::SHA512, b"abc").as_ref(), + hex::decode(concat!( + "DDAF35A193617ABACC417349AE20413112E6FA4E89A97EA2", + "0A9EEEE64B55D39A2192992A274FC1A836BA3C23A3FEEBBD", + "454D4423643CE80E2A9AC94FA54CA49F", + )) + .unwrap() + ); + } + + #[test] + fn test_swdigest_sha_partial() { + let input = b"abc"; + let expected = + hex::decode("BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD") + .unwrap(); + + let mut ctx = SwContext::new(&ring::digest::SHA256); + for i in input.chunks(input.len()) { + ctx.update(i); + } + + assert_eq!(ctx.finish().as_ref(), expected); + } +} diff --git a/gbl/libgbl/src/error.rs b/gbl/libgbl/src/error.rs new file mode 100644 index 0000000..ef023d3 --- /dev/null +++ b/gbl/libgbl/src/error.rs @@ -0,0 +1,44 @@ +// Copyright 2023, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Error types used in libgbl. + +use core::fmt::{Debug, Display, Formatter}; + +/// Helper type GBL functions will return. +pub type Result<T> = core::result::Result<T, Error>; + +#[derive(Debug, PartialEq)] +/// Error values that can be returned by function in this library +pub enum Error { + /// Generic error + Error, + /// Missing all images required to boot system + MissingImage, + /// Functionality is not implemented + NotImplemented, +} + +// Unfortunately thiserror is not available in `no_std` world. +// Thus `Display` implementation is required. +impl Display for Error { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + let str = match self { + Error::Error => "Generic error", + Error::MissingImage => "Missing image required to boot system", + Error::NotImplemented => "Functionality is not implemented", + }; + write!(f, "{str}") + } +} diff --git a/gbl/libgbl/src/lib.rs b/gbl/libgbl/src/lib.rs index 33655f3..4255124 100644 --- a/gbl/libgbl/src/lib.rs +++ b/gbl/libgbl/src/lib.rs @@ -14,36 +14,532 @@ //! # Generic Boot Loader (gbl) Library //! -//! TODO: documentation. +//! TODO: b/312610098 - add documentation. +//! +//! The intended users of this library are firmware, bootloader, and bring-up teams at OEMs and SOC +//! Vendors // This code is intended for use in bootloaders that typically will not support // the Rust standard library -#![cfg_attr(not(test), no_std)] - -// Adding ZBI library usage to check dependencies +#![cfg_attr(not(any(test, android_dylib)), no_std)] +// TODO: b/312610985 - return warning for unused partitions +#![allow(unused_variables, dead_code)] +// TODO: b/312608163 - Adding ZBI library usage to check dependencies extern crate zbi; -/// Placeholder code to get a build rule and tests in place. -pub fn foo() -> u32 { - 42 +use core::fmt::{Debug, Display, Formatter}; +use lazy_static::lazy_static; +use spin::Mutex; + +pub mod boot_mode; +pub mod boot_reason; +pub mod digest; +pub mod error; +pub mod ops; + +pub use boot_mode::BootMode; +pub use boot_reason::KnownBootReason; +pub use digest::{Context, Digest, SwContext, SwDigest}; +pub use error::{Error, Result}; +pub use ops::{DefaultGblOps, GblOps}; + +// TODO: b/312607649 - Replace placeholders with actual structures: https://r.android.com/2721974, etc +/// TODO: b/312607649 - placeholder type +pub struct Partition {} +/// TODO: b/312607649 - placeholder type +pub struct InfoStruct {} +/// TODO: b/312607649 - placeholder type +pub struct AvbDescriptor {} +/// TODO: b/312607649 - placeholder type +pub struct AvbVerificationFlags(u32); // AvbVBMetaImageFlags from + // external/avb/libavb/avb_vbmeta_image.h + +/// Structure representing partition and optional address it is required to be loaded. +/// If no address is provided GBL will use default one. +pub struct PartitionRamMap<'b, 'c> { + /// Partition details + pub partition: &'b Partition, + + /// Optional memory region to load partitions. + /// If it's not provided default values will be used. + pub address: Option<&'c mut [u8]>, + + loaded: bool, + verified: bool, +} + +/// Boot Image in memory +pub struct BootImage<'a>(&'a mut [u8]); + +/// Vendor Boot Image in memory +pub struct VendorBootImage<'a>(&'a mut [u8]); + +/// Init Boot Image in memory +pub struct InitBootImage<'a>(&'a mut [u8]); + +/// Kernel Image in memory +pub struct KernelImage<'a>(&'a mut [u8]); + +/// Ramdisk in memory +pub struct Ramdisk<'a>(&'a mut [u8]); +/// Bootconfig in memory +pub struct Bootconfig<'a>(&'a mut [u8]); +/// DTB in memory +pub struct Dtb<'a>(&'a mut [u8]); + +/// Create Boot Image from corresponding partition for `partitions_ram_map` and `avb_descriptors` +/// lists +pub fn get_boot_image<'a: 'b, 'b: 'c, 'c>( + avb_descriptors: &[AvbDescriptor], + partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], +) -> (Option<BootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) { + match partitions_ram_map.len() { + 0 => (None, partitions_ram_map), + _ => { + let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap(); + (partition_map.address.take().map(BootImage), tail) + } + } +} + +/// Create Vendor Boot Image from corresponding partition for `partitions_ram_map` and +/// `avb_descriptors` lists +pub fn get_vendor_boot_image<'a: 'b, 'b: 'c, 'c>( + avb_descriptors: &[AvbDescriptor], + partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], +) -> (Option<VendorBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) { + match partitions_ram_map.len() { + 0 => (None, partitions_ram_map), + _ => { + let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap(); + (partition_map.address.take().map(VendorBootImage), tail) + } + } +} + +/// Create Init Boot Image from corresponding partition for `partitions` and `avb_descriptors` lists +pub fn get_init_boot_image<'a: 'b, 'b: 'c, 'c>( + avb_descriptors: &[AvbDescriptor], + partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], +) -> (Option<InitBootImage<'c>>, &'a mut [PartitionRamMap<'b, 'c>]) { + match partitions_ram_map.len() { + 0 => (None, partitions_ram_map), + _ => { + let (partition_map, tail) = partitions_ram_map.split_first_mut().unwrap(); + (partition_map.address.take().map(InitBootImage), tail) + } + } +} + +/// Create separate image types from [AvbDescriptor] +pub fn get_images<'a: 'b, 'b: 'c, 'c>( + avb_descriptors: &mut [AvbDescriptor], + partitions_ram_map: &'a mut [PartitionRamMap<'b, 'c>], +) -> ( + Option<BootImage<'c>>, + Option<InitBootImage<'c>>, + Option<VendorBootImage<'c>>, + &'a mut [PartitionRamMap<'b, 'c>], +) { + let (boot_image, partitions_ram_map) = get_boot_image(avb_descriptors, partitions_ram_map); + let (init_boot_image, partitions_ram_map) = + get_init_boot_image(avb_descriptors, partitions_ram_map); + let (vendor_boot_image, partitions_ram_map) = + get_vendor_boot_image(avb_descriptors, partitions_ram_map); + (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}") + } } -/// Placeholder code to check `zbi` lib dependencies. -pub fn bar() -> usize { - zbi::ZBI_ALIGNMENT_USIZE +// 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. +unsafe fn mutex_buffer(address: usize, size: usize) -> Mutex<&'static mut [u8]> { + // SAFETY: user should make sure that multiple function user doesn't use overlaping ranges. + // And (addr, size) pair is safe to convert to slice. + Mutex::new(unsafe { core::slice::from_raw_parts_mut(address as *mut u8, size) }) +} + +lazy_static! { + // SAFETY: `test_default_memory_regions()' tests that slices don't overlap + static ref BOOT_IMAGE: Mutex<&'static mut[u8]> = unsafe { + mutex_buffer(0x1000_0000, 0x1000_0000) + }; + // SAFETY: `test_default_memory_regions()' tests that slices don't overlap + static ref KERNEL_IMAGE: Mutex<&'static mut[u8]> = unsafe { + mutex_buffer(0x8020_0000, 0x0100_0000) + }; + // SAFETY: `test_default_memory_regions()' tests that slices don't overlap + static ref VENDOR_BOOT_IMAGE: Mutex<&'static mut[u8]> = unsafe { + mutex_buffer(0x3000_0000, 0x1000_0000) + }; + // SAFETY: `test_default_memory_regions()' tests that slices don't overlap + static ref INIT_BOOT_IMAGE: Mutex<&'static mut[u8]> = unsafe { + mutex_buffer(0x4000_0000, 0x1000_0000) + }; + // SAFETY: `test_default_memory_regions()' tests that slices don't overlap + static ref RAMDISK: Mutex<&'static mut[u8]> = unsafe { + mutex_buffer(0x8420_0000, 0x0100_0000) + }; + // SAFETY: `test_default_memory_regions()' tests that slices don't overlap + static ref BOOTCONFIG: Mutex<&'static mut[u8]> = unsafe { + mutex_buffer(0x6000_0000, 0x1000_0000) + }; + // SAFETY: `test_default_memory_regions()' tests that slices don't overlap + static ref DTB: Mutex<&'static mut[u8]> = unsafe { + mutex_buffer(0x8000_0000, 0x0010_0000) + }; +} + +#[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, +} + +impl<'a, D, C> Gbl<'a, D, C> +where + D: Digest, + C: Context<D>, +{ + fn new<'b>(ops: &'b mut impl GblOps<D, C>) -> Gbl<'a, D, C> + where + 'b: 'a, + { + 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 } + } +} + +impl<'a, D, C> Gbl<'a, D, C> +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 + /// + /// # 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 + /// + /// # Returns + /// + /// * `Ok(&[avb_descriptor])` - Array of AVB Descriptors - AVB return codes, partition name, + /// image load address, image size, AVB Footer contents (version details, etc.) + /// * `Err(Error)` - on failure + pub fn load_and_verify_image<'b>( + &self, + partitions_ram_map: &mut [PartitionRamMap], + avb_verification_flags: AvbVerificationFlags, + boot_slot: Option<&BootSlot>, + ) -> Result<&'b mut [AvbDescriptor]> + where + 'a: 'b, + { + // TODO: b/312608785 - check that provided buffers don't overlap. + // Default addresses to use + let default_boot_image_buffer = BOOT_IMAGE.lock(); + let default_vendor_boot_image_buffer = VENDOR_BOOT_IMAGE.lock(); + let default_init_boot_image_buffer = INIT_BOOT_IMAGE.lock(); + unimplemented!("partition loading and verification"); + } + + /// Info Load + /// + /// Unpack boot image in RAM + /// + /// # Arguments + /// * `boot_image_buffer` - Buffer that contains (Optionally Verified) Boot Image + /// * `boot_mode` - Boot Mode + /// * `boot_slot` - [Optional] Boot Slot + /// + /// # Returns + /// + /// * `Ok(InfoStruct)` - Info Struct (Concatenated kernel commandline - includes slot, + /// bootconfig selection, normal_mode, Concatenated bootconfig) on success + /// * `Err(Error)` - on failure + pub fn unpack_boot_image( + &self, + boot_image_buffer: &BootImage, + boot_mode: &BootMode, + boot_slot: Option<&BootSlot>, + ) -> Result<InfoStruct> { + unimplemented!(); + } + + /// Kernel Load + /// + /// Prepare kernel in RAM for booting + /// + /// # Arguments + /// * `info` - Info Struct from Info Load + /// * `image_buffer` - Buffer that contains (Verified) Boot Image + /// * `load_buffer` - Kernel Load buffer + /// + /// # Returns + /// + /// * `Ok(())` - on success + /// * `Err(Error)` - on failure + pub fn kernel_load( + &self, + info: &InfoStruct, + image_buffer: BootImage, + load_buffer: &mut [u8], + ) -> Result<KernelImage> { + unimplemented!(); + } + + /// Ramdisk + Bootconfig Load + /// + /// Kernel Load + /// (Could break this into a RD and Bootconfig specific function each, TBD) + /// Prepare ramdisk/bootconfig in RAM for booting + /// + /// # Arguments + /// * `info` - Info Struct from Info Load + /// * `vendor_boot_image` - Buffer that contains (Verified) Vendor Boot Image + /// * `init_boot_image` - Buffer that contains (Verified) Init Boot Image + /// * `ramdisk_load_buffer` - Ramdisk Load buffer (not compressed) + /// * `bootconfig_load_buffer` - Bootconfig Load buffer (not compressed). This bootconfig + /// will have data added at the end to include bootloader specific options such as + /// force_normal_boot and other other android specific details + /// + /// # Returns + /// + /// * `Ok(&str)` - on success returns Kernel command line + /// * `Err(Error)` - on failure + pub fn ramdisk_bootconfig_load( + &self, + info: &InfoStruct, + vendor_boot_image: &VendorBootImage, + init_boot_image: &InitBootImage, + ramdisk_load_buffer: &mut [u8], + bootconfig_load_buffer: &mut [u8], + ) -> Result<&'static str> { + unimplemented!(); + } + + /// DTB Update And Load + /// + /// Prepare DTB in RAM for booting + /// + /// # Arguments + /// * `info` - Info Struct from Info Load + /// * `vendor_boot_image_buffer` - Buffer that contains (Verified) Vendor Boot Image + /// + /// # Returns + /// + /// * `Ok()` - on success + /// * `Err(Error)` - on failure + pub fn dtb_update_and_load( + &self, + info: &InfoStruct, + vendor_boot_image_buffer: VendorBootImage, + ) -> Result<Dtb> { + unimplemented!(); + } + + /// Kernel Jump + /// + /// + /// # Arguments + /// * `kernel_load_buffer` - Kernel Load buffer + /// * `ramdisk_bootconfi_load_buffer` - Concatenated Ramdisk, (Bootconfig if present) Load + /// buffer + /// * `dtb_load_buffer` - DTB Load buffer + /// + /// # Returns + /// + /// * doesn't return on success + /// * `Err(Error)` - on failure + // Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812 + pub fn kernel_jump( + &self, + kernel_load_buffer: KernelImage, + ramdisk_load_buffer: Ramdisk, + dtb_load_buffer: Dtb, + ) -> Result<()> { + unimplemented!(); + } + + /// Load, verify, and boot + /// + /// Wrapper around the above functions for devices that don't need custom behavior between each + /// step + /// + /// # Arguments + /// * `partitions_ram_map` - Partitions to verify and optional address for them to be loaded. + /// * `avb_verification_flags` - AVB verification flags/options + /// + /// # Returns + /// + /// * doesn't return on success + /// * `Err(Error)` - on failure + // Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812 + pub fn load_verify_boot<'b: 'c, 'c, 'd: 'b>( + &mut self, + partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>], + avb_verification_flags: AvbVerificationFlags, + ) -> 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 + Err(Error::NotImplemented) => (), + // Fail on any other error + Err(e) => return Err(e), + } + } + + let boot_slot = self.get_boot_slot()?; + let avb_descriptors = self.load_and_verify_image( + partitions_ram_map, + AvbVerificationFlags(0), + Some(&boot_slot), + )?; + + let (boot_image, init_boot_image, vendor_boot_image, _) = + get_images(avb_descriptors, partitions_ram_map); + let boot_image = boot_image.ok_or(Error::MissingImage)?; + 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 mut kernel_load_buffer = KERNEL_IMAGE.lock(); + let kernel_image = self.kernel_load(&info_struct, boot_image, &mut 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, + &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) + } +} + +impl<'a> Default for Gbl<'a, SwDigest, SwContext> { + fn default() -> Self { + Self { ops: DefaultGblOps::new(), image_verification: true } + } } #[cfg(test)] mod tests { + extern crate itertools; + use super::*; + use itertools::Itertools; - #[test] - fn test_foo() { - assert_eq!(foo(), 42); + pub fn get_end(buf: &[u8]) -> Option<*const u8> { + Some((buf.as_ptr() as usize).checked_add(buf.len())? as *const u8) + } + + // Check if ranges overlap + // Range is in form of [lower, upper) + fn is_overlap(a: &[u8], b: &[u8]) -> bool { + !((get_end(b).unwrap() <= a.as_ptr()) || (get_end(a).unwrap() <= b.as_ptr())) } #[test] - fn test_bar() { - assert_eq!(bar(), 8); + fn test_default_memory_regions() { + let memory_ranges = [ + &BOOT_IMAGE.lock(), + &KERNEL_IMAGE.lock(), + &VENDOR_BOOT_IMAGE.lock(), + &INIT_BOOT_IMAGE.lock(), + &RAMDISK.lock(), + &BOOTCONFIG.lock(), + &DTB.lock(), + ]; + + for (r1, r2) in memory_ranges.into_iter().tuple_combinations() { + let overlap = is_overlap(r1, r2); + assert!( + !overlap, + "Following pair overlap: ({}..{}), ({}..{})", + r1.as_ptr() as usize, + get_end(r1).unwrap() as usize, + r2.as_ptr() as usize, + get_end(r2).unwrap() as usize + ); + } } } diff --git a/gbl/libgbl/src/ops.rs b/gbl/libgbl/src/ops.rs new file mode 100644 index 0000000..fcdccc7 --- /dev/null +++ b/gbl/libgbl/src/ops.rs @@ -0,0 +1,129 @@ +// Copyright 2023, The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! GblOps trait that defines GBL callbacks. +//! +#[cfg(feature = "alloc")] +extern crate alloc; + +use crate::digest::{Algorithm, Context, Digest}; +use crate::error::{Error, Result}; +#[cfg(feature = "alloc")] +use alloc::ffi::CString; +use avb::{IoError, Ops, PublicKeyForPartitionInfo}; +use core::{ffi::CStr, fmt::Debug, ptr::NonNull}; + +/// TODO: b/312607649 - Placeholder. Use result type from `avb` when it is stable and public +pub type AvbResult<T> = core::result::Result<T, IoError>; + +// https://stackoverflow.com/questions/41081240/idiomatic-callbacks-in-rust +// should we use traits for this? or optional/box FnMut? +// +/* TODO: b/312612203 - needed callbacks: +missing: +- validate_public_key_for_partition: None, +- key management => atx extension in callback => atx_ops: ptr::null_mut(), // support optional ATX. +*/ +/// Trait that defines callbacks that can be provided to Gbl. +pub trait GblOps<D: Digest, C: Context<D>>: Ops + Debug { + /// Create digest object to use for hash computations. + /// + /// Context interface allows to update value adding more data to process. + /// # Arguments + /// + /// * algorithm - algorithm to use for hash computation. + fn new_digest(&self, algorithm: &'static Algorithm) -> C { + Context::new(algorithm) + } + /// Calculate digest of provided data with requested algorithm. Single use unlike [new_digest] + /// flow. + fn digest(&self, algorithm: &'static Algorithm, data: &[u8]) -> D { + let mut ctx = self.new_digest(algorithm); + 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<()> { + 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<()>; +} + +/// Default [GblOps] implementation that returns errors and does nothing. +#[derive(Debug)] +pub struct DefaultGblOps {} +impl Ops for DefaultGblOps { + fn validate_vbmeta_public_key(&mut self, _: &[u8], _: Option<&[u8]>) -> AvbResult<bool> { + Err(IoError::NotImplemented) + } + fn read_from_partition(&mut self, _: &CStr, _: i64, _: &mut [u8]) -> AvbResult<usize> { + Err(IoError::NotImplemented) + } + fn read_rollback_index(&mut self, _: usize) -> AvbResult<u64> { + Err(IoError::NotImplemented) + } + fn write_rollback_index(&mut self, _: usize, _: u64) -> AvbResult<()> { + Err(IoError::NotImplemented) + } + fn read_is_device_unlocked(&mut self) -> AvbResult<bool> { + Err(IoError::NotImplemented) + } + fn get_size_of_partition(&mut self, partition: &CStr) -> AvbResult<u64> { + Err(IoError::NotImplemented) + } + fn read_persistent_value(&mut self, name: &CStr, value: &mut [u8]) -> AvbResult<usize> { + Err(IoError::NotImplemented) + } + fn write_persistent_value(&mut self, name: &CStr, value: &[u8]) -> AvbResult<()> { + Err(IoError::NotImplemented) + } + fn erase_persistent_value(&mut self, name: &CStr) -> AvbResult<()> { + Err(IoError::NotImplemented) + } + fn validate_public_key_for_partition( + &mut self, + partition: &CStr, + public_key: &[u8], + public_key_metadata: Option<&[u8]>, + ) -> AvbResult<PublicKeyForPartitionInfo> { + Err(IoError::NotImplemented) + } +} + +impl<D, C> GblOps<D, C> for DefaultGblOps +where + D: Digest, + C: Context<D>, +{ + #[cfg(feature = "alloc")] + fn gbl_alloc_extra_action(&mut self, s: &str) -> Result<()> { + let _c_string = CString::new(s); + Err(Error::Error) + } +} + +static_assertions::const_assert_eq!(core::mem::size_of::<DefaultGblOps>(), 0); +impl DefaultGblOps { + /// Create new DefaultGblOps object + pub fn new() -> &'static mut Self { + let mut ptr: NonNull<Self> = NonNull::dangling(); + // SAFETY: Self is a ZST, asserted above, and ptr is appropriately aligned and nonzero by + // NonNull::dangling() + unsafe { ptr.as_mut() } + } +} diff --git a/gbl/libgbl/tests/nostd.rs b/gbl/libgbl/tests/nostd.rs index 26e3dbb..fe3a99f 100644 --- a/gbl/libgbl/tests/nostd.rs +++ b/gbl/libgbl/tests/nostd.rs @@ -25,6 +25,14 @@ use core::panic::PanicInfo; use gbl as _; +use buddy_system_allocator::LockedHeap; + +// Providing allocator to satisfy AVB dependency +#[global_allocator] +static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new(); + +static mut HEAP: [u8; 65536] = [0; 65536]; + #[panic_handler] fn panic(_: &PanicInfo) -> ! { loop {} @@ -33,5 +41,11 @@ fn panic(_: &PanicInfo) -> ! { /// main() entry point replacement required by [no_std]. #[no_mangle] pub fn main() -> ! { + // SAFETY: Safe because `HEAP` is only used here and `entry` is only called once. + unsafe { + // Give the allocator some memory to allocate. + HEAP_ALLOCATOR.lock().init(HEAP.as_mut_ptr() as usize, HEAP.len()); + } + panic!() } diff --git a/gbl/third_party/libzbi/src/lib.rs b/gbl/third_party/libzbi/src/lib.rs index e439884..529a87d 100644 --- a/gbl/third_party/libzbi/src/lib.rs +++ b/gbl/third_party/libzbi/src/lib.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![cfg_attr(not(test), no_std)] +#![cfg_attr(not(any(test, android_dylib)), no_std)] //! ZBI Processing Library //! |