Merge "Remove unneccessary unsafe code in logger."
diff --git a/authfs/Android.bp b/authfs/Android.bp
index 2532026..154a1d6 100644
--- a/authfs/Android.bp
+++ b/authfs/Android.bp
@@ -23,7 +23,7 @@
"liblog_rust",
"libnix",
"libopenssl",
- "libprotobuf_deprecated",
+ "libprotobuf",
"librpcbinder_rs",
"libthiserror",
],
diff --git a/compos/Android.bp b/compos/Android.bp
index c120b0f..2f6be98 100644
--- a/compos/Android.bp
+++ b/compos/Android.bp
@@ -18,7 +18,7 @@
"libminijail_rust",
"libnix",
"libodsign_proto_rust",
- "libprotobuf_deprecated",
+ "libprotobuf",
"libregex",
"librpcbinder_rs",
"librustutils",
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/compos/composd/Android.bp b/compos/composd/Android.bp
index f66de32..b0294dd 100644
--- a/compos/composd/Android.bp
+++ b/compos/composd/Android.bp
@@ -22,7 +22,7 @@
"liblibc",
"liblog_rust",
"libodsign_proto_rust",
- "libprotobuf_deprecated",
+ "libprotobuf",
"librustutils",
"libshared_child",
"libvmclient",
diff --git a/compos/src/artifact_signer.rs b/compos/src/artifact_signer.rs
index d3843fc..908e438 100644
--- a/compos/src/artifact_signer.rs
+++ b/compos/src/artifact_signer.rs
@@ -63,7 +63,7 @@
/// with accompanying sigature file.
pub fn write_info_and_signature(self, info_path: &Path) -> Result<()> {
let mut info = OdsignInfo::new();
- info.mut_file_hashes().extend(self.file_digests.into_iter());
+ info.file_hashes.extend(self.file_digests.into_iter());
let bytes = info.write_to_bytes()?;
let signature = compos_key::sign(&bytes)?;
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/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/microdroid/payload/Android.bp b/microdroid/payload/Android.bp
index 4814a64..8225875 100644
--- a/microdroid/payload/Android.bp
+++ b/microdroid/payload/Android.bp
@@ -31,6 +31,7 @@
protos: ["metadata.proto"],
source_stem: "microdroid_metadata",
host_supported: true,
+ use_protobuf3: true,
apex_available: [
"com.android.virt",
],
diff --git a/microdroid/payload/metadata/Android.bp b/microdroid/payload/metadata/Android.bp
index e3138e8..cd182fc 100644
--- a/microdroid/payload/metadata/Android.bp
+++ b/microdroid/payload/metadata/Android.bp
@@ -12,7 +12,7 @@
rustlibs: [
"libanyhow",
"libmicrodroid_metadata_proto_rust",
- "libprotobuf_deprecated",
+ "libprotobuf",
],
apex_available: [
"com.android.virt",
diff --git a/microdroid/payload/metadata/src/lib.rs b/microdroid/payload/metadata/src/lib.rs
index bfbec60..f00391a 100644
--- a/microdroid/payload/metadata/src/lib.rs
+++ b/microdroid/payload/metadata/src/lib.rs
@@ -24,7 +24,7 @@
use std::io::Write;
pub use microdroid_metadata::metadata::{
- ApexPayload, ApkPayload, Metadata, Metadata_oneof_payload as PayloadMetadata, PayloadConfig,
+ metadata::Payload as PayloadMetadata, ApexPayload, ApkPayload, Metadata, PayloadConfig,
};
/// Reads a metadata from a reader
diff --git a/microdroid_manager/src/dice.rs b/microdroid_manager/src/dice.rs
index 3a2a1e6..bacefcd 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -170,21 +170,23 @@
/// PayloadConfig = {
/// 1: tstr // payload_binary_name
/// }
-pub fn format_payload_config_descriptor(payload_metadata: &PayloadMetadata) -> Result<Vec<u8>> {
+pub fn format_payload_config_descriptor(payload: &PayloadMetadata) -> Result<Vec<u8>> {
const MICRODROID_PAYLOAD_COMPONENT_NAME: &str = "Microdroid payload";
- let config_descriptor_cbor_value = match payload_metadata {
- PayloadMetadata::config_path(payload_config_path) => cbor!({
+ let config_descriptor_cbor_value = match payload {
+ PayloadMetadata::ConfigPath(payload_config_path) => cbor!({
-70002 => MICRODROID_PAYLOAD_COMPONENT_NAME,
-71000 => payload_config_path
}),
- PayloadMetadata::config(payload_config) => cbor!({
+ PayloadMetadata::Config(payload_config) => cbor!({
-70002 => MICRODROID_PAYLOAD_COMPONENT_NAME,
-71001 => {1 => payload_config.payload_binary_name}
}),
+ _ => bail!("Failed to match the payload against a config type: {:?}", payload),
}
.context("Failed to build a CBOR Value from payload metadata")?;
let mut config_descriptor = Vec::new();
+
ser::into_writer(&config_descriptor_cbor_value, &mut config_descriptor)?;
Ok(config_descriptor)
}
@@ -196,7 +198,7 @@
#[test]
fn payload_metadata_with_path_formats_correctly() -> Result<()> {
- let payload_metadata = PayloadMetadata::config_path("/config_path".to_string());
+ let payload_metadata = PayloadMetadata::ConfigPath("/config_path".to_string());
let config_descriptor = format_payload_config_descriptor(&payload_metadata)?;
static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
@@ -214,7 +216,7 @@
payload_binary_name: "payload_binary".to_string(),
..Default::default()
};
- let payload_metadata = PayloadMetadata::config(payload_config);
+ let payload_metadata = PayloadMetadata::Config(payload_config);
let config_descriptor = format_payload_config_descriptor(&payload_metadata)?;
static EXPECTED_CONFIG_DESCRIPTOR: &[u8] = &[
0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72,
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 9c19feb..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()
@@ -435,8 +435,9 @@
// Restricted APIs are only allowed to be used by platform or test components. Infer this from
// the use of a VM config file since those can only be used by platform and test components.
let allow_restricted_apis = match payload_metadata {
- PayloadMetadata::config_path(_) => true,
- PayloadMetadata::config(_) => false,
+ PayloadMetadata::ConfigPath(_) => true,
+ PayloadMetadata::Config(_) => false,
+ _ => false, // default is false for safety
};
let config = load_config(payload_metadata).context("Failed to load payload metadata")?;
@@ -792,14 +793,14 @@
fn load_config(payload_metadata: PayloadMetadata) -> Result<VmPayloadConfig> {
match payload_metadata {
- PayloadMetadata::config_path(path) => {
+ PayloadMetadata::ConfigPath(path) => {
let path = Path::new(&path);
info!("loading config from {:?}...", path);
let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)
.with_context(|| format!("Failed to read {:?}", path))?;
Ok(serde_json::from_reader(file)?)
}
- PayloadMetadata::config(payload_config) => {
+ PayloadMetadata::Config(payload_config) => {
let task = Task {
type_: TaskType::MicrodroidLauncher,
command: payload_config.payload_binary_name,
@@ -814,6 +815,7 @@
enable_authfs: false,
})
}
+ _ => bail!("Failed to match config against a config type."),
}
}
diff --git a/pvmfw/src/exceptions.rs b/pvmfw/src/exceptions.rs
index c3f8a29..797138c 100644
--- a/pvmfw/src/exceptions.rs
+++ b/pvmfw/src/exceptions.rs
@@ -114,9 +114,22 @@
}
}
+/// Prints the details of an exception failure, excluding UART exceptions.
#[inline]
-fn handling_uart_exception(esr: Esr, far: usize) -> bool {
- esr == Esr::DataAbortSyncExternalAbort && page_4kb_of(far) == UART_PAGE
+fn print_exception_failure(
+ esr: Esr,
+ far: usize,
+ elr: u64,
+ e: HandleExceptionError,
+ exception_name: &str,
+) {
+ let is_uart_exception = esr == Esr::DataAbortSyncExternalAbort && page_4kb_of(far) == UART_PAGE;
+ // Don't print to the UART if we are handling an exception it could raise.
+ if !is_uart_exception {
+ eprintln!("{exception_name}");
+ eprintln!("{e}");
+ eprintln!("{esr}, far={far:#08x}, elr={elr:#08x}");
+ }
}
#[no_mangle]
@@ -127,12 +140,7 @@
let far = read_sysreg!("far_el1");
if let Err(e) = handle_exception(esr, far) {
- // Don't print to the UART if we are handling an exception it could raise.
- if !handling_uart_exception(esr, far) {
- eprintln!("sync_exception_current");
- eprintln!("{e}");
- eprintln!("{esr}, far={far:#08x}, elr={elr:#08x}");
- }
+ print_exception_failure(esr, far, elr, e, "sync_exception_current");
reboot()
}
}
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/gpt.rs b/pvmfw/src/gpt.rs
index b553705..892850c 100644
--- a/pvmfw/src/gpt.rs
+++ b/pvmfw/src/gpt.rs
@@ -24,9 +24,11 @@
use uuid::Uuid;
use virtio_drivers::device::blk::SECTOR_SIZE;
use vmbase::util::ceiling_div;
-use vmbase::virtio::pci::VirtIOBlk;
+use vmbase::virtio::{pci, HalImpl};
use zerocopy::FromBytes;
+type VirtIOBlk = pci::VirtIOBlk<HalImpl>;
+
pub enum Error {
/// VirtIO error during read operation.
FailedRead(virtio_drivers::Error),
diff --git a/pvmfw/src/instance.rs b/pvmfw/src/instance.rs
index 1035559..f2b34da 100644
--- a/pvmfw/src/instance.rs
+++ b/pvmfw/src/instance.rs
@@ -32,6 +32,7 @@
use vmbase::rand;
use vmbase::util::ceiling_div;
use vmbase::virtio::pci::{PciTransportIterator, VirtIOBlk};
+use vmbase::virtio::HalImpl;
use zerocopy::AsBytes;
use zerocopy::FromBytes;
@@ -183,10 +184,11 @@
}
fn find_instance_img(pci_root: &mut PciRoot) -> Result<Partition> {
- for transport in
- PciTransportIterator::new(pci_root).filter(|t| DeviceType::Block == t.device_type())
+ for transport in PciTransportIterator::<HalImpl>::new(pci_root)
+ .filter(|t| DeviceType::Block == t.device_type())
{
- let device = VirtIOBlk::new(transport).map_err(Error::VirtIOBlkCreationFailed)?;
+ let device =
+ VirtIOBlk::<HalImpl>::new(transport).map_err(Error::VirtIOBlkCreationFailed)?;
match Partition::get_by_name(device, "vm-instance") {
Ok(Some(p)) => return Ok(p),
Ok(None) => {}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 61e2312..ba453e7 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -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
})?;
diff --git a/rialto/Android.bp b/rialto/Android.bp
index 9aa4667..1840278 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -13,6 +13,7 @@
"libfdtpci",
"liblibfdt",
"liblog_rust_nostd",
+ "libvirtio_drivers",
"libvmbase",
],
}
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 ce83624..61c985e 100644
--- a/rialto/src/main.rs
+++ b/rialto/src/main.rs
@@ -37,12 +37,12 @@
main,
memory::{MemoryTracker, PageTable, MEMORY, PAGE_SIZE, SIZE_64KB},
power::reboot,
+ virtio::pci,
};
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())?;
@@ -91,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| {
@@ -116,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(())
}
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..7a38062 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -548,6 +548,14 @@
.setVmOutputCaptured(true);
e = assertThrows(IllegalStateException.class, () -> captureOutputOnNonDebuggable.build());
assertThat(e).hasMessageThat().contains("debug level must be FULL to capture output");
+
+ VirtualMachineConfig.Builder captureInputOnNonDebuggable =
+ newVmConfigBuilder()
+ .setPayloadBinaryName("binary.so")
+ .setDebugLevel(VirtualMachineConfig.DEBUG_LEVEL_NONE)
+ .setVmConsoleInputSupported(true);
+ e = assertThrows(IllegalStateException.class, () -> captureInputOnNonDebuggable.build());
+ assertThat(e).hasMessageThat().contains("debug level must be FULL to use console input");
}
@Test
@@ -586,6 +594,9 @@
newBaselineBuilder().setDebugLevel(DEBUG_LEVEL_FULL);
VirtualMachineConfig debuggable = debuggableBuilder.build();
assertConfigCompatible(debuggable, debuggableBuilder.setVmOutputCaptured(true)).isFalse();
+ assertConfigCompatible(debuggable, debuggableBuilder.setVmOutputCaptured(false)).isTrue();
+ assertConfigCompatible(debuggable, debuggableBuilder.setVmConsoleInputSupported(true))
+ .isFalse();
VirtualMachineConfig currentContextConfig =
new VirtualMachineConfig.Builder(getContext())
@@ -1575,6 +1586,7 @@
.setProtectedVm(mProtectedVm)
.setPayloadBinaryName("MicrodroidTestNativeLib.so")
.setDebugLevel(DEBUG_LEVEL_FULL)
+ .setVmConsoleInputSupported(true) // even if console input is supported
.build();
final VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_forward_log", vmConfig);
vm.run();
@@ -1589,6 +1601,28 @@
}
}
+ @Test
+ public void inputShouldBeExplicitlyAllowed() throws Exception {
+ assumeSupportedDevice();
+
+ final VirtualMachineConfig vmConfig =
+ new VirtualMachineConfig.Builder(getContext())
+ .setProtectedVm(mProtectedVm)
+ .setPayloadBinaryName("MicrodroidTestNativeLib.so")
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .setVmOutputCaptured(true) // even if output is captured
+ .build();
+ final VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_forward_log", vmConfig);
+ vm.run();
+
+ try {
+ assertThrowsVmExceptionContaining(
+ () -> vm.getConsoleInput(), "VM console input is not supported");
+ } finally {
+ vm.stop();
+ }
+ }
+
private boolean checkVmOutputIsRedirectedToLogcat(boolean debuggable) throws Exception {
String time =
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"));
@@ -1652,6 +1686,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/virtualizationmanager/src/payload.rs b/virtualizationmanager/src/payload.rs
index 33659d4..733add6 100644
--- a/virtualizationmanager/src/payload.rs
+++ b/virtualizationmanager/src/payload.rs
@@ -194,12 +194,12 @@
temporary_directory: &Path,
) -> Result<ParcelFileDescriptor> {
let payload_metadata = match &app_config.payload {
- Payload::PayloadConfig(payload_config) => PayloadMetadata::config(PayloadConfig {
+ Payload::PayloadConfig(payload_config) => PayloadMetadata::Config(PayloadConfig {
payload_binary_name: payload_config.payloadBinaryName.clone(),
..Default::default()
}),
Payload::ConfigPath(config_path) => {
- PayloadMetadata::config_path(format!("/mnt/apk/{}", config_path))
+ PayloadMetadata::ConfigPath(format!("/mnt/apk/{}", config_path))
}
};
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 392fa1c..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,28 +295,35 @@
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")?;
@@ -361,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/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/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/pci.rs b/vmbase/example/src/pci.rs
index 6abe66e..7188cde 100644
--- a/vmbase/example/src/pci.rs
+++ b/vmbase/example/src/pci.rs
@@ -20,13 +20,14 @@
use fdtpci::PciInfo;
use log::{debug, info};
use virtio_drivers::{
- device::{blk::VirtIOBlk, console::VirtIOConsole},
+ device::console::VirtIOConsole,
transport::{
- pci::{bus::PciRoot, virtio_device_type, PciTransport},
+ pci::{bus::PciRoot, PciTransport},
DeviceType, Transport,
},
BufferDirection, Error, Hal, PhysAddr, PAGE_SIZE,
};
+use vmbase::virtio::pci::{self, PciTransportIterator};
/// The standard sector size of a VirtIO block device, in bytes.
const SECTOR_SIZE_BYTES: usize = 512;
@@ -37,29 +38,23 @@
pub fn check_pci(pci_root: &mut PciRoot) {
let mut checked_virtio_device_count = 0;
let mut block_device_count = 0;
- for (device_function, info) in pci_root.enumerate_bus(0) {
- let (status, command) = pci_root.get_status_command(device_function);
- info!("Found {} at {}, status {:?} command {:?}", info, device_function, status, command);
- if let Some(virtio_type) = virtio_device_type(&info) {
- info!(" VirtIO {:?}", virtio_type);
- let mut transport = PciTransport::new::<HalImpl>(pci_root, device_function).unwrap();
- info!(
- "Detected virtio PCI device with device type {:?}, features {:#018x}",
- transport.device_type(),
- transport.read_device_features(),
- );
- match virtio_type {
- DeviceType::Block => {
- check_virtio_block_device(transport, block_device_count);
- block_device_count += 1;
- checked_virtio_device_count += 1;
- }
- DeviceType::Console => {
- check_virtio_console_device(transport);
- checked_virtio_device_count += 1;
- }
- _ => {}
+ for mut transport in PciTransportIterator::<HalImpl>::new(pci_root) {
+ info!(
+ "Detected virtio PCI device with device type {:?}, features {:#018x}",
+ transport.device_type(),
+ transport.read_device_features(),
+ );
+ match transport.device_type() {
+ DeviceType::Block => {
+ check_virtio_block_device(transport, block_device_count);
+ block_device_count += 1;
+ checked_virtio_device_count += 1;
}
+ DeviceType::Console => {
+ check_virtio_console_device(transport);
+ checked_virtio_device_count += 1;
+ }
+ _ => {}
}
}
@@ -68,8 +63,8 @@
}
/// Checks the given VirtIO block device.
-fn check_virtio_block_device(transport: impl Transport, index: usize) {
- let mut blk = VirtIOBlk::<HalImpl, _>::new(transport).expect("failed to create blk driver");
+fn check_virtio_block_device(transport: PciTransport, index: usize) {
+ let mut blk = pci::VirtIOBlk::<HalImpl>::new(transport).expect("failed to create blk driver");
info!("Found {} KiB block device.", blk.capacity() * SECTOR_SIZE_BYTES as u64 / 1024);
match index {
0 => {
@@ -94,8 +89,8 @@
}
/// Checks the given VirtIO console device.
-fn check_virtio_console_device(transport: impl Transport) {
- let mut console = VirtIOConsole::<HalImpl, _>::new(transport)
+fn check_virtio_console_device(transport: PciTransport) {
+ let mut console = VirtIOConsole::<HalImpl, PciTransport>::new(transport)
.expect("Failed to create VirtIO console driver");
info!("Found console device: {:?}", console.info());
for &c in b"Hello VirtIO console\n" {
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/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/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/mod.rs b/vmbase/src/virtio/mod.rs
index df916bc..fbe41e3 100644
--- a/vmbase/src/virtio/mod.rs
+++ b/vmbase/src/virtio/mod.rs
@@ -16,3 +16,5 @@
mod hal;
pub mod pci;
+
+pub use hal::HalImpl;
diff --git a/vmbase/src/virtio/pci.rs b/vmbase/src/virtio/pci.rs
index cbb4d26..a75f0e2 100644
--- a/vmbase/src/virtio/pci.rs
+++ b/vmbase/src/virtio/pci.rs
@@ -14,10 +14,10 @@
//! Functions to scan the PCI bus for VirtIO devices.
-use super::hal::HalImpl;
use crate::memory::{MemoryTracker, MemoryTrackerError};
use alloc::boxed::Box;
use core::fmt;
+use core::marker::PhantomData;
use fdtpci::PciInfo;
use log::debug;
use once_cell::race::OnceBox;
@@ -27,6 +27,7 @@
bus::{BusDeviceIterator, PciRoot},
virtio_device_type, PciTransport,
},
+ Hal,
};
pub(super) static PCI_INFO: OnceBox<PciInfo> = OnceBox::new();
@@ -63,7 +64,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)?;
@@ -76,23 +77,24 @@
}
/// Virtio Block device.
-pub type VirtIOBlk = blk::VirtIOBlk<HalImpl, PciTransport>;
+pub type VirtIOBlk<T> = blk::VirtIOBlk<T, PciTransport>;
/// An iterator that iterates over the PCI transport for each device.
-pub struct PciTransportIterator<'a> {
+pub struct PciTransportIterator<'a, T: Hal> {
pci_root: &'a mut PciRoot,
bus: BusDeviceIterator,
+ _hal: PhantomData<T>,
}
-impl<'a> PciTransportIterator<'a> {
+impl<'a, T: Hal> PciTransportIterator<'a, T> {
/// Creates a new iterator.
pub fn new(pci_root: &'a mut PciRoot) -> Self {
let bus = pci_root.enumerate_bus(0);
- Self { pci_root, bus }
+ Self { pci_root, bus, _hal: PhantomData }
}
}
-impl<'a> Iterator for PciTransportIterator<'a> {
+impl<'a, T: Hal> Iterator for PciTransportIterator<'a, T> {
type Item = PciTransport;
fn next(&mut self) -> Option<Self::Item> {
@@ -109,7 +111,7 @@
};
debug!(" VirtIO {:?}", virtio_type);
- return PciTransport::new::<HalImpl>(self.pci_root, device_function).ok();
+ return PciTransport::new::<T>(self.pci_root, device_function).ok();
}
}
}
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()?;