pvmfw: debug policy application failure is recoverable
If pvmfw fails to apply the debug policy, the original fdt is recovered
from the backup.
In this change also moves apply_debug_policy into modify_for_next_stage,
so that modification to the fdt after the verification is done in one
place.
Finally, error codes that are specific to debug policy are replaced with
the original errors from libfdt because the former don't give much
detail information than the other.
Bug: 275132866
Bug: 275306568
Test: forcibly modify a bit in debug_policy inside pvmfw and see if the
VM boots (without applying it). Flashing an invalid debug_policy was not
an option for the test because then ABL would reject it and doesn't pass
it to pvmfw at all.
Change-Id: I1c40967087449deb89a9698a87109fc16e588b70
diff --git a/pvmfw/src/debug_policy.rs b/pvmfw/src/debug_policy.rs
deleted file mode 100644
index bbf7e04..0000000
--- a/pvmfw/src/debug_policy.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2023, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Support for the debug policy overlay in pvmfw
-
-use core::fmt;
-use libfdt::FdtError;
-
-#[derive(Debug, Clone)]
-pub enum DebugPolicyError {
- /// The provided baseline FDT was invalid or malformed, so cannot access certain node/prop
- Fdt(&'static str, FdtError),
- /// The provided debug policy FDT was invalid or malformed.
- DebugPolicyFdt(&'static str, FdtError),
- /// The overlaid result FDT is invalid or malformed, and may be corrupted.
- OverlaidFdt(&'static str, FdtError),
-}
-
-impl fmt::Display for DebugPolicyError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::Fdt(s, e) => write!(f, "Invalid baseline FDT. {s}: {e}"),
- Self::DebugPolicyFdt(s, e) => write!(f, "Invalid overlay FDT. {s}: {e}"),
- Self::OverlaidFdt(s, e) => write!(f, "Invalid overlaid FDT. {s}: {e}"),
- }
- }
-}
-
-/// Applies the debug policy device tree overlay to the pVM DT.
-///
-/// # Safety
-///
-/// When an error is returned by this function, the input `Fdt` should be
-/// discarded as it may have have been partially corrupted during the overlay
-/// application process.
-unsafe fn apply_debug_policy(
- fdt: &mut libfdt::Fdt,
- debug_policy: &mut [u8],
-) -> Result<(), DebugPolicyError> {
- let overlay = libfdt::Fdt::from_mut_slice(debug_policy)
- .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to load debug policy overlay", e))?;
-
- fdt.unpack().map_err(|e| DebugPolicyError::Fdt("Failed to unpack", e))?;
-
- let fdt = fdt
- .apply_overlay(overlay)
- .map_err(|e| DebugPolicyError::DebugPolicyFdt("Failed to apply overlay", e))?;
-
- fdt.pack().map_err(|e| DebugPolicyError::OverlaidFdt("Failed to re-pack", e))
-}
-
-/// Handles debug policies.
-///
-/// # Safety
-///
-/// This may corrupt the input `Fdt` when overlaying debug policy or applying
-/// ramdump configuration.
-pub unsafe fn handle_debug_policy(
- fdt: &mut libfdt::Fdt,
- debug_policy: Option<&mut [u8]>,
-) -> Result<(), DebugPolicyError> {
- if let Some(dp) = debug_policy {
- apply_debug_policy(fdt, dp)?;
- }
-
- Ok(())
-}
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index ffbc4a8..00f0e9b 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -16,7 +16,6 @@
use crate::config;
use crate::crypto;
-use crate::debug_policy::{handle_debug_policy, DebugPolicyError};
use crate::fdt;
use crate::heap;
use crate::helpers;
@@ -54,16 +53,6 @@
SecretDerivationError,
}
-impl From<DebugPolicyError> for RebootReason {
- fn from(error: DebugPolicyError) -> Self {
- match error {
- DebugPolicyError::Fdt(_, _) => RebootReason::InvalidFdt,
- DebugPolicyError::DebugPolicyFdt(_, _) => RebootReason::InvalidConfig,
- DebugPolicyError::OverlaidFdt(_, _) => RebootReason::InternalError,
- }
- }
-}
-
main!(start);
/// Entry point for pVM firmware.
@@ -237,19 +226,11 @@
})?;
// This wrapper allows main() to be blissfully ignorant of platform details.
- crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, &mut memory)?;
+ crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, debug_policy, &mut memory)?;
helpers::flushed_zeroize(bcc_slice);
helpers::flush(slices.fdt.as_slice());
- // SAFETY - As we `?` the result, there is no risk of using a bad `slices.fdt`.
- unsafe {
- handle_debug_policy(slices.fdt, debug_policy).map_err(|e| {
- error!("Unexpected error when handling debug policy: {e:?}");
- RebootReason::from(e)
- })?;
- }
-
info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
memory.mmio_unmap_all().map_err(|e| {
error!("Failed to unshare MMIO ranges: {e}");
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index 7d88455..d15eaba 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -22,6 +22,7 @@
use crate::memory::MAX_ADDR;
use crate::RebootReason;
use alloc::ffi::CString;
+use alloc::vec::Vec;
use core::cmp::max;
use core::cmp::min;
use core::ffi::CStr;
@@ -36,6 +37,7 @@
use libfdt::FdtNode;
use log::debug;
use log::error;
+use log::info;
use tinyvec::ArrayVec;
/// Extract from /config the address range containing the pre-loaded kernel. Absence of /config is
@@ -672,6 +674,7 @@
bcc: &[u8],
new_instance: bool,
strict_boot: bool,
+ debug_policy: Option<&mut [u8]>,
) -> libfdt::Result<()> {
fdt.unpack()?;
@@ -680,6 +683,13 @@
set_or_clear_chosen_flag(fdt, cstr!("avf,strict-boot"), strict_boot)?;
set_or_clear_chosen_flag(fdt, cstr!("avf,new-instance"), new_instance)?;
+ if let Some(debug_policy) = debug_policy {
+ apply_debug_policy(fdt, debug_policy)?;
+ info!("Debug policy applied.");
+ } else {
+ info!("No debug policy found.");
+ }
+
fdt.pack()?;
Ok(())
@@ -712,3 +722,26 @@
Ok(())
}
+
+fn apply_debug_policy(fdt: &mut Fdt, debug_policy: &mut [u8]) -> libfdt::Result<()> {
+ let backup_fdt = Vec::from(fdt.as_slice());
+
+ let overlay = match Fdt::from_mut_slice(debug_policy) {
+ Ok(overlay) => overlay,
+ Err(e) => {
+ info!("Corrupted debug policy found: {e}. Not applying.");
+ return Ok(());
+ }
+ };
+ let backup_overlay = Vec::from(overlay.as_slice());
+
+ // SAFETY - on failure, the corrupted fdts are discarded and are restored using the backups.
+ if let Err(e) = unsafe { fdt.apply_overlay(overlay) } {
+ error!("Failed to apply debug policy: {e}. Recovering...");
+ fdt.copy_from_slice(backup_fdt.as_slice())?;
+ overlay.copy_from_slice(backup_overlay.as_slice())?;
+ // A successful restoration is considered success because an invalid debug policy
+ // shouldn't DOS the pvmfw
+ }
+ Ok(())
+}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 00ff61f..06cc81e 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -21,7 +21,6 @@
mod config;
mod crypto;
-mod debug_policy;
mod dice;
mod entry;
mod exceptions;
@@ -65,6 +64,7 @@
signed_kernel: &[u8],
ramdisk: Option<&[u8]>,
current_bcc_handover: &[u8],
+ debug_policy: Option<&mut [u8]>,
memory: &mut MemoryTracker,
) -> Result<(), RebootReason> {
info!("pVM firmware");
@@ -122,7 +122,7 @@
flush(next_bcc);
let strict_boot = true;
- modify_for_next_stage(fdt, next_bcc, new_instance, strict_boot).map_err(|e| {
+ modify_for_next_stage(fdt, next_bcc, new_instance, strict_boot, debug_policy).map_err(|e| {
error!("Failed to configure device tree: {e}");
RebootReason::InternalError
})?;