Merge "Only add vendor_a to microdroid_super if vendor modules flag is disabled" into main
diff --git a/Android.bp b/Android.bp
index 550a6be..9c17c7f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -22,6 +22,7 @@
module_type: "rust_defaults",
config_namespace: "ANDROID",
bool_variables: [
+ "release_avf_enable_device_assignment",
"release_avf_enable_dice_changes",
"release_avf_enable_llpvm_changes",
"release_avf_enable_multi_tenant_microdroid_vm",
@@ -36,6 +37,9 @@
avf_flag_aware_rust_defaults {
name: "avf_build_flags_rust",
soong_config_variables: {
+ release_avf_enable_device_assignment: {
+ cfgs: ["device_assignment"],
+ },
release_avf_enable_dice_changes: {
cfgs: ["dice_changes"],
},
diff --git a/apex/Android.bp b/apex/Android.bp
index b7fd67e..e04dbd2 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -71,8 +71,14 @@
name: "avf_flag_aware_apex_defaults",
module_type: "apex_defaults",
config_namespace: "ANDROID",
- bool_variables: ["release_avf_enable_vendor_modules"],
- properties: ["prebuilts"],
+ bool_variables: [
+ "release_avf_enable_device_assignment",
+ "release_avf_enable_vendor_modules",
+ ],
+ properties: [
+ "arch",
+ "prebuilts",
+ ],
}
avf_flag_aware_apex_defaults {
@@ -87,7 +93,6 @@
arm64: {
binaries: [
"crosvm",
- "vfio_handler",
"virtmgr",
"virtualizationservice",
],
@@ -96,7 +101,6 @@
x86_64: {
binaries: [
"crosvm",
- "vfio_handler",
"virtmgr",
"virtualizationservice",
],
@@ -124,11 +128,25 @@
"EmptyPayloadApp",
],
soong_config_variables: {
+ release_avf_enable_device_assignment: {
+ prebuilts: [
+ "com.android.virt.vfio_handler.rc",
+ ],
+ arch: {
+ arm64: {
+ binaries: ["vfio_handler"],
+ },
+ x86_64: {
+ binaries: ["vfio_handler"],
+ },
+ },
+ },
release_avf_enable_vendor_modules: {
prebuilts: [
"microdroid_gki_initrd_debuggable",
"microdroid_gki_initrd_normal",
"microdroid_gki_kernel",
+ "microdroid_gki.json",
],
},
},
@@ -158,6 +176,13 @@
installable: false,
}
+prebuilt_etc {
+ name: "com.android.virt.vfio_handler.rc",
+ src: "vfio_handler.rc",
+ filename: "vfio_handler.rc",
+ installable: false,
+}
+
sh_binary_host {
name: "prepare_device_vfio",
src: "prepare_device_vfio.sh",
diff --git a/apex/vfio_handler.rc b/apex/vfio_handler.rc
new file mode 100644
index 0000000..419acef
--- /dev/null
+++ b/apex/vfio_handler.rc
@@ -0,0 +1,20 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+service vfio_handler /apex/com.android.virt/bin/vfio_handler
+ user root
+ group system
+ interface aidl android.system.virtualizationservice_internal.IVfioHandler
+ disabled
+ oneshot
diff --git a/apex/virtualizationservice.rc b/apex/virtualizationservice.rc
index 8283594..02b2081 100644
--- a/apex/virtualizationservice.rc
+++ b/apex/virtualizationservice.rc
@@ -19,10 +19,3 @@
interface aidl android.system.virtualizationservice
disabled
oneshot
-
-service vfio_handler /apex/com.android.virt/bin/vfio_handler
- user root
- group system
- interface aidl android.system.virtualizationservice_internal.IVfioHandler
- disabled
- oneshot
diff --git a/libs/cstr/Android.bp b/libs/cstr/Android.bp
new file mode 100644
index 0000000..4ea87df
--- /dev/null
+++ b/libs/cstr/Android.bp
@@ -0,0 +1,36 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library_rlib {
+ name: "libcstr",
+ crate_name: "cstr",
+ defaults: ["avf_build_flags_rust"],
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ host_supported: true,
+ prefer_rlib: true,
+ target: {
+ android: {
+ no_stdlibs: true,
+ stdlibs: [
+ "libcompiler_builtins.rust_sysroot",
+ "libcore.rust_sysroot",
+ ],
+ },
+ },
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+}
+
+rust_test {
+ name: "libcstr.tests",
+ crate_name: "libcstr_test",
+ defaults: ["avf_build_flags_rust"],
+ srcs: ["src/lib.rs"],
+ test_suites: ["general-tests"],
+ prefer_rlib: true,
+ rustlibs: ["libcstr"],
+}
diff --git a/libs/cstr/src/lib.rs b/libs/cstr/src/lib.rs
new file mode 100644
index 0000000..ddf20fc
--- /dev/null
+++ b/libs/cstr/src/lib.rs
@@ -0,0 +1,50 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Provide a safe const-compatible no_std macro for readable &'static CStr.
+
+#![no_std]
+
+/// Create &CStr out of &str literal
+#[macro_export]
+macro_rules! cstr {
+ ($str:literal) => {{
+ const S: &str = concat!($str, "\0");
+ const C: &::core::ffi::CStr = match ::core::ffi::CStr::from_bytes_with_nul(S.as_bytes()) {
+ Ok(v) => v,
+ Err(_) => panic!("string contains interior NUL"),
+ };
+ C
+ }};
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::ffi::CString;
+
+ #[test]
+ fn valid_input_string() {
+ let expected = CString::new("aaa").unwrap();
+ assert_eq!(cstr!("aaa"), expected.as_c_str());
+ }
+
+ #[test]
+ fn valid_empty_string() {
+ let expected = CString::new("").unwrap();
+ assert_eq!(cstr!(""), expected.as_c_str());
+ }
+
+ // As cstr!() panics at compile time, tests covering invalid inputs fail to compile!
+}
diff --git a/libs/libfdt/Android.bp b/libs/libfdt/Android.bp
index b889ee5..5920d5d 100644
--- a/libs/libfdt/Android.bp
+++ b/libs/libfdt/Android.bp
@@ -37,6 +37,7 @@
"libcore.rust_sysroot",
],
rustlibs: [
+ "libcstr",
"liblibfdt_bindgen",
"libzerocopy_nostd",
],
@@ -61,6 +62,7 @@
],
prefer_rlib: true,
rustlibs: [
+ "libcstr",
"liblibfdt",
],
}
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index b369390..b513649 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -31,15 +31,9 @@
use core::ops::Range;
use core::ptr;
use core::result;
+use cstr::cstr;
use zerocopy::AsBytes as _;
-// TODO(b/308694211): Use cstr!() from vmbase
-macro_rules! cstr {
- ($str:literal) => {{
- core::ffi::CStr::from_bytes_with_nul(concat!($str, "\0").as_bytes()).unwrap()
- }};
-}
-
/// Error type corresponding to libfdt error codes.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FdtError {
@@ -526,7 +520,7 @@
/// Phandle of a FDT node
#[repr(transparent)]
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct Phandle(u32);
impl Phandle {
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index d76b1a4..63cbdee 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -17,23 +17,12 @@
//! Integration tests of the library libfdt.
use core::ffi::CStr;
+use cstr::cstr;
use libfdt::{Fdt, FdtError, FdtNodeMut, Phandle};
use std::ffi::CString;
use std::fs;
use std::ops::Range;
-// TODO(b/308694211): Use cstr!() from vmbase
-macro_rules! cstr {
- ($str:literal) => {{
- const S: &str = concat!($str, "\0");
- const C: &::core::ffi::CStr = match ::core::ffi::CStr::from_bytes_with_nul(S.as_bytes()) {
- Ok(v) => v,
- Err(_) => panic!("string contains interior NUL"),
- };
- C
- }};
-}
-
const TEST_TREE_WITH_ONE_MEMORY_RANGE_PATH: &str = "data/test_tree_one_memory_range.dtb";
const TEST_TREE_WITH_MULTIPLE_MEMORY_RANGES_PATH: &str =
"data/test_tree_multiple_memory_ranges.dtb";
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index f134eef..c93cb4c 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -418,6 +418,11 @@
}
prebuilt_etc {
+ name: "microdroid_gki.json",
+ src: "microdroid_gki.json",
+}
+
+prebuilt_etc {
name: "microdroid_manifest",
src: "microdroid_manifest.xml",
filename: "manifest.xml",
diff --git a/microdroid/microdroid_gki.json b/microdroid/microdroid_gki.json
new file mode 100644
index 0000000..d7ba53e
--- /dev/null
+++ b/microdroid/microdroid_gki.json
@@ -0,0 +1,20 @@
+{
+ "kernel": "/apex/com.android.virt/etc/fs/microdroid_gki_kernel",
+ "disks": [
+ {
+ "partitions": [
+ {
+ "label": "vbmeta_a",
+ "path": "/apex/com.android.virt/etc/fs/microdroid_vbmeta.img"
+ },
+ {
+ "label": "super",
+ "path": "/apex/com.android.virt/etc/fs/microdroid_super.img"
+ }
+ ],
+ "writable": false
+ }
+ ],
+ "memory_mib": 256,
+ "platform_version": "~1.0"
+}
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index b7b5900..103619f 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -16,6 +16,7 @@
"libbssl_ffi_nostd",
"libciborium_nostd",
"libciborium_io_nostd",
+ "libcstr",
"libdiced_open_dice_nostd",
"libfdtpci",
"libhyp",
@@ -55,6 +56,7 @@
unit_test: true,
},
rustlibs: [
+ "libcstr",
"libzeroize",
],
}
@@ -80,6 +82,34 @@
out: ["test_pvmfw_devices_with_rng.dtb"],
}
+genrule {
+ name: "test_pvmfw_devices_with_rng_iommu",
+ defaults: ["dts_to_dtb"],
+ srcs: ["testdata/test_pvmfw_devices_with_rng_iommu.dts"],
+ out: ["test_pvmfw_devices_with_rng_iommu.dtb"],
+}
+
+genrule {
+ name: "test_pvmfw_devices_with_multiple_devices_iommus",
+ defaults: ["dts_to_dtb"],
+ srcs: ["testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts"],
+ out: ["test_pvmfw_devices_with_multiple_devices_iommus.dtb"],
+}
+
+genrule {
+ name: "test_pvmfw_devices_with_iommu_sharing",
+ defaults: ["dts_to_dtb"],
+ srcs: ["testdata/test_pvmfw_devices_with_iommu_sharing.dts"],
+ out: ["test_pvmfw_devices_with_iommu_sharing.dtb"],
+}
+
+genrule {
+ name: "test_pvmfw_devices_with_iommu_id_conflict",
+ defaults: ["dts_to_dtb"],
+ srcs: ["testdata/test_pvmfw_devices_with_iommu_id_conflict.dts"],
+ out: ["test_pvmfw_devices_with_iommu_id_conflict.dtb"],
+}
+
rust_test {
name: "libpvmfw.device_assignment.test",
srcs: ["src/device_assignment.rs"],
@@ -90,6 +120,7 @@
},
prefer_rlib: true,
rustlibs: [
+ "libcstr",
"liblibfdt",
"liblog_rust",
"libpvmfw_fdt_template",
@@ -98,6 +129,10 @@
":test_pvmfw_devices_vm_dtbo",
":test_pvmfw_devices_vm_dtbo_without_symbols",
":test_pvmfw_devices_with_rng",
+ ":test_pvmfw_devices_with_rng_iommu",
+ ":test_pvmfw_devices_with_multiple_devices_iommus",
+ ":test_pvmfw_devices_with_iommu_sharing",
+ ":test_pvmfw_devices_with_iommu_id_conflict",
],
// To use libpvmfw_fdt_template for testing
enabled: false,
diff --git a/pvmfw/src/bootargs.rs b/pvmfw/src/bootargs.rs
index a089a67..aacd8e0 100644
--- a/pvmfw/src/bootargs.rs
+++ b/pvmfw/src/bootargs.rs
@@ -108,19 +108,7 @@
#[cfg(test)]
mod tests {
use super::*;
-
- // TODO(b/308694211): Use cstr!() from vmbase
- macro_rules! cstr {
- ($str:literal) => {{
- const S: &str = concat!($str, "\0");
- const C: &::core::ffi::CStr = match ::core::ffi::CStr::from_bytes_with_nul(S.as_bytes())
- {
- Ok(v) => v,
- Err(_) => panic!("string contains interior NUL"),
- };
- C
- }};
- }
+ use cstr::cstr;
fn check(raw: &CStr, expected: Result<&[(&str, Option<&str>)], ()>) {
let actual = BootArgsIterator::new(raw);
diff --git a/pvmfw/src/crypto.rs b/pvmfw/src/crypto.rs
index 2b3d921..8f31553 100644
--- a/pvmfw/src/crypto.rs
+++ b/pvmfw/src/crypto.rs
@@ -33,7 +33,7 @@
use bssl_ffi::EVP_aead_aes_256_gcm_randnonce;
use bssl_ffi::EVP_AEAD;
use bssl_ffi::EVP_AEAD_CTX;
-use vmbase::cstr;
+use cstr::cstr;
#[derive(Debug)]
pub struct Error {
diff --git a/pvmfw/src/device_assignment.rs b/pvmfw/src/device_assignment.rs
index a92b418..3f84a8d 100644
--- a/pvmfw/src/device_assignment.rs
+++ b/pvmfw/src/device_assignment.rs
@@ -19,6 +19,7 @@
#[cfg(test)]
extern crate alloc;
+use alloc::collections::{BTreeMap, BTreeSet};
use alloc::ffi::CString;
use alloc::fmt;
use alloc::vec;
@@ -26,7 +27,7 @@
use core::ffi::CStr;
use core::iter::Iterator;
use core::mem;
-use libfdt::{Fdt, FdtError, FdtNode};
+use libfdt::{Fdt, FdtError, FdtNode, Phandle};
// TODO(b/308694211): Use cstr! from vmbase instead.
macro_rules! cstr {
@@ -52,8 +53,16 @@
InvalidSymbols,
/// Invalid <interrupts>
InvalidInterrupts,
+ /// Invalid <iommus>
+ InvalidIommus,
+ /// Too many pvIOMMU
+ TooManyPvIommu,
+ /// Duplicated pvIOMMU IDs exist
+ DuplicatedPvIommuIds,
/// Unsupported overlay target syntax. Only supports <target-path> with full path.
UnsupportedOverlayTarget,
+ /// Internal error
+ Internal,
/// Unexpected error from libfdt
UnexpectedFdtError(FdtError),
}
@@ -73,9 +82,18 @@
"Invalid property in /__symbols__. Must point to valid assignable device node."
),
Self::InvalidInterrupts => write!(f, "Invalid <interrupts>"),
+ Self::InvalidIommus => write!(f, "Invalid <iommus>"),
+ Self::TooManyPvIommu => write!(
+ f,
+ "Too many pvIOMMU node. Insufficient pre-populated pvIOMMUs in platform DT"
+ ),
+ Self::DuplicatedPvIommuIds => {
+ write!(f, "Duplicated pvIOMMU IDs exist. IDs must unique")
+ }
Self::UnsupportedOverlayTarget => {
write!(f, "Unsupported overlay target. Only supports 'target-path = \"/\"'")
}
+ Self::Internal => write!(f, "Internal error"),
Self::UnexpectedFdtError(e) => write!(f, "Unexpected Error from libfdt: {e}"),
}
}
@@ -169,6 +187,19 @@
}
}
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+struct PvIommu {
+ // ID from pvIOMMU node
+ id: u32,
+}
+
+impl PvIommu {
+ fn parse(node: &FdtNode) -> Result<Self> {
+ let id = node.getprop_u32(cstr!("id"))?.ok_or(DeviceAssignmentError::InvalidIommus)?;
+ Ok(Self { id })
+ }
+}
+
/// Assigned device information parsed from crosvm DT.
/// Keeps everything in the owned data because underlying FDT will be reused for platform DT.
#[derive(Debug, Eq, PartialEq)]
@@ -181,6 +212,8 @@
reg: Vec<u8>,
// <interrupts> property from the crosvm DT
interrupts: Vec<u8>,
+ // Parsed <iommus> property from the crosvm DT.
+ iommus: Vec<PvIommu>,
}
impl AssignedDeviceInfo {
@@ -199,41 +232,91 @@
Ok(node.getprop(cstr!("interrupts")).unwrap().unwrap().into())
}
- // TODO(b/277993056): Read and validate iommu
- fn parse(fdt: &Fdt, vm_dtbo: &VmDtbo, dtbo_node_path: &CStr) -> Result<Option<Self>> {
+ // TODO(b/277993056): Also validate /__local_fixups__ to ensure that <iommus> has phandle.
+ // TODO(b/277993056): Also keep vSID.
+ // TODO(b/277993056): Validate #iommu-cells values.
+ fn parse_iommus(node: &FdtNode, pviommus: &BTreeMap<Phandle, PvIommu>) -> Result<Vec<PvIommu>> {
+ let mut iommus = vec![];
+ let Some(cells) = node.getprop_cells(cstr!("iommus"))? else {
+ return Ok(iommus);
+ };
+ for cell in cells {
+ let phandle = Phandle::try_from(cell).or(Err(DeviceAssignmentError::InvalidIommus))?;
+ let pviommu = pviommus.get(&phandle).ok_or(DeviceAssignmentError::InvalidIommus)?;
+ iommus.push(*pviommu)
+ }
+ Ok(iommus)
+ }
+
+ fn parse(
+ fdt: &Fdt,
+ vm_dtbo: &VmDtbo,
+ dtbo_node_path: &CStr,
+ pviommus: &BTreeMap<Phandle, PvIommu>,
+ ) -> Result<Option<Self>> {
let node_path = vm_dtbo.locate_overlay_target_path(dtbo_node_path)?;
let Some(node) = fdt.node(&node_path)? else { return Ok(None) };
// TODO(b/277993056): Validate reg with HVC, and keep reg with FdtNode::reg()
let reg = node.getprop(cstr!("reg")).unwrap().unwrap();
-
let interrupts = Self::parse_interrupts(&node)?;
-
+ let iommus = Self::parse_iommus(&node, pviommus)?;
Ok(Some(Self {
node_path,
dtbo_node_path: dtbo_node_path.into(),
reg: reg.to_vec(),
- interrupts: interrupts.to_vec(),
+ interrupts,
+ iommus,
}))
}
- fn patch(&self, fdt: &mut Fdt) -> Result<()> {
+ fn patch(&self, fdt: &mut Fdt, pviommu_phandles: &BTreeMap<PvIommu, Phandle>) -> Result<()> {
let mut dst = fdt.node_mut(&self.node_path)?.unwrap();
dst.setprop(cstr!("reg"), &self.reg)?;
dst.setprop(cstr!("interrupts"), &self.interrupts)?;
- // TODO(b/277993056): Read and patch iommu
+
+ let iommus: Vec<u8> = self
+ .iommus
+ .iter()
+ .flat_map(|pviommu| {
+ let phandle = pviommu_phandles.get(pviommu).unwrap();
+ u32::from(*phandle).to_be_bytes()
+ })
+ .collect();
+ dst.setprop(cstr!("iommus"), &iommus)?;
+
Ok(())
}
}
#[derive(Debug, Default, Eq, PartialEq)]
pub struct DeviceAssignmentInfo {
+ pviommus: BTreeSet<PvIommu>,
assigned_devices: Vec<AssignedDeviceInfo>,
filtered_dtbo_paths: Vec<CString>,
}
impl DeviceAssignmentInfo {
+ const PVIOMMU_COMPATIBLE: &CStr = cstr!("pkvm,pviommu");
+
+ /// Parses pvIOMMUs in fdt
+ // Note: This will validate pvIOMMU ids' uniqueness, even when unassigned.
+ fn parse_pviommus(fdt: &Fdt) -> Result<BTreeMap<Phandle, PvIommu>> {
+ // TODO(b/277993056): Validated `<#iommu-cells>`.
+ let mut pviommus = BTreeMap::new();
+ for compatible in fdt.compatible_nodes(Self::PVIOMMU_COMPATIBLE)? {
+ let Some(phandle) = compatible.get_phandle()? else {
+ continue; // Skips unreachable pvIOMMU node
+ };
+ let pviommu = PvIommu::parse(&compatible)?;
+ if pviommus.insert(phandle, pviommu).is_some() {
+ return Err(FdtError::BadPhandle.into());
+ }
+ }
+ Ok(pviommus)
+ }
+
/// Parses fdt and vm_dtbo, and creates new DeviceAssignmentInfo
// TODO(b/277993056): Parse __local_fixups__
// TODO(b/277993056): Parse __fixups__
@@ -244,25 +327,32 @@
return Ok(None);
};
+ let pviommus = Self::parse_pviommus(fdt)?;
+ let unique_pviommus: BTreeSet<_> = pviommus.values().cloned().collect();
+ if pviommus.len() != unique_pviommus.len() {
+ return Err(DeviceAssignmentError::DuplicatedPvIommuIds);
+ }
+
let mut assigned_devices = vec![];
let mut filtered_dtbo_paths = vec![];
for symbol_prop in symbols_node.properties()? {
let symbol_prop_value = symbol_prop.value()?;
let dtbo_node_path = CStr::from_bytes_with_nul(symbol_prop_value)
.or(Err(DeviceAssignmentError::InvalidSymbols))?;
- let assigned_device = AssignedDeviceInfo::parse(fdt, vm_dtbo, dtbo_node_path)?;
+ let assigned_device =
+ AssignedDeviceInfo::parse(fdt, vm_dtbo, dtbo_node_path, &pviommus)?;
if let Some(assigned_device) = assigned_device {
assigned_devices.push(assigned_device);
} else {
filtered_dtbo_paths.push(dtbo_node_path.into());
}
}
- filtered_dtbo_paths.push(CString::new("/__symbols__").unwrap());
-
if assigned_devices.is_empty() {
return Ok(None);
}
- Ok(Some(Self { assigned_devices, filtered_dtbo_paths }))
+ filtered_dtbo_paths.push(CString::new("/__symbols__").unwrap());
+
+ Ok(Some(Self { pviommus: unique_pviommus, assigned_devices, filtered_dtbo_paths }))
}
/// Filters VM DTBO to only contain necessary information for booting pVM
@@ -290,16 +380,46 @@
for assigned_device in &self.assigned_devices {
let mut node = vm_dtbo.node_mut(&assigned_device.dtbo_node_path).unwrap().unwrap();
for prop in FILTERED_VM_DTBO_PROP {
- node.nop_property(prop)?;
+ match node.nop_property(prop) {
+ Err(FdtError::NotFound) => Ok(()), // allows not exists
+ other => other,
+ }?;
}
}
+
Ok(())
}
- pub fn patch(&self, fdt: &mut Fdt) -> Result<()> {
- for device in &self.assigned_devices {
- device.patch(fdt)?
+ fn patch_pviommus(&self, fdt: &mut Fdt) -> Result<BTreeMap<PvIommu, Phandle>> {
+ let mut compatible = fdt.root_mut()?.next_compatible(Self::PVIOMMU_COMPATIBLE)?;
+ let mut pviommu_phandles = BTreeMap::new();
+
+ for pviommu in &self.pviommus {
+ let mut node = compatible.ok_or(DeviceAssignmentError::TooManyPvIommu)?;
+ let phandle = node.as_node().get_phandle()?.ok_or(DeviceAssignmentError::Internal)?;
+ node.setprop_inplace(cstr!("id"), &pviommu.id.to_be_bytes())?;
+ if pviommu_phandles.insert(*pviommu, phandle).is_some() {
+ return Err(DeviceAssignmentError::Internal);
+ }
+ compatible = node.next_compatible(Self::PVIOMMU_COMPATIBLE)?;
}
+
+ // Filters pre-populated but unassigned pvIOMMUs.
+ while let Some(filtered_pviommu) = compatible {
+ compatible = filtered_pviommu.delete_and_next_compatible(Self::PVIOMMU_COMPATIBLE)?;
+ }
+
+ Ok(pviommu_phandles)
+ }
+
+ pub fn patch(&self, fdt: &mut Fdt) -> Result<()> {
+ let pviommu_phandles = self.patch_pviommus(fdt)?;
+
+ // Patches assigned devices
+ for device in &self.assigned_devices {
+ device.patch(fdt, &pviommu_phandles)?;
+ }
+
Ok(())
}
}
@@ -307,12 +427,68 @@
#[cfg(test)]
mod tests {
use super::*;
+ use alloc::collections::BTreeSet;
use std::fs;
const VM_DTBO_FILE_PATH: &str = "test_pvmfw_devices_vm_dtbo.dtbo";
const VM_DTBO_WITHOUT_SYMBOLS_FILE_PATH: &str =
"test_pvmfw_devices_vm_dtbo_without_symbols.dtbo";
const FDT_FILE_PATH: &str = "test_pvmfw_devices_with_rng.dtb";
+ const FDT_WITH_IOMMU_FILE_PATH: &str = "test_pvmfw_devices_with_rng_iommu.dtb";
+ const FDT_WITH_MULTIPLE_DEVICES_IOMMUS_FILE_PATH: &str =
+ "test_pvmfw_devices_with_multiple_devices_iommus.dtb";
+ const FDT_WITH_IOMMU_SHARING: &str = "test_pvmfw_devices_with_iommu_sharing.dtb";
+ const FDT_WITH_IOMMU_ID_CONFLICT: &str = "test_pvmfw_devices_with_iommu_id_conflict.dtb";
+
+ #[derive(Debug, Eq, PartialEq)]
+ struct AssignedDeviceNode {
+ path: CString,
+ reg: Vec<u8>,
+ interrupts: Vec<u8>,
+ iommus: Vec<u32>, // pvIOMMU ids
+ }
+
+ impl AssignedDeviceNode {
+ fn parse(fdt: &Fdt, path: &CStr) -> Result<Self> {
+ let Some(node) = fdt.node(path)? else {
+ return Err(FdtError::NotFound.into());
+ };
+
+ // TODO(b/277993056): Replace DeviceAssignmentError::Internal
+ let reg = node.getprop(cstr!("reg"))?.ok_or(DeviceAssignmentError::Internal)?;
+ let interrupts = node
+ .getprop(cstr!("interrupts"))?
+ .ok_or(DeviceAssignmentError::InvalidInterrupts)?;
+ let mut iommus = vec![];
+ if let Some(cells) = node.getprop_cells(cstr!("iommus"))? {
+ for cell in cells {
+ let phandle = Phandle::try_from(cell)?;
+ let pviommu = fdt
+ .node_with_phandle(phandle)?
+ .ok_or(DeviceAssignmentError::InvalidIommus)?;
+ let compatible = pviommu.getprop_str(cstr!("compatible"));
+ if compatible != Ok(Some(cstr!("pkvm,pviommu"))) {
+ return Err(DeviceAssignmentError::InvalidIommus);
+ }
+ let id = pviommu
+ .getprop_u32(cstr!("id"))?
+ .ok_or(DeviceAssignmentError::InvalidIommus)?;
+ iommus.push(id);
+ }
+ }
+ Ok(Self { path: path.into(), reg: reg.into(), interrupts: interrupts.into(), iommus })
+ }
+ }
+
+ fn collect_pviommus(fdt: &Fdt) -> Result<Vec<u32>> {
+ let mut pviommus = BTreeSet::new();
+ for pviommu in fdt.compatible_nodes(cstr!("pkvm,pviommu"))? {
+ if let Ok(Some(id)) = pviommu.getprop_u32(cstr!("id")) {
+ pviommus.insert(id);
+ }
+ }
+ Ok(pviommus.iter().cloned().collect())
+ }
fn into_fdt_prop(native_bytes: Vec<u32>) -> Vec<u8> {
let mut v = Vec::with_capacity(native_bytes.len() * 4);
@@ -347,16 +523,18 @@
dtbo_node_path: cstr!("/fragment@rng/__overlay__/rng").into(),
reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
+ iommus: vec![],
}];
assert_eq!(device_info.assigned_devices, expected);
}
+ // TODO(b/311655051): Test with real once instead of empty FDT.
#[test]
- fn device_info_new_without_assigned_devices() {
- let mut fdt_data: Vec<u8> = pvmfw_fdt_template::RAW.into();
+ fn device_info_new_with_empty_device_tree() {
+ let mut fdt_data = vec![0; pvmfw_fdt_template::RAW.len()];
let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
- let fdt = Fdt::from_mut_slice(fdt_data.as_mut_slice()).unwrap();
+ let fdt = Fdt::create_empty_tree(&mut fdt_data).unwrap();
let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap();
@@ -403,11 +581,13 @@
}
device_info.patch(platform_dt).unwrap();
+ // Note: Intentionally not using AssignedDeviceNode for matching all props.
type FdtResult<T> = libfdt::Result<T>;
let expected: Vec<(FdtResult<&CStr>, FdtResult<Vec<u8>>)> = vec![
(Ok(cstr!("android,rng,ignore-gctrl-reset")), Ok(Vec::new())),
(Ok(cstr!("compatible")), Ok(Vec::from(*b"android,rng\0"))),
(Ok(cstr!("interrupts")), Ok(into_fdt_prop(vec![0x0, 0xF, 0x4]))),
+ (Ok(cstr!("iommus")), Ok(Vec::new())),
(Ok(cstr!("reg")), Ok(into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]))),
];
@@ -425,4 +605,137 @@
assert_eq!(properties, expected);
}
+
+ #[test]
+ fn device_info_overlay_iommu() {
+ let mut fdt_data = fs::read(FDT_WITH_IOMMU_FILE_PATH).unwrap();
+ let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
+ let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
+ let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
+ let mut platform_dt_data = pvmfw_fdt_template::RAW.to_vec();
+ platform_dt_data.resize(pvmfw_fdt_template::RAW.len() * 2, 0);
+ let platform_dt = Fdt::from_mut_slice(&mut platform_dt_data).unwrap();
+ platform_dt.unpack().unwrap();
+
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+ device_info.filter(vm_dtbo).unwrap();
+
+ // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
+ unsafe {
+ platform_dt.apply_overlay(vm_dtbo.as_mut()).unwrap();
+ }
+ device_info.patch(platform_dt).unwrap();
+
+ let expected = AssignedDeviceNode {
+ path: CString::new("/rng").unwrap(),
+ reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
+ interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
+ iommus: vec![0x4],
+ };
+
+ let node = AssignedDeviceNode::parse(platform_dt, &expected.path);
+ assert_eq!(node, Ok(expected));
+
+ let pviommus = collect_pviommus(platform_dt);
+ assert_eq!(pviommus, Ok(vec![0x4]));
+ }
+
+ #[test]
+ fn device_info_multiple_devices_iommus() {
+ let mut fdt_data = fs::read(FDT_WITH_MULTIPLE_DEVICES_IOMMUS_FILE_PATH).unwrap();
+ let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
+ let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
+ let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
+ let mut platform_dt_data = pvmfw_fdt_template::RAW.to_vec();
+ platform_dt_data.resize(pvmfw_fdt_template::RAW.len() * 2, 0);
+ let platform_dt = Fdt::from_mut_slice(&mut platform_dt_data).unwrap();
+ platform_dt.unpack().unwrap();
+
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+ device_info.filter(vm_dtbo).unwrap();
+
+ // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
+ unsafe {
+ platform_dt.apply_overlay(vm_dtbo.as_mut()).unwrap();
+ }
+ device_info.patch(platform_dt).unwrap();
+
+ let expected_devices = [
+ AssignedDeviceNode {
+ path: CString::new("/rng").unwrap(),
+ reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
+ interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
+ iommus: vec![0x4, 0x9],
+ },
+ AssignedDeviceNode {
+ path: CString::new("/light").unwrap(),
+ reg: into_fdt_prop(vec![0x100, 0x9]),
+ interrupts: into_fdt_prop(vec![0x0, 0xF, 0x5]),
+ iommus: vec![0x40, 0x50, 0x60],
+ },
+ ];
+
+ for expected in expected_devices {
+ let node = AssignedDeviceNode::parse(platform_dt, &expected.path);
+ assert_eq!(node, Ok(expected));
+ }
+ let pviommus = collect_pviommus(platform_dt);
+ assert_eq!(pviommus, Ok(vec![0x4, 0x9, 0x40, 0x50, 0x60]));
+ }
+
+ #[test]
+ fn device_info_iommu_sharing() {
+ let mut fdt_data = fs::read(FDT_WITH_IOMMU_SHARING).unwrap();
+ let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
+ let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
+ let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
+ let mut platform_dt_data = pvmfw_fdt_template::RAW.to_vec();
+ platform_dt_data.resize(pvmfw_fdt_template::RAW.len() * 2, 0);
+ let platform_dt = Fdt::from_mut_slice(&mut platform_dt_data).unwrap();
+ platform_dt.unpack().unwrap();
+
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+ device_info.filter(vm_dtbo).unwrap();
+
+ // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
+ unsafe {
+ platform_dt.apply_overlay(vm_dtbo.as_mut()).unwrap();
+ }
+ device_info.patch(platform_dt).unwrap();
+
+ let expected_devices = [
+ AssignedDeviceNode {
+ path: CString::new("/rng").unwrap(),
+ reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
+ interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
+ iommus: vec![0x4, 0x9],
+ },
+ AssignedDeviceNode {
+ path: CString::new("/light").unwrap(),
+ reg: into_fdt_prop(vec![0x100, 0x9]),
+ interrupts: into_fdt_prop(vec![0x0, 0xF, 0x5]),
+ iommus: vec![0x9, 0x40],
+ },
+ ];
+
+ for expected in expected_devices {
+ let node = AssignedDeviceNode::parse(platform_dt, &expected.path);
+ assert_eq!(node, Ok(expected));
+ }
+
+ let pviommus = collect_pviommus(platform_dt);
+ assert_eq!(pviommus, Ok(vec![0x4, 0x9, 0x40]));
+ }
+
+ #[test]
+ fn device_info_iommu_id_conflict() {
+ let mut fdt_data = fs::read(FDT_WITH_IOMMU_ID_CONFLICT).unwrap();
+ let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
+ let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
+ let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
+
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo);
+
+ assert_eq!(device_info, Err(DeviceAssignmentError::DuplicatedPvIommuIds));
+ }
}
diff --git a/pvmfw/src/dice.rs b/pvmfw/src/dice.rs
index cc31f34..112c24c 100644
--- a/pvmfw/src/dice.rs
+++ b/pvmfw/src/dice.rs
@@ -17,12 +17,12 @@
use core::ffi::c_void;
use core::mem::size_of;
use core::slice;
+use cstr::cstr;
use diced_open_dice::{
bcc_format_config_descriptor, bcc_handover_main_flow, hash, Config, DiceConfigValues, DiceMode,
Hash, InputValues, HIDDEN_SIZE,
};
use pvmfw_avb::{DebugLevel, Digest, VerifiedBootData};
-use vmbase::cstr;
use vmbase::memory::flushed_zeroize;
fn to_dice_mode(debug_level: DebugLevel) -> DiceMode {
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index 7655614..4fe2c34 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -28,6 +28,7 @@
use core::fmt;
use core::mem::size_of;
use core::ops::Range;
+use cstr::cstr;
use fdtpci::PciMemoryFlags;
use fdtpci::PciRangeType;
use libfdt::AddressRange;
@@ -41,7 +42,6 @@
use log::info;
use log::warn;
use tinyvec::ArrayVec;
-use vmbase::cstr;
use vmbase::fdt::SwiotlbInfo;
use vmbase::layout::{crosvm::MEM_START, MAX_VIRT_ADDR};
use vmbase::memory::SIZE_4KB;
diff --git a/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts b/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts
new file mode 100644
index 0000000..f0a7162
--- /dev/null
+++ b/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts
@@ -0,0 +1,84 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ chosen {
+ stdout-path = "/uart@3f8";
+ linux,pci-probe-only = <1>;
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00 0x80000000 0xFFFFFFFF>;
+ };
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ swiotlb: restricted_dma_reserved {
+ compatible = "restricted-dma-pool";
+ reg = <0xFFFFFFFF>;
+ size = <0xFFFFFFFF>;
+ alignment = <0xFFFFFFFF>;
+ };
+
+ dice {
+ compatible = "google,open-dice";
+ no-map;
+ reg = <0xFFFFFFFF>;
+ };
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu@0 {
+ device_type = "cpu";
+ };
+ cpu@1 {
+ device_type = "cpu";
+ reg = <0x00 0x80000000 0xFFFFFFFF>;
+ };
+ };
+
+ rng@90000000 {
+ compatible = "android,rng";
+ reg = <0x0 0x9 0x0 0xFF>;
+ interrupts = <0x0 0xF 0x4>;
+ google,eh,ignore-gctrl-reset;
+ status = "okay";
+ iommus = <&pviommu_0>, <&pviommu_1>;
+ };
+
+ pviommu_0: pviommu0 {
+ compatible = "pkvm,pviommu";
+ id = <0x4>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_1: pviommu1 {
+ compatible = "pkvm,pviommu";
+ id = <0x9>;
+ #iommu-cells = <0>;
+ };
+
+ light@70000000 {
+ compatible = "android,light";
+ reg = <0x100 0x9>;
+ interrupts = <0x0 0xF 0x5>;
+ iommus = <&pviommu_0>, <&pviommu_a>, <&pviommu_b>;
+ };
+
+ pviommu_a: pviommua {
+ compatible = "pkvm,pviommu";
+ id = <0x40>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_b: pviommub {
+ compatible = "pkvm,pviommu";
+ id = <0x9>;
+ #iommu-cells = <0>;
+ };
+};
diff --git a/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts b/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts
new file mode 100644
index 0000000..d6952fa
--- /dev/null
+++ b/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts
@@ -0,0 +1,78 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ chosen {
+ stdout-path = "/uart@3f8";
+ linux,pci-probe-only = <1>;
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00 0x80000000 0xFFFFFFFF>;
+ };
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ swiotlb: restricted_dma_reserved {
+ compatible = "restricted-dma-pool";
+ reg = <0xFFFFFFFF>;
+ size = <0xFFFFFFFF>;
+ alignment = <0xFFFFFFFF>;
+ };
+
+ dice {
+ compatible = "google,open-dice";
+ no-map;
+ reg = <0xFFFFFFFF>;
+ };
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu@0 {
+ device_type = "cpu";
+ };
+ cpu@1 {
+ device_type = "cpu";
+ reg = <0x00 0x80000000 0xFFFFFFFF>;
+ };
+ };
+
+ rng@90000000 {
+ compatible = "android,rng";
+ reg = <0x0 0x9 0x0 0xFF>;
+ interrupts = <0x0 0xF 0x4>;
+ google,eh,ignore-gctrl-reset;
+ status = "okay";
+ iommus = <&pviommu_0>, <&pviommu_1>;
+ };
+
+ light@70000000 {
+ compatible = "android,light";
+ reg = <0x100 0x9>;
+ interrupts = <0x0 0xF 0x5>;
+ iommus = <&pviommu_1>, <&pviommu_a>;
+ };
+
+ pviommu_0: pviommu0 {
+ compatible = "pkvm,pviommu";
+ id = <0x4>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_1: pviommu1 {
+ compatible = "pkvm,pviommu";
+ id = <0x9>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_a: pviommua {
+ compatible = "pkvm,pviommu";
+ id = <0x40>;
+ #iommu-cells = <0>;
+ };
+};
diff --git a/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts b/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts
new file mode 100644
index 0000000..2609c45
--- /dev/null
+++ b/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts
@@ -0,0 +1,90 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ chosen {
+ stdout-path = "/uart@3f8";
+ linux,pci-probe-only = <1>;
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00 0x80000000 0xFFFFFFFF>;
+ };
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ swiotlb: restricted_dma_reserved {
+ compatible = "restricted-dma-pool";
+ reg = <0xFFFFFFFF>;
+ size = <0xFFFFFFFF>;
+ alignment = <0xFFFFFFFF>;
+ };
+
+ dice {
+ compatible = "google,open-dice";
+ no-map;
+ reg = <0xFFFFFFFF>;
+ };
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu@0 {
+ device_type = "cpu";
+ };
+ cpu@1 {
+ device_type = "cpu";
+ reg = <0x00 0x80000000 0xFFFFFFFF>;
+ };
+ };
+
+ rng@90000000 {
+ compatible = "android,rng";
+ reg = <0x0 0x9 0x0 0xFF>;
+ interrupts = <0x0 0xF 0x4>;
+ google,eh,ignore-gctrl-reset;
+ status = "okay";
+ iommus = <&pviommu_0>, <&pviommu_1>;
+ };
+
+ pviommu_0: pviommu0 {
+ compatible = "pkvm,pviommu";
+ id = <0x4>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_1: pviommu1 {
+ compatible = "pkvm,pviommu";
+ id = <0x9>;
+ #iommu-cells = <0>;
+ };
+
+ light@70000000 {
+ compatible = "android,light";
+ reg = <0x100 0x9>;
+ interrupts = <0x0 0xF 0x5>;
+ iommus = <&pviommu_a>, <&pviommu_b>, <&pviommu_c>;
+ };
+
+ pviommu_a: pviommua {
+ compatible = "pkvm,pviommu";
+ id = <0x40>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_b: pviommub {
+ compatible = "pkvm,pviommu";
+ id = <0x50>;
+ #iommu-cells = <0>;
+ };
+
+ pviommu_c: pviommuc {
+ compatible = "pkvm,pviommu";
+ id = <0x60>;
+ #iommu-cells = <0>;
+ };
+};
diff --git a/pvmfw/testdata/test_pvmfw_devices_with_rng_iommu.dts b/pvmfw/testdata/test_pvmfw_devices_with_rng_iommu.dts
new file mode 100644
index 0000000..6a5068c
--- /dev/null
+++ b/pvmfw/testdata/test_pvmfw_devices_with_rng_iommu.dts
@@ -0,0 +1,59 @@
+/dts-v1/;
+/plugin/;
+
+/ {
+ chosen {
+ stdout-path = "/uart@3f8";
+ linux,pci-probe-only = <1>;
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00 0x80000000 0xFFFFFFFF>;
+ };
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+ swiotlb: restricted_dma_reserved {
+ compatible = "restricted-dma-pool";
+ reg = <0xFFFFFFFF>;
+ size = <0xFFFFFFFF>;
+ alignment = <0xFFFFFFFF>;
+ };
+
+ dice {
+ compatible = "google,open-dice";
+ no-map;
+ reg = <0xFFFFFFFF>;
+ };
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cpu@0 {
+ device_type = "cpu";
+ };
+ cpu@1 {
+ device_type = "cpu";
+ reg = <0x00 0x80000000 0xFFFFFFFF>;
+ };
+ };
+
+ rng@90000000 {
+ compatible = "android,rng";
+ reg = <0x0 0x9 0x0 0xFF>;
+ interrupts = <0x0 0xF 0x4>;
+ google,eh,ignore-gctrl-reset;
+ status = "okay";
+ iommus = <&pviommu_0>;
+ };
+
+ pviommu_0: pviommu0 {
+ compatible = "pkvm,pviommu";
+ id = <0x4>;
+ #iommu-cells = <0>;
+ };
+};
diff --git a/rialto/Android.bp b/rialto/Android.bp
index 28c261e..728c1eb 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -13,6 +13,7 @@
"libbssl_ffi_nostd",
"libciborium_io_nostd",
"libciborium_nostd",
+ "libcstr",
"libdiced_open_dice_nostd",
"libdiced_sample_inputs_nostd",
"libhyp",
diff --git a/rialto/src/fdt.rs b/rialto/src/fdt.rs
index 8bb40c3..09cdd36 100644
--- a/rialto/src/fdt.rs
+++ b/rialto/src/fdt.rs
@@ -15,8 +15,8 @@
//! High-level FDT functions.
use core::ops::Range;
+use cstr::cstr;
use libfdt::{Fdt, FdtError};
-use vmbase::cstr;
/// Reads the DICE data range from the given `fdt`.
pub fn read_dice_range_from(fdt: &Fdt) -> libfdt::Result<Range<usize>> {
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index c6a30aa..6ae3bbd 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -101,6 +101,8 @@
const MICRODROID_OS_NAME: &str = "microdroid";
+const MICRODROID_GKI_OS_NAME: &str = "microdroid_gki";
+
const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
/// Roughly estimated sufficient size for storing vendor public key into DTBO.
@@ -694,6 +696,16 @@
}
}
+fn is_valid_os(os_name: &str) -> bool {
+ if os_name == MICRODROID_OS_NAME {
+ return true;
+ }
+ if cfg!(vendor_modules) && os_name == MICRODROID_GKI_OS_NAME {
+ return true;
+ }
+ false
+}
+
fn load_app_config(
config: &VirtualMachineAppConfig,
debug_config: &DebugConfig,
@@ -717,9 +729,9 @@
Payload::PayloadConfig(payload_config) => create_vm_payload_config(payload_config)?,
};
- // For now, the only supported OS is Microdroid
+ // For now, the only supported OS is Microdroid and Microdroid GKI
let os_name = vm_payload_config.os.name.as_str();
- if os_name != MICRODROID_OS_NAME {
+ if !is_valid_os(os_name) {
bail!("Unknown OS \"{}\"", os_name);
}
@@ -753,7 +765,7 @@
vm_config.cpuTopology = config.cpuTopology;
// Microdroid takes additional init ramdisk & (optionally) storage image
- add_microdroid_system_images(config, instance_file, storage_image, &mut vm_config)?;
+ add_microdroid_system_images(config, instance_file, storage_image, os_name, &mut vm_config)?;
// Include Microdroid payload disk (contains apks, idsigs) in vm config
add_microdroid_payload_images(
@@ -788,8 +800,9 @@
}
let task = Task { type_: TaskType::MicrodroidLauncher, command: payload_binary_name.clone() };
+ let name = payload_config.osName.clone();
Ok(VmPayloadConfig {
- os: OsConfig { name: MICRODROID_OS_NAME.to_owned() },
+ os: OsConfig { name },
task: Some(task),
apexes: vec![],
extra_apks: vec![],
@@ -871,7 +884,7 @@
/// Check that a file SELinux label is acceptable.
///
/// We only want to allow code in a VM to be sourced from places that apps, and the
-/// system, do not have write access to.
+/// system or vendor, do not have write access to.
///
/// Note that sepolicy must also grant read access for these types to both virtualization
/// service and crosvm.
@@ -885,6 +898,7 @@
| "staging_data_file" // updated/staged APEX images
| "system_file" // immutable dm-verity protected partition
| "virtualizationservice_data_file" // files created by VS / VirtMgr
+ | "vendor_microdroid_file" // immutable dm-verity protected partition (/vendor/etc/avf/microdroid/.*)
=> Ok(()),
_ => bail!("Label {} is not allowed", context),
}
@@ -1195,10 +1209,24 @@
Ok(())
}
+fn check_no_devices(config: &VirtualMachineConfig) -> binder::Result<()> {
+ let VirtualMachineConfig::AppConfig(config) = config else { return Ok(()) };
+ if let Some(custom_config) = &config.customConfig {
+ if !custom_config.devices.is_empty() {
+ return Err(anyhow!("device assignment feature is disabled"))
+ .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
+ }
+ }
+ Ok(())
+}
+
fn check_config_features(config: &VirtualMachineConfig) -> binder::Result<()> {
if !cfg!(vendor_modules) {
check_no_vendor_modules(config)?;
}
+ if !cfg!(device_assignment) {
+ check_no_devices(config)?;
+ }
Ok(())
}
@@ -1332,7 +1360,7 @@
}
fn requestAttestation(&self, csr: &[u8]) -> binder::Result<Vec<Certificate>> {
- GLOBAL_SERVICE.requestAttestation(csr)
+ GLOBAL_SERVICE.requestAttestation(csr, get_calling_uid() as i32)
}
}
diff --git a/virtualizationmanager/src/payload.rs b/virtualizationmanager/src/payload.rs
index 3bfad33..c19c103 100644
--- a/virtualizationmanager/src/payload.rs
+++ b/virtualizationmanager/src/payload.rs
@@ -425,6 +425,7 @@
config: &VirtualMachineAppConfig,
instance_file: File,
storage_image: Option<File>,
+ os_name: &str,
vm_config: &mut VirtualMachineRawConfig,
) -> Result<()> {
let debug_suffix = match config.debugLevel {
@@ -432,7 +433,7 @@
DebugLevel::FULL => "debuggable",
_ => return Err(anyhow!("unsupported debug level: {:?}", config.debugLevel)),
};
- let initrd = format!("/apex/com.android.virt/etc/microdroid_initrd_{}.img", debug_suffix);
+ let initrd = format!("/apex/com.android.virt/etc/{os_name}_initrd_{debug_suffix}.img");
vm_config.initrd = Some(open_parcel_file(Path::new(&initrd), false)?);
let mut writable_partitions = vec![Partition {
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index c00445d..bef7dd0 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -34,6 +34,7 @@
"liblibc",
"liblog_rust",
"libnix",
+ "librkpd_client",
"librustutils",
"libvmclient",
"libstatslog_virtualization_rust",
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl
index 55c2f5d..f3f54f3 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachinePayloadConfig.aidl
@@ -23,4 +23,9 @@
* function invoked.
*/
@utf8InCpp String payloadBinaryName;
+
+ /**
+ * Name of the OS to run the payload. Currently "microdroid" and "microdroid_gki" is supported.
+ */
+ @utf8InCpp String osName = "microdroid";
}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
index 2592135..c384a6f 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
@@ -60,10 +60,14 @@
* Requests a certificate chain for the provided certificate signing request (CSR).
*
* @param csr The certificate signing request.
+ * @param requesterUid The UID of the app that requests remote attestation. The client VM to be
+ * attested is owned by this app.
+ * The uniqueness of the UID ensures that no two VMs owned by different apps
+ * are able to correlate keys.
* @return A sequence of DER-encoded X.509 certificates that make up the attestation
* key's certificate chain. The attestation key is provided in the CSR.
*/
- Certificate[] requestAttestation(in byte[] csr);
+ Certificate[] requestAttestation(in byte[] csr, int requesterUid);
/**
* Get a list of assignable devices.
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 2be2b19..938225e 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -14,7 +14,7 @@
//! Implementation of the AIDL interface of the VirtualizationService.
-use crate::{get_calling_pid, get_calling_uid};
+use crate::{get_calling_pid, get_calling_uid, REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME};
use crate::atom::{forward_vm_booted_atom, forward_vm_creation_atom, forward_vm_exited_atom};
use crate::rkpvm::request_attestation;
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
@@ -39,6 +39,7 @@
use binder::{self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, Status, Strong, IntoBinderResult};
use libc::VMADDR_CID_HOST;
use log::{error, info, warn};
+use rkpd_client::get_rkpd_attestation_key;
use rustutils::system_properties;
use serde::Deserialize;
use std::collections::{HashMap, HashSet};
@@ -159,14 +160,31 @@
Ok(cids)
}
- fn requestAttestation(&self, csr: &[u8]) -> binder::Result<Vec<Certificate>> {
+ fn requestAttestation(
+ &self,
+ csr: &[u8],
+ requester_uid: i32,
+ ) -> binder::Result<Vec<Certificate>> {
check_manage_access()?;
info!("Received csr. Requestting attestation...");
if cfg!(remote_attestation) {
- request_attestation(csr)
+ let attestation_key = get_rkpd_attestation_key(
+ REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
+ requester_uid as u32,
+ )
+ .context("Failed to retrieve the remotely provisioned keys")
+ .with_log()
+ .or_service_specific_exception(-1)?;
+ let certificate = request_attestation(csr, &attestation_key.keyBlob)
.context("Failed to request attestation")
.with_log()
- .or_service_specific_exception(-1)
+ .or_service_specific_exception(-1)?;
+ // TODO(b/309780089): Parse the remotely provisioned certificate chain into
+ // individual certificates.
+ let mut certificate_chain =
+ vec![Certificate { encodedCertificate: attestation_key.encodedCertChain }];
+ certificate_chain.push(Certificate { encodedCertificate: certificate });
+ Ok(certificate_chain)
} else {
Err(Status::new_exception_str(
ExceptionCode::UNSUPPORTED_OPERATION,
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index fd668bc..d80ddd4 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -33,7 +33,7 @@
use std::path::Path;
const LOG_TAG: &str = "VirtualizationService";
-const _REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME: &str =
+pub(crate) const REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME: &str =
"android.system.virtualization.IRemotelyProvisionedComponent/avf";
fn get_calling_pid() -> pid_t {
diff --git a/virtualizationservice/src/rkpvm.rs b/virtualizationservice/src/rkpvm.rs
index 8f1de6b..5087120 100644
--- a/virtualizationservice/src/rkpvm.rs
+++ b/virtualizationservice/src/rkpvm.rs
@@ -17,23 +17,25 @@
//! serves as a trusted platform to attest a client VM.
use android_hardware_security_rkp::aidl::android::hardware::security::keymint::MacedPublicKey::MacedPublicKey;
-use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::Certificate::Certificate;
use anyhow::{bail, Context, Result};
-use service_vm_comm::{GenerateCertificateRequestParams, Request, Response};
+use service_vm_comm::{
+ ClientVmAttestationParams, GenerateCertificateRequestParams, Request, Response,
+};
use service_vm_manager::ServiceVm;
-pub(crate) fn request_attestation(csr: &[u8]) -> Result<Vec<Certificate>> {
+pub(crate) fn request_attestation(
+ csr: &[u8],
+ remotely_provisioned_keyblob: &[u8],
+) -> Result<Vec<u8>> {
let mut vm = ServiceVm::start()?;
- // TODO(b/271275206): Send the correct request type with client VM's
- // information to be attested.
- let request = Request::Reverse(csr.to_vec());
+ let params = ClientVmAttestationParams {
+ csr: csr.to_vec(),
+ remotely_provisioned_key_blob: remotely_provisioned_keyblob.to_vec(),
+ };
+ let request = Request::RequestClientVmAttestation(params);
match vm.process_request(request).context("Failed to process request")? {
- // TODO(b/271275206): Adjust the response type.
- Response::Reverse(cert) => {
- let cert = Certificate { encodedCertificate: cert };
- Ok(vec![cert])
- }
+ Response::RequestClientVmAttestation(cert) => Ok(cert),
_ => bail!("Incorrect response type"),
}
}
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 0af9791..87278bc 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -97,33 +97,24 @@
#[arg(long)]
storage_size: Option<u64>,
- /// Path to custom kernel image to use when booting Microdroid.
- #[cfg(vendor_modules)]
- #[arg(long)]
- kernel: Option<PathBuf>,
-
/// Path to disk image containing vendor-specific modules.
#[cfg(vendor_modules)]
#[arg(long)]
vendor: Option<PathBuf>,
/// SysFS nodes of devices to assign to VM
+ #[cfg(device_assignment)]
#[arg(long)]
devices: Vec<PathBuf>,
+
+ /// If set, use GKI instead of microdroid kernel
+ #[cfg(vendor_modules)]
+ #[arg(long)]
+ gki: bool,
}
impl MicrodroidConfig {
#[cfg(vendor_modules)]
- fn kernel(&self) -> &Option<PathBuf> {
- &self.kernel
- }
-
- #[cfg(not(vendor_modules))]
- fn kernel(&self) -> Option<PathBuf> {
- None
- }
-
- #[cfg(vendor_modules)]
fn vendor(&self) -> &Option<PathBuf> {
&self.vendor
}
@@ -132,6 +123,26 @@
fn vendor(&self) -> Option<PathBuf> {
None
}
+
+ #[cfg(vendor_modules)]
+ fn gki(&self) -> bool {
+ self.gki
+ }
+
+ #[cfg(not(vendor_modules))]
+ fn gki(&self) -> bool {
+ false
+ }
+
+ #[cfg(device_assignment)]
+ fn devices(&self) -> &Vec<PathBuf> {
+ &self.devices
+ }
+
+ #[cfg(not(device_assignment))]
+ fn devices(&self) -> Vec<PathBuf> {
+ Vec::new()
+ }
}
#[derive(Args)]
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 1ba9dec..44ba9af 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -98,9 +98,6 @@
None
};
- let kernel =
- config.microdroid.kernel().as_ref().map(|p| open_parcel_file(p, false)).transpose()?;
-
let vendor =
config.microdroid.vendor().as_ref().map(|p| open_parcel_file(p, false)).transpose()?;
@@ -114,8 +111,11 @@
}
Payload::ConfigPath(config_path)
} else if let Some(payload_binary_name) = config.payload_binary_name {
+ let os_name =
+ if config.microdroid.gki() { "microdroid_gki" } else { "microdroid" }.to_owned();
Payload::PayloadConfig(VirtualMachinePayloadConfig {
payloadBinaryName: payload_binary_name,
+ osName: os_name,
})
} else {
bail!("Either --config-path or --payload-binary-name must be defined")
@@ -124,13 +124,13 @@
let payload_config_str = format!("{:?}!{:?}", config.apk, payload);
let custom_config = CustomConfig {
- customKernelImage: kernel,
+ customKernelImage: None,
gdbPort: config.debug.gdb.map(u16::from).unwrap_or(0) as i32, // 0 means no gdb
taskProfiles: config.common.task_profiles,
vendorImage: vendor,
devices: config
.microdroid
- .devices
+ .devices()
.iter()
.map(|x| {
x.to_str().map(String::from).ok_or(anyhow!("Failed to convert {x:?} to String"))
diff --git a/vmbase/Android.bp b/vmbase/Android.bp
index b2b1549..e682773 100644
--- a/vmbase/Android.bp
+++ b/vmbase/Android.bp
@@ -76,6 +76,7 @@
rustlibs: [
"libaarch64_paging",
"libbuddy_system_allocator",
+ "libcstr",
"libfdtpci",
"libhyp",
"liblibfdt",
diff --git a/vmbase/example/Android.bp b/vmbase/example/Android.bp
index ae1a593..fe9de44 100644
--- a/vmbase/example/Android.bp
+++ b/vmbase/example/Android.bp
@@ -9,6 +9,7 @@
srcs: ["src/main.rs"],
rustlibs: [
"libaarch64_paging",
+ "libcstr",
"libdiced_open_dice_nostd",
"libfdtpci",
"liblibfdt",
diff --git a/vmbase/example/src/main.rs b/vmbase/example/src/main.rs
index ebd981c..6f513ee 100644
--- a/vmbase/example/src/main.rs
+++ b/vmbase/example/src/main.rs
@@ -28,11 +28,12 @@
use aarch64_paging::paging::MemoryRegion;
use aarch64_paging::MapError;
use alloc::{vec, vec::Vec};
+use cstr::cstr;
use fdtpci::PciInfo;
use libfdt::Fdt;
use log::{debug, error, info, trace, warn, LevelFilter};
use vmbase::{
- bionic, configure_heap, cstr,
+ bionic, configure_heap,
layout::{dtb_range, rodata_range, scratch_range, text_range},
linker, logger, main,
memory::{PageTable, SIZE_64KB},
diff --git a/vmbase/src/bionic.rs b/vmbase/src/bionic.rs
index f8db1fe..a049616 100644
--- a/vmbase/src/bionic.rs
+++ b/vmbase/src/bionic.rs
@@ -22,11 +22,12 @@
use core::str;
use crate::console;
-use crate::cstr;
use crate::eprintln;
use crate::rand::fill_with_entropy;
use crate::read_sysreg;
+use cstr::cstr;
+
const EOF: c_int = -1;
const EIO: c_int = 5;
diff --git a/vmbase/src/fdt.rs b/vmbase/src/fdt.rs
index 537ca03..4101f7e 100644
--- a/vmbase/src/fdt.rs
+++ b/vmbase/src/fdt.rs
@@ -14,8 +14,8 @@
//! High-level FDT functions.
-use crate::cstr;
use core::ops::Range;
+use cstr::cstr;
use libfdt::{self, Fdt, FdtError};
/// Represents information about a SWIOTLB buffer.
diff --git a/vmbase/src/util.rs b/vmbase/src/util.rs
index 25586bc..8c230a1 100644
--- a/vmbase/src/util.rs
+++ b/vmbase/src/util.rs
@@ -16,19 +16,6 @@
use core::ops::Range;
-/// Create &CStr out of &str literal
-#[macro_export]
-macro_rules! cstr {
- ($str:literal) => {{
- const S: &str = concat!($str, "\0");
- const C: &::core::ffi::CStr = match ::core::ffi::CStr::from_bytes_with_nul(S.as_bytes()) {
- Ok(v) => v,
- Err(_) => panic!("string contains interior NUL"),
- };
- C
- }};
-}
-
/// Flatten [[T; N]] into &[T]
/// TODO: use slice::flatten when it graduates from experimental
pub fn flatten<T, const N: usize>(original: &[[T; N]]) -> &[T] {