Merge "Change *Service::SERVICE_PORT to *Service::PORT"
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index 25eb909..62529b3 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -11,8 +11,8 @@
 We support the following device:
 
 * aosp_cf_x86_64_phone (Cuttlefish a.k.a. Cloud Android)
-* oriole (Pixel 6)
-* raven (Pixel 6 Pro)
+* oriole/raven (Pixel 6, and 6 Pro)
+* panther/cheetah (Pixel 7, and 7 Pro)
 
 ### Cuttlefish
 
@@ -30,12 +30,14 @@
 acloud create --local-instance --local-image
 ```
 
-### Pixel 6 and 6 Pro
+### Google Pixel phones
 
-If the device is running Android 12, join the [Android Beta
-Program](https://www.google.com/android/beta) to upgrade to Android 13 Beta.
+If the device is running Android 13 or earlier, join the [Android Beta
+Program](https://developer.android.com/about/versions/14/get#on_pixel) to upgrade to Android 14
+Beta.
 
-Once upgraded to Android 13, execute the following command to enable pKVM.
+Once upgraded to Android 14, and if you are using Pixel 6 or 6 Pro, execute the following command to
+enable pKVM. You don't need to do this for Pixel 7 and 7 Pro.
 
 ```shell
 adb reboot bootloader
@@ -44,41 +46,6 @@
 fastboot reboot
 ```
 
-Due to a bug in Android 13 for these devices, pKVM may stop working after an
-[OTA update](https://source.android.com/devices/tech/ota). To prevent this, it
-is necessary to manually replicate the `pvmfw` partition to the other slot:
-
-```shell
-git -C <android_root>/packages/modules/Virtualization
-show de6b0b2ecf6225a0a7b43241de27e74fc3e6ceb2:pvmfw/pvmfw.img > /tmp/pvmfw.img
-adb reboot bootloader
-fastboot --slot other flash pvmfw /tmp/pvmfw.img
-fastboot reboot
-```
-
-Otherwise, if an OTA has already made pKVM unusable, the working partition
-should be copied to the "current" slot:
-
-```shell
-adb reboot bootloader
-fastboot flash pvmfw /tmp/pvmfw.img
-fastboot reboot
-```
-
-Finally, if the `pvmfw` partition has been corrupted, both slots may be flashed
-using the [`pvmfw.img` pre-built](https://android.googlesource.com/platform/packages/modules/Virtualization/+/08deac98acefd62e222edfa374d5292458cf97eb%5E/pvmfw/pvmfw.img)
-as long as the bootloader remains unlocked. Otherwise, a fresh install of
-Android 13 followed by the manual steps above for flashing the `other` slot
-should be used as a last resort.
-
-Starting in Android 14, `pvmfw.img` can be built using the Android Build system:
-```
-lunch <target>  # where PRODUCT_BUILD_PVMFW_IMAGE=true
-m pvmfwimage    # partition image under ${ANDROID_PRODUCT_OUT}/pvmfw.img
-```
-Note that the result is not intended to be used in Android 13 as not
-backward-compatibility is guaranteed.
-
 ## Running demo app
 
 The instruction is [here](../../demo/README.md).
@@ -140,23 +107,6 @@
 adb shell /apex/com.android.virt/bin/vm run-microdroid --debug full
 ```
 
-The `instance.img` and `apk.idsig` files will be stored in a subdirectory under
-`/data/local/tmp/microdroid`, that `vm` will create.
-
-Atlernatively, you can manually run the demo app on top of Microdroid as follows:
-
-```shell
-UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true TARGET_BUILD_APPS=MicrodroidDemoApp m apps_only dist
-adb shell mkdir -p /data/local/tmp/virt
-adb push out/dist/MicrodroidDemoApp.apk /data/local/tmp/virt/
-adb shell /apex/com.android.virt/bin/vm run-app \
-  --debug full \
-  /data/local/tmp/virt/MicrodroidDemoApp.apk \
-  /data/local/tmp/virt/MicrodroidDemoApp.apk.idsig \
-  /data/local/tmp/virt/instance.img \
-  --payload-path MicrodroidTestNativeLib.so
-```
-
 ## Building and updating CrosVM and VirtualizationService {#building-and-updating}
 
 You can update CrosVM and the VirtualizationService by updating the `com.android.virt` APEX instead
