• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Rust wrapper for `GBL_EFI_SLOT_PROTOCOL`.
16 extern crate libgbl;
17 
18 use crate::efi_call;
19 use crate::protocol::{Protocol, ProtocolInfo};
20 use efi_types::{
21     EfiGuid, GblEfiABSlotProtocol, GblEfiBootReason, GblEfiSlotInfo, GblEfiSlotMetadataBlock,
22     GblEfiUnbootableReason, GBL_EFI_UNBOOTABLE_REASON_GBL_EFI_NO_MORE_TRIES as NO_MORE_TRIES,
23     GBL_EFI_UNBOOTABLE_REASON_GBL_EFI_SYSTEM_UPDATE as SYSTEM_UPDATE,
24     GBL_EFI_UNBOOTABLE_REASON_GBL_EFI_USER_REQUESTED as USER_REQUESTED,
25     GBL_EFI_UNBOOTABLE_REASON_GBL_EFI_VERIFICATION_FAILURE as VERIFICATION_FAILURE,
26 };
27 use liberror::{Error, Result};
28 
29 use libgbl::slots::{Bootability, Slot, UnbootableReason};
30 
31 /// Wraps `GBL_EFI_SLOT_PROTOCOL`.
32 pub struct GblSlotProtocol;
33 
34 impl ProtocolInfo for GblSlotProtocol {
35     type InterfaceType = GblEfiABSlotProtocol;
36 
37     const GUID: EfiGuid =
38         EfiGuid::new(0x9a7a7db4, 0x614b, 0x4a08, [0x3d, 0xf9, 0x00, 0x6f, 0x49, 0xb0, 0xd8, 0x0c]);
39 }
40 
from_efi_unbootable_reason(reason: GblEfiUnbootableReason) -> UnbootableReason41 fn from_efi_unbootable_reason(reason: GblEfiUnbootableReason) -> UnbootableReason {
42     match reason {
43         NO_MORE_TRIES => UnbootableReason::NoMoreTries,
44         SYSTEM_UPDATE => UnbootableReason::SystemUpdate,
45         USER_REQUESTED => UnbootableReason::UserRequested,
46         VERIFICATION_FAILURE => UnbootableReason::VerificationFailure,
47         _ => UnbootableReason::Unknown,
48     }
49 }
50 
51 /// Newtype around GblEfiSlotInfo to bypass orphan rule.
52 pub struct GblSlot(pub(crate) GblEfiSlotInfo);
53 
54 impl From<GblEfiSlotInfo> for GblSlot {
from(slot: GblEfiSlotInfo) -> Self55     fn from(slot: GblEfiSlotInfo) -> Self {
56         Self(slot)
57     }
58 }
59 
60 impl TryFrom<GblSlot> for libgbl::slots::Slot {
61     type Error = liberror::Error;
try_from(info: GblSlot) -> Result<Self>62     fn try_from(info: GblSlot) -> Result<Self> {
63         let info = info.0;
64         Ok(Slot {
65             suffix: info.suffix.try_into()?,
66             priority: info.priority.into(),
67             bootability: match (info.successful, info.tries) {
68                 (s, _) if s != 0 => Bootability::Successful,
69                 (0, t) if t > 0 => Bootability::Retriable(info.tries.into()),
70                 _ => Bootability::Unbootable(from_efi_unbootable_reason(info.unbootable_reason)),
71             },
72         })
73     }
74 }
75 
76 impl<'a> Protocol<'a, GblSlotProtocol> {
77     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.load_boot_data()`
load_boot_data(&self) -> Result<GblEfiSlotMetadataBlock>78     pub fn load_boot_data(&self) -> Result<GblEfiSlotMetadataBlock> {
79         let mut block: GblEfiSlotMetadataBlock = Default::default();
80         // SAFETY:
81         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
82         // established by `Protocol::new()`.
83         // `self.interface` is an input parameter and will not be retained. It outlives the call.
84         // `block` is an output parameter and will not be retained. It outlives the call.
85         unsafe { efi_call!(self.interface()?.load_boot_data, self.interface, &mut block)? }
86         Ok(block)
87     }
88 
89     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.get_slot_info()`
get_slot_info(&self, idx: u8) -> Result<GblSlot>90     pub fn get_slot_info(&self, idx: u8) -> Result<GblSlot> {
91         let mut info: GblEfiSlotInfo = Default::default();
92         // SAFETY:
93         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
94         // established by `Protocol::new()`.
95         // `self.interface` is an input parameter and will not be retained. It outlives the call.
96         // `info` is an output parameter and will not be retained. It outlives the call.
97         unsafe { efi_call!(self.interface()?.get_slot_info, self.interface, idx, &mut info,)? }
98         Ok(info.into())
99     }
100 
101     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.get_current_slot()`
get_current_slot(&self) -> Result<GblSlot>102     pub fn get_current_slot(&self) -> Result<GblSlot> {
103         let mut info: GblEfiSlotInfo = Default::default();
104         // SAFETY:
105         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
106         // established by `Protocol::new()`.
107         // `self.interface` is an input parameter and will not be retained. It outlives the call.
108         // `info` is an output parameter and will not be retained. It outlives the call.
109         unsafe { efi_call!(self.interface()?.get_current_slot, self.interface, &mut info)? };
110         Ok(info.into())
111     }
112 
113     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.GetNextSlot()`
get_next_slot(&self, mark_boot_attempt: bool) -> Result<GblSlot>114     pub fn get_next_slot(&self, mark_boot_attempt: bool) -> Result<GblSlot> {
115         let mut info = GblEfiSlotInfo::default();
116         // SAFETY:
117         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
118         // established by `Protocol::new()`.
119         // `self.interface`, `info` are input/output parameter and will not be retained. It
120         // outlives the call.
121         unsafe {
122             efi_call!(
123                 self.interface()?.get_next_slot,
124                 self.interface,
125                 mark_boot_attempt,
126                 &mut info as _
127             )?;
128         }
129         Ok(GblSlot::from(info))
130     }
131 
132     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.set_active_slot()`
set_active_slot(&self, idx: u8) -> Result<()>133     pub fn set_active_slot(&self, idx: u8) -> Result<()> {
134         // SAFETY:
135         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
136         // established by `Protocol::new()`.
137         // `self.interface` is an input parameter and will not be retained. It outlives the call.
138         unsafe { efi_call!(self.interface()?.set_active_slot, self.interface, idx) }
139     }
140 
141     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.set_slot_unbootable()`
set_slot_unbootable(&self, idx: u8, reason: GblEfiUnbootableReason) -> Result<()>142     pub fn set_slot_unbootable(&self, idx: u8, reason: GblEfiUnbootableReason) -> Result<()> {
143         let reason: u32 = reason.try_into().or(Err(Error::InvalidInput))?;
144         // SAFETY:
145         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
146         // established by `Protocol::new()`.
147         // `self.interface` is an input parameter and will not be retained. It outlives the call.
148         unsafe { efi_call!(self.interface()?.set_slot_unbootable, self.interface, idx, reason) }
149     }
150 
151     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.reinitialize()`
reinitialize(&self) -> Result<()>152     pub fn reinitialize(&self) -> Result<()> {
153         // SAFETY:
154         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
155         // established by `Protocol::new()`.
156         // `self.interface` is an input parameter and will not be retained. It outlives the call.
157         unsafe { efi_call!(self.interface()?.reinitialize, self.interface) }
158     }
159 
160     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.get_boot_reason()`
get_boot_reason(&self, subreason: &mut [u8]) -> Result<(GblEfiBootReason, usize)>161     pub fn get_boot_reason(&self, subreason: &mut [u8]) -> Result<(GblEfiBootReason, usize)> {
162         let mut reason: u32 = 0;
163         let mut subreason_size = subreason.len();
164         // SAFETY:
165         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
166         // established by `Protocol::new()`.
167         // `self.interface` is an input parameter and will not be retained. It outlives the call.
168         // `reason` is an output parameter. It is not retained, and it outlives the call.
169         // `subreason_size` is an in-out parameter. It is not retained, and it outlives the call.
170         // `subreason` remains valid during the call.
171         unsafe {
172             efi_call!(
173                 @bufsize subreason_size,
174                 self.interface()?.get_boot_reason,
175                 self.interface,
176                 &mut reason,
177                 &mut subreason_size,
178                 subreason.as_mut_ptr(),
179             )?
180         }
181 
182         let reason: GblEfiBootReason = reason.try_into().or(Err(Error::InvalidInput))?;
183         Ok((reason, subreason_size))
184     }
185 
186     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.set_boot_reason()`
set_boot_reason(&self, reason: GblEfiBootReason, subreason: &[u8]) -> Result<()>187     pub fn set_boot_reason(&self, reason: GblEfiBootReason, subreason: &[u8]) -> Result<()> {
188         // SAFETY:
189         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
190         // established by `Protocol::new()`.
191         // `self.interface` is an input parameter and will not be retained. It outlives the call.
192         // `subreason` is not modified or retained. It outlives the call.
193         unsafe {
194             efi_call!(
195                 self.interface()?.set_boot_reason,
196                 self.interface,
197                 reason.try_into().or(Err(Error::InvalidInput))?,
198                 subreason.len(),
199                 subreason.as_ptr(),
200             )
201         }
202     }
203 
204     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.flush()`
flush(&self) -> Result<()>205     pub fn flush(&self) -> Result<()> {
206         // SAFETY:
207         // `self.interface()?` guarantees self.interface is non-null and points to a valid object
208         // established by `Protocol::new()`.
209         // `self.interface` is an input parameter and will not be retained. It outlives the call.
210         unsafe { efi_call!(self.interface()?.flush, self.interface) }
211     }
212 
213     /// Wrapper of `GBL_EFI_SLOT_PROTOCOL.version`
version(&self) -> Result<u32>214     pub fn version(&self) -> Result<u32> {
215         Ok(self.interface()?.version)
216     }
217 }
218