summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergii Parubochyi <sergiip@google.com>2023-11-29 00:51:21 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-11-29 00:51:21 +0000
commit926d34bb0475ae9f445ee6f93a173ffdc13ca4a3 (patch)
tree119fa67b7584b720a394df222abcc400a57c044c
parent93840210808f66fe14738855c814d8fc47190af0 (diff)
parent33f022a83a1d8bcf6d19ec149e33fb4233c11c7a (diff)
downloadlibbootloader-926d34bb0475ae9f445ee6f93a173ffdc13ca4a3.tar.gz
Add top level GBL API am: f84f7d261a am: 04c67e0bdc am: 33f022a83a
Original change: https://android-review.googlesource.com/c/platform/bootable/libbootloader/+/2770825 Change-Id: I08567c09b38c654c6fd76d2aa59783645a321e6e Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--gbl/libgbl/Android.bp54
-rw-r--r--gbl/libgbl/src/boot_mode.rs165
-rw-r--r--gbl/libgbl/src/boot_reason.rs69
-rw-r--r--gbl/libgbl/src/digest.rs142
-rw-r--r--gbl/libgbl/src/error.rs44
-rw-r--r--gbl/libgbl/src/lib.rs526
-rw-r--r--gbl/libgbl/src/ops.rs129
-rw-r--r--gbl/libgbl/tests/nostd.rs14
-rw-r--r--gbl/third_party/libzbi/src/lib.rs2
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
//!