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