Merge "Importing rustc-1.66.0"
diff --git a/authfs/testdata/README.md b/authfs/testdata/README.md
index cf641a9..2df6753 100644
--- a/authfs/testdata/README.md
+++ b/authfs/testdata/README.md
@@ -2,7 +2,7 @@
 =================
 With a key pair, fs-verity signature can be generated by simply running
 `fsverity_metadata_generator` command line tool, which uses
-[fsverity-util](https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/fsverity-utils.git).
+[fsverity-utils](https://git.kernel.org/pub/scm/fs/fsverity/fsverity-utils.git)
 
 ```
 fsverity_metadata_generator --fsverity-path {fsverity_path} --key key.pem --key-format pem \
diff --git a/javalib/api/system-current.txt b/javalib/api/system-current.txt
index 1977321..fe9943d 100644
--- a/javalib/api/system-current.txt
+++ b/javalib/api/system-current.txt
@@ -56,7 +56,7 @@
   }
 
   public final class VirtualMachineConfig {
-    method @NonNull public String getApkPath();
+    method @Nullable public String getApkPath();
     method @NonNull public int getDebugLevel();
     method @IntRange(from=0) public long getEncryptedStorageKib();
     method @IntRange(from=0) public int getMemoryMib();
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index e66cf29..7c7f4b5 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -779,7 +779,8 @@
                     createVmPipes();
                 }
 
-                VirtualMachineAppConfig appConfig = getConfig().toVsConfig();
+                VirtualMachineAppConfig appConfig =
+                        getConfig().toVsConfig(mContext.getPackageManager());
                 appConfig.name = mName;
 
                 try {
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 1fd49c8..b358f9e 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -29,6 +29,8 @@
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.sysprop.HypervisorProperties;
@@ -58,8 +60,9 @@
     private static final String[] EMPTY_STRING_ARRAY = {};
 
     // These define the schema of the config file persisted on disk.
-    private static final int VERSION = 3;
+    private static final int VERSION = 4;
     private static final String KEY_VERSION = "version";
+    private static final String KEY_PACKAGENAME = "packageName";
     private static final String KEY_APKPATH = "apkPath";
     private static final String KEY_PAYLOADCONFIGPATH = "payloadConfigPath";
     private static final String KEY_PAYLOADBINARYNAME = "payloadBinaryPath";
@@ -94,8 +97,11 @@
      */
     @SystemApi public static final int DEBUG_LEVEL_FULL = 1;
 
+    /** Name of a package whose primary APK contains the VM payload. */
+    @Nullable private final String mPackageName;
+
     /** Absolute path to the APK file containing the VM payload. */
-    @NonNull private final String mApkPath;
+    @Nullable private final String mApkPath;
 
     @DebugLevel private final int mDebugLevel;
 
@@ -129,7 +135,8 @@
     private final boolean mVmOutputCaptured;
 
     private VirtualMachineConfig(
-            @NonNull String apkPath,
+            @Nullable String packageName,
+            @Nullable String apkPath,
             @Nullable String payloadConfigPath,
             @Nullable String payloadBinaryName,
             @DebugLevel int debugLevel,
@@ -139,6 +146,7 @@
             long encryptedStorageKib,
             boolean vmOutputCaptured) {
         // This is only called from Builder.build(); the builder handles parameter validation.
+        mPackageName = packageName;
         mApkPath = apkPath;
         mPayloadConfigPath = payloadConfigPath;
         mPayloadBinaryName = payloadBinaryName;
@@ -191,8 +199,13 @@
                     "Version " + version + " too high; current is " + VERSION);
         }
 
-        Builder builder = new Builder();
-        builder.setApkPath(b.getString(KEY_APKPATH));
+        String packageName = b.getString(KEY_PACKAGENAME);
+        Builder builder = new Builder(packageName);
+
+        String apkPath = b.getString(KEY_APKPATH);
+        if (apkPath != null) {
+            builder.setApkPath(apkPath);
+        }
 
         String payloadConfigPath = b.getString(KEY_PAYLOADCONFIGPATH);
         if (payloadConfigPath == null) {
@@ -234,7 +247,12 @@
     private void serializeOutputStream(@NonNull OutputStream output) throws IOException {
         PersistableBundle b = new PersistableBundle();
         b.putInt(KEY_VERSION, VERSION);
-        b.putString(KEY_APKPATH, mApkPath);
+        if (mPackageName != null) {
+            b.putString(KEY_PACKAGENAME, mPackageName);
+        }
+        if (mApkPath != null) {
+            b.putString(KEY_APKPATH, mApkPath);
+        }
         b.putString(KEY_PAYLOADCONFIGPATH, mPayloadConfigPath);
         b.putString(KEY_PAYLOADBINARYNAME, mPayloadBinaryName);
         b.putInt(KEY_DEBUGLEVEL, mDebugLevel);
@@ -252,12 +270,13 @@
 
     /**
      * Returns the absolute path of the APK which should contain the binary payload that will
-     * execute within the VM.
+     * execute within the VM. Returns null if no specific path has been set, so the primary APK will
+     * be used.
      *
      * @hide
      */
     @SystemApi
-    @NonNull
+    @Nullable
     public String getApkPath() {
         return mApkPath;
     }
@@ -383,7 +402,8 @@
                 && this.mVmOutputCaptured == other.mVmOutputCaptured
                 && Objects.equals(this.mPayloadConfigPath, other.mPayloadConfigPath)
                 && Objects.equals(this.mPayloadBinaryName, other.mPayloadBinaryName)
-                && this.mApkPath.equals(other.mApkPath);
+                && Objects.equals(this.mPackageName, other.mPackageName)
+                && Objects.equals(this.mApkPath, other.mApkPath);
     }
 
     /**
@@ -393,11 +413,25 @@
      * app-owned files and that could be abused to run a VM with software that the calling
      * application doesn't own.
      */
-    VirtualMachineAppConfig toVsConfig() throws VirtualMachineException {
+    VirtualMachineAppConfig toVsConfig(@NonNull PackageManager packageManager)
+            throws VirtualMachineException {
         VirtualMachineAppConfig vsConfig = new VirtualMachineAppConfig();
 
+        String apkPath = mApkPath;
+        if (apkPath == null) {
+            try {
+                ApplicationInfo appInfo =
+                        packageManager.getApplicationInfo(
+                                mPackageName, PackageManager.ApplicationInfoFlags.of(0));
+                // This really is the path to the APK, not a directory.
+                apkPath = appInfo.sourceDir;
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new VirtualMachineException("Package not found", e);
+            }
+        }
+
         try {
-            vsConfig.apk = ParcelFileDescriptor.open(new File(mApkPath), MODE_READ_ONLY);
+            vsConfig.apk = ParcelFileDescriptor.open(new File(apkPath), MODE_READ_ONLY);
         } catch (FileNotFoundException e) {
             throw new VirtualMachineException("Failed to open APK", e);
         }
@@ -433,7 +467,7 @@
      */
     @SystemApi
     public static final class Builder {
-        @Nullable private final Context mContext;
+        @Nullable private final String mPackageName;
         @Nullable private String mApkPath;
         @Nullable private String mPayloadConfigPath;
         @Nullable private String mPayloadBinaryName;
@@ -452,15 +486,15 @@
          */
         @SystemApi
         public Builder(@NonNull Context context) {
-            mContext = requireNonNull(context, "context must not be null");
+            mPackageName = requireNonNull(context, "context must not be null").getPackageName();
         }
 
         /**
-         * Creates a builder with no associated context; {@link #setApkPath} must be called to
-         * specify which APK contains the payload.
+         * Creates a builder for a specific package. If packageName is null, {@link #setApkPath}
+         * must be called to specify the APK containing the payload.
          */
-        private Builder() {
-            mContext = null;
+        private Builder(@Nullable String packageName) {
+            mPackageName = packageName;
         }
 
         /**
@@ -471,14 +505,16 @@
         @SystemApi
         @NonNull
         public VirtualMachineConfig build() {
-            String apkPath;
-            if (mApkPath == null) {
-                if (mContext == null) {
-                    throw new IllegalStateException("apkPath must be specified");
-                }
-                apkPath = mContext.getPackageCodePath();
-            } else {
+            String apkPath = null;
+            String packageName = null;
+
+            if (mApkPath != null) {
                 apkPath = mApkPath;
+            } else if (mPackageName != null) {
+                packageName = mPackageName;
+            } else {
+                // This should never happen, unless we're deserializing a bad config
+                throw new IllegalStateException("apkPath or packageName must be specified");
             }
 
             if (mPayloadBinaryName == null) {
@@ -501,6 +537,7 @@
             }
 
             return new VirtualMachineConfig(
+                    packageName,
                     apkPath,
                     mPayloadConfigPath,
                     mPayloadBinaryName,
diff --git a/libs/fdtpci/src/lib.rs b/libs/fdtpci/src/lib.rs
index 1ddda9f..e32e16d 100644
--- a/libs/fdtpci/src/lib.rs
+++ b/libs/fdtpci/src/lib.rs
@@ -91,7 +91,7 @@
 }
 
 /// Information about the PCI bus parsed from the device tree.
-#[derive(Debug)]
+#[derive(Clone, Debug)]
 pub struct PciInfo {
     /// The MMIO range used by the memory-mapped PCI CAM.
     pub cam_range: Range<usize>,
diff --git a/microdroid/init.rc b/microdroid/init.rc
index bc42791..ce0cab4 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -149,6 +149,10 @@
     # Mark boot completed. This will notify microdroid_manager to run payload.
     setprop dev.bootcomplete 1
 
+on property:tombstone_transmit.start=1
+    mkdir /data/tombstones 0771 system system
+    start tombstone_transmit
+
 service tombstone_transmit /system/bin/tombstone_transmit.microdroid -cid 2 -port 2000 -remove_tombstones_after_transmitting
     user system
     group system
@@ -175,4 +179,3 @@
     group shell log readproc
     seclabel u:r:shell:s0
     setenv HOSTNAME console
-
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 24a12f7..f1c41b9 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -426,7 +426,8 @@
 
     // Start tombstone_transmit if enabled
     if config.export_tombstones {
-        control_service("start", "tombstone_transmit")?;
+        system_properties::write("tombstone_transmit.start", "1")
+            .context("set tombstone_transmit.start")?;
     } else {
         control_service("stop", "tombstoned")?;
     }
@@ -436,10 +437,6 @@
 
     register_vm_payload_service(allow_restricted_apis, service.clone(), dice_context)?;
 
-    if config.export_tombstones {
-        wait_for_tombstone_transmit_done()?;
-    }
-
     // Wait for encryptedstore to finish mounting the storage (if enabled) before setting
     // microdroid_manager.init_done. Reason is init stops uneventd after that.
     // Encryptedstore, however requires ueventd
@@ -451,6 +448,12 @@
     wait_for_property_true("dev.bootcomplete").context("failed waiting for dev.bootcomplete")?;
     system_properties::write("microdroid_manager.init_done", "1")
         .context("set microdroid_manager.init_done")?;
+
+    // Wait for tombstone_transmit to init
+    if config.export_tombstones {
+        wait_for_tombstone_transmit_done()?;
+    }
+
     info!("boot completed, time to run payload");
     exec_task(task, service).context("Failed to run payload")
 }
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index f5e214e..21f84a5 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -18,6 +18,7 @@
         "libfdtpci",
         "liblibfdt",
         "liblog_rust_nostd",
+        "libonce_cell_nostd",
         "libpvmfw_avb_nostd",
         "libpvmfw_embedded_key",
         "libtinyvec_nostd",
diff --git a/pvmfw/README.md b/pvmfw/README.md
index f46c718..1e4b605 100644
--- a/pvmfw/README.md
+++ b/pvmfw/README.md
@@ -214,7 +214,9 @@
 
 [AVB]: https://source.android.com/docs/security/features/verifiedboot/boot-flow
 [BccHandover]: https://pigweed.googlesource.com/open-dice/+/825e3beb6c/src/android/bcc.c#260
+[BccHandoverMainFlow]: https://pigweed.googlesource.com/open-dice/+/825e3beb6c/src/android/bcc.c#199
 [CDDL]: https://datatracker.ietf.org/doc/rfc8610
+[dice-mode]: https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#Mode-Value-Details
 [dice-dt]: https://www.kernel.org/doc/Documentation/devicetree/bindings/reserved-memory/google%2Copen-dice.yaml
 [Layering]: https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#layering-details
 [Trusty-BCC]: https://android.googlesource.com/trusty/lib/+/1696be0a8f3a7103/lib/hwbcc/common/swbcc.c#554
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index a249e8d..24c36b3 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -45,7 +45,7 @@
     helpers::flush,
     helpers::GUEST_PAGE_SIZE,
     memory::MemoryTracker,
-    virtio::pci::{find_virtio_devices, map_mmio},
+    virtio::pci::{self, find_virtio_devices},
 };
 use ::dice::bcc;
 use fdtpci::{PciError, PciInfo};
@@ -76,10 +76,7 @@
     // Set up PCI bus for VirtIO devices.
     let pci_info = PciInfo::from_fdt(fdt).map_err(handle_pci_error)?;
     debug!("PCI: {:#x?}", pci_info);
-    map_mmio(&pci_info, memory)?;
-    // Safety: This is the only place where we call make_pci_root, and this main function is only
-    // called once.
-    let mut pci_root = unsafe { pci_info.make_pci_root() };
+    let mut pci_root = pci::initialise(pci_info, memory)?;
     find_virtio_devices(&mut pci_root).map_err(handle_pci_error)?;
 
     verify_payload(signed_kernel, ramdisk, PUBLIC_KEY).map_err(|e| {
diff --git a/pvmfw/src/virtio/hal.rs b/pvmfw/src/virtio/hal.rs
index c6c7a99..5f70b33 100644
--- a/pvmfw/src/virtio/hal.rs
+++ b/pvmfw/src/virtio/hal.rs
@@ -1,5 +1,9 @@
+use super::pci::PCI_INFO;
 use crate::memory::{alloc_shared, dealloc_shared, phys_to_virt, virt_to_phys};
-use core::ptr::{copy_nonoverlapping, NonNull};
+use core::{
+    ops::Range,
+    ptr::{copy_nonoverlapping, NonNull},
+};
 use log::debug;
 use virtio_drivers::{BufferDirection, Hal, PhysAddr, PAGE_SIZE};
 
@@ -26,7 +30,21 @@
         0
     }
 
-    fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull<u8> {
+    fn mmio_phys_to_virt(paddr: PhysAddr, size: usize) -> NonNull<u8> {
+        let pci_info = PCI_INFO.get().expect("VirtIO HAL used before PCI_INFO was initialised");
+        // Check that the region is within the PCI MMIO range that we read from the device tree. If
+        // not, the host is probably trying to do something malicious.
+        if !contains_range(
+            &pci_info.bar_range,
+            &(paddr.try_into().expect("PCI MMIO region start was outside of 32-bit address space")
+                ..paddr
+                    .checked_add(size)
+                    .expect("PCI MMIO region end overflowed")
+                    .try_into()
+                    .expect("PCI MMIO region end was outside of 32-bit address space")),
+        ) {
+            panic!("PCI MMIO region was outside of expected BAR range.");
+        }
         phys_to_virt(paddr)
     }
 
@@ -68,3 +86,8 @@
         }
     }
 }
+
+/// Returns true if `inner` is entirely contained within `outer`.
+fn contains_range(outer: &Range<u32>, inner: &Range<u32>) -> bool {
+    inner.start >= outer.start && inner.end <= outer.end
+}
diff --git a/pvmfw/src/virtio/pci.rs b/pvmfw/src/virtio/pci.rs
index f9d36c6..d3b3124 100644
--- a/pvmfw/src/virtio/pci.rs
+++ b/pvmfw/src/virtio/pci.rs
@@ -16,8 +16,10 @@
 
 use super::hal::HalImpl;
 use crate::{entry::RebootReason, memory::MemoryTracker};
+use alloc::boxed::Box;
 use fdtpci::{PciError, PciInfo};
 use log::{debug, error, info};
+use once_cell::race::OnceBox;
 use virtio_drivers::{
     device::blk::VirtIOBlk,
     transport::{
@@ -26,8 +28,29 @@
     },
 };
 
+pub(super) static PCI_INFO: OnceBox<PciInfo> = OnceBox::new();
+
+/// Prepares to use VirtIO PCI devices.
+///
+/// In particular:
+///
+/// 1. Maps the PCI CAM and BAR range in the page table and MMIO guard.
+/// 2. Stores the `PciInfo` for the VirtIO HAL to use later.
+/// 3. Creates and returns a `PciRoot`.
+///
+/// This must only be called once; it will panic if it is called a second time.
+pub fn initialise(pci_info: PciInfo, memory: &mut MemoryTracker) -> Result<PciRoot, RebootReason> {
+    map_mmio(&pci_info, memory)?;
+
+    PCI_INFO.set(Box::new(pci_info.clone())).expect("Tried to set PCI_INFO a second time");
+
+    // Safety: This is the only place where we call make_pci_root, and `PCI_INFO.set` above will
+    // panic if it is called a second time.
+    Ok(unsafe { pci_info.make_pci_root() })
+}
+
 /// Maps the CAM and BAR range in the page table and MMIO guard.
-pub fn map_mmio(pci_info: &PciInfo, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
+fn map_mmio(pci_info: &PciInfo, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
     memory.map_mmio_range(pci_info.cam_range.clone()).map_err(|e| {
         error!("Failed to map PCI CAM: {}", e);
         RebootReason::InternalError
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index e1a2e40..7bd5f08 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -31,6 +31,7 @@
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 
 import android.content.Context;
+import android.content.ContextWrapper;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
@@ -342,7 +343,7 @@
         VirtualMachineConfig.Builder minimalBuilder = newVmConfigBuilder();
         VirtualMachineConfig minimal = minimalBuilder.setPayloadBinaryName("binary.so").build();
 
-        assertThat(minimal.getApkPath()).isEqualTo(getContext().getPackageCodePath());
+        assertThat(minimal.getApkPath()).isNull();
         assertThat(minimal.getDebugLevel()).isEqualTo(DEBUG_LEVEL_NONE);
         assertThat(minimal.getMemoryMib()).isEqualTo(0);
         assertThat(minimal.getNumCpus()).isEqualTo(1);
@@ -425,13 +426,9 @@
         assertThat(e).hasMessageThat().contains("debug level must be FULL to capture output");
     }
 
-    private VirtualMachineConfig.Builder newBaselineBuilder() {
-        return newVmConfigBuilder().setPayloadBinaryName("binary.so").setApkPath("/apk/path");
-    }
-
     @Test
     @CddTest(requirements = {"9.17/C-1-1"})
-    public void compatibleConfigTests() throws Exception {
+    public void compatibleConfigTests() {
         int maxCpus = Runtime.getRuntime().availableProcessors();
 
         VirtualMachineConfig baseline = newBaselineBuilder().build();
@@ -467,6 +464,31 @@
                 newBaselineBuilder().setDebugLevel(DEBUG_LEVEL_FULL);
         VirtualMachineConfig debuggable = debuggableBuilder.build();
         assertConfigCompatible(debuggable, debuggableBuilder.setVmOutputCaptured(true)).isFalse();
+
+        VirtualMachineConfig currentContextConfig =
+                new VirtualMachineConfig.Builder(getContext())
+                        .setProtectedVm(isProtectedVm())
+                        .setPayloadBinaryName("binary.so")
+                        .build();
+
+        // packageName is not directly exposed by the config, so we have to be a bit creative
+        // to modify it.
+        Context otherContext =
+                new ContextWrapper(getContext()) {
+                    @Override
+                    public String getPackageName() {
+                        return "other.package.name";
+                    }
+                };
+        VirtualMachineConfig.Builder otherContextBuilder =
+                new VirtualMachineConfig.Builder(otherContext)
+                        .setProtectedVm(isProtectedVm())
+                        .setPayloadBinaryName("binary.so");
+        assertConfigCompatible(currentContextConfig, otherContextBuilder).isFalse();
+    }
+
+    private VirtualMachineConfig.Builder newBaselineBuilder() {
+        return newVmConfigBuilder().setPayloadBinaryName("binary.so").setApkPath("/apk/path");
     }
 
     private BooleanSubject assertConfigCompatible(