Merge "Update Virtualization module to protobuf3."
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 77a1204..a8a176a 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -140,8 +140,15 @@
// Let logs go to logcat.
let (console_fd, log_fd) = (None, None);
let callback = Box::new(Callback {});
- let instance = VmInstance::create(service, &config, console_fd, log_fd, Some(callback))
- .context("Failed to create VM")?;
+ let instance = VmInstance::create(
+ service,
+ &config,
+ console_fd,
+ /*console_in_fd */ None,
+ log_fd,
+ Some(callback),
+ )
+ .context("Failed to create VM")?;
instance.start()?;
diff --git a/demo_native/main.cpp b/demo_native/main.cpp
index fa87549..bc42036 100644
--- a/demo_native/main.cpp
+++ b/demo_native/main.cpp
@@ -223,10 +223,11 @@
std::shared_ptr<IVirtualMachine> vm;
VirtualMachineConfig config = std::move(app_config);
- ScopedFileDescriptor console_fd(fcntl(fileno(stdout), F_DUPFD_CLOEXEC));
+ ScopedFileDescriptor console_out_fd(fcntl(fileno(stdout), F_DUPFD_CLOEXEC));
+ ScopedFileDescriptor console_in_fd(fcntl(fileno(stdin), F_DUPFD_CLOEXEC));
ScopedFileDescriptor log_fd(fcntl(fileno(stdout), F_DUPFD_CLOEXEC));
- ScopedAStatus ret = service.createVm(config, console_fd, log_fd, &vm);
+ ScopedAStatus ret = service.createVm(config, console_out_fd, console_in_fd, log_fd, &vm);
if (!ret.isOk()) {
return Error() << "Failed to create VM";
}
diff --git a/docs/getting_started/index.md b/docs/getting_started/index.md
index 0e4f2be..9dcd4fa 100644
--- a/docs/getting_started/index.md
+++ b/docs/getting_started/index.md
@@ -103,9 +103,12 @@
on pVM. You can run a Microdroid with empty payload using the following command:
```shell
-adb shell /apex/com.android.virt/bin/vm run-microdroid --debug full
+adb shell /apex/com.android.virt/bin/vm run-microdroid
```
+which spawns a "debuggable" VM by default to allow access to guest kernel logs.
+To run a production non-debuggable VM, pass `--debug none`.
+
## 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/javalib/api/test-current.txt b/javalib/api/test-current.txt
index 8b7ec11..1298000 100644
--- a/javalib/api/test-current.txt
+++ b/javalib/api/test-current.txt
@@ -2,15 +2,18 @@
package android.system.virtualmachine {
public class VirtualMachine implements java.lang.AutoCloseable {
+ method @NonNull @WorkerThread public java.io.OutputStream getConsoleInput() throws android.system.virtualmachine.VirtualMachineException;
method @NonNull public java.io.File getRootDir();
}
public final class VirtualMachineConfig {
method @Nullable public String getPayloadConfigPath();
+ method public boolean isVmConsoleInputSupported();
}
public static final class VirtualMachineConfig.Builder {
method @NonNull @RequiresPermission(android.system.virtualmachine.VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION) public android.system.virtualmachine.VirtualMachineConfig.Builder setPayloadConfigPath(@NonNull String);
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setVmConsoleInputSupported(boolean);
}
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index f96effa..675a046 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -76,11 +76,13 @@
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.io.OutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.channels.FileChannel;
@@ -294,6 +296,8 @@
private final boolean mVmOutputCaptured;
+ private final boolean mVmConsoleInputSupported;
+
/** The configuration that is currently associated with this VM. */
@GuardedBy("mLock")
@NonNull
@@ -306,11 +310,19 @@
@GuardedBy("mLock")
@Nullable
- private ParcelFileDescriptor mConsoleReader;
+ private ParcelFileDescriptor mConsoleOutReader;
@GuardedBy("mLock")
@Nullable
- private ParcelFileDescriptor mConsoleWriter;
+ private ParcelFileDescriptor mConsoleOutWriter;
+
+ @GuardedBy("mLock")
+ @Nullable
+ private ParcelFileDescriptor mConsoleInReader;
+
+ @GuardedBy("mLock")
+ @Nullable
+ private ParcelFileDescriptor mConsoleInWriter;
@GuardedBy("mLock")
@Nullable
@@ -372,6 +384,7 @@
: null;
mVmOutputCaptured = config.isVmOutputCaptured();
+ mVmConsoleInputSupported = config.isVmConsoleInputSupported();
}
/**
@@ -787,7 +800,11 @@
try {
if (mVmOutputCaptured) {
- createVmPipes();
+ createVmOutputPipes();
+ }
+
+ if (mVmConsoleInputSupported) {
+ createVmInputPipes();
}
VirtualMachineAppConfig appConfig =
@@ -804,7 +821,9 @@
android.system.virtualizationservice.VirtualMachineConfig.appConfig(
appConfig);
- mVirtualMachine = service.createVm(vmConfigParcel, mConsoleWriter, mLogWriter);
+ mVirtualMachine =
+ service.createVm(
+ vmConfigParcel, mConsoleOutWriter, mConsoleInReader, mLogWriter);
mVirtualMachine.registerCallback(new CallbackTranslator(service));
mContext.registerComponentCallbacks(mMemoryManagementCallbacks);
mVirtualMachine.start();
@@ -843,12 +862,12 @@
}
@GuardedBy("mLock")
- private void createVmPipes() throws VirtualMachineException {
+ private void createVmOutputPipes() throws VirtualMachineException {
try {
- if (mConsoleReader == null || mConsoleWriter == null) {
+ if (mConsoleOutReader == null || mConsoleOutWriter == null) {
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- mConsoleReader = pipe[0];
- mConsoleWriter = pipe[1];
+ mConsoleOutReader = pipe[0];
+ mConsoleOutWriter = pipe[1];
}
if (mLogReader == null || mLogWriter == null) {
@@ -857,7 +876,20 @@
mLogWriter = pipe[1];
}
} catch (IOException e) {
- throw new VirtualMachineException("Failed to create stream for VM", e);
+ throw new VirtualMachineException("Failed to create output stream for VM", e);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void createVmInputPipes() throws VirtualMachineException {
+ try {
+ if (mConsoleInReader == null || mConsoleInWriter == null) {
+ ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+ mConsoleInReader = pipe[0];
+ mConsoleInWriter = pipe[1];
+ }
+ } catch (IOException e) {
+ throw new VirtualMachineException("Failed to create input stream for VM", e);
}
}
@@ -883,12 +915,37 @@
throw new VirtualMachineException("Capturing vm outputs is turned off");
}
synchronized (mLock) {
- createVmPipes();
- return new FileInputStream(mConsoleReader.getFileDescriptor());
+ createVmOutputPipes();
+ return new FileInputStream(mConsoleOutReader.getFileDescriptor());
}
}
/**
+ * Returns the stream object representing the console input to the virtual machine. The console
+ * input is only available if the {@link VirtualMachineConfig} specifies that it should be
+ * {@linkplain VirtualMachineConfig#isVmConsoleInputSupported supported}.
+ *
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
+ * @throws VirtualMachineException if the stream could not be created, or console input is not
+ * supported.
+ * @hide
+ */
+ @TestApi
+ @WorkerThread
+ @NonNull
+ public OutputStream getConsoleInput() throws VirtualMachineException {
+ if (!mVmConsoleInputSupported) {
+ throw new VirtualMachineException("VM console input is not supported");
+ }
+ synchronized (mLock) {
+ createVmInputPipes();
+ return new FileOutputStream(mConsoleInWriter.getFileDescriptor());
+ }
+ }
+
+
+ /**
* Returns the stream object representing the log output from the virtual machine. The log
* output is only available if the VirtualMachineConfig specifies that it should be {@linkplain
* VirtualMachineConfig#isVmOutputCaptured captured}.
@@ -910,7 +967,7 @@
throw new VirtualMachineException("Capturing vm outputs is turned off");
}
synchronized (mLock) {
- createVmPipes();
+ createVmOutputPipes();
return new FileInputStream(mLogReader.getFileDescriptor());
}
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 5f24f5b..b400eeb 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -75,6 +75,7 @@
private static final String KEY_CPU_TOPOLOGY = "cpuTopology";
private static final String KEY_ENCRYPTED_STORAGE_BYTES = "encryptedStorageBytes";
private static final String KEY_VM_OUTPUT_CAPTURED = "vmOutputCaptured";
+ private static final String KEY_VM_CONSOLE_INPUT_SUPPORTED = "vmConsoleInputSupported";
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -163,6 +164,9 @@
/** Whether the app can read console and log output. */
private final boolean mVmOutputCaptured;
+ /** Whether the app can write console input to the VM */
+ private final boolean mVmConsoleInputSupported;
+
private VirtualMachineConfig(
@Nullable String packageName,
@Nullable String apkPath,
@@ -173,7 +177,8 @@
long memoryBytes,
@CpuTopology int cpuTopology,
long encryptedStorageBytes,
- boolean vmOutputCaptured) {
+ boolean vmOutputCaptured,
+ boolean vmConsoleInputSupported) {
// This is only called from Builder.build(); the builder handles parameter validation.
mPackageName = packageName;
mApkPath = apkPath;
@@ -185,6 +190,7 @@
mCpuTopology = cpuTopology;
mEncryptedStorageBytes = encryptedStorageBytes;
mVmOutputCaptured = vmOutputCaptured;
+ mVmConsoleInputSupported = vmConsoleInputSupported;
}
/** Loads a config from a file. */
@@ -259,6 +265,7 @@
builder.setEncryptedStorageBytes(encryptedStorageBytes);
}
builder.setVmOutputCaptured(b.getBoolean(KEY_VM_OUTPUT_CAPTURED));
+ builder.setVmConsoleInputSupported(b.getBoolean(KEY_VM_CONSOLE_INPUT_SUPPORTED));
return builder.build();
}
@@ -294,6 +301,7 @@
b.putLong(KEY_ENCRYPTED_STORAGE_BYTES, mEncryptedStorageBytes);
}
b.putBoolean(KEY_VM_OUTPUT_CAPTURED, mVmOutputCaptured);
+ b.putBoolean(KEY_VM_CONSOLE_INPUT_SUPPORTED, mVmConsoleInputSupported);
b.writeToStream(output);
}
@@ -412,6 +420,17 @@
}
/**
+ * Returns whether the app can write to the VM console.
+ *
+ * @see Builder#setVmConsoleInputSupported
+ * @hide
+ */
+ @TestApi
+ public boolean isVmConsoleInputSupported() {
+ return mVmConsoleInputSupported;
+ }
+
+ /**
* Tests if this config is compatible with other config. Being compatible means that the configs
* can be interchangeably used for the same virtual machine; they do not change the VM identity
* or secrets. Such changes include varying the number of CPUs or the size of the RAM. Changes
@@ -430,6 +449,7 @@
&& this.mProtectedVm == other.mProtectedVm
&& this.mEncryptedStorageBytes == other.mEncryptedStorageBytes
&& this.mVmOutputCaptured == other.mVmOutputCaptured
+ && this.mVmConsoleInputSupported == other.mVmConsoleInputSupported
&& Objects.equals(this.mPayloadConfigPath, other.mPayloadConfigPath)
&& Objects.equals(this.mPayloadBinaryName, other.mPayloadBinaryName)
&& Objects.equals(this.mPackageName, other.mPackageName)
@@ -551,6 +571,7 @@
@CpuTopology private int mCpuTopology = CPU_TOPOLOGY_ONE_CPU;
private long mEncryptedStorageBytes;
private boolean mVmOutputCaptured = false;
+ private boolean mVmConsoleInputSupported = false;
/**
* Creates a builder for the given context.
@@ -609,6 +630,10 @@
throw new IllegalStateException("debug level must be FULL to capture output");
}
+ if (mVmConsoleInputSupported && mDebugLevel != DEBUG_LEVEL_FULL) {
+ throw new IllegalStateException("debug level must be FULL to use console input");
+ }
+
return new VirtualMachineConfig(
packageName,
apkPath,
@@ -619,7 +644,8 @@
mMemoryBytes,
mCpuTopology,
mEncryptedStorageBytes,
- mVmOutputCaptured);
+ mVmOutputCaptured,
+ mVmConsoleInputSupported);
}
/**
@@ -819,5 +845,23 @@
mVmOutputCaptured = captured;
return this;
}
+
+ /**
+ * Sets whether to allow the app to write to the VM console. Default is {@code false}.
+ *
+ * <p>Setting this as {@code true} will allow the app to directly write into {@linkplain
+ * VirtualMachine#getConsoleInput console input}.
+ *
+ * <p>The {@linkplain #setDebugLevel debug level} must be {@link #DEBUG_LEVEL_FULL} to be
+ * set as true.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public Builder setVmConsoleInputSupported(boolean supported) {
+ mVmConsoleInputSupported = supported;
+ return this;
+ }
}
}
diff --git a/libs/avb/Android.bp b/libs/avb/Android.bp
index 3a671e2..a2d9e1a 100644
--- a/libs/avb/Android.bp
+++ b/libs/avb/Android.bp
@@ -10,7 +10,6 @@
visibility: ["//packages/modules/Virtualization:__subpackages__"],
source_stem: "bindings",
bindgen_flags: [
- "--size_t-is-usize",
"--constified-enum-module AvbDescriptorTag",
"--default-enum-style rust",
"--allowlist-type=AvbDescriptorTag",
diff --git a/libs/libfdt/Android.bp b/libs/libfdt/Android.bp
index 2a6e75f..0540f26 100644
--- a/libs/libfdt/Android.bp
+++ b/libs/libfdt/Android.bp
@@ -8,7 +8,6 @@
wrapper_src: "bindgen/fdt.h",
source_stem: "bindings",
bindgen_flags: [
- "--size_t-is-usize",
"--allowlist-type=fdt_.*",
"--allowlist-function=fdt_.*",
"--allowlist-var=FDT_.*",
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 67b44f0..1cdcde1 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -228,7 +228,7 @@
load_crashkernel_if_supported().context("Failed to load crashkernel")?;
- swap::init_swap().context("Failed to initialise swap")?;
+ swap::init_swap().context("Failed to initialize swap")?;
info!("swap enabled.");
let service = get_vms_rpc_binder()
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index c9909e6..bbe00b5 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -7,7 +7,6 @@
crate_name: "pvmfw",
defaults: ["vmbase_ffi_defaults"],
srcs: ["src/main.rs"],
- edition: "2021",
// Require unsafe blocks for inside unsafe functions.
flags: ["-Dunsafe_op_in_unsafe_fn"],
features: [
@@ -16,7 +15,6 @@
rustlibs: [
"libaarch64_paging",
"libbssl_ffi_nostd",
- "libbuddy_system_allocator",
"libciborium_nostd",
"libciborium_io_nostd",
"libdiced_open_dice_nostd",
@@ -83,7 +81,6 @@
// partition image. This is just to package the unstripped file into the
// symbols zip file for debugging purpose.
installable: true,
- native_coverage: false,
}
raw_binary {
@@ -135,11 +132,9 @@
rust_library_rlib {
name: "libpvmfw_embedded_key",
- defaults: ["vmbase_ffi_defaults"],
- prefer_rlib: true,
+ defaults: ["vmbase_rlib_defaults"],
srcs: [":pvmfw_embedded_key_rs"],
crate_name: "pvmfw_embedded_key",
- apex_available: ["com.android.virt"],
}
prebuilt_etc {
@@ -193,8 +188,7 @@
rust_library_rlib {
name: "libpvmfw_fdt_template",
- defaults: ["vmbase_ffi_defaults"],
- prefer_rlib: true,
+ defaults: ["vmbase_rlib_defaults"],
srcs: [":pvmfw_fdt_template_rs"],
crate_name: "pvmfw_fdt_template",
}
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index 78383d2..6f96fc0 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -15,10 +15,8 @@
//! Low-level entry and exit points of pvmfw.
use crate::config;
-use crate::configure_global_allocator_size;
use crate::crypto;
use crate::fdt;
-use crate::heap;
use crate::memory;
use core::arch::asm;
use core::mem::{drop, size_of};
@@ -33,7 +31,7 @@
use log::LevelFilter;
use vmbase::util::RangeExt as _;
use vmbase::{
- console,
+ configure_heap, console,
layout::{self, crosvm},
logger, main,
memory::{min_dcache_line_size, MemoryTracker, MEMORY, SIZE_128KB, SIZE_4KB},
@@ -63,7 +61,7 @@
}
main!(start);
-configure_global_allocator_size!(SIZE_128KB);
+configure_heap!(SIZE_128KB);
/// Entry point for pVM firmware.
pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
@@ -71,9 +69,6 @@
// - can't access non-pvmfw memory (only statically-mapped memory)
// - can't access MMIO (therefore, no logging)
- // SAFETY - This function should and will only be called once, here.
- unsafe { heap::init() };
-
match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
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.
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index efb354c..319100f 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -124,8 +124,24 @@
node.setprop(cstr!("bootargs"), bootargs.to_bytes_with_nul())
}
-/// Check if memory range is ok
-fn validate_memory_range(range: &Range<usize>) -> Result<(), RebootReason> {
+/// Reads and validates the memory range in the DT.
+///
+/// Only one memory range is expected with the crosvm setup for now.
+fn read_and_validate_memory_range(fdt: &Fdt) -> Result<Range<usize>, RebootReason> {
+ let mut memory = fdt.memory().map_err(|e| {
+ error!("Failed to read memory range from DT: {e}");
+ RebootReason::InvalidFdt
+ })?;
+ let range = memory.next().ok_or_else(|| {
+ error!("The /memory node in the DT contains no range.");
+ RebootReason::InvalidFdt
+ })?;
+ if memory.next().is_some() {
+ warn!(
+ "The /memory node in the DT contains more than one memory range, \
+ while only one is expected."
+ );
+ }
let base = range.start;
if base != MEM_START {
error!("Memory base address {:#x} is not {:#x}", base, MEM_START);
@@ -142,7 +158,7 @@
error!("Memory size is 0");
return Err(RebootReason::InvalidFdt);
}
- Ok(())
+ Ok(range)
}
fn patch_memory_range(fdt: &mut Fdt, memory_range: &Range<usize>) -> libfdt::Result<()> {
@@ -600,11 +616,7 @@
RebootReason::InvalidFdt
})?;
- let memory_range = fdt.first_memory_range().map_err(|e| {
- error!("Failed to read memory range from DT: {e}");
- RebootReason::InvalidFdt
- })?;
- validate_memory_range(&memory_range)?;
+ let memory_range = read_and_validate_memory_range(fdt)?;
let bootargs = read_bootargs_from(fdt).map_err(|e| {
error!("Failed to read bootargs from DT: {e}");
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 8c5d5d5..ba453e7 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -28,7 +28,6 @@
mod exceptions;
mod fdt;
mod gpt;
-mod heap;
mod helpers;
mod instance;
mod memory;
@@ -49,6 +48,7 @@
use pvmfw_avb::Capability;
use pvmfw_avb::DebugLevel;
use pvmfw_embedded_key::PUBLIC_KEY;
+use vmbase::heap;
use vmbase::memory::flush;
use vmbase::memory::MEMORY;
use vmbase::virtio::pci;
@@ -95,8 +95,8 @@
// Set up PCI bus for VirtIO devices.
let pci_info = PciInfo::from_fdt(fdt).map_err(handle_pci_error)?;
debug!("PCI: {:#x?}", pci_info);
- let mut pci_root = pci::initialise(pci_info, MEMORY.lock().as_mut().unwrap()).map_err(|e| {
- error!("Failed to initialise PCI: {e}");
+ let mut pci_root = pci::initialize(pci_info, MEMORY.lock().as_mut().unwrap()).map_err(|e| {
+ error!("Failed to initialize PCI: {e}");
RebootReason::InternalError
})?;
@@ -104,6 +104,11 @@
error!("Failed to verify the payload: {e}");
RebootReason::PayloadVerificationError
})?;
+ let debuggable = verified_boot_data.debug_level != DebugLevel::None;
+ if debuggable {
+ info!("Successfully verified a debuggable payload.");
+ info!("Please disregard any previous libavb ERROR about initrd_normal.");
+ }
if verified_boot_data.capabilities.contains(&Capability::RemoteAttest) {
info!("Service VM capable of remote attestation detected");
@@ -146,7 +151,6 @@
flush(next_bcc);
let strict_boot = true;
- let debuggable = verified_boot_data.debug_level != DebugLevel::None;
modify_for_next_stage(fdt, next_bcc, new_instance, strict_boot, debug_policy, debuggable)
.map_err(|e| {
error!("Failed to configure device tree: {e}");
diff --git a/rialto/Android.bp b/rialto/Android.bp
index 59f8ba2..1840278 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -6,18 +6,16 @@
name: "librialto",
crate_name: "rialto",
srcs: ["src/main.rs"],
- edition: "2021",
defaults: ["vmbase_ffi_defaults"],
rustlibs: [
"libaarch64_paging",
- "libbuddy_system_allocator",
"libhyp",
"libfdtpci",
"liblibfdt",
"liblog_rust_nostd",
+ "libvirtio_drivers",
"libvmbase",
],
- apex_available: ["com.android.virt"],
}
cc_binary {
@@ -29,13 +27,11 @@
],
static_libs: [
"librialto",
- "libvmbase_entry",
],
linker_scripts: [
"image.ld",
":vmbase_sections",
],
- apex_available: ["com.android.virt"],
}
raw_binary {
diff --git a/rialto/src/error.rs b/rialto/src/error.rs
index 8e2991c..84228c4 100644
--- a/rialto/src/error.rs
+++ b/rialto/src/error.rs
@@ -19,7 +19,7 @@
use fdtpci::PciError;
use hyp::Error as HypervisorError;
use libfdt::FdtError;
-use vmbase::memory::MemoryTrackerError;
+use vmbase::{memory::MemoryTrackerError, virtio::pci};
pub type Result<T> = result::Result<T, Error>;
@@ -37,6 +37,8 @@
InvalidPci(PciError),
/// Failed memory operation.
MemoryOperationFailed(MemoryTrackerError),
+ /// Failed to initialize PCI.
+ PciInitializationFailed(pci::PciError),
}
impl fmt::Display for Error {
@@ -50,6 +52,7 @@
Self::InvalidFdt(e) => write!(f, "Invalid FDT: {e}"),
Self::InvalidPci(e) => write!(f, "Invalid PCI: {e}"),
Self::MemoryOperationFailed(e) => write!(f, "Failed memory operation: {e}"),
+ Self::PciInitializationFailed(e) => write!(f, "Failed to initialize PCI: {e}"),
}
}
}
diff --git a/rialto/src/main.rs b/rialto/src/main.rs
index 29056f1..61c985e 100644
--- a/rialto/src/main.rs
+++ b/rialto/src/main.rs
@@ -23,7 +23,6 @@
extern crate alloc;
use crate::error::{Error, Result};
-use buddy_system_allocator::LockedHeap;
use core::num::NonZeroUsize;
use core::result;
use core::slice;
@@ -32,32 +31,18 @@
use libfdt::FdtError;
use log::{debug, error, info};
use vmbase::{
+ configure_heap,
fdt::SwiotlbInfo,
layout::{self, crosvm},
main,
- memory::{MemoryTracker, PageTable, MEMORY, PAGE_SIZE},
+ memory::{MemoryTracker, PageTable, MEMORY, PAGE_SIZE, SIZE_64KB},
power::reboot,
+ virtio::pci,
};
-const SZ_1K: usize = 1024;
-const SZ_64K: usize = 64 * SZ_1K;
-
-#[global_allocator]
-static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new();
-
-static mut HEAP: [u8; SZ_64K] = [0; SZ_64K];
-
-fn init_heap() {
- // SAFETY: Allocator set to otherwise unused, static memory.
- unsafe {
- HEAP_ALLOCATOR.lock().init(&mut HEAP as *mut u8 as usize, HEAP.len());
- }
-}
-
fn new_page_table() -> Result<PageTable> {
let mut page_table = PageTable::default();
- page_table.map_device(&crosvm::MMIO_RANGE)?;
page_table.map_data(&layout::scratch_range())?;
page_table.map_data(&layout::stack_range(40 * PAGE_SIZE))?;
page_table.map_code(&layout::text_range())?;
@@ -106,8 +91,6 @@
let fdt = unsafe { slice::from_raw_parts(fdt_range.start as *mut u8, fdt_range.len()) };
// We do not need to validate the DT since it is already validated in pvmfw.
let fdt = libfdt::Fdt::from_slice(fdt)?;
- let pci_info = PciInfo::from_fdt(fdt)?;
- debug!("PCI: {pci_info:#x?}");
let memory_range = fdt.first_memory_range()?;
MEMORY.lock().as_mut().unwrap().shrink(&memory_range).map_err(|e| {
@@ -131,6 +114,12 @@
e
})?;
}
+
+ let pci_info = PciInfo::from_fdt(fdt)?;
+ debug!("PCI: {pci_info:#x?}");
+ let pci_root = pci::initialize(pci_info, MEMORY.lock().as_mut().unwrap())
+ .map_err(Error::PciInitializationFailed)?;
+ debug!("PCI root: {pci_root:#x?}");
Ok(())
}
@@ -163,7 +152,6 @@
/// Entry point for Rialto.
pub fn main(fdt_addr: u64, _a1: u64, _a2: u64, _a3: u64) {
- init_heap();
let Ok(mmio_guard_supported) = try_init_logger() else {
// Don't log anything if the logger initialization fails.
reboot();
@@ -181,3 +169,4 @@
}
main!(main);
+configure_heap!(SIZE_64KB);
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index 7048b44..98c935d 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -114,8 +114,15 @@
taskProfiles: vec![],
gdbPort: 0, // No gdb
});
- let vm = VmInstance::create(service.as_ref(), &config, Some(console), Some(log), None)
- .context("Failed to create VM")?;
+ let vm = VmInstance::create(
+ service.as_ref(),
+ &config,
+ Some(console),
+ /* consoleIn */ None,
+ Some(log),
+ None,
+ )
+ .context("Failed to create VM")?;
vm.start().context("Failed to start VM")?;
diff --git a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
index a6f1c80..8d467cd 100644
--- a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
@@ -70,6 +70,9 @@
/** Requests the VM to asynchronously call appCallback.setVmCallback() */
void requestCallback(IAppCallback appCallback);
+ /** Read a line from /dev/console */
+ String readLineFromConsole();
+
/**
* Request the service to exit, triggering the termination of the VM. This may cause any
* requests in flight to fail.
diff --git a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
index 7e6080f..e6d90ea 100644
--- a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
@@ -456,6 +456,7 @@
public long[] mTimings;
public int mFileMode;
public int mMountFlags;
+ public String mConsoleInput;
public void assertNoException() {
if (mException != null) {
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 1f5fcee..deaf08a 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -55,7 +55,6 @@
import org.json.JSONObject;
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@@ -496,7 +495,7 @@
vmInfo.mProcess.destroy();
}
- private void waitForCrosvmExit(CommandRunner android) throws Exception {
+ private void waitForCrosvmExit(CommandRunner android, String testStartTime) throws Exception {
// TODO: improve crosvm exit check. b/258848245
android.runWithTimeout(
15000,
@@ -504,10 +503,12 @@
"-m",
"1",
"-e",
- "'virtualizationmanager::crosvm.*exited with status exit status:'");
+ "'virtualizationmanager::crosvm.*exited with status exit status:'",
+ "-T",
+ "'" + testStartTime + "'");
}
- private boolean isTombstoneReceivedFromHostLogcat() throws Exception {
+ private boolean isTombstoneReceivedFromHostLogcat(String testStartTime) throws Exception {
// Note this method relies on logcat values being printed by the receiver on host
// userspace crash log: virtualizationservice/src/aidl.rs
// kernel ramdump log: virtualizationmanager/src/crosvm.rs
@@ -526,12 +527,17 @@
"-m",
"1",
"-e",
- ramdumpRegex);
+ ramdumpRegex,
+ "-T",
+ testStartTime);
return !result.trim().isEmpty();
}
private boolean isTombstoneGeneratedWithCmd(
boolean protectedVm, String configPath, String... crashCommand) throws Exception {
+ CommandRunner android = new CommandRunner(getDevice());
+ String testStartTime = android.runWithTimeout(1000, "date", "'+%Y-%m-%d %H:%M:%S.%N'");
+
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
.debugLevel("full")
@@ -546,10 +552,9 @@
microdroid.run(crashCommand);
// check until microdroid is shut down
- CommandRunner android = new CommandRunner(getDevice());
- waitForCrosvmExit(android);
+ waitForCrosvmExit(android, testStartTime);
- return isTombstoneReceivedFromHostLogcat();
+ return isTombstoneReceivedFromHostLogcat(testStartTime);
}
@Test
@@ -607,6 +612,7 @@
throws Exception {
// we can't use microdroid builder as it wants ADB connection (debuggable)
CommandRunner android = new CommandRunner(getDevice());
+ String testStartTime = android.runWithTimeout(1000, "date", "'+%Y-%m-%d %H:%M:%S.%N'");
android.run("rm", "-rf", TEST_ROOT + "*");
android.run("mkdir", "-p", TEST_ROOT + "*");
@@ -627,7 +633,7 @@
Collections.addAll(cmd, additionalArgs);
android.run(cmd.toArray(new String[0]));
- return isTombstoneReceivedFromHostLogcat();
+ return isTombstoneReceivedFromHostLogcat(testStartTime);
}
private boolean isTombstoneGeneratedWithCrashPayload(boolean debuggable) throws Exception {
@@ -960,9 +966,6 @@
prepareVirtualizationTestSetup(getDevice());
getDevice().installPackage(findTestFile(APK_NAME), /* reinstall */ false);
-
- // clear the log
- getDevice().executeShellV2Command("logcat -c");
}
@After
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 a64b62a..ffb2c11 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -1652,6 +1652,35 @@
}
@Test
+ public void testConsoleInputSupported() throws Exception {
+ assumeSupportedDevice();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilder()
+ .setPayloadBinaryName("MicrodroidTestNativeLib.so")
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .setVmConsoleInputSupported(true)
+ .setVmOutputCaptured(true)
+ .build();
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_console_in", config);
+
+ final String TYPED = "this is a console input\n";
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ OutputStreamWriter consoleIn =
+ new OutputStreamWriter(vm.getConsoleInput());
+ consoleIn.write(TYPED);
+ consoleIn.close();
+ tr.mConsoleInput = ts.readLineFromConsole();
+ });
+ testResults.assertNoException();
+ assertThat(testResults.mConsoleInput).isEqualTo(TYPED);
+ }
+
+ @Test
public void testStartVmWithPayloadOfAnotherApp() throws Exception {
assumeSupportedDevice();
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 7e0fc5b..297b505 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -313,6 +313,26 @@
return ScopedAStatus::ok();
}
+ ScopedAStatus readLineFromConsole(std::string* out) {
+ FILE* f = fopen("/dev/console", "r");
+ if (f == nullptr) {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ "failed to open /dev/console");
+ }
+ char* line = nullptr;
+ size_t len = 0;
+ ssize_t nread = getline(&line, &len, f);
+
+ if (nread == -1) {
+ free(line);
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ "failed to read /dev/console");
+ }
+ out->append(line, nread);
+ free(line);
+ return ScopedAStatus::ok();
+ }
+
ScopedAStatus quit() override { exit(0); }
};
auto testService = ndk::SharedRefBase::make<TestService>();
diff --git a/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java b/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java
index 1f7ffb7..0ddf70b 100644
--- a/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java
+++ b/tests/vmshareapp/src/java/com/android/microdroid/test/sharevm/VmShareServiceImpl.java
@@ -245,6 +245,11 @@
}
@Override
+ public String readLineFromConsole() {
+ throw new UnsupportedOperationException("Not supported");
+ }
+
+ @Override
public void quit() throws RemoteException {
throw new UnsupportedOperationException("Not supported");
}
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 968d2d2..dd74d55 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -186,11 +186,18 @@
fn createVm(
&self,
config: &VirtualMachineConfig,
- console_fd: Option<&ParcelFileDescriptor>,
+ console_out_fd: Option<&ParcelFileDescriptor>,
+ console_in_fd: Option<&ParcelFileDescriptor>,
log_fd: Option<&ParcelFileDescriptor>,
) -> binder::Result<Strong<dyn IVirtualMachine>> {
let mut is_protected = false;
- let ret = self.create_vm_internal(config, console_fd, log_fd, &mut is_protected);
+ let ret = self.create_vm_internal(
+ config,
+ console_out_fd,
+ console_in_fd,
+ log_fd,
+ &mut is_protected,
+ );
write_vm_creation_stats(config, is_protected, &ret);
ret
}
@@ -303,7 +310,8 @@
fn create_vm_internal(
&self,
config: &VirtualMachineConfig,
- console_fd: Option<&ParcelFileDescriptor>,
+ console_out_fd: Option<&ParcelFileDescriptor>,
+ console_in_fd: Option<&ParcelFileDescriptor>,
log_fd: Option<&ParcelFileDescriptor>,
is_protected: &mut bool,
) -> binder::Result<Strong<dyn IVirtualMachine>> {
@@ -350,8 +358,9 @@
};
let state = &mut *self.state.lock().unwrap();
- let console_fd =
- clone_or_prepare_logger_fd(&debug_config, console_fd, format!("Console({})", cid))?;
+ let console_out_fd =
+ clone_or_prepare_logger_fd(&debug_config, console_out_fd, format!("Console({})", cid))?;
+ let console_in_fd = console_in_fd.map(clone_file).transpose()?;
let log_fd = clone_or_prepare_logger_fd(&debug_config, log_fd, format!("Log({})", cid))?;
// Counter to generate unique IDs for temporary image files.
@@ -455,7 +464,8 @@
cpus,
host_cpu_topology,
task_profiles: config.taskProfiles.clone(),
- console_fd,
+ console_out_fd,
+ console_in_fd,
log_fd,
ramdump,
indirect_files,
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 856ff1e..8c412f6 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -107,7 +107,8 @@
pub cpus: Option<NonZeroU32>,
pub host_cpu_topology: bool,
pub task_profiles: Vec<String>,
- pub console_fd: Option<File>,
+ pub console_out_fd: Option<File>,
+ pub console_in_fd: Option<File>,
pub log_fd: Option<File>,
pub ramdump: Option<File>,
pub indirect_files: Vec<File>,
@@ -526,7 +527,7 @@
}
}
Ok(VmResponse::Err(e)) => {
- // ENOTSUP is returned when the balloon protocol is not initialised. This
+ // ENOTSUP is returned when the balloon protocol is not initialized. This
// can occur for numerous reasons: Guest is still booting, guest doesn't
// support ballooning, host doesn't support ballooning. We don't log or
// raise an error in this case: trim is just a hint and we can ignore it.
@@ -776,21 +777,29 @@
//
// When [console|log]_fd is not specified, the devices are attached to sink, which means what's
// written there is discarded.
- let console_arg = format_serial_arg(&mut preserved_fds, &config.console_fd);
- let log_arg = format_serial_arg(&mut preserved_fds, &config.log_fd);
+ let console_out_arg = format_serial_out_arg(&mut preserved_fds, &config.console_out_fd);
+ let console_in_arg = config
+ .console_in_fd
+ .as_ref()
+ .map(|fd| format!(",input={}", add_preserved_fd(&mut preserved_fds, fd)))
+ .unwrap_or_default();
+ let log_arg = format_serial_out_arg(&mut preserved_fds, &config.log_fd);
let failure_serial_path = add_preserved_fd(&mut preserved_fds, &failure_pipe_write);
- let ramdump_arg = format_serial_arg(&mut preserved_fds, &config.ramdump);
+ let ramdump_arg = format_serial_out_arg(&mut preserved_fds, &config.ramdump);
// Warning: Adding more serial devices requires you to shift the PCI device ID of the boot
// disks in bootconfig.x86_64. This is because x86 crosvm puts serial devices and the block
// devices in the same PCI bus and serial devices comes before the block devices. Arm crosvm
// doesn't have the issue.
// /dev/ttyS0
- command.arg(format!("--serial={},hardware=serial,num=1", &console_arg));
+ command.arg(format!("--serial={},hardware=serial,num=1", &console_out_arg));
// /dev/ttyS1
command.arg(format!("--serial=type=file,path={},hardware=serial,num=2", &failure_serial_path));
// /dev/hvc0
- command.arg(format!("--serial={},hardware=virtio-console,num=1", &console_arg));
+ command.arg(format!(
+ "--serial={}{},hardware=virtio-console,num=1",
+ &console_out_arg, &console_in_arg
+ ));
// /dev/hvc1
command.arg(format!("--serial={},hardware=virtio-console,num=2", &ramdump_arg));
// /dev/hvc2
@@ -890,7 +899,7 @@
/// Adds the file descriptor for `file` (if any) to `preserved_fds`, and returns the appropriate
/// string for a crosvm `--serial` flag. If `file` is none, creates a dummy sink device.
-fn format_serial_arg(preserved_fds: &mut Vec<RawFd>, file: &Option<File>) -> String {
+fn format_serial_out_arg(preserved_fds: &mut Vec<RawFd>, file: &Option<File>) -> String {
if let Some(file) = file {
format!("type=file,path={}", add_preserved_fd(preserved_fds, file))
} else {
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
index d72d5ac..99bc076 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
@@ -23,12 +23,14 @@
interface IVirtualizationService {
/**
* Create the VM with the given config file, and return a handle to it ready to start it. If
- * `consoleFd` is provided then console output from the VM will be sent to it. If `osLogFd` is
+ * `consoleOutFd` is provided then console output from the VM will be sent to it. If
+ * `consoleInFd` is provided then console input to the VM will be read from it. If `osLogFd` is
* provided then the OS-level logs will be sent to it. `osLogFd` is supported only when the OS
* running in the VM has the logging system. In case of Microdroid, the logging system is logd.
*/
IVirtualMachine createVm(in VirtualMachineConfig config,
- in @nullable ParcelFileDescriptor consoleFd,
+ in @nullable ParcelFileDescriptor consoleOutFd,
+ in @nullable ParcelFileDescriptor consoleInFd,
in @nullable ParcelFileDescriptor osLogFd);
/**
diff --git a/virtualizationservice/src/rkpvm.rs b/virtualizationservice/src/rkpvm.rs
index a4649f6..ebfb667 100644
--- a/virtualizationservice/src/rkpvm.rs
+++ b/virtualizationservice/src/rkpvm.rs
@@ -79,7 +79,7 @@
taskProfiles: vec![],
gdbPort: 0, // No gdb
});
- let vm = VmInstance::create(service.as_ref(), &config, None, None, None)
+ let vm = VmInstance::create(service.as_ref(), &config, None, None, None, None)
.context("Failed to create service VM")?;
info!("service_vm: Starting Service VM...");
diff --git a/vm/src/main.rs b/vm/src/main.rs
index bc3f4da..0800f57 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -74,6 +74,10 @@
#[clap(long)]
console: Option<PathBuf>,
+ /// Path to file for VM console input.
+ #[clap(long)]
+ console_in: Option<PathBuf>,
+
/// Path to file for VM log output.
#[clap(long)]
log: Option<PathBuf>,
@@ -138,6 +142,10 @@
#[clap(long)]
console: Option<PathBuf>,
+ /// Path to file for VM console input.
+ #[clap(long)]
+ console_in: Option<PathBuf>,
+
/// Path to file for VM log output.
#[clap(long)]
log: Option<PathBuf>,
@@ -193,6 +201,10 @@
#[clap(long)]
console: Option<PathBuf>,
+ /// Path to file for VM console input.
+ #[clap(long)]
+ console_in: Option<PathBuf>,
+
/// Path to file for VM log output.
#[clap(long)]
log: Option<PathBuf>,
@@ -277,6 +289,7 @@
config_path,
payload_binary_name,
console,
+ console_in,
log,
debug,
protected,
@@ -297,6 +310,7 @@
config_path,
payload_binary_name,
console.as_deref(),
+ console_in.as_deref(),
log.as_deref(),
debug,
protected,
@@ -313,6 +327,7 @@
storage,
storage_size,
console,
+ console_in,
log,
debug,
protected,
@@ -328,6 +343,7 @@
storage.as_deref(),
storage_size,
console.as_deref(),
+ console_in.as_deref(),
log.as_deref(),
debug,
protected,
@@ -337,12 +353,13 @@
gdb,
kernel.as_deref(),
),
- Opt::Run { name, config, cpu_topology, task_profiles, console, log, gdb } => {
+ Opt::Run { name, config, cpu_topology, task_profiles, console, console_in, log, gdb } => {
command_run(
name,
get_service()?.as_ref(),
&config,
console.as_deref(),
+ console_in.as_deref(),
log.as_deref(),
/* mem */ None,
cpu_topology,
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 663fa25..84072ca 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -54,7 +54,8 @@
storage_size: Option<u64>,
config_path: Option<String>,
payload_binary_name: Option<String>,
- console_path: Option<&Path>,
+ console_out_path: Option<&Path>,
+ console_in_path: Option<&Path>,
log_path: Option<&Path>,
debug_level: DebugLevel,
protected: bool,
@@ -159,7 +160,7 @@
cpuTopology: cpu_topology,
customConfig: Some(custom_config),
});
- run(service, &config, &payload_config_str, console_path, log_path)
+ run(service, &config, &payload_config_str, console_out_path, console_in_path, log_path)
}
fn find_empty_payload_apk_path() -> Result<PathBuf, Error> {
@@ -192,7 +193,8 @@
work_dir: Option<PathBuf>,
storage: Option<&Path>,
storage_size: Option<u64>,
- console_path: Option<&Path>,
+ console_out_path: Option<&Path>,
+ console_in_path: Option<&Path>,
log_path: Option<&Path>,
debug_level: DebugLevel,
protected: bool,
@@ -223,7 +225,8 @@
storage_size,
/* config_path= */ None,
Some(payload_binary_name.to_owned()),
- console_path,
+ console_out_path,
+ console_in_path,
log_path,
debug_level,
protected,
@@ -242,7 +245,8 @@
name: Option<String>,
service: &dyn IVirtualizationService,
config_path: &Path,
- console_path: Option<&Path>,
+ console_out_path: Option<&Path>,
+ console_in_path: Option<&Path>,
log_path: Option<&Path>,
mem: Option<u32>,
cpu_topology: CpuTopology,
@@ -269,7 +273,8 @@
service,
&VirtualMachineConfig::RawConfig(config),
&format!("{:?}", config_path),
- console_path,
+ console_out_path,
+ console_in_path,
log_path,
)
}
@@ -290,33 +295,45 @@
service: &dyn IVirtualizationService,
config: &VirtualMachineConfig,
payload_config: &str,
- console_path: Option<&Path>,
+ console_out_path: Option<&Path>,
+ console_in_path: Option<&Path>,
log_path: Option<&Path>,
) -> Result<(), Error> {
- let console = if let Some(console_path) = console_path {
- Some(
- File::create(console_path)
- .with_context(|| format!("Failed to open console file {:?}", console_path))?,
- )
+ let console_out = if let Some(console_out_path) = console_out_path {
+ Some(File::create(console_out_path).with_context(|| {
+ format!("Failed to open console output file {:?}", console_out_path)
+ })?)
} else {
- Some(duplicate_stdout()?)
+ Some(duplicate_fd(io::stdout())?)
};
+ let console_in =
+ if let Some(console_in_path) = console_in_path {
+ Some(File::create(console_in_path).with_context(|| {
+ format!("Failed to open console input file {:?}", console_in_path)
+ })?)
+ } else {
+ Some(duplicate_fd(io::stdin())?)
+ };
let log = if let Some(log_path) = log_path {
Some(
File::create(log_path)
.with_context(|| format!("Failed to open log file {:?}", log_path))?,
)
} else {
- Some(duplicate_stdout()?)
+ Some(duplicate_fd(io::stdout())?)
};
-
let callback = Box::new(Callback {});
- let vm = VmInstance::create(service, config, console, log, Some(callback))
+ let vm = VmInstance::create(service, config, console_out, console_in, log, Some(callback))
.context("Failed to create VM")?;
vm.start().context("Failed to start VM")?;
+ let debug_level = match config {
+ VirtualMachineConfig::AppConfig(config) => config.debugLevel,
+ _ => DebugLevel::NONE,
+ };
println!(
- "Created VM from {} with CID {}, state is {}.",
+ "Created {} from {} with CID {}, state is {}.",
+ if debug_level == DebugLevel::FULL { "debuggable VM" } else { "VM" },
payload_config,
vm.cid(),
state_to_str(vm.state()?)
@@ -356,12 +373,12 @@
}
}
-/// Safely duplicate the standard output file descriptor.
-fn duplicate_stdout() -> io::Result<File> {
- let stdout_fd = io::stdout().as_raw_fd();
+/// Safely duplicate the file descriptor.
+fn duplicate_fd<T: AsRawFd>(file: T) -> io::Result<File> {
+ let fd = file.as_raw_fd();
// Safe because this just duplicates a file descriptor which we know to be valid, and we check
// for an error.
- let dup_fd = unsafe { libc::dup(stdout_fd) };
+ let dup_fd = unsafe { libc::dup(fd) };
if dup_fd < 0 {
Err(io::Error::last_os_error())
} else {
diff --git a/vmbase/Android.bp b/vmbase/Android.bp
index ac010b9..46f4937 100644
--- a/vmbase/Android.bp
+++ b/vmbase/Android.bp
@@ -2,11 +2,28 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// The hierarchy of Soong modules to produce a vmbase-based binary is
+//
+// 0. rlibs may be used to provide high-level code (see "vmbase_rlib_defaults");
+// 1. rust_ffi_static packages low-level Rust code and any rlib into a static
+// library (see "vmbase_ffi_defaults") that cc_binary supports;
+// 2. cc_library_static may be used for extra C code (see "vmbase_cc_defaults");
+// 3. cc_binary produces an ELF from the (single) Rust-wrapping static library,
+// optional extra C libraries, and linker script (see "vmbase_elf_defaults");
+// 4. raw_binary strips the ELF into an image that can be loaded to memory;
+
+// Used by intermediate rust_library_rlib for vmbase-based binaries.
rust_defaults {
- name: "vmbase_rust_defaults",
+ name: "vmbase_rlib_defaults",
edition: "2021",
+ prefer_rlib: true,
host_supported: false,
enabled: false,
+ no_stdlibs: true,
+ stdlibs: [
+ "libcompiler_builtins.rust_sysroot",
+ "libcore.rust_sysroot",
+ ],
target: {
android_arm64: {
enabled: true,
@@ -14,19 +31,17 @@
},
}
+// Used by the "top-level" rust_ffi_static of vmbase-based binaries.
rust_defaults {
name: "vmbase_ffi_defaults",
- defaults: ["vmbase_rust_defaults"],
- no_stdlibs: true,
- stdlibs: [
- "libcompiler_builtins.rust_sysroot",
- "libcore.rust_sysroot",
- ],
+ defaults: ["vmbase_rlib_defaults"],
}
+// Used by extra cc_library_static linked into the final ELF.
cc_defaults {
name: "vmbase_cc_defaults",
nocrt: true,
+ no_libcrt: true,
system_shared_libs: [],
stl: "none",
installable: false,
@@ -39,8 +54,10 @@
sanitize: {
hwaddress: false,
},
+ native_coverage: false,
}
+// Used by cc_binary when producing the ELF of a vmbase-based binary.
cc_defaults {
name: "vmbase_elf_defaults",
defaults: ["vmbase_cc_defaults"],
@@ -48,18 +65,11 @@
static_libs: [
"libvmbase_entry",
],
- installable: false,
- enabled: false,
- target: {
- android_arm64: {
- enabled: true,
- },
- },
}
rust_library_rlib {
name: "libvmbase",
- defaults: ["vmbase_rust_defaults"],
+ defaults: ["vmbase_rlib_defaults"],
crate_name: "vmbase",
srcs: ["src/lib.rs"],
rustlibs: [
@@ -76,14 +86,12 @@
"libvirtio_drivers",
"libzeroize_nostd",
],
- no_stdlibs: true,
whole_static_libs: [
"librust_baremetal",
],
features: [
"cpu_feat_hafdbs",
],
- apex_available: ["com.android.virt"],
}
cc_library_static {
@@ -94,8 +102,6 @@
"exceptions.S",
"exceptions_panic.S",
],
- no_libcrt: true,
- apex_available: ["com.android.virt"],
}
filegroup {
diff --git a/vmbase/README.md b/vmbase/README.md
index 7f621fb..280d7e1 100644
--- a/vmbase/README.md
+++ b/vmbase/README.md
@@ -6,7 +6,7 @@
In particular it provides:
-- An [entry point](entry.S) that initialises the MMU with a hard-coded identity mapping, enables the
+- An [entry point](entry.S) that initializes the MMU with a hard-coded identity mapping, enables the
cache, prepares the image and allocates a stack.
- An [exception vector](exceptions.S) to call your exception handlers.
- A UART driver and `println!` macro for early console logging.
@@ -62,7 +62,7 @@
}
```
-vmbase adds a wrapper around your main function to initialise the console driver first (with the
+vmbase adds a wrapper around your main function to initialize the console driver first (with the
UART at base address `0x3f8`, the first UART allocated by crosvm), and make a PSCI `SYSTEM_OFF` call
to shutdown the VM if your main function ever returns.
@@ -93,7 +93,7 @@
The `println!` macro shouldn't be used in exception handlers, because it relies on a global instance
of the UART driver which might be locked when the exception happens, which would result in deadlock.
-Instead you can use `emergency_write_str` and `eprintln!`, which will re-initialise the UART every
+Instead you can use `emergency_write_str` and `eprintln!`, which will re-initialize the UART every
time to ensure that it can be used. This should still be used with care, as it may interfere with
whatever the rest of the program is doing with the UART.
diff --git a/vmbase/example/Android.bp b/vmbase/example/Android.bp
index dc9a090..ae1a593 100644
--- a/vmbase/example/Android.bp
+++ b/vmbase/example/Android.bp
@@ -7,10 +7,8 @@
defaults: ["vmbase_ffi_defaults"],
crate_name: "vmbase_example",
srcs: ["src/main.rs"],
- edition: "2021",
rustlibs: [
"libaarch64_paging",
- "libbuddy_system_allocator",
"libdiced_open_dice_nostd",
"libfdtpci",
"liblibfdt",
diff --git a/vmbase/example/src/layout.rs b/vmbase/example/src/layout.rs
index 2e9d27a..f95958f 100644
--- a/vmbase/example/src/layout.rs
+++ b/vmbase/example/src/layout.rs
@@ -48,7 +48,7 @@
into_va_range(layout::data_range())
}
-/// Zero-initialised writable data.
+/// Zero-initialized writable data.
pub fn bss_range() -> Range<VirtualAddress> {
into_va_range(layout::bss_range())
}
diff --git a/vmbase/example/src/main.rs b/vmbase/example/src/main.rs
index 1dd8517..b3b5732 100644
--- a/vmbase/example/src/main.rs
+++ b/vmbase/example/src/main.rs
@@ -30,11 +30,10 @@
use crate::pci::{check_pci, get_bar_region};
use aarch64_paging::{idmap::IdMap, paging::Attributes};
use alloc::{vec, vec::Vec};
-use buddy_system_allocator::LockedHeap;
use fdtpci::PciInfo;
use libfdt::Fdt;
use log::{debug, error, info, trace, warn, LevelFilter};
-use vmbase::{cstr, logger, main};
+use vmbase::{configure_heap, cstr, logger, main, memory::SIZE_64KB};
static INITIALISED_DATA: [u32; 4] = [1, 2, 3, 4];
static mut ZEROED_DATA: [u32; 10] = [0; 10];
@@ -43,12 +42,8 @@
const ASID: usize = 1;
const ROOT_LEVEL: usize = 1;
-#[global_allocator]
-static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new();
-
-static mut HEAP: [u8; 65536] = [0; 65536];
-
main!(main);
+configure_heap!(SIZE_64KB);
/// Entry point for VM bootloader.
pub fn main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) {
@@ -74,10 +69,6 @@
modify_fdt(fdt);
- unsafe {
- HEAP_ALLOCATOR.lock().init(HEAP.as_mut_ptr() as usize, HEAP.len());
- }
-
check_alloc();
let mut idmap = IdMap::new(ASID, ROOT_LEVEL);
@@ -164,7 +155,6 @@
unsafe {
info!("ZEROED_DATA: {:?}", ZEROED_DATA.as_ptr());
info!("MUTABLE_DATA: {:?}", MUTABLE_DATA.as_ptr());
- info!("HEAP: {:?}", HEAP.as_ptr());
}
assert_eq!(INITIALISED_DATA[0], 1);
diff --git a/vmbase/example/tests/test.rs b/vmbase/example/tests/test.rs
index 085a620..3594523 100644
--- a/vmbase/example/tests/test.rs
+++ b/vmbase/example/tests/test.rs
@@ -106,8 +106,15 @@
});
let (handle, console) = android_log_fd()?;
let (mut log_reader, log_writer) = pipe()?;
- let vm = VmInstance::create(service.as_ref(), &config, Some(console), Some(log_writer), None)
- .context("Failed to create VM")?;
+ let vm = VmInstance::create(
+ service.as_ref(),
+ &config,
+ Some(console),
+ /* consoleIn */ None,
+ Some(log_writer),
+ None,
+ )
+ .context("Failed to create VM")?;
vm.start().context("Failed to start VM")?;
info!("Started example VM.");
diff --git a/vmbase/src/console.rs b/vmbase/src/console.rs
index 7c8ddf6..e9298cc 100644
--- a/vmbase/src/console.rs
+++ b/vmbase/src/console.rs
@@ -51,7 +51,7 @@
write(CONSOLE.lock().as_mut().unwrap(), format_args).unwrap();
}
-/// Reinitialises the UART driver and writes a string to it.
+/// Reinitializes the UART driver and writes a string to it.
///
/// This is intended for use in situations where the UART may be in an unknown state or the global
/// instance may be locked, such as in an exception handler or panic handler.
@@ -60,7 +60,7 @@
let _ = uart.write_str(s);
}
-/// Reinitialises the UART driver and writes a formatted string to it.
+/// Reinitializes the UART driver and writes a formatted string to it.
///
/// This is intended for use in situations where the UART may be in an unknown state or the global
/// instance may be locked, such as in an exception handler or panic handler.
@@ -71,7 +71,7 @@
/// Prints the given formatted string to the console, followed by a newline.
///
-/// Panics if the console has not yet been initialised. May hang if used in an exception context;
+/// Panics if the console has not yet been initialized. May hang if used in an exception context;
/// use `eprintln!` instead.
macro_rules! println {
() => ($crate::console::write_str("\n"));
diff --git a/vmbase/src/entry.rs b/vmbase/src/entry.rs
index 8cdfe77..df0bb7c 100644
--- a/vmbase/src/entry.rs
+++ b/vmbase/src/entry.rs
@@ -14,11 +14,13 @@
//! Rust entry point.
-use crate::{console, power::shutdown};
+use crate::{console, heap, power::shutdown};
/// This is the entry point to the Rust code, called from the binary entry point in `entry.S`.
#[no_mangle]
extern "C" fn rust_entry(x0: u64, x1: u64, x2: u64, x3: u64) -> ! {
+ // SAFETY - Only called once, from here, and inaccessible to client code.
+ unsafe { heap::init() };
console::init();
unsafe {
main(x0, x1, x2, x3);
diff --git a/pvmfw/src/heap.rs b/vmbase/src/heap.rs
similarity index 97%
rename from pvmfw/src/heap.rs
rename to vmbase/src/heap.rs
index a28a02c..b00ca6f 100644
--- a/pvmfw/src/heap.rs
+++ b/vmbase/src/heap.rs
@@ -29,7 +29,7 @@
/// Configures the size of the global allocator.
#[macro_export]
-macro_rules! configure_global_allocator_size {
+macro_rules! configure_heap {
($len:expr) => {
static mut __HEAP_ARRAY: [u8; $len] = [0; $len];
#[export_name = "HEAP"]
@@ -39,7 +39,7 @@
}
extern "Rust" {
- /// Slice used by the global allocator, configured using configure_global_allocator_size!().
+ /// Slice used by the global allocator, configured using configure_heap!().
static mut HEAP: &'static mut [u8];
}
@@ -51,7 +51,7 @@
/// # Safety
///
/// Must be called no more than once.
-pub unsafe fn init() {
+pub(crate) unsafe fn init() {
// SAFETY: Nothing else accesses this memory, and we hand it over to the heap to manage and
// never touch it again. The heap is locked, so there cannot be any races.
let (start, size) = unsafe { (HEAP.as_mut_ptr() as usize, HEAP.len()) };
diff --git a/vmbase/src/layout/mod.rs b/vmbase/src/layout/mod.rs
index 21c113a..f67e518 100644
--- a/vmbase/src/layout/mod.rs
+++ b/vmbase/src/layout/mod.rs
@@ -62,7 +62,7 @@
linker_region!(data_begin, data_end)
}
-/// Zero-initialised writable data.
+/// Zero-initialized writable data.
pub fn bss_range() -> Range<usize> {
linker_region!(bss_begin, bss_end)
}
diff --git a/vmbase/src/lib.rs b/vmbase/src/lib.rs
index 7fc7b20..88bad8b 100644
--- a/vmbase/src/lib.rs
+++ b/vmbase/src/lib.rs
@@ -23,6 +23,7 @@
pub mod console;
mod entry;
pub mod fdt;
+pub mod heap;
mod hvc;
pub mod layout;
mod linker;
diff --git a/vmbase/src/memory/mod.rs b/vmbase/src/memory/mod.rs
index 6bc600d..5e78565 100644
--- a/vmbase/src/memory/mod.rs
+++ b/vmbase/src/memory/mod.rs
@@ -25,5 +25,5 @@
pub use shared::{alloc_shared, dealloc_shared, MemoryRange, MemoryTracker, MEMORY};
pub use util::{
flush, flushed_zeroize, min_dcache_line_size, page_4kb_of, phys_to_virt, virt_to_phys,
- PAGE_SIZE, SIZE_128KB, SIZE_2MB, SIZE_4KB, SIZE_4MB,
+ PAGE_SIZE, SIZE_128KB, SIZE_2MB, SIZE_4KB, SIZE_4MB, SIZE_64KB,
};
diff --git a/vmbase/src/memory/util.rs b/vmbase/src/memory/util.rs
index 48007f3..b9ef5c9 100644
--- a/vmbase/src/memory/util.rs
+++ b/vmbase/src/memory/util.rs
@@ -22,6 +22,8 @@
/// The size of a 4KB memory in bytes.
pub const SIZE_4KB: usize = 4 << 10;
+/// The size of a 64KB memory in bytes.
+pub const SIZE_64KB: usize = 64 << 10;
/// The size of a 128KB memory in bytes.
pub const SIZE_128KB: usize = 128 << 10;
/// The size of a 2MB memory in bytes.
diff --git a/vmbase/src/virtio/hal.rs b/vmbase/src/virtio/hal.rs
index 36f9e56..c84ca5e 100644
--- a/vmbase/src/virtio/hal.rs
+++ b/vmbase/src/virtio/hal.rs
@@ -68,7 +68,7 @@
/// range. It can't alias any other allocations because we previously validated in
/// `map_mmio_range` that the PCI MMIO range didn't overlap with any other memory ranges.
unsafe 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");
+ let pci_info = PCI_INFO.get().expect("VirtIO HAL used before PCI_INFO was initialized");
let bar_range = {
let start = pci_info.bar_range.start.try_into().unwrap();
let end = pci_info.bar_range.end.try_into().unwrap();
diff --git a/vmbase/src/virtio/pci.rs b/vmbase/src/virtio/pci.rs
index cbb4d26..534d91a 100644
--- a/vmbase/src/virtio/pci.rs
+++ b/vmbase/src/virtio/pci.rs
@@ -63,7 +63,7 @@
/// 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, PciError> {
+pub fn initialize(pci_info: PciInfo, memory: &mut MemoryTracker) -> Result<PciRoot, PciError> {
PCI_INFO.set(Box::new(pci_info.clone())).map_err(|_| PciError::DuplicateInitialization)?;
memory.map_mmio_range(pci_info.cam_range.clone()).map_err(PciError::CamMapFailed)?;
diff --git a/vmclient/src/lib.rs b/vmclient/src/lib.rs
index 8f25b99..cfd015a 100644
--- a/vmclient/src/lib.rs
+++ b/vmclient/src/lib.rs
@@ -175,14 +175,17 @@
pub fn create(
service: &dyn IVirtualizationService,
config: &VirtualMachineConfig,
- console: Option<File>,
+ console_out: Option<File>,
+ console_in: Option<File>,
log: Option<File>,
callback: Option<Box<dyn VmCallback + Send + Sync>>,
) -> BinderResult<Self> {
- let console = console.map(ParcelFileDescriptor::new);
+ let console_out = console_out.map(ParcelFileDescriptor::new);
+ let console_in = console_in.map(ParcelFileDescriptor::new);
let log = log.map(ParcelFileDescriptor::new);
- let vm = service.createVm(config, console.as_ref(), log.as_ref())?;
+ let vm =
+ service.createVm(config, console_out.as_ref(), console_in.as_ref(), log.as_ref())?;
let cid = vm.getCid()?;