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");
-}