Add AB slots protocol header definition, bindgen target, and rust wrapper Add the header definition for the GBL AB slot UEFI protocol, including protocol struct definition, enum/constant definitions, GUID, and data structures. Add a rust wrapper for the protocol in libefi. Change-Id: Ib15051119a86c67c575ff2d9218cd9ca2b6972c2
diff --git a/gbl/docs/efi_protocols.md b/gbl/docs/efi_protocols.md index 32d09f3..becdbc0 100644 --- a/gbl/docs/efi_protocols.md +++ b/gbl/docs/efi_protocols.md
@@ -63,3 +63,9 @@ #### SimpleTextInputProtocol TODO: remove this protocol + +#### GblSlotProtocol + +If present, the system is expected to boot Android. This protocol abstracts over +implementation details in the system AB metadata and provides a common interface +for GBL.
diff --git a/gbl/libefi/BUILD b/gbl/libefi/BUILD index 639b361..e3e55a0 100644 --- a/gbl/libefi/BUILD +++ b/gbl/libefi/BUILD
@@ -35,6 +35,7 @@ hdrs = [ "defs/boot_service.h", "defs/efi.h", + "defs/protocols/ab_slot_protocol.h", "defs/protocols/android_boot_protocol.h", "defs/protocols/block_io_protocol.h", "defs/protocols/device_path_protocol.h", @@ -102,6 +103,7 @@ "src/defs.rs", # Generated by :efi_defs_genrule "src/lib.rs", "src/protocol.rs", + "src/protocol/ab_slot.rs", "src/protocol/android_boot.rs", "src/protocol/block_io.rs", "src/protocol/device_path.rs",
diff --git a/gbl/libefi/defs/efi.h b/gbl/libefi/defs/efi.h index 2bbe5e3..37b5cee 100644 --- a/gbl/libefi/defs/efi.h +++ b/gbl/libefi/defs/efi.h
@@ -23,6 +23,7 @@ #include <stdint.h> #include "boot_service.h" +#include "protocols/ab_slot_protocol.h" #include "protocols/android_boot_protocol.h" #include "protocols/block_io_protocol.h" #include "protocols/device_path_protocol.h"
diff --git a/gbl/libefi/defs/protocols/ab_slot_protocol.h b/gbl/libefi/defs/protocols/ab_slot_protocol.h new file mode 100644 index 0000000..54baee6 --- /dev/null +++ b/gbl/libefi/defs/protocols/ab_slot_protocol.h
@@ -0,0 +1,93 @@ +/* + * 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. + * + */ + +#ifndef __AB_SLOT_PROTOCOL_H__ +#define __AB_SLOT_PROTOCOL_H__ + +#include "system_table.h" +#include "types.h" + +typedef enum EFI_UNBOOTABLE_REASON { + UNKNOWN_REASON = 0, + NO_MORE_TRIES, + SYSTEM_UPDATE, + USER_REQUESTED, + VERIFICATION_FAILURE, +} EfiUnbootableReason; + +typedef enum EFI_BOOT_REASON { + EMPTY_EFI_BOOT_REASON = 0, + UNKNOWN_EFI_BOOT_REASON = 1, + WATCHDOG = 14, + KERNEL_PANIC = 15, + RECOVERY = 3, + BOOTLOADER = 55, + COLD = 56, + HARD = 57, + WARM = 58, + SHUTDOWN, + REBOOT = 18, +} EfiBootReason; + +typedef struct EfiGblSlotInfo { + // One UTF-8 encoded single character + uint32_t suffix; + // Any value other than those explicitly enumerated in EFI_UNBOOTABLE_REASON + // will be interpreted as UNKNOWN_REASON. + uint32_t unbootable_reason; + uint8_t priority; + uint8_t tries; + // Value of 1 if slot has successfully booted. + uint8_t successful; + uint8_t merge_status; +} EfiGblSlotInfo; + +typedef struct EfiGblSlotMetadataBlock { + uint32_t boot_reason; + // Value of 1 if persistent metadata tracks slot unbootable reasons. + uint8_t unbootable_metadata; + uint8_t max_retries; + uint8_t slot_count; +} EfiGblSlotMetadataBlock; + +typedef struct EfiGblSlotProtocol { + // Currently must contain 0x00010000 + uint32_t version; + // Slot metadata query methods + EfiStatus (*load_boot_data)(struct EfiGblSlotProtocol*, + EfiGblSlotMetadataBlock* /* out param*/); + EfiStatus (*get_slot_info)(struct EfiGblSlotProtocol*, uint8_t, + EfiGblSlotInfo* /* out param */); + EfiStatus (*get_current_slot)(struct EfiGblSlotProtocol*, + EfiGblSlotInfo* /* out param */); + // Slot metadata manipulation methods + EfiStatus (*set_active_slot)(struct EfiGblSlotProtocol*, uint8_t); + EfiStatus (*set_slot_unbootable)(struct EfiGblSlotProtocol*, uint8_t, + uint32_t); + EfiStatus (*mark_boot_attempt)(struct EfiGblSlotProtocol*); + EfiStatus (*reinitialize)(struct EfiGblSlotProtocol*); + // Miscellaneous methods + EfiStatus (*get_boot_reason)(struct EfiGblSlotProtocol*, + uint32_t* /* out param */, + size_t* /* in-out param */, + uint8_t* /* out param*/); + EfiStatus (*set_boot_reason)(struct EfiGblSlotProtocol*, uint32_t, size_t, + const uint8_t*); + EfiStatus (*flush)(struct EfiGblSlotProtocol*); +} EfiGblSlotProtocol; + +#endif
diff --git a/gbl/libefi/defs/protocols/loaded_image_protocol.h b/gbl/libefi/defs/protocols/loaded_image_protocol.h index 820907a..69a46f2 100644 --- a/gbl/libefi/defs/protocols/loaded_image_protocol.h +++ b/gbl/libefi/defs/protocols/loaded_image_protocol.h
@@ -15,6 +15,9 @@ * */ +#ifndef __LOADED_IMAGE_PROTOCOL_H__ +#define __LOADED_IMAGE_PROTOCOL_H__ + #include "system_table.h" #include "types.h" @@ -36,3 +39,5 @@ EfiStatus (*unload)(EfiHandle img); } EfiLoadedImageProtocol; + +#endif
diff --git a/gbl/libefi/src/protocol.rs b/gbl/libefi/src/protocol.rs index 309d4a7..9896d48 100644 --- a/gbl/libefi/src/protocol.rs +++ b/gbl/libefi/src/protocol.rs
@@ -17,6 +17,7 @@ use crate::defs::*; use crate::{DeviceHandle, EfiEntry, EfiResult}; +pub mod ab_slot; pub mod android_boot; pub mod block_io; pub mod device_path;
diff --git a/gbl/libefi/src/protocol/ab_slot.rs b/gbl/libefi/src/protocol/ab_slot.rs new file mode 100644 index 0000000..91bc1d0 --- /dev/null +++ b/gbl/libefi/src/protocol/ab_slot.rs
@@ -0,0 +1,163 @@ +// 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. + +use crate::defs::{ + EfiBootReason, EfiGblSlotInfo, EfiGblSlotMetadataBlock, EfiGblSlotProtocol, EfiGuid, + EfiUnbootableReason, EFI_STATUS_INVALID_PARAMETER, EFI_STATUS_NOT_FOUND, +}; +use crate::protocol::{Protocol, ProtocolInfo}; +use crate::{efi_call, error::EfiError, map_efi_err, EfiResult}; + +pub struct GblSlotProtocol; + +impl ProtocolInfo for GblSlotProtocol { + type InterfaceType = EfiGblSlotProtocol; + + const GUID: EfiGuid = + EfiGuid::new(0xDEADBEEF, 0xCAFE, 0xD00D, [0xCA, 0xBB, 0xA6, 0xE5, 0xCA, 0xBB, 0xA6, 0xE5]); +} + +impl<'a> Protocol<'a, GblSlotProtocol> { + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.load_boot_data()` + pub fn load_boot_data(&self) -> EfiResult<EfiGblSlotMetadataBlock> { + let mut block: EfiGblSlotMetadataBlock = Default::default(); + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` is an input parameter and will not be retained. It outlives the call. + // `block` is an output parameter and will not be retained. It outlives the call. + unsafe { efi_call!(self.interface()?.load_boot_data, self.interface, &mut block)? } + Ok(block) + } + + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.get_slot_info()` + pub fn get_slot_info(&self, idx: u8) -> EfiResult<EfiGblSlotInfo> { + let mut info: EfiGblSlotInfo = Default::default(); + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` is an input parameter and will not be retained. It outlives the call. + // `info` is an output parameter and will not be retained. It outlives the call. + unsafe { efi_call!(self.interface()?.get_slot_info, self.interface, idx, &mut info,)? } + Ok(info) + } + + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.get_current_slot()` + pub fn get_current_slot(&self) -> EfiResult<EfiGblSlotInfo> { + let mut info: EfiGblSlotInfo = Default::default(); + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` is an input parameter and will not be retained. It outlives the call. + // `info` is an output parameter and will not be retained. It outlives the call. + unsafe { efi_call!(self.interface()?.get_current_slot, self.interface, &mut info)? } + Ok(info) + } + + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.set_active_slot()` + pub fn set_active_slot(&self, idx: u8) -> EfiResult<()> { + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` is an input parameter and will not be retained. It outlives the call. + unsafe { efi_call!(self.interface()?.set_active_slot, self.interface, idx) } + } + + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.set_slot_unbootable()` + pub fn set_slot_unbootable(&self, idx: u8, reason: EfiUnbootableReason) -> EfiResult<()> { + let reason: u32 = + reason.try_into().or(Err(EfiError::from(EFI_STATUS_INVALID_PARAMETER)))?; + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` is an input parameter and will not be retained. It outlives the call. + unsafe { efi_call!(self.interface()?.set_slot_unbootable, self.interface, idx, reason) } + } + + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.mark_boot_attempt()` + pub fn mark_boot_attempt(&self) -> EfiResult<()> { + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` is an input parameter and will not be retained. It outlives the call. + unsafe { efi_call!(self.interface()?.mark_boot_attempt, self.interface) } + } + + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.reinitialize()` + pub fn reinitialize(&self) -> EfiResult<()> { + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` is an input parameter and will not be retained. It outlives the call. + unsafe { efi_call!(self.interface()?.reinitialize, self.interface) } + } + + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.get_boot_reason()` + pub fn get_boot_reason(&self, subreason: &mut [u8]) -> EfiResult<(EfiBootReason, usize)> { + let mut reason: u32 = 0; + let mut subreason_size = subreason.len(); + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` is an input parameter and will not be retained. It outlives the call. + // `reason` is an output parameter. It is not retained, and it outlives the call. + // `subreason_size` is an in-out parameter. It is not retained, and it outlives the call. + // `subreason` remains valid during the call. + unsafe { + efi_call!( + self.interface()?.get_boot_reason, + self.interface, + &mut reason, + &mut subreason_size, + subreason.as_mut_ptr(), + )? + } + + let reason: EfiBootReason = + reason.try_into().or(Err(EfiError::from(EFI_STATUS_INVALID_PARAMETER)))?; + Ok((reason, subreason_size)) + } + + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.set_boot_reason()` + pub fn set_boot_reason(&self, reason: EfiBootReason, subreason: &[u8]) -> EfiResult<()> { + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` is an input parameter and will not be retained. It outlives the call. + // `subreason` is not modified or retained. It outlives the call. + unsafe { + efi_call!( + self.interface()?.set_boot_reason, + self.interface, + reason.try_into().or(Err(EfiError::from(EFI_STATUS_INVALID_PARAMETER)))?, + subreason.len(), + subreason.as_ptr(), + ) + } + } + + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.flush()` + pub fn flush(&self) -> EfiResult<()> { + // SAFETY: + // `self.interface()?` guarantees self.interface is non-null and points to a valid object + // established by `Protocol::new()`. + // `self.interface` is an input parameter and will not be retained. It outlives the call. + unsafe { efi_call!(self.interface()?.flush, self.interface) } + } + + /// Wrapper of `EFI_GBL_SLOT_PROTOCOL.version` + pub fn version(&self) -> EfiResult<u32> { + Ok(self.interface()?.version) + } +}