diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-05-26 07:00:21 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2024-05-26 07:00:21 +0000 |
commit | 0118ae892029a0b39603683a99f8ea57f7b7b5f7 (patch) | |
tree | 10061290ba3cfbc5eca190ca3b3460a67e39df01 | |
parent | 1426942d34a7a9d9b8bb9a5212ec0c6ad52adbcf (diff) | |
parent | 8ea03b70f66dc598fa0bd65f73668b614823c610 (diff) | |
download | libbootloader-busytown-mac-infra-release.tar.gz |
Snap for 11878398 from 8ea03b70f66dc598fa0bd65f73668b614823c610 to busytown-mac-infra-releasebusytown-mac-infra-release
Change-Id: I49f82e1868092398ae8497b29055b048d9731c4b
31 files changed, 537 insertions, 389 deletions
@@ -0,0 +1,37 @@ +# Copyright (C) 2024 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. + +load(":readme.bzl", "readme_test") + +readme_test( + name = "readme_test", + readme = "docs/efi_protocols.md", + # Note: can't read files in subpackages, not sure how to add a dependency on all, + # globbed, subpackages and all the rust_lib targets they contain. + deps = [ + "@gbl//efi:main", + "@gbl//libavb:sysdeps", + "@gbl//libboot", + "@gbl//libbootconfig", + "@gbl//libbootimg", + "@gbl//libc", + "@gbl//libefi", + "@gbl//libfastboot", + "@gbl//libfdt", + "@gbl//libgbl", + "@gbl//libmisc", + "@gbl//libsafemath", + "@gbl//libstorage", + ], +) diff --git a/gbl/README.md b/gbl/README.md index 430040a..d0b33c4 100644 --- a/gbl/README.md +++ b/gbl/README.md @@ -96,3 +96,7 @@ configurations: -drive format=raw,file=fat:rw:/tmp/esp,id=blk0 \ -device virtio-blk-device,drive=blk0 ``` + +## EFI Protocols + +List of EFI protocols used by GBL and a brief description of each [here](./docs/efi_protocols.md). diff --git a/gbl/docs/efi_protocols.md b/gbl/docs/efi_protocols.md new file mode 100644 index 0000000..32d09f3 --- /dev/null +++ b/gbl/docs/efi_protocols.md @@ -0,0 +1,65 @@ +The EFI application of GBL requires certain EFI protocols in order to boot, +and can require other protocols for certain targets or to enable optional features. + +### Required Protocols + +#### BlockIoProtocol + +The BlockIo protocol is required for loading system images from disk. +If a target supports Fastboot mode, it is also used for writing images to disk. + +#### SimpleTextOutputProtocol + +The SimpleTextOutput protocol is used for logging +and the text-based interface of Fastboot. +On systems where there is no output functionality, +this can be implemented as a series of no-op functions. + +### Conditionally Required Protocols + +#### RiscvBootProtocol + +Determines the boot hart ID which is then passed to the kernel. +Only required for RISC-V targets. + +### Optional Protocols + +#### AndroidBootProtocol + +This is a custom protocol intended to provide +specific functionality needed to boot Android. +A full description is available [here](./EFI_ANDROID_BOOT_PROTOCOL.md). + +#### DevicePathProtocol + +The DevicePath protocol is a variable length binary structure +made of variable length Device Path nodes. +A handle representing a hardware resource is mapped +to the protocol and provides specific data about that resource. + +If all three of DevicePath protocol, DevicePathToText protocol, +and LoadedImage protocol are present, the GBL image path is logged +to the console on load. + +This is a useful proof of concept for development to demonstrate +that GBL is running and can interact with the UEFI environment. + +#### DevicePathToTextProtocol + +The DevicePathToText protocol converts device paths and nodes to text. + +#### LoadedImageProtocol + +The LoadedImage protocol can be used on the handle of an image to provide +information about the image, including its device handle and device path. + +#### SimpleNetworkProtocol + +If present, the SimpleNetwork protocol is used to provide Fastboot over TCP. +No other EFI protocols are required: GBL wraps SimpleNetwork to provide TCP. + +Note: for security reasons, Fastboot over TCP is only available in dev builds. + +#### SimpleTextInputProtocol + +TODO: remove this protocol diff --git a/gbl/libgbl/BUILD b/gbl/libgbl/BUILD index 75b58fd..df19be0 100644 --- a/gbl/libgbl/BUILD +++ b/gbl/libgbl/BUILD @@ -46,12 +46,13 @@ rust_test( "@gbl//libgbl/testdata:sparse_test.bin", "@gbl//libgbl/testdata:sparse_test_blk1024.bin", "@gbl//libgbl/testdata:sparse_test_raw.bin", - "@gbl//libgbl/testdata:test_image.img", - "@gbl//libgbl/testdata:testkey_rsa4096.pem", - "@gbl//libgbl/testdata:testkey_rsa4096.pub.pem", + "@gbl//libgbl/testdata:testkey_rsa4096_pub.bin", "@gbl//libgbl/testdata:writeback_test_disk.bin", + "@gbl//libgbl/testdata:zircon_a.bin", + "@gbl//libgbl/testdata:zircon_a.vbmeta", ], deps = [ + "@avb//:avb_crypto_ops_sha_impl_staticlib", "@avb//:avb_test", "@gbl//libavb:sysdeps", "@gbl//libstorage:libstorage_testlib", diff --git a/gbl/libgbl/Cargo.toml b/gbl/libgbl/Cargo.toml index f383fb3..8d4d65a 100644 --- a/gbl/libgbl/Cargo.toml +++ b/gbl/libgbl/Cargo.toml @@ -21,14 +21,12 @@ name = "gbl" version = "0.1.0" [features] -default = ["sw_digest"] +default = [] alloc = [] -sw_digest = [] [dependencies] zbi = {version = "0.1", path = "../third_party/libzbi"} gbl_storage = {version = "0.1", path = "../libstorage"} -ring = "0.17" spin = "0.9" static_assertions = "0" lazy_static = "1" diff --git a/gbl/libgbl/src/digest.rs b/gbl/libgbl/src/digest.rs index 2505e42..c0435a1 100644 --- a/gbl/libgbl/src/digest.rs +++ b/gbl/libgbl/src/digest.rs @@ -13,9 +13,6 @@ // limitations under the License. //! GBL Digest trait that defines interface for hash computation. -//! -//! Software implementation available in `sw_digest` module. Specifically -//! [SwContext] and [SwDigest]. /// List of supported algorithms #[derive(Debug, Eq, PartialEq, Clone, Copy)] diff --git a/gbl/libgbl/src/error.rs b/gbl/libgbl/src/error.rs index e7474ce..fcc4a1a 100644 --- a/gbl/libgbl/src/error.rs +++ b/gbl/libgbl/src/error.rs @@ -23,7 +23,7 @@ use gbl_storage::StorageError; /// Helper type GBL functions will return. pub type Result<T> = core::result::Result<T, IntegrationError>; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] /// Errors originating from GBL native logic. pub enum Error { ArithmeticOverflow, @@ -133,7 +133,7 @@ macro_rules! composite_enum { composite_enum! { /// Top level error type that integrates errors from various dependency libraries. - #[derive(Debug)] + #[derive(Debug, PartialEq, Eq)] pub enum IntegrationError { /// Failed to get descriptor from AvbMeta AvbDescriptorError(DescriptorError), diff --git a/gbl/libgbl/src/lib.rs b/gbl/libgbl/src/lib.rs index fc619e0..a7f894a 100644 --- a/gbl/libgbl/src/lib.rs +++ b/gbl/libgbl/src/lib.rs @@ -20,7 +20,6 @@ //! Vendors //! //! # Features -//! * `sw_digest` - enables software implementation of digests: [SwDigest], [SwContext] //! * `alloc` - enables AVB ops related logic that relies on allocation and depends on allocation. // This code is intended for use in bootloaders that typically will not support @@ -45,7 +44,6 @@ use spin::Mutex; pub mod boot_mode; pub mod boot_reason; -pub mod digest; pub mod error; pub mod fastboot; pub mod ops; @@ -56,19 +54,13 @@ pub mod slots; use slots::{BootTarget, BootToken, Cursor, Manager, OneShot, SuffixBytes, UnbootableReason}; -#[cfg(feature = "sw_digest")] -pub mod sw_digest; - pub use avb::Descriptor; pub use boot_mode::BootMode; pub use boot_reason::KnownBootReason; -pub use digest::{Context, Digest}; pub use error::{Error, IntegrationError, Result}; pub use ops::{ AndroidBootImages, BootImages, DefaultGblOps, FuchsiaBootImages, GblOps, GblOpsError, }; -#[cfg(feature = "sw_digest")] -pub use sw_digest::{SwContext, SwDigest}; use ops::GblUtils; @@ -77,9 +69,7 @@ use ops::GblUtils; pub struct Partition {} /// TODO: b/312607649 - placeholder type pub struct InfoStruct {} -/// TODO: b/312607649 - placeholder type -pub struct AvbVerificationFlags(u32); // AvbVBMetaImageFlags from - // external/avb/libavb/avb_vbmeta_image.h + /// Data structure holding verified slot data. #[derive(Debug)] pub struct VerifiedData<'a>(SlotVerifyData<'a>); @@ -225,7 +215,7 @@ where &mut self, avb_ops: &mut impl avb::Ops<'b>, partitions_ram_map: &mut [PartitionRamMap], - avb_verification_flags: AvbVerificationFlags, + slot_verify_flags: SlotVerifyFlags, boot_target: Option<BootTarget>, ) -> Result<VerifiedData<'b>> { let bytes: SuffixBytes = @@ -239,7 +229,7 @@ where avb_ops, &requested_partitions, Some(avb_suffix), - SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE, + slot_verify_flags, HashtreeErrorMode::AVB_HASHTREE_ERROR_MODE_EIO, ) .map_err(|v| v.without_verify_data())?, @@ -414,7 +404,7 @@ where &mut self, avb_ops: &mut impl avb::Ops<'b>, partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>], - avb_verification_flags: AvbVerificationFlags, + slot_verify_flags: SlotVerifyFlags, slot_cursor: Cursor<B, impl Manager>, kernel_load_buffer: &mut [u8], ramdisk_load_buffer: &mut [u8], @@ -431,7 +421,7 @@ where &mut ramdisk, kernel_load_buffer, partitions_ram_map, - avb_verification_flags, + slot_verify_flags, slot_cursor, )?; @@ -463,7 +453,7 @@ where ramdisk: &mut Ramdisk, kernel_load_buffer: &'e mut [u8], partitions_ram_map: &'d mut [PartitionRamMap<'b, 'c>], - avb_verification_flags: AvbVerificationFlags, + slot_verify_flags: SlotVerifyFlags, mut slot_cursor: Cursor<B, impl Manager>, ) -> Result<(KernelImage<'e>, BootToken)> { let mut oneshot_status = slot_cursor.ctx.get_oneshot_status(); @@ -486,7 +476,7 @@ where .load_and_verify_image( avb_ops, partitions_ram_map, - AvbVerificationFlags(0), + slot_verify_flags, Some(boot_target), ) .map_err(|e: IntegrationError| { @@ -558,13 +548,6 @@ where } } -#[cfg(feature = "sw_digest")] -impl<'a> Default for Gbl<'a, DefaultGblOps> { - fn default() -> Self { - GblBuilder::new(DefaultGblOps::new()).build() - } -} - /// Builder for GBL object #[derive(Debug)] pub struct GblBuilder<'a, G> @@ -610,15 +593,14 @@ where #[cfg(test)] mod tests { + extern crate avb_sysdeps; extern crate avb_test; use super::*; use avb::IoError; use avb::IoResult as AvbIoResult; use avb::PublicKeyForPartitionInfo; - #[cfg(feature = "sw_digest")] - use avb_test::TestOps; - #[cfg(feature = "sw_digest")] - use std::fs; + use avb_test::{FakeVbmetaKey, TestOps}; + use std::{fs, path::Path}; struct AvbOpsUnimplemented {} impl avb::Ops<'_> for AvbOpsUnimplemented { @@ -663,68 +645,78 @@ mod tests { } } - #[cfg(feature = "sw_digest")] #[test] fn test_load_and_verify_image_avb_io_error() { - let mut gbl = GblBuilder::new(DefaultGblOps::new()).build(); + let mut gbl_ops = DefaultGblOps {}; + let mut gbl = GblBuilder::new(&mut gbl_ops).build(); let mut avb_ops = AvbOpsUnimplemented {}; let mut partitions_ram_map: [PartitionRamMap; 0] = []; - let avb_verification_flags = AvbVerificationFlags(0); let res = gbl.load_and_verify_image( &mut avb_ops, &mut partitions_ram_map, - avb_verification_flags, + SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE, None, ); - assert_eq!(res.unwrap_err(), Error::AvbSlotVerifyError(SlotVerifyError::Io)); + assert_eq!(res.unwrap_err(), IntegrationError::AvbSlotVerifyError(SlotVerifyError::Io)); } - const TEST_PARTITION_NAME: &str = "test_part"; - const TEST_IMAGE_PATH: &str = "testdata/test_image.img"; - const TEST_VBMETA_PATH: &str = "testdata/test_vbmeta.img"; - const TEST_PUBLIC_KEY_PATH: &str = "testdata/testkey_rsa4096_pub.bin"; + const TEST_ZIRCON_PARTITION_NAME: &str = "zircon_a"; + const TEST_ZIRCON_IMAGE_PATH: &str = "zircon_a.bin"; + const TEST_ZIRCON_VBMETA_PATH: &str = "zircon_a.vbmeta"; + const TEST_PUBLIC_KEY_PATH: &str = "testkey_rsa4096_pub.bin"; const TEST_VBMETA_ROLLBACK_LOCATION: usize = 0; // Default value, we don't explicitly set this. - #[cfg(feature = "sw_digest")] + /// Returns the contents of a test data file. + /// + /// Panicks if the requested file cannot be read. + /// + /// # Arguments + /// * `path`: file path relative to libgbl's `testdata/` directory. + fn testdata(path: &str) -> Vec<u8> { + let full_path = Path::new("external/gbl/libgbl/testdata").join(path); + fs::read(full_path).unwrap() + } + #[test] fn test_load_and_verify_image_stub() { - let mut gbl = GblBuilder::new(DefaultGblOps::new()).build(); + let mut gbl_ops = DefaultGblOps {}; + let mut gbl = GblBuilder::new(&mut gbl_ops).build(); let mut avb_ops = TestOps::default(); - avb_ops.add_partition(TEST_PARTITION_NAME, fs::read(TEST_IMAGE_PATH).unwrap()); - avb_ops.add_partition("vbmeta", fs::read(TEST_VBMETA_PATH).unwrap()); - avb_ops.add_vbmeta_key(fs::read(TEST_PUBLIC_KEY_PATH).unwrap(), None, true); + avb_ops.add_partition(TEST_ZIRCON_PARTITION_NAME, testdata(TEST_ZIRCON_IMAGE_PATH)); + avb_ops.add_partition("vbmeta", testdata(TEST_ZIRCON_VBMETA_PATH)); + avb_ops.default_vbmeta_key = Some(FakeVbmetaKey::Avb { + public_key: testdata(TEST_PUBLIC_KEY_PATH), + public_key_metadata: None, + }); avb_ops.rollbacks.insert(TEST_VBMETA_ROLLBACK_LOCATION, 0); avb_ops.unlock_state = Ok(false); let mut partitions_ram_map: [PartitionRamMap; 0] = []; - let avb_verification_flags = AvbVerificationFlags(0); let res = gbl.load_and_verify_image( &mut avb_ops, &mut partitions_ram_map, - avb_verification_flags, + SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE, None, ); assert!(res.is_ok()); } - #[cfg(feature = "sw_digest")] #[test] fn test_load_and_verify_image_avb_error() { const TEST_ERROR: SlotVerifyError<'static> = SlotVerifyError::Verification(None); let expected_error = SlotVerifyError::Verification(None); - let mut gbl = GblBuilder::new(DefaultGblOps::new()) - .verify_slot(|_, _, _, _, _| Err(TEST_ERROR)) - .build(); + let mut gbl_ops = DefaultGblOps {}; + let mut gbl = + GblBuilder::new(&mut gbl_ops).verify_slot(|_, _, _, _, _| Err(TEST_ERROR)).build(); let mut avb_ops = AvbOpsUnimplemented {}; let mut partitions_ram_map: [PartitionRamMap; 0] = []; - let avb_verification_flags = AvbVerificationFlags(0); let res = gbl.load_and_verify_image( &mut avb_ops, &mut partitions_ram_map, - avb_verification_flags, + SlotVerifyFlags::AVB_SLOT_VERIFY_FLAGS_NONE, None, ); - assert_eq!(res.unwrap_err(), Error::AvbSlotVerifyError(TEST_ERROR)); + assert_eq!(res.unwrap_err(), IntegrationError::AvbSlotVerifyError(TEST_ERROR)); } } diff --git a/gbl/libgbl/src/ops.rs b/gbl/libgbl/src/ops.rs index b89547d..6063c4c 100644 --- a/gbl/libgbl/src/ops.rs +++ b/gbl/libgbl/src/ops.rs @@ -19,15 +19,11 @@ extern crate alloc; #[cfg(test)] extern crate static_assertions; -use crate::digest::{Algorithm, Context}; use crate::error::{Error, Result as GblResult}; -#[cfg(feature = "sw_digest")] -use crate::sw_digest::SwContext; #[cfg(feature = "alloc")] use alloc::ffi::CString; use core::{ fmt::{Debug, Write}, - ptr::NonNull, result::Result, }; use gbl_storage::{ @@ -57,7 +53,7 @@ pub enum BootImages<'a> { } /// `GblOpsError` is the error type returned by required methods in `GblOps`. -#[derive(Default, Debug)] +#[derive(Default, Debug, PartialEq, Eq)] pub struct GblOpsError(Option<&'static str>); // https://stackoverflow.com/questions/41081240/idiomatic-callbacks-in-rust @@ -70,9 +66,6 @@ missing: */ /// Trait that defines callbacks that can be provided to Gbl. pub trait GblOps { - /// Digest context type - type Context: Context; - /// Iterates block devices on the platform. /// /// For each block device, implementation should call `f` with its 1) `BlockIo` trait @@ -99,23 +92,13 @@ pub trait GblOps { /// Implementation is not expected to return on success. fn boot(&mut self, boot_images: BootImages) -> Result<(), GblOpsError>; - /// 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: Algorithm) -> Self::Context { - Context::new(algorithm) - } - - /// Calculate digest of provided data with requested algorithm. Single use unlike [new_digest] - /// flow. - fn digest(&self, algorithm: Algorithm, data: &[u8]) -> <Self::Context as Context>::Digest { - let mut ctx = self.new_digest(algorithm); - ctx.update(data); - ctx.finish() - } + // TODO(b/334962570): figure out how to plumb ops-provided hash implementations into + // libavb. The tricky part is that libavb hashing APIs are global with no way to directly + // correlate the implementation to a particular [GblOps] object, so we'll probably have to + // create a [Context] ahead of time and store it globally for the hashing APIs to access. + // However this would mean that [Context] must be a standalone object and cannot hold a + // reference to [GblOps], which may restrict implementations. + // fn new_digest(&self) -> Option<Self::Context>; /// Callback for when fastboot mode is requested. // Nevertype could be used here when it is stable https://github.com/serde-rs/serde/issues/812 @@ -214,19 +197,23 @@ impl<T: GblOps> Write for GblUtils<'_, '_, T> { #[derive(Debug)] pub struct DefaultGblOps {} -#[cfg(feature = "sw_digest")] impl GblOps for DefaultGblOps { - type Context = SwContext; -} + fn visit_block_devices( + &mut self, + f: &mut dyn FnMut(&mut dyn BlockIo, u64, u64), + ) -> Result<(), GblOpsError> { + Err(GblOpsError(Some("unimplemented"))) + } -#[cfg(test)] -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() } + fn console_put_char(&mut self, ch: u8) -> Result<(), GblOpsError> { + Err(GblOpsError(Some("unimplemented"))) + } + + fn should_stop_in_fastboot(&mut self) -> Result<bool, GblOpsError> { + Err(GblOpsError(Some("unimplemented"))) + } + + fn boot(&mut self, boot_images: BootImages) -> Result<(), GblOpsError> { + Err(GblOpsError(Some("unimplemented"))) } } diff --git a/gbl/libgbl/src/sw_digest.rs b/gbl/libgbl/src/sw_digest.rs deleted file mode 100644 index 5e745f1..0000000 --- a/gbl/libgbl/src/sw_digest.rs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2024, 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. - -//! Software implementation for GBL Digest trait. -//! -//! `sw_digest` feature is needed to use this implementation. - -extern crate ring; - -use crate::digest::{Algorithm, Context, Digest}; - -impl From<Algorithm> for &ring::digest::Algorithm { - fn from(val: Algorithm) -> Self { - match val { - Algorithm::SHA256 => &ring::digest::SHA256, - Algorithm::SHA512 => &ring::digest::SHA512, - } - } -} - -/// Software implementation for digest Context -pub struct SwContext { - algorithm: Algorithm, - ring_context: ring::digest::Context, -} - -impl Context for SwContext { - type Digest = SwDigest; - - fn new(algorithm: Algorithm) -> Self - where - Self: Sized, - { - Self { algorithm, ring_context: ring::digest::Context::new(algorithm.into()) } - } - - fn update(&mut self, input: &[u8]) { - self.ring_context.update(input) - } - - fn finish(self) -> SwDigest { - SwDigest { algorithm: self.algorithm, ring_digest: self.ring_context.finish() } - } - - fn algorithm(&self) -> &Algorithm { - &self.algorithm - } -} - -/// Software implementation of Digest. -pub struct SwDigest { - algorithm: Algorithm, - 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) -> &Algorithm { - &self.algorithm - } -} - -mod tests { - use super::*; - - // Compute digest on provided input using algorithm - fn digest(algorithm: 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(Algorithm::SHA256, input).as_ref(), expected); - } - - #[test] - fn test_swdigest_sha512() { - assert_eq!( - digest(Algorithm::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(Algorithm::SHA256); - for i in input.chunks(input.len()) { - ctx.update(i); - } - - assert_eq!(ctx.finish().as_ref(), expected); - } -} diff --git a/gbl/libgbl/testdata/README.md b/gbl/libgbl/testdata/README.md deleted file mode 100644 index e14ec4f..0000000 --- a/gbl/libgbl/testdata/README.md +++ /dev/null @@ -1,7 +0,0 @@ -Generating test image: -``` -echo test > file.txt -echo "file.txt=file.txt" > manifest -zbi -o test_image.img manifest -rm file.txt manifest -``` diff --git a/gbl/libgbl/testdata/gen_test_data.py b/gbl/libgbl/testdata/gen_test_data.py index a968917..2718571 100644..100755 --- a/gbl/libgbl/testdata/gen_test_data.py +++ b/gbl/libgbl/testdata/gen_test_data.py @@ -17,12 +17,25 @@ import os import pathlib +import random +import shutil import subprocess +import tempfile +from typing import List SCRIPT_DIR = pathlib.Path(os.path.dirname(os.path.realpath(__file__))) GPT_TOOL = pathlib.Path(SCRIPT_DIR.parents[1]) / "tools" / "gen_gpt_disk.py" +AVB_DIR = pathlib.Path(SCRIPT_DIR.parents[4]) / "external" / "avb" +AVB_TOOL = AVB_DIR / "avbtool.py" +AVB_TEST_DATA_DIR = AVB_DIR / "test" / "data" SZ_KB = 1024 +# RNG seed values. Keep the same seed value for a given file to ensure +# reproducibility as much as possible; this will prevent adding a bunch of +# unnecessary test binaries to the git history. +RNG_SEED_SPARSE_TEST_RAW = 1 +RNG_SEED_ZIRCON = {"a": 2, "b": 3, "r": 4} + # A helper for writing bytes to a file at a given offset. def write_file(file, offset, data): @@ -32,58 +45,129 @@ def write_file(file, offset, data): # Generates sparse image for flashing test def gen_sparse_test_file(): - sz_kb = 1024 out_file_raw = SCRIPT_DIR / "sparse_test_raw.bin" + random.seed(RNG_SEED_SPARSE_TEST_RAW) with open(out_file_raw, "wb") as f: # 4k filled with 0x78563412 write_file(f, 0, b"\x12\x34\x56\x78" * 1024) # 8k file hole (will become dont-care with the "-s" option) # 12k raw data - write_file(f, 12 * sz_kb, os.urandom(12 * sz_kb)) + write_file(f, 12 * SZ_KB, random.randbytes(12 * SZ_KB)) # 8k filled with 0x78563412 - write_file(f, 24 * sz_kb, b"\x12\x34\x56\x78" * 1024 * 2) + write_file(f, 24 * SZ_KB, b"\x12\x34\x56\x78" * 1024 * 2) # 12k raw data - write_file(f, 32 * sz_kb, os.urandom(12 * sz_kb)) + write_file(f, 32 * SZ_KB, random.randbytes(12 * SZ_KB)) # 4k filled with 0x78563412 - write_file(f, 44 * sz_kb, b"\x12\x34\x56\x78" * 1024) + write_file(f, 44 * SZ_KB, b"\x12\x34\x56\x78" * 1024) # 8k filled with 0xEFCDAB90 - write_file(f, 48 * sz_kb, b"\x90\xab\xcd\xef" * 1024 * 2) + write_file(f, 48 * SZ_KB, b"\x90\xab\xcd\xef" * 1024 * 2) + # For now this requires that img2simg exists on $PATH. + # It can be built from an Android checkout via `m img2simg`; the resulting + # binary will be at out/host/linux-x86/bin/img2simg. + subprocess.run(["img2simg", "-s", out_file_raw, SCRIPT_DIR / "sparse_test.bin"]) subprocess.run( - ["img2simg", "-s", out_file_raw, SCRIPT_DIR / "sparse_test.bin"]) - subprocess.run([ - "img2simg", - "-s", - out_file_raw, - SCRIPT_DIR / "sparse_test_blk1024.bin", - "1024", - ]) + [ + "img2simg", + "-s", + out_file_raw, + SCRIPT_DIR / "sparse_test_blk1024.bin", + "1024", + ] + ) # Generates GPT disk, kernel data for Zircon tests def gen_zircon_gpt(): gen_gpt_args = [] for suffix in ["a", "b", "r"]: - zircon = os.urandom(16 * SZ_KB) + random.seed(RNG_SEED_ZIRCON[suffix]) + zircon = random.randbytes(16 * SZ_KB) out_file = SCRIPT_DIR / f"zircon_{suffix}.bin" out_file.write_bytes(zircon) gen_gpt_args.append(f"--partition=zircon_{suffix},16K,{str(out_file)}") - subprocess.run([GPT_TOOL, SCRIPT_DIR / "zircon_gpt.bin", "128K"] + - gen_gpt_args, - check=True) + subprocess.run( + [GPT_TOOL, SCRIPT_DIR / "zircon_gpt.bin", "128K"] + gen_gpt_args, check=True + ) # Generates test data for A/B slot Manager writeback test def gen_writeback_test_bin(): - subprocess.run([ - GPT_TOOL, SCRIPT_DIR / "writeback_test_disk.bin", "64K", - "--partition=test_partition,4k,/dev/zero" - ], - check=True) + subprocess.run( + [ + GPT_TOOL, + SCRIPT_DIR / "writeback_test_disk.bin", + "64K", + "--partition=test_partition,4k,/dev/zero", + ], + check=True, + ) + + +def gen_vbmeta(): + """Creates the vbmeta keys and signs some images.""" + # Use the test vbmeta keys from libavb. + for name in ["testkey_rsa4096.pem", "testkey_rsa4096_pub.pem"]: + shutil.copyfile(AVB_TEST_DATA_DIR / name, SCRIPT_DIR / name) + + # Convert the public key to raw bytes for use in verification. + subprocess.run( + [ + AVB_TOOL, + "extract_public_key", + "--key", + SCRIPT_DIR / "testkey_rsa4096_pub.pem", + "--output", + SCRIPT_DIR / "testkey_rsa4096_pub.bin", + ], + check=True, + ) + + with tempfile.TemporaryDirectory() as temp_dir: + temp_dir = pathlib.Path(temp_dir) + + # Create the hash descriptor. We only need this temporarily until we add + # it into the final vbmeta image. + hash_descriptor_path = temp_dir / "hash_descriptor.bin" + subprocess.run( + [ + AVB_TOOL, + "add_hash_footer", + "--dynamic_partition_size", + "--do_not_append_vbmeta_image", + "--partition_name", + "zircon_a", + "--image", + SCRIPT_DIR / "zircon_a.bin", + "--output_vbmeta_image", + hash_descriptor_path, + "--salt", + "2000", + ], + check=True, + ) + + # Create the final signed vbmeta including the hash descriptor. + subprocess.run( + [ + AVB_TOOL, + "make_vbmeta_image", + "--key", + SCRIPT_DIR / "testkey_rsa4096.pem", + "--algorithm", + "SHA512_RSA4096", + "--include_descriptors_from_image", + hash_descriptor_path, + "--output", + SCRIPT_DIR / "zircon_a.vbmeta", + ], + check=True, + ) -if __name__ == '__main__': +if __name__ == "__main__": gen_writeback_test_bin() gen_sparse_test_file() gen_zircon_gpt() + gen_vbmeta() diff --git a/gbl/libgbl/testdata/sparse_test.bin b/gbl/libgbl/testdata/sparse_test.bin Binary files differindex 009689e..f6c1a39 100644 --- a/gbl/libgbl/testdata/sparse_test.bin +++ b/gbl/libgbl/testdata/sparse_test.bin diff --git a/gbl/libgbl/testdata/sparse_test_blk1024.bin b/gbl/libgbl/testdata/sparse_test_blk1024.bin Binary files differindex e273137..c23fdf5 100644 --- a/gbl/libgbl/testdata/sparse_test_blk1024.bin +++ b/gbl/libgbl/testdata/sparse_test_blk1024.bin diff --git a/gbl/libgbl/testdata/sparse_test_raw.bin b/gbl/libgbl/testdata/sparse_test_raw.bin Binary files differindex ab13637..4ca95c7 100644 --- a/gbl/libgbl/testdata/sparse_test_raw.bin +++ b/gbl/libgbl/testdata/sparse_test_raw.bin diff --git a/gbl/libgbl/testdata/test_image.img b/gbl/libgbl/testdata/test_image.img Binary files differdeleted file mode 100644 index c988db1..0000000 --- a/gbl/libgbl/testdata/test_image.img +++ /dev/null diff --git a/gbl/libgbl/testdata/testkey_rsa4096.pem b/gbl/libgbl/testdata/testkey_rsa4096.pem index 6ffa543..26db5c3 100644 --- a/gbl/libgbl/testdata/testkey_rsa4096.pem +++ b/gbl/libgbl/testdata/testkey_rsa4096.pem @@ -1,51 +1,51 @@ -----BEGIN RSA PRIVATE KEY----- -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 -----END RSA PRIVATE KEY----- diff --git a/gbl/libgbl/testdata/testkey_rsa4096.pub.pem b/gbl/libgbl/testdata/testkey_rsa4096.pub.pem deleted file mode 100644 index 7ce6238..0000000 --- a/gbl/libgbl/testdata/testkey_rsa4096.pub.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxibxF3SoULtkS6RWQpuY -H+N2JqLg4Dd5BkCueB0Ii0DGb1YN5/on+e9Czn+iaasA/DsOwpx1/+jCtPgHK08X -w8sRxQWJ6LJ8uV+kZr1h8JMeG8XyMckBTdpEOzomuW691h+hphyHqxkv1VycnmwU -kNz7QeEEKIZSXT07gC7cMDoJv4iDe+vrGlEh7Y5YrxkFJd86iEXFHDghoAxW/EFy -I6bHbO7WQI38eexP1QceXyiz2YNx2krtj1pVyjQ0cLxOG/ZrRsjIffKNT5j9vLYD -/JTybusC5G7FKuE2Y11jTPwhvsGqk8SBiGXmpPBuQXVdDYoU8pEntFfCvsHvoCml -W0CSp6jyz/jlMeFqoydhkDQmpiP+p7SeXSwGIOdhlDQwyZg0j2THfLsr29deATvW -kom43jllweZHDPN7Kxyyy4sCPpO3NXCMlVdaFL9F/EL6Ieptk6X1u5qOgz11f1Uw -BKdLq/gx5wk4SwZLF1b1A2voBZvuQeJKb3iMwoWiE3sS2V2pT3eBI54nMehOgXaK -fSLFxku7uAltHfW5i7tD5QEiHPNXUIfHRYwCucQ2lotKxSkoWB5+Q62pWHCP+1HG -Gdfgt8nhfwsB/fdfJKkEy8MoVlCCGHxyvXgIKG8e85aVVm+Q+9/TtcJYr4osUDe9 -X5xISZ/2BTT7z5N1fLBFA4cCAwEAAQ== ------END PUBLIC KEY----- diff --git a/gbl/libgbl/testdata/testkey_rsa4096_pub.bin b/gbl/libgbl/testdata/testkey_rsa4096_pub.bin Binary files differnew file mode 100644 index 0000000..f2e8fbd --- /dev/null +++ b/gbl/libgbl/testdata/testkey_rsa4096_pub.bin diff --git a/gbl/libgbl/testdata/testkey_rsa4096_pub.pem b/gbl/libgbl/testdata/testkey_rsa4096_pub.pem new file mode 100644 index 0000000..efd7144 --- /dev/null +++ b/gbl/libgbl/testdata/testkey_rsa4096_pub.pem @@ -0,0 +1,14 @@ +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2ASv49OEbH4NiT3CjNMS +VeliyfEPXswWcqtEfCxlSpS1FisAuwbvEwdTTPlkuSh6G4SYiNhnpCP5p0vcSg/3 +OhiuVKgV/rCtrDXaO60nvK/o0y83NNZRK2xaJ9eWBq9ruIDK+jC0sYWzTaqqwxY0 +Grjnx/r5CXerl5PrRK7PILzwgBHbIwxHcblt1ntgR4cWVpO3wiqasEwBDDDYk4fw +7W6LvjBb9qav3YB8RV6PkZNeRP64ggfuecq/MXNiWOPNxLzCER2hSr/+J32h9jWj +XsrcVy8+8Mldhmr4r2an7c247aFfupuFGtUJrpROO8/LXMl5gPfMpkqoatjTMRH5 +9gJjKhot0RpmGxZBvb33TcBK5SdJX39Y4yct5clmDlI4Fjj7FutTP+b96aJeJVnY +eUX/A0wmogBajsJRoRX5e/RcgZsYRzXYLQXprQ81dBWjjovMJ9p8XeT6BNMFC7o6 +sklFL0fHDUE/l4BNP8G1u3BfpzevSCISRS71D4eS4oQB+RIPFBUkzomZ7rnEF3Bw +Feq+xmwfYrP0LRaH+1YeRauuMuReke1TZl697a3mEjkNg8noa2wtpe7EWmaujJfX +DWxJx/XEkjGLCe4z2qk3tkkY+A5gRcgzke8gVxC+eC2DJtbKYfkv4L8FMFJaEhwA +p13MfC7FlYujO/BDLl7dANsCAwEAAQ== +-----END PUBLIC KEY----- diff --git a/gbl/libgbl/testdata/writeback_test_disk.bin b/gbl/libgbl/testdata/writeback_test_disk.bin Binary files differindex ebb428d..50cbc21 100644 --- a/gbl/libgbl/testdata/writeback_test_disk.bin +++ b/gbl/libgbl/testdata/writeback_test_disk.bin diff --git a/gbl/libgbl/testdata/zircon_a.bin b/gbl/libgbl/testdata/zircon_a.bin Binary files differindex bd30d49..10e7ce4 100644 --- a/gbl/libgbl/testdata/zircon_a.bin +++ b/gbl/libgbl/testdata/zircon_a.bin diff --git a/gbl/libgbl/testdata/zircon_a.vbmeta b/gbl/libgbl/testdata/zircon_a.vbmeta Binary files differnew file mode 100644 index 0000000..13e15d1 --- /dev/null +++ b/gbl/libgbl/testdata/zircon_a.vbmeta diff --git a/gbl/libgbl/testdata/zircon_b.bin b/gbl/libgbl/testdata/zircon_b.bin Binary files differindex aa9faa9..fb38746 100644 --- a/gbl/libgbl/testdata/zircon_b.bin +++ b/gbl/libgbl/testdata/zircon_b.bin diff --git a/gbl/libgbl/testdata/zircon_gpt.bin b/gbl/libgbl/testdata/zircon_gpt.bin Binary files differindex 54624ff..f873c07 100644 --- a/gbl/libgbl/testdata/zircon_gpt.bin +++ b/gbl/libgbl/testdata/zircon_gpt.bin diff --git a/gbl/libgbl/testdata/zircon_r.bin b/gbl/libgbl/testdata/zircon_r.bin Binary files differindex 653837e..d7fd360 100644 --- a/gbl/libgbl/testdata/zircon_r.bin +++ b/gbl/libgbl/testdata/zircon_r.bin diff --git a/gbl/libgbl/tests/integration_tests.rs b/gbl/libgbl/tests/integration_tests.rs index e4764fe..ebbdcb4 100644 --- a/gbl/libgbl/tests/integration_tests.rs +++ b/gbl/libgbl/tests/integration_tests.rs @@ -14,10 +14,7 @@ use gbl_storage::BlockIo; use gbl_storage_testlib::TestBlockIo; -use libgbl::{ - digest::Algorithm, BootImages, Context, Digest, FuchsiaBootImages, GblBuilder, GblOps, - GblOpsError, -}; +use libgbl::{BootImages, FuchsiaBootImages, GblBuilder, GblOps, GblOpsError}; use std::{collections::VecDeque, vec::Vec}; extern crate avb_sysdeps; @@ -52,8 +49,6 @@ impl TestGblOps<'_> { } impl GblOps for TestGblOps<'_> { - type Context = TestDigestContext; - fn visit_block_devices( &mut self, f: &mut dyn FnMut(&mut dyn BlockIo, u64, u64), @@ -77,44 +72,6 @@ impl GblOps for TestGblOps<'_> { } } -/// Placeholder. -struct DigestBytes(Vec<u8>); - -impl AsRef<[u8]> for DigestBytes { - fn as_ref(&self) -> &[u8] { - self.0.as_ref() - } -} - -impl Digest for DigestBytes { - fn algorithm(&self) -> &Algorithm { - unimplemented!(); - } -} - -/// Placeholder. -struct TestDigestContext {} - -impl Context for TestDigestContext { - type Digest = DigestBytes; - - fn new(_: Algorithm) -> Self { - unimplemented!(); - } - - fn update(&mut self, _: &[u8]) { - unimplemented!(); - } - - fn finish(self) -> Self::Digest { - unimplemented!(); - } - - fn algorithm(&self) -> &Algorithm { - unimplemented!(); - } -} - /// `MustUse` wraps an object and checks that it is accessed at least once before it's dropped. /// In this integration test, it is mainly used to check that test provided ops callbacks are run. struct MustUse<T: ?Sized> { diff --git a/gbl/libstorage/src/lib.rs b/gbl/libstorage/src/lib.rs index 40d4a1c..8a27836 100644 --- a/gbl/libstorage/src/lib.rs +++ b/gbl/libstorage/src/lib.rs @@ -122,7 +122,7 @@ pub use multi_blocks::AsMultiBlockDevices; pub type Result<T> = core::result::Result<T, StorageError>; /// Error code for this library. -#[derive(Debug, Copy, Clone, PartialEq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum StorageError { ArithmeticOverflow(safemath::Error), BlockDeviceNotFound, diff --git a/gbl/readme.bzl b/gbl/readme.bzl new file mode 100644 index 0000000..cc8bdc9 --- /dev/null +++ b/gbl/readme.bzl @@ -0,0 +1,117 @@ +# Copyright (C) 2024 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. + +""" +Action that verifies all EFI protocols used by GBL are explicitly listed in README.md +""" + +load("@rules_rust//rust/private:providers.bzl", "CrateInfo") + +def _readme_test_rule_impl(ctx): + shell_script = """ +while [[ $# -gt 0 ]]; do + case $1 in + --in) + INPUT=$2 + shift + shift + ;; + --out) + OUTPUT=$2 + shift + shift + ;; + --readme) + README=$2 + shift + shift + ;; + *) + echo "Unexpected argument: $1" + exit 1 + ;; + esac +done + +if [ ! -f $README ]; then + echo "README file doesn't exist: ${README}" + exit 1 +fi + +ALL_INPUTS=$(echo ${INPUT} | sed 's/,/ /g') + +DOCLESS_PROTOCOLS="" +PROTOCOLS=($(grep -hE 'impl ProtocolInfo for .* \\{' ${ALL_INPUTS} | awk '{print $4}' | sort)) +for P in ${PROTOCOLS[@]} +do + grep -Lq $P ${README} || DOCLESS_PROTOCOLS+="\n\t$P" +done + +if [ ! -z "${DOCLESS_PROTOCOLS}" ]; then + echo -e "Missing documentation for protocol(s):$DOCLESS_PROTOCOLS" + exit 1 +fi + +UNUSED_PROTOCOLS="" +README_PROTOCOLS=($(grep -P " ?.*?Protocol$" ${README} | awk '{print $NF}' | sort | uniq)) +for P in ${README_PROTOCOLS[@]} +do + grep -qhE "impl ProtocolInfo for $P" ${ALL_INPUTS} || UNUSED_PROTOCOLS+="\n\t$P" +done + +if [ ! -z "${UNUSED_PROTOCOLS}" ]; then + echo -e "Unused protocol(s) found in documentation:$UNUSED_PROTOCOLS" + exit 1 +fi + +touch $OUTPUT +""" + + out_file = ctx.actions.declare_file("%s.script" % ctx.attr.name) + in_files = [s for d in ctx.attr.deps for s in d[CrateInfo].srcs.to_list()] + readme = ctx.attr.readme + args = ctx.actions.args() + args.add_joined( + "--in", + in_files, + join_with = ",", + ) + args.add( + "--out", + out_file, + ) + args.add( + "--readme", + readme[DefaultInfo].files.to_list()[0], + ) + ctx.actions.run_shell( + inputs = in_files + readme[DefaultInfo].files.to_list(), + outputs = [out_file], + arguments = [args], + command = shell_script, + ) + return [DefaultInfo(executable = out_file)] + +readme_test = rule( + implementation = _readme_test_rule_impl, + attrs = { + "deps": attr.label_list( + providers = [CrateInfo], + ), + "readme": attr.label( + allow_single_file = [".md"], + ), + }, + test = True, +) diff --git a/gbl/tests/BUILD b/gbl/tests/BUILD index 3a9fbda..e51b5cd 100644 --- a/gbl/tests/BUILD +++ b/gbl/tests/BUILD @@ -15,6 +15,7 @@ test_suite( name = "tests", tests = [ + "@gbl//:readme_test", "@gbl//libabr:libabr_tests", "@gbl//libbootconfig:libbootconfig_test", "@gbl//libbootimg:libbootimg_test", diff --git a/gbl/tools/gen_gpt_disk.py b/gbl/tools/gen_gpt_disk.py index 09428e7..883880d 100755 --- a/gbl/tools/gen_gpt_disk.py +++ b/gbl/tools/gen_gpt_disk.py @@ -25,6 +25,8 @@ The following command creates a 16mb disk file gpt.bin with two partitions: gen_gpt_disk.py ./gpt.bin 16M --partition "boot_a,4096K,<path to file_a>" \ --partition "boot_b,8192K," + +All GUIDs will be non-deterministic for reproducibility. """ import argparse @@ -37,18 +39,46 @@ import tempfile GPT_BLOCK_SIZE = 512 +# Some GPT libraries may expect a valid GUID here, these are just pre-generated +# valid GUIDs for reproducibility. +DISK_GUID = "b116114b-b6ae-4186-8231-b35104cb4c9e" +PARTITION_GUIDS = [ + "36bc51fd-c3d6-4109-a2ac-35bddd757e2a", + "42aaac2e-37e3-43ba-9930-42dfa96e6334", + "bdadfeca-879c-43e9-8f0d-8ef7da29b5e7", + "8d5b90b2-0d65-476b-8691-94f74fcac271", + "dafe682f-3f2b-4472-a1b8-a0f217701909", + "7097fd78-f559-461e-bb9b-db176a8169d8", + "c03dd176-117b-4e65-8205-37ebec007c1a", + "6d9159db-9b9e-4906-8fa4-31f5ffaef50e", + "4cdfda9f-23aa-4b27-9fea-24bb08238135", + "2a0a3df9-e8ef-4627-b4ce-ef1e847924f4", + "3c9b64f9-c659-4e5d-9d25-72028c46e6b8", + "95f142f9-d1f3-41ad-96dc-b969ee8242a1", + "5181811b-e836-4e66-bfe2-91aa86da515f", + "f2dbad77-38ac-43de-b4c7-2f7432d2339e", + "fc172d2c-f735-49a5-8be0-a475d0fc5be9", + "5ad48195-8e26-4496-a7e2-669028db2dce", + "f49a6965-8168-4c0c-8102-7b64a8176c25", + "be495262-5d9b-4fcb-9240-d9e84b195abe", + "c5ab3a8d-f898-420f-9039-a7445760fb0f", + "bc79912b-44bf-4ed8-8320-796ba47714d1", +] + def parse_args(): parser = argparse.ArgumentParser() - parser.add_argument('out', help="output file") - parser.add_argument('disk_size', - type=str, - help="disk size of the image. i.e. 64k, 1M etc") - parser.add_argument("--partition", - type=str, - action='append', - help="specifies a partition. Format should be" - "--partition=<part name>,<size>,<file name>") + parser.add_argument("out", help="output file") + parser.add_argument( + "disk_size", type=str, help="disk size of the image. i.e. 64k, 1M etc" + ) + parser.add_argument( + "--partition", + type=str, + action="append", + help="specifies a partition. Format should be" + "--partition=<part name>,<size>,<file name>", + ) return parser.parse_args() @@ -80,13 +110,12 @@ def _append(src_file: str, offset: int, size: int, dst_file: str): Returns: number of bytes actually copied. """ - with open(src_file, 'rb') as src_f: + with open(src_file, "rb") as src_f: src_f.seek(offset, 0) data = src_f.read(size) if len(data) != size: - raise ValueError( - f"Want {size} bytes from {src_file}, but got {len(data)}.") - with open(dst_file, 'ab') as dst_f: + raise ValueError(f"Want {size} bytes from {src_file}, but got {len(data)}.") + with open(dst_file, "ab") as dst_f: dst_f.write(data) return size @@ -97,26 +126,34 @@ def main() -> int: temp_disk = pathlib.Path(temp_dir) / "temp_disk" disk_size = parse_size_str(args.disk_size) - temp_disk.write_bytes(disk_size * b'\x00') + temp_disk.write_bytes(disk_size * b"\x00") part_start = 34 * GPT_BLOCK_SIZE # MBR + GPT header + entries partition_info = [] sgdisk_command = [ "sgdisk", - str(temp_disk), "--clear", "--set-alignment", "1" + str(temp_disk), + "--clear", + "--set-alignment", + "1", + "--disk-guid", + DISK_GUID, ] for i, part in enumerate(args.partition, start=1): - name, size, file = part.split(',') + name, size, file = part.split(",") if not size: raise ValueError("Must provide a size") size = parse_size_str(size) sgdisk_command += [ "--new", - f"{i}:{part_start // GPT_BLOCK_SIZE}:{(part_start + size) // GPT_BLOCK_SIZE - 1}" + f"{i}:{part_start // GPT_BLOCK_SIZE}:{(part_start + size) // GPT_BLOCK_SIZE - 1}", + "--partition-guid", + f"{i}:{PARTITION_GUIDS[i]}", + "--change-name", + f"{i}:{name}", ] - sgdisk_command += ["--change-name", f"{i}:{name}"] partition_info.append((name, part_start, size, file)) part_start += size @@ -125,13 +162,14 @@ def main() -> int: # Create the final disk with partition content dest_file = pathlib.Path(args.out) - dest_file.write_bytes(0 * b'\x00') + dest_file.write_bytes(0 * b"\x00") append_offset = 0 for name, start, size, file in partition_info: end = start + size # Fill gap - append_offset += _append(str(temp_disk), append_offset, - start - append_offset, args.out) + append_offset += _append( + str(temp_disk), append_offset, start - append_offset, args.out + ) # Copy over file if file: @@ -142,15 +180,17 @@ def main() -> int: # If partition size greater than file size, copy the remaining # partition content from `temp_disk` (zeroes) - append_offset += _append(str(temp_disk), append_offset, - end - append_offset, args.out) + append_offset += _append( + str(temp_disk), append_offset, end - append_offset, args.out + ) # Copy the remaining disk from `temp_disk` (zeroes + back up header/entries) - append_offset += _append(str(temp_disk), append_offset, - disk_size - append_offset, args.out) + append_offset += _append( + str(temp_disk), append_offset, disk_size - append_offset, args.out + ) return 0 -if __name__ == '__main__': +if __name__ == "__main__": sys.exit(main()) |