Merge "Add a test for kernel ramdump"
diff --git a/libs/libfdt/src/iterators.rs b/libs/libfdt/src/iterators.rs
index 41fd492..a7ea0ee 100644
--- a/libs/libfdt/src/iterators.rs
+++ b/libs/libfdt/src/iterators.rs
@@ -178,16 +178,13 @@
     }
 }
 
-impl FromAddrCells for u128 {
+impl FromAddrCells for (u32, u64) {
     fn from_addr_cells(cells: &mut CellIterator, cell_count: AddrCells) -> Option<Self> {
         Some(match cell_count {
-            AddrCells::Single => cells.next()?.into(),
-            AddrCells::Double => (cells.next()? as Self) << 32 | cells.next()? as Self,
             AddrCells::Triple => {
-                (cells.next()? as Self) << 64
-                    | (cells.next()? as Self) << 32
-                    | cells.next()? as Self
+                (cells.next()?, (cells.next()? as u64) << 32 | cells.next()? as u64)
             }
+            _ => panic!("Invalid addr_cells {:?} for (u32, u64)", cell_count),
         })
     }
 }
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 6a01713..b6c115b 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -22,9 +22,6 @@
         "libtinyvec_nostd",
         "libvmbase",
     ],
-    static_libs: [
-        "libarm-optimized-routines-mem",
-    ],
     apex_available: ["com.android.virt"],
 }
 
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 07cbd0c..7222a0d 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -32,11 +32,7 @@
 mod pci;
 mod smccc;
 
-use crate::{
-    entry::RebootReason,
-    memory::MemoryTracker,
-    pci::{map_cam, pci_node},
-};
+use crate::{entry::RebootReason, memory::MemoryTracker, pci::PciInfo};
 use avb::PUBLIC_KEY;
 use avb_nostd::verify_image;
 use dice::bcc;
@@ -61,8 +57,9 @@
     trace!("BCC: {bcc:x?}");
 
     // Set up PCI bus for VirtIO devices.
