xref: /aosp_15_r20/bootable/libbootloader/gbl/libefi/src/ab_slots.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1*5225e6b1SAndroid Build Coastguard Worker // Copyright 2024, The Android Open Source Project
2*5225e6b1SAndroid Build Coastguard Worker //
3*5225e6b1SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*5225e6b1SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*5225e6b1SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*5225e6b1SAndroid Build Coastguard Worker //
7*5225e6b1SAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*5225e6b1SAndroid Build Coastguard Worker //
9*5225e6b1SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*5225e6b1SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*5225e6b1SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*5225e6b1SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*5225e6b1SAndroid Build Coastguard Worker // limitations under the License.
14*5225e6b1SAndroid Build Coastguard Worker 
15*5225e6b1SAndroid Build Coastguard Worker extern crate gbl_storage;
16*5225e6b1SAndroid Build Coastguard Worker extern crate libgbl as gbl;
17*5225e6b1SAndroid Build Coastguard Worker 
18*5225e6b1SAndroid Build Coastguard Worker use core::convert::TryInto;
19*5225e6b1SAndroid Build Coastguard Worker use gbl::slots::{
20*5225e6b1SAndroid Build Coastguard Worker     BootTarget, BootToken, Manager, OneShot, RecoveryTarget, Slot, SlotIterator, Suffix, Tries,
21*5225e6b1SAndroid Build Coastguard Worker     UnbootableReason,
22*5225e6b1SAndroid Build Coastguard Worker };
23*5225e6b1SAndroid Build Coastguard Worker use liberror::{Error, Result};
24*5225e6b1SAndroid Build Coastguard Worker 
25*5225e6b1SAndroid Build Coastguard Worker use efi_types::{
26*5225e6b1SAndroid Build Coastguard Worker     GBL_EFI_BOOT_REASON_BOOTLOADER as REASON_BOOTLOADER,
27*5225e6b1SAndroid Build Coastguard Worker     GBL_EFI_BOOT_REASON_EMPTY_BOOT_REASON as REASON_EMPTY,
28*5225e6b1SAndroid Build Coastguard Worker     GBL_EFI_BOOT_REASON_RECOVERY as REASON_RECOVERY,
29*5225e6b1SAndroid Build Coastguard Worker };
30*5225e6b1SAndroid Build Coastguard Worker 
31*5225e6b1SAndroid Build Coastguard Worker use crate::protocol::{gbl_efi_ab_slot as ab_slot, Protocol};
32*5225e6b1SAndroid Build Coastguard Worker 
33*5225e6b1SAndroid Build Coastguard Worker const SUBREASON_BUF_LEN: usize = 64;
34*5225e6b1SAndroid Build Coastguard Worker 
35*5225e6b1SAndroid Build Coastguard Worker /// Implementation for A/B slot manager based on custom EFI protocol.
36*5225e6b1SAndroid Build Coastguard Worker pub struct ABManager<'a> {
37*5225e6b1SAndroid Build Coastguard Worker     protocol: Protocol<'a, ab_slot::GblSlotProtocol>,
38*5225e6b1SAndroid Build Coastguard Worker     boot_token: Option<BootToken>,
39*5225e6b1SAndroid Build Coastguard Worker     last_set_active_idx: Option<u8>,
40*5225e6b1SAndroid Build Coastguard Worker }
41*5225e6b1SAndroid Build Coastguard Worker 
42*5225e6b1SAndroid Build Coastguard Worker impl<'a> ABManager<'a> {
43*5225e6b1SAndroid Build Coastguard Worker     #[cfg(test)]
new_without_token(protocol: Protocol<'a, ab_slot::GblSlotProtocol>) -> Self44*5225e6b1SAndroid Build Coastguard Worker     fn new_without_token(protocol: Protocol<'a, ab_slot::GblSlotProtocol>) -> Self {
45*5225e6b1SAndroid Build Coastguard Worker         Self { protocol, boot_token: None, last_set_active_idx: None }
46*5225e6b1SAndroid Build Coastguard Worker     }
47*5225e6b1SAndroid Build Coastguard Worker }
48*5225e6b1SAndroid Build Coastguard Worker 
49*5225e6b1SAndroid Build Coastguard Worker impl gbl::slots::private::SlotGet for ABManager<'_> {
get_slot_by_number(&self, number: usize) -> Result<Slot>50*5225e6b1SAndroid Build Coastguard Worker     fn get_slot_by_number(&self, number: usize) -> Result<Slot> {
51*5225e6b1SAndroid Build Coastguard Worker         let idx = u8::try_from(number).or(Err(Error::BadIndex(number)))?;
52*5225e6b1SAndroid Build Coastguard Worker         let info = self.protocol.get_slot_info(idx).or(Err(Error::BadIndex(number)))?;
53*5225e6b1SAndroid Build Coastguard Worker         info.try_into()
54*5225e6b1SAndroid Build Coastguard Worker     }
55*5225e6b1SAndroid Build Coastguard Worker }
56*5225e6b1SAndroid Build Coastguard Worker 
57*5225e6b1SAndroid Build Coastguard Worker impl Manager for ABManager<'_> {
get_boot_target(&self) -> Result<BootTarget>58*5225e6b1SAndroid Build Coastguard Worker     fn get_boot_target(&self) -> Result<BootTarget> {
59*5225e6b1SAndroid Build Coastguard Worker         let slot = self.get_slot_last_set_active()?;
60*5225e6b1SAndroid Build Coastguard Worker         let mut subreason = [0u8; SUBREASON_BUF_LEN];
61*5225e6b1SAndroid Build Coastguard Worker         let (reason, _) = self.protocol.get_boot_reason(subreason.as_mut_slice())?;
62*5225e6b1SAndroid Build Coastguard Worker         // Don't currently care about the subreason
63*5225e6b1SAndroid Build Coastguard Worker         // CStr::from_bytes_until_nul(&subreason[..strlen])?
64*5225e6b1SAndroid Build Coastguard Worker         let target = match reason {
65*5225e6b1SAndroid Build Coastguard Worker             REASON_RECOVERY => BootTarget::Recovery(RecoveryTarget::Slotted(slot)),
66*5225e6b1SAndroid Build Coastguard Worker             _ => BootTarget::NormalBoot(slot),
67*5225e6b1SAndroid Build Coastguard Worker         };
68*5225e6b1SAndroid Build Coastguard Worker         Ok(target)
69*5225e6b1SAndroid Build Coastguard Worker     }
70*5225e6b1SAndroid Build Coastguard Worker 
slots_iter(&self) -> SlotIterator71*5225e6b1SAndroid Build Coastguard Worker     fn slots_iter(&self) -> SlotIterator {
72*5225e6b1SAndroid Build Coastguard Worker         SlotIterator::new(self)
73*5225e6b1SAndroid Build Coastguard Worker     }
74*5225e6b1SAndroid Build Coastguard Worker 
get_slot_last_set_active(&self) -> Result<Slot>75*5225e6b1SAndroid Build Coastguard Worker     fn get_slot_last_set_active(&self) -> Result<Slot> {
76*5225e6b1SAndroid Build Coastguard Worker         use gbl::slots::private::SlotGet;
77*5225e6b1SAndroid Build Coastguard Worker 
78*5225e6b1SAndroid Build Coastguard Worker         if let Some(idx) = self.last_set_active_idx {
79*5225e6b1SAndroid Build Coastguard Worker             self.get_slot_by_number(idx.into())
80*5225e6b1SAndroid Build Coastguard Worker         } else {
81*5225e6b1SAndroid Build Coastguard Worker             self.protocol.get_current_slot()?.try_into()
82*5225e6b1SAndroid Build Coastguard Worker         }
83*5225e6b1SAndroid Build Coastguard Worker     }
84*5225e6b1SAndroid Build Coastguard Worker 
mark_boot_attempt(&mut self) -> Result<BootToken>85*5225e6b1SAndroid Build Coastguard Worker     fn mark_boot_attempt(&mut self) -> Result<BootToken> {
86*5225e6b1SAndroid Build Coastguard Worker         self.boot_token.take().ok_or(Error::OperationProhibited)
87*5225e6b1SAndroid Build Coastguard Worker     }
88*5225e6b1SAndroid Build Coastguard Worker 
set_active_slot(&mut self, slot_suffix: Suffix) -> Result<()>89*5225e6b1SAndroid Build Coastguard Worker     fn set_active_slot(&mut self, slot_suffix: Suffix) -> Result<()> {
90*5225e6b1SAndroid Build Coastguard Worker         let idx: u8 = self
91*5225e6b1SAndroid Build Coastguard Worker             .slots_iter()
92*5225e6b1SAndroid Build Coastguard Worker             .position(|s| s.suffix == slot_suffix)
93*5225e6b1SAndroid Build Coastguard Worker             .ok_or(Error::InvalidInput)?
94*5225e6b1SAndroid Build Coastguard Worker             .try_into()
95*5225e6b1SAndroid Build Coastguard Worker             // This 'or' is technically unreachable because the protocol
96*5225e6b1SAndroid Build Coastguard Worker             // can't give us an index larger than a u8.
97*5225e6b1SAndroid Build Coastguard Worker             .or(Err(Error::Other(None)))?;
98*5225e6b1SAndroid Build Coastguard Worker         self.protocol.set_active_slot(idx).or(Err(Error::Other(None))).and_then(|_| {
99*5225e6b1SAndroid Build Coastguard Worker             self.last_set_active_idx = Some(idx);
100*5225e6b1SAndroid Build Coastguard Worker             Ok(())
101*5225e6b1SAndroid Build Coastguard Worker         })
102*5225e6b1SAndroid Build Coastguard Worker     }
103*5225e6b1SAndroid Build Coastguard Worker 
set_slot_unbootable(&mut self, slot_suffix: Suffix, reason: UnbootableReason) -> Result<()>104*5225e6b1SAndroid Build Coastguard Worker     fn set_slot_unbootable(&mut self, slot_suffix: Suffix, reason: UnbootableReason) -> Result<()> {
105*5225e6b1SAndroid Build Coastguard Worker         let idx: u8 = self
106*5225e6b1SAndroid Build Coastguard Worker             .slots_iter()
107*5225e6b1SAndroid Build Coastguard Worker             .position(|s| s.suffix == slot_suffix)
108*5225e6b1SAndroid Build Coastguard Worker             .ok_or(Error::InvalidInput)?
109*5225e6b1SAndroid Build Coastguard Worker             .try_into()
110*5225e6b1SAndroid Build Coastguard Worker             // This 'or' is technically unreachable because the protocol
111*5225e6b1SAndroid Build Coastguard Worker             // can't give us an index larger than a u8.
112*5225e6b1SAndroid Build Coastguard Worker             .or(Err(Error::Other(None)))?;
113*5225e6b1SAndroid Build Coastguard Worker         self.protocol.set_slot_unbootable(idx, u8::from(reason).into())
114*5225e6b1SAndroid Build Coastguard Worker     }
115*5225e6b1SAndroid Build Coastguard Worker 
get_max_retries(&self) -> Result<Tries>116*5225e6b1SAndroid Build Coastguard Worker     fn get_max_retries(&self) -> Result<Tries> {
117*5225e6b1SAndroid Build Coastguard Worker         Ok(self.protocol.load_boot_data()?.max_retries.into())
118*5225e6b1SAndroid Build Coastguard Worker     }
119*5225e6b1SAndroid Build Coastguard Worker 
get_oneshot_status(&self) -> Option<OneShot>120*5225e6b1SAndroid Build Coastguard Worker     fn get_oneshot_status(&self) -> Option<OneShot> {
121*5225e6b1SAndroid Build Coastguard Worker         let mut subreason = [0u8; SUBREASON_BUF_LEN];
122*5225e6b1SAndroid Build Coastguard Worker         let (reason, _) = self.protocol.get_boot_reason(subreason.as_mut_slice()).ok()?;
123*5225e6b1SAndroid Build Coastguard Worker         // Currently we only care if the primary boot reason is BOOTLOADER.
124*5225e6b1SAndroid Build Coastguard Worker         // CStr::from_bytes_until_nul(&subreason[..strlen]).ok()?
125*5225e6b1SAndroid Build Coastguard Worker         match reason {
126*5225e6b1SAndroid Build Coastguard Worker             REASON_BOOTLOADER => Some(OneShot::Bootloader),
127*5225e6b1SAndroid Build Coastguard Worker             _ => None,
128*5225e6b1SAndroid Build Coastguard Worker         }
129*5225e6b1SAndroid Build Coastguard Worker     }
130*5225e6b1SAndroid Build Coastguard Worker 
set_oneshot_status(&mut self, os: OneShot) -> Result<()>131*5225e6b1SAndroid Build Coastguard Worker     fn set_oneshot_status(&mut self, os: OneShot) -> Result<()> {
132*5225e6b1SAndroid Build Coastguard Worker         // Android doesn't have a concept of OneShot to recovery,
133*5225e6b1SAndroid Build Coastguard Worker         // and the subreason shouldn't matter.
134*5225e6b1SAndroid Build Coastguard Worker         match os {
135*5225e6b1SAndroid Build Coastguard Worker             OneShot::Bootloader => {
136*5225e6b1SAndroid Build Coastguard Worker                 self.protocol.set_boot_reason(REASON_BOOTLOADER, &[]).or(Err(Error::Other(None)))
137*5225e6b1SAndroid Build Coastguard Worker             }
138*5225e6b1SAndroid Build Coastguard Worker             _ => Err(Error::OperationProhibited),
139*5225e6b1SAndroid Build Coastguard Worker         }
140*5225e6b1SAndroid Build Coastguard Worker     }
141*5225e6b1SAndroid Build Coastguard Worker 
clear_oneshot_status(&mut self)142*5225e6b1SAndroid Build Coastguard Worker     fn clear_oneshot_status(&mut self) {
143*5225e6b1SAndroid Build Coastguard Worker         let mut subreason = [0u8; SUBREASON_BUF_LEN];
144*5225e6b1SAndroid Build Coastguard Worker         // Only clear if the boot reason is the one we care about.
145*5225e6b1SAndroid Build Coastguard Worker         // CStr::from_bytes_until_nul(&subreason[..strlen]).or(Err(Error::Other))?
146*5225e6b1SAndroid Build Coastguard Worker         if let Ok((REASON_BOOTLOADER, _)) = self.protocol.get_boot_reason(subreason.as_mut_slice())
147*5225e6b1SAndroid Build Coastguard Worker         {
148*5225e6b1SAndroid Build Coastguard Worker             let _ = self.protocol.set_boot_reason(REASON_EMPTY, &[]);
149*5225e6b1SAndroid Build Coastguard Worker         }
150*5225e6b1SAndroid Build Coastguard Worker     }
151*5225e6b1SAndroid Build Coastguard Worker 
write_back(&mut self, _: &mut dyn FnMut(&mut [u8]) -> Result<()>)152*5225e6b1SAndroid Build Coastguard Worker     fn write_back(&mut self, _: &mut dyn FnMut(&mut [u8]) -> Result<()>) {
153*5225e6b1SAndroid Build Coastguard Worker         // Note: `expect` instead of swallowing the error.
154*5225e6b1SAndroid Build Coastguard Worker         // It is important that changes are not silently dropped.
155*5225e6b1SAndroid Build Coastguard Worker         self.protocol.flush().expect("could not write back modifications to slot metadata");
156*5225e6b1SAndroid Build Coastguard Worker     }
157*5225e6b1SAndroid Build Coastguard Worker }
158*5225e6b1SAndroid Build Coastguard Worker 
159*5225e6b1SAndroid Build Coastguard Worker #[cfg(test)]
160*5225e6b1SAndroid Build Coastguard Worker mod test {
161*5225e6b1SAndroid Build Coastguard Worker     extern crate avb_sysdeps;
162*5225e6b1SAndroid Build Coastguard Worker 
163*5225e6b1SAndroid Build Coastguard Worker     use super::*;
164*5225e6b1SAndroid Build Coastguard Worker     use crate::protocol::Protocol;
165*5225e6b1SAndroid Build Coastguard Worker     use crate::test::*;
166*5225e6b1SAndroid Build Coastguard Worker     use crate::EfiEntry;
167*5225e6b1SAndroid Build Coastguard Worker     use efi_types::{
168*5225e6b1SAndroid Build Coastguard Worker         EfiStatus, GblEfiABSlotProtocol, GblEfiSlotInfo, GblEfiSlotMetadataBlock,
169*5225e6b1SAndroid Build Coastguard Worker         EFI_STATUS_INVALID_PARAMETER, EFI_STATUS_SUCCESS,
170*5225e6b1SAndroid Build Coastguard Worker         GBL_EFI_BOOT_REASON_EMPTY_BOOT_REASON as REASON_EMPTY,
171*5225e6b1SAndroid Build Coastguard Worker         GBL_EFI_BOOT_REASON_RECOVERY as REASON_RECOVERY,
172*5225e6b1SAndroid Build Coastguard Worker         GBL_EFI_BOOT_REASON_WATCHDOG as REASON_WATCHDOG,
173*5225e6b1SAndroid Build Coastguard Worker     };
174*5225e6b1SAndroid Build Coastguard Worker     use gbl::{
175*5225e6b1SAndroid Build Coastguard Worker         ops::{
176*5225e6b1SAndroid Build Coastguard Worker             AvbIoResult, CertPermanentAttributes, RebootReason, SlotsMetadata, SHA256_DIGEST_SIZE,
177*5225e6b1SAndroid Build Coastguard Worker         },
178*5225e6b1SAndroid Build Coastguard Worker         partition::GblDisk,
179*5225e6b1SAndroid Build Coastguard Worker         slots::{Bootability, Cursor, RecoveryTarget, UnbootableReason},
180*5225e6b1SAndroid Build Coastguard Worker         Gbl, GblOps, Os, Result as GblResult,
181*5225e6b1SAndroid Build Coastguard Worker     };
182*5225e6b1SAndroid Build Coastguard Worker     use gbl_storage::{BlockIo, BlockIoNull, Disk, Gpt};
183*5225e6b1SAndroid Build Coastguard Worker     use libgbl::{
184*5225e6b1SAndroid Build Coastguard Worker         device_tree::DeviceTreeComponentsRegistry,
185*5225e6b1SAndroid Build Coastguard Worker         gbl_avb::state::{BootStateColor, KeyValidationStatus},
186*5225e6b1SAndroid Build Coastguard Worker         ops::ImageBuffer,
187*5225e6b1SAndroid Build Coastguard Worker     };
188*5225e6b1SAndroid Build Coastguard Worker     // TODO(b/350526796): use ptr.is_aligned() when Rust 1.79 is in Android
189*5225e6b1SAndroid Build Coastguard Worker     use core::ops::DerefMut;
190*5225e6b1SAndroid Build Coastguard Worker     use std::{
191*5225e6b1SAndroid Build Coastguard Worker         ffi::CStr,
192*5225e6b1SAndroid Build Coastguard Worker         fmt::Write,
193*5225e6b1SAndroid Build Coastguard Worker         mem::align_of,
194*5225e6b1SAndroid Build Coastguard Worker         num::NonZeroUsize,
195*5225e6b1SAndroid Build Coastguard Worker         sync::atomic::{AtomicBool, AtomicU32, Ordering},
196*5225e6b1SAndroid Build Coastguard Worker     };
197*5225e6b1SAndroid Build Coastguard Worker     use zbi::ZbiContainer;
198*5225e6b1SAndroid Build Coastguard Worker 
199*5225e6b1SAndroid Build Coastguard Worker     // The thread-local atomics are an ugly, ugly hack to pass state between
200*5225e6b1SAndroid Build Coastguard Worker     // the protocol method functions and the rest of the test body.
201*5225e6b1SAndroid Build Coastguard Worker     // Because the variables are thread-local, it is safe to run tests concurrently
202*5225e6b1SAndroid Build Coastguard Worker     // so long as they establish correct initial values.
203*5225e6b1SAndroid Build Coastguard Worker     // Also, because no atomic is being read or written to by more than one thread,
204*5225e6b1SAndroid Build Coastguard Worker     // Ordering::Relaxed is perfectly fine.
205*5225e6b1SAndroid Build Coastguard Worker     thread_local! {
206*5225e6b1SAndroid Build Coastguard Worker         static ATOMIC: AtomicBool = AtomicBool::new(false);
207*5225e6b1SAndroid Build Coastguard Worker     }
208*5225e6b1SAndroid Build Coastguard Worker 
209*5225e6b1SAndroid Build Coastguard Worker     thread_local! {
210*5225e6b1SAndroid Build Coastguard Worker         static BOOT_REASON: AtomicU32 = AtomicU32::new(REASON_EMPTY);
211*5225e6b1SAndroid Build Coastguard Worker     }
212*5225e6b1SAndroid Build Coastguard Worker 
213*5225e6b1SAndroid Build Coastguard Worker     // This provides reasonable defaults for all tests that need to get slot info.
214*5225e6b1SAndroid Build Coastguard Worker     //
215*5225e6b1SAndroid Build Coastguard Worker     // SAFETY: checks that `info` is properly aligned and not null.
216*5225e6b1SAndroid Build Coastguard Worker     // Caller must make sure `info` points to a valid GblEfiSlotInfo struct.
get_info( _: *mut GblEfiABSlotProtocol, idx: u8, info: *mut GblEfiSlotInfo, ) -> EfiStatus217*5225e6b1SAndroid Build Coastguard Worker     unsafe extern "C" fn get_info(
218*5225e6b1SAndroid Build Coastguard Worker         _: *mut GblEfiABSlotProtocol,
219*5225e6b1SAndroid Build Coastguard Worker         idx: u8,
220*5225e6b1SAndroid Build Coastguard Worker         info: *mut GblEfiSlotInfo,
221*5225e6b1SAndroid Build Coastguard Worker     ) -> EfiStatus {
222*5225e6b1SAndroid Build Coastguard Worker         // TODO(b/350526796): use ptr.is_aligned() when Rust 1.79 is in Android
223*5225e6b1SAndroid Build Coastguard Worker         if !info.is_null() && (info as usize) % align_of::<GblEfiSlotInfo>() == 0 && idx < 3 {
224*5225e6b1SAndroid Build Coastguard Worker             let slot_info = GblEfiSlotInfo {
225*5225e6b1SAndroid Build Coastguard Worker                 suffix: ('a' as u8 + idx).into(),
226*5225e6b1SAndroid Build Coastguard Worker                 unbootable_reason: 0,
227*5225e6b1SAndroid Build Coastguard Worker                 priority: idx + 1,
228*5225e6b1SAndroid Build Coastguard Worker                 tries: idx,
229*5225e6b1SAndroid Build Coastguard Worker                 successful: 2 & idx,
230*5225e6b1SAndroid Build Coastguard Worker             };
231*5225e6b1SAndroid Build Coastguard Worker             unsafe { *info = slot_info };
232*5225e6b1SAndroid Build Coastguard Worker             EFI_STATUS_SUCCESS
233*5225e6b1SAndroid Build Coastguard Worker         } else {
234*5225e6b1SAndroid Build Coastguard Worker             EFI_STATUS_INVALID_PARAMETER
235*5225e6b1SAndroid Build Coastguard Worker         }
236*5225e6b1SAndroid Build Coastguard Worker     }
237*5225e6b1SAndroid Build Coastguard Worker 
flush(_: *mut GblEfiABSlotProtocol) -> EfiStatus238*5225e6b1SAndroid Build Coastguard Worker     extern "C" fn flush(_: *mut GblEfiABSlotProtocol) -> EfiStatus {
239*5225e6b1SAndroid Build Coastguard Worker         ATOMIC.with(|a| a.store(true, Ordering::Relaxed));
240*5225e6b1SAndroid Build Coastguard Worker         EFI_STATUS_SUCCESS
241*5225e6b1SAndroid Build Coastguard Worker     }
242*5225e6b1SAndroid Build Coastguard Worker 
243*5225e6b1SAndroid Build Coastguard Worker     struct TestGblOps<'a> {
244*5225e6b1SAndroid Build Coastguard Worker         manager: ABManager<'a>,
245*5225e6b1SAndroid Build Coastguard Worker     }
246*5225e6b1SAndroid Build Coastguard Worker 
247*5225e6b1SAndroid Build Coastguard Worker     impl<'a> TestGblOps<'a> {
new(protocol: Protocol<'a, ab_slot::GblSlotProtocol>) -> Self248*5225e6b1SAndroid Build Coastguard Worker         fn new(protocol: Protocol<'a, ab_slot::GblSlotProtocol>) -> Self {
249*5225e6b1SAndroid Build Coastguard Worker             Self { manager: ABManager::new_without_token(protocol) }
250*5225e6b1SAndroid Build Coastguard Worker         }
251*5225e6b1SAndroid Build Coastguard Worker     }
252*5225e6b1SAndroid Build Coastguard Worker 
253*5225e6b1SAndroid Build Coastguard Worker     impl<'a, 'd> GblOps<'a, 'd> for TestGblOps<'_> {
console_out(&mut self) -> Option<&mut dyn Write>254*5225e6b1SAndroid Build Coastguard Worker         fn console_out(&mut self) -> Option<&mut dyn Write> {
255*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
256*5225e6b1SAndroid Build Coastguard Worker         }
257*5225e6b1SAndroid Build Coastguard Worker 
should_stop_in_fastboot(&mut self) -> Result<bool>258*5225e6b1SAndroid Build Coastguard Worker         fn should_stop_in_fastboot(&mut self) -> Result<bool> {
259*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
260*5225e6b1SAndroid Build Coastguard Worker         }
261*5225e6b1SAndroid Build Coastguard Worker 
reboot(&mut self)262*5225e6b1SAndroid Build Coastguard Worker         fn reboot(&mut self) {
263*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
264*5225e6b1SAndroid Build Coastguard Worker         }
265*5225e6b1SAndroid Build Coastguard Worker 
disks( &self, ) -> &'a [GblDisk< Disk<impl BlockIo + 'a, impl DerefMut<Target = [u8]> + 'a>, Gpt<impl DerefMut<Target = [u8]> + 'a>, >]266*5225e6b1SAndroid Build Coastguard Worker         fn disks(
267*5225e6b1SAndroid Build Coastguard Worker             &self,
268*5225e6b1SAndroid Build Coastguard Worker         ) -> &'a [GblDisk<
269*5225e6b1SAndroid Build Coastguard Worker             Disk<impl BlockIo + 'a, impl DerefMut<Target = [u8]> + 'a>,
270*5225e6b1SAndroid Build Coastguard Worker             Gpt<impl DerefMut<Target = [u8]> + 'a>,
271*5225e6b1SAndroid Build Coastguard Worker         >] {
272*5225e6b1SAndroid Build Coastguard Worker             &[] as &[GblDisk<Disk<BlockIoNull, &mut [u8]>, Gpt<&mut [u8]>>]
273*5225e6b1SAndroid Build Coastguard Worker         }
274*5225e6b1SAndroid Build Coastguard Worker 
expected_os(&mut self) -> Result<Option<Os>>275*5225e6b1SAndroid Build Coastguard Worker         fn expected_os(&mut self) -> Result<Option<Os>> {
276*5225e6b1SAndroid Build Coastguard Worker             Ok(None)
277*5225e6b1SAndroid Build Coastguard Worker         }
278*5225e6b1SAndroid Build Coastguard Worker 
zircon_add_device_zbi_items(&mut self, _: &mut ZbiContainer<&mut [u8]>) -> Result<()>279*5225e6b1SAndroid Build Coastguard Worker         fn zircon_add_device_zbi_items(&mut self, _: &mut ZbiContainer<&mut [u8]>) -> Result<()> {
280*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
281*5225e6b1SAndroid Build Coastguard Worker         }
282*5225e6b1SAndroid Build Coastguard Worker 
get_zbi_bootloader_files_buffer(&mut self) -> Option<&mut [u8]>283*5225e6b1SAndroid Build Coastguard Worker         fn get_zbi_bootloader_files_buffer(&mut self) -> Option<&mut [u8]> {
284*5225e6b1SAndroid Build Coastguard Worker             None
285*5225e6b1SAndroid Build Coastguard Worker         }
286*5225e6b1SAndroid Build Coastguard Worker 
load_slot_interface<'b>( &'b mut self, persist: &'b mut dyn FnMut(&mut [u8]) -> Result<()>, boot_token: BootToken, ) -> GblResult<Cursor<'b>>287*5225e6b1SAndroid Build Coastguard Worker         fn load_slot_interface<'b>(
288*5225e6b1SAndroid Build Coastguard Worker             &'b mut self,
289*5225e6b1SAndroid Build Coastguard Worker             persist: &'b mut dyn FnMut(&mut [u8]) -> Result<()>,
290*5225e6b1SAndroid Build Coastguard Worker             boot_token: BootToken,
291*5225e6b1SAndroid Build Coastguard Worker         ) -> GblResult<Cursor<'b>> {
292*5225e6b1SAndroid Build Coastguard Worker             self.manager.boot_token = Some(boot_token);
293*5225e6b1SAndroid Build Coastguard Worker             Ok(Cursor { ctx: &mut self.manager, persist })
294*5225e6b1SAndroid Build Coastguard Worker         }
295*5225e6b1SAndroid Build Coastguard Worker 
avb_read_is_device_unlocked(&mut self) -> AvbIoResult<bool>296*5225e6b1SAndroid Build Coastguard Worker         fn avb_read_is_device_unlocked(&mut self) -> AvbIoResult<bool> {
297*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
298*5225e6b1SAndroid Build Coastguard Worker         }
299*5225e6b1SAndroid Build Coastguard Worker 
avb_read_rollback_index(&mut self, _rollback_index_location: usize) -> AvbIoResult<u64>300*5225e6b1SAndroid Build Coastguard Worker         fn avb_read_rollback_index(&mut self, _rollback_index_location: usize) -> AvbIoResult<u64> {
301*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
302*5225e6b1SAndroid Build Coastguard Worker         }
303*5225e6b1SAndroid Build Coastguard Worker 
avb_write_rollback_index( &mut self, _rollback_index_location: usize, _index: u64, ) -> AvbIoResult<()>304*5225e6b1SAndroid Build Coastguard Worker         fn avb_write_rollback_index(
305*5225e6b1SAndroid Build Coastguard Worker             &mut self,
306*5225e6b1SAndroid Build Coastguard Worker             _rollback_index_location: usize,
307*5225e6b1SAndroid Build Coastguard Worker             _index: u64,
308*5225e6b1SAndroid Build Coastguard Worker         ) -> AvbIoResult<()> {
309*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
310*5225e6b1SAndroid Build Coastguard Worker         }
311*5225e6b1SAndroid Build Coastguard Worker 
avb_validate_vbmeta_public_key( &self, _public_key: &[u8], _public_key_metadata: Option<&[u8]>, ) -> AvbIoResult<KeyValidationStatus>312*5225e6b1SAndroid Build Coastguard Worker         fn avb_validate_vbmeta_public_key(
313*5225e6b1SAndroid Build Coastguard Worker             &self,
314*5225e6b1SAndroid Build Coastguard Worker             _public_key: &[u8],
315*5225e6b1SAndroid Build Coastguard Worker             _public_key_metadata: Option<&[u8]>,
316*5225e6b1SAndroid Build Coastguard Worker         ) -> AvbIoResult<KeyValidationStatus> {
317*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
318*5225e6b1SAndroid Build Coastguard Worker         }
319*5225e6b1SAndroid Build Coastguard Worker 
avb_cert_read_permanent_attributes( &mut self, _attributes: &mut CertPermanentAttributes, ) -> AvbIoResult<()>320*5225e6b1SAndroid Build Coastguard Worker         fn avb_cert_read_permanent_attributes(
321*5225e6b1SAndroid Build Coastguard Worker             &mut self,
322*5225e6b1SAndroid Build Coastguard Worker             _attributes: &mut CertPermanentAttributes,
323*5225e6b1SAndroid Build Coastguard Worker         ) -> AvbIoResult<()> {
324*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
325*5225e6b1SAndroid Build Coastguard Worker         }
326*5225e6b1SAndroid Build Coastguard Worker 
avb_cert_read_permanent_attributes_hash( &mut self, ) -> AvbIoResult<[u8; SHA256_DIGEST_SIZE]>327*5225e6b1SAndroid Build Coastguard Worker         fn avb_cert_read_permanent_attributes_hash(
328*5225e6b1SAndroid Build Coastguard Worker             &mut self,
329*5225e6b1SAndroid Build Coastguard Worker         ) -> AvbIoResult<[u8; SHA256_DIGEST_SIZE]> {
330*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
331*5225e6b1SAndroid Build Coastguard Worker         }
332*5225e6b1SAndroid Build Coastguard Worker 
avb_read_persistent_value( &mut self, _name: &CStr, _value: &mut [u8], ) -> AvbIoResult<usize>333*5225e6b1SAndroid Build Coastguard Worker         fn avb_read_persistent_value(
334*5225e6b1SAndroid Build Coastguard Worker             &mut self,
335*5225e6b1SAndroid Build Coastguard Worker             _name: &CStr,
336*5225e6b1SAndroid Build Coastguard Worker             _value: &mut [u8],
337*5225e6b1SAndroid Build Coastguard Worker         ) -> AvbIoResult<usize> {
338*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
339*5225e6b1SAndroid Build Coastguard Worker         }
340*5225e6b1SAndroid Build Coastguard Worker 
avb_write_persistent_value(&mut self, _name: &CStr, _value: &[u8]) -> AvbIoResult<()>341*5225e6b1SAndroid Build Coastguard Worker         fn avb_write_persistent_value(&mut self, _name: &CStr, _value: &[u8]) -> AvbIoResult<()> {
342*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
343*5225e6b1SAndroid Build Coastguard Worker         }
344*5225e6b1SAndroid Build Coastguard Worker 
avb_erase_persistent_value(&mut self, _name: &CStr) -> AvbIoResult<()>345*5225e6b1SAndroid Build Coastguard Worker         fn avb_erase_persistent_value(&mut self, _name: &CStr) -> AvbIoResult<()> {
346*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
347*5225e6b1SAndroid Build Coastguard Worker         }
348*5225e6b1SAndroid Build Coastguard Worker 
avb_handle_verification_result( &mut self, _color: BootStateColor, _digest: Option<&CStr>, _boot_os_version: Option<&[u8]>, _boot_security_patch: Option<&[u8]>, _system_os_version: Option<&[u8]>, _system_security_patch: Option<&[u8]>, _vendor_os_version: Option<&[u8]>, _vendor_security_patch: Option<&[u8]>, ) -> AvbIoResult<()>349*5225e6b1SAndroid Build Coastguard Worker         fn avb_handle_verification_result(
350*5225e6b1SAndroid Build Coastguard Worker             &mut self,
351*5225e6b1SAndroid Build Coastguard Worker             _color: BootStateColor,
352*5225e6b1SAndroid Build Coastguard Worker             _digest: Option<&CStr>,
353*5225e6b1SAndroid Build Coastguard Worker             _boot_os_version: Option<&[u8]>,
354*5225e6b1SAndroid Build Coastguard Worker             _boot_security_patch: Option<&[u8]>,
355*5225e6b1SAndroid Build Coastguard Worker             _system_os_version: Option<&[u8]>,
356*5225e6b1SAndroid Build Coastguard Worker             _system_security_patch: Option<&[u8]>,
357*5225e6b1SAndroid Build Coastguard Worker             _vendor_os_version: Option<&[u8]>,
358*5225e6b1SAndroid Build Coastguard Worker             _vendor_security_patch: Option<&[u8]>,
359*5225e6b1SAndroid Build Coastguard Worker         ) -> AvbIoResult<()> {
360*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
361*5225e6b1SAndroid Build Coastguard Worker         }
362*5225e6b1SAndroid Build Coastguard Worker 
get_image_buffer( &mut self, _image_name: &str, _size: NonZeroUsize, ) -> GblResult<ImageBuffer<'d>>363*5225e6b1SAndroid Build Coastguard Worker         fn get_image_buffer(
364*5225e6b1SAndroid Build Coastguard Worker             &mut self,
365*5225e6b1SAndroid Build Coastguard Worker             _image_name: &str,
366*5225e6b1SAndroid Build Coastguard Worker             _size: NonZeroUsize,
367*5225e6b1SAndroid Build Coastguard Worker         ) -> GblResult<ImageBuffer<'d>> {
368*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
369*5225e6b1SAndroid Build Coastguard Worker         }
370*5225e6b1SAndroid Build Coastguard Worker 
get_custom_device_tree(&mut self) -> Option<&'a [u8]>371*5225e6b1SAndroid Build Coastguard Worker         fn get_custom_device_tree(&mut self) -> Option<&'a [u8]> {
372*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
373*5225e6b1SAndroid Build Coastguard Worker         }
374*5225e6b1SAndroid Build Coastguard Worker 
fixup_os_commandline<'c>( &mut self, _commandline: &CStr, _fixup_buffer: &'c mut [u8], ) -> Result<Option<&'c str>>375*5225e6b1SAndroid Build Coastguard Worker         fn fixup_os_commandline<'c>(
376*5225e6b1SAndroid Build Coastguard Worker             &mut self,
377*5225e6b1SAndroid Build Coastguard Worker             _commandline: &CStr,
378*5225e6b1SAndroid Build Coastguard Worker             _fixup_buffer: &'c mut [u8],
379*5225e6b1SAndroid Build Coastguard Worker         ) -> Result<Option<&'c str>> {
380*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
381*5225e6b1SAndroid Build Coastguard Worker         }
382*5225e6b1SAndroid Build Coastguard Worker 
fixup_bootconfig<'c>( &mut self, _bootconfig: &[u8], _fixup_buffer: &'c mut [u8], ) -> Result<Option<&'c [u8]>>383*5225e6b1SAndroid Build Coastguard Worker         fn fixup_bootconfig<'c>(
384*5225e6b1SAndroid Build Coastguard Worker             &mut self,
385*5225e6b1SAndroid Build Coastguard Worker             _bootconfig: &[u8],
386*5225e6b1SAndroid Build Coastguard Worker             _fixup_buffer: &'c mut [u8],
387*5225e6b1SAndroid Build Coastguard Worker         ) -> Result<Option<&'c [u8]>> {
388*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
389*5225e6b1SAndroid Build Coastguard Worker         }
390*5225e6b1SAndroid Build Coastguard Worker 
fixup_device_tree(&mut self, _device_tree: &mut [u8]) -> Result<()>391*5225e6b1SAndroid Build Coastguard Worker         fn fixup_device_tree(&mut self, _device_tree: &mut [u8]) -> Result<()> {
392*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
393*5225e6b1SAndroid Build Coastguard Worker         }
394*5225e6b1SAndroid Build Coastguard Worker 
select_device_trees( &mut self, _components: &mut DeviceTreeComponentsRegistry, ) -> Result<()>395*5225e6b1SAndroid Build Coastguard Worker         fn select_device_trees(
396*5225e6b1SAndroid Build Coastguard Worker             &mut self,
397*5225e6b1SAndroid Build Coastguard Worker             _components: &mut DeviceTreeComponentsRegistry,
398*5225e6b1SAndroid Build Coastguard Worker         ) -> Result<()> {
399*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
400*5225e6b1SAndroid Build Coastguard Worker         }
401*5225e6b1SAndroid Build Coastguard Worker 
fastboot_variable<'arg>( &mut self, _: &CStr, _: impl Iterator<Item = &'arg CStr> + Clone, _: &mut [u8], ) -> Result<usize>402*5225e6b1SAndroid Build Coastguard Worker         fn fastboot_variable<'arg>(
403*5225e6b1SAndroid Build Coastguard Worker             &mut self,
404*5225e6b1SAndroid Build Coastguard Worker             _: &CStr,
405*5225e6b1SAndroid Build Coastguard Worker             _: impl Iterator<Item = &'arg CStr> + Clone,
406*5225e6b1SAndroid Build Coastguard Worker             _: &mut [u8],
407*5225e6b1SAndroid Build Coastguard Worker         ) -> Result<usize> {
408*5225e6b1SAndroid Build Coastguard Worker             unimplemented!()
409*5225e6b1SAndroid Build Coastguard Worker         }
410*5225e6b1SAndroid Build Coastguard Worker 
fastboot_visit_all_variables(&mut self, _: impl FnMut(&[&CStr], &CStr)) -> Result<()>411*5225e6b1SAndroid Build Coastguard Worker         fn fastboot_visit_all_variables(&mut self, _: impl FnMut(&[&CStr], &CStr)) -> Result<()> {
412*5225e6b1SAndroid Build Coastguard Worker             unimplemented!()
413*5225e6b1SAndroid Build Coastguard Worker         }
414*5225e6b1SAndroid Build Coastguard Worker 
slots_metadata(&mut self) -> Result<SlotsMetadata>415*5225e6b1SAndroid Build Coastguard Worker         fn slots_metadata(&mut self) -> Result<SlotsMetadata> {
416*5225e6b1SAndroid Build Coastguard Worker             unimplemented!();
417*5225e6b1SAndroid Build Coastguard Worker         }
418*5225e6b1SAndroid Build Coastguard Worker 
get_current_slot(&mut self) -> Result<Slot>419*5225e6b1SAndroid Build Coastguard Worker         fn get_current_slot(&mut self) -> Result<Slot> {
420*5225e6b1SAndroid Build Coastguard Worker             unimplemented!()
421*5225e6b1SAndroid Build Coastguard Worker         }
422*5225e6b1SAndroid Build Coastguard Worker 
get_next_slot(&mut self, _: bool) -> Result<Slot>423*5225e6b1SAndroid Build Coastguard Worker         fn get_next_slot(&mut self, _: bool) -> Result<Slot> {
424*5225e6b1SAndroid Build Coastguard Worker             unimplemented!()
425*5225e6b1SAndroid Build Coastguard Worker         }
426*5225e6b1SAndroid Build Coastguard Worker 
set_active_slot(&mut self, _: u8) -> Result<()>427*5225e6b1SAndroid Build Coastguard Worker         fn set_active_slot(&mut self, _: u8) -> Result<()> {
428*5225e6b1SAndroid Build Coastguard Worker             unimplemented!()
429*5225e6b1SAndroid Build Coastguard Worker         }
430*5225e6b1SAndroid Build Coastguard Worker 
set_reboot_reason(&mut self, _: RebootReason) -> Result<()>431*5225e6b1SAndroid Build Coastguard Worker         fn set_reboot_reason(&mut self, _: RebootReason) -> Result<()> {
432*5225e6b1SAndroid Build Coastguard Worker             unimplemented!()
433*5225e6b1SAndroid Build Coastguard Worker         }
434*5225e6b1SAndroid Build Coastguard Worker 
get_reboot_reason(&mut self) -> Result<RebootReason>435*5225e6b1SAndroid Build Coastguard Worker         fn get_reboot_reason(&mut self) -> Result<RebootReason> {
436*5225e6b1SAndroid Build Coastguard Worker             unimplemented!()
437*5225e6b1SAndroid Build Coastguard Worker         }
438*5225e6b1SAndroid Build Coastguard Worker     }
439*5225e6b1SAndroid Build Coastguard Worker 
440*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_manager_flush_on_close()441*5225e6b1SAndroid Build Coastguard Worker     fn test_manager_flush_on_close() {
442*5225e6b1SAndroid Build Coastguard Worker         ATOMIC.with(|a| a.store(false, Ordering::Relaxed));
443*5225e6b1SAndroid Build Coastguard Worker         run_test(|image_handle, systab_ptr| {
444*5225e6b1SAndroid Build Coastguard Worker             let mut ab = GblEfiABSlotProtocol { flush: Some(flush), ..Default::default() };
445*5225e6b1SAndroid Build Coastguard Worker             let efi_entry = EfiEntry { image_handle, systab_ptr };
446*5225e6b1SAndroid Build Coastguard Worker             let protocol = generate_protocol::<ab_slot::GblSlotProtocol>(&efi_entry, &mut ab);
447*5225e6b1SAndroid Build Coastguard Worker 
448*5225e6b1SAndroid Build Coastguard Worker             {
449*5225e6b1SAndroid Build Coastguard Worker                 let mut persist = |_: &mut [u8]| Ok(());
450*5225e6b1SAndroid Build Coastguard Worker                 let mut test_ops = TestGblOps::new(protocol);
451*5225e6b1SAndroid Build Coastguard Worker                 let mut gbl = Gbl::<TestGblOps>::new(&mut test_ops);
452*5225e6b1SAndroid Build Coastguard Worker                 let _ = gbl.load_slot_interface(&mut persist).unwrap();
453*5225e6b1SAndroid Build Coastguard Worker             }
454*5225e6b1SAndroid Build Coastguard Worker         });
455*5225e6b1SAndroid Build Coastguard Worker         assert!(ATOMIC.with(|a| a.load(Ordering::Relaxed)));
456*5225e6b1SAndroid Build Coastguard Worker     }
457*5225e6b1SAndroid Build Coastguard Worker 
458*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_iterator()459*5225e6b1SAndroid Build Coastguard Worker     fn test_iterator() {
460*5225e6b1SAndroid Build Coastguard Worker         run_test(|image_handle, systab_ptr| {
461*5225e6b1SAndroid Build Coastguard Worker             let mut ab = GblEfiABSlotProtocol {
462*5225e6b1SAndroid Build Coastguard Worker                 get_slot_info: Some(get_info),
463*5225e6b1SAndroid Build Coastguard Worker                 flush: Some(flush),
464*5225e6b1SAndroid Build Coastguard Worker                 ..Default::default()
465*5225e6b1SAndroid Build Coastguard Worker             };
466*5225e6b1SAndroid Build Coastguard Worker             let efi_entry = EfiEntry { image_handle, systab_ptr };
467*5225e6b1SAndroid Build Coastguard Worker             let protocol = generate_protocol::<ab_slot::GblSlotProtocol>(&efi_entry, &mut ab);
468*5225e6b1SAndroid Build Coastguard Worker             let mut persist = |_: &mut [u8]| Ok(());
469*5225e6b1SAndroid Build Coastguard Worker             let mut test_ops = TestGblOps::new(protocol);
470*5225e6b1SAndroid Build Coastguard Worker             let mut gbl = Gbl::<TestGblOps>::new(&mut test_ops);
471*5225e6b1SAndroid Build Coastguard Worker             let cursor = gbl.load_slot_interface(&mut persist).unwrap();
472*5225e6b1SAndroid Build Coastguard Worker 
473*5225e6b1SAndroid Build Coastguard Worker             let slots: Vec<Slot> = cursor.ctx.slots_iter().collect();
474*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(
475*5225e6b1SAndroid Build Coastguard Worker                 slots,
476*5225e6b1SAndroid Build Coastguard Worker                 vec![
477*5225e6b1SAndroid Build Coastguard Worker                     Slot {
478*5225e6b1SAndroid Build Coastguard Worker                         suffix: 'a'.into(),
479*5225e6b1SAndroid Build Coastguard Worker                         priority: 1usize.into(),
480*5225e6b1SAndroid Build Coastguard Worker                         bootability: Bootability::Unbootable(UnbootableReason::Unknown),
481*5225e6b1SAndroid Build Coastguard Worker                     },
482*5225e6b1SAndroid Build Coastguard Worker                     Slot {
483*5225e6b1SAndroid Build Coastguard Worker                         suffix: 'b'.into(),
484*5225e6b1SAndroid Build Coastguard Worker                         priority: 2usize.into(),
485*5225e6b1SAndroid Build Coastguard Worker                         bootability: Bootability::Retriable(1usize.into()),
486*5225e6b1SAndroid Build Coastguard Worker                     },
487*5225e6b1SAndroid Build Coastguard Worker                     Slot {
488*5225e6b1SAndroid Build Coastguard Worker                         suffix: 'c'.into(),
489*5225e6b1SAndroid Build Coastguard Worker                         priority: 3usize.into(),
490*5225e6b1SAndroid Build Coastguard Worker                         bootability: Bootability::Successful,
491*5225e6b1SAndroid Build Coastguard Worker                     }
492*5225e6b1SAndroid Build Coastguard Worker                 ]
493*5225e6b1SAndroid Build Coastguard Worker             )
494*5225e6b1SAndroid Build Coastguard Worker         });
495*5225e6b1SAndroid Build Coastguard Worker     }
496*5225e6b1SAndroid Build Coastguard Worker 
497*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_active_slot()498*5225e6b1SAndroid Build Coastguard Worker     fn test_active_slot() {
499*5225e6b1SAndroid Build Coastguard Worker         // SAFETY: verfies that `info` properly aligned and not null.
500*5225e6b1SAndroid Build Coastguard Worker         // It is the callers responsibility to make sure
501*5225e6b1SAndroid Build Coastguard Worker         // that `info` points to a valid GblEfiSlotInfo.
502*5225e6b1SAndroid Build Coastguard Worker         unsafe extern "C" fn get_current_slot(
503*5225e6b1SAndroid Build Coastguard Worker             _: *mut GblEfiABSlotProtocol,
504*5225e6b1SAndroid Build Coastguard Worker             info: *mut GblEfiSlotInfo,
505*5225e6b1SAndroid Build Coastguard Worker         ) -> EfiStatus {
506*5225e6b1SAndroid Build Coastguard Worker             // TODO(b/350526796): use ptr.is_aligned() when Rust 1.79 is in Android
507*5225e6b1SAndroid Build Coastguard Worker             if info.is_null() || (info as usize) % align_of::<GblEfiSlotInfo>() != 0 {
508*5225e6b1SAndroid Build Coastguard Worker                 return EFI_STATUS_INVALID_PARAMETER;
509*5225e6b1SAndroid Build Coastguard Worker             }
510*5225e6b1SAndroid Build Coastguard Worker             let slot_info = GblEfiSlotInfo {
511*5225e6b1SAndroid Build Coastguard Worker                 suffix: 'a' as u32,
512*5225e6b1SAndroid Build Coastguard Worker                 unbootable_reason: 0,
513*5225e6b1SAndroid Build Coastguard Worker                 priority: 7,
514*5225e6b1SAndroid Build Coastguard Worker                 tries: 15,
515*5225e6b1SAndroid Build Coastguard Worker                 successful: 1,
516*5225e6b1SAndroid Build Coastguard Worker             };
517*5225e6b1SAndroid Build Coastguard Worker 
518*5225e6b1SAndroid Build Coastguard Worker             unsafe { *info = slot_info };
519*5225e6b1SAndroid Build Coastguard Worker             EFI_STATUS_SUCCESS
520*5225e6b1SAndroid Build Coastguard Worker         }
521*5225e6b1SAndroid Build Coastguard Worker 
522*5225e6b1SAndroid Build Coastguard Worker         // SAFETY: verifies that `reason` and `subreason_size` are aligned and not null.
523*5225e6b1SAndroid Build Coastguard Worker         // It is the caller's responsibility to make sure that `reason`
524*5225e6b1SAndroid Build Coastguard Worker         // and `subreason_size` point to valid output parameters.
525*5225e6b1SAndroid Build Coastguard Worker         unsafe extern "C" fn get_boot_reason(
526*5225e6b1SAndroid Build Coastguard Worker             _: *mut GblEfiABSlotProtocol,
527*5225e6b1SAndroid Build Coastguard Worker             reason: *mut u32,
528*5225e6b1SAndroid Build Coastguard Worker             subreason_size: *mut usize,
529*5225e6b1SAndroid Build Coastguard Worker             _subreason: *mut u8,
530*5225e6b1SAndroid Build Coastguard Worker         ) -> EfiStatus {
531*5225e6b1SAndroid Build Coastguard Worker             if reason.is_null()
532*5225e6b1SAndroid Build Coastguard Worker                 || subreason_size.is_null()
533*5225e6b1SAndroid Build Coastguard Worker             // TODO(b/350526796): use ptr.is_aligned() when Rust 1.79 is in Android
534*5225e6b1SAndroid Build Coastguard Worker                 || (reason as usize) % align_of::<u32>() != 0
535*5225e6b1SAndroid Build Coastguard Worker                 || (subreason_size as usize) % align_of::<usize>() != 0
536*5225e6b1SAndroid Build Coastguard Worker             {
537*5225e6b1SAndroid Build Coastguard Worker                 return EFI_STATUS_INVALID_PARAMETER;
538*5225e6b1SAndroid Build Coastguard Worker             }
539*5225e6b1SAndroid Build Coastguard Worker 
540*5225e6b1SAndroid Build Coastguard Worker             unsafe {
541*5225e6b1SAndroid Build Coastguard Worker                 *reason = BOOT_REASON.with(|r| r.load(Ordering::Relaxed));
542*5225e6b1SAndroid Build Coastguard Worker                 *subreason_size = 0;
543*5225e6b1SAndroid Build Coastguard Worker             }
544*5225e6b1SAndroid Build Coastguard Worker             EFI_STATUS_SUCCESS
545*5225e6b1SAndroid Build Coastguard Worker         }
546*5225e6b1SAndroid Build Coastguard Worker 
547*5225e6b1SAndroid Build Coastguard Worker         BOOT_REASON.with(|r| r.store(REASON_EMPTY, Ordering::Relaxed));
548*5225e6b1SAndroid Build Coastguard Worker         run_test(|image_handle, systab_ptr| {
549*5225e6b1SAndroid Build Coastguard Worker             let mut ab = GblEfiABSlotProtocol {
550*5225e6b1SAndroid Build Coastguard Worker                 get_current_slot: Some(get_current_slot),
551*5225e6b1SAndroid Build Coastguard Worker                 get_boot_reason: Some(get_boot_reason),
552*5225e6b1SAndroid Build Coastguard Worker                 flush: Some(flush),
553*5225e6b1SAndroid Build Coastguard Worker                 ..Default::default()
554*5225e6b1SAndroid Build Coastguard Worker             };
555*5225e6b1SAndroid Build Coastguard Worker             let efi_entry = EfiEntry { image_handle, systab_ptr };
556*5225e6b1SAndroid Build Coastguard Worker             let protocol = generate_protocol::<ab_slot::GblSlotProtocol>(&efi_entry, &mut ab);
557*5225e6b1SAndroid Build Coastguard Worker             let mut persist = |_: &mut [u8]| Ok(());
558*5225e6b1SAndroid Build Coastguard Worker             let mut test_ops = TestGblOps::new(protocol);
559*5225e6b1SAndroid Build Coastguard Worker             let mut gbl = Gbl::<TestGblOps>::new(&mut test_ops);
560*5225e6b1SAndroid Build Coastguard Worker             let cursor = gbl.load_slot_interface(&mut persist).unwrap();
561*5225e6b1SAndroid Build Coastguard Worker 
562*5225e6b1SAndroid Build Coastguard Worker             let slot = Slot {
563*5225e6b1SAndroid Build Coastguard Worker                 suffix: 'a'.into(),
564*5225e6b1SAndroid Build Coastguard Worker                 priority: 7usize.into(),
565*5225e6b1SAndroid Build Coastguard Worker                 bootability: Bootability::Successful,
566*5225e6b1SAndroid Build Coastguard Worker             };
567*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.get_boot_target().unwrap(), BootTarget::NormalBoot(slot));
568*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.get_slot_last_set_active().unwrap(), slot);
569*5225e6b1SAndroid Build Coastguard Worker 
570*5225e6b1SAndroid Build Coastguard Worker             BOOT_REASON.with(|r| r.store(REASON_RECOVERY, Ordering::Relaxed));
571*5225e6b1SAndroid Build Coastguard Worker 
572*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(
573*5225e6b1SAndroid Build Coastguard Worker                 cursor.ctx.get_boot_target().unwrap(),
574*5225e6b1SAndroid Build Coastguard Worker                 BootTarget::Recovery(RecoveryTarget::Slotted(slot))
575*5225e6b1SAndroid Build Coastguard Worker             );
576*5225e6b1SAndroid Build Coastguard Worker         });
577*5225e6b1SAndroid Build Coastguard Worker     }
578*5225e6b1SAndroid Build Coastguard Worker 
579*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_mark_boot_attempt()580*5225e6b1SAndroid Build Coastguard Worker     fn test_mark_boot_attempt() {
581*5225e6b1SAndroid Build Coastguard Worker         run_test(|image_handle, systab_ptr| {
582*5225e6b1SAndroid Build Coastguard Worker             let mut ab = GblEfiABSlotProtocol { flush: Some(flush), ..Default::default() };
583*5225e6b1SAndroid Build Coastguard Worker             let efi_entry = EfiEntry { image_handle, systab_ptr };
584*5225e6b1SAndroid Build Coastguard Worker             let protocol = generate_protocol::<ab_slot::GblSlotProtocol>(&efi_entry, &mut ab);
585*5225e6b1SAndroid Build Coastguard Worker             let mut persist = |_: &mut [u8]| Ok(());
586*5225e6b1SAndroid Build Coastguard Worker             let mut test_ops = TestGblOps::new(protocol);
587*5225e6b1SAndroid Build Coastguard Worker             let mut gbl = Gbl::<TestGblOps>::new(&mut test_ops);
588*5225e6b1SAndroid Build Coastguard Worker             let cursor = gbl.load_slot_interface(&mut persist).unwrap();
589*5225e6b1SAndroid Build Coastguard Worker             assert!(cursor.ctx.mark_boot_attempt().is_ok());
590*5225e6b1SAndroid Build Coastguard Worker 
591*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.mark_boot_attempt(), Err(Error::OperationProhibited));
592*5225e6b1SAndroid Build Coastguard Worker         });
593*5225e6b1SAndroid Build Coastguard Worker     }
594*5225e6b1SAndroid Build Coastguard Worker 
595*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_get_max_retries()596*5225e6b1SAndroid Build Coastguard Worker     fn test_get_max_retries() {
597*5225e6b1SAndroid Build Coastguard Worker         // SAFETY: verifies that `meta` is properly aligned and not null.
598*5225e6b1SAndroid Build Coastguard Worker         // It is the caller's responsibility to make sure that `meta` points to
599*5225e6b1SAndroid Build Coastguard Worker         // a valid GblEfiSlotMetadataBlock.
600*5225e6b1SAndroid Build Coastguard Worker         unsafe extern "C" fn load_boot_data(
601*5225e6b1SAndroid Build Coastguard Worker             _: *mut GblEfiABSlotProtocol,
602*5225e6b1SAndroid Build Coastguard Worker             meta: *mut GblEfiSlotMetadataBlock,
603*5225e6b1SAndroid Build Coastguard Worker         ) -> EfiStatus {
604*5225e6b1SAndroid Build Coastguard Worker             // TODO(b/350526796): use ptr.is_aligned() when Rust 1.79 is in Android
605*5225e6b1SAndroid Build Coastguard Worker             if meta.is_null() || (meta as usize) % align_of::<GblEfiSlotMetadataBlock>() != 0 {
606*5225e6b1SAndroid Build Coastguard Worker                 return EFI_STATUS_INVALID_PARAMETER;
607*5225e6b1SAndroid Build Coastguard Worker             }
608*5225e6b1SAndroid Build Coastguard Worker 
609*5225e6b1SAndroid Build Coastguard Worker             let meta_block = GblEfiSlotMetadataBlock {
610*5225e6b1SAndroid Build Coastguard Worker                 unbootable_metadata: 1,
611*5225e6b1SAndroid Build Coastguard Worker                 max_retries: 66,
612*5225e6b1SAndroid Build Coastguard Worker                 slot_count: 32, // why not?
613*5225e6b1SAndroid Build Coastguard Worker                 merge_status: 0,
614*5225e6b1SAndroid Build Coastguard Worker             };
615*5225e6b1SAndroid Build Coastguard Worker 
616*5225e6b1SAndroid Build Coastguard Worker             unsafe { *meta = meta_block };
617*5225e6b1SAndroid Build Coastguard Worker             EFI_STATUS_SUCCESS
618*5225e6b1SAndroid Build Coastguard Worker         }
619*5225e6b1SAndroid Build Coastguard Worker 
620*5225e6b1SAndroid Build Coastguard Worker         run_test(|image_handle, systab_ptr| {
621*5225e6b1SAndroid Build Coastguard Worker             let mut ab = GblEfiABSlotProtocol {
622*5225e6b1SAndroid Build Coastguard Worker                 load_boot_data: Some(load_boot_data),
623*5225e6b1SAndroid Build Coastguard Worker                 flush: Some(flush),
624*5225e6b1SAndroid Build Coastguard Worker                 ..Default::default()
625*5225e6b1SAndroid Build Coastguard Worker             };
626*5225e6b1SAndroid Build Coastguard Worker             let efi_entry = EfiEntry { image_handle, systab_ptr };
627*5225e6b1SAndroid Build Coastguard Worker             let protocol = generate_protocol::<ab_slot::GblSlotProtocol>(&efi_entry, &mut ab);
628*5225e6b1SAndroid Build Coastguard Worker             let mut persist = |_: &mut [u8]| Ok(());
629*5225e6b1SAndroid Build Coastguard Worker             let mut test_ops = TestGblOps::new(protocol);
630*5225e6b1SAndroid Build Coastguard Worker             let mut gbl = Gbl::<TestGblOps>::new(&mut test_ops);
631*5225e6b1SAndroid Build Coastguard Worker             let cursor = gbl.load_slot_interface(&mut persist).unwrap();
632*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.get_max_retries().unwrap(), 66usize.into());
633*5225e6b1SAndroid Build Coastguard Worker         });
634*5225e6b1SAndroid Build Coastguard Worker     }
635*5225e6b1SAndroid Build Coastguard Worker 
636*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_set_active_slot()637*5225e6b1SAndroid Build Coastguard Worker     fn test_set_active_slot() {
638*5225e6b1SAndroid Build Coastguard Worker         extern "C" fn set_active_slot(_: *mut GblEfiABSlotProtocol, idx: u8) -> EfiStatus {
639*5225e6b1SAndroid Build Coastguard Worker             // This is deliberate: we want to make sure that other logic catches
640*5225e6b1SAndroid Build Coastguard Worker             // 'no such slot' first but we also want to verify that errors propagate.
641*5225e6b1SAndroid Build Coastguard Worker             if idx != 2 {
642*5225e6b1SAndroid Build Coastguard Worker                 EFI_STATUS_SUCCESS
643*5225e6b1SAndroid Build Coastguard Worker             } else {
644*5225e6b1SAndroid Build Coastguard Worker                 EFI_STATUS_INVALID_PARAMETER
645*5225e6b1SAndroid Build Coastguard Worker             }
646*5225e6b1SAndroid Build Coastguard Worker         }
647*5225e6b1SAndroid Build Coastguard Worker 
648*5225e6b1SAndroid Build Coastguard Worker         run_test(|image_handle, systab_ptr| {
649*5225e6b1SAndroid Build Coastguard Worker             let mut ab = GblEfiABSlotProtocol {
650*5225e6b1SAndroid Build Coastguard Worker                 get_slot_info: Some(get_info),
651*5225e6b1SAndroid Build Coastguard Worker                 set_active_slot: Some(set_active_slot),
652*5225e6b1SAndroid Build Coastguard Worker                 flush: Some(flush),
653*5225e6b1SAndroid Build Coastguard Worker                 ..Default::default()
654*5225e6b1SAndroid Build Coastguard Worker             };
655*5225e6b1SAndroid Build Coastguard Worker             let efi_entry = EfiEntry { image_handle, systab_ptr };
656*5225e6b1SAndroid Build Coastguard Worker             let protocol = generate_protocol::<ab_slot::GblSlotProtocol>(&efi_entry, &mut ab);
657*5225e6b1SAndroid Build Coastguard Worker             let mut persist = |_: &mut [u8]| Ok(());
658*5225e6b1SAndroid Build Coastguard Worker             let mut test_ops = TestGblOps::new(protocol);
659*5225e6b1SAndroid Build Coastguard Worker             let mut gbl = Gbl::<TestGblOps>::new(&mut test_ops);
660*5225e6b1SAndroid Build Coastguard Worker             let cursor = gbl.load_slot_interface(&mut persist).unwrap();
661*5225e6b1SAndroid Build Coastguard Worker 
662*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.set_active_slot('b'.into()), Ok(()));
663*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.set_active_slot('c'.into()), Err(Error::Other(None)));
664*5225e6b1SAndroid Build Coastguard Worker 
665*5225e6b1SAndroid Build Coastguard Worker             let bad_suffix = '$'.into();
666*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.set_active_slot(bad_suffix), Err(Error::InvalidInput));
667*5225e6b1SAndroid Build Coastguard Worker         });
668*5225e6b1SAndroid Build Coastguard Worker     }
669*5225e6b1SAndroid Build Coastguard Worker 
670*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_set_slot_unbootable()671*5225e6b1SAndroid Build Coastguard Worker     fn test_set_slot_unbootable() {
672*5225e6b1SAndroid Build Coastguard Worker         extern "C" fn set_slot_unbootable(
673*5225e6b1SAndroid Build Coastguard Worker             _: *mut GblEfiABSlotProtocol,
674*5225e6b1SAndroid Build Coastguard Worker             idx: u8,
675*5225e6b1SAndroid Build Coastguard Worker             _: u32,
676*5225e6b1SAndroid Build Coastguard Worker         ) -> EfiStatus {
677*5225e6b1SAndroid Build Coastguard Worker             // Same thing here as with set_active_slot.
678*5225e6b1SAndroid Build Coastguard Worker             // We want to make sure that iteration over the slots
679*5225e6b1SAndroid Build Coastguard Worker             // catches invalid suffixes, but we also want to make sure
680*5225e6b1SAndroid Build Coastguard Worker             // that errors from the protocol percolate up.
681*5225e6b1SAndroid Build Coastguard Worker             if idx == 0 {
682*5225e6b1SAndroid Build Coastguard Worker                 EFI_STATUS_SUCCESS
683*5225e6b1SAndroid Build Coastguard Worker             } else {
684*5225e6b1SAndroid Build Coastguard Worker                 EFI_STATUS_INVALID_PARAMETER
685*5225e6b1SAndroid Build Coastguard Worker             }
686*5225e6b1SAndroid Build Coastguard Worker         }
687*5225e6b1SAndroid Build Coastguard Worker 
688*5225e6b1SAndroid Build Coastguard Worker         run_test(|image_handle, systab_ptr| {
689*5225e6b1SAndroid Build Coastguard Worker             let mut ab = GblEfiABSlotProtocol {
690*5225e6b1SAndroid Build Coastguard Worker                 get_slot_info: Some(get_info),
691*5225e6b1SAndroid Build Coastguard Worker                 set_slot_unbootable: Some(set_slot_unbootable),
692*5225e6b1SAndroid Build Coastguard Worker                 flush: Some(flush),
693*5225e6b1SAndroid Build Coastguard Worker                 ..Default::default()
694*5225e6b1SAndroid Build Coastguard Worker             };
695*5225e6b1SAndroid Build Coastguard Worker             let efi_entry = EfiEntry { image_handle, systab_ptr };
696*5225e6b1SAndroid Build Coastguard Worker             let protocol = generate_protocol::<ab_slot::GblSlotProtocol>(&efi_entry, &mut ab);
697*5225e6b1SAndroid Build Coastguard Worker             let mut persist = |_: &mut [u8]| Ok(());
698*5225e6b1SAndroid Build Coastguard Worker             let mut test_ops = TestGblOps::new(protocol);
699*5225e6b1SAndroid Build Coastguard Worker             let mut gbl = Gbl::<TestGblOps>::new(&mut test_ops);
700*5225e6b1SAndroid Build Coastguard Worker             let cursor = gbl.load_slot_interface(&mut persist).unwrap();
701*5225e6b1SAndroid Build Coastguard Worker 
702*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(
703*5225e6b1SAndroid Build Coastguard Worker                 cursor.ctx.set_slot_unbootable('a'.into(), UnbootableReason::SystemUpdate),
704*5225e6b1SAndroid Build Coastguard Worker                 Ok(())
705*5225e6b1SAndroid Build Coastguard Worker             );
706*5225e6b1SAndroid Build Coastguard Worker 
707*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(
708*5225e6b1SAndroid Build Coastguard Worker                 cursor.ctx.set_slot_unbootable('b'.into(), UnbootableReason::UserRequested),
709*5225e6b1SAndroid Build Coastguard Worker                 Err(Error::InvalidInput)
710*5225e6b1SAndroid Build Coastguard Worker             );
711*5225e6b1SAndroid Build Coastguard Worker         });
712*5225e6b1SAndroid Build Coastguard Worker     }
713*5225e6b1SAndroid Build Coastguard Worker 
714*5225e6b1SAndroid Build Coastguard Worker     #[test]
test_oneshot()715*5225e6b1SAndroid Build Coastguard Worker     fn test_oneshot() {
716*5225e6b1SAndroid Build Coastguard Worker         // SAFETY: checks that `reason` is not null and is properly aligned.
717*5225e6b1SAndroid Build Coastguard Worker         // Caller must make sure reason points to a valid u32.
718*5225e6b1SAndroid Build Coastguard Worker         unsafe extern "C" fn get_boot_reason(
719*5225e6b1SAndroid Build Coastguard Worker             _: *mut GblEfiABSlotProtocol,
720*5225e6b1SAndroid Build Coastguard Worker             reason: *mut u32,
721*5225e6b1SAndroid Build Coastguard Worker             _: *mut usize,
722*5225e6b1SAndroid Build Coastguard Worker             _: *mut u8,
723*5225e6b1SAndroid Build Coastguard Worker         ) -> EfiStatus {
724*5225e6b1SAndroid Build Coastguard Worker             // TODO(b/350526796): use ptr.is_aligned() when Rust 1.79 is in Android
725*5225e6b1SAndroid Build Coastguard Worker             if reason.is_null() || (reason as usize) % align_of::<u32>() != 0 {
726*5225e6b1SAndroid Build Coastguard Worker                 return EFI_STATUS_INVALID_PARAMETER;
727*5225e6b1SAndroid Build Coastguard Worker             }
728*5225e6b1SAndroid Build Coastguard Worker 
729*5225e6b1SAndroid Build Coastguard Worker             unsafe { *reason = BOOT_REASON.with(|r| r.load(Ordering::Relaxed)) };
730*5225e6b1SAndroid Build Coastguard Worker 
731*5225e6b1SAndroid Build Coastguard Worker             EFI_STATUS_SUCCESS
732*5225e6b1SAndroid Build Coastguard Worker         }
733*5225e6b1SAndroid Build Coastguard Worker 
734*5225e6b1SAndroid Build Coastguard Worker         extern "C" fn set_boot_reason(
735*5225e6b1SAndroid Build Coastguard Worker             _: *mut GblEfiABSlotProtocol,
736*5225e6b1SAndroid Build Coastguard Worker             reason: u32,
737*5225e6b1SAndroid Build Coastguard Worker             _: usize,
738*5225e6b1SAndroid Build Coastguard Worker             _: *const u8,
739*5225e6b1SAndroid Build Coastguard Worker         ) -> EfiStatus {
740*5225e6b1SAndroid Build Coastguard Worker             BOOT_REASON.with(|r| r.store(reason, Ordering::Relaxed));
741*5225e6b1SAndroid Build Coastguard Worker             EFI_STATUS_SUCCESS
742*5225e6b1SAndroid Build Coastguard Worker         }
743*5225e6b1SAndroid Build Coastguard Worker 
744*5225e6b1SAndroid Build Coastguard Worker         BOOT_REASON.with(|r| r.store(REASON_EMPTY, Ordering::Relaxed));
745*5225e6b1SAndroid Build Coastguard Worker         run_test(|image_handle, systab_ptr| {
746*5225e6b1SAndroid Build Coastguard Worker             let mut ab = GblEfiABSlotProtocol {
747*5225e6b1SAndroid Build Coastguard Worker                 get_boot_reason: Some(get_boot_reason),
748*5225e6b1SAndroid Build Coastguard Worker                 set_boot_reason: Some(set_boot_reason),
749*5225e6b1SAndroid Build Coastguard Worker                 flush: Some(flush),
750*5225e6b1SAndroid Build Coastguard Worker                 ..Default::default()
751*5225e6b1SAndroid Build Coastguard Worker             };
752*5225e6b1SAndroid Build Coastguard Worker             let efi_entry = EfiEntry { image_handle, systab_ptr };
753*5225e6b1SAndroid Build Coastguard Worker             let protocol = generate_protocol::<ab_slot::GblSlotProtocol>(&efi_entry, &mut ab);
754*5225e6b1SAndroid Build Coastguard Worker             let mut persist = |_: &mut [u8]| Ok(());
755*5225e6b1SAndroid Build Coastguard Worker             let mut test_ops = TestGblOps::new(protocol);
756*5225e6b1SAndroid Build Coastguard Worker             let mut gbl = Gbl::<TestGblOps>::new(&mut test_ops);
757*5225e6b1SAndroid Build Coastguard Worker             let cursor = gbl.load_slot_interface(&mut persist).unwrap();
758*5225e6b1SAndroid Build Coastguard Worker 
759*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.get_oneshot_status(), None);
760*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(
761*5225e6b1SAndroid Build Coastguard Worker                 cursor.ctx.set_oneshot_status(OneShot::Continue(RecoveryTarget::Dedicated)),
762*5225e6b1SAndroid Build Coastguard Worker                 Err(Error::OperationProhibited)
763*5225e6b1SAndroid Build Coastguard Worker             );
764*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.set_oneshot_status(OneShot::Bootloader), Ok(()));
765*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.get_oneshot_status(), Some(OneShot::Bootloader));
766*5225e6b1SAndroid Build Coastguard Worker 
767*5225e6b1SAndroid Build Coastguard Worker             cursor.ctx.clear_oneshot_status();
768*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.get_oneshot_status(), None);
769*5225e6b1SAndroid Build Coastguard Worker 
770*5225e6b1SAndroid Build Coastguard Worker             BOOT_REASON.with(|r| r.store(REASON_WATCHDOG, Ordering::Relaxed));
771*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(cursor.ctx.get_oneshot_status(), None);
772*5225e6b1SAndroid Build Coastguard Worker             cursor.ctx.clear_oneshot_status();
773*5225e6b1SAndroid Build Coastguard Worker             assert_eq!(BOOT_REASON.with(|r| r.load(Ordering::Relaxed)), REASON_WATCHDOG);
774*5225e6b1SAndroid Build Coastguard Worker         });
775*5225e6b1SAndroid Build Coastguard Worker     }
776*5225e6b1SAndroid Build Coastguard Worker }
777