Merge "Rename bssl-ffi to bssl-sys" into main
diff --git a/microdroid/init.rc b/microdroid/init.rc
index 4cc0475..672f47d 100644
--- a/microdroid/init.rc
+++ b/microdroid/init.rc
@@ -94,9 +94,6 @@
trigger post-fs-data
- # Load persist properties and override properties (if enabled) from /data.
- trigger load_persist_props_action
-
trigger early-boot
trigger boot
diff --git a/rialto/Android.bp b/rialto/Android.bp
index fcb1ce6..c102c89 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -142,7 +142,7 @@
"libservice_vm_fake_chain",
"libservice_vm_manager",
"libvmclient",
- "libx509_parser",
+ "libx509_cert_nostd",
],
data: [
":rialto_unsigned",
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index 02a5a28..c918db5 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -40,12 +40,13 @@
use std::io;
use std::panic;
use std::path::PathBuf;
+use std::str::FromStr;
use vmclient::VmInstance;
-use x509_parser::{
- certificate::X509Certificate,
- der_parser::{ber::BerObject, der::parse_der, oid, oid::Oid},
- prelude::FromDer,
- x509::{AlgorithmIdentifier, SubjectPublicKeyInfo, X509Version},
+use x509_cert::{
+ certificate::{Certificate, Version},
+ der::{self, asn1, Decode, Encode},
+ name::Name,
+ spki::{AlgorithmIdentifier, ObjectIdentifier, SubjectPublicKeyInfo},
};
const UNSIGNED_RIALTO_PATH: &str = "/data/local/tmp/rialto_test/arm64/rialto_unsigned.bin";
@@ -136,7 +137,12 @@
let dice_artifacts = fake_client_vm_dice_artifacts()?;
let attestation_data = generate_attestation_key_and_csr(&CHALLENGE, &dice_artifacts)?;
let cert_chain = fs::read(TEST_CERT_CHAIN_PATH)?;
- let (remaining, cert) = X509Certificate::from_der(&cert_chain)?;
+ // The certificate chain contains several certificates, but we only need the first one.
+ // Parsing the data with trailing data always fails with a `TrailingData` error.
+ let cert_len: usize = match Certificate::from_der(&cert_chain).unwrap_err().kind() {
+ der::ErrorKind::TrailingData { decoded, .. } => decoded.try_into().unwrap(),
+ e => bail!("Unexpected error: {e}"),
+ };
// Builds the mock parameters for the client VM attestation.
// The `csr` and `remotely_provisioned_key_blob` parameters are extracted from the same
@@ -146,7 +152,7 @@
let params = ClientVmAttestationParams {
csr: attestation_data.csr.clone().into_cbor_vec()?,
remotely_provisioned_key_blob: remotely_provisioned_key_pair.key_blob.to_vec(),
- remotely_provisioned_cert: cert_chain[..(cert_chain.len() - remaining.len())].to_vec(),
+ remotely_provisioned_cert: cert_chain[..cert_len].to_vec(),
};
let request = Request::RequestClientVmAttestation(params);
@@ -162,7 +168,7 @@
&certificate,
&remotely_provisioned_key_pair.maced_public_key,
&attestation_data.csr,
- &cert,
+ &Certificate::from_der(&cert_chain[..cert_len]).unwrap(),
)?;
Ok(())
}
@@ -177,22 +183,26 @@
}
}
-fn check_vm_components(vm_components: &[BerObject]) -> Result<()> {
+fn check_vm_components(vm_components: &asn1::SequenceOf<asn1::Any, 4>) -> Result<()> {
let expected_components = fake_sub_components();
assert_eq!(expected_components.len(), vm_components.len());
- for i in 0..expected_components.len() {
- check_vm_component(&vm_components[i], &expected_components[i])?;
+ for (i, expected_component) in expected_components.iter().enumerate() {
+ check_vm_component(vm_components.get(i).unwrap(), expected_component)?;
}
Ok(())
}
-fn check_vm_component(vm_component: &BerObject, expected_component: &SubComponent) -> Result<()> {
- let vm_component = vm_component.as_sequence()?;
+fn check_vm_component(vm_component: &asn1::Any, expected_component: &SubComponent) -> Result<()> {
+ let vm_component = vm_component.decode_as::<asn1::SequenceOf<asn1::Any, 4>>().unwrap();
assert_eq!(4, vm_component.len());
- assert_eq!(expected_component.name, vm_component[0].as_str()?);
- assert_eq!(expected_component.version, vm_component[1].as_u64()?);
- assert_eq!(expected_component.code_hash, vm_component[2].as_slice()?);
- assert_eq!(expected_component.authority_hash, vm_component[3].as_slice()?);
+ let name = vm_component.get(0).unwrap().decode_as::<asn1::Utf8StringRef>().unwrap();
+ assert_eq!(expected_component.name, name.as_ref());
+ let version = vm_component.get(1).unwrap().decode_as::<u64>().unwrap();
+ assert_eq!(expected_component.version, version);
+ let code_hash = vm_component.get(2).unwrap().decode_as::<asn1::OctetString>().unwrap();
+ assert_eq!(expected_component.code_hash, code_hash.as_bytes());
+ let authority_hash = vm_component.get(3).unwrap().decode_as::<asn1::OctetString>().unwrap();
+ assert_eq!(expected_component.authority_hash, authority_hash.as_bytes());
Ok(())
}
@@ -200,22 +210,22 @@
certificate: &[u8],
maced_public_key: &[u8],
csr: &Csr,
- parent_certificate: &X509Certificate,
+ parent_certificate: &Certificate,
) -> Result<()> {
let cose_mac = CoseMac0::from_slice(maced_public_key)?;
let authority_public_key =
EcKey::from_cose_public_key_slice(&cose_mac.payload.unwrap()).unwrap();
- let (remaining, cert) = X509Certificate::from_der(certificate)?;
- assert!(remaining.is_empty());
+ let cert = Certificate::from_der(certificate).unwrap();
// Checks the certificate signature against the authority public key.
- const ECDSA_WITH_SHA_256: Oid<'static> = oid!(1.2.840 .10045 .4 .3 .2);
- let expected_algorithm =
- AlgorithmIdentifier { algorithm: ECDSA_WITH_SHA_256, parameters: None };
+ const ECDSA_WITH_SHA_256: ObjectIdentifier =
+ ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
+ let expected_algorithm = AlgorithmIdentifier { oid: ECDSA_WITH_SHA_256, parameters: None };
assert_eq!(expected_algorithm, cert.signature_algorithm);
- let digest = sha256(cert.tbs_certificate.as_ref()).unwrap();
+ let tbs_cert = cert.tbs_certificate;
+ let digest = sha256(&tbs_cert.to_der().unwrap()).unwrap();
authority_public_key
- .ecdsa_verify(cert.signature_value.as_ref(), &digest)
+ .ecdsa_verify(cert.signature.raw_bytes(), &digest)
.expect("Failed to verify the certificate signature with the authority public key");
// Checks that the certificate's subject public key is equal to the key in the CSR.
@@ -225,38 +235,39 @@
let subject_public_key = EcKey::from_cose_public_key_slice(&csr_payload.public_key).unwrap();
let expected_spki_data =
PKey::try_from(subject_public_key).unwrap().subject_public_key_info().unwrap();
- let (remaining, expected_spki) = SubjectPublicKeyInfo::from_der(&expected_spki_data)?;
- assert!(remaining.is_empty());
- assert_eq!(&expected_spki, cert.public_key());
+ let expected_spki = SubjectPublicKeyInfo::from_der(&expected_spki_data).unwrap();
+ assert_eq!(expected_spki, tbs_cert.subject_public_key_info);
// Checks the certificate extension.
- const ATTESTATION_EXTENSION_OID: Oid<'static> = oid!(1.3.6 .1 .4 .1 .11129 .2 .1 .29 .1);
- let extensions = cert.extensions();
+ const ATTESTATION_EXTENSION_OID: ObjectIdentifier =
+ ObjectIdentifier::new_unwrap("1.3.6.1.4.1.11129.2.1.29.1");
+ let extensions = tbs_cert.extensions.unwrap();
assert_eq!(1, extensions.len());
let extension = &extensions[0];
- assert_eq!(ATTESTATION_EXTENSION_OID, extension.oid);
+ assert_eq!(ATTESTATION_EXTENSION_OID, extension.extn_id);
assert!(!extension.critical);
- let (remaining, extension) = parse_der(extension.value)?;
- assert!(remaining.is_empty());
- let attestation_ext = extension.as_sequence()?;
+ let attestation_ext =
+ asn1::SequenceOf::<asn1::Any, 3>::from_der(extension.extn_value.as_bytes()).unwrap();
assert_eq!(3, attestation_ext.len());
- assert_eq!(csr_payload.challenge, attestation_ext[0].as_slice()?);
- let is_vm_secure = attestation_ext[1].as_bool()?;
+ let challenge = attestation_ext.get(0).unwrap().decode_as::<asn1::OctetString>().unwrap();
+ assert_eq!(csr_payload.challenge, challenge.as_bytes());
+ let is_vm_secure = attestation_ext.get(1).unwrap().decode_as::<bool>().unwrap();
assert!(
!is_vm_secure,
"The VM shouldn't be secure as the last payload added in the test is in Debug mode"
);
- let vm_components = attestation_ext[2].as_sequence()?;
- check_vm_components(vm_components)?;
+ let vm_components =
+ attestation_ext.get(2).unwrap().decode_as::<asn1::SequenceOf<asn1::Any, 4>>().unwrap();
+ check_vm_components(&vm_components)?;
// Checks other fields on the certificate
- assert_eq!(X509Version::V3, cert.version());
- assert_eq!(parent_certificate.validity(), cert.validity());
+ assert_eq!(Version::V3, tbs_cert.version);
+ assert_eq!(parent_certificate.tbs_certificate.validity, tbs_cert.validity);
assert_eq!(
- String::from("CN=Android Protected Virtual Machine Key"),
- cert.subject().to_string()
+ Name::from_str("CN=Android Protected Virtual Machine Key").unwrap(),
+ tbs_cert.subject
);
- assert_eq!(parent_certificate.subject(), cert.issuer());
+ assert_eq!(parent_certificate.tbs_certificate.subject, tbs_cert.issuer);
Ok(())
}
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index 5b1bf6c..6ebed50 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -28,7 +28,7 @@
use coset::{AsCborValue, CborSerializable, CoseSign, CoseSign1};
use der::{Decode, Encode};
use diced_open_dice::{DiceArtifacts, HASH_SIZE};
-use log::error;
+use log::{error, info};
use microdroid_kernel_hashes::{INITRD_DEBUG_HASH, INITRD_NORMAL_HASH, KERNEL_HASH};
use service_vm_comm::{ClientVmAttestationParams, Csr, CsrPayload, RequestProcessingError};
use x509_cert::{certificate::Certificate, name::Name};
@@ -86,9 +86,11 @@
// Builds the TBSCertificate.
// The serial number can be up to 20 bytes according to RFC5280 s4.1.2.2.
- // In this case, a serial number with a length of 20 bytes is used to ensure that each
+ // In this case, a serial number with a length of 16 bytes is used to ensure that each
// certificate signed by RKP VM has a unique serial number.
- let mut serial_number = [0u8; 20];
+ // Attention: Do not use 20 bytes here as when the MSB is 1, a leading 0 byte can be
+ // added during the encoding to make the serial number length exceed 20 bytes.
+ let mut serial_number = [0u8; 16];
rand_bytes(&mut serial_number)?;
let subject = Name::encode_from_string("CN=Android Protected Virtual Machine Key")?;
let rkp_cert = Certificate::from_der(¶ms.remotely_provisioned_cert)?;
@@ -98,6 +100,8 @@
} else {
Vec::new()
};
+
+ info!("The client VM DICE chain validation succeeded. Beginning to generate the certificate.");
let attestation_ext = cert::AttestationExtension::new(
&csr_payload.challenge,
client_vm_dice_chain.all_entries_are_secure(),
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 8c2099f..d775555 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -18,7 +18,7 @@
use crate::atom::{
write_vm_booted_stats, write_vm_creation_stats};
use crate::composite::make_composite_image;
-use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VfioDevice, VmContext, VmInstance, VmState};
+use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmContext, VmInstance, VmState};
use crate::debug_config::DebugConfig;
use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images, add_microdroid_vendor_image};
use crate::selinux::{getfilecon, SeContext};
@@ -510,14 +510,7 @@
.or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT);
}
}
- let devices = GLOBAL_SERVICE
- .bindDevicesToVfioDriver(&config.devices)?
- .into_iter()
- .map(|x| VfioDevice {
- sysfs_path: PathBuf::from(&x.sysfsPath),
- dtbo_label: x.dtboLabel,
- })
- .collect::<Vec<_>>();
+ let devices = GLOBAL_SERVICE.bindDevicesToVfioDriver(&config.devices)?;
let dtbo_file = File::from(
GLOBAL_SERVICE
.getDtboFile()?
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index f0c3e4b..4b3478e 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -47,6 +47,7 @@
VirtualMachineAppConfig::DebugLevel::DebugLevel
};
use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IGlobalVmContext::IGlobalVmContext;
+use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IBoundDevice::IBoundDevice;
use binder::Strong;
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
@@ -127,11 +128,7 @@
pub writable: bool,
}
-#[derive(Clone, Debug)]
-pub struct VfioDevice {
- pub sysfs_path: PathBuf,
- pub dtbo_label: String,
-}
+type VfioDevice = Strong<dyn IBoundDevice>;
/// The lifecycle state which the payload in the VM has reported itself to be in.
///
@@ -412,10 +409,7 @@
error!("Error removing temporary files from {:?}: {}", self.temporary_directory, e);
});
- // TODO(b/278008182): clean up assigned devices.
- for device in vfio_devices.iter() {
- info!("NOT RELEASING {device:?}");
- }
+ drop(vfio_devices); // Cleanup devices.
}
/// Waits until payload is started, or timeout expires. When timeout occurs, kill
@@ -706,7 +700,7 @@
fn vfio_argument_for_platform_device(device: &VfioDevice) -> Result<String, Error> {
// Check platform device exists
- let path = device.sysfs_path.canonicalize()?;
+ let path = Path::new(&device.getSysfsPath()?).canonicalize()?;
if !path.starts_with(SYSFS_PLATFORM_DEVICES_PATH) {
bail!("{path:?} is not a platform device");
}
@@ -718,7 +712,7 @@
}
if let Some(p) = path.to_str() {
- Ok(format!("--vfio={p},iommu=pkvm-iommu,dt-symbol={0}", device.dtbo_label))
+ Ok(format!("--vfio={p},iommu=pkvm-iommu,dt-symbol={0}", device.getDtboLabel()?))
} else {
bail!("invalid path {path:?}");
}
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 3f8d193..e0bb97f 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -32,9 +32,11 @@
"libavflog",
"libbinder_rs",
"libhypervisor_props",
+ "liblazy_static",
"liblibc",
"liblog_rust",
"libnix",
+ "libopenssl",
"librkpd_client",
"librustutils",
"libvmclient",
@@ -45,7 +47,6 @@
"libserde_xml_rs",
"libservice_vm_comm",
"libservice_vm_manager",
- "libx509_parser",
],
apex_available: ["com.android.virt"],
}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IBoundDevice.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IBoundDevice.aidl
new file mode 100644
index 0000000..4a37bf7
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IBoundDevice.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+package android.system.virtualizationservice_internal;
+
+/** A device bound to VFIO driver. */
+interface IBoundDevice {
+ /** Path to SysFS node of the device. */
+ String getSysfsPath();
+
+ /** DTBO label of the device. */
+ String getDtboLabel();
+}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVfioHandler.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVfioHandler.aidl
index 01906cb..2cf4efd 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVfioHandler.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVfioHandler.aidl
@@ -20,16 +20,22 @@
import android.system.virtualizationservice_internal.AtomVmBooted;
import android.system.virtualizationservice_internal.AtomVmCreationRequested;
import android.system.virtualizationservice_internal.AtomVmExited;
+import android.system.virtualizationservice_internal.IBoundDevice;
import android.system.virtualizationservice_internal.IGlobalVmContext;
/** VFIO related methods which should be done as root. */
interface IVfioHandler {
+ parcelable VfioDev {
+ String sysfsPath;
+ String dtboLabel;
+ }
/**
* Bind given devices to vfio driver.
*
- * @param devices paths of sysfs nodes of devices to assign.
+ * @param devices a list of pairs (sysfs path, DTBO node label) for devices.
+ * @return IBoundDevice list representing a VFIO bound devices.
*/
- void bindDevicesToVfioDriver(in String[] devices);
+ IBoundDevice[] bindDevicesToVfioDriver(in VfioDev[] devices);
/**
* Store VM DTBO via the file descriptor.
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
index a2cb693..dd94526 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
@@ -21,13 +21,10 @@
import android.system.virtualizationservice_internal.AtomVmBooted;
import android.system.virtualizationservice_internal.AtomVmCreationRequested;
import android.system.virtualizationservice_internal.AtomVmExited;
+import android.system.virtualizationservice_internal.IBoundDevice;
import android.system.virtualizationservice_internal.IGlobalVmContext;
interface IVirtualizationServiceInternal {
- parcelable BoundDevice {
- String sysfsPath;
- String dtboLabel;
- }
/**
* Removes the memlock rlimit of the calling process.
*
@@ -78,9 +75,9 @@
* Bind given devices to vfio driver.
*
* @param devices paths of sysfs nodes of devices to assign.
- * @return a list of pairs (sysfs path, DTBO node label) for devices.
+ * @return a list of IBoundDevices representing VFIO bound devices.
*/
- BoundDevice[] bindDevicesToVfioDriver(in String[] devices);
+ IBoundDevice[] bindDevicesToVfioDriver(in String[] devices);
/** Returns a read-only file descriptor of the VM DTBO file. */
ParcelFileDescriptor getDtboFile();
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 3ac1e60..a1a1fb9 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -28,15 +28,17 @@
AtomVmBooted::AtomVmBooted,
AtomVmCreationRequested::AtomVmCreationRequested,
AtomVmExited::AtomVmExited,
+ IBoundDevice::IBoundDevice,
IGlobalVmContext::{BnGlobalVmContext, IGlobalVmContext},
- IVirtualizationServiceInternal::BoundDevice::BoundDevice,
IVirtualizationServiceInternal::IVirtualizationServiceInternal,
IVfioHandler::{BpVfioHandler, IVfioHandler},
+ IVfioHandler::VfioDev::VfioDev,
};
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::VM_TOMBSTONES_SERVICE_PORT;
use anyhow::{anyhow, ensure, Context, Result};
use avflog::LogResult;
use binder::{self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, Status, Strong, IntoBinderResult};
+use lazy_static::lazy_static;
use libc::VMADDR_CID_HOST;
use log::{error, info, warn};
use rkpd_client::get_rkpd_attestation_key;
@@ -52,7 +54,7 @@
use tombstoned_client::{DebuggerdDumpType, TombstonedConnection};
use vsock::{VsockListener, VsockStream};
use nix::unistd::{chown, Uid};
-use x509_parser::{traits::FromDer, certificate::X509Certificate};
+use openssl::x509::X509;
/// The unique ID of a VM used (together with a port number) for vsock communication.
pub type Cid = u32;
@@ -71,6 +73,12 @@
const CHUNK_RECV_MAX_LEN: usize = 1024;
+lazy_static! {
+ static ref VFIO_SERVICE: Strong<dyn IVfioHandler> =
+ wait_for_interface(<BpVfioHandler as IVfioHandler>::get_descriptor())
+ .expect("Could not connect to VfioHandler");
+}
+
fn is_valid_guest_cid(cid: Cid) -> bool {
(GUEST_CID_MIN..=GUEST_CID_MAX).contains(&cid)
}
@@ -219,24 +227,26 @@
.collect::<Vec<_>>())
}
- fn bindDevicesToVfioDriver(&self, devices: &[String]) -> binder::Result<Vec<BoundDevice>> {
+ fn bindDevicesToVfioDriver(
+ &self,
+ devices: &[String],
+ ) -> binder::Result<Vec<Strong<dyn IBoundDevice>>> {
check_use_custom_virtual_machine()?;
- let vfio_service: Strong<dyn IVfioHandler> =
- wait_for_interface(<BpVfioHandler as IVfioHandler>::get_descriptor())?;
- vfio_service.bindDevicesToVfioDriver(devices)?;
-
- Ok(get_assignable_devices()?
+ let devices = get_assignable_devices()?
.device
.into_iter()
.filter_map(|x| {
if devices.contains(&x.sysfs_path) {
- Some(BoundDevice { sysfsPath: x.sysfs_path, dtboLabel: x.dtbo_label })
+ Some(VfioDev { sysfsPath: x.sysfs_path, dtboLabel: x.dtbo_label })
} else {
+ warn!("device {} is not assignable", x.sysfs_path);
None
}
})
- .collect::<Vec<_>>())
+ .collect::<Vec<VfioDev>>();
+
+ VFIO_SERVICE.bindDevicesToVfioDriver(devices.as_slice())
}
fn getDtboFile(&self) -> binder::Result<ParcelFileDescriptor> {
@@ -303,10 +313,10 @@
fn split_x509_certificate_chain(mut cert_chain: &[u8]) -> Result<Vec<Certificate>> {
let mut out = Vec::new();
while !cert_chain.is_empty() {
- let (remaining, _) = X509Certificate::from_der(cert_chain)?;
- let end = cert_chain.len() - remaining.len();
+ let cert = X509::from_der(cert_chain)?;
+ let end = cert.to_der()?.len();
out.push(Certificate { encodedCertificate: cert_chain[..end].to_vec() });
- cert_chain = remaining;
+ cert_chain = &cert_chain[end..];
}
Ok(out)
}
@@ -424,10 +434,7 @@
// Open a write-only file descriptor for vfio_handler.
let write_fd = File::create(&path).context("Failed to create VM DTBO file")?;
-
- let vfio_service: Strong<dyn IVfioHandler> =
- wait_for_interface(<BpVfioHandler as IVfioHandler>::get_descriptor())?;
- vfio_service.writeVmDtbo(&ParcelFileDescriptor::new(write_fd))?;
+ VFIO_SERVICE.writeVmDtbo(&ParcelFileDescriptor::new(write_fd))?;
// Open read-only. This FD will be cached and returned to clients.
let read_fd = File::open(&path).context("Failed to open VM DTBO file")?;
@@ -599,8 +606,8 @@
assert_eq!(4, cert_chain.len());
for cert in cert_chain {
- let (remaining, _) = X509Certificate::from_der(&cert.encodedCertificate)?;
- assert!(remaining.is_empty());
+ let x509_cert = X509::from_der(&cert.encodedCertificate)?;
+ assert_eq!(x509_cert.to_der()?.len(), cert.encodedCertificate.len());
}
Ok(())
}
diff --git a/virtualizationservice/vfio_handler/src/aidl.rs b/virtualizationservice/vfio_handler/src/aidl.rs
index 63f19c6..c0967af 100644
--- a/virtualizationservice/vfio_handler/src/aidl.rs
+++ b/virtualizationservice/vfio_handler/src/aidl.rs
@@ -15,10 +15,13 @@
//! Implementation of the AIDL interface of the VirtualizationService.
use anyhow::{anyhow, Context};
+use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IBoundDevice::{IBoundDevice, BnBoundDevice};
use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IVfioHandler::IVfioHandler;
+use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IVfioHandler::VfioDev::VfioDev;
use android_system_virtualizationservice_internal::binder::ParcelFileDescriptor;
-use binder::{self, ExceptionCode, Interface, IntoBinderResult};
+use binder::{self, BinderFeatures, ExceptionCode, Interface, IntoBinderResult, Strong};
use lazy_static::lazy_static;
+use log::error;
use std::fs::{read_link, write, File};
use std::io::{Read, Seek, SeekFrom, Write};
use std::mem::size_of;
@@ -30,6 +33,38 @@
FromBytes,
};
+// Device bound to VFIO driver.
+struct BoundDevice {
+ sysfs_path: String,
+ dtbo_label: String,
+}
+
+impl Interface for BoundDevice {}
+
+impl IBoundDevice for BoundDevice {
+ fn getSysfsPath(&self) -> binder::Result<String> {
+ Ok(self.sysfs_path.clone())
+ }
+
+ fn getDtboLabel(&self) -> binder::Result<String> {
+ Ok(self.dtbo_label.clone())
+ }
+}
+
+impl Drop for BoundDevice {
+ fn drop(&mut self) {
+ unbind_device(Path::new(&self.sysfs_path)).unwrap_or_else(|e| {
+ error!("did not restore {} driver: {}", self.sysfs_path, e);
+ });
+ }
+}
+
+impl BoundDevice {
+ fn new_binder(sysfs_path: String, dtbo_label: String) -> Strong<dyn IBoundDevice> {
+ BnBoundDevice::new_binder(BoundDevice { sysfs_path, dtbo_label }, BinderFeatures::default())
+ }
+}
+
#[derive(Debug, Default)]
pub struct VfioHandler {}
@@ -42,14 +77,22 @@
impl Interface for VfioHandler {}
impl IVfioHandler for VfioHandler {
- fn bindDevicesToVfioDriver(&self, devices: &[String]) -> binder::Result<()> {
+ fn bindDevicesToVfioDriver(
+ &self,
+ devices: &[VfioDev],
+ ) -> binder::Result<Vec<Strong<dyn IBoundDevice>>> {
// permission check is already done by IVirtualizationServiceInternal.
if !*IS_VFIO_SUPPORTED {
return Err(anyhow!("VFIO-platform not supported"))
.or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
}
- devices.iter().try_for_each(|x| bind_device(Path::new(x)))?;
- Ok(())
+ devices
+ .iter()
+ .map(|d| {
+ bind_device(Path::new(&d.sysfsPath))?;
+ Ok(BoundDevice::new_binder(d.sysfsPath.clone(), d.dtboLabel.clone()))
+ })
+ .collect::<binder::Result<Vec<_>>>()
}
fn writeVmDtbo(&self, dtbo_fd: &ParcelFileDescriptor) -> binder::Result<()> {
@@ -79,6 +122,11 @@
const VFIO_PLATFORM_DRIVER_PATH: &str = "/sys/bus/platform/drivers/vfio-platform";
const SYSFS_PLATFORM_DRIVERS_PROBE_PATH: &str = "/sys/bus/platform/drivers_probe";
const DT_TABLE_MAGIC: u32 = 0xd7b7ab1e;
+const VFIO_PLATFORM_DRIVER_NAME: &str = "vfio-platform";
+// To remove the override and match the device driver by "compatible" string again,
+// driver_override file must be cleared. Writing an empty string (same as
+// `echo -n "" > driver_override`) won't' clear the file, so append a newline char.
+const DEFAULT_DRIVER: &str = "\n";
/// The structure of DT table header in dtbo.img.
/// https://source.android.com/docs/core/architecture/dto/partitions
@@ -146,18 +194,15 @@
group.to_str()?.parse().ok()
}
-fn is_bound_to_vfio_driver(path: &Path) -> bool {
- let Ok(driver_path) = read_link(path.join("driver")) else {
- return false;
- };
- let Some(driver) = driver_path.file_name() else {
- return false;
- };
- driver.to_str().unwrap_or("") == "vfio-platform"
+fn current_driver(path: &Path) -> Option<String> {
+ let driver_path = read_link(path.join("driver")).ok()?;
+ let bound_driver = driver_path.file_name()?;
+ bound_driver.to_str().map(str::to_string)
}
-fn bind_vfio_driver(path: &Path) -> binder::Result<()> {
- if is_bound_to_vfio_driver(path) {
+// Try to bind device driver by writing its name to driver_override and triggering driver probe.
+fn try_bind_driver(path: &Path, driver: &str) -> binder::Result<()> {
+ if Some(driver) == current_driver(path).as_deref() {
// already bound
return Ok(());
}
@@ -177,10 +222,13 @@
.with_context(|| format!("could not unbind {device_str}"))
.or_service_specific_exception(-1)?;
}
+ if path.join("driver").exists() {
+ return Err(anyhow!("could not unbind {device_str}")).or_service_specific_exception(-1);
+ }
- // bind to VFIO
- write(path.join("driver_override"), b"vfio-platform")
- .with_context(|| format!("could not bind {device_str} to vfio-platform"))
+ // bind to new driver
+ write(path.join("driver_override"), driver.as_bytes())
+ .with_context(|| format!("could not bind {device_str} to '{driver}' driver"))
.or_service_specific_exception(-1)?;
write(SYSFS_PLATFORM_DRIVERS_PROBE_PATH, device_str.as_bytes())
@@ -188,13 +236,9 @@
.or_service_specific_exception(-1)?;
// final check
- if !is_bound_to_vfio_driver(path) {
- return Err(anyhow!("{path:?} still not bound to vfio driver"))
- .or_service_specific_exception(-1);
- }
-
- if get_device_iommu_group(path).is_none() {
- return Err(anyhow!("can't get iommu group for {path:?}"))
+ let new_driver = current_driver(path);
+ if new_driver.is_none() || Some(driver) != new_driver.as_deref() && driver != DEFAULT_DRIVER {
+ return Err(anyhow!("{path:?} still not bound to '{driver}' driver"))
.or_service_specific_exception(-1);
}
@@ -208,7 +252,29 @@
.or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?;
check_platform_device(&path)?;
- bind_vfio_driver(&path)
+ try_bind_driver(&path, VFIO_PLATFORM_DRIVER_NAME)?;
+
+ if get_device_iommu_group(&path).is_none() {
+ Err(anyhow!("can't get iommu group for {path:?}")).or_service_specific_exception(-1)
+ } else {
+ Ok(())
+ }
+}
+
+fn unbind_device(path: &Path) -> binder::Result<()> {
+ let path = path
+ .canonicalize()
+ .with_context(|| format!("can't canonicalize {path:?}"))
+ .or_binder_exception(ExceptionCode::ILLEGAL_ARGUMENT)?;
+
+ check_platform_device(&path)?;
+ try_bind_driver(&path, DEFAULT_DRIVER)?;
+
+ if Some(VFIO_PLATFORM_DRIVER_NAME) == current_driver(&path).as_deref() {
+ Err(anyhow!("{path:?} still bound to vfio driver")).or_service_specific_exception(-1)
+ } else {
+ Ok(())
+ }
}
fn get_dtbo_img_path() -> binder::Result<PathBuf> {