crdyboot: Add FirmwareError enum
This matches the error-handling style used in other modules.
BUG=b:338423918
TEST=cargo xtask check
Change-Id: I1436cd985c5f4ccd3ce5bebdd7a75943deda3928
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crdyboot/+/5757600
Tested-by: chromeos-cop-builder@chromeos-cop.iam.gserviceaccount.com <chromeos-cop-builder@chromeos-cop.iam.gserviceaccount.com>
Reviewed-by: Jeffery Miller <jefferymiller@google.com>
Commit-Queue: Nicholas Bishop <nicholasbishop@google.com>
diff --git a/crdyboot/src/firmware.rs b/crdyboot/src/firmware.rs
index 3962bb3..3322048 100644
--- a/crdyboot/src/firmware.rs
+++ b/crdyboot/src/firmware.rs
@@ -9,12 +9,13 @@
use crate::disk;
use alloc::borrow::ToOwned;
use alloc::vec::Vec;
+use core::fmt::{self, Display, Formatter};
use core::mem::size_of;
use log::{error, info, warn};
use uefi::prelude::*;
use uefi::proto::device_path::DevicePath;
use uefi::table::runtime::{CapsuleFlags, Time, VariableAttributes, VariableKey, VariableVendor};
-use uefi::{guid, CStr16, CString16, Guid};
+use uefi::{guid, CStr16, CString16, Guid, Status};
const FWUPDATE_ATTEMPT_UPDATE: u32 = 0x0000_0001;
const FWUPDATE_ATTEMPTED: u32 = 0x0000_0002;
@@ -27,6 +28,31 @@
const MAX_UPDATE_CAPSULES: usize = 128;
+#[derive(Debug, Eq, PartialEq)]
+pub enum FirmwareError {
+ GetVariableKeysFailed(Status),
+ GetVariableFailed(Status),
+ SetVariableFailed(Status),
+ UpdateInfoTooShort,
+ UpdateInfoMalformedDevicePath,
+}
+
+impl Display for FirmwareError {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match self {
+ Self::GetVariableKeysFailed(status) => {
+ write!(f, "failed to get variable keys: {status}")
+ }
+ Self::GetVariableFailed(status) => write!(f, "failed to read variable: {status}"),
+ Self::SetVariableFailed(status) => write!(f, "failed to write variable: {status}"),
+ Self::UpdateInfoTooShort => write!(f, "invalid update variable: not enough data"),
+ Self::UpdateInfoMalformedDevicePath => {
+ write!(f, "invalid update variable: malformed device path")
+ }
+ }
+ }
+}
+
/// This struct closely matches the format of the data written to UEFI
/// vars by the fwupd UEFI plugin [1], with an exception noted below. It
/// is used to create an update capsule.
@@ -110,7 +136,7 @@
}
impl<'a> TryFrom<&[u8]> for UpdateInfo<'a> {
- type Error = uefi::Error;
+ type Error = FirmwareError;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
if size_of::<UpdateInfo>() <= bytes.len() {
@@ -125,7 +151,7 @@
let time_attempted = Time::try_from(time).unwrap_or(Time::invalid());
let status = u32::from_le_bytes(bytes[48..52].try_into().unwrap());
let path = <&DevicePath>::try_from(&bytes[52..])
- .map_err(|_| uefi::Error::from(Status::INVALID_PARAMETER))?;
+ .map_err(|_| FirmwareError::UpdateInfoMalformedDevicePath)?;
let update = UpdateInfo {
version,
@@ -138,7 +164,7 @@
};
Ok(update)
} else {
- Err(uefi::Error::from(Status::INVALID_PARAMETER))
+ Err(FirmwareError::UpdateInfoTooShort)
}
}
}
@@ -164,7 +190,7 @@
fn get_update_table(
st: &SystemTable<Boot>,
variables: Vec<VariableKey>,
-) -> uefi::Result<Vec<UpdateTable>> {
+) -> Result<Vec<UpdateTable>, FirmwareError> {
let mut updates: Vec<UpdateTable> = Vec::new();
for var in variables {
// Must be a fwupd state variable.
@@ -193,7 +219,8 @@
let (data, attrs) = st
.runtime_services()
- .get_variable_boxed(&name, &FWUPDATE_VENDOR)?;
+ .get_variable_boxed(&name, &FWUPDATE_VENDOR)
+ .map_err(|err| FirmwareError::GetVariableFailed(err.status()))?;
let mut info = match UpdateInfo::try_from(&*data) {
Ok(i) => i,
@@ -225,26 +252,34 @@
}
/// Mark all updates as [`FWUPDATE_ATTEMPTED`] and note the time of the attempt.
-fn set_update_statuses(st: &SystemTable<Boot>, updates: &Vec<UpdateTable>) -> uefi::Result {
+fn set_update_statuses(
+ st: &SystemTable<Boot>,
+ updates: &Vec<UpdateTable>,
+) -> Result<(), FirmwareError> {
for update in updates {
- if let Err(err) = st.runtime_services().set_variable(
- &update.name,
- &FWUPDATE_VENDOR,
- update.attrs,
- &update.info.to_bytes(),
- ) {
- warn!(
- "could not update variable status for {0}: {err}",
- update.name
- );
- return Err(err);
- };
+ st.runtime_services()
+ .set_variable(
+ &update.name,
+ &FWUPDATE_VENDOR,
+ update.attrs,
+ &update.info.to_bytes(),
+ )
+ .map_err(|err| {
+ warn!(
+ "could not update variable status for {0}: {err}",
+ update.name
+ );
+ FirmwareError::SetVariableFailed(err.status())
+ })?;
}
Ok(())
}
-pub fn update_firmware(st: &SystemTable<Boot>) -> uefi::Result {
- let variables = st.runtime_services().variable_keys()?;
+pub fn update_firmware(st: &SystemTable<Boot>) -> Result<(), FirmwareError> {
+ let variables = st
+ .runtime_services()
+ .variable_keys()
+ .map_err(|err| FirmwareError::GetVariableKeysFailed(err.status()))?;
// Check if any updates are available by searching for and validating
// any update state variables.
let updates = get_update_table(st, variables)?;