Merge "pvmfw: apply_debug_policy: Improve failure path"
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index 965d6b9..3897c1f 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -19,11 +19,14 @@
 use crate::fdt;
 use crate::heap;
 use crate::helpers;
+use crate::helpers::RangeExt as _;
 use crate::memory::{MemoryTracker, MEMORY};
 use crate::mmu;
 use crate::rand;
 use core::arch::asm;
+use core::mem::size_of;
 use core::num::NonZeroUsize;
+use core::ops::Range;
 use core::slice;
 use hyp::{get_hypervisor, HypervisorCap};
 use log::debug;
@@ -62,7 +65,7 @@
     // - can't access MMIO (therefore, no logging)
 
     match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
-        Ok(entry) => jump_to_payload(fdt_address, entry.try_into().unwrap()),
+        Ok((entry, bcc)) => jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc),
         Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
     }
 
@@ -170,7 +173,11 @@
 ///
 /// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
 /// the assumption that its environment has been properly configured.
-fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<usize, RebootReason> {
+fn main_wrapper(
+    fdt: usize,
+    payload: usize,
+    payload_size: usize,
+) -> Result<(usize, Range<usize>), RebootReason> {
     // Limitations in this function:
     // - only access MMIO once (and while) it has been mapped and configured
     // - only perform logging once the logger has been initialized
@@ -238,7 +245,7 @@
     })?;
 
     // This wrapper allows main() to be blissfully ignorant of platform details.
-    crate::main(
+    let next_bcc = crate::main(
         slices.fdt,
         slices.kernel,
         slices.ramdisk,
@@ -261,10 +268,11 @@
     })?;
     MEMORY.lock().take().unwrap();
 
-    Ok(slices.kernel.as_ptr() as usize)
+    Ok((slices.kernel.as_ptr() as usize, next_bcc))
 }
 
-fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
+fn jump_to_payload(fdt_address: u64, payload_start: u64, bcc: Range<usize>) -> ! {
+    const ASM_STP_ALIGN: usize = size_of::<u64>() * 2;
     const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
     // Stage 1 instruction access cacheability is unaffected.
     const SCTLR_EL1_I: u64 = 0b1 << 12;
@@ -275,12 +283,67 @@
 
     const SCTLR_EL1_VAL: u64 = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I;
 
+    let scratch = layout::scratch_range();
+
+    assert_ne!(scratch.len(), 0, "scratch memory is empty.");
+    assert_eq!(scratch.start % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
+    assert_eq!(scratch.end % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
+
+    assert!(bcc.is_within(&scratch));
+    assert_eq!(bcc.start % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
+    assert_eq!(bcc.end % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
+
+    let stack = mmu::stack_range();
+
+    assert_ne!(stack.len(), 0, "stack region is empty.");
+    assert_eq!(stack.start % ASM_STP_ALIGN, 0, "Misaligned stack region.");
+    assert_eq!(stack.end % ASM_STP_ALIGN, 0, "Misaligned stack region.");
+
+    // Zero all memory that could hold secrets and that can't be safely written to from Rust.
     // Disable the exception vector, caches and page table and then jump to the payload at the
     // given address, passing it the given FDT pointer.
     //
     // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
     unsafe {
         asm!(
+            "cmp {scratch}, {bcc}",
+            "b.hs 1f",
+
+            // Zero .data & .bss until BCC.
+            "0: stp xzr, xzr, [{scratch}], 16",
+            "cmp {scratch}, {bcc}",
+            "b.lo 0b",
+
+            "1:",
+            // Skip BCC.
+            "mov {scratch}, {bcc_end}",
+            "cmp {scratch}, {scratch_end}",
+            "b.hs 1f",
+
+            // Keep zeroing .data & .bss.
+            "0: stp xzr, xzr, [{scratch}], 16",
+            "cmp {scratch}, {scratch_end}",
+            "b.lo 0b",
+
+            "1:",
+            // Flush d-cache over .data & .bss (including BCC).
+            "0: dc cvau, {cache_line}",
+            "add {cache_line}, {cache_line}, {dcache_line_size}",
+            "cmp {cache_line}, {scratch_end}",
+            "b.lo 0b",
+
+            "mov {cache_line}, {stack}",
+            // Zero stack region.
+            "0: stp xzr, xzr, [{stack}], 16",
+            "cmp {stack}, {stack_end}",
+            "b.lo 0b",
+
+            // Flush d-cache over stack region.
+            "0: dc cvau, {cache_line}",
+            "add {cache_line}, {cache_line}, {dcache_line_size}",
+            "cmp {cache_line}, {stack_end}",
+            "b.lo 0b",
+
             "msr sctlr_el1, {sctlr_el1_val}",
             "isb",
             "mov x1, xzr",
@@ -313,13 +376,21 @@
             "mov x28, xzr",
             "mov x29, xzr",
             "msr ttbr0_el1, xzr",
-            "isb",
+            // Ensure that CMOs have completed before entering payload.
             "dsb nsh",
             "br x30",
             sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
+            bcc = in(reg) u64::try_from(bcc.start).unwrap(),
+            bcc_end = in(reg) u64::try_from(bcc.end).unwrap(),
+            cache_line = in(reg) u64::try_from(scratch.start).unwrap(),
+            scratch = in(reg) u64::try_from(scratch.start).unwrap(),
+            scratch_end = in(reg) u64::try_from(scratch.end).unwrap(),
+            stack = in(reg) u64::try_from(stack.start).unwrap(),
+            stack_end = in(reg) u64::try_from(stack.end).unwrap(),
+            dcache_line_size = in(reg) u64::try_from(helpers::min_dcache_line_size()).unwrap(),
             in("x0") fdt_address,
             in("x30") payload_start,
-            options(nomem, noreturn, nostack),
+            options(noreturn),
         );
     };
 }
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index b3ce483..4c1362d 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -17,7 +17,7 @@
 use crate::bootargs::BootArgsIterator;
 use crate::cstr;
 use crate::helpers::flatten;
-use crate::helpers::RangeExt;
+use crate::helpers::RangeExt as _;
 use crate::helpers::GUEST_PAGE_SIZE;
 use crate::helpers::SIZE_4KB;
 use crate::memory::BASE_ADDR;
diff --git a/pvmfw/src/helpers.rs b/pvmfw/src/helpers.rs
index 933a6aa..a6f0dd5 100644
--- a/pvmfw/src/helpers.rs
+++ b/pvmfw/src/helpers.rs
@@ -112,7 +112,8 @@
 }
 
 #[inline]
-fn min_dcache_line_size() -> usize {
+/// Read the number of words in the smallest cache line of all the data caches and unified caches.
+pub fn min_dcache_line_size() -> usize {
     const DMINLINE_SHIFT: usize = 16;
     const DMINLINE_MASK: usize = 0xf;
     let ctr_el0 = read_sysreg!("ctr_el0");
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 1c22861..96b707e 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -38,6 +38,7 @@
 
 use alloc::boxed::Box;
 use alloc::string::ToString;
+use core::ops::Range;
 
 use crate::dice::PartialInputs;
 use crate::entry::RebootReason;
@@ -80,7 +81,7 @@
     current_bcc_handover: &[u8],
     debug_policy: Option<&mut [u8]>,
     memory: &mut MemoryTracker,
-) -> Result<(), RebootReason> {
+) -> Result<Range<usize>, RebootReason> {
     info!("pVM firmware");
     debug!("FDT: {:?}", fdt.as_ptr());
     debug!("Signed kernel: {:?} ({:#x} bytes)", signed_kernel.as_ptr(), signed_kernel.len());
@@ -156,7 +157,13 @@
         })?;
 
     info!("Starting payload...");
-    Ok(())
+
+    let bcc_range = {
+        let r = next_bcc.as_ptr_range();
+        (r.start as usize)..(r.end as usize)
+    };
+
+    Ok(bcc_range)
 }
 
 /// Logs the given PCI error and returns the appropriate `RebootReason`.
diff --git a/pvmfw/src/mmu.rs b/pvmfw/src/mmu.rs
index c21b69b..e33dcba 100644
--- a/pvmfw/src/mmu.rs
+++ b/pvmfw/src/mmu.rs
@@ -46,7 +46,7 @@
 }
 
 /// Region allocated for the stack.
-fn stack_range() -> Range<usize> {
+pub fn stack_range() -> Range<usize> {
     const STACK_PAGES: usize = 8;
 
     layout::stack_range(STACK_PAGES * PVMFW_PAGE_SIZE)