Merge "Rename odsign_e2e_tests to odsign_e2e_tests_full"
diff --git a/authfs/src/file/remote_file.rs b/authfs/src/file/remote_file.rs
index 039285f..4c112bd 100644
--- a/authfs/src/file/remote_file.rs
+++ b/authfs/src/file/remote_file.rs
@@ -40,7 +40,6 @@
}
pub struct RemoteFileReader {
- // This needs to have Sync trait to be used in fuse::worker::start_message_loop.
service: VirtFdService,
file_fd: i32,
}
@@ -81,7 +80,6 @@
}
pub struct RemoteMerkleTreeReader {
- // This needs to be a Sync to be used in fuse::worker::start_message_loop.
service: VirtFdService,
file_fd: i32,
}
@@ -108,7 +106,6 @@
}
pub struct RemoteFileEditor {
- // This needs to have Sync trait to be used in fuse::worker::start_message_loop.
service: VirtFdService,
file_fd: i32,
}
diff --git a/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 511db68..beb6b30 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -184,10 +184,9 @@
type DirHandleTable = BTreeMap<Handle, Arc<DirEntriesSnapshot>>;
-// AuthFS needs to be `Sync` to be accepted by fuse::worker::start_message_loop as a `FileSystem`.
+// AuthFS needs to be `Sync` to be used with the `fuse` crate.
pub struct AuthFs {
- /// Table for `Inode` to `InodeState` lookup. This needs to be `Sync` to be used in
- /// `fuse::worker::start_message_loop`.
+ /// Table for `Inode` to `InodeState` lookup.
inode_table: RwLock<BTreeMap<Inode, InodeState>>,
/// The next available inode number.
diff --git a/authfs/src/fusefs/mount.rs b/authfs/src/fusefs/mount.rs
index 294c6b1..38503df 100644
--- a/authfs/src/fusefs/mount.rs
+++ b/authfs/src/fusefs/mount.rs
@@ -21,11 +21,12 @@
use super::AuthFs;
-/// Maximum bytes in the write transaction to the FUSE device. This limits the maximum buffer
-/// size in a read request (including FUSE protocol overhead) that the filesystem writes to.
+/// Maximum bytes (excluding the FUSE header) `AuthFs` will receive from the kernel for write
+/// operations by another process.
pub const MAX_WRITE_BYTES: u32 = 65536;
-/// Maximum bytes in a read operation.
+/// Maximum bytes (excluding the FUSE header) `AuthFs` will receive from the kernel for read
+/// operations by another process.
/// TODO(victorhsieh): This option is deprecated by FUSE. Figure out if we can remove this.
const MAX_READ_BYTES: u32 = 65536;
@@ -61,5 +62,7 @@
)
.expect("Failed to mount fuse");
- fuse::worker::start_message_loop(dev_fuse, MAX_WRITE_BYTES, MAX_READ_BYTES, authfs)
+ let mut config = fuse::FuseConfig::new();
+ config.dev_fuse(dev_fuse).max_write(MAX_WRITE_BYTES).max_read(MAX_READ_BYTES);
+ config.enter_message_loop(authfs)
}
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 9d7e4f9..60f8fd4 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -77,6 +77,7 @@
"linkerconfig",
"servicemanager.microdroid",
"tombstoned",
+ "tombstone_transmit.microdroid",
"cgroups.json",
"public.libraries.android.txt",
diff --git a/microdroid/bootconfig.common b/microdroid/bootconfig.common
index eda95a2..c202ba0 100644
--- a/microdroid/bootconfig.common
+++ b/microdroid/bootconfig.common
@@ -1,2 +1,6 @@
androidboot.first_stage_console = 1
androidboot.hardware = microdroid
+
+# tombstone_transmit is enabled. Tombstones will be transmitted to the host
+# TODO(b/227443903) : Make this configurable by VM owners
+androidboot.tombstone_transmit.enabled=1
diff --git a/microdroid/init.rc b/microdroid/init.rc
index f6d5092..dbe5ac7 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -173,6 +173,13 @@
mkdir /data/local 0751 root root
mkdir /data/local/tmp 0771 shell shell
+on init && property:ro.boot.tombstone_transmit.enabled=1
+ start tombstone_transmit
+
+service tombstone_transmit /system/bin/tombstone_transmit.microdroid -cid 2 -port 2000
+ user root
+ group system
+
service apexd-vm /system/bin/apexd --vm
user root
group system
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index a5e4920..66b4c9e 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
rust_ffi_static {
name: "libpvmfw",
crate_name: "pvmfw",
diff --git a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
index d5c5d62..df3e247 100644
--- a/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
+++ b/tests/hostside/helper/java/android/virt/test/VirtualizationTestCaseBase.java
@@ -105,7 +105,7 @@
}
// Run an arbitrary command in the host side and returns the result
- private static String runOnHost(String... cmd) {
+ public static String runOnHost(String... cmd) {
return runOnHostWithTimeout(10000, cmd);
}
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 5b71eba..7944245 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -22,6 +22,7 @@
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -100,6 +101,13 @@
false);
}
+ // Wait until logd-init starts. The service is one of the last services that are started in
+ // the microdroid boot procedure. Therefore, waiting for the service means that we wait for
+ // the boot to complete. TODO: we need a better marker eventually.
+ private void waitForLogdInit() {
+ tryRunOnMicrodroid("watch -e \"getprop init.svc.logd-reinit | grep '^$'\"");
+ }
+
@Test
public void testCreateVmRequiresPermission() throws Exception {
// Revoke the MANAGE_VIRTUAL_MACHINE permission for the test app
@@ -350,6 +358,41 @@
}
@Test
+ public void testTombstonesAreBeingForwarded() throws Exception {
+ // Note this test relies on logcat values being printed by tombstone_transmit on
+ // and the reeceiver on host (virtualization_service)
+ final String configPath = "assets/vm_config.json"; // path inside the APK
+ final String cid =
+ startMicrodroid(
+ getDevice(),
+ getBuild(),
+ APK_NAME,
+ PACKAGE_NAME,
+ configPath,
+ /* debug */ true,
+ minMemorySize(),
+ Optional.of(NUM_VCPUS),
+ Optional.of(CPU_AFFINITY));
+ adbConnectToMicrodroid(getDevice(), cid);
+ waitForLogdInit();
+ runOnMicrodroid("logcat -c");
+ // We need root permission to write to /data/tombstones/
+ rootMicrodroid();
+ // Write a test tombstone file in /data/tombstones
+ runOnMicrodroid("echo -n \'Test tombstone in VM with 34 bytes\'"
+ + "> /data/tombstones/transmit.txt");
+ // check if the tombstone have been tranferred from VM
+ assertNotEquals(runOnMicrodroid("timeout 15s logcat | grep -m 1 "
+ + "'tombstone_transmit.microdroid:.*data/tombstones/transmit.txt'"),
+ "");
+ // Confirm that tombstone is received (from host logcat)
+ assertNotEquals(runOnHost("adb", "-s", getDevice().getSerialNumber(),
+ "logcat", "-d", "-e",
+ "Received 34 bytes from guest & wrote to tombstone file.*"),
+ "");
+ }
+
+ @Test
public void testMicrodroidBoots() throws Exception {
final String configPath = "assets/vm_config.json"; // path inside the APK
final String cid =
@@ -364,12 +407,7 @@
Optional.of(NUM_VCPUS),
Optional.of(CPU_AFFINITY));
adbConnectToMicrodroid(getDevice(), cid);
-
- // Wait until logd-init starts. The service is one of the last services that are started in
- // the microdroid boot procedure. Therefore, waiting for the service means that we wait for
- // the boot to complete. TODO: we need a better marker eventually.
- tryRunOnMicrodroid("watch -e \"getprop init.svc.logd-reinit | grep '^$'\"");
-
+ waitForLogdInit();
// Test writing to /data partition
runOnMicrodroid("echo MicrodroidTest > /data/local/tmp/test.txt");
assertThat(runOnMicrodroid("cat /data/local/tmp/test.txt"), is("MicrodroidTest"));
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 9b2b740..7a8da96 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -45,6 +45,7 @@
"libserde_xml_rs",
"libshared_child",
"libstatslog_virtualization_rust",
+ "libtombstoned_client_rust",
"libvmconfig",
"libzip",
"libvsock",
diff --git a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
index 1a16f2a..dff5d46 100644
--- a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
@@ -30,6 +30,12 @@
const int VM_BINDER_SERVICE_PORT = 5000;
/**
+ * Port number that VirtualMachineService listens on connections from the guest VMs for the
+ * tombtones
+ */
+ const int VM_TOMBSTONES_SERVICE_PORT = 2000;
+
+ /**
* Notifies that the payload has started.
*/
void notifyPayloadStarted();
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index d9825dc..73cbcfa 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -42,7 +42,7 @@
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::{
IVirtualMachineService::{
BnVirtualMachineService, IVirtualMachineService, VM_BINDER_SERVICE_PORT,
- VM_STREAM_SERVICE_PORT,
+ VM_STREAM_SERVICE_PORT, VM_TOMBSTONES_SERVICE_PORT,
},
};
use anyhow::{anyhow, bail, Context, Result};
@@ -57,13 +57,14 @@
use std::convert::TryInto;
use std::ffi::CStr;
use std::fs::{create_dir, File, OpenOptions};
-use std::io::{Error, ErrorKind, Write};
+use std::io::{Error, ErrorKind, Write, Read};
use std::num::NonZeroU32;
use std::os::raw;
use std::os::unix::io::{FromRawFd, IntoRawFd};
use std::path::{Path, PathBuf};
use std::ptr::null_mut;
use std::sync::{Arc, Mutex, Weak};
+use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
use vmconfig::VmConfig;
use vsock::{SockAddr, VsockListener, VsockStream};
use zip::ZipArchive;
@@ -86,6 +87,8 @@
/// Version of the instance image format
const ANDROID_VM_INSTANCE_VERSION: u16 = 1;
+const CHUNK_RECV_MAX_LEN: usize = 1024;
+
/// Implementation of `IVirtualizationService`, the entry point of the AIDL service.
#[derive(Debug, Default)]
pub struct VirtualizationService {
@@ -371,6 +374,55 @@
}
}
+fn handle_stream_connection_tombstoned() -> Result<()> {
+ let listener =
+ VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as u32)?;
+ info!("Listening to tombstones from guests ...");
+ for incoming_stream in listener.incoming() {
+ let mut incoming_stream = match incoming_stream {
+ Err(e) => {
+ warn!("invalid incoming connection: {}", e);
+ continue;
+ }
+ Ok(s) => s,
+ };
+ std::thread::spawn(move || {
+ if let Err(e) = handle_tombstone(&mut incoming_stream) {
+ error!("Failed to write tombstone- {:?}", e);
+ }
+ });
+ }
+ Ok(())
+}
+
+fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
+ if let Ok(SockAddr::Vsock(addr)) = stream.peer_addr() {
+ info!("Vsock Stream connected to cid={} for tombstones", addr.cid());
+ }
+ let tb_connection =
+ TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
+ .context("Failed to connect to tombstoned")?;
+ let mut text_output = tb_connection
+ .text_output
+ .as_ref()
+ .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
+ let mut num_bytes_read = 0;
+ loop {
+ let mut chunk_recv = [0; CHUNK_RECV_MAX_LEN];
+ let n = stream
+ .read(&mut chunk_recv)
+ .context("Failed to read tombstone data from Vsock stream")?;
+ if n == 0 {
+ break;
+ }
+ num_bytes_read += n;
+ text_output.write_all(&chunk_recv[0..n]).context("Failed to write guests tombstones")?;
+ }
+ info!("Received {} bytes from guest & wrote to tombstone file", num_bytes_read);
+ tb_connection.notify_completion()?;
+ Ok(())
+}
+
impl VirtualizationService {
pub fn init() -> VirtualizationService {
let service = VirtualizationService::default();
@@ -381,8 +433,15 @@
handle_stream_connection_from_vm(state).unwrap();
});
+ std::thread::spawn(|| {
+ if let Err(e) = handle_stream_connection_tombstoned() {
+ warn!("Error receiving tombstone from guest or writing them. Error: {}", e);
+ }
+ });
+
// binder server for vm
- let mut state = service.state.clone(); // reference to state (not the state itself) is copied
+ // reference to state (not the state itself) is copied
+ let mut state = service.state.clone();
std::thread::spawn(move || {
let state_ptr = &mut state as *mut _ as *mut raw::c_void;
diff --git a/zipfuse/src/main.rs b/zipfuse/src/main.rs
index a91642c..c3fae69 100644
--- a/zipfuse/src/main.rs
+++ b/zipfuse/src/main.rs
@@ -82,7 +82,9 @@
libc::MS_NOSUID | libc::MS_NODEV | libc::MS_RDONLY,
&mount_options,
)?;
- Ok(fuse::worker::start_message_loop(dev_fuse, MAX_READ, MAX_WRITE, ZipFuse::new(zip_file)?)?)
+ let mut config = fuse::FuseConfig::new();
+ config.dev_fuse(dev_fuse).max_write(MAX_WRITE).max_read(MAX_READ);
+ Ok(config.enter_message_loop(ZipFuse::new(zip_file)?)?)
}
struct ZipFuse {