diff --git a/docs/getting_started/pixel6.md b/docs/getting_started/pixel6.md
deleted file mode 100644
index 82391c4..0000000
--- a/docs/getting_started/pixel6.md
+++ /dev/null
@@ -1,129 +0,0 @@
-# Instructions for building custom AVF on Pixel 6 or 6 Pro
-
-This document provides steps for building AVF from AOSP, and then install it to
-Pixel 6 series to better understand AVF and do some experiments.
-
-**WARNING**: Unless Android 13 is released to AOSP (expected to be at Summer
-2022, exact date TBD) by the time when you read this documentation, or you or
-your company have early access to Android Tiramisu source tree, you **CANNOT**
-follow this instruction. In that case, you can only **USE** the AVF that is
-shipped in the Android 13 Beta Image.
-
-This is because AVF in the beta image is signed by Google and therefore it can't
-be updated to a new AVF built in AOSP which can't be signed by the Google key
-that is not shared with AOSP.
-
-## Upgrade to Android 13 Beta Image
-
-First, upgrade your Pixel 6 or Pixel 6 Pro to the Android 13 Beta Image. This
-can be done in two ways:
-
-* Join [Android Beta Program](https://www.google.com/android/beta) and then OTA
-  to Android 13.
-* Manually flash [Android 13 Beta Images](https://developer.android.com/about/versions/13/download#factory-images).
-
-Then enable ADB debugging in "Settings" -> "System" -> "Developer options".
-Finally, enable PKVM.
-
-```shell
-adb reboot bootloader
-fastboot flashing unlock
-fastboot oem pkvm enable
-fastboot reboot
-```
-
-## Building GSI and flashing it
-
-Prepare your Android 13 (Tiramisu) source tree.
-
-```shell
-mkdir tm
-cd tm
-repo init -u <URL> -m <your_tm_branch>
-repo sync -c --no-tags -j 10
-```
-
-Patch GSI so that it includes AVF. Edit
-`build/make/target/product/gsi_release.mk` and add the following line to the
-end (or anywhere in the file that makes sense):
-
-```
-PRODUCT_PACKAGES += com.android.virt
-```
-
-Build GSI.
-
-```shell
-source build/envsetup.sh
-choosecombo 1 aosp_arm64 userdebug
-m
-```
-
-Flash GSI to the Pixel device.
-
-```shell
-adb reboot bootloader
-fastboot reboot fastboot
-fastboot delete-logical-partition product_a
-fastboot flash system out/target/product/generic_arm64/system.img
-fastboot --disable-verification flash vbmeta out/target/product/generic_arm64/vbmeta.img
-fastboot -w reboot
-```
-
-Deleting the logical partition `product_a` is needed because the GSI image is
-bigger than the logical partition `system_a` of the beta image.
-`--disable-verification` when flashing the `vbmeta` partition is critical. Don't
-miss it.
-
-Lastly, check if you are running GSI.
-
-```shell
-adb shell getprop ro.build.product
-adb shell ls /dev/kvm
-adb shell ls /apex/com.android.virt/bin/vm
-```
-
-The result should be as follows.
-
-```
-generic_arm64
-/dev/kvm
-/apex/com.android.virt/bin/vm
-```
-
-## Building and installing AVF from AOSP
-
-Checkout AOSP master branch.
-
-```shell
-mkdir aosp
-cd aosp
-repo init -u https://android.googlesource.com/platform/manifest -b master
-repo sync -c --no-tags -j 10
-```
-
-Then build the `com.android.virt` APEX.
-
-```shell
-source build/envsetup.sh
-banchan com.android.virt aosp_arm64
-UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true m apps_only dist
-```
-
-Install the newly built AVF to the device
-
-```shell
-adb install out/dist/com.android.virt.apex
-adb reboot
-```
-
-If this doesn't work for some reason, try this:
-
-```
-adb root
-adb shell setenforce 0
-adb push out/dist/com.android.virt.apex /data/local/
-adb shell cmd -w apexservice deactivatePackage /system/system_ext/apex/com.android.virt.apex
-adb shell cmd -w apexservice activatePackage /data/local/com.android.virt.apex
-// Don't adb reboot
-```
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 61b69f5..9785941 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -780,11 +780,11 @@
 
     /// Return a shared pointer to the device tree.
     pub fn as_ptr(&self) -> *const c_void {
-        self as *const _ as *const c_void
+        self.buffer.as_ptr().cast::<_>()
     }
 
     fn as_mut_ptr(&mut self) -> *mut c_void {
-        self as *mut _ as *mut c_void
+        self.buffer.as_mut_ptr().cast::<_>()
     }
 
     fn capacity(&self) -> usize {
@@ -792,8 +792,9 @@
     }
 
     fn header(&self) -> &libfdt_bindgen::fdt_header {
+        let p = self.as_ptr().cast::<_>();
         // SAFETY - A valid FDT (verified by constructor) must contain a valid fdt_header.
-        unsafe { &*(&self as *const _ as *const libfdt_bindgen::fdt_header) }
+        unsafe { &*p }
     }
 
     fn totalsize(&self) -> usize {
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index 965d6b9..999baee 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,
@@ -248,7 +255,6 @@
     )?;
 
     helpers::flushed_zeroize(bcc_slice);
-    helpers::flush(slices.fdt.as_slice());
 
     info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
     MEMORY.lock().as_mut().unwrap().mmio_unmap_all().map_err(|e| {
@@ -261,10 +267,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 +282,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 +375,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 70916ac..5ecb038 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;
@@ -41,6 +41,7 @@
 use log::debug;
 use log::error;
 use log::info;
+use log::warn;
 use tinyvec::ArrayVec;
 
 /// Extract from /config the address range containing the pre-loaded kernel. Absence of /config is
@@ -721,20 +722,26 @@
     debug_policy: Option<&mut [u8]>,
     debuggable: bool,
 ) -> libfdt::Result<()> {
-    fdt.unpack()?;
+    if let Some(debug_policy) = debug_policy {
+        let backup = Vec::from(fdt.as_slice());
+        fdt.unpack()?;
+        let backup_fdt = Fdt::from_slice(backup.as_slice()).unwrap();
+        if apply_debug_policy(fdt, backup_fdt, debug_policy)? {
+            info!("Debug policy applied.");
+        } else {
+            // apply_debug_policy restored fdt to backup_fdt so unpack it again.
+            fdt.unpack()?;
+        }
+    } else {
+        info!("No debug policy found.");
+        fdt.unpack()?;
+    }
 
     patch_dice_node(fdt, bcc.as_ptr() as usize, bcc.len())?;
 
     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.");
-    }
-
     if debuggable {
         if let Some(bootargs) = read_bootargs_from(fdt)? {
             filter_out_dangerous_bootargs(fdt, &bootargs)?;
@@ -774,27 +781,33 @@
     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) {
+/// Apply the debug policy overlay to the guest DT.
+///
+/// Returns Ok(true) on success, Ok(false) on recovered failure and Err(_) on corruption of the DT.
+fn apply_debug_policy(
+    fdt: &mut Fdt,
+    backup_fdt: &Fdt,
+    debug_policy: &[u8],
+) -> libfdt::Result<bool> {
+    let mut debug_policy = Vec::from(debug_policy);
+    let overlay = match Fdt::from_mut_slice(debug_policy.as_mut_slice()) {
         Ok(overlay) => overlay,
         Err(e) => {
-            info!("Corrupted debug policy found: {e}. Not applying.");
-            return Ok(());
+            warn!("Corrupted debug policy found: {e}. Not applying.");
+            return Ok(false);
         }
     };
-    let backup_overlay = Vec::from(overlay.as_slice());
 
-    // SAFETY - on failure, the corrupted fdts are discarded and are restored using the backups.
+    // SAFETY - on failure, the corrupted DT is restored using the backup.
     if let Err(e) = unsafe { fdt.apply_overlay(overlay) } {
-        error!("Failed to apply debug policy: {e}. Recovering...");
+        warn!("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(false)
+    } else {
+        Ok(true)
     }
-    Ok(())
 }
 
 fn read_common_debug_policy(fdt: &Fdt, debug_feature_name: &CStr) -> libfdt::Result<bool> {
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)