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)
+    }
+}