Merge "Update for new version of der and friends." into main
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index a975be0..b21a355 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -108,6 +108,7 @@
action='store_true',
help='This will NOT update the vbmeta related bootconfigs while signing the apex.\
Used for testing only!!')
+ parser.add_argument('--do_not_validate_avb_version', action='store_true', help='Do not validate the avb_version when updating vbmeta bootconfig. Only use in tests!')
args = parser.parse_args(argv)
# preprocess --key_override into a map
args.key_overrides = {}
@@ -328,7 +329,8 @@
detach_bootconfigs(initrd, tmp_initrd, tmp_bc)
bc_file = open(tmp_bc, "rt", encoding="utf-8")
bc_data = bc_file.read()
- validate_avb_version(bc_data)
+ if not args.do_not_validate_avb_version:
+ validate_avb_version(bc_data)
bc_data = update_vbmeta_digest(bc_data)
bc_data = update_vbmeta_size(bc_data)
bc_file.close()
diff --git a/libs/dice/open_dice/Android.bp b/libs/dice/open_dice/Android.bp
index 646080d..2d0f52c 100644
--- a/libs/dice/open_dice/Android.bp
+++ b/libs/dice/open_dice/Android.bp
@@ -27,12 +27,14 @@
],
visibility: [
"//packages/modules/Virtualization:__subpackages__",
+ "//system/authgraph/tests:__subpackages__",
],
}
rust_library {
name: "libdiced_open_dice",
defaults: ["libdiced_open_dice_defaults"],
+ host_supported: true,
vendor_available: true,
rustlibs: [
"libopen_dice_android_bindgen",
@@ -54,6 +56,7 @@
],
visibility: [
"//packages/modules/Virtualization:__subpackages__",
+ "//system/authgraph/tests:__subpackages__",
],
apex_available: [
"//apex_available:platform",
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index aae75f7..7eb08b2 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -527,6 +527,32 @@
Ok(None)
}
}
+
+ /// Returns the subnode of the given name. The name doesn't need to be nul-terminated.
+ pub fn subnode(&self, name: &CStr) -> Result<Option<Self>> {
+ let offset = self.subnode_offset(name.to_bytes())?;
+ Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
+ }
+
+ /// Returns the subnode of the given name bytes
+ pub fn subnode_with_name_bytes(&self, name: &[u8]) -> Result<Option<Self>> {
+ let offset = self.subnode_offset(name)?;
+ Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
+ }
+
+ fn subnode_offset(&self, name: &[u8]) -> Result<Option<c_int>> {
+ let namelen = name.len().try_into().unwrap();
+ // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
+ let ret = unsafe {
+ libfdt_bindgen::fdt_subnode_offset_namelen(
+ self.fdt.as_ptr(),
+ self.offset,
+ name.as_ptr().cast::<_>(),
+ namelen,
+ )
+ };
+ fdt_err_or_option(ret)
+ }
}
impl<'a> PartialEq for FdtNode<'a> {
@@ -751,24 +777,38 @@
fdt_err(ret)
}
- /// Returns the subnode of the given name with len.
- pub fn subnode_with_namelen(&'a mut self, name: &CStr, namelen: usize) -> Result<Option<Self>> {
- let offset = self.subnode_offset(&name.to_bytes()[..namelen])?;
- Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
+ /// Returns the first subnode of this
+ pub fn first_subnode(&'a mut self) -> Result<Option<Self>> {
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
+
+ Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
}
- fn subnode_offset(&self, name: &[u8]) -> Result<Option<c_int>> {
- let namelen = name.len().try_into().unwrap();
- // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
- let ret = unsafe {
- libfdt_bindgen::fdt_subnode_offset_namelen(
- self.fdt.as_ptr(),
- self.offset,
- name.as_ptr().cast::<_>(),
- namelen,
- )
- };
- fdt_err_or_option(ret)
+ /// Returns the next subnode that shares the same parent with this
+ pub fn next_subnode(self) -> Result<Option<Self>> {
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
+
+ Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
+ }
+
+ /// Deletes the current node and returns the next subnode
+ pub fn delete_and_next_subnode(mut self) -> Result<Option<Self>> {
+ // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+ let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
+
+ let next_offset = fdt_err_or_option(ret)?;
+
+ if Some(self.offset) == next_offset {
+ return Err(FdtError::Internal);
+ }
+
+ // SAFETY: nop_self() only touches bytes of the self and its properties and subnodes, and
+ // doesn't alter any other blob in the tree. self.fdt and next_offset would remain valid.
+ unsafe { self.nop_self()? };
+
+ Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
}
fn parent(&'a self) -> Result<FdtNode<'a>> {
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index d5d6ece..e68557f 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -262,14 +262,15 @@
let subnode_name = cstr!("123456789");
for len in 0..subnode_name.to_bytes().len() {
- let mut node = fdt.node_mut(node_path).unwrap().unwrap();
- assert!(node.subnode_with_namelen(subnode_name, len).unwrap().is_none());
+ let name = &subnode_name.to_bytes()[0..len];
+ let node = fdt.node(node_path).unwrap().unwrap();
+ assert_eq!(Ok(None), node.subnode_with_name_bytes(name));
let mut node = fdt.node_mut(node_path).unwrap().unwrap();
node.add_subnode_with_namelen(subnode_name, len).unwrap();
- let mut node = fdt.node_mut(node_path).unwrap().unwrap();
- assert!(node.subnode_with_namelen(subnode_name, len).unwrap().is_some());
+ let node = fdt.node(node_path).unwrap().unwrap();
+ assert_ne!(Ok(None), node.subnode_with_name_bytes(name));
}
let node_path = node_path.to_str().unwrap();
@@ -283,6 +284,48 @@
}
#[test]
+fn node_subnode() {
+ let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_slice(&data).unwrap();
+
+ let name = cstr!("node_a");
+ let root = fdt.root().unwrap();
+ let node = root.subnode(name).unwrap();
+ assert_ne!(None, node);
+ let node = node.unwrap();
+
+ assert_eq!(Ok(name), node.name());
+}
+
+#[test]
+fn node_subnode_with_name_bytes() {
+ let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_slice(&data).unwrap();
+
+ let name = b"node_aaaaa";
+ let root = fdt.root().unwrap();
+ let node = root.subnode_with_name_bytes(&name[0..6]).unwrap();
+ assert_ne!(None, node);
+ let node = node.unwrap();
+
+ assert_eq!(Ok(cstr!("node_a")), node.name());
+}
+
+#[test]
+fn node_subnode_borrow_checker() {
+ let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_slice(&data).unwrap();
+
+ let name = cstr!("node_a");
+ let node = {
+ let root = fdt.root().unwrap();
+ root.subnode(name).unwrap().unwrap()
+ };
+
+ assert_eq!(Ok(name), node.name());
+}
+
+#[test]
fn fdt_symbols() {
let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
let fdt = Fdt::from_mut_slice(&mut data).unwrap();
@@ -328,3 +371,31 @@
]
);
}
+
+#[test]
+fn node_mut_delete_and_next_subnode() {
+ let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+ let fdt = Fdt::from_mut_slice(&mut data).unwrap();
+
+ let mut root = fdt.root_mut().unwrap();
+ let mut subnode_iter = root.first_subnode().unwrap();
+
+ while let Some(subnode) = subnode_iter {
+ if subnode.as_node().name() == Ok(cstr!("node_z")) {
+ subnode_iter = subnode.delete_and_next_subnode().unwrap();
+ } else {
+ subnode_iter = subnode.next_subnode().unwrap();
+ }
+ }
+
+ let root = fdt.root().unwrap();
+ let expected_names = vec![
+ Ok(cstr!("node_a")),
+ Ok(cstr!("node_b")),
+ Ok(cstr!("node_c")),
+ Ok(cstr!("__symbols__")),
+ ];
+ let subnode_names: Vec<_> = root.subnodes().unwrap().map(|node| node.name()).collect();
+
+ assert_eq!(expected_names, subnode_names);
+}
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index 8481edf..cb3b2aa 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -5,7 +5,10 @@
rust_defaults {
name: "microdroid_manager_defaults",
crate_name: "microdroid_manager",
- defaults: ["avf_build_flags_rust"],
+ defaults: [
+ "avf_build_flags_rust",
+ "secretkeeper_use_latest_hal_aidl_rust",
+ ],
srcs: ["src/main.rs"],
edition: "2021",
prefer_rlib: true,
@@ -43,6 +46,8 @@
"libprotobuf",
"librpcbinder_rs",
"librustutils",
+ "libsecretkeeper_client",
+ "libsecretkeeper_comm_nostd",
"libscopeguard",
"libserde",
"libserde_cbor",
@@ -51,6 +56,7 @@
"libuuid",
"libvsock",
"librand",
+ "libzeroize",
],
init_rc: ["microdroid_manager.rc"],
multilib: {
diff --git a/microdroid_manager/src/dice.rs b/microdroid_manager/src/dice.rs
index 0cf7013..a8b88aa 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -107,7 +107,7 @@
apks.chain(apexes).collect()
}
-// Returns a configuration descriptor of the given payload. See vm_config.cddl for a definition
+// Returns a configuration descriptor of the given payload. See vm_config.cddl for the definition
// of the format.
fn format_payload_config_descriptor(
payload: &PayloadMetadata,
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 9e167a4..c94a937 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -105,7 +105,6 @@
MicrodroidError::PayloadInvalidConfig(msg) => {
(ErrorCode::PAYLOAD_INVALID_CONFIG, msg.to_string())
}
-
// Connection failure won't be reported to VS; return the default value
MicrodroidError::FailedToConnectToVirtualizationService(msg) => {
(ErrorCode::UNKNOWN, msg.to_string())
@@ -282,7 +281,8 @@
// To minimize the exposure to untrusted data, derive dice profile as soon as possible.
info!("DICE derivation for payload");
let dice_artifacts = dice_derivation(dice, &instance_data, &payload_metadata)?;
- let vm_secret = VmSecret::new(dice_artifacts).context("Failed to create VM secrets")?;
+ let vm_secret =
+ VmSecret::new(dice_artifacts, service).context("Failed to create VM secrets")?;
if cfg!(dice_changes) {
// Now that the DICE derivation is done, it's ok to allow payload code to run.
diff --git a/microdroid_manager/src/vm_config.cddl b/microdroid_manager/src/vm_config.cddl
index 052262d..8508e8f 100644
--- a/microdroid_manager/src/vm_config.cddl
+++ b/microdroid_manager/src/vm_config.cddl
@@ -11,6 +11,10 @@
; The configuration descriptor node for a Microdroid VM, with extensions to describe the contents
; of the VM payload.
+; The subcomponents describe the APKs and then the APEXes that are part of the VM. The main APK
+; is first, followed by any extra APKs in the order they are specified in the VM config.
+; The APEXes are listed in the order specified when the VM is created, which is normally alphabetic
+; order by name.
VmConfigDescriptor = {
-70002 : "Microdroid payload", ; Component name
(? -71000: tstr // ; Path to the payload config file
@@ -23,9 +27,30 @@
}
; Describes a unit of code (e.g. an APK or an APEX) present inside the VM.
+;
+; For an APK, the fields are as follows:
+; - Component name: The string "apk:" followed by the package name.
+; - Security version: The long version code from the APK manifest
+; (https://developer.android.com/reference/android/content/pm/PackageInfo#getLongVersionCode()).
+; - Code hash: This is the root hash of a Merkle tree computed over all bytes of the APK, as used
+; in the APK Signature Scheme v4 (https://source.android.com/docs/security/features/apksigning/v4)
+; with empty salt and using SHA-256 as the hash algorithm.
+; - Authority hash: The SHA-512 hash of the DER representation of the X.509 certificate for the
+; public key used to sign the APK.
+;
+; For an APEX, they are as follows:
+; - Component name: The string "apex:" followed by the APEX name as specified in the APEX Manifest
+; (see https://source.android.com/docs/core/ota/apex).
+; - Security version: The version number from the APEX Manifest.
+; - Code hash: The root hash of the apex_payload.img file within the APEX, taken from the first
+; hashtree descriptor in the VBMeta image
+; (see https://android.googlesource.com/platform/external/avb/+/master/README.md).
+; - Authority hash: The SHA-512 hash of the public key used to sign the file system image in the
+; APEX (as stored in the apex_pubkey file). The format is as described for AvbRSAPublicKeyHeader
+; in https://cs.android.com/android/platform/superproject/main/+/main:external/avb/libavb/avb_crypto.h.
SubcomponentDescriptor = {
1: tstr, ; Component name
2: uint, ; Security version
- ? 3: bstr, ; Code hash
+ 3: bstr, ; Code hash
4: bstr, ; Authority hash
}
diff --git a/microdroid_manager/src/vm_secret.rs b/microdroid_manager/src/vm_secret.rs
index d84c2e2..df5d318 100644
--- a/microdroid_manager/src/vm_secret.rs
+++ b/microdroid_manager/src/vm_secret.rs
@@ -14,18 +14,28 @@
//! Class for encapsulating & managing represent VM secrets.
-use anyhow::Result;
+use anyhow::{anyhow, ensure, Result};
+use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
+use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::ISecretkeeper;
+use secretkeeper_comm::data_types::request::Request;
+use binder::{Strong};
+use coset::CborSerializable;
use diced_open_dice::{DiceArtifacts, OwnedDiceArtifacts};
use keystore2_crypto::ZVec;
use openssl::hkdf::hkdf;
use openssl::md::Md;
use openssl::sha;
+use secretkeeper_client::SkSession;
+use secretkeeper_comm::data_types::{Id, ID_SIZE, Secret, SECRET_SIZE};
+use secretkeeper_comm::data_types::response::Response;
+use secretkeeper_comm::data_types::packet::{ResponsePacket, ResponseType};
+use secretkeeper_comm::data_types::request_response_impl::{
+ StoreSecretRequest, GetSecretResponse, GetSecretRequest};
+use secretkeeper_comm::data_types::error::SecretkeeperError;
+use zeroize::Zeroizing;
const ENCRYPTEDSTORE_KEY_IDENTIFIER: &str = "encryptedstore_key";
-// Size of the secret stored in Secretkeeper.
-const SK_SECRET_SIZE: usize = 64;
-
// Generated using hexdump -vn32 -e'14/1 "0x%02X, " 1 "\n"' /dev/urandom
const SALT_ENCRYPTED_STORE: &[u8] = &[
0xFC, 0x1D, 0x35, 0x7B, 0x96, 0xF3, 0xEF, 0x17, 0x78, 0x7D, 0x70, 0xED, 0xEA, 0xFE, 0x1D, 0x6F,
@@ -36,6 +46,24 @@
0x55, 0xF8, 0x08, 0x23, 0x81, 0x5F, 0xF5, 0x16, 0x20, 0x3E, 0xBE, 0xBA, 0xB7, 0xA8, 0x43, 0x92,
];
+// TODO(b/291213394): Remove this once policy is generated from dice_chain
+const HYPOTHETICAL_DICE_POLICY: [u8; 43] = [
+ 0x83, 0x01, 0x81, 0x83, 0x01, 0x80, 0xA1, 0x01, 0x00, 0x82, 0x83, 0x01, 0x81, 0x01, 0x73, 0x74,
+ 0x65, 0x73, 0x74, 0x69, 0x6E, 0x67, 0x5F, 0x64, 0x69, 0x63, 0x65, 0x5F, 0x70, 0x6F, 0x6C, 0x69,
+ 0x63, 0x79, 0x83, 0x02, 0x82, 0x03, 0x18, 0x64, 0x19, 0xE9, 0x75,
+];
+// TODO(b/291213394): Differentiate the Id of nPVM based on 'salt'
+const ID_NP_VM: [u8; ID_SIZE] = [
+ 0xF1, 0xB2, 0xED, 0x3B, 0xD1, 0xBD, 0xF0, 0x7D, 0xE1, 0xF0, 0x01, 0xFC, 0x61, 0x71, 0xD3, 0x42,
+ 0xE5, 0x8A, 0xAF, 0x33, 0x6C, 0x11, 0xDC, 0xC8, 0x6F, 0xAE, 0x12, 0x5C, 0x26, 0x44, 0x6B, 0x86,
+ 0xCC, 0x24, 0xFD, 0xBF, 0x91, 0x4A, 0x54, 0x84, 0xF9, 0x01, 0x59, 0x25, 0x70, 0x89, 0x38, 0x8D,
+ 0x5E, 0xE6, 0x91, 0xDF, 0x68, 0x60, 0x69, 0x26, 0xBE, 0xFE, 0x79, 0x58, 0xF7, 0xEA, 0x81, 0x7D,
+];
+const SKP_SECRET_NP_VM: [u8; SECRET_SIZE] = [
+ 0xA9, 0x89, 0x97, 0xFE, 0xAE, 0x97, 0x55, 0x4B, 0x32, 0x35, 0xF0, 0xE8, 0x93, 0xDA, 0xEA, 0x24,
+ 0x06, 0xAC, 0x36, 0x8B, 0x3C, 0x95, 0x50, 0x16, 0x67, 0x71, 0x65, 0x26, 0xEB, 0xD0, 0xC3, 0x98,
+];
+
pub enum VmSecret {
// V2 secrets are derived from 2 independently secured secrets:
// 1. Secretkeeper protected secrets (skp secret).
@@ -54,15 +82,47 @@
V1 { dice: OwnedDiceArtifacts },
}
+fn get_id() -> [u8; ID_SIZE] {
+ if super::is_strict_boot() {
+ todo!("Id for protected VM is not implemented");
+ } else {
+ ID_NP_VM
+ }
+}
+
impl VmSecret {
- pub fn new(dice_artifacts: OwnedDiceArtifacts) -> Result<VmSecret> {
- if is_sk_supported() {
- // TODO(b/291213394): Change this to real Sk protected secret.
- let fake_skp_secret = ZVec::new(SK_SECRET_SIZE)?;
- return Ok(Self::V2 { dice: dice_artifacts, skp_secret: fake_skp_secret });
+ pub fn new(
+ dice_artifacts: OwnedDiceArtifacts,
+ vm_service: &Strong<dyn IVirtualMachineService>,
+ ) -> Result<VmSecret> {
+ ensure!(dice_artifacts.bcc().is_some(), "Dice chain missing");
+
+ if let Some(sk_service) = is_sk_supported(vm_service)? {
+ let id = get_id();
+ let mut skp_secret = Zeroizing::new([0u8; SECRET_SIZE]);
+ if super::is_strict_boot() {
+ if super::is_new_instance() {
+ *skp_secret = rand::random();
+ store_secret(sk_service.clone(), id, skp_secret.clone(), &dice_artifacts)?;
+ } else {
+ // Subsequent run of the pVM -> get the secret stored in Secretkeeper.
+ *skp_secret = get_secret(sk_service.clone(), id, &dice_artifacts)?;
+ }
+ } else {
+ // TODO(b/291213394): Non protected VM don't need to use Secretkeeper, remove this
+ // once we have sufficient testing on protected VM.
+ store_secret(sk_service.clone(), id, SKP_SECRET_NP_VM.into(), &dice_artifacts)?;
+ *skp_secret = get_secret(sk_service.clone(), id, &dice_artifacts)?;
+ }
+ return Ok(Self::V2 {
+ dice: dice_artifacts,
+ skp_secret: ZVec::try_from(skp_secret.to_vec())?,
+ });
}
+ // Use V1 secrets if Secretkeeper is not supported.
Ok(Self::V1 { dice: dice_artifacts })
}
+
pub fn dice(&self) -> &OwnedDiceArtifacts {
match self {
Self::V2 { dice, .. } => dice,
@@ -94,13 +154,87 @@
}
}
-// Does the hardware support Secretkeeper.
-fn is_sk_supported() -> bool {
- if cfg!(llpvm_changes) {
- return false;
+fn store_secret(
+ secretkeeper: binder::Strong<dyn ISecretkeeper>,
+ id: [u8; ID_SIZE],
+ secret: Zeroizing<[u8; SECRET_SIZE]>,
+ _dice_chain: &OwnedDiceArtifacts,
+) -> Result<()> {
+ // Start a new secretkeeper session!
+ let session = SkSession::new(secretkeeper).map_err(anyhow_err)?;
+ let store_request = StoreSecretRequest {
+ id: Id(id),
+ secret: Secret(*secret),
+ // TODO(b/291233371): Construct policy out of dice_chain.
+ sealing_policy: HYPOTHETICAL_DICE_POLICY.to_vec(),
};
- // TODO(b/292209416): This value should be extracted from device tree.
- // Note: this does not affect the security of pVM. pvmfw & microdroid_manager continue to block
- // upgraded images. Setting this true is equivalent to including constant salt in vm secrets.
- true
+ log::info!("Secretkeeper operation: {:?}", store_request);
+
+ let store_request = store_request.serialize_to_packet().to_vec().map_err(anyhow_err)?;
+ let store_response = session.secret_management_request(&store_request).map_err(anyhow_err)?;
+ let store_response = ResponsePacket::from_slice(&store_response).map_err(anyhow_err)?;
+ let response_type = store_response.response_type().map_err(anyhow_err)?;
+ ensure!(
+ response_type == ResponseType::Success,
+ "Secretkeeper store failed with error: {:?}",
+ *SecretkeeperError::deserialize_from_packet(store_response).map_err(anyhow_err)?
+ );
+ Ok(())
+}
+
+fn get_secret(
+ secretkeeper: binder::Strong<dyn ISecretkeeper>,
+ id: [u8; ID_SIZE],
+ _dice_chain: &OwnedDiceArtifacts,
+) -> Result<[u8; SECRET_SIZE]> {
+ // Start a new secretkeeper session!
+ let session = SkSession::new(secretkeeper).map_err(anyhow_err)?;
+ let get_request = GetSecretRequest {
+ id: Id(id),
+ // TODO(b/291233371): Construct policy out of dice_chain.
+ updated_sealing_policy: None,
+ };
+ log::info!("Secretkeeper operation: {:?}", get_request);
+
+ let get_request = get_request.serialize_to_packet().to_vec().map_err(anyhow_err)?;
+ let get_response = session.secret_management_request(&get_request).map_err(anyhow_err)?;
+ let get_response = ResponsePacket::from_slice(&get_response).map_err(anyhow_err)?;
+ let response_type = get_response.response_type().map_err(anyhow_err)?;
+ ensure!(
+ response_type == ResponseType::Success,
+ "Secretkeeper get failed with error: {:?}",
+ *SecretkeeperError::deserialize_from_packet(get_response).map_err(anyhow_err)?
+ );
+ let get_response =
+ *GetSecretResponse::deserialize_from_packet(get_response).map_err(anyhow_err)?;
+ Ok(get_response.secret.0)
+}
+
+#[inline]
+fn anyhow_err<E: core::fmt::Debug>(err: E) -> anyhow::Error {
+ anyhow!("{:?}", err)
+}
+
+// Get the secretkeeper connection if supported. Host can be consulted whether the device supports
+// secretkeeper but that should be used with caution for protected VM.
+fn is_sk_supported(
+ host: &Strong<dyn IVirtualMachineService>,
+) -> Result<Option<Strong<dyn ISecretkeeper>>> {
+ let sk = if cfg!(llpvm_changes) {
+ if super::is_strict_boot() {
+ // TODO: For protected VM check for Secretkeeper authentication data in device tree.
+ None
+ } else {
+ // For non-protected VM, believe what host claims.
+ host.getSecretkeeper()
+ // TODO rename this error!
+ .map_err(|e| {
+ super::MicrodroidError::FailedToConnectToVirtualizationService(e.to_string())
+ })?
+ }
+ } else {
+ // LLPVM flag is disabled
+ None
+ };
+ Ok(sk)
}
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index d267e2e..37d8ac9 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -26,6 +26,7 @@
"libpvmfw_avb_nostd",
"libpvmfw_embedded_key",
"libpvmfw_fdt_template",
+ "libservice_vm_version",
"libsmccc",
"libstatic_assertions",
"libtinyvec_nostd",
@@ -73,6 +74,7 @@
srcs: ["src/device_assignment.rs"],
defaults: ["libpvmfw.test.defaults"],
rustlibs: [
+ "libhyp",
"liblibfdt",
"liblog_rust",
"libpvmfw_fdt_template",
@@ -84,6 +86,7 @@
":test_pvmfw_devices_with_multiple_devices_iommus",
":test_pvmfw_devices_with_iommu_sharing",
":test_pvmfw_devices_with_iommu_id_conflict",
+ ":test_pvmfw_devices_without_device",
":test_pvmfw_devices_without_iommus",
],
// To use libpvmfw_fdt_template for testing
@@ -142,6 +145,13 @@
}
genrule {
+ name: "test_pvmfw_devices_without_device",
+ defaults: ["test_device_assignment_dts_to_dtb"],
+ srcs: ["testdata/test_pvmfw_devices_without_device.dts"],
+ out: ["test_pvmfw_devices_without_device.dtb"],
+}
+
+genrule {
name: "test_pvmfw_devices_with_multiple_devices_iommus",
defaults: ["test_device_assignment_dts_to_dtb"],
srcs: ["testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts"],
diff --git a/pvmfw/src/config.rs b/pvmfw/src/config.rs
index 2fe4ec9..3f78a88 100644
--- a/pvmfw/src/config.rs
+++ b/pvmfw/src/config.rs
@@ -19,11 +19,10 @@
use core::num::NonZeroUsize;
use core::ops::Range;
use core::result;
-use core::slice;
use log::{info, warn};
use static_assertions::const_assert_eq;
use vmbase::util::RangeExt;
-use zerocopy::{FromBytes, FromZeroes, LayoutVerified};
+use zerocopy::{FromBytes, FromZeroes};
/// Configuration data header.
#[repr(C, packed)]
@@ -129,12 +128,14 @@
impl Entry {
const COUNT: usize = Self::_VARIANT_COUNT as usize;
+
+ const ALL_ENTRIES: [Entry; Self::COUNT] = [Self::Bcc, Self::DebugPolicy, Self::VmDtbo];
}
#[derive(Default)]
pub struct Entries<'a> {
pub bcc: &'a mut [u8],
- pub debug_policy: Option<&'a mut [u8]>,
+ pub debug_policy: Option<&'a [u8]>,
pub vm_dtbo: Option<&'a mut [u8]>,
}
@@ -203,7 +204,7 @@
}
let (header, rest) =
- LayoutVerified::<_, Header>::new_from_prefix(bytes).ok_or(Error::HeaderMisaligned)?;
+ zerocopy::Ref::<_, Header>::new_from_prefix(bytes).ok_or(Error::HeaderMisaligned)?;
let header = header.into_ref();
if header.magic != Header::MAGIC {
@@ -230,7 +231,7 @@
};
let (header_entries, body) =
- LayoutVerified::<_, [HeaderEntry]>::new_slice_from_prefix(rest, header.entry_count()?)
+ zerocopy::Ref::<_, [HeaderEntry]>::new_slice_from_prefix(rest, header.entry_count()?)
.ok_or(Error::BufferTooSmall)?;
// Validate that we won't get an invalid alignment in the following due to padding to u64.
@@ -240,7 +241,7 @@
let limits = header.body_lowest_bound()?..total_size;
let mut ranges: [Option<NonEmptyRange>; Entry::COUNT] = [None; Entry::COUNT];
let mut last_end = 0;
- for entry in [Entry::Bcc, Entry::DebugPolicy, Entry::VmDtbo] {
+ for entry in Entry::ALL_ENTRIES {
let Some(header_entry) = header_entries.get(entry as usize) else { continue };
let entry_offset = header_entry.offset.try_into().unwrap();
let entry_size = header_entry.size.try_into().unwrap();
@@ -266,36 +267,31 @@
Ok(Self { body, ranges })
}
- /// Get slice containing the platform BCC.
- pub fn get_entries(&mut self) -> Entries<'_> {
- // This assumes that the blobs are in-order w.r.t. the entries.
- let bcc_range = self.get_entry_range(Entry::Bcc);
- let dp_range = self.get_entry_range(Entry::DebugPolicy);
- let vm_dtbo_range = self.get_entry_range(Entry::VmDtbo);
- // TODO(b/291191157): Provision device assignment with this.
- if let Some(vm_dtbo_range) = vm_dtbo_range {
- info!("Found VM DTBO at {:?}", vm_dtbo_range);
+ /// Locate the various config entries.
+ pub fn get_entries(self) -> Entries<'a> {
+ // We require the blobs to be in the same order as the `Entry` enum (and this is checked
+ // in `new` above)
+ // So we can just work through the body range and split off the parts we are interested in.
+ let mut offset = 0;
+ let mut body = self.body;
+
+ let mut entries: [Option<&mut [u8]>; Entry::COUNT] = Default::default();
+ for (i, range) in self.ranges.iter().enumerate() {
+ if let Some(range) = range {
+ body = &mut body[range.start - offset..];
+ let (chunk, rest) = body.split_at_mut(range.len());
+ offset = range.end();
+ body = rest;
+ entries[i] = Some(chunk);
+ }
}
+ let [bcc, debug_policy, vm_dtbo] = entries;
- // SAFETY: When instantiated, ranges are validated to be in the body range without
- // overlapping.
- let (bcc, debug_policy, vm_dtbo) = unsafe {
- let ptr = self.body.as_mut_ptr() as usize;
- (
- Self::from_raw_range_mut(ptr, bcc_range.unwrap()),
- dp_range.map(|dp_range| Self::from_raw_range_mut(ptr, dp_range)),
- vm_dtbo_range.map(|vm_dtbo_range| Self::from_raw_range_mut(ptr, vm_dtbo_range)),
- )
- };
+ // The platform BCC has always been required.
+ let bcc = bcc.unwrap();
+
+ // We have no reason to mutate so drop the `mut`.
+ let debug_policy = debug_policy.map(|x| &*x);
Entries { bcc, debug_policy, vm_dtbo }
}
-
- fn get_entry_range(&self, entry: Entry) -> Option<NonEmptyRange> {
- self.ranges[entry as usize]
- }
-
- unsafe fn from_raw_range_mut(ptr: usize, range: NonEmptyRange) -> &'a mut [u8] {
- // SAFETY: The caller must ensure that the range is valid from ptr.
- unsafe { slice::from_raw_parts_mut((ptr + range.start) as *mut u8, range.len()) }
- }
}
diff --git a/pvmfw/src/device_assignment.rs b/pvmfw/src/device_assignment.rs
index 14f1fe5..8d4d840 100644
--- a/pvmfw/src/device_assignment.rs
+++ b/pvmfw/src/device_assignment.rs
@@ -27,7 +27,9 @@
use core::ffi::CStr;
use core::iter::Iterator;
use core::mem;
-use libfdt::{Fdt, FdtError, FdtNode, Phandle};
+use hyp::DeviceAssigningHypervisor;
+use libfdt::{Fdt, FdtError, FdtNode, Phandle, Reg};
+use log::error;
// TODO(b/308694211): Use cstr! from vmbase instead.
macro_rules! cstr {
@@ -47,10 +49,12 @@
/// Errors in device assignment.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DeviceAssignmentError {
- // Invalid VM DTBO
+ /// Invalid VM DTBO
InvalidDtbo,
/// Invalid __symbols__
InvalidSymbols,
+ /// Invalid <reg>
+ InvalidReg,
/// Invalid <interrupts>
InvalidInterrupts,
/// Invalid <iommus>
@@ -83,6 +87,7 @@
f,
"Invalid property in /__symbols__. Must point to valid assignable device node."
),
+ Self::InvalidReg => write!(f, "Invalid <reg>"),
Self::InvalidInterrupts => write!(f, "Invalid <interrupts>"),
Self::InvalidIommus => write!(f, "Invalid <iommus>"),
Self::InvalidPvIommu => write!(f, "Invalid pvIOMMU node"),
@@ -178,6 +183,15 @@
}
}
+fn is_overlayable_node(dtbo_path: &CStr) -> bool {
+ dtbo_path
+ .to_bytes()
+ .split(|char| *char == b'/')
+ .filter(|&component| !component.is_empty())
+ .nth(1)
+ .map_or(false, |name| name == b"__overlay__")
+}
+
impl AsRef<Fdt> for VmDtbo {
fn as_ref(&self) -> &Fdt {
&self.0
@@ -214,6 +228,36 @@
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
struct Vsid(u32);
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+struct DeviceReg {
+ addr: u64,
+ size: u64,
+}
+
+impl TryFrom<Reg<u64>> for DeviceReg {
+ type Error = DeviceAssignmentError;
+
+ fn try_from(reg: Reg<u64>) -> Result<Self> {
+ Ok(Self { addr: reg.addr, size: reg.size.ok_or(DeviceAssignmentError::InvalidReg)? })
+ }
+}
+
+fn parse_node_reg(node: &FdtNode) -> Result<Vec<DeviceReg>> {
+ node.reg()?
+ .ok_or(DeviceAssignmentError::InvalidReg)?
+ .map(DeviceReg::try_from)
+ .collect::<Result<Vec<_>>>()
+}
+
+fn to_be_bytes(reg: &[DeviceReg]) -> Vec<u8> {
+ let mut reg_cells = vec![];
+ for x in reg {
+ reg_cells.extend_from_slice(&x.addr.to_be_bytes());
+ reg_cells.extend_from_slice(&x.size.to_be_bytes());
+ }
+ reg_cells
+}
+
/// 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)]
@@ -223,7 +267,7 @@
// DTBO node path of the assigned device (e.g. "/fragment@rng/__overlay__/rng")
dtbo_node_path: CString,
// <reg> property from the crosvm DT
- reg: Vec<u8>,
+ reg: Vec<DeviceReg>,
// <interrupts> property from the crosvm DT
interrupts: Vec<u8>,
// Parsed <iommus> property from the crosvm DT. Tuple of PvIommu and vSID.
@@ -231,6 +275,22 @@
}
impl AssignedDeviceInfo {
+ fn parse_reg(
+ node: &FdtNode,
+ hypervisor: &dyn DeviceAssigningHypervisor,
+ ) -> Result<Vec<DeviceReg>> {
+ let device_reg = parse_node_reg(node)?;
+ // TODO(b/277993056): Valid the result back with physical reg
+ for reg in &device_reg {
+ hypervisor.get_phys_mmio_token(reg.addr, reg.size).map_err(|e| {
+ let name = node.name();
+ error!("Failed to validate device <reg>, error={e:?}, name={name:?}, reg={reg:?}");
+ DeviceAssignmentError::InvalidReg
+ })?;
+ }
+ Ok(device_reg)
+ }
+
fn parse_interrupts(node: &FdtNode) -> Result<Vec<u8>> {
// Validation: Validate if interrupts cell numbers are multiple of #interrupt-cells.
// We can't know how many interrupts would exist.
@@ -250,6 +310,7 @@
fn parse_iommus(
node: &FdtNode,
pviommus: &BTreeMap<Phandle, PvIommu>,
+ hypervisor: &dyn DeviceAssigningHypervisor,
) -> Result<Vec<(PvIommu, Vsid)>> {
let mut iommus = vec![];
let Some(mut cells) = node.getprop_cells(cstr!("iommus"))? else {
@@ -266,6 +327,15 @@
};
let vsid = Vsid(cell);
+ // TODO(b/277993056): Valid the result back with phys iommu id and sid..
+ hypervisor
+ .get_phys_iommu_token(pviommu.id.into(), vsid.0.into())
+ .map_err(|e| {
+ let name = node.name().unwrap_or_default();
+ error!("Failed to validate device <iommus>, error={e:?}, name={name:?}, pviommu={pviommu:?}, vsid={:?}", vsid.0);
+ DeviceAssignmentError::InvalidIommus
+ })?;
+
iommus.push((*pviommu, vsid));
}
Ok(iommus)
@@ -276,27 +346,21 @@
vm_dtbo: &VmDtbo,
dtbo_node_path: &CStr,
pviommus: &BTreeMap<Phandle, PvIommu>,
+ hypervisor: &dyn DeviceAssigningHypervisor,
) -> 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 reg = Self::parse_reg(&node, hypervisor)?;
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,
- iommus,
- }))
+ let iommus = Self::parse_iommus(&node, pviommus, hypervisor)?;
+ Ok(Some(Self { node_path, dtbo_node_path: dtbo_node_path.into(), reg, interrupts, iommus }))
}
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!("reg"), &to_be_bytes(&self.reg))?;
dst.setprop(cstr!("interrupts"), &self.interrupts)?;
let mut iommus = Vec::with_capacity(8 * self.iommus.len());
for (pviommu, vsid) in &self.iommus {
@@ -339,7 +403,11 @@
/// Parses fdt and vm_dtbo, and creates new DeviceAssignmentInfo
// TODO(b/277993056): Parse __local_fixups__
// TODO(b/277993056): Parse __fixups__
- pub fn parse(fdt: &Fdt, vm_dtbo: &VmDtbo) -> Result<Option<Self>> {
+ pub fn parse(
+ fdt: &Fdt,
+ vm_dtbo: &VmDtbo,
+ hypervisor: &dyn DeviceAssigningHypervisor,
+ ) -> Result<Option<Self>> {
let Some(symbols_node) = vm_dtbo.as_ref().symbols()? else {
// /__symbols__ should contain all assignable devices.
// If empty, then nothing can be assigned.
@@ -358,8 +426,11 @@
let symbol_prop_value = symbol_prop.value()?;
let dtbo_node_path = CStr::from_bytes_with_nul(symbol_prop_value)
.or(Err(DeviceAssignmentError::InvalidSymbols))?;
+ if !is_overlayable_node(dtbo_node_path) {
+ continue;
+ }
let assigned_device =
- AssignedDeviceInfo::parse(fdt, vm_dtbo, dtbo_node_path, &pviommus)?;
+ AssignedDeviceInfo::parse(fdt, vm_dtbo, dtbo_node_path, &pviommus, hypervisor)?;
if let Some(assigned_device) = assigned_device {
assigned_devices.push(assigned_device);
} else {
@@ -369,7 +440,15 @@
if assigned_devices.is_empty() {
return Ok(None);
}
+
+ // Clean up any nodes that wouldn't be overlaid but may contain reference to filtered nodes.
+ // Otherwise, `fdt_apply_overlay()` would fail because of missing phandle reference.
filtered_dtbo_paths.push(CString::new("/__symbols__").unwrap());
+ // TODO(b/277993056): Also filter other unused nodes/props in __local_fixups__
+ filtered_dtbo_paths.push(CString::new("/__local_fixups__/host").unwrap());
+
+ // Note: Any node without __overlay__ will be ignored by fdt_apply_overlay,
+ // so doesn't need to be filtered.
Ok(Some(Self { pviommus: unique_pviommus, assigned_devices, filtered_dtbo_paths }))
}
@@ -390,22 +469,6 @@
node.nop()?;
}
- // Filters pvmfw-specific properties in assigned device node.
- const FILTERED_VM_DTBO_PROP: [&CStr; 3] = [
- cstr!("android,pvmfw,phy-reg"),
- cstr!("android,pvmfw,phy-iommu"),
- cstr!("android,pvmfw,phy-sid"),
- ];
- 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 {
- match node.nop_property(prop) {
- Err(FdtError::NotFound) => Ok(()), // allows not exists
- other => other,
- }?;
- }
- }
-
Ok(())
}
@@ -446,19 +509,42 @@
#[cfg(test)]
mod tests {
use super::*;
- use alloc::collections::BTreeSet;
+ use alloc::collections::{BTreeMap, 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_WITHOUT_IOMMUS_FILE_PATH: &str = "test_pvmfw_devices_without_iommus.dtb";
+ const FDT_WITHOUT_DEVICE_FILE_PATH: &str = "test_pvmfw_devices_without_device.dtb";
const FDT_FILE_PATH: &str = "test_pvmfw_devices_with_rng.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, Default)]
+ struct MockHypervisor {
+ mmio_tokens: BTreeMap<(u64, u64), u64>,
+ iommu_tokens: BTreeMap<(u64, u64), (u64, u64)>,
+ }
+
+ impl DeviceAssigningHypervisor for MockHypervisor {
+ fn get_phys_mmio_token(&self, base_ipa: u64, size: u64) -> hyp::Result<u64> {
+ Ok(*self.mmio_tokens.get(&(base_ipa, size)).ok_or(hyp::Error::KvmError(
+ hyp::KvmError::InvalidParameter,
+ 0xc6000012, /* VENDOR_HYP_KVM_DEV_REQ_MMIO_FUNC_ID */
+ ))?)
+ }
+
+ fn get_phys_iommu_token(&self, pviommu_id: u64, vsid: u64) -> hyp::Result<(u64, u64)> {
+ Ok(*self.iommu_tokens.get(&(pviommu_id, vsid)).ok_or(hyp::Error::KvmError(
+ hyp::KvmError::InvalidParameter,
+ 0xc6000013, /* VENDOR_HYP_KVM_DEV_REQ_DMA_FUNC_ID */
+ ))?)
+ }
+ }
+
#[derive(Debug, Eq, PartialEq)]
struct AssignedDeviceNode {
path: CString,
@@ -473,8 +559,7 @@
return Err(FdtError::NotFound.into());
};
- // TODO(b/277993056): Replace DeviceAssignmentError::Internal
- let reg = node.getprop(cstr!("reg"))?.ok_or(DeviceAssignmentError::Internal)?;
+ let reg = node.getprop(cstr!("reg"))?.ok_or(DeviceAssignmentError::InvalidReg)?;
let interrupts = node
.getprop(cstr!("interrupts"))?
.ok_or(DeviceAssignmentError::InvalidInterrupts)?;
@@ -524,6 +609,12 @@
v
}
+ impl From<[u64; 2]> for DeviceReg {
+ fn from(fdt_cells: [u64; 2]) -> Self {
+ DeviceReg { addr: fdt_cells[0], size: fdt_cells[1] }
+ }
+ }
+
#[test]
fn device_info_new_without_symbols() {
let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
@@ -531,7 +622,20 @@
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).unwrap();
+ let hypervisor: MockHypervisor = Default::default();
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap();
+ assert_eq!(device_info, None);
+ }
+
+ #[test]
+ fn device_info_new_without_device() {
+ let mut fdt_data = fs::read(FDT_WITHOUT_DEVICE_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 hypervisor: MockHypervisor = Default::default();
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap();
assert_eq!(device_info, None);
}
@@ -542,12 +646,16 @@
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).unwrap().unwrap();
+ let hypervisor = MockHypervisor {
+ mmio_tokens: [((0x9, 0xFF), 0x300)].into(),
+ iommu_tokens: BTreeMap::new(),
+ };
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
let expected = [AssignedDeviceInfo {
- node_path: CString::new("/backlight").unwrap(),
- dtbo_node_path: cstr!("/fragment@backlight/__overlay__/backlight").into(),
- reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
+ node_path: CString::new("/bus0/backlight").unwrap(),
+ dtbo_node_path: cstr!("/fragment@backlight/__overlay__/bus0/backlight").into(),
+ reg: vec![[0x9, 0xFF].into()],
interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
iommus: vec![],
}];
@@ -562,12 +670,16 @@
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).unwrap().unwrap();
+ let hypervisor = MockHypervisor {
+ mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
+ iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
+ };
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
let expected = [AssignedDeviceInfo {
node_path: CString::new("/rng").unwrap(),
dtbo_node_path: cstr!("/fragment@rng/__overlay__/rng").into(),
- reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
+ reg: vec![[0x9, 0xFF].into()],
interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
iommus: vec![(PvIommu { id: 0x4 }, Vsid(0xFF0))],
}];
@@ -583,7 +695,8 @@
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();
+ let hypervisor: MockHypervisor = Default::default();
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap();
assert_eq!(device_info, None);
}
@@ -594,7 +707,11 @@
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).unwrap().unwrap();
+ let hypervisor = MockHypervisor {
+ mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
+ iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
+ };
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
device_info.filter(vm_dtbo).unwrap();
let vm_dtbo = vm_dtbo.as_mut();
@@ -608,7 +725,8 @@
let led = vm_dtbo.node(cstr!("/fragment@led/__overlay__/led")).unwrap();
assert_eq!(led, None);
- let backlight = vm_dtbo.node(cstr!("/fragment@backlight/__overlay__/backlight")).unwrap();
+ let backlight =
+ vm_dtbo.node(cstr!("/fragment@backlight/__overlay__/bus0/backlight")).unwrap();
assert_eq!(backlight, None);
let symbols_node = vm_dtbo.symbols().unwrap();
@@ -624,7 +742,11 @@
let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
let platform_dt = Fdt::create_empty_tree(data.as_mut_slice()).unwrap();
- let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+ let hypervisor = MockHypervisor {
+ mmio_tokens: [((0x9, 0xFF), 0x300)].into(),
+ iommu_tokens: BTreeMap::new(),
+ };
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
device_info.filter(vm_dtbo).unwrap();
// SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
@@ -633,6 +755,10 @@
}
device_info.patch(platform_dt).unwrap();
+ let rng_node = platform_dt.node(cstr!("/bus0/backlight")).unwrap().unwrap();
+ let phandle = rng_node.getprop_u32(cstr!("phandle")).unwrap();
+ assert_ne!(None, phandle);
+
// Note: Intentionally not using AssignedDeviceNode for matching all props.
type FdtResult<T> = libfdt::Result<T>;
let expected: Vec<(FdtResult<&CStr>, FdtResult<Vec<u8>>)> = vec![
@@ -640,10 +766,10 @@
(Ok(cstr!("compatible")), Ok(Vec::from(*b"android,backlight\0"))),
(Ok(cstr!("interrupts")), Ok(into_fdt_prop(vec![0x0, 0xF, 0x4]))),
(Ok(cstr!("iommus")), Ok(Vec::new())),
+ (Ok(cstr!("phandle")), Ok(into_fdt_prop(vec![phandle.unwrap()]))),
(Ok(cstr!("reg")), Ok(into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]))),
];
- let rng_node = platform_dt.node(cstr!("/backlight")).unwrap().unwrap();
let mut properties: Vec<_> = rng_node
.properties()
.unwrap()
@@ -669,7 +795,11 @@
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();
+ let hypervisor = MockHypervisor {
+ mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
+ iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
+ };
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
device_info.filter(vm_dtbo).unwrap();
// SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
@@ -703,7 +833,21 @@
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();
+ let hypervisor = MockHypervisor {
+ mmio_tokens: [
+ ((0x9, 0xFF), 0x12F00000),
+ ((0x100, 0x1000), 0xF00000),
+ ((0x200, 0x1000), 0xF10000),
+ ]
+ .into(),
+ iommu_tokens: [
+ ((0x4, 0xFF0), (0x12E40000, 3)),
+ ((0x40, 0xFFA), (0x40000, 0x4)),
+ ((0x50, 0xFFB), (0x50000, 0x5)),
+ ]
+ .into(),
+ };
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
device_info.filter(vm_dtbo).unwrap();
// SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
@@ -721,7 +865,7 @@
},
AssignedDeviceNode {
path: CString::new("/light").unwrap(),
- reg: into_fdt_prop(vec![0x100, 0x9]),
+ reg: into_fdt_prop(vec![0x0, 0x100, 0x0, 0x1000, 0x0, 0x200, 0x0, 0x1000]),
interrupts: into_fdt_prop(vec![0x0, 0xF, 0x5]),
iommus: vec![0x40, 0xFFA, 0x50, 0xFFB],
},
@@ -746,7 +890,11 @@
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();
+ let hypervisor = MockHypervisor {
+ mmio_tokens: [((0x9, 0xFF), 0x12F00000), ((0x100, 0x9), 0x12000000)].into(),
+ iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 3))].into(),
+ };
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
device_info.filter(vm_dtbo).unwrap();
// SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
@@ -764,7 +912,7 @@
},
AssignedDeviceNode {
path: CString::new("/led").unwrap(),
- reg: into_fdt_prop(vec![0x100, 0x9]),
+ reg: into_fdt_prop(vec![0x0, 0x100, 0x0, 0x9]),
interrupts: into_fdt_prop(vec![0x0, 0xF, 0x5]),
iommus: vec![0x4, 0xFF0],
},
@@ -786,8 +934,44 @@
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);
+ let hypervisor = MockHypervisor {
+ mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
+ iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
+ };
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
assert_eq!(device_info, Err(DeviceAssignmentError::DuplicatedPvIommuIds));
}
+
+ #[test]
+ fn device_info_invalid_reg() {
+ let mut fdt_data = fs::read(FDT_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 hypervisor = MockHypervisor {
+ mmio_tokens: BTreeMap::new(),
+ iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
+ };
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
+
+ assert_eq!(device_info, Err(DeviceAssignmentError::InvalidReg));
+ }
+
+ #[test]
+ fn device_info_invalid_iommus() {
+ let mut fdt_data = fs::read(FDT_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 hypervisor = MockHypervisor {
+ mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
+ iommu_tokens: BTreeMap::new(),
+ };
+ let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
+
+ assert_eq!(device_info, Err(DeviceAssignmentError::InvalidIommus));
+ }
}
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index f4078a3..03f2f62 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -207,7 +207,7 @@
// then remapped by `init_page_table()`.
let appended_data = unsafe { get_appended_data_slice() };
- let mut appended = AppendedPayload::new(appended_data).ok_or_else(|| {
+ let appended = AppendedPayload::new(appended_data).ok_or_else(|| {
error!("No valid configuration found");
RebootReason::InvalidConfig
})?;
@@ -438,7 +438,7 @@
}
}
- fn get_entries(&mut self) -> config::Entries<'_> {
+ fn get_entries(self) -> config::Entries<'a> {
match self {
Self::Config(cfg) => cfg.get_entries(),
Self::LegacyBcc(bcc) => config::Entries { bcc, ..Default::default() },
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index 7a89b75..2a6819b 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -15,8 +15,7 @@
//! High-level FDT functions.
use crate::bootargs::BootArgsIterator;
-use crate::device_assignment::DeviceAssignmentInfo;
-use crate::device_assignment::VmDtbo;
+use crate::device_assignment::{DeviceAssignmentInfo, VmDtbo};
use crate::helpers::GUEST_PAGE_SIZE;
use crate::Box;
use crate::RebootReason;
@@ -201,19 +200,27 @@
Ok(())
}
-fn read_vendor_public_key_from(fdt: &Fdt) -> libfdt::Result<Option<Vec<u8>>> {
+fn read_vendor_hashtree_descriptor_root_digest_from(fdt: &Fdt) -> libfdt::Result<Option<Vec<u8>>> {
if let Some(avf_node) = fdt.node(cstr!("/avf"))? {
- if let Some(vendor_public_key) = avf_node.getprop(cstr!("vendor_public_key"))? {
- return Ok(Some(vendor_public_key.to_vec()));
+ if let Some(vendor_hashtree_descriptor_root_digest) =
+ avf_node.getprop(cstr!("vendor_hashtree_descriptor_root_digest"))?
+ {
+ return Ok(Some(vendor_hashtree_descriptor_root_digest.to_vec()));
}
}
Ok(None)
}
-fn patch_vendor_public_key(fdt: &mut Fdt, vendor_public_key: &[u8]) -> libfdt::Result<()> {
+fn patch_vendor_hashtree_descriptor_root_digest(
+ fdt: &mut Fdt,
+ vendor_hashtree_descriptor_root_digest: &[u8],
+) -> libfdt::Result<()> {
let mut root_node = fdt.root_mut()?;
let mut avf_node = root_node.add_subnode(cstr!("/avf"))?;
- avf_node.setprop(cstr!("vendor_public_key"), vendor_public_key)?;
+ avf_node.setprop(
+ cstr!("vendor_hashtree_descriptor_root_digest"),
+ vendor_hashtree_descriptor_root_digest,
+ )?;
Ok(())
}
@@ -609,7 +616,7 @@
serial_info: SerialInfo,
pub swiotlb_info: SwiotlbInfo,
device_assignment: Option<DeviceAssignmentInfo>,
- vendor_public_key: Option<Vec<u8>>,
+ vendor_hashtree_descriptor_root_digest: Option<Vec<u8>>,
}
impl DeviceTreeInfo {
@@ -721,24 +728,35 @@
validate_swiotlb_info(&swiotlb_info, &memory_range)?;
let device_assignment = match vm_dtbo {
- Some(vm_dtbo) => DeviceAssignmentInfo::parse(fdt, vm_dtbo).map_err(|e| {
- error!("Failed to parse device assignment from DT and VM DTBO: {e}");
- RebootReason::InvalidFdt
- })?,
+ Some(vm_dtbo) => {
+ if let Some(hypervisor) = hyp::get_device_assigner() {
+ DeviceAssignmentInfo::parse(fdt, vm_dtbo, hypervisor).map_err(|e| {
+ error!("Failed to parse device assignment from DT and VM DTBO: {e}");
+ RebootReason::InvalidFdt
+ })?
+ } else {
+ warn!(
+ "Device assignment is ignored because device assigning hypervisor is missing"
+ );
+ None
+ }
+ }
None => None,
};
// TODO(b/285854379) : A temporary solution lives. This is for enabling
// microdroid vendor partition for non-protected VM as well. When passing
- // DT path containing vendor_public_key via fstab, init stage will check
- // if vendor_public_key exists in the init stage, regardless the protection.
- // Adding this temporary solution will prevent fatal in init stage for
- // protected VM. However, this data is not trustable without validating
- // with vendor public key value comes from ABL.
- let vendor_public_key = read_vendor_public_key_from(fdt).map_err(|e| {
- error!("Failed to read vendor_public_key from DT: {e}");
- RebootReason::InvalidFdt
- })?;
+ // DT path containing vendor_hashtree_descriptor_root_digest via fstab, init
+ // stage will check if vendor_hashtree_descriptor_root_digest exists in the
+ // init stage, regardless the protection. Adding this temporary solution
+ // will prevent fatal in init stage for protected VM. However, this data is
+ // not trustable without validating root digest of vendor hashtree
+ // descriptor comes from ABL.
+ let vendor_hashtree_descriptor_root_digest =
+ read_vendor_hashtree_descriptor_root_digest_from(fdt).map_err(|e| {
+ error!("Failed to read vendor_hashtree_descriptor_root_digest from DT: {e}");
+ RebootReason::InvalidFdt
+ })?;
Ok(DeviceTreeInfo {
kernel_range,
@@ -750,7 +768,7 @@
serial_info,
swiotlb_info,
device_assignment,
- vendor_public_key,
+ vendor_hashtree_descriptor_root_digest,
})
}
@@ -803,9 +821,12 @@
RebootReason::InvalidFdt
})?;
}
- if let Some(vendor_public_key) = &info.vendor_public_key {
- patch_vendor_public_key(fdt, vendor_public_key).map_err(|e| {
- error!("Failed to patch vendor_public_key to DT: {e}");
+ if let Some(vendor_hashtree_descriptor_root_digest) =
+ &info.vendor_hashtree_descriptor_root_digest
+ {
+ patch_vendor_hashtree_descriptor_root_digest(fdt, vendor_hashtree_descriptor_root_digest)
+ .map_err(|e| {
+ error!("Failed to patch vendor_hashtree_descriptor_root_digest to DT: {e}");
RebootReason::InvalidFdt
})?;
}
@@ -819,7 +840,7 @@
bcc: &[u8],
new_instance: bool,
strict_boot: bool,
- debug_policy: Option<&mut [u8]>,
+ debug_policy: Option<&[u8]>,
debuggable: bool,
kaslr_seed: u64,
) -> libfdt::Result<()> {
diff --git a/pvmfw/src/instance.rs b/pvmfw/src/instance.rs
index f2cd6a3..a998bfb 100644
--- a/pvmfw/src/instance.rs
+++ b/pvmfw/src/instance.rs
@@ -141,6 +141,17 @@
let decrypted = aead.open(&mut entry, payload).map_err(Error::FailedOpen)?;
let body = EntryBody::read_from(decrypted).unwrap();
+ if dice_inputs.rkp_vm_marker {
+ // The RKP VM is allowed to run if it has passed the verified boot check and
+ // contains the expected version in its AVB footer.
+ // The comparison below with the previous boot information is skipped to enable the
+ // simultaneous update of the pvmfw and RKP VM.
+ // For instance, when both the pvmfw and RKP VM are updated, the code hash of the
+ // RKP VM will differ from the one stored in the instance image. In this case, the
+ // RKP VM is still allowed to run.
+ // This ensures that the updated RKP VM will retain the same CDIs in the next stage.
+ return Ok((false, body.salt));
+ }
if body.code_hash != dice_inputs.code_hash {
Err(Error::RecordedCodeHashMismatch)
} else if body.auth_hash != dice_inputs.auth_hash {
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 8aa5274..09bb899 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -63,7 +63,7 @@
signed_kernel: &[u8],
ramdisk: Option<&[u8]>,
current_bcc_handover: &[u8],
- mut debug_policy: Option<&mut [u8]>,
+ mut debug_policy: Option<&[u8]>,
) -> Result<Range<usize>, RebootReason> {
info!("pVM firmware");
debug!("FDT: {:?}", fdt.as_ptr());
@@ -115,6 +115,17 @@
if verified_boot_data.has_capability(Capability::RemoteAttest) {
info!("Service VM capable of remote attestation detected");
+ if service_vm_version::VERSION != verified_boot_data.rollback_index {
+ // For RKP VM, we only boot if the version in the AVB footer of its kernel matches
+ // the one embedded in pvmfw at build time.
+ // This prevents the pvmfw from booting a roll backed RKP VM.
+ error!(
+ "Service VM version mismatch: expected {}, found {}",
+ service_vm_version::VERSION,
+ verified_boot_data.rollback_index
+ );
+ return Err(RebootReason::InvalidPayload);
+ }
}
if verified_boot_data.has_capability(Capability::SecretkeeperProtection) {
diff --git a/pvmfw/testdata/test_pvmfw_devices_vm_dtbo.dts b/pvmfw/testdata/test_pvmfw_devices_vm_dtbo.dts
index da08694..91693f7 100644
--- a/pvmfw/testdata/test_pvmfw_devices_vm_dtbo.dts
+++ b/pvmfw/testdata/test_pvmfw_devices_vm_dtbo.dts
@@ -1,61 +1,118 @@
/dts-v1/;
-/plugin/;
/ {
- fragment@rng {
- target-path = "/";
- __overlay__ {
- rng {
- compatible = "android,rng";
- android,rng,ignore-gctrl-reset;
- android,pvmfw,phy-reg = <0x0 0x12F00000 0x1000>;
- android,pvmfw,phy-iommu = <0x0 0x12E40000>;
- android,pvmfw,phy-sid = <3>;
- };
- };
- };
-
- fragment@sensor {
- target-path = "/";
- __overlay__ {
- light {
- compatible = "android,light";
- version = <0x1 0x2>;
- android,pvmfw,phy-reg = <0x0 0xF00000 0x1000>;
- android,pvmfw,phy-iommu = <0x0 0x40000>, <0x0 0x50000>;
- android,pvmfw,phy-sid = <4>, <5>;
- };
- };
- };
-
- fragment@led {
- target-path = "/";
- __overlay__ {
- led {
- compatible = "android,led";
- prop = <0x555>;
- android,pvmfw,phy-reg = <0x0 0x12000000 0x1000>;
- android,pvmfw,phy-iommu = <0x0 0x12E40000>;
- android,pvmfw,phy-sid = <3>;
- };
- };
- };
-
- fragment@backlight {
- target-path = "/";
- __overlay__ {
- backlight {
- compatible = "android,backlight";
- android,backlight,ignore-gctrl-reset;
- android,pvmfw,phy-reg = <0x0 0x300 0x100>;
- };
- };
- };
-
- __symbols__ {
- rng = "/fragment@rng/__overlay__/rng";
- sensor = "/fragment@sensor/__overlay__/light";
- led = "/fragment@led/__overlay__/led";
- backlight = "/fragment@backlight/__overlay__/backlight";
- };
+ host {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ rng {
+ reg = <0x0 0x12f00000 0x1000>;
+ iommus = <0x1 0x3>;
+ android,pvmfw,target = <0x2>;
+ };
+ light {
+ reg = <0x0 0x00f00000 0x1000>, <0x0 0x00f10000 0x1000>;
+ iommus = <0x3 0x4>, <0x4 0x5>;
+ android,pvmfw,target = <0x5>;
+ };
+ led {
+ reg = <0x0 0x12000000 0x1000>;
+ iommus = <0x1 0x3>;
+ android,pvmfw,target = <0x6>;
+ };
+ bus0 {
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ backlight {
+ reg = <0x300 0x100>;
+ android,pvmfw,target = <0x7>;
+ };
+ };
+ iommu0 {
+ #iommu-cells = <0x1>;
+ android,pvmfw,token = <0x0 0x12e40000>;
+ phandle = <0x1>;
+ };
+ iommu1 {
+ #iommu-cells = <0x1>;
+ android,pvmfw,token = <0x0 0x40000>;
+ phandle = <0x3>;
+ };
+ iommu2 {
+ #iommu-cells = <0x1>;
+ android,pvmfw,token = <0x0 0x50000>;
+ phandle = <0x4>;
+ };
+ };
+ fragment@rng {
+ target-path = "/";
+ __overlay__ {
+ rng {
+ compatible = "android,rng";
+ android,rng,ignore-gctrl-reset;
+ phandle = <0x2>;
+ };
+ };
+ };
+ fragment@sensor {
+ target-path = "/";
+ __overlay__ {
+ light {
+ compatible = "android,light";
+ version = <0x1 0x2>;
+ phandle = <0x5>;
+ };
+ };
+ };
+ fragment@led {
+ target-path = "/";
+ __overlay__ {
+ led {
+ compatible = "android,led";
+ prop = <0x555>;
+ phandle = <0x6>;
+ };
+ };
+ };
+ fragment@backlight {
+ target-path = "/";
+ __overlay__ {
+ bus0 {
+ backlight {
+ compatible = "android,backlight";
+ android,backlight,ignore-gctrl-reset;
+ phandle = <0x7>;
+ };
+ };
+ };
+ };
+ __symbols__ {
+ iommu0 = "/host/iommu0";
+ iommu1 = "/host/iommu1";
+ iommu2 = "/host/iommu2";
+ rng = "/fragment@rng/__overlay__/rng";
+ light = "/fragment@sensor/__overlay__/light";
+ led = "/fragment@led/__overlay__/led";
+ backlight = "/fragment@backlight/__overlay__/bus0/backlight";
+ };
+ __local_fixups__ {
+ host {
+ rng {
+ iommus = <0x0>;
+ android,pvmfw,target = <0x0>;
+ };
+ light {
+ iommus = <0x0 0x8>;
+ android,pvmfw,target = <0x0>;
+ };
+ led {
+ iommus = <0x0>;
+ android,pvmfw,target = <0x0>;
+ };
+ bus0 {
+ backlight {
+ android,pvmfw,target = <0x0>;
+ };
+ };
+ };
+ };
};
diff --git a/pvmfw/testdata/test_pvmfw_devices_vm_dtbo_without_symbols.dts b/pvmfw/testdata/test_pvmfw_devices_vm_dtbo_without_symbols.dts
index 18b9e79..2bc8081 100644
--- a/pvmfw/testdata/test_pvmfw_devices_vm_dtbo_without_symbols.dts
+++ b/pvmfw/testdata/test_pvmfw_devices_vm_dtbo_without_symbols.dts
@@ -1,43 +1,114 @@
/dts-v1/;
-/plugin/;
/ {
- fragment@rng {
- target-path = "/";
- __overlay__ {
- rng {
- compatible = "android,rng";
- android,rng,ignore-gctrl-reset;
- android,pvmfw,phy-reg = <0x0 0x12F00000 0x1000>;
- android,pvmfw,phy-iommu = <0x0 0x12E40000>;
- android,pvmfw,phy-sid = <3>;
- };
- };
- };
-
- fragment@sensor {
- target-path = "/";
- __overlay__ {
- light {
- compatible = "android,light";
- version = <0x1 0x2>;
- android,pvmfw,phy-reg = <0x0 0xF00000 0x1000>;
- android,pvmfw,phy-iommu = <0x0 0x40000>, <0x0 0x50000>;
- android,pvmfw,phy-sid = <4>, <5>;
- };
- };
- };
-
- fragment@led {
- target-path = "/";
- __overlay__ {
- led {
- compatible = "android,led";
- prop;
- android,pvmfw,phy-reg = <0x0 0x12F00000 0x1000>;
- android,pvmfw,phy-iommu = <0x0 0x20000>, <0x0 0x30000>;
- android,pvmfw,phy-sid = <7>, <8>;
- };
- };
- };
+ host {
+ #address-cells = <0x2>;
+ #size-cells = <0x1>;
+ rng {
+ reg = <0x0 0x12f00000 0x1000>;
+ iommus = <0x1 0x3>;
+ android,pvmfw,target = <0x2>;
+ };
+ light {
+ reg = <0x0 0x00f00000 0x1000>, <0x0 0x00f10000 0x1000>;
+ iommus = <0x3 0x4>, <0x4 0x5>;
+ android,pvmfw,target = <0x5>;
+ };
+ led {
+ reg = <0x0 0x12000000 0x1000>;
+ iommus = <0x1 0x3>;
+ android,pvmfw,target = <0x6>;
+ };
+ bus0 {
+ #address-cells = <0x1>;
+ #size-cells = <0x1>;
+ backlight {
+ reg = <0x300 0x100>;
+ android,pvmfw,target = <0x7>;
+ };
+ };
+ iommu0 {
+ #iommu-cells = <0x1>;
+ android,pvmfw,token = <0x0 0x12e40000>;
+ phandle = <0x1>;
+ };
+ iommu1 {
+ #iommu-cells = <0x1>;
+ android,pvmfw,token = <0x0 0x40000>;
+ phandle = <0x3>;
+ };
+ iommu2 {
+ #iommu-cells = <0x1>;
+ android,pvmfw,token = <0x0 0x50000>;
+ phandle = <0x4>;
+ };
+ };
+ fragment@rng {
+ target-path = "/";
+ __overlay__ {
+ rng {
+ compatible = "android,rng";
+ android,rng,ignore-gctrl-reset;
+ phandle = <0x2>;
+ };
+ };
+ };
+ fragment@sensor {
+ target-path = "/";
+ __overlay__ {
+ light {
+ compatible = "android,light";
+ version = <0x1 0x2>;
+ phandle = <0x5>;
+ };
+ };
+ };
+ fragment@led {
+ target-path = "/";
+ __overlay__ {
+ led {
+ compatible = "android,led";
+ prop = <0x555>;
+ phandle = <0x6>;
+ };
+ };
+ };
+ fragment@backlight {
+ target-path = "/";
+ __overlay__ {
+ bus0 {
+ backlight {
+ compatible = "android,backlight";
+ android,backlight,ignore-gctrl-reset;
+ phandle = <0x7>;
+ };
+ };
+ };
+ };
+ __symbols__ {
+ iommu0 = "/host/iommu0";
+ iommu1 = "/host/iommu1";
+ iommu2 = "/host/iommu2";
+ };
+ __local_fixups__ {
+ host {
+ rng {
+ iommus = <0x0>;
+ android,pvmfw,target = <0x0>;
+ };
+ light {
+ iommus = <0x0 0x8>;
+ android,pvmfw,target = <0x0>;
+ };
+ led {
+ iommus = <0x0>;
+ android,pvmfw,target = <0x0>;
+ };
+ bus0 {
+ backlight {
+ android,pvmfw,target = <0x0>;
+ };
+ };
+ };
+ };
};
diff --git a/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts b/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts
index 70b633c..a9e30be 100644
--- a/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts
+++ b/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts
@@ -27,7 +27,7 @@
light@70000000 {
compatible = "android,light";
- reg = <0x100 0x9>;
+ reg = <0x0 0x100 0x0 0x100>, <0x0 0x200 0x0 0x100>;
interrupts = <0x0 0xF 0x5>;
iommus = <&pviommu_a 0xA>, <&pviommu_b 0xB>;
};
diff --git a/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts b/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts
index 7c6d2f2..78ff868 100644
--- a/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts
+++ b/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts
@@ -14,8 +14,8 @@
};
led@70000000 {
- compatible = "android,light";
- reg = <0x100 0x9>;
+ compatible = "android,led";
+ reg = <0x0 0x100 0x0 0x9>;
interrupts = <0x0 0xF 0x5>;
iommus = <&pviommu_0 0xFF0>;
};
diff --git a/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts b/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts
index 76c99c9..ca7e7f3 100644
--- a/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts
+++ b/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts
@@ -20,7 +20,7 @@
light@70000000 {
compatible = "android,light";
- reg = <0x100 0x9>;
+ reg = <0x0 0x100 0x0 0x1000>, <0x0 0x200 0x0 0x1000>;
interrupts = <0x0 0xF 0x5>;
iommus = <&pviommu_a 0xFFA>, <&pviommu_b 0xFFB>;
};
diff --git a/pvmfw/testdata/test_pvmfw_devices_without_device.dts b/pvmfw/testdata/test_pvmfw_devices_without_device.dts
new file mode 100644
index 0000000..ee0be3a
--- /dev/null
+++ b/pvmfw/testdata/test_pvmfw_devices_without_device.dts
@@ -0,0 +1,7 @@
+/dts-v1/;
+/plugin/;
+
+/include/ "test_crosvm_dt_base.dtsi"
+
+/ {
+};
diff --git a/pvmfw/testdata/test_pvmfw_devices_without_iommus.dts b/pvmfw/testdata/test_pvmfw_devices_without_iommus.dts
index 2036c9c..1a12c87 100644
--- a/pvmfw/testdata/test_pvmfw_devices_without_iommus.dts
+++ b/pvmfw/testdata/test_pvmfw_devices_without_iommus.dts
@@ -4,11 +4,16 @@
/include/ "test_crosvm_dt_base.dtsi"
/ {
- backlight@90000000 {
- compatible = "android,backlight";
- reg = <0x0 0x9 0x0 0xFF>;
- interrupts = <0x0 0xF 0x4>;
- google,eh,ignore-gctrl-reset;
- status = "okay";
+ bus0 {
+ #address-cells = <0x2>;
+ #size-cells = <0x2>;
+
+ backlight@90000000 {
+ compatible = "android,backlight";
+ reg = <0x0 0x9 0x0 0xFF>;
+ interrupts = <0x0 0xF 0x4>;
+ google,eh,ignore-gctrl-reset;
+ status = "okay";
+ };
};
};
diff --git a/rialto/Android.bp b/rialto/Android.bp
index bbb5e54..5e7fe1f 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -63,6 +63,28 @@
srcs: [":avb_testkey_rsa4096"],
}
+// Both SERVICE_VM_VERSION and SERVICE_VM_VERSION_STRING should represent the
+// same version number for the service VM.
+SERVICE_VM_VERSION = 1
+SERVICE_VM_VERSION_STRING = "1"
+
+genrule {
+ name: "service_vm_version_rs",
+ out: ["lib.rs"],
+ cmd: "(" +
+ " echo '#![no_std]';" +
+ " echo '#![allow(missing_docs)]';" +
+ " echo 'pub const VERSION: u64 = " + SERVICE_VM_VERSION_STRING + ";'" +
+ ") > $(out)",
+}
+
+rust_library_rlib {
+ name: "libservice_vm_version",
+ crate_name: "service_vm_version",
+ defaults: ["vmbase_rlib_defaults"],
+ srcs: [":service_vm_version_rs"],
+}
+
avb_add_hash_footer {
name: "rialto_signed",
src: ":empty_file",
@@ -70,6 +92,7 @@
partition_name: "boot",
private_key: ":rialto_sign_key",
salt: rialto_salt,
+ rollback_index: SERVICE_VM_VERSION,
props: [
{
name: "com.android.virt.cap",
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 60f3e52..6f1286f 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -164,6 +164,12 @@
if (!updateBootconfigs) {
command.add("--do_not_update_bootconfigs");
}
+ // In some cases we run a CTS binary that is built from a different branch that the /system
+ // image under test. In such cases we might end up in a situation when avb_version used in
+ // CTS binary and avb_version used to sign the com.android.virt APEX do not match.
+ // This is a weird configuration, but unfortunately it can happen, hence we pass here
+ // --do_not_validate_avb_version flag to make sure that CTS doesn't fail on it.
+ command.add("--do_not_validate_avb_version");
keyOverrides.forEach(
(filename, keyFile) ->
command.add("--key_override " + filename + "=" + keyFile.getPath()));
diff --git a/virtualizationmanager/Android.bp b/virtualizationmanager/Android.bp
index 33897b2..60c94fc 100644
--- a/virtualizationmanager/Android.bp
+++ b/virtualizationmanager/Android.bp
@@ -5,7 +5,11 @@
rust_defaults {
name: "virtualizationmanager_defaults",
crate_name: "virtualizationmanager",
- defaults: ["avf_build_flags_rust"],
+ defaults: [
+ "avf_build_flags_rust",
+ "secretkeeper_use_latest_hal_aidl_rust",
+ "authgraph_use_latest_hal_aidl_rust",
+ ],
edition: "2021",
// Only build on targets which crosvm builds on.
enabled: false,
@@ -34,6 +38,7 @@
"libclap",
"libcommand_fds",
"libdisk",
+ "libhex",
"libhypervisor_props",
"liblazy_static",
"liblibc",
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 7f98fe8..8c2099f 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -52,6 +52,14 @@
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
BnVirtualMachineService, IVirtualMachineService,
};
+use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::{BnSecretkeeper, ISecretkeeper};
+use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::SecretId::SecretId;
+use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
+ Arc::Arc as AuthgraphArc, IAuthGraphKeyExchange::IAuthGraphKeyExchange,
+ IAuthGraphKeyExchange::BnAuthGraphKeyExchange, Identity::Identity, KeInitResult::KeInitResult,
+ Key::Key, PubKey::PubKey, SessionIdSignature::SessionIdSignature, SessionInfo::SessionInfo,
+ SessionInitiationInfo::SessionInitiationInfo,
+};
use anyhow::{anyhow, bail, Context, Result};
use apkverify::{HashAlgorithm, V4Signature};
use avflog::LogResult;
@@ -102,9 +110,13 @@
const MICRODROID_OS_NAME: &str = "microdroid";
+// TODO(b/291213394): Use 'default' instance for secretkeeper instead of 'nonsecure'
+const SECRETKEEPER_IDENTIFIER: &str =
+ "android.hardware.security.secretkeeper.ISecretkeeper/nonsecure";
+
const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
-/// Roughly estimated sufficient size for storing vendor public key into DTBO.
+/// Rough size for storing root digest of vendor hash descriptor into DTBO.
const EMPTY_VENDOR_DT_OVERLAY_BUF_SIZE: usize = 10000;
/// crosvm requires all partitions to be a multiple of 4KiB.
@@ -369,13 +381,17 @@
check_gdb_allowed(config)?;
}
- let vendor_public_key = extract_vendor_public_key(config)
- .context("Failed to extract vendor public key")
- .or_service_specific_exception(-1)?;
- let dtbo_vendor = if let Some(vendor_public_key) = vendor_public_key {
+ let vendor_hashtree_descriptor_root_digest =
+ extract_vendor_hashtree_descriptor_root_digest(config)
+ .context("Failed to extract root digest of vendor")
+ .or_service_specific_exception(-1)?;
+ let dtbo_vendor = if let Some(vendor_hashtree_descriptor_root_digest) =
+ vendor_hashtree_descriptor_root_digest
+ {
+ let root_digest_hex = hex::encode(vendor_hashtree_descriptor_root_digest);
let dtbo_for_vendor_image = temporary_directory.join("dtbo_vendor");
- create_dtbo_for_vendor_image(&vendor_public_key, &dtbo_for_vendor_image)
- .context("Failed to write vendor_public_key")
+ create_dtbo_for_vendor_image(root_digest_hex.as_bytes(), &dtbo_for_vendor_image)
+ .context("Failed to write root digest of vendor")
.or_service_specific_exception(-1)?;
let file = File::open(dtbo_for_vendor_image)
.context("Failed to open dtbo_vendor")
@@ -559,7 +575,9 @@
}
}
-fn extract_vendor_public_key(config: &VirtualMachineConfig) -> Result<Option<Vec<u8>>> {
+fn extract_vendor_hashtree_descriptor_root_digest(
+ config: &VirtualMachineConfig,
+) -> Result<Option<Vec<u8>>> {
let VirtualMachineConfig::AppConfig(config) = config else {
return Ok(None);
};
@@ -574,15 +592,19 @@
let size = file.metadata().context("Failed to get metadata from microdroid-vendor.img")?.len();
let vbmeta = VbMetaImage::verify_reader_region(&file, 0, size)
.context("Failed to get vbmeta from microdroid-vendor.img")?;
- let vendor_public_key = vbmeta
- .public_key()
- .ok_or(anyhow!("No public key is extracted from microdroid-vendor.img"))?
- .to_vec();
- Ok(Some(vendor_public_key))
+ for descriptor in vbmeta.descriptors()?.iter() {
+ if let vbmeta::Descriptor::Hashtree(_) = descriptor {
+ return Ok(Some(descriptor.to_hashtree()?.root_digest().to_vec()));
+ }
+ }
+ Err(anyhow!("No root digest is extracted from microdroid-vendor.img"))
}
-fn create_dtbo_for_vendor_image(vendor_public_key: &[u8], dtbo: &PathBuf) -> Result<()> {
+fn create_dtbo_for_vendor_image(
+ vendor_hashtree_descriptor_root_digest: &[u8],
+ dtbo: &PathBuf,
+) -> Result<()> {
if dtbo.exists() {
return Err(anyhow!("DTBO file already exists"));
}
@@ -610,10 +632,16 @@
let mut avf_node = overlay_node
.add_subnode(avf_node_name.as_c_str())
.map_err(|e| anyhow!("Failed to create avf node: {:?}", e))?;
- let vendor_public_key_name = CString::new("vendor_public_key")?;
+ let vendor_hashtree_descriptor_root_digest_name =
+ CString::new("vendor_hashtree_descriptor_root_digest")?;
avf_node
- .setprop(vendor_public_key_name.as_c_str(), vendor_public_key)
- .map_err(|e| anyhow!("Failed to set avf/vendor_public_key: {:?}", e))?;
+ .setprop(
+ vendor_hashtree_descriptor_root_digest_name.as_c_str(),
+ vendor_hashtree_descriptor_root_digest,
+ )
+ .map_err(|e| {
+ anyhow!("Failed to set avf/vendor_hashtree_descriptor_root_digest: {:?}", e)
+ })?;
fdt.pack().map_err(|e| anyhow!("Failed to pack fdt: {:?}", e))?;
let mut file = File::create(dtbo)?;
@@ -1370,6 +1398,20 @@
}
}
+ fn getSecretkeeper(&self) -> binder::Result<Option<Strong<dyn ISecretkeeper>>> {
+ let sk = match binder::get_interface(SECRETKEEPER_IDENTIFIER) {
+ Ok(sk) => {
+ Some(BnSecretkeeper::new_binder(SecretkeeperProxy(sk), BinderFeatures::default()))
+ }
+ Err(StatusCode::NAME_NOT_FOUND) => None,
+ Err(e) => {
+ error!("unexpected error while fetching connection to Secretkeeper {:?}", e);
+ return Err(e.into());
+ }
+ };
+ Ok(sk)
+ }
+
fn requestAttestation(&self, csr: &[u8]) -> binder::Result<Vec<Certificate>> {
GLOBAL_SERVICE.requestAttestation(csr, get_calling_uid() as i32)
}
@@ -1502,13 +1544,14 @@
#[test]
fn test_create_dtbo_for_vendor_image() -> Result<()> {
- let vendor_public_key = String::from("foo");
- let vendor_public_key = vendor_public_key.as_bytes();
+ let vendor_hashtree_descriptor_root_digest = String::from("foo");
+ let vendor_hashtree_descriptor_root_digest =
+ vendor_hashtree_descriptor_root_digest.as_bytes();
let tmp_dir = tempfile::TempDir::new()?;
let dtbo_path = tmp_dir.path().to_path_buf().join("bar");
- create_dtbo_for_vendor_image(vendor_public_key, &dtbo_path)?;
+ create_dtbo_for_vendor_image(vendor_hashtree_descriptor_root_digest, &dtbo_path)?;
let data = std::fs::read(dtbo_path)?;
let fdt = Fdt::from_slice(&data).unwrap();
@@ -1529,9 +1572,11 @@
let Some(avf_node) = avf_node else {
bail!("avf_node shouldn't be None.");
};
- let vendor_public_key_name = CString::new("vendor_public_key")?;
- let key_from_dtbo = avf_node.getprop(vendor_public_key_name.as_c_str()).unwrap();
- assert_eq!(key_from_dtbo, Some(vendor_public_key));
+ let vendor_hashtree_descriptor_root_digest_name =
+ CString::new("vendor_hashtree_descriptor_root_digest")?;
+ let digest_from_dtbo =
+ avf_node.getprop(vendor_hashtree_descriptor_root_digest_name.as_c_str()).unwrap();
+ assert_eq!(digest_from_dtbo, Some(vendor_hashtree_descriptor_root_digest));
tmp_dir.close()?;
Ok(())
@@ -1539,18 +1584,84 @@
#[test]
fn test_create_dtbo_for_vendor_image_throws_error_if_already_exists() -> Result<()> {
- let vendor_public_key = String::from("foo");
- let vendor_public_key = vendor_public_key.as_bytes();
+ let vendor_hashtree_descriptor_root_digest = String::from("foo");
+ let vendor_hashtree_descriptor_root_digest =
+ vendor_hashtree_descriptor_root_digest.as_bytes();
let tmp_dir = tempfile::TempDir::new()?;
let dtbo_path = tmp_dir.path().to_path_buf().join("bar");
- create_dtbo_for_vendor_image(vendor_public_key, &dtbo_path)?;
+ create_dtbo_for_vendor_image(vendor_hashtree_descriptor_root_digest, &dtbo_path)?;
- let ret_second_trial = create_dtbo_for_vendor_image(vendor_public_key, &dtbo_path);
+ let ret_second_trial =
+ create_dtbo_for_vendor_image(vendor_hashtree_descriptor_root_digest, &dtbo_path);
assert!(ret_second_trial.is_err(), "should fail");
tmp_dir.close()?;
Ok(())
}
}
+
+struct SecretkeeperProxy(Strong<dyn ISecretkeeper>);
+
+impl Interface for SecretkeeperProxy {}
+
+impl ISecretkeeper for SecretkeeperProxy {
+ fn processSecretManagementRequest(&self, req: &[u8]) -> binder::Result<Vec<u8>> {
+ // Pass the request to the channel, and read the response.
+ self.0.processSecretManagementRequest(req)
+ }
+
+ fn getAuthGraphKe(&self) -> binder::Result<Strong<dyn IAuthGraphKeyExchange>> {
+ let ag = AuthGraphKeyExchangeProxy(self.0.getAuthGraphKe()?);
+ Ok(BnAuthGraphKeyExchange::new_binder(ag, BinderFeatures::default()))
+ }
+
+ fn deleteIds(&self, ids: &[SecretId]) -> binder::Result<()> {
+ self.0.deleteIds(ids)
+ }
+
+ fn deleteAll(&self) -> binder::Result<()> {
+ self.0.deleteAll()
+ }
+}
+
+struct AuthGraphKeyExchangeProxy(Strong<dyn IAuthGraphKeyExchange>);
+
+impl Interface for AuthGraphKeyExchangeProxy {}
+
+impl IAuthGraphKeyExchange for AuthGraphKeyExchangeProxy {
+ fn create(&self) -> binder::Result<SessionInitiationInfo> {
+ self.0.create()
+ }
+
+ fn init(
+ &self,
+ peer_pub_key: &PubKey,
+ peer_id: &Identity,
+ peer_nonce: &[u8],
+ peer_version: i32,
+ ) -> binder::Result<KeInitResult> {
+ self.0.init(peer_pub_key, peer_id, peer_nonce, peer_version)
+ }
+
+ fn finish(
+ &self,
+ peer_pub_key: &PubKey,
+ peer_id: &Identity,
+ peer_signature: &SessionIdSignature,
+ peer_nonce: &[u8],
+ peer_version: i32,
+ own_key: &Key,
+ ) -> binder::Result<SessionInfo> {
+ self.0.finish(peer_pub_key, peer_id, peer_signature, peer_nonce, peer_version, own_key)
+ }
+
+ fn authenticationComplete(
+ &self,
+ peer_signature: &SessionIdSignature,
+ shared_keys: &[AuthgraphArc; 2],
+ ) -> binder::Result<[AuthgraphArc; 2]> {
+ self.0.authenticationComplete(peer_signature, shared_keys)
+ }
+}
diff --git a/virtualizationservice/aidl/Android.bp b/virtualizationservice/aidl/Android.bp
index 91d91aa..8ca375a 100644
--- a/virtualizationservice/aidl/Android.bp
+++ b/virtualizationservice/aidl/Android.bp
@@ -57,11 +57,14 @@
aidl_interface {
name: "android.system.virtualmachineservice",
srcs: ["android/system/virtualmachineservice/**/*.aidl"],
- imports: ["android.system.virtualizationcommon"],
+ imports: [
+ "android.hardware.security.secretkeeper-V1",
+ "android.system.virtualizationcommon",
+ ],
unstable: true,
backend: {
java: {
- sdk_version: "module_current",
+ enabled: false,
},
rust: {
enabled: true,
diff --git a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
index 3c60478..cf91302 100644
--- a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
@@ -15,6 +15,7 @@
*/
package android.system.virtualmachineservice;
+import android.hardware.security.secretkeeper.ISecretkeeper;
import android.system.virtualizationcommon.Certificate;
import android.system.virtualizationcommon.ErrorCode;
@@ -54,4 +55,11 @@
* key's certificate chain. The attestation key is provided in the CSR.
*/
Certificate[] requestAttestation(in byte[] csr);
+
+ /**
+ * Request connection to Secretkeeper. This is used by pVM to store Anti-Rollback protected
+ * secrets. Note that the return value is nullable to reflect that Secretkeeper HAL may not be
+ * present.
+ */
+ @nullable ISecretkeeper getSecretkeeper();
}