-    let pci_node = pci_node(fdt)?;
-    map_cam(&pci_node, memory)?;
+    let pci_info = PciInfo::from_fdt(fdt)?;
+    info!("PCI: {:#x?}", pci_info);
+    pci_info.map(memory)?;
 
     verify_image(signed_kernel, PUBLIC_KEY).map_err(|e| {
         error!("Failed to verify the payload: {e}");
diff --git a/pvmfw/src/memory.rs b/pvmfw/src/memory.rs
index ca1024d..892089e 100644
--- a/pvmfw/src/memory.rs
+++ b/pvmfw/src/memory.rs
@@ -26,7 +26,7 @@
 use log::error;
 use tinyvec::ArrayVec;
 
-type MemoryRange = Range<usize>;
+pub type MemoryRange = Range<usize>;
 
 #[derive(Clone, Copy, Debug, Default)]
 enum MemoryType {
diff --git a/pvmfw/src/pci.rs b/pvmfw/src/pci.rs
index 7baabed..3e6915a 100644
--- a/pvmfw/src/pci.rs
+++ b/pvmfw/src/pci.rs
@@ -14,16 +14,57 @@
 
 //! Functions to scan the PCI bus for VirtIO device and allocate BARs.
 
-use crate::{entry::RebootReason, memory::MemoryTracker};
-use core::ffi::CStr;
-use libfdt::{Fdt, FdtNode};
+use crate::{
+    entry::RebootReason,
+    memory::{MemoryRange, MemoryTracker},
+};
+use core::{ffi::CStr, ops::Range};
+use libfdt::{AddressRange, Fdt, FdtNode};
 use log::{debug, error};
 
 /// PCI MMIO configuration region size.
 const PCI_CFG_SIZE: usize = 0x100_0000;
 
+/// Information about the PCI bus parsed from the device tree.
+#[derive(Debug)]
+pub struct PciInfo {
+    /// The MMIO range used by the memory-mapped PCI CAM.
+    cam_range: MemoryRange,
+    /// The MMIO range from which 32-bit PCI BARs should be allocated.
+    bar_range: Range<u32>,
+}
+
+impl PciInfo {
+    /// Finds the PCI node in the FDT, parses its properties and validates it.
+    pub fn from_fdt(fdt: &Fdt) -> Result<Self, RebootReason> {
+        let pci_node = pci_node(fdt)?;
+
+        let cam_range = parse_cam_range(&pci_node)?;
+        let bar_range = parse_ranges(&pci_node)?;
+
+        Ok(Self { cam_range, bar_range })
+    }
+
+    /// Maps the CAM and BAR range in the page table and MMIO guard.
+    pub fn map(&self, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
+        memory.map_mmio_range(self.cam_range.clone()).map_err(|e| {
+            error!("Failed to map PCI CAM: {}", e);
+            RebootReason::InternalError
+        })?;
+
+        memory.map_mmio_range(self.bar_range.start as usize..self.bar_range.end as usize).map_err(
+            |e| {
+                error!("Failed to map PCI MMIO range: {}", e);
+                RebootReason::InternalError
+            },
+        )?;
+
+        Ok(())
+    }
+}
+
 /// Finds an FDT node with compatible=pci-host-cam-generic.
-pub fn pci_node(fdt: &Fdt) -> Result<FdtNode, RebootReason> {
+fn pci_node(fdt: &Fdt) -> Result<FdtNode, RebootReason> {
     fdt.compatible_nodes(CStr::from_bytes_with_nul(b"pci-host-cam-generic\0").unwrap())
         .map_err(|e| {
             error!("Failed to find PCI bus in FDT: {}", e);
@@ -33,8 +74,8 @@
         .ok_or(RebootReason::InvalidFdt)
 }
 
-pub fn map_cam(pci_node: &FdtNode, memory: &mut MemoryTracker) -> Result<(), RebootReason> {
-    // Parse reg property to find CAM.
+/// Parses the "reg" property of the given PCI FDT node to find the MMIO CAM range.
+fn parse_cam_range(pci_node: &FdtNode) -> Result<MemoryRange, RebootReason> {
     let pci_reg = pci_node
         .reg()
         .map_err(|e| {
@@ -64,11 +105,94 @@
         return Err(RebootReason::InvalidFdt);
     }
 
-    // Map the CAM as MMIO.
-    memory.map_mmio_range(cam_addr..cam_addr + cam_size).map_err(|e| {
-        error!("Failed to map PCI CAM: {}", e);
-        RebootReason::InternalError
-    })?;
+    Ok(cam_addr..cam_addr + cam_size)
+}
 
-    Ok(())
+/// Parses the "ranges" property of the given PCI FDT node, and returns the largest suitable range
+/// to use for non-prefetchable 32-bit memory BARs.
+fn parse_ranges(pci_node: &FdtNode) -> Result<Range<u32>, RebootReason> {
+    let mut memory_address = 0;
+    let mut memory_size = 0;
+
+    for AddressRange { addr: (flags, bus_address), parent_addr: cpu_physical, size } in pci_node
+        .ranges::<(u32, u64), u64, u64>()
+        .map_err(|e| {
+            error!("Error getting ranges property from PCI node: {}", e);
+            RebootReason::InvalidFdt
+        })?
+        .ok_or_else(|| {
+            error!("PCI node missing ranges property.");
+            RebootReason::InvalidFdt
+        })?
+    {
+        let flags = PciMemoryFlags(flags);
+        let prefetchable = flags.prefetchable();
+        let range_type = flags.range_type();
+        debug!(
+            "range: {:?} {}prefetchable bus address: {:#018x} CPU physical address: {:#018x} size: {:#018x}",
+            range_type,
+            if prefetchable { "" } else { "non-" },
+            bus_address,
+            cpu_physical,
+            size,
+        );
+
+        // Use a 64-bit range for 32-bit memory, if it is low enough, because crosvm doesn't
+        // currently provide any 32-bit ranges.
+        if !prefetchable
+            && matches!(range_type, PciRangeType::Memory32 | PciRangeType::Memory64)
+            && size > memory_size.into()
+            && bus_address + size < u32::MAX.into()
+        {
+            if bus_address != cpu_physical {
+                error!(
+                    "bus address {:#018x} != CPU physical address {:#018x}",
+                    bus_address, cpu_physical
+                );
+                return Err(RebootReason::InvalidFdt);
+            }
+            memory_address = u32::try_from(cpu_physical).unwrap();
+            memory_size = u32::try_from(size).unwrap();
+        }
+    }
+
+    if memory_size == 0 {
+        error!("No suitable PCI memory range found.");
+        return Err(RebootReason::InvalidFdt);
+    }
+
+    Ok(memory_address..memory_address + memory_size)
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+struct PciMemoryFlags(u32);
+
+impl PciMemoryFlags {
+    pub fn prefetchable(self) -> bool {
+        self.0 & 0x80000000 != 0
+    }
+
+    pub fn range_type(self) -> PciRangeType {
+        PciRangeType::from((self.0 & 0x3000000) >> 24)
+    }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+enum PciRangeType {
+    ConfigurationSpace,
+    IoSpace,
+    Memory32,
+    Memory64,
+}
+
+impl From<u32> for PciRangeType {
+    fn from(value: u32) -> Self {
+        match value {
+            0 => Self::ConfigurationSpace,
+            1 => Self::IoSpace,
+            2 => Self::Memory32,
+            3 => Self::Memory64,
+            _ => panic!("Tried to convert invalid range type {}", value),
+        }
+    }
 }
diff --git a/vm_payload/README.md b/vm_payload/README.md
new file mode 100644
index 0000000..6ce6770
--- /dev/null
+++ b/vm_payload/README.md
@@ -0,0 +1,70 @@
+# VM Payload API
+
+This directory contains the definition of the VM Payload API. This is a native
+API, exposed as a set of C functions, available to payload code running inside a
+[Microdroid](../microdroid/README.md) VM.
+
+Note that only native code is supported in Microdroid, so no Java APIs are
+available in the VM, and only 64 bit code is supported.
+
+To create a VM and run the payload from Android, see
+[android.system.virtualmachine.VirtualMachineManager](../javalib/src/android/system/virtualmachine/VirtualMachineManager.java).
+
+## Entry point
+
+The payload should be packaged as one (or more) .so files inside the app's APK -
+under the `lib/<ABI>` directory, like other JNI code.
+
+The primary .so, which is specified as part of the VM configuration via
+[VirtualMachineConfig.Builder#setPayloadBinaryPath](../javalib/src/android/system/virtualmachine/VirtualMachineConfig.java),
+must define the entry point for the payload.
+
+This entry point is a C function called `AVmPayload_main()`, as declared in
+[vm_main.h](include/vm_main.h). (In C++ this must be defined as `extern "C"`.)
+
+## API header
+
+The functions available to the payload once it starts are declared in
+[vm_payload.h](include/vm_payload.h).
+
+### Linking
+
+In the Android build system, the payload binary should be built with
+`libvm_payload#current` specified as one of the `shared_libs`; this links
+against a stub `libvm_payload.so`, where the dependencies will be satisfied at
+runtime from the real `libvm_payload.so` hosted within the Microdroid VM.
+
+See `MicrodroidTestNativeLib` in the [test APK](../tests/testapk/Android.bp) for
+an example.
+
+In other build systems a similar stub `libvm_payload.so` can be built using
+[stub.c](stub/stub.c).
+
+## Available NDK APIs
+
+In addition to the VM Payload APIs, a small subset of the [Android
+NDK](https://developer.android.com/ndk) can be used by the payload.
+
+This subset consists of:
+- The [standard C library](https://developer.android.com/ndk/guides/stable_apis#c_library).
+- The [Logging APIs](https://developer.android.com/ndk/guides/stable_apis#logging).
+- The [NdkBinder
+  API](https://developer.android.com/ndk/reference/group/ndk-binder). However
+  note that the payload can only host a binder server via
+  `AVmPayload_runVsockRpcServer`, defined in
+  [vm_payload.h](include/vm_payload.h), rather than
+  `AServiceManager_addService`, and cannot connect to any binder server. Passing
+  file descriptors to and from the VM is not supported.
+
+## C++
+
+C++ can be used, but you will need to include the C++ runtime in your APK along
+with your payload, either statically linked (if
+[appropriate](https://developer.android.com/ndk/guides/cpp-support#sr) or as a
+separate .so.
+
+The same is true for other languages such as Rust.
+
+See [AIDL
+backends](https://source.android.com/docs/core/architecture/aidl/aidl-backends)
+for information on using AIDL with the NDK Binder from C++.
diff --git a/vm_payload/include/vm_main.h b/vm_payload/include/vm_main.h
index e351174..5158b43 100644
--- a/vm_payload/include/vm_main.h
+++ b/vm_payload/include/vm_main.h
@@ -23,5 +23,17 @@
 }
 #else
 typedef int AVmPayload_main_t(void);
+
+/**
+ * Entry point for the VM payload. This function must be implemented by the
+ * payload binary, and is called by Microdroid to start the payload inside the
+ * VM.
+ *
+ * When the function returns the VM will be shut down.  If the host app has set
+ * a `VirtualMachineCallback` for the VM, its `onPayloadFinished` method will be
+ * called with the VM's exit code.
+ *
+ * \return the exit code of the VM.
+ */
 extern int AVmPayload_main(void);
 #endif
diff --git a/vmbase/Android.bp b/vmbase/Android.bp
index fb525b5..7a36a0a 100644
--- a/vmbase/Android.bp
+++ b/vmbase/Android.bp
@@ -46,7 +46,6 @@
     defaults: ["vmbase_cc_defaults"],
     static_executable: true,
     static_libs: [
-        "libarm-optimized-routines-mem",
         "libvmbase_entry",
     ],
     installable: false,
@@ -68,6 +67,9 @@
         "libpsci",
         "libspin_nostd",
     ],
+    whole_static_libs: [
+        "libarm-optimized-routines-mem",
+    ],
     apex_available: ["com.android.virt"],
 }
 
diff --git a/vmbase/example/Android.bp b/vmbase/example/Android.bp
index e0a87db..fbad8f4 100644
--- a/vmbase/example/Android.bp
+++ b/vmbase/example/Android.bp
@@ -17,9 +17,6 @@
         "libvirtio_drivers",
         "libvmbase",
     ],
-    static_libs: [
-        "libarm-optimized-routines-mem",
-    ],
     apex_available: ["com.android.virt"],
 }
 
diff --git a/vmbase/example/src/pci.rs b/vmbase/example/src/pci.rs
index 10a67b9..82ea7cc 100644
--- a/vmbase/example/src/pci.rs
+++ b/vmbase/example/src/pci.rs
@@ -17,7 +17,7 @@
 use aarch64_paging::paging::MemoryRegion;
 use alloc::alloc::{alloc, dealloc, Layout};
 use core::{ffi::CStr, mem::size_of};
-use libfdt::{Fdt, FdtNode, Reg};
+use libfdt::{AddressRange, Fdt, FdtNode, Reg};
 use log::{debug, info};
 use virtio_drivers::{
     pci::{
@@ -113,17 +113,14 @@
     pub fn for_pci_ranges(pci_node: &FdtNode) -> Self {
         let mut memory_32_address = 0;
         let mut memory_32_size = 0;
-        for range in pci_node
-            .ranges::<u128, u64, u64>()
+        for AddressRange { addr: (flags, bus_address), parent_addr: cpu_physical, size } in pci_node
+            .ranges::<(u32, u64), u64, u64>()
             .expect("Error getting ranges property from PCI node")
             .expect("PCI node missing ranges property.")
         {
-            let flags = PciMemoryFlags((range.addr >> 64) as u32);
+            let flags = PciMemoryFlags(flags);
             let prefetchable = flags.prefetchable();
             let range_type = flags.range_type();
-            let bus_address = range.addr as u64;
-            let cpu_physical = range.parent_addr;
-            let size = range.size;
             info!(
                 "range: {:?} {}prefetchable bus address: {:#018x} host physical address: {:#018x} size: {:#018x}",
                 range_type,
diff --git a/vmbase/src/bionic.rs b/vmbase/src/bionic.rs
new file mode 100644
index 0000000..8b3a076
--- /dev/null
+++ b/vmbase/src/bionic.rs
@@ -0,0 +1,25 @@
+// Copyright 2022, 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.
+
+//! Low-level compatibility layer between baremetal Rust and Bionic C functions.
+
+use crate::linker;
+
+/// Reference to __stack_chk_guard.
+pub static STACK_CHK_GUARD: &u64 = unsafe { &linker::__stack_chk_guard };
+
+#[no_mangle]
+extern "C" fn __stack_chk_fail() -> ! {
+    panic!("stack guard check failed");
+}
diff --git a/vmbase/src/lib.rs b/vmbase/src/lib.rs
index a012442..d577802 100644
--- a/vmbase/src/lib.rs
+++ b/vmbase/src/lib.rs
@@ -16,6 +16,7 @@
 
 #![no_std]
 
+mod bionic;
 pub mod console;
 mod entry;
 pub mod layout;
@@ -24,6 +25,8 @@
 pub mod power;
 pub mod uart;
 
+pub use bionic::STACK_CHK_GUARD;
+
 use core::panic::PanicInfo;
 use power::reboot;
 
@@ -32,11 +35,3 @@
     eprintln!("{}", info);
     reboot()
 }
-
-/// Reference to __stack_chk_guard.
-pub static STACK_CHK_GUARD: &u64 = unsafe { &linker::__stack_chk_guard };
-
-#[no_mangle]
-extern "C" fn __stack_chk_fail() -> ! {
-    panic!("stack guard check failed");
-}