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)