Merge "Revert "Support vendor partition in non-debuggable pVMs"" into main
diff --git a/OWNERS b/OWNERS
index 40c709f..717a4db 100644
--- a/OWNERS
+++ b/OWNERS
@@ -28,3 +28,10 @@
tabba@google.com
vdonnefort@google.com
victorhsieh@google.com
+
+# Ferrochrome
+per-file android/FerrochromeApp/**=jiyong@google.com,jeongik@google.com
+per-file android/LinuxInstaller/**=jiyong@google.com,jeongik@google.com
+per-file android/TerminalApp/**=jiyong@google.com,jeongik@google.com
+per-file android/VmLauncherApp/**=jiyong@google.com,jeongik@google.com
+per-file libs/vm_launcher_lib/**=jiyong@google.com,jeongik@google.com
diff --git a/android/LinuxInstaller/linux_image_builder/ttyd.service b/android/LinuxInstaller/linux_image_builder/ttyd.service
index 3a8f181..f71557d 100644
--- a/android/LinuxInstaller/linux_image_builder/ttyd.service
+++ b/android/LinuxInstaller/linux_image_builder/ttyd.service
@@ -3,7 +3,7 @@
After=syslog.target
After=network.target
[Service]
-ExecStart=/usr/local/bin/ttyd -W login
+ExecStart=/usr/local/bin/ttyd -W screen -aAxR -S main login
Type=simple
Restart=always
User=root
diff --git a/android/TerminalApp/Android.bp b/android/TerminalApp/Android.bp
index 3ae014e..1a7c581 100644
--- a/android/TerminalApp/Android.bp
+++ b/android/TerminalApp/Android.bp
@@ -9,8 +9,10 @@
static_libs: [
"vm_launcher_lib",
],
- sdk_version: "system_current",
+ platform_apis: true,
+ privileged: true,
optimize: {
+ proguard_flags_files: ["proguard.flags"],
shrink_resources: true,
},
apex_available: [
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index c92da67..e338c49 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -2,9 +2,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.virtualization.terminal" >
+ <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+ <uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
<uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
+ <uses-feature android:name="android.software.virtualization_framework" android:required="true" />
<application
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
@@ -27,6 +31,20 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
+
+ <service
+ android:name="com.android.virtualization.vmlauncher.VmLauncherService"
+ android:enabled="true"
+ android:exported="false"
+ android:foregroundServiceType="specialUse">
+ <property
+ android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
+ android:value="Run VM instances" />
+ <intent-filter>
+ <action android:name="android.virtualization.START_VM_LAUNCHER_SERVICE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </service>
</application>
</manifest>
diff --git a/android/TerminalApp/proguard.flags b/android/TerminalApp/proguard.flags
new file mode 100644
index 0000000..13ec24e
--- /dev/null
+++ b/android/TerminalApp/proguard.flags
@@ -0,0 +1,7 @@
+# Keep the no-args constructor of the deserialized class
+-keepclassmembers class com.android.virtualization.vmlauncher.ConfigJson {
+ <init>();
+}
+-keepclassmembers class com.android.virtualization.vmlauncher.ConfigJson$* {
+ <init>();
+}
diff --git a/android/VmLauncherApp/Android.bp b/android/VmLauncherApp/Android.bp
index 7dd2473..2e8cc93 100644
--- a/android/VmLauncherApp/Android.bp
+++ b/android/VmLauncherApp/Android.bp
@@ -11,7 +11,7 @@
"android.system.virtualizationservice_internal-java",
// TODO(b/331708504): will be removed when AVF framework handles surface
"libcrosvm_android_display_service-java",
- "gson",
+ "vm_launcher_lib",
],
libs: [
"framework-virtualization.impl",
diff --git a/android/VmLauncherApp/AndroidManifest.xml b/android/VmLauncherApp/AndroidManifest.xml
index 583fce7..4fb4b5c 100644
--- a/android/VmLauncherApp/AndroidManifest.xml
+++ b/android/VmLauncherApp/AndroidManifest.xml
@@ -6,8 +6,6 @@
<uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
- <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>
<uses-feature android:name="android.software.virtualization_framework" android:required="true" />
<permission android:name="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"
@@ -28,20 +26,6 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
- <service
- android:name=".VmLauncherService"
- android:enabled="true"
- android:exported="true"
- android:permission="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"
- android:foregroundServiceType="specialUse">
- <property
- android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
- android:value="Run VM instances" />
- <intent-filter>
- <action android:name="android.virtualization.START_VM_LAUNCHER_SERVICE" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </service>
</application>
diff --git a/android/fd_server/Android.bp b/android/fd_server/Android.bp
index b02c104..748c660 100644
--- a/android/fd_server/Android.bp
+++ b/android/fd_server/Android.bp
@@ -18,6 +18,7 @@
"liblog_rust",
"libnix",
"librpcbinder_rs",
+ "librustutils",
],
prefer_rlib: true,
apex_available: ["com.android.virt"],
@@ -39,6 +40,7 @@
"liblog_rust",
"libnix",
"librpcbinder_rs",
+ "librustutils",
],
prefer_rlib: true,
test_suites: ["general-tests"],
diff --git a/android/fd_server/src/main.rs b/android/fd_server/src/main.rs
index 26315bf..07f0896 100644
--- a/android/fd_server/src/main.rs
+++ b/android/fd_server/src/main.rs
@@ -29,9 +29,10 @@
use log::debug;
use nix::sys::stat::{umask, Mode};
use rpcbinder::RpcServer;
+use rustutils::inherited_fd::take_fd_ownership;
use std::collections::BTreeMap;
use std::fs::File;
-use std::os::unix::io::{FromRawFd, OwnedFd};
+use std::os::unix::io::OwnedFd;
use aidl::{FdConfig, FdService};
use authfs_fsverity_metadata::parse_fsverity_metadata;
@@ -39,20 +40,6 @@
// TODO(b/259920193): support dynamic port for multiple fd_server instances
const RPC_SERVICE_PORT: u32 = 3264;
-fn is_fd_valid(fd: i32) -> bool {
- // SAFETY: a query-only syscall
- let retval = unsafe { libc::fcntl(fd, libc::F_GETFD) };
- retval >= 0
-}
-
-fn fd_to_owned<T: FromRawFd>(fd: i32) -> Result<T> {
- if !is_fd_valid(fd) {
- bail!("Bad FD: {}", fd);
- }
- // SAFETY: The caller is supposed to provide valid FDs to this process.
- Ok(unsafe { T::from_raw_fd(fd) })
-}
-
fn parse_arg_ro_fds(arg: &str) -> Result<(i32, FdConfig)> {
let result: Result<Vec<i32>, _> = arg.split(':').map(|x| x.parse::<i32>()).collect();
let fds = result?;
@@ -62,13 +49,13 @@
Ok((
fds[0],
FdConfig::Readonly {
- file: fd_to_owned(fds[0])?,
+ file: take_fd_ownership(fds[0])?.into(),
// Alternative metadata source, if provided
alt_metadata: fds
.get(1)
- .map(|fd| fd_to_owned(*fd))
+ .map(|fd| take_fd_ownership(*fd))
.transpose()?
- .and_then(|f| parse_fsverity_metadata(f).ok()),
+ .and_then(|f| parse_fsverity_metadata(f.into()).ok()),
},
))
}
@@ -105,23 +92,26 @@
fd_pool.insert(fd, config);
}
for fd in args.rw_fds {
- let file = fd_to_owned::<File>(fd)?;
+ let file: File = take_fd_ownership(fd)?.into();
if file.metadata()?.len() > 0 {
bail!("File is expected to be empty");
}
fd_pool.insert(fd, FdConfig::ReadWrite(file));
}
for fd in args.ro_dirs {
- fd_pool.insert(fd, FdConfig::InputDir(fd_to_owned(fd)?));
+ fd_pool.insert(fd, FdConfig::InputDir(take_fd_ownership(fd)?));
}
for fd in args.rw_dirs {
- fd_pool.insert(fd, FdConfig::OutputDir(fd_to_owned(fd)?));
+ fd_pool.insert(fd, FdConfig::OutputDir(take_fd_ownership(fd)?));
}
- let ready_fd = args.ready_fd.map(fd_to_owned).transpose()?;
+ let ready_fd = args.ready_fd.map(take_fd_ownership).transpose()?;
Ok((fd_pool, ready_fd))
}
fn main() -> Result<()> {
+ // SAFETY: nobody has taken ownership of the inherited FDs yet.
+ unsafe { rustutils::inherited_fd::init_once()? };
+
android_logger::init_once(
android_logger::Config::default()
.with_tag("fd_server")
diff --git a/android/virtmgr/Android.bp b/android/virtmgr/Android.bp
index f0b6881..d0d7915 100644
--- a/android/virtmgr/Android.bp
+++ b/android/virtmgr/Android.bp
@@ -34,7 +34,6 @@
"libapkverify",
"libavf_features",
"libavflog",
- "libbase_rust",
"libbinder_rs",
"libcfg_if",
"libclap",
@@ -70,6 +69,7 @@
"liblibfdt",
"libfsfdt",
"libhypervisor_props",
+ "libzerocopy",
"libuuid",
// TODO(b/202115393) stabilize the interface
"packagemanager_aidl-rust",
diff --git a/android/virtmgr/src/composite.rs b/android/virtmgr/src/composite.rs
index 681ec59..1219150 100644
--- a/android/virtmgr/src/composite.rs
+++ b/android/virtmgr/src/composite.rs
@@ -15,13 +15,16 @@
//! Functions for creating a composite disk image.
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::Partition::Partition;
-use anyhow::{anyhow, Context, Error};
-use disk::{
- create_composite_disk, create_disk_file, ImagePartitionType, PartitionInfo, MAX_NESTING_DEPTH,
-};
+use anyhow::{bail, Context, Error};
+use disk::{create_composite_disk, ImagePartitionType, PartitionInfo};
use std::fs::{File, OpenOptions};
+use std::io::ErrorKind;
+use std::os::unix::fs::FileExt;
use std::os::unix::io::AsRawFd;
use std::path::{Path, PathBuf};
+use zerocopy::AsBytes;
+use zerocopy::FromBytes;
+use zerocopy::FromZeroes;
use uuid::Uuid;
@@ -98,7 +101,7 @@
.context("Failed to clone partition image file descriptor")?
.into();
let path = fd_path_for_file(&file);
- let size = get_partition_size(&file, &path)?;
+ let size = get_partition_size(&file)?;
files.push(file);
Ok(PartitionInfo {
@@ -122,16 +125,74 @@
/// Find the size of the partition image in the given file by parsing the header.
///
-/// This will work for raw, QCOW2, composite and Android sparse images.
-fn get_partition_size(partition: &File, path: &Path) -> Result<u64, Error> {
- // TODO: Use `context` once disk::Error implements std::error::Error.
- // TODO: Add check for is_sparse_file
- Ok(create_disk_file(
- partition.try_clone()?,
- /* is_sparse_file */ false,
- MAX_NESTING_DEPTH,
- path,
- )
- .map_err(|e| anyhow!("Failed to open partition image: {}", e))?
- .get_len()?)
+/// This will work for raw and Android sparse images. QCOW2 and composite images aren't supported.
+fn get_partition_size(file: &File) -> Result<u64, Error> {
+ match detect_image_type(file).context("failed to detect partition image type")? {
+ ImageType::Raw => Ok(file.metadata().context("failed to get metadata")?.len()),
+ ImageType::AndroidSparse => {
+ // Source: system/core/libsparse/sparse_format.h
+ #[repr(C)]
+ #[derive(Clone, Copy, Debug, AsBytes, FromZeroes, FromBytes)]
+ struct SparseHeader {
+ magic: u32,
+ major_version: u16,
+ minor_version: u16,
+ file_hdr_sz: u16,
+ chunk_hdr_size: u16,
+ blk_sz: u32,
+ total_blks: u32,
+ total_chunks: u32,
+ image_checksum: u32,
+ }
+ let mut header = SparseHeader::new_zeroed();
+ file.read_exact_at(header.as_bytes_mut(), 0)
+ .context("failed to read android sparse header")?;
+ let len = u64::from(header.total_blks)
+ .checked_mul(header.blk_sz.into())
+ .context("android sparse image len too big")?;
+ Ok(len)
+ }
+ t => bail!("unsupported partition image type: {t:?}"),
+ }
+}
+
+/// Image file types we can detect.
+#[derive(Debug, PartialEq, Eq)]
+enum ImageType {
+ Raw,
+ Qcow2,
+ CompositeDisk,
+ AndroidSparse,
+}
+
+/// Detect image type by looking for magic bytes.
+fn detect_image_type(file: &File) -> std::io::Result<ImageType> {
+ const CDISK_MAGIC: &str = "composite_disk\x1d";
+ const QCOW_MAGIC: u32 = 0x5146_49fb;
+ const SPARSE_HEADER_MAGIC: u32 = 0xed26ff3a;
+
+ let mut magic4 = [0u8; 4];
+ match file.read_exact_at(&mut magic4[..], 0) {
+ Ok(()) => {}
+ Err(e) if e.kind() == ErrorKind::UnexpectedEof => return Ok(ImageType::Raw),
+ Err(e) => return Err(e),
+ }
+ if magic4 == QCOW_MAGIC.to_be_bytes() {
+ return Ok(ImageType::Qcow2);
+ }
+ if magic4 == SPARSE_HEADER_MAGIC.to_le_bytes() {
+ return Ok(ImageType::AndroidSparse);
+ }
+
+ let mut buf = [0u8; CDISK_MAGIC.len()];
+ match file.read_exact_at(buf.as_bytes_mut(), 0) {
+ Ok(()) => {}
+ Err(e) if e.kind() == ErrorKind::UnexpectedEof => return Ok(ImageType::Raw),
+ Err(e) => return Err(e),
+ }
+ if buf == CDISK_MAGIC.as_bytes() {
+ return Ok(ImageType::CompositeDisk);
+ }
+
+ Ok(ImageType::Raw)
}
diff --git a/android/virtmgr/src/crosvm.rs b/android/virtmgr/src/crosvm.rs
index 5886535..b2be736 100644
--- a/android/virtmgr/src/crosvm.rs
+++ b/android/virtmgr/src/crosvm.rs
@@ -57,7 +57,6 @@
use rpcbinder::RpcServer;
/// external/crosvm
-use base::UnixSeqpacketListener;
use vm_control::{BalloonControlCommand, VmRequest, VmResponse};
const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
@@ -1045,8 +1044,9 @@
}
for disk in config.disks {
+ // Disk file locking is disabled because of missing SELinux policies.
command.arg("--block").arg(format!(
- "path={},ro={}",
+ "path={},ro={},lock=false",
add_preserved_fd(&mut preserved_fds, disk.image),
!disk.writable,
));
@@ -1056,8 +1056,8 @@
command.arg(add_preserved_fd(&mut preserved_fds, kernel));
}
- let control_sock = UnixSeqpacketListener::bind(crosvm_control_socket_path)
- .context("failed to create control server")?;
+ let control_sock = create_crosvm_control_listener(crosvm_control_socket_path)
+ .context("failed to create control listener")?;
command.arg("--socket").arg(add_preserved_fd(&mut preserved_fds, control_sock));
if let Some(dt_overlay) = config.device_tree_overlay {
@@ -1271,3 +1271,22 @@
let (read_fd, write_fd) = pipe2(OFlag::O_CLOEXEC)?;
Ok((read_fd.into(), write_fd.into()))
}
+
+/// Creates and binds a unix seqpacket listening socket to be passed as crosvm's `--socket`
+/// argument. See `UnixSeqpacketListener::bind` in crosvm's code for reference.
+fn create_crosvm_control_listener(crosvm_control_socket_path: &Path) -> Result<OwnedFd> {
+ use nix::sys::socket;
+ let fd = socket::socket(
+ socket::AddressFamily::Unix,
+ socket::SockType::SeqPacket,
+ socket::SockFlag::empty(),
+ None,
+ )
+ .context("socket failed")?;
+ socket::bind(fd.as_raw_fd(), &socket::UnixAddr::new(crosvm_control_socket_path)?)
+ .context("bind failed")?;
+ // The exact backlog size isn't imporant. crosvm uses 128 internally. We use 127 here
+ // because of a `nix` bug.
+ socket::listen(&fd, socket::Backlog::new(127).unwrap()).context("listen failed")?;
+ Ok(fd)
+}
diff --git a/android/virtmgr/src/main.rs b/android/virtmgr/src/main.rs
index 67e7282..1625009 100644
--- a/android/virtmgr/src/main.rs
+++ b/android/virtmgr/src/main.rs
@@ -25,15 +25,15 @@
use crate::aidl::{GLOBAL_SERVICE, VirtualizationService};
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::BnVirtualizationService;
-use anyhow::{bail, Context, Result};
+use anyhow::{bail, Result};
use binder::{BinderFeatures, ProcessState};
use log::{info, LevelFilter};
use rpcbinder::{FileDescriptorTransportMode, RpcServer};
-use std::os::unix::io::{AsFd, FromRawFd, OwnedFd, RawFd};
+use std::os::unix::io::{AsFd, RawFd};
use std::sync::LazyLock;
use clap::Parser;
-use nix::fcntl::{fcntl, F_GETFD, F_SETFD, FdFlag};
use nix::unistd::{write, Pid, Uid};
+use rustutils::inherited_fd::take_fd_ownership;
use std::os::unix::raw::{pid_t, uid_t};
const LOG_TAG: &str = "virtmgr";
@@ -71,32 +71,6 @@
ready_fd: RawFd,
}
-fn take_fd_ownership(raw_fd: RawFd, owned_fds: &mut Vec<RawFd>) -> Result<OwnedFd, anyhow::Error> {
- // Basic check that the integer value does correspond to a file descriptor.
- fcntl(raw_fd, F_GETFD).with_context(|| format!("Invalid file descriptor {raw_fd}"))?;
-
- // The file descriptor had CLOEXEC disabled to be inherited from the parent.
- // Re-enable it to make sure it is not accidentally inherited further.
- fcntl(raw_fd, F_SETFD(FdFlag::FD_CLOEXEC))
- .with_context(|| format!("Could not set CLOEXEC on file descriptor {raw_fd}"))?;
-
- // Creating OwnedFd for stdio FDs is not safe.
- if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
- bail!("File descriptor {raw_fd} is standard I/O descriptor");
- }
-
- // Reject RawFds that already have a corresponding OwnedFd.
- if owned_fds.contains(&raw_fd) {
- bail!("File descriptor {raw_fd} already owned");
- }
- owned_fds.push(raw_fd);
-
- // SAFETY: Initializing OwnedFd for a RawFd provided in cmdline arguments.
- // We checked that the integer value corresponds to a valid FD and that this
- // is the first argument to claim its ownership.
- Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
-}
-
fn check_vm_support() -> Result<()> {
if hypervisor_props::is_any_vm_supported()? {
Ok(())
@@ -109,6 +83,10 @@
}
fn main() {
+ // SAFETY: nobody has taken ownership of the inherited FDs yet.
+ unsafe { rustutils::inherited_fd::init_once() }
+ .expect("Failed to take ownership of inherited FDs");
+
android_logger::init_once(
android_logger::Config::default()
.with_tag(LOG_TAG)
@@ -120,11 +98,9 @@
let args = Args::parse();
- let mut owned_fds = vec![];
- let rpc_server_fd = take_fd_ownership(args.rpc_server_fd, &mut owned_fds)
- .expect("Failed to take ownership of rpc_server_fd");
- let ready_fd = take_fd_ownership(args.ready_fd, &mut owned_fds)
- .expect("Failed to take ownership of ready_fd");
+ let rpc_server_fd =
+ take_fd_ownership(args.rpc_server_fd).expect("Failed to take ownership of rpc_server_fd");
+ let ready_fd = take_fd_ownership(args.ready_fd).expect("Failed to take ownership of ready_fd");
// Start thread pool for kernel Binder connection to VirtualizationServiceInternal.
ProcessState::start_thread_pool();
diff --git a/android/virtualizationservice/aidl/Android.bp b/android/virtualizationservice/aidl/Android.bp
index c1bff5e..79a9d40 100644
--- a/android/virtualizationservice/aidl/Android.bp
+++ b/android/virtualizationservice/aidl/Android.bp
@@ -29,6 +29,7 @@
rust: {
enabled: true,
apex_available: [
+ "//apex_available:platform",
"com.android.virt",
"com.android.compos",
"com.android.microfuchsia",
@@ -149,6 +150,7 @@
rust: {
enabled: true,
apex_available: [
+ "//apex_available:platform",
"com.android.virt",
"com.android.compos",
"com.android.microfuchsia",
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
index 11a2115..99dc648 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/Partition.aidl
@@ -20,7 +20,12 @@
/** A label for the partition. */
@utf8InCpp String label;
- /** The backing file descriptor of the partition image. */
+ /**
+ * The backing file descriptor of the partition image.
+ *
+ * The image file must either be a raw binary file, or an android-sparse
+ * formatted file.
+ */
ParcelFileDescriptor image;
/** Whether the partition should be writable by the VM. */
diff --git a/android/vm/Android.bp b/android/vm/Android.bp
index c1d9b6b..ba8b416 100644
--- a/android/vm/Android.bp
+++ b/android/vm/Android.bp
@@ -16,6 +16,7 @@
"libbinder_rs",
"libclap",
"libenv_logger",
+ "libcfg_if",
"libglob",
"libhypervisor_props",
"liblibc",
diff --git a/android/vm/src/main.rs b/android/vm/src/main.rs
index f2c2fa4..609bbdf 100644
--- a/android/vm/src/main.rs
+++ b/android/vm/src/main.rs
@@ -75,14 +75,14 @@
}
impl CommonConfig {
- #[cfg(network)]
fn network_supported(&self) -> bool {
- self.network_supported
- }
-
- #[cfg(not(network))]
- fn network_supported(&self) -> bool {
- false
+ cfg_if::cfg_if! {
+ if #[cfg(network)] {
+ self.network_supported
+ } else {
+ false
+ }
+ }
}
}
@@ -117,14 +117,14 @@
}
impl DebugConfig {
- #[cfg(debuggable_vms_improvements)]
fn enable_earlycon(&self) -> bool {
- self.enable_earlycon
- }
-
- #[cfg(not(debuggable_vms_improvements))]
- fn enable_earlycon(&self) -> bool {
- false
+ cfg_if::cfg_if! {
+ if #[cfg(debuggable_vms_improvements)] {
+ self.enable_earlycon
+ } else {
+ false
+ }
+ }
}
}
@@ -158,34 +158,34 @@
}
impl MicrodroidConfig {
- #[cfg(vendor_modules)]
fn vendor(&self) -> Option<&PathBuf> {
- self.vendor.as_ref()
+ cfg_if::cfg_if! {
+ if #[cfg(vendor_modules)] {
+ self.vendor.as_ref()
+ } else {
+ None
+ }
+ }
}
- #[cfg(not(vendor_modules))]
- fn vendor(&self) -> Option<&PathBuf> {
- None
- }
-
- #[cfg(vendor_modules)]
fn gki(&self) -> Option<&str> {
- self.gki.as_deref()
+ cfg_if::cfg_if! {
+ if #[cfg(vendor_modules)] {
+ self.gki.as_deref()
+ } else {
+ None
+ }
+ }
}
- #[cfg(not(vendor_modules))]
- fn gki(&self) -> Option<&str> {
- None
- }
-
- #[cfg(device_assignment)]
fn devices(&self) -> &[PathBuf] {
- &self.devices
- }
-
- #[cfg(not(device_assignment))]
- fn devices(&self) -> &[PathBuf] {
- &[]
+ cfg_if::cfg_if! {
+ if #[cfg(device_assignment)] {
+ &self.devices
+ } else {
+ &[]
+ }
+ }
}
}
@@ -236,35 +236,36 @@
}
impl RunAppConfig {
- #[cfg(multi_tenant)]
fn extra_apks(&self) -> &[PathBuf] {
- &self.extra_apks
+ cfg_if::cfg_if! {
+ if #[cfg(multi_tenant)] {
+ &self.extra_apks
+ } else {
+ &[]
+ }
+ }
}
- #[cfg(not(multi_tenant))]
- fn extra_apks(&self) -> &[PathBuf] {
- &[]
- }
-
- #[cfg(llpvm_changes)]
fn instance_id(&self) -> Result<PathBuf, Error> {
- Ok(self.instance_id.clone())
+ cfg_if::cfg_if! {
+ if #[cfg(llpvm_changes)] {
+ Ok(self.instance_id.clone())
+ } else {
+ Err(anyhow!("LLPVM feature is disabled, --instance_id flag not supported"))
+ }
+ }
}
- #[cfg(not(llpvm_changes))]
- fn instance_id(&self) -> Result<PathBuf, Error> {
- Err(anyhow!("LLPVM feature is disabled, --instance_id flag not supported"))
- }
-
- #[cfg(llpvm_changes)]
fn set_instance_id(&mut self, instance_id_file: PathBuf) -> Result<(), Error> {
- self.instance_id = instance_id_file;
- Ok(())
- }
-
- #[cfg(not(llpvm_changes))]
- fn set_instance_id(&mut self, _: PathBuf) -> Result<(), Error> {
- Err(anyhow!("LLPVM feature is disabled, --instance_id flag not supported"))
+ cfg_if::cfg_if! {
+ if #[cfg(llpvm_changes)] {
+ self.instance_id = instance_id_file;
+ Ok(())
+ } else {
+ let _ = instance_id_file;
+ Err(anyhow!("LLPVM feature is disabled, --instance_id flag not supported"))
+ }
+ }
}
}
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index f493202..4916df7 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -107,6 +107,7 @@
filesystems: microdroid_filesystem_images,
prebuilts: [
"rialto_bin",
+ "android_bootloader_crosvm_aarch64",
],
},
x86_64: {
@@ -125,6 +126,9 @@
default: [],
}),
filesystems: microdroid_filesystem_images,
+ prebuilts: [
+ "android_bootloader_crosvm_x86_64",
+ ],
},
},
binaries: [
@@ -132,13 +136,11 @@
"vm",
],
prebuilts: [
- "features_com.android.virt.xml",
"microdroid_initrd_debuggable",
"microdroid_initrd_normal",
"microdroid.json",
"microdroid_kernel",
"com.android.virt.init.rc",
- "android_bootloader_crosvm_aarch64",
] + select(soong_config_variable("ANDROID", "avf_microdroid_guest_gki_version"), {
"android15_66": [
"microdroid_gki-android15-6.6_initrd_debuggable",
diff --git a/build/apex/permissions/Android.bp b/build/apex/permissions/Android.bp
index 0c925ce..678a4f2 100644
--- a/build/apex/permissions/Android.bp
+++ b/build/apex/permissions/Android.bp
@@ -21,4 +21,5 @@
name: "features_com.android.virt.xml",
sub_dir: "permissions",
src: "features_com.android.virt.xml",
+ soc_specific: true,
}
diff --git a/build/apex/product_packages.mk b/build/apex/product_packages.mk
index a024192..b2a4ca2 100644
--- a/build/apex/product_packages.mk
+++ b/build/apex/product_packages.mk
@@ -24,6 +24,7 @@
PRODUCT_PACKAGES += \
com.android.compos \
+ features_com.android.virt.xml
# TODO(b/207336449): Figure out how to get these off /system
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST := \
diff --git a/build/debian/build.sh b/build/debian/build.sh
new file mode 100755
index 0000000..4d59981
--- /dev/null
+++ b/build/debian/build.sh
@@ -0,0 +1,109 @@
+#!/bin/bash
+
+# This is a script to build a Debian image that can run in a VM created via AVF.
+# TODOs:
+# - Support x86_64 architecture
+# - Add Android-specific packages via a new class
+# - Use a stable release from debian-cloud-images
+
+show_help() {
+ echo Usage: $0 [OPTION]... [FILE]
+ echo Builds a debian image and save it to FILE.
+ echo Options:
+ echo -h Pring usage and this help message and exit.
+}
+
+check_sudo() {
+ if [ "$EUID" -ne 0 ]; then
+ echo "Please run as root."
+ exit
+ fi
+}
+
+parse_options() {
+ while getopts ":h" option; do
+ case ${option} in
+ h)
+ show_help
+ exit;;
+ esac
+ done
+ if [ -n "$1" ]; then
+ built_image=$1
+ fi
+}
+
+install_prerequisites() {
+ apt update
+ DEBIAN_FRONTEND=noninteractive \
+ apt install --no-install-recommends --assume-yes \
+ ca-certificates \
+ debsums \
+ dosfstools \
+ fai-server \
+ fai-setup-storage \
+ fdisk \
+ make \
+ python3 \
+ python3-libcloud \
+ python3-marshmallow \
+ python3-pytest \
+ python3-yaml \
+ qemu-utils \
+ udev \
+ qemu-system-arm \
+ qemu-user-static
+
+ sed -i s/losetup\ -f/losetup\ -P\ -f/g /usr/sbin/fai-diskimage
+}
+
+download_debian_cloud_image() {
+ local ver=master
+ local prj=debian-cloud-images
+ local url=https://salsa.debian.org/cloud-team/${prj}/-/archive/${ver}/${prj}-${ver}.tar.gz
+ local outdir=${debian_cloud_image}
+
+ mkdir -p ${outdir}
+ wget -O - ${url} | tar xz -C ${outdir} --strip-components=1
+}
+
+copy_android_config() {
+ local src=$(dirname $0)/fai_config
+ local dst=${config_space}
+
+ cp -R ${src}/* ${dst}
+ cp $(dirname $0)/image.yaml ${resources_dir}
+
+ local ttyd_version=1.7.7
+ local url=https://github.com/tsl0922/ttyd/releases/download/${ttyd_version}/ttyd.aarch64
+ mkdir -p ${dst}/files/usr/local/bin/ttyd
+ wget ${url} -O ${dst}/files/usr/local/bin/ttyd/AVF
+ chmod 777 ${dst}/files/usr/local/bin/ttyd/AVF
+}
+
+run_fai() {
+ local out=${built_image}
+ make -C ${debian_cloud_image} image_bookworm_nocloud_arm64
+ mv ${debian_cloud_image}/image_bookworm_nocloud_arm64.raw ${out}
+}
+
+clean_up() {
+ rm -rf ${workdir}
+}
+
+set -e
+trap clean_up EXIT
+
+built_image=image.raw
+workdir=$(mktemp -d)
+debian_cloud_image=${workdir}/debian_cloud_image
+debian_version=bookworm
+config_space=${debian_cloud_image}/config_space/${debian_version}
+resources_dir=${debian_cloud_image}/src/debian_cloud_images/resources
+check_sudo
+parse_options $@
+install_prerequisites
+download_debian_cloud_image
+copy_android_config
+run_fai
+fdisk -l image.raw
diff --git a/build/debian/fai_config/class/AVF.var b/build/debian/fai_config/class/AVF.var
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/build/debian/fai_config/class/AVF.var
diff --git a/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF b/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
new file mode 100644
index 0000000..f71557d
--- /dev/null
+++ b/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
@@ -0,0 +1,12 @@
+[Unit]
+Description=TTYD
+After=syslog.target
+After=network.target
+[Service]
+ExecStart=/usr/local/bin/ttyd -W screen -aAxR -S main login
+Type=simple
+Restart=always
+User=root
+Group=root
+[Install]
+WantedBy=multi-user.target
diff --git a/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF b/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF
new file mode 100644
index 0000000..a29020b
--- /dev/null
+++ b/build/debian/fai_config/files/etc/systemd/system/vsockip.service/AVF
@@ -0,0 +1,12 @@
+[Unit]
+Description=vsock ip service
+After=syslog.target
+After=network.target
+[Service]
+ExecStart=/usr/bin/python3 /usr/local/bin/vsock.py
+Type=simple
+Restart=always
+User=root
+Group=root
+[Install]
+WantedBy=multi-user.target
diff --git a/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF b/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF
new file mode 100755
index 0000000..292d953
--- /dev/null
+++ b/build/debian/fai_config/files/usr/local/bin/vsock.py/AVF
@@ -0,0 +1,57 @@
+#!/usr/bin/env python3
+
+import socket
+
+# Constants for vsock (from linux/vm_sockets.h)
+AF_VSOCK = 40
+SOCK_STREAM = 1
+VMADDR_CID_ANY = -1
+
+def get_local_ip():
+ """Retrieves the first IPv4 address found on the system.
+
+ Returns:
+ str: The local IPv4 address, or '127.0.0.1' if no IPv4 address is found.
+ """
+
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ try:
+ s.connect(('8.8.8.8', 80))
+ ip = s.getsockname()[0]
+ except Exception:
+ ip = '127.0.0.1'
+ finally:
+ s.close()
+ return ip
+
+def main():
+ PORT = 1024
+
+ # Create a vsock socket
+ server_socket = socket.socket(AF_VSOCK, SOCK_STREAM)
+
+ # Bind the socket to the server address
+ server_address = (VMADDR_CID_ANY, PORT)
+ server_socket.bind(server_address)
+
+ # Listen for incoming connections
+ server_socket.listen(1)
+ print(f"VSOCK server listening on port {PORT}...")
+
+ while True:
+ # Accept a connection
+ connection, client_address = server_socket.accept()
+ print(f"Connection from: {client_address}")
+
+ try:
+ # Get the local IP address
+ local_ip = get_local_ip()
+
+ # Send the IP address to the client
+ connection.sendall(local_ip.encode())
+ finally:
+ # Close the connection
+ connection.close()
+
+if __name__ == "__main__":
+ main()
diff --git a/build/debian/fai_config/hooks/mountdisks.BASE b/build/debian/fai_config/hooks/mountdisks.BASE
new file mode 100755
index 0000000..33ebb05
--- /dev/null
+++ b/build/debian/fai_config/hooks/mountdisks.BASE
@@ -0,0 +1,25 @@
+#!/bin/bash
+set -eu
+touch $LOGDIR/skip.mountdisks
+
+# Set filesystem context if running with SELinux enabled
+options="context=$(cat /proc/self/attr/current 2>/dev/null || :)"
+
+set -- $disklist
+device=/dev/$1
+file=$(losetup -O BACK-FILE ${device} | tail -1)
+parted -m ${device} unit B print
+root_offset=$(parted -m ${device} unit B print | awk -F '[B:]' '/1:/{ print $2 }')
+efi_offset=$( parted -m ${device} unit B print | awk -F '[B:]' '/15:/{ print $2 }')
+device_root=$(losetup -O NAME,OFFSET -j ${file} | grep ${root_offset} | cut -d ' ' -f 1)
+device_efi=$(losetup -O NAME,OFFSET -j ${file} | grep ${efi_offset} | cut -d ' ' -f 1)
+
+echo root=${device_root} efi=${device_efi}
+
+mount -o noatime -o "$options" "$device_root" "$FAI_ROOT"
+if ifclass -o AMD64 ARM64 RISCV64; then
+ mkdir -p "${FAI_ROOT}/boot/efi"
+ mount -o noatime -o "$options" "$device_efi" "${FAI_ROOT}/boot/efi"
+fi
+
+mount
diff --git a/build/debian/fai_config/hooks/partition.ARM64 b/build/debian/fai_config/hooks/partition.ARM64
new file mode 100755
index 0000000..56f239c
--- /dev/null
+++ b/build/debian/fai_config/hooks/partition.ARM64
@@ -0,0 +1,46 @@
+#!/bin/sh
+set -eu
+touch $LOGDIR/skip.partition
+
+set -- $disklist
+device=/dev/$1
+
+wait_for_device() {
+ for s in $(seq 10); do
+ if [ -e "$1" ]; then
+ break
+ fi
+ sleep 1
+ done
+}
+
+sfdisk "$device" << EOF
+label: gpt
+unit: sectors
+
+# EFI system
+p15 : start=2048, size=260096, type="EFI System", uuid=${PARTUUID_ESP}
+# Linux
+p1 : start=262144, type="Linux root (ARM-64)", uuid=${PARTUUID_ROOT}
+EOF
+
+file=$(losetup -O BACK-FILE ${device} | tail -1)
+
+root_offset=$(parted -m ${device} unit B print | awk -F '[B:]' '/1:/{ print $2 }')
+root_size=$( parted -m ${device} unit B print | awk -F '[B:]' '/1:/{ print $6 }')
+efi_offset=$( parted -m ${device} unit B print | awk -F '[B:]' '/15:/{ print $2 }')
+efi_size=$( parted -m ${device} unit B print | awk -F '[B:]' '/15:/{ print $6 }')
+device_root=$(losetup -o ${root_offset} --sizelimit ${root_size} --show -f ${file})
+device_efi=$(losetup -o ${efi_offset} --sizelimit ${efi_size} --show -f ${file})
+
+losetup -a -l
+parted ${device} unit B print
+
+partprobe "$device"
+
+wait_for_device "$device_root"
+mkfs.ext4 -U "$FSUUID_ROOT" "$device_root"
+tune2fs -c 0 -i 0 "$device_root"
+
+wait_for_device "$device_efi"
+mkfs.vfat "$device_efi"
diff --git a/build/debian/fai_config/package_config/AVF b/build/debian/fai_config/package_config/AVF
new file mode 100644
index 0000000..7d86d41
--- /dev/null
+++ b/build/debian/fai_config/package_config/AVF
@@ -0,0 +1,4 @@
+PACKAGES install
+
+# Just for testing
+tree
diff --git a/build/debian/fai_config/scripts/AVF/10-systemd b/build/debian/fai_config/scripts/AVF/10-systemd
new file mode 100755
index 0000000..e04a562
--- /dev/null
+++ b/build/debian/fai_config/scripts/AVF/10-systemd
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+chmod +x $target/usr/local/bin/ttyd
+chmod +x $target/usr/local/bin/vsock.py
+ln -s /etc/systemd/system/ttyd.service $target/etc/systemd/system/multi-user.target.wants/ttyd.service
+ln -s /etc/systemd/system/vsockip.service $target/etc/systemd/system/multi-user.target.wants/vsockip.service
\ No newline at end of file
diff --git a/build/debian/image.yaml b/build/debian/image.yaml
new file mode 100644
index 0000000..eb42a07
--- /dev/null
+++ b/build/debian/image.yaml
@@ -0,0 +1,60 @@
+# After modifications, please call:
+# "python3 -m debian_cloud_images.cli.generate_ci .gitlab/ci/generated.yml"
+---
+apiVersion: cloud.debian.org/v1alpha1
+kind: ImageConfig
+
+archs:
+- name: amd64
+ azureName: X64
+ ociArch: amd64
+ faiClasses: [AMD64]
+- name: arm64
+ azureName: Arm64
+ ociArch: arm64
+ faiClasses: [ARM64]
+- name: ppc64el
+ faiClasses: [PPC64EL]
+ ociArch: ppc64le
+- name: riscv64
+ faiClasses: [RISCV64]
+ ociArch: riscv64
+
+releases:
+- name: bookworm
+ basename: bookworm
+ id: '12'
+ baseid: '12'
+ faiClasses: [BOOKWORM, LINUX_VERSION_BASE, EXTRAS]
+ matches:
+ - matchArches: [amd64, arm64, ppc64el]
+- name: bookworm-backports
+ basename: bookworm-backports
+ id: 12-backports
+ baseid: '12'
+ faiClasses: [BOOKWORM, LINUX_VERSION_BACKPORTS, EXTRAS]
+ matches:
+ - matchArches: [amd64, arm64, ppc64el]
+- name: trixie
+ basename: trixie
+ id: '13'
+ baseid: '13'
+ faiClasses: [TRIXIE, LINUX_VERSION_BASE, EXTRAS]
+ matches:
+ - matchArches: [amd64, arm64, ppc64el]
+
+vendors:
+- name: nocloud
+ faiClasses: [SYSTEM_BOOT, NOCLOUD, LINUX_VARIANT_BASE, TIME_SYSTEMD, AVF]
+ size: 2
+
+types:
+- name: dev
+ faiClasses: [TYPE_DEV]
+ outputName: 'debian-{release}-{vendor}-{arch}-{build_type}-{build_id}-{version}'
+ outputVersion: '{version}'
+ outputVersionAzure: '0.0.{version!s}'
+- name: official
+ outputName: 'debian-{release}-{vendor}-{arch}-{build_type}-{version}'
+ outputVersion: '{date}-{version}'
+ outputVersionAzure: '0.{date!s}.{version!s}'
diff --git a/build/debian/kokoro/build.sh b/build/debian/kokoro/build.sh
new file mode 100644
index 0000000..cf563ef
--- /dev/null
+++ b/build/debian/kokoro/build.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -e
+
+cd "${KOKORO_ARTIFACTS_DIR}/git/avf/build/debian/"
+sudo ./build.sh
diff --git a/build/debian/kokoro/continuous.cfg b/build/debian/kokoro/continuous.cfg
new file mode 100644
index 0000000..018812c
--- /dev/null
+++ b/build/debian/kokoro/continuous.cfg
@@ -0,0 +1,12 @@
+# -*- protobuffer -*-
+# proto-file: google3/devtools/kokoro/config/proto/build.proto
+# proto-message: BuildConfig
+
+# Location of the bash script. Should have value <git_on_borg_scm.name>/<path_from_repository_root>.
+# git_on_borg_scm.name is specified in the job configuration (next section).
+build_file: "avf/build/debian/kokoro/build.sh"
+container_properties {
+ docker_image: "us-central1-docker.pkg.dev/kokoro-container-bakery/kokoro/ubuntu/ubuntu2204/full:next"
+ docker_privileged: true
+ docker_user: "root"
+}
diff --git a/docs/device_assignment.md b/docs/device_assignment.md
index 4b2296c..6011d8f 100644
--- a/docs/device_assignment.md
+++ b/docs/device_assignment.md
@@ -205,6 +205,18 @@
* `<sysfs_path>`: Sysfs path of the device in host, used to bind to the VFIO
driver. Must be non-empty and unique in the XML.
+### List support assignable devices
+
+In order to query list of the devices that can be assigned to a pVM, run the
+following command:
+
+```bash
+adb shell /apex/com.android.virt/bin/vm info
+```
+
+All supported assignable devices will be located under the "Assignable devices:"
+section of the output.
+
## Boot with VM DTBO
Bootloader should provide VM DTBO to both Android and pvmfw.
diff --git a/docs/img/rkpvm-dice-chain.png b/docs/img/rkpvm-dice-chain.png
new file mode 100644
index 0000000..6847f7f
--- /dev/null
+++ b/docs/img/rkpvm-dice-chain.png
Binary files differ
diff --git a/docs/pvm_dice_chain.md b/docs/pvm_dice_chain.md
index 11cdb6f..67d1f28 100644
--- a/docs/pvm_dice_chain.md
+++ b/docs/pvm_dice_chain.md
@@ -6,11 +6,13 @@
![][pvm-dice-chain-img]
-The full RKP VM DICE chain, starting from `UDS_Pub` rooted in ROM, is
-sent to the RKP server during [pVM remote attestation][vm-attestation].
+The full [RKP VM DICE chain][rkpvm-dice-chain], starting from `UDS_Pub`
+rooted in ROM, is sent to the RKP server during
+[pVM remote attestation][vm-attestation].
[vm-attestation]: vm_remote_attestation.md
[pvm-dice-chain-img]: img/pvm-dice.png
+[rkpvm-dice-chain]: vm_remote_attestation.md#rkp-vm-marker
## Key derivation
diff --git a/docs/vm_remote_attestation.md b/docs/vm_remote_attestation.md
index 79f44b9..ee20591 100644
--- a/docs/vm_remote_attestation.md
+++ b/docs/vm_remote_attestation.md
@@ -46,17 +46,17 @@
spec.
[open-dice]: https://android.googlesource.com/platform/external/open-dice/+/main/docs/android.md
-[rkpvm-marker]: https://android.googlesource.com/platform/external/open-dice/+/main/docs/android.md#Configuration-descriptor
-[rkp-hal]: https://android.googlesource.com/platform/hardware/interfaces/+/main/security/rkp/README.md
### pVM attestation
Once the RKP VM is successfully attested, it acts as a trusted platform to
attest pVMs. Leveraging its trusted status, the RKP VM validates the integrity
-of each pVM's DICE chain by comparing it against its own DICE chain. This
-validation process ensures that the pVMs are running in the expected VM
-environment and certifies the payload executed within each pVM. Currently, only
-Microdroid VMs are supported.
+of each [pVM DICE chain][pvm-dice-chain] by comparing it against its own DICE
+chain. This validation process ensures that the pVMs are running in the expected
+VM environment and certifies the payload executed within each pVM. Currently,
+only Microdroid VMs are supported.
+
+[pvm-dice-chain]: ./pvm_dice_chain.md
## API
@@ -113,13 +113,37 @@
## To Support It
-VM remote attestation is a strongly recommended feature from Android V. To support
-it, you only need to provide a valid VM DICE chain satisfying the following
-requirements:
+VM remote attestation is a strongly recommended feature from Android V. To
+support it, you only need to provide a valid VM DICE chain satisfying the
+following requirements:
-- The DICE chain must have a UDS-rooted public key registered at the RKP factory.
-- The DICE chain should have RKP VM markers that help identify RKP VM as required
- by the [remote provisioning HAL][rkp-hal-markers].
+- The DICE chain must have a UDS-rooted public key registered at the RKP
+ factory.
+- The DICE chain must use [RKP VM markers][rkpvm-marker] to help identify the
+ RKP VM as required by the [remote provisioning HAL][rkp-hal].
+
+### RKP VM marker
+
+To support VM remote attestation, vendors must include an RKP VM marker in their
+DICE certificates. This marker should be present from the early boot stage
+within the TEE and continue through to the last DICE certificate before
+[pvmfw][pvmfw] takes over.
+
+![RKP VM DICE chain][rkpvm-dice-chain]
+
+Pvmfw will add an RKP VM marker when it's launching an RKP VM. The __continuous
+presence__ of this marker throughout the chain allows the RKP server to clearly
+identify legitimate RKP VM DICE chains.
+
+This mechanism also serves as a security measure. If an attacker tries to launch
+a malicious guest OS or payload, their DICE chain will be rejected by the RKP
+server because it will lack the RKP VM marker that pvmfw would have added in a
+genuine RKP VM boot process.
+
+[pvmfw]: ../guest/pvmfw/README.md
+[rkpvm-dice-chain]: img/rkpvm-dice-chain.png
+
+## To Disable It
The feature is enabled by default. To disable it, you have two options:
@@ -133,4 +157,5 @@
If you don't set any of these variables, VM remote attestation will be enabled
by default.
-[rkp-hal-markers]: https://android.googlesource.com/platform/hardware/interfaces/+/main/security/rkp/README.md#hal
+[rkpvm-marker]: https://pigweed.googlesource.com/open-dice/+/HEAD/docs/android.md#configuration-descriptor
+[rkp-hal]: https://android.googlesource.com/platform/hardware/interfaces/+/main/security/rkp/README.md#hal
diff --git a/guest/authfs_service/src/main.rs b/guest/authfs_service/src/main.rs
index 97e684d..be0f1b2 100644
--- a/guest/authfs_service/src/main.rs
+++ b/guest/authfs_service/src/main.rs
@@ -28,7 +28,6 @@
use rustutils::sockets::android_get_control_socket;
use std::ffi::OsString;
use std::fs::{create_dir, read_dir, remove_dir_all, remove_file};
-use std::os::unix::io::{FromRawFd, OwnedFd};
use std::sync::atomic::{AtomicUsize, Ordering};
use authfs_aidl_interface::aidl::com::android::virt::fs::AuthFsConfig::AuthFsConfig;
@@ -108,27 +107,11 @@
Ok(())
}
-/// Prepares a socket file descriptor for the authfs service.
-///
-/// # Safety requirement
-///
-/// The caller must ensure that this function is the only place that claims ownership
-/// of the file descriptor and it is called only once.
-unsafe fn prepare_authfs_service_socket() -> Result<OwnedFd> {
- let raw_fd = android_get_control_socket(AUTHFS_SERVICE_SOCKET_NAME)?;
-
- // Creating OwnedFd for stdio FDs is not safe.
- if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
- bail!("File descriptor {raw_fd} is standard I/O descriptor");
- }
- // SAFETY: Initializing OwnedFd for a RawFd created by the init.
- // We checked that the integer value corresponds to a valid FD and that the caller
- // ensures that this is the only place to claim its ownership.
- Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
-}
-
#[allow(clippy::eq_op)]
fn try_main() -> Result<()> {
+ // SAFETY: nobody has taken ownership of the inherited FDs yet.
+ unsafe { rustutils::inherited_fd::init_once()? };
+
let debuggable = env!("TARGET_BUILD_VARIANT") != "user";
let log_level = if debuggable { log::LevelFilter::Trace } else { log::LevelFilter::Info };
android_logger::init_once(
@@ -137,8 +120,7 @@
clean_up_working_directory()?;
- // SAFETY: This is the only place we take the ownership of the fd of the authfs service.
- let socket_fd = unsafe { prepare_authfs_service_socket()? };
+ let socket_fd = android_get_control_socket(AUTHFS_SERVICE_SOCKET_NAME)?;
let service = AuthFsService::new_binder(debuggable).as_binder();
debug!("{} is starting as a rpc service.", AUTHFS_SERVICE_SOCKET_NAME);
let server = RpcServer::new_bound_socket(service, socket_fd)?;
diff --git a/guest/microdroid_manager/src/main.rs b/guest/microdroid_manager/src/main.rs
index 7352a2c..fa089fa 100644
--- a/guest/microdroid_manager/src/main.rs
+++ b/guest/microdroid_manager/src/main.rs
@@ -56,7 +56,7 @@
use std::ffi::CString;
use std::fs::{self, create_dir, File, OpenOptions};
use std::io::{Read, Write};
-use std::os::unix::io::{FromRawFd, OwnedFd};
+use std::os::unix::io::OwnedFd;
use std::os::unix::process::CommandExt;
use std::os::unix::process::ExitStatusExt;
use std::path::Path;
@@ -170,6 +170,9 @@
}
fn main() -> Result<()> {
+ // SAFETY: nobody has taken ownership of the inherited FDs yet.
+ unsafe { rustutils::inherited_fd::init_once()? };
+
// If debuggable, print full backtrace to console log with stdio_to_kmsg
if is_debuggable()? {
env::set_var("RUST_BACKTRACE", "full");
@@ -199,13 +202,7 @@
);
info!("started.");
- // SAFETY: This is the only place we take the ownership of the fd of the vm payload service.
- //
- // To ensure that the CLOEXEC flag is set on the file descriptor as early as possible,
- // it is necessary to fetch the socket corresponding to vm_payload_service at the
- // very beginning, as android_get_control_socket() sets the CLOEXEC flag on the file
- // descriptor.
- let vm_payload_service_fd = unsafe { prepare_vm_payload_service_socket()? };
+ let vm_payload_service_fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
load_crashkernel_if_supported().context("Failed to load crashkernel")?;
@@ -486,25 +483,6 @@
.context("Could not connect to IVirtualMachineService")
}
-/// Prepares a socket file descriptor for the vm payload service.
-///
-/// # Safety
-///
-/// The caller must ensure that this function is the only place that claims ownership
-/// of the file descriptor and it is called only once.
-unsafe fn prepare_vm_payload_service_socket() -> Result<OwnedFd> {
- let raw_fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
-
- // Creating OwnedFd for stdio FDs is not safe.
- if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
- bail!("File descriptor {raw_fd} is standard I/O descriptor");
- }
- // SAFETY: Initializing OwnedFd for a RawFd created by the init.
- // We checked that the integer value corresponds to a valid FD and that the caller
- // ensures that this is the only place to claim its ownership.
- Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
-}
-
fn is_strict_boot() -> bool {
Path::new(AVF_STRICT_BOOT).exists()
}
@@ -654,7 +632,7 @@
if requested {
let status = Command::new("/system/bin/kexec_load").status()?;
if !status.success() {
- return Err(anyhow!("Failed to load crashkernel: {:?}", status));
+ return Err(anyhow!("Failed to load crashkernel: {status}"));
}
info!("ramdump is loaded: debuggable={debuggable}, ramdump={ramdump}");
}
diff --git a/guest/pvmfw/Android.bp b/guest/pvmfw/Android.bp
index 144e81e..b502af6 100644
--- a/guest/pvmfw/Android.bp
+++ b/guest/pvmfw/Android.bp
@@ -13,7 +13,6 @@
rustlibs: [
"libaarch64_paging",
"libbssl_avf_nostd",
- "libbssl_sys_nostd",
"libcbor_util_nostd",
"libciborium_nostd",
"libciborium_io_nostd",
diff --git a/guest/pvmfw/src/entry.rs b/guest/pvmfw/src/entry.rs
index 8f9340b..945ad6a 100644
--- a/guest/pvmfw/src/entry.rs
+++ b/guest/pvmfw/src/entry.rs
@@ -17,7 +17,6 @@
use crate::config;
use crate::fdt;
use crate::memory;
-use bssl_sys::CRYPTO_library_init;
use core::arch::asm;
use core::mem::{drop, size_of};
use core::num::NonZeroUsize;
@@ -216,12 +215,6 @@
// - only access non-pvmfw memory once (and while) it has been mapped
log::set_max_level(LevelFilter::Info);
- // TODO(https://crbug.com/boringssl/35): Remove this init when BoringSSL can handle this
- // internally.
- // SAFETY: Configures the internal state of the library - may be called multiple times.
- unsafe {
- CRYPTO_library_init();
- }
let page_table = memory::init_page_table().map_err(|e| {
error!("Failed to set up the dynamic page tables: {e}");
diff --git a/guest/rialto/Android.bp b/guest/rialto/Android.bp
index b26a1c4..4c18bf9 100644
--- a/guest/rialto/Android.bp
+++ b/guest/rialto/Android.bp
@@ -10,7 +10,6 @@
rustlibs: [
"libaarch64_paging",
"libbssl_avf_nostd",
- "libbssl_sys_nostd",
"libciborium_io_nostd",
"libciborium_nostd",
"libcstr",
diff --git a/guest/rialto/src/main.rs b/guest/rialto/src/main.rs
index 7de8718..9265775 100644
--- a/guest/rialto/src/main.rs
+++ b/guest/rialto/src/main.rs
@@ -28,7 +28,6 @@
use crate::error::{Error, Result};
use crate::fdt::{read_dice_range_from, read_is_strict_boot, read_vendor_hashtree_root_digest};
use alloc::boxed::Box;
-use bssl_sys::CRYPTO_library_init;
use ciborium_io::Write;
use core::num::NonZeroUsize;
use core::slice;
@@ -133,12 +132,6 @@
})?;
}
- // Initializes the crypto library before any crypto operations and after the heap is
- // initialized.
- // SAFETY: It is safe to call this function multiple times and concurrently.
- unsafe {
- CRYPTO_library_init();
- }
let bcc_handover: Box<dyn DiceArtifacts> = match vm_type(fdt)? {
VmType::ProtectedVm => {
let dice_range = read_dice_range_from(fdt)?;
@@ -228,6 +221,28 @@
}
}
+/// Flushes data caches over the provided address range.
+///
+/// # Safety
+///
+/// The provided address and size must be to an address range that is valid for read and write
+/// (typically on the stack, .bss, .data, or provided BCC) from a single allocation
+/// (e.g. stack array).
+#[no_mangle]
+unsafe extern "C" fn DiceClearMemory(
+ _ctx: *mut core::ffi::c_void,
+ size: usize,
+ addr: *mut core::ffi::c_void,
+) {
+ use core::slice;
+ use vmbase::memory::flushed_zeroize;
+
+ // SAFETY: We require our caller to provide a valid range within a single object. The open-dice
+ // always calls this on individual stack-allocated arrays which ensures that.
+ let region = unsafe { slice::from_raw_parts_mut(addr as *mut u8, size) };
+ flushed_zeroize(region)
+}
+
generate_image_header!();
main!(main);
configure_heap!(SIZE_128KB * 2);
diff --git a/libs/dice/open_dice/Android.bp b/libs/dice/open_dice/Android.bp
index efe350f..d1129fb 100644
--- a/libs/dice/open_dice/Android.bp
+++ b/libs/dice/open_dice/Android.bp
@@ -22,7 +22,6 @@
"alloc",
],
whole_static_libs: [
- "libopen_dice_cbor",
"libcrypto_baremetal",
],
visibility: [
@@ -55,6 +54,7 @@
"//packages/modules/Virtualization:__subpackages__",
"//system/authgraph/tests:__subpackages__",
"//system/secretkeeper/client:__subpackages__",
+ "//system/software_defined_vehicle:__subpackages__",
],
apex_available: [
"//apex_available:platform",
diff --git a/libs/dice/sample_inputs/tests/api_test.rs b/libs/dice/sample_inputs/tests/api_test.rs
index 0823f16..d713168 100644
--- a/libs/dice/sample_inputs/tests/api_test.rs
+++ b/libs/dice/sample_inputs/tests/api_test.rs
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#![cfg_attr(not(feature = "std"), no_std)]
+
use anyhow::Result;
use diced_open_dice::{derive_cdi_leaf_priv, sign, DiceArtifacts};
use diced_sample_inputs::make_sample_bcc_and_cdis;
@@ -144,3 +146,21 @@
let public_key = chain.leaf().subject_public_key();
public_key.verify(&signature, MESSAGE)
}
+
+/// Flushes data caches over the provided address range in open-dice.
+///
+/// # Safety
+///
+/// The provided address and size must be to an address range that is valid for read and write
+/// (typically on the stack, .bss, .data, or provided BCC) from a single allocation
+/// (e.g. stack array).
+#[cfg(not(feature = "std"))]
+#[no_mangle]
+unsafe extern "C" fn DiceClearMemory(
+ _ctx: *mut core::ffi::c_void,
+ size: usize,
+ addr: *mut core::ffi::c_void,
+) {
+ // SAFETY: The caller ensures that the address and size are valid for write.
+ unsafe { core::ptr::write_bytes(addr as *mut u8, 0, size) };
+}
diff --git a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
index cb21ccf..de1b081 100644
--- a/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/libs/framework-virtualization/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -78,7 +78,8 @@
private static final String TAG = "VirtualMachineConfig";
private static String[] EMPTY_STRING_ARRAY = {};
- private static final String U_BOOT_PREBUILT_PATH = "/apex/com.android.virt/etc/u-boot.bin";
+ private static final String U_BOOT_PREBUILT_PATH_ARM = "/apex/com.android.virt/etc/u-boot.bin";
+ private static final String U_BOOT_PREBUILT_PATH_X86 = "/apex/com.android.virt/etc/u-boot.rom";
// These define the schema of the config file persisted on disk.
// Please bump up the version number when adding a new key.
@@ -668,7 +669,11 @@
.orElse(null);
if (config.kernel == null && config.bootloader == null) {
- config.bootloader = openOrNull(new File(U_BOOT_PREBUILT_PATH), MODE_READ_ONLY);
+ if (Arrays.stream(Build.SUPPORTED_ABIS).anyMatch("x86_64"::equals)) {
+ config.bootloader = openOrNull(new File(U_BOOT_PREBUILT_PATH_X86), MODE_READ_ONLY);
+ } else {
+ config.bootloader = openOrNull(new File(U_BOOT_PREBUILT_PATH_ARM), MODE_READ_ONLY);
+ }
}
config.params =
diff --git a/libs/libvmclient/Android.bp b/libs/libvmclient/Android.bp
index 5bd59da..d318d0e 100644
--- a/libs/libvmclient/Android.bp
+++ b/libs/libvmclient/Android.bp
@@ -23,6 +23,7 @@
"com.android.compos",
"com.android.microfuchsia",
"com.android.virt",
+ "//apex_available:platform",
],
}
diff --git a/libs/libvmclient/src/lib.rs b/libs/libvmclient/src/lib.rs
index bc9d683..ce7d5a5 100644
--- a/libs/libvmclient/src/lib.rs
+++ b/libs/libvmclient/src/lib.rs
@@ -55,6 +55,7 @@
time::Duration,
};
+const EARLY_VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/early_virtmgr";
const VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/virtmgr";
const VIRTMGR_THREADS: usize = 2;
@@ -122,10 +123,20 @@
/// Spawns a new instance of virtmgr, a child process that will host
/// the VirtualizationService AIDL service.
pub fn new() -> Result<VirtualizationService, io::Error> {
+ Self::new_with_path(VIRTMGR_PATH)
+ }
+
+ /// Spawns a new instance of early_virtmgr, a child process that will host
+ /// the VirtualizationService AIDL service for early VMs.
+ pub fn new_early() -> Result<VirtualizationService, io::Error> {
+ Self::new_with_path(EARLY_VIRTMGR_PATH)
+ }
+
+ fn new_with_path(virtmgr_path: &str) -> Result<VirtualizationService, io::Error> {
let (wait_fd, ready_fd) = posix_pipe()?;
let (client_fd, server_fd) = posix_socketpair()?;
- let mut command = Command::new(VIRTMGR_PATH);
+ let mut command = Command::new(virtmgr_path);
// Can't use BorrowedFd as it doesn't implement Display
command.arg("--rpc-server-fd").arg(format!("{}", server_fd.as_raw_fd()));
command.arg("--ready-fd").arg(format!("{}", ready_fd.as_raw_fd()));
diff --git a/libs/vm_launcher_lib/Android.bp b/libs/vm_launcher_lib/Android.bp
index 8591c8d..cb6fc9e 100644
--- a/libs/vm_launcher_lib/Android.bp
+++ b/libs/vm_launcher_lib/Android.bp
@@ -9,5 +9,12 @@
"//apex_available:platform",
"com.android.virt",
],
- sdk_version: "system_current",
+ platform_apis: true,
+ static_libs: [
+ "gson",
+ ],
+ libs: [
+ "framework-virtualization.impl",
+ "framework-annotations-lib",
+ ],
}
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/ConfigJson.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
similarity index 100%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/ConfigJson.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Logger.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Logger.java
similarity index 100%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Logger.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Logger.java
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Runner.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Runner.java
similarity index 98%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Runner.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Runner.java
index a5f58fe..9b97fee 100644
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/Runner.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/Runner.java
@@ -30,7 +30,7 @@
/** Utility class for creating a VM and waiting for it to finish. */
class Runner {
- private static final String TAG = MainActivity.TAG;
+ private static final String TAG = Runner.class.getSimpleName();
private final VirtualMachine mVirtualMachine;
private final Callback mCallback;
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmLauncherService.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
similarity index 100%
rename from android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmLauncherService.java
rename to libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherService.java
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java b/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java
index 3814cdd..8604553 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/KvmHypTracer.java
@@ -78,9 +78,9 @@
/** This class provides utilities to interact with the hyp tracing subsystem */
public final class KvmHypTracer {
- private static final String HYP_TRACING_ROOT = "/sys/kernel/tracing/hyp/";
private static final int DEFAULT_BUF_SIZE_KB = 4 * 1024;
+ private final String mHypTracingRoot;
private final CommandRunner mRunner;
private final ITestDevice mDevice;
private final int mNrCpus;
@@ -88,17 +88,41 @@
private final ArrayList<File> mTraces;
- private void setNode(String node, int val) throws Exception {
- mRunner.run("echo " + val + " > " + HYP_TRACING_ROOT + node);
+ private static String getHypTracingRoot(ITestDevice device) throws Exception {
+ String legacy = "/sys/kernel/tracing/hyp/";
+ String path = "/sys/kernel/tracing/hypervisor/";
+
+ if (device.doesFileExist(path)) {
+ return path;
+ }
+
+ if (device.doesFileExist(legacy)) {
+ return legacy;
+ }
+
+ throw new Exception("Hypervisor tracing not found");
}
- private static String eventDir(String event) {
- return "events/hyp/" + event + "/";
+ private static String getHypEventsDir(String root) {
+ if (root.endsWith("/hypervisor/"))
+ return "events/hypervisor/";
+
+ return "events/hyp/";
}
public static boolean isSupported(ITestDevice device, String[] events) throws Exception {
- for (String event : events) {
- if (!device.doesFileExist(HYP_TRACING_ROOT + eventDir(event) + "/enable")) return false;
+ String dir;
+
+ try {
+ dir = getHypTracingRoot(device);
+ dir += getHypEventsDir(dir);
+ } catch (Exception e) {
+ return false;
+ }
+
+ for (String event: events) {
+ if (!device.doesFileExist(dir + event + "/enable"))
+ return false;
}
return true;
}
@@ -108,6 +132,7 @@
.that(isSupported(device, events))
.isTrue();
+ mHypTracingRoot = getHypTracingRoot(device);
mDevice = device;
mRunner = new CommandRunner(mDevice);
mTraces = new ArrayList<File>();
@@ -115,17 +140,25 @@
mHypEvents = events;
}
+ private void setNode(String node, int val) throws Exception {
+ mRunner.run("echo " + val + " > " + mHypTracingRoot + node);
+ }
+
public String run(String payload_cmd) throws Exception {
mTraces.clear();
setNode("tracing_on", 0);
- mRunner.run("echo 0 | tee " + HYP_TRACING_ROOT + "events/*/*/enable");
+ mRunner.run("echo 0 | tee " + mHypTracingRoot + "events/*/*/enable");
setNode("buffer_size_kb", DEFAULT_BUF_SIZE_KB);
- for (String event : mHypEvents) setNode(eventDir(event) + "/enable", 1);
+
+ for (String event: mHypEvents) {
+ setNode(getHypEventsDir(mHypTracingRoot) + event + "/enable", 1);
+ }
+
setNode("trace", 0);
/* Cat each per-cpu trace_pipe in its own tmp file in the background */
- String cmd = "cd " + HYP_TRACING_ROOT + ";";
+ String cmd = "cd " + mHypTracingRoot + ";";
String trace_pipes[] = new String[mNrCpus];
for (int i = 0; i < mNrCpus; i++) {
trace_pipes[i] = mRunner.run("mktemp -t trace_pipe.cpu" + i + ".XXXXXXXXXX");