Merge "pvmfw: Validate incoming <reg> and <iommus> against phys'" 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/authfs/src/fusefs.rs b/authfs/src/fusefs.rs
index 64b340a..ab75dac 100644
--- a/authfs/src/fusefs.rs
+++ b/authfs/src/fusefs.rs
@@ -669,6 +669,7 @@
         mode: u32,
         _flags: u32,
         umask: u32,
+        _security_ctx: Option<&CStr>,
     ) -> io::Result<(Entry, Option<Self::Handle>, FuseOpenOptions)> {
         let new_inode = self.create_new_entry_with_ref_count(
             parent,
@@ -852,6 +853,7 @@
         name: &CStr,
         mode: u32,
         umask: u32,
+        _security_ctx: Option<&CStr>,
     ) -> io::Result<Entry> {
         let new_inode = self.create_new_entry_with_ref_count(
             parent,
diff --git a/libs/bssl/Android.bp b/libs/bssl/Android.bp
index bed3dfb..2bb5ba5 100644
--- a/libs/bssl/Android.bp
+++ b/libs/bssl/Android.bp
@@ -22,7 +22,7 @@
     ],
     rustlibs: [
         "libbssl_avf_error_nostd",
-        "libbssl_ffi_nostd",
+        "libbssl_sys_nostd",
         "libcbor_util_nostd",
         "libciborium_nostd",
         "libcoset_nostd",
diff --git a/libs/bssl/src/aead.rs b/libs/bssl/src/aead.rs
index e0c9fbb..9aa1885 100644
--- a/libs/bssl/src/aead.rs
+++ b/libs/bssl/src/aead.rs
@@ -16,10 +16,10 @@
 
 use crate::util::{check_int_result, to_call_failed_error};
 use bssl_avf_error::{ApiName, Result};
-use bssl_ffi::{
+use bssl_sys::{
     EVP_AEAD_CTX_free, EVP_AEAD_CTX_new, EVP_AEAD_CTX_open, EVP_AEAD_CTX_seal,
-    EVP_AEAD_max_overhead, EVP_AEAD_nonce_length, EVP_aead_aes_256_gcm, EVP_AEAD, EVP_AEAD_CTX,
-    EVP_AEAD_DEFAULT_TAG_LENGTH,
+    EVP_AEAD_max_overhead, EVP_AEAD_nonce_length, EVP_aead_aes_256_gcm,
+    EVP_aead_aes_256_gcm_randnonce, EVP_AEAD, EVP_AEAD_CTX, EVP_AEAD_DEFAULT_TAG_LENGTH,
 };
 use core::ptr::NonNull;
 
@@ -51,6 +51,17 @@
         Self(unsafe { &*p })
     }
 
+    /// AES-256 in Galois Counter Mode with internal nonce generation.
+    /// The 12-byte nonce is appended to the tag and is generated internally.
+    pub fn aes_256_gcm_randnonce() -> Self {
+        // SAFETY: This function does not access any Rust variables and simply returns
+        // a pointer to the static variable in BoringSSL.
+        let p = unsafe { EVP_aead_aes_256_gcm_randnonce() };
+        // SAFETY: The returned pointer should always be valid and points to a static
+        // `EVP_AEAD`.
+        Self(unsafe { &*p })
+    }
+
     /// Returns the maximum number of additional bytes added by the act of sealing data.
     pub fn max_overhead(&self) -> usize {
         // SAFETY: This function only reads from self.
diff --git a/libs/bssl/src/cbb.rs b/libs/bssl/src/cbb.rs
index 9b5f7fe..a48c714 100644
--- a/libs/bssl/src/cbb.rs
+++ b/libs/bssl/src/cbb.rs
@@ -14,7 +14,7 @@
 
 //! Helpers for using BoringSSL CBB (crypto byte builder) objects.
 
-use bssl_ffi::{CBB_init_fixed, CBB};
+use bssl_sys::{CBB_init_fixed, CBB};
 use core::marker::PhantomData;
 use core::mem::MaybeUninit;
 
diff --git a/libs/bssl/src/cbs.rs b/libs/bssl/src/cbs.rs
index 9718903..12671cf 100644
--- a/libs/bssl/src/cbs.rs
+++ b/libs/bssl/src/cbs.rs
@@ -14,7 +14,7 @@
 
 //! Helpers for using BoringSSL CBS (crypto byte string) objects.
 
-use bssl_ffi::{CBS_init, CBS};
+use bssl_sys::{CBS_init, CBS};
 use core::marker::PhantomData;
 use core::mem::MaybeUninit;
 
diff --git a/libs/bssl/src/curve25519.rs b/libs/bssl/src/curve25519.rs
index 499a3d0..4c1c398 100644
--- a/libs/bssl/src/curve25519.rs
+++ b/libs/bssl/src/curve25519.rs
@@ -17,8 +17,8 @@
 use crate::util::check_int_result;
 use bssl_avf_error::{ApiName, Result};
 
-const ED25519_PUBLIC_KEY_LEN: usize = bssl_ffi::ED25519_PUBLIC_KEY_LEN as usize;
-const ED25519_SIGNATURE_LEN: usize = bssl_ffi::ED25519_SIGNATURE_LEN as usize;
+const ED25519_PUBLIC_KEY_LEN: usize = bssl_sys::ED25519_PUBLIC_KEY_LEN as usize;
+const ED25519_SIGNATURE_LEN: usize = bssl_sys::ED25519_SIGNATURE_LEN as usize;
 
 /// Verifies the signature of a message with the given ED25519 public key.
 pub fn ed25519_verify(
@@ -28,7 +28,7 @@
 ) -> Result<()> {
     // SAFETY: The function only reads the parameters within their bounds.
     let ret = unsafe {
-        bssl_ffi::ED25519_verify(
+        bssl_sys::ED25519_verify(
             message.as_ptr(),
             message.len(),
             signature.as_ptr(),
diff --git a/libs/bssl/src/digest.rs b/libs/bssl/src/digest.rs
index e986a38..8a51b11 100644
--- a/libs/bssl/src/digest.rs
+++ b/libs/bssl/src/digest.rs
@@ -18,7 +18,7 @@
 use alloc::vec;
 use alloc::vec::Vec;
 use bssl_avf_error::{ApiName, Error, Result};
-use bssl_ffi::{
+use bssl_sys::{
     EVP_Digest, EVP_MD_CTX_free, EVP_MD_CTX_new, EVP_MD_size, EVP_sha256, EVP_sha384, EVP_sha512,
     EVP_MAX_MD_SIZE, EVP_MD, EVP_MD_CTX,
 };
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index 894934d..6c9910c 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -21,7 +21,7 @@
 use alloc::vec;
 use alloc::vec::Vec;
 use bssl_avf_error::{ApiName, Error, Result};
-use bssl_ffi::{
+use bssl_sys::{
     BN_bin2bn, BN_bn2bin_padded, BN_clear_free, BN_new, CBB_flush, CBB_len, ECDSA_sign, ECDSA_size,
     ECDSA_verify, EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_check_key,
     EC_KEY_free, EC_KEY_generate_key, EC_KEY_get0_group, EC_KEY_get0_public_key,
diff --git a/libs/bssl/src/err.rs b/libs/bssl/src/err.rs
index 7040441..60bab98 100644
--- a/libs/bssl/src/err.rs
+++ b/libs/bssl/src/err.rs
@@ -15,7 +15,7 @@
 //! Wrappers of the error handling functions in BoringSSL err.h.
 
 use bssl_avf_error::{CipherError, EcError, EcdsaError, GlobalError, ReasonCode};
-use bssl_ffi::{self, ERR_get_error, ERR_GET_LIB_RUST, ERR_GET_REASON_RUST};
+use bssl_sys::{self, ERR_get_error, ERR_GET_LIB_RUST, ERR_GET_REASON_RUST};
 
 const NO_ERROR_REASON_CODE: i32 = 0;
 
@@ -61,12 +61,12 @@
 /// Global errors may occur in any library.
 fn map_global_reason_code(reason: i32) -> Option<GlobalError> {
     let reason = match reason {
-        bssl_ffi::ERR_R_FATAL => GlobalError::Fatal,
-        bssl_ffi::ERR_R_MALLOC_FAILURE => GlobalError::MallocFailure,
-        bssl_ffi::ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED => GlobalError::ShouldNotHaveBeenCalled,
-        bssl_ffi::ERR_R_PASSED_NULL_PARAMETER => GlobalError::PassedNullParameter,
-        bssl_ffi::ERR_R_INTERNAL_ERROR => GlobalError::InternalError,
-        bssl_ffi::ERR_R_OVERFLOW => GlobalError::Overflow,
+        bssl_sys::ERR_R_FATAL => GlobalError::Fatal,
+        bssl_sys::ERR_R_MALLOC_FAILURE => GlobalError::MallocFailure,
+        bssl_sys::ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED => GlobalError::ShouldNotHaveBeenCalled,
+        bssl_sys::ERR_R_PASSED_NULL_PARAMETER => GlobalError::PassedNullParameter,
+        bssl_sys::ERR_R_INTERNAL_ERROR => GlobalError::InternalError,
+        bssl_sys::ERR_R_OVERFLOW => GlobalError::Overflow,
         _ => return None,
     };
     Some(reason)
@@ -74,40 +74,40 @@
 
 fn map_library_reason_code(reason: i32, lib: i32) -> Option<ReasonCode> {
     u32::try_from(lib).ok().and_then(|x| match x {
-        bssl_ffi::ERR_LIB_CIPHER => map_cipher_reason_code(reason).map(ReasonCode::Cipher),
-        bssl_ffi::ERR_LIB_EC => map_ec_reason_code(reason).map(ReasonCode::Ec),
-        bssl_ffi::ERR_LIB_ECDSA => map_ecdsa_reason_code(reason).map(ReasonCode::Ecdsa),
+        bssl_sys::ERR_LIB_CIPHER => map_cipher_reason_code(reason).map(ReasonCode::Cipher),
+        bssl_sys::ERR_LIB_EC => map_ec_reason_code(reason).map(ReasonCode::Ec),
+        bssl_sys::ERR_LIB_ECDSA => map_ecdsa_reason_code(reason).map(ReasonCode::Ecdsa),
         _ => None,
     })
 }
 
 fn map_cipher_reason_code(reason: i32) -> Option<CipherError> {
     let error = match reason {
-        bssl_ffi::CIPHER_R_AES_KEY_SETUP_FAILED => CipherError::AesKeySetupFailed,
-        bssl_ffi::CIPHER_R_BAD_DECRYPT => CipherError::BadDecrypt,
-        bssl_ffi::CIPHER_R_BAD_KEY_LENGTH => CipherError::BadKeyLength,
-        bssl_ffi::CIPHER_R_BUFFER_TOO_SMALL => CipherError::BufferTooSmall,
-        bssl_ffi::CIPHER_R_CTRL_NOT_IMPLEMENTED => CipherError::CtrlNotImplemented,
-        bssl_ffi::CIPHER_R_CTRL_OPERATION_NOT_IMPLEMENTED => {
+        bssl_sys::CIPHER_R_AES_KEY_SETUP_FAILED => CipherError::AesKeySetupFailed,
+        bssl_sys::CIPHER_R_BAD_DECRYPT => CipherError::BadDecrypt,
+        bssl_sys::CIPHER_R_BAD_KEY_LENGTH => CipherError::BadKeyLength,
+        bssl_sys::CIPHER_R_BUFFER_TOO_SMALL => CipherError::BufferTooSmall,
+        bssl_sys::CIPHER_R_CTRL_NOT_IMPLEMENTED => CipherError::CtrlNotImplemented,
+        bssl_sys::CIPHER_R_CTRL_OPERATION_NOT_IMPLEMENTED => {
             CipherError::CtrlOperationNotImplemented
         }
-        bssl_ffi::CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH => {
+        bssl_sys::CIPHER_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH => {
             CipherError::DataNotMultipleOfBlockLength
         }
-        bssl_ffi::CIPHER_R_INITIALIZATION_ERROR => CipherError::InitializationError,
-        bssl_ffi::CIPHER_R_INPUT_NOT_INITIALIZED => CipherError::InputNotInitialized,
-        bssl_ffi::CIPHER_R_INVALID_AD_SIZE => CipherError::InvalidAdSize,
-        bssl_ffi::CIPHER_R_INVALID_KEY_LENGTH => CipherError::InvalidKeyLength,
-        bssl_ffi::CIPHER_R_INVALID_NONCE_SIZE => CipherError::InvalidNonceSize,
-        bssl_ffi::CIPHER_R_INVALID_OPERATION => CipherError::InvalidOperation,
-        bssl_ffi::CIPHER_R_IV_TOO_LARGE => CipherError::IvTooLarge,
-        bssl_ffi::CIPHER_R_NO_CIPHER_SET => CipherError::NoCipherSet,
-        bssl_ffi::CIPHER_R_OUTPUT_ALIASES_INPUT => CipherError::OutputAliasesInput,
-        bssl_ffi::CIPHER_R_TAG_TOO_LARGE => CipherError::TagTooLarge,
-        bssl_ffi::CIPHER_R_TOO_LARGE => CipherError::TooLarge,
-        bssl_ffi::CIPHER_R_WRONG_FINAL_BLOCK_LENGTH => CipherError::WrongFinalBlockLength,
-        bssl_ffi::CIPHER_R_NO_DIRECTION_SET => CipherError::NoDirectionSet,
-        bssl_ffi::CIPHER_R_INVALID_NONCE => CipherError::InvalidNonce,
+        bssl_sys::CIPHER_R_INITIALIZATION_ERROR => CipherError::InitializationError,
+        bssl_sys::CIPHER_R_INPUT_NOT_INITIALIZED => CipherError::InputNotInitialized,
+        bssl_sys::CIPHER_R_INVALID_AD_SIZE => CipherError::InvalidAdSize,
+        bssl_sys::CIPHER_R_INVALID_KEY_LENGTH => CipherError::InvalidKeyLength,
+        bssl_sys::CIPHER_R_INVALID_NONCE_SIZE => CipherError::InvalidNonceSize,
+        bssl_sys::CIPHER_R_INVALID_OPERATION => CipherError::InvalidOperation,
+        bssl_sys::CIPHER_R_IV_TOO_LARGE => CipherError::IvTooLarge,
+        bssl_sys::CIPHER_R_NO_CIPHER_SET => CipherError::NoCipherSet,
+        bssl_sys::CIPHER_R_OUTPUT_ALIASES_INPUT => CipherError::OutputAliasesInput,
+        bssl_sys::CIPHER_R_TAG_TOO_LARGE => CipherError::TagTooLarge,
+        bssl_sys::CIPHER_R_TOO_LARGE => CipherError::TooLarge,
+        bssl_sys::CIPHER_R_WRONG_FINAL_BLOCK_LENGTH => CipherError::WrongFinalBlockLength,
+        bssl_sys::CIPHER_R_NO_DIRECTION_SET => CipherError::NoDirectionSet,
+        bssl_sys::CIPHER_R_INVALID_NONCE => CipherError::InvalidNonce,
         _ => return None,
     };
     Some(error)
@@ -115,40 +115,40 @@
 
 fn map_ec_reason_code(reason: i32) -> Option<EcError> {
     let error = match reason {
-        bssl_ffi::EC_R_BUFFER_TOO_SMALL => EcError::BufferTooSmall,
-        bssl_ffi::EC_R_COORDINATES_OUT_OF_RANGE => EcError::CoordinatesOutOfRange,
-        bssl_ffi::EC_R_D2I_ECPKPARAMETERS_FAILURE => EcError::D2IEcpkparametersFailure,
-        bssl_ffi::EC_R_EC_GROUP_NEW_BY_NAME_FAILURE => EcError::EcGroupNewByNameFailure,
-        bssl_ffi::EC_R_GROUP2PKPARAMETERS_FAILURE => EcError::Group2PkparametersFailure,
-        bssl_ffi::EC_R_I2D_ECPKPARAMETERS_FAILURE => EcError::I2DEcpkparametersFailure,
-        bssl_ffi::EC_R_INCOMPATIBLE_OBJECTS => EcError::IncompatibleObjects,
-        bssl_ffi::EC_R_INVALID_COMPRESSED_POINT => EcError::InvalidCompressedPoint,
-        bssl_ffi::EC_R_INVALID_COMPRESSION_BIT => EcError::InvalidCompressionBit,
-        bssl_ffi::EC_R_INVALID_ENCODING => EcError::InvalidEncoding,
-        bssl_ffi::EC_R_INVALID_FIELD => EcError::InvalidField,
-        bssl_ffi::EC_R_INVALID_FORM => EcError::InvalidForm,
-        bssl_ffi::EC_R_INVALID_GROUP_ORDER => EcError::InvalidGroupOrder,
-        bssl_ffi::EC_R_INVALID_PRIVATE_KEY => EcError::InvalidPrivateKey,
-        bssl_ffi::EC_R_MISSING_PARAMETERS => EcError::MissingParameters,
-        bssl_ffi::EC_R_MISSING_PRIVATE_KEY => EcError::MissingPrivateKey,
-        bssl_ffi::EC_R_NON_NAMED_CURVE => EcError::NonNamedCurve,
-        bssl_ffi::EC_R_NOT_INITIALIZED => EcError::NotInitialized,
-        bssl_ffi::EC_R_PKPARAMETERS2GROUP_FAILURE => EcError::Pkparameters2GroupFailure,
-        bssl_ffi::EC_R_POINT_AT_INFINITY => EcError::PointAtInfinity,
-        bssl_ffi::EC_R_POINT_IS_NOT_ON_CURVE => EcError::PointIsNotOnCurve,
-        bssl_ffi::EC_R_SLOT_FULL => EcError::SlotFull,
-        bssl_ffi::EC_R_UNDEFINED_GENERATOR => EcError::UndefinedGenerator,
-        bssl_ffi::EC_R_UNKNOWN_GROUP => EcError::UnknownGroup,
-        bssl_ffi::EC_R_UNKNOWN_ORDER => EcError::UnknownOrder,
-        bssl_ffi::EC_R_WRONG_ORDER => EcError::WrongOrder,
-        bssl_ffi::EC_R_BIGNUM_OUT_OF_RANGE => EcError::BignumOutOfRange,
-        bssl_ffi::EC_R_WRONG_CURVE_PARAMETERS => EcError::WrongCurveParameters,
-        bssl_ffi::EC_R_DECODE_ERROR => EcError::DecodeError,
-        bssl_ffi::EC_R_ENCODE_ERROR => EcError::EncodeError,
-        bssl_ffi::EC_R_GROUP_MISMATCH => EcError::GroupMismatch,
-        bssl_ffi::EC_R_INVALID_COFACTOR => EcError::InvalidCofactor,
-        bssl_ffi::EC_R_PUBLIC_KEY_VALIDATION_FAILED => EcError::PublicKeyValidationFailed,
-        bssl_ffi::EC_R_INVALID_SCALAR => EcError::InvalidScalar,
+        bssl_sys::EC_R_BUFFER_TOO_SMALL => EcError::BufferTooSmall,
+        bssl_sys::EC_R_COORDINATES_OUT_OF_RANGE => EcError::CoordinatesOutOfRange,
+        bssl_sys::EC_R_D2I_ECPKPARAMETERS_FAILURE => EcError::D2IEcpkparametersFailure,
+        bssl_sys::EC_R_EC_GROUP_NEW_BY_NAME_FAILURE => EcError::EcGroupNewByNameFailure,
+        bssl_sys::EC_R_GROUP2PKPARAMETERS_FAILURE => EcError::Group2PkparametersFailure,
+        bssl_sys::EC_R_I2D_ECPKPARAMETERS_FAILURE => EcError::I2DEcpkparametersFailure,
+        bssl_sys::EC_R_INCOMPATIBLE_OBJECTS => EcError::IncompatibleObjects,
+        bssl_sys::EC_R_INVALID_COMPRESSED_POINT => EcError::InvalidCompressedPoint,
+        bssl_sys::EC_R_INVALID_COMPRESSION_BIT => EcError::InvalidCompressionBit,
+        bssl_sys::EC_R_INVALID_ENCODING => EcError::InvalidEncoding,
+        bssl_sys::EC_R_INVALID_FIELD => EcError::InvalidField,
+        bssl_sys::EC_R_INVALID_FORM => EcError::InvalidForm,
+        bssl_sys::EC_R_INVALID_GROUP_ORDER => EcError::InvalidGroupOrder,
+        bssl_sys::EC_R_INVALID_PRIVATE_KEY => EcError::InvalidPrivateKey,
+        bssl_sys::EC_R_MISSING_PARAMETERS => EcError::MissingParameters,
+        bssl_sys::EC_R_MISSING_PRIVATE_KEY => EcError::MissingPrivateKey,
+        bssl_sys::EC_R_NON_NAMED_CURVE => EcError::NonNamedCurve,
+        bssl_sys::EC_R_NOT_INITIALIZED => EcError::NotInitialized,
+        bssl_sys::EC_R_PKPARAMETERS2GROUP_FAILURE => EcError::Pkparameters2GroupFailure,
+        bssl_sys::EC_R_POINT_AT_INFINITY => EcError::PointAtInfinity,
+        bssl_sys::EC_R_POINT_IS_NOT_ON_CURVE => EcError::PointIsNotOnCurve,
+        bssl_sys::EC_R_SLOT_FULL => EcError::SlotFull,
+        bssl_sys::EC_R_UNDEFINED_GENERATOR => EcError::UndefinedGenerator,
+        bssl_sys::EC_R_UNKNOWN_GROUP => EcError::UnknownGroup,
+        bssl_sys::EC_R_UNKNOWN_ORDER => EcError::UnknownOrder,
+        bssl_sys::EC_R_WRONG_ORDER => EcError::WrongOrder,
+        bssl_sys::EC_R_BIGNUM_OUT_OF_RANGE => EcError::BignumOutOfRange,
+        bssl_sys::EC_R_WRONG_CURVE_PARAMETERS => EcError::WrongCurveParameters,
+        bssl_sys::EC_R_DECODE_ERROR => EcError::DecodeError,
+        bssl_sys::EC_R_ENCODE_ERROR => EcError::EncodeError,
+        bssl_sys::EC_R_GROUP_MISMATCH => EcError::GroupMismatch,
+        bssl_sys::EC_R_INVALID_COFACTOR => EcError::InvalidCofactor,
+        bssl_sys::EC_R_PUBLIC_KEY_VALIDATION_FAILED => EcError::PublicKeyValidationFailed,
+        bssl_sys::EC_R_INVALID_SCALAR => EcError::InvalidScalar,
         _ => return None,
     };
     Some(error)
@@ -156,15 +156,15 @@
 
 fn map_ecdsa_reason_code(reason: i32) -> Option<EcdsaError> {
     let error = match reason {
-        bssl_ffi::ECDSA_R_BAD_SIGNATURE => EcdsaError::BadSignature,
-        bssl_ffi::ECDSA_R_MISSING_PARAMETERS => EcdsaError::MissingParameters,
-        bssl_ffi::ECDSA_R_NEED_NEW_SETUP_VALUES => EcdsaError::NeedNewSetupValues,
-        bssl_ffi::ECDSA_R_NOT_IMPLEMENTED => EcdsaError::NotImplemented,
-        bssl_ffi::ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED => {
+        bssl_sys::ECDSA_R_BAD_SIGNATURE => EcdsaError::BadSignature,
+        bssl_sys::ECDSA_R_MISSING_PARAMETERS => EcdsaError::MissingParameters,
+        bssl_sys::ECDSA_R_NEED_NEW_SETUP_VALUES => EcdsaError::NeedNewSetupValues,
+        bssl_sys::ECDSA_R_NOT_IMPLEMENTED => EcdsaError::NotImplemented,
+        bssl_sys::ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED => {
             EcdsaError::RandomNumberGenerationFailed
         }
-        bssl_ffi::ECDSA_R_ENCODE_ERROR => EcdsaError::EncodeError,
-        bssl_ffi::ECDSA_R_TOO_MANY_ITERATIONS => EcdsaError::TooManyIterations,
+        bssl_sys::ECDSA_R_ENCODE_ERROR => EcdsaError::EncodeError,
+        bssl_sys::ECDSA_R_TOO_MANY_ITERATIONS => EcdsaError::TooManyIterations,
         _ => return None,
     };
     Some(error)
diff --git a/libs/bssl/src/evp.rs b/libs/bssl/src/evp.rs
index fe3d88e..fca189c 100644
--- a/libs/bssl/src/evp.rs
+++ b/libs/bssl/src/evp.rs
@@ -20,7 +20,7 @@
 use crate::util::{check_int_result, to_call_failed_error};
 use alloc::vec::Vec;
 use bssl_avf_error::{ApiName, Error, Result};
-use bssl_ffi::{
+use bssl_sys::{
     CBB_flush, CBB_len, EVP_DigestVerify, EVP_DigestVerifyInit, EVP_PKEY_free, EVP_PKEY_new,
     EVP_PKEY_new_raw_public_key, EVP_PKEY_set1_EC_KEY, EVP_marshal_public_key, EVP_PKEY,
     EVP_PKEY_ED25519, EVP_PKEY_X25519,
diff --git a/libs/bssl/src/hkdf.rs b/libs/bssl/src/hkdf.rs
index 85bd1ff..d5aeb76 100644
--- a/libs/bssl/src/hkdf.rs
+++ b/libs/bssl/src/hkdf.rs
@@ -17,7 +17,7 @@
 use crate::digest::Digester;
 use crate::util::check_int_result;
 use bssl_avf_error::{ApiName, Result};
-use bssl_ffi::HKDF;
+use bssl_sys::HKDF;
 use zeroize::Zeroizing;
 
 /// Computes HKDF (as specified by [RFC 5869]) of initial keying material `secret` with
diff --git a/libs/bssl/src/hmac.rs b/libs/bssl/src/hmac.rs
index 1b3a403..f820285 100644
--- a/libs/bssl/src/hmac.rs
+++ b/libs/bssl/src/hmac.rs
@@ -18,7 +18,7 @@
 use crate::sha::SHA256_DIGEST_LENGTH;
 use crate::util::to_call_failed_error;
 use bssl_avf_error::{ApiName, Result};
-use bssl_ffi::HMAC;
+use bssl_sys::HMAC;
 
 /// Computes the HMAC using SHA-256 for the given `data` with the given `key`.
 pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result<[u8; SHA256_DIGEST_LENGTH]> {
diff --git a/libs/bssl/src/rand.rs b/libs/bssl/src/rand.rs
index 9343284..e1b3ebe 100644
--- a/libs/bssl/src/rand.rs
+++ b/libs/bssl/src/rand.rs
@@ -16,7 +16,7 @@
 
 use crate::util::check_int_result;
 use bssl_avf_error::{ApiName, Result};
-use bssl_ffi::RAND_bytes;
+use bssl_sys::RAND_bytes;
 
 /// Fills the given `dest` with random data.
 pub fn rand_bytes(dest: &mut [u8]) -> Result<()> {
diff --git a/libs/bssl/src/sha.rs b/libs/bssl/src/sha.rs
index 6c65d7f..66b4a7c 100644
--- a/libs/bssl/src/sha.rs
+++ b/libs/bssl/src/sha.rs
@@ -16,10 +16,10 @@
 
 use crate::util::to_call_failed_error;
 use bssl_avf_error::{ApiName, Result};
-use bssl_ffi::SHA256;
+use bssl_sys::SHA256;
 
 /// The length of a SHA256 digest.
-pub(crate) const SHA256_DIGEST_LENGTH: usize = bssl_ffi::SHA256_DIGEST_LENGTH as usize;
+pub(crate) const SHA256_DIGEST_LENGTH: usize = bssl_sys::SHA256_DIGEST_LENGTH as usize;
 
 /// Computes the SHA256 digest of the provided `data``.
 pub fn sha256(data: &[u8]) -> Result<[u8; SHA256_DIGEST_LENGTH]> {
diff --git a/libs/bssl/tests/eckey_test.rs b/libs/bssl/tests/eckey_test.rs
index 9c7eb4f..3c0e45d 100644
--- a/libs/bssl/tests/eckey_test.rs
+++ b/libs/bssl/tests/eckey_test.rs
@@ -15,8 +15,8 @@
 use bssl_avf::{sha256, ApiName, Digester, EcKey, EcdsaError, Error, PKey, Result};
 use coset::CborSerializable;
 use spki::{
-    der::{AnyRef, Decode},
-    AlgorithmIdentifier, ObjectIdentifier, SubjectPublicKeyInfo,
+    der::{AnyRef, Decode, Encode},
+    AlgorithmIdentifier, ObjectIdentifier, SubjectPublicKeyInfoRef,
 };
 
 /// OID value for general-use NIST EC keys held in PKCS#8 and X.509; see RFC 5480 s2.1.1.
@@ -46,13 +46,14 @@
     let pkey: PKey = ec_key.try_into()?;
     let subject_public_key_info = pkey.subject_public_key_info()?;
 
-    let subject_public_key_info = SubjectPublicKeyInfo::from_der(&subject_public_key_info).unwrap();
+    let subject_public_key_info =
+        SubjectPublicKeyInfoRef::from_der(&subject_public_key_info).unwrap();
     let expected_algorithm = AlgorithmIdentifier {
         oid: X509_NIST_OID,
         parameters: Some(AnyRef::from(&ALGO_PARAM_P256_OID)),
     };
     assert_eq!(expected_algorithm, subject_public_key_info.algorithm);
-    assert!(!subject_public_key_info.subject_public_key.to_vec().is_empty());
+    assert!(!subject_public_key_info.subject_public_key.to_der().unwrap().is_empty());
     Ok(())
 }
 
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/microdroid/payload/Android.bp b/microdroid/payload/Android.bp
index 8225875..4814a64 100644
--- a/microdroid/payload/Android.bp
+++ b/microdroid/payload/Android.bp
@@ -31,7 +31,6 @@
     protos: ["metadata.proto"],
     source_stem: "microdroid_metadata",
     host_supported: true,
-    use_protobuf3: true,
     apex_available: [
         "com.android.virt",
     ],
diff --git a/microdroid_manager/src/vm_secret.rs b/microdroid_manager/src/vm_secret.rs
index df5d318..89c27c9 100644
--- a/microdroid_manager/src/vm_secret.rs
+++ b/microdroid_manager/src/vm_secret.rs
@@ -161,7 +161,7 @@
     _dice_chain: &OwnedDiceArtifacts,
 ) -> Result<()> {
     // Start a new secretkeeper session!
-    let session = SkSession::new(secretkeeper).map_err(anyhow_err)?;
+    let mut session = SkSession::new(secretkeeper).map_err(anyhow_err)?;
     let store_request = StoreSecretRequest {
         id: Id(id),
         secret: Secret(*secret),
@@ -188,7 +188,7 @@
     _dice_chain: &OwnedDiceArtifacts,
 ) -> Result<[u8; SECRET_SIZE]> {
     // Start a new secretkeeper session!
-    let session = SkSession::new(secretkeeper).map_err(anyhow_err)?;
+    let mut session = SkSession::new(secretkeeper).map_err(anyhow_err)?;
     let get_request = GetSecretRequest {
         id: Id(id),
         // TODO(b/291233371): Construct policy out of dice_chain.
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index b12ae26..b7b17e4 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -13,7 +13,7 @@
     rustlibs: [
         "libaarch64_paging",
         "libbssl_avf_nostd",
-        "libbssl_ffi_nostd",
+        "libbssl_sys_nostd",
         "libciborium_nostd",
         "libciborium_io_nostd",
         "libcstr",
diff --git a/pvmfw/src/crypto.rs b/pvmfw/src/crypto.rs
deleted file mode 100644
index 8f31553..0000000
--- a/pvmfw/src/crypto.rs
+++ /dev/null
@@ -1,271 +0,0 @@
-// 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.
-
-//! Wrapper around BoringSSL/OpenSSL symbols.
-
-use core::convert::AsRef;
-use core::ffi::{c_char, c_int, CStr};
-use core::fmt;
-use core::mem::MaybeUninit;
-use core::num::NonZeroU32;
-use core::ptr;
-
-use bssl_ffi::CRYPTO_library_init;
-use bssl_ffi::ERR_get_error_line;
-use bssl_ffi::ERR_lib_error_string;
-use bssl_ffi::ERR_reason_error_string;
-use bssl_ffi::EVP_AEAD_CTX_aead;
-use bssl_ffi::EVP_AEAD_CTX_init;
-use bssl_ffi::EVP_AEAD_CTX_open;
-use bssl_ffi::EVP_AEAD_CTX_seal;
-use bssl_ffi::EVP_AEAD_max_overhead;
-use bssl_ffi::EVP_aead_aes_256_gcm_randnonce;
-use bssl_ffi::EVP_AEAD;
-use bssl_ffi::EVP_AEAD_CTX;
-use cstr::cstr;
-
-#[derive(Debug)]
-pub struct Error {
-    packed: NonZeroU32,
-    file: Option<&'static CStr>,
-    line: c_int,
-}
-
-impl Error {
-    fn get() -> Option<Self> {
-        let mut file = ptr::null();
-        let mut line = 0;
-        // SAFETY: The function writes to the provided pointers, which are valid because they come
-        // from references. It doesn't retain them after it returns.
-        let packed = unsafe { ERR_get_error_line(&mut file, &mut line) };
-
-        let packed = packed.try_into().ok()?;
-        // SAFETY: Any non-NULL result is expected to point to a global const C string.
-        let file = unsafe { as_static_cstr(file) };
-
-        Some(Self { packed, file, line })
-    }
-
-    fn packed_value(&self) -> u32 {
-        self.packed.get()
-    }
-
-    fn library_name(&self) -> Option<&'static CStr> {
-        // SAFETY: Call to a pure function.
-        let name = unsafe { ERR_lib_error_string(self.packed_value()) };
-        // SAFETY: Any non-NULL result is expected to point to a global const C string.
-        unsafe { as_static_cstr(name) }
-    }
-
-    fn reason(&self) -> Option<&'static CStr> {
-        // SAFETY: Call to a pure function.
-        let reason = unsafe { ERR_reason_error_string(self.packed_value()) };
-        // SAFETY: Any non-NULL result is expected to point to a global const C string.
-        unsafe { as_static_cstr(reason) }
-    }
-}
-
-impl fmt::Display for Error {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let packed = self.packed_value();
-        let library = self.library_name().unwrap_or(cstr!("{unknown library}")).to_str().unwrap();
-        let reason = self.reason().unwrap_or(cstr!("{unknown reason}")).to_str().unwrap();
-        let file = self.file.unwrap_or(cstr!("??")).to_str().unwrap();
-        let line = self.line;
-
-        write!(f, "{file}:{line}: {library}: {reason} ({packed:#x})")
-    }
-}
-
-#[derive(Copy, Clone)]
-pub struct ErrorIterator {}
-
-impl Iterator for ErrorIterator {
-    type Item = Error;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        Self::Item::get()
-    }
-}
-
-pub type Result<T> = core::result::Result<T, ErrorIterator>;
-
-#[repr(transparent)]
-pub struct Aead(EVP_AEAD);
-
-impl Aead {
-    pub fn aes_256_gcm_randnonce() -> Option<&'static Self> {
-        // SAFETY: Returned pointer is checked below.
-        let aead = unsafe { EVP_aead_aes_256_gcm_randnonce() };
-        if aead.is_null() {
-            None
-        } else {
-            // SAFETY: We assume that the non-NULL value points to a valid and static EVP_AEAD.
-            Some(unsafe { &*(aead as *const _) })
-        }
-    }
-
-    pub fn max_overhead(&self) -> usize {
-        // SAFETY: Function should only read from self.
-        unsafe { EVP_AEAD_max_overhead(self.as_ref() as *const _) }
-    }
-}
-
-#[repr(transparent)]
-pub struct AeadCtx(EVP_AEAD_CTX);
-
-impl AeadCtx {
-    pub fn new_aes_256_gcm_randnonce(key: &[u8]) -> Result<Self> {
-        let aead = Aead::aes_256_gcm_randnonce().unwrap();
-
-        Self::new(aead, key)
-    }
-
-    fn new(aead: &'static Aead, key: &[u8]) -> Result<Self> {
-        const DEFAULT_TAG_LENGTH: usize = 0;
-        let engine = ptr::null_mut(); // Use default implementation.
-        let mut ctx = MaybeUninit::zeroed();
-        // SAFETY: Initialize the EVP_AEAD_CTX with const pointers to the AEAD and key.
-        let result = unsafe {
-            EVP_AEAD_CTX_init(
-                ctx.as_mut_ptr(),
-                aead.as_ref() as *const _,
-                key.as_ptr(),
-                key.len(),
-                DEFAULT_TAG_LENGTH,
-                engine,
-            )
-        };
-
-        if result == 1 {
-            // SAFETY: We assume that the non-NULL value points to a valid and static EVP_AEAD.
-            Ok(Self(unsafe { ctx.assume_init() }))
-        } else {
-            Err(ErrorIterator {})
-        }
-    }
-
-    pub fn aead(&self) -> Option<&'static Aead> {
-        // SAFETY: The function should only read from self.
-        let aead = unsafe { EVP_AEAD_CTX_aead(self.as_ref() as *const _) };
-        if aead.is_null() {
-            None
-        } else {
-            // SAFETY: We assume that the non-NULL value points to a valid and static EVP_AEAD.
-            Some(unsafe { &*(aead as *const _) })
-        }
-    }
-
-    pub fn open<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]> {
-        let nonce = ptr::null_mut();
-        let nonce_len = 0;
-        let ad = ptr::null_mut();
-        let ad_len = 0;
-        let mut out_len = MaybeUninit::uninit();
-        // SAFETY: The function should only read from self and write to out (at most the provided
-        // number of bytes) and out_len while reading from data (at most the provided number of
-        // bytes), ignoring any NULL input.
-        let result = unsafe {
-            EVP_AEAD_CTX_open(
-                self.as_ref() as *const _,
-                out.as_mut_ptr(),
-                out_len.as_mut_ptr(),
-                out.len(),
-                nonce,
-                nonce_len,
-                data.as_ptr(),
-                data.len(),
-                ad,
-                ad_len,
-            )
-        };
-
-        if result == 1 {
-            // SAFETY: Any value written to out_len could be a valid usize. The value itself is
-            // validated as being a proper slice length by panicking in the following indexing
-            // otherwise.
-            let out_len = unsafe { out_len.assume_init() };
-            Ok(&mut out[..out_len])
-        } else {
-            Err(ErrorIterator {})
-        }
-    }
-
-    pub fn seal<'b>(&self, out: &'b mut [u8], data: &[u8]) -> Result<&'b mut [u8]> {
-        let nonce = ptr::null_mut();
-        let nonce_len = 0;
-        let ad = ptr::null_mut();
-        let ad_len = 0;
-        let mut out_len = MaybeUninit::uninit();
-        // SAFETY: The function should only read from self and write to out (at most the provided
-        // number of bytes) while reading from data (at most the provided number of bytes),
-        // ignoring any NULL input.
-        let result = unsafe {
-            EVP_AEAD_CTX_seal(
-                self.as_ref() as *const _,
-                out.as_mut_ptr(),
-                out_len.as_mut_ptr(),
-                out.len(),
-                nonce,
-                nonce_len,
-                data.as_ptr(),
-                data.len(),
-                ad,
-                ad_len,
-            )
-        };
-
-        if result == 1 {
-            // SAFETY: Any value written to out_len could be a valid usize. The value itself is
-            // validated as being a proper slice length by panicking in the following indexing
-            // otherwise.
-            let out_len = unsafe { out_len.assume_init() };
-            Ok(&mut out[..out_len])
-        } else {
-            Err(ErrorIterator {})
-        }
-    }
-}
-
-/// Cast a C string pointer to a static non-mutable reference.
-///
-/// # Safety
-///
-/// The caller needs to ensure that the pointer is null or points to a valid C string and that the
-/// C lifetime of the string is compatible with a static Rust lifetime.
-unsafe fn as_static_cstr(p: *const c_char) -> Option<&'static CStr> {
-    if p.is_null() {
-        None
-    } else {
-        // Safety: Safe given the requirements of this function.
-        Some(unsafe { CStr::from_ptr(p) })
-    }
-}
-
-impl AsRef<EVP_AEAD> for Aead {
-    fn as_ref(&self) -> &EVP_AEAD {
-        &self.0
-    }
-}
-
-impl AsRef<EVP_AEAD_CTX> for AeadCtx {
-    fn as_ref(&self) -> &EVP_AEAD_CTX {
-        &self.0
-    }
-}
-
-pub fn init() {
-    // SAFETY: Configures the internal state of the library - may be called multiple times.
-    unsafe { CRYPTO_library_init() }
-}
diff --git a/pvmfw/src/device_assignment.rs b/pvmfw/src/device_assignment.rs
index b785ffd..3d060ac 100644
--- a/pvmfw/src/device_assignment.rs
+++ b/pvmfw/src/device_assignment.rs
@@ -935,19 +935,6 @@
         assert_eq!(device_info.assigned_devices, expected);
     }
 
-    // TODO(b/311655051): Test with real once instead of empty FDT.
-    #[test]
-    fn device_info_new_with_empty_device_tree() {
-        let mut fdt_data = vec![0; pvmfw_fdt_template::RAW.len()];
-        let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
-        let fdt = Fdt::create_empty_tree(&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);
-    }
-
     #[test]
     fn device_info_filter() {
         let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index 03f2f62..8c4396d 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -15,9 +15,9 @@
 //! Low-level entry and exit points of pvmfw.
 
 use crate::config;
-use crate::crypto;
 use crate::fdt;
 use crate::memory;
+use bssl_sys::CRYPTO_library_init;
 use core::arch::asm;
 use core::mem::{drop, size_of};
 use core::num::NonZeroUsize;
@@ -196,7 +196,12 @@
     // - only access non-pvmfw memory once (and while) it has been mapped
 
     log::set_max_level(LevelFilter::Info);
-    crypto::init();
+    // TODO(https://crbug.com/boringssl/35): Remove this init when BoringSSL can handle this
+    // internally.
+    // SAFETY: Configures the internal state of the library - may be called multiple times.
+    unsafe {
+        CRYPTO_library_init();
+    }
 
     let page_table = memory::init_page_table().map_err(|e| {
         error!("Failed to set up the dynamic page tables: {e}");
diff --git a/pvmfw/src/instance.rs b/pvmfw/src/instance.rs
index a998bfb..e98f663 100644
--- a/pvmfw/src/instance.rs
+++ b/pvmfw/src/instance.rs
@@ -14,13 +14,11 @@
 
 //! Support for reading and writing to the instance.img.
 
-use crate::crypto;
-use crate::crypto::AeadCtx;
 use crate::dice::PartialInputs;
 use crate::gpt;
 use crate::gpt::Partition;
 use crate::gpt::Partitions;
-use bssl_avf::{self, hkdf, Digester};
+use bssl_avf::{self, hkdf, Aead, AeadContext, Digester};
 use core::fmt;
 use core::mem::size_of;
 use diced_open_dice::DiceMode;
@@ -40,12 +38,8 @@
 pub enum Error {
     /// Unexpected I/O error while accessing the underlying disk.
     FailedIo(gpt::Error),
-    /// Failed to decrypt the entry.
-    FailedOpen(crypto::ErrorIterator),
     /// Failed to generate a random salt to be stored.
     FailedSaltGeneration(rand::Error),
-    /// Failed to encrypt the entry.
-    FailedSeal(crypto::ErrorIterator),
     /// Impossible to create a new instance.img entry.
     InstanceImageFull,
     /// Badly formatted instance.img header block.
@@ -72,21 +66,7 @@
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match self {
             Self::FailedIo(e) => write!(f, "Failed I/O to disk: {e}"),
-            Self::FailedOpen(e_iter) => {
-                writeln!(f, "Failed to open the instance.img partition:")?;
-                for e in *e_iter {
-                    writeln!(f, "\t{e}")?;
-                }
-                Ok(())
-            }
             Self::FailedSaltGeneration(e) => write!(f, "Failed to generate salt: {e}"),
-            Self::FailedSeal(e_iter) => {
-                writeln!(f, "Failed to seal the instance.img partition:")?;
-                for e in *e_iter {
-                    writeln!(f, "\t{e}")?;
-                }
-                Ok(())
-            }
             Self::InstanceImageFull => write!(f, "Failed to obtain a free instance.img partition"),
             Self::InvalidInstanceImageHeader => write!(f, "instance.img header is invalid"),
             Self::MissingInstanceImage => write!(f, "Failed to find the instance.img partition"),
@@ -124,6 +104,13 @@
     trace!("Found pvmfw instance.img entry: {entry:?}");
 
     let key = hkdf::<32>(secret, /* salt= */ &[], b"vm-instance", Digester::sha512())?;
+    let tag_len = None;
+    let aead_ctx = AeadContext::new(Aead::aes_256_gcm_randnonce(), key.as_slice(), tag_len)?;
+    let ad = &[];
+    // The nonce is generated internally for `aes_256_gcm_randnonce`, so no additional
+    // nonce is required.
+    let nonce = &[];
+
     let mut blk = [0; BLK_SIZE];
     match entry {
         PvmfwEntry::Existing { header_index, payload_size } => {
@@ -136,9 +123,7 @@
 
             let payload = &blk[..payload_size];
             let mut entry = [0; size_of::<EntryBody>()];
-            let aead =
-                AeadCtx::new_aes_256_gcm_randnonce(key.as_slice()).map_err(Error::FailedOpen)?;
-            let decrypted = aead.open(&mut entry, payload).map_err(Error::FailedOpen)?;
+            let decrypted = aead_ctx.open(payload, nonce, ad, &mut entry)?;
 
             let body = EntryBody::read_from(decrypted).unwrap();
             if dice_inputs.rkp_vm_marker {
@@ -166,12 +151,10 @@
             let salt = rand::random_array().map_err(Error::FailedSaltGeneration)?;
             let body = EntryBody::new(dice_inputs, &salt);
 
-            let aead =
-                AeadCtx::new_aes_256_gcm_randnonce(key.as_slice()).map_err(Error::FailedSeal)?;
             // We currently only support single-blk entries.
             let plaintext = body.as_bytes();
-            assert!(plaintext.len() + aead.aead().unwrap().max_overhead() < blk.len());
-            let encrypted = aead.seal(&mut blk, plaintext).map_err(Error::FailedSeal)?;
+            assert!(plaintext.len() + aead_ctx.aead().max_overhead() < blk.len());
+            let encrypted = aead_ctx.seal(plaintext, nonce, ad, &mut blk)?;
             let payload_size = encrypted.len();
             let payload_index = header_index + 1;
             instance_img.write_block(payload_index, &blk).map_err(Error::FailedIo)?;
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 09bb899..f80bae1 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -22,7 +22,6 @@
 mod bcc;
 mod bootargs;
 mod config;
-mod crypto;
 mod device_assignment;
 mod dice;
 mod entry;
diff --git a/rialto/Android.bp b/rialto/Android.bp
index 5e7fe1f..c102c89 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -10,7 +10,7 @@
     rustlibs: [
         "libaarch64_paging",
         "libbssl_avf_nostd",
-        "libbssl_ffi_nostd",
+        "libbssl_sys_nostd",
         "libciborium_io_nostd",
         "libciborium_nostd",
         "libcstr",
@@ -142,7 +142,7 @@
         "libservice_vm_fake_chain",
         "libservice_vm_manager",
         "libvmclient",
-        "libx509_parser",
+        "libx509_cert_nostd",
     ],
     data: [
         ":rialto_unsigned",
diff --git a/rialto/src/main.rs b/rialto/src/main.rs
index 1215021..e705562 100644
--- a/rialto/src/main.rs
+++ b/rialto/src/main.rs
@@ -28,7 +28,7 @@
 use crate::error::{Error, Result};
 use crate::fdt::read_dice_range_from;
 use alloc::boxed::Box;
-use bssl_ffi::CRYPTO_library_init;
+use bssl_sys::CRYPTO_library_init;
 use ciborium_io::Write;
 use core::num::NonZeroUsize;
 use core::slice;
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/cert.rs b/service_vm/requests/src/cert.rs
index 73828a7..91281e7 100644
--- a/service_vm/requests/src/cert.rs
+++ b/service_vm/requests/src/cert.rs
@@ -18,7 +18,7 @@
 use alloc::vec;
 use alloc::vec::Vec;
 use der::{
-    asn1::{BitStringRef, ObjectIdentifier, UIntRef, Utf8StringRef},
+    asn1::{BitString, ObjectIdentifier, OctetString, Utf8StringRef},
     oid::AssociatedOid,
     Decode, Sequence,
 };
@@ -27,6 +27,7 @@
     certificate::{Certificate, TbsCertificate, Version},
     ext::Extension,
     name::Name,
+    serial_number::SerialNumber,
     time::Validity,
 };
 
@@ -111,14 +112,14 @@
 ///   signature            BIT STRING
 /// }
 /// ```
-pub(crate) fn build_certificate<'a>(
-    tbs_cert: TbsCertificate<'a>,
-    signature: &'a [u8],
-) -> der::Result<Certificate<'a>> {
+pub(crate) fn build_certificate(
+    tbs_cert: TbsCertificate,
+    signature: &[u8],
+) -> der::Result<Certificate> {
     Ok(Certificate {
-        signature_algorithm: tbs_cert.signature,
+        signature_algorithm: tbs_cert.signature.clone(),
         tbs_certificate: tbs_cert,
-        signature: BitStringRef::new(0, signature)?,
+        signature: BitString::new(0, signature)?,
     })
 }
 
@@ -141,24 +142,24 @@
 ///                        -- If present, version MUST be v3 --
 /// }
 /// ```
-pub(crate) fn build_tbs_certificate<'a>(
-    serial_number: &'a [u8],
-    issuer: Name<'a>,
-    subject: Name<'a>,
+pub(crate) fn build_tbs_certificate(
+    serial_number: &[u8],
+    issuer: Name,
+    subject: Name,
     validity: Validity,
-    subject_public_key_info: &'a [u8],
-    attestation_ext: &'a [u8],
-) -> der::Result<TbsCertificate<'a>> {
+    subject_public_key_info: &[u8],
+    attestation_ext: &[u8],
+) -> der::Result<TbsCertificate> {
     let signature = AlgorithmIdentifier { oid: ECDSA_WITH_SHA_256, parameters: None };
     let subject_public_key_info = SubjectPublicKeyInfo::from_der(subject_public_key_info)?;
     let extensions = vec![Extension {
         extn_id: AttestationExtension::OID,
         critical: false,
-        extn_value: attestation_ext,
+        extn_value: OctetString::new(attestation_ext)?,
     }];
     Ok(TbsCertificate {
         version: Version::V3,
-        serial_number: UIntRef::new(serial_number)?,
+        serial_number: SerialNumber::new(serial_number)?,
         signature,
         issuer,
         validity,
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index c2f39e7..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(&params.remotely_provisioned_cert)?;
@@ -98,12 +100,14 @@
         } 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(),
         vm_components,
     )
-    .to_vec()?;
+    .to_der()?;
     let tbs_cert = cert::build_tbs_certificate(
         &serial_number,
         rkp_cert.tbs_certificate.subject,
@@ -122,9 +126,9 @@
                 RequestProcessingError::FailedToDecryptKeyBlob
             })?;
     let ec_private_key = EcKey::from_ec_private_key(private_key.as_slice())?;
-    let signature = ecdsa_sign(&ec_private_key, &tbs_cert.to_vec()?)?;
+    let signature = ecdsa_sign(&ec_private_key, &tbs_cert.to_der()?)?;
     let certificate = cert::build_certificate(tbs_cert, &signature)?;
-    Ok(certificate.to_vec()?)
+    Ok(certificate.to_der()?)
 }
 
 fn ecdsa_verify(key: &EcKey, signature: &[u8], message: &[u8]) -> bssl_avf::Result<()> {
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index 937fbee..be13196 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -36,6 +36,8 @@
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
 
 public abstract class MicrodroidHostTestCaseBase extends BaseHostJUnit4Test {
     protected static final String TEST_ROOT = "/data/local/tmp/virt/";
@@ -52,6 +54,9 @@
             (int) (MICRODROID_ADB_CONNECT_TIMEOUT_MINUTES * 60 * 1000
                 / MICRODROID_COMMAND_RETRY_INTERVAL_MILLIS);
 
+    protected static final Set<String> SUPPORTED_GKI_VERSIONS =
+            new HashSet(Arrays.asList("android14-6.1"));
+
     public static void prepareVirtualizationTestSetup(ITestDevice androidDevice)
             throws DeviceNotAvailableException {
         CommandRunner android = new CommandRunner(androidDevice);
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 60f3e52..a54a22a 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -25,6 +25,7 @@
 
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeFalse;
 import static org.junit.Assume.assumeTrue;
 
@@ -102,14 +103,24 @@
         }
     }
 
-    @Parameterized.Parameters(name = "protectedVm={0}")
+    @Parameterized.Parameters(name = "protectedVm={0},gki={1}")
     public static Collection<Object[]> params() {
-        return List.of(new Object[] {true}, new Object[] {false});
+        List<Object[]> ret = new ArrayList<>();
+        ret.add(new Object[] {true /* protectedVm */, null /* use microdroid kernel */});
+        ret.add(new Object[] {false /* protectedVm */, null /* use microdroid kernel */});
+        for (String gki : SUPPORTED_GKI_VERSIONS) {
+            ret.add(new Object[] {true /* protectedVm */, gki});
+            ret.add(new Object[] {false /* protectedVm */, gki});
+        }
+        return ret;
     }
 
     @Parameterized.Parameter(0)
     public boolean mProtectedVm;
 
+    @Parameterized.Parameter(1)
+    public String mGki;
+
     @Rule public TestLogData mTestLogs = new TestLogData();
     @Rule public TestName mTestName = new TestName();
     @Rule public TestMetrics mMetrics = new TestMetrics();
@@ -164,6 +175,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()));
@@ -316,7 +333,8 @@
         //   - its idsig
 
         // Load etc/microdroid.json
-        File microdroidConfigFile = new File(virtApexEtcDir, "microdroid.json");
+        String os = mGki != null ? "microdroid_gki-" + mGki : "microdroid";
+        File microdroidConfigFile = new File(virtApexEtcDir, os + ".json");
         JSONObject config = new JSONObject(FileUtil.readStringFromFile(microdroidConfigFile));
 
         // Replace paths so that the config uses re-signed images from TEST_ROOT
@@ -332,7 +350,7 @@
         }
 
         // Add partitions to the second disk
-        final String initrdPath = TEST_ROOT + "etc/microdroid_initrd_debuggable.img";
+        final String initrdPath = TEST_ROOT + "etc/" + os + "_initrd_debuggable.img";
         config.put("initrd", initrdPath);
         // Add instance image as a partition in disks[1]
         disks.put(
@@ -400,6 +418,7 @@
                         .memoryMib(minMemorySize())
                         .cpuTopology("match_host")
                         .protectedVm(true)
+                        .gki(mGki)
                         .build(getAndroidDevice());
 
         // Assert
@@ -526,6 +545,7 @@
                         .memoryMib(minMemorySize())
                         .cpuTopology("match_host")
                         .protectedVm(protectedVm)
+                        .gki(mGki)
                         .build(getAndroidDevice());
         mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
         mMicrodroidDevice.enableAdbRoot();
@@ -680,6 +700,7 @@
                         .memoryMib(minMemorySize())
                         .cpuTopology("match_host")
                         .protectedVm(mProtectedVm)
+                        .gki(mGki)
                         .build(device);
         microdroid.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
         device.shutdownMicrodroid(microdroid);
@@ -808,24 +829,8 @@
                         .debugLevel("full")
                         .memoryMib(minMemorySize())
                         .cpuTopology("match_host")
-                        .protectedVm(mProtectedVm));
-    }
-
-    @Test
-    @CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
-    public void testMicrodroidBootsWithGki() throws Exception {
-        List<String> supportedVersions = getSupportedGKIVersions();
-        assumeFalse("no available gki", supportedVersions.isEmpty());
-        for (String ver : supportedVersions) {
-            final String configPath = "assets/vm_config.json"; // path inside the APK
-            testMicrodroidBootsWithBuilder(
-                    MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
-                            .debugLevel("full")
-                            .memoryMib(minMemorySize())
-                            .cpuTopology("match_host")
-                            .protectedVm(mProtectedVm)
-                            .gki(ver));
-        }
+                        .protectedVm(mProtectedVm)
+                        .gki(mGki));
     }
 
     @Test
@@ -837,6 +842,7 @@
                         .memoryMib(minMemorySize())
                         .cpuTopology("match_host")
                         .protectedVm(mProtectedVm)
+                        .gki(mGki)
                         .build(getAndroidDevice());
         mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
         mMicrodroidDevice.enableAdbRoot();
@@ -992,11 +998,21 @@
                         .cpuTopology("match_host")
                         .protectedVm(true)
                         .addAssignableDevice(devices.get(0))
+                        .gki(mGki)
                         .build(getAndroidDevice());
 
         mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
     }
 
+    @Test
+    public void testGkiVersions() throws Exception {
+        for (String gki : getSupportedGKIVersions()) {
+            assertTrue(
+                    "Unknown gki \"" + gki + "\". Supported gkis: " + SUPPORTED_GKI_VERSIONS,
+                    SUPPORTED_GKI_VERSIONS.contains(gki));
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
         assumeDeviceIsCapable(getDevice());
@@ -1011,6 +1027,12 @@
         assumeTrue(
                 "Microdroid is not supported for specific VM protection type",
                 getAndroidDevice().supportsMicrodroid(mProtectedVm));
+
+        if (mGki != null) {
+            assumeTrue(
+                    "GKI version \"" + mGki + "\" is not supported on this device",
+                    getSupportedGKIVersions().contains(mGki));
+        }
     }
 
     @After
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 2ba0e0e..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:?}");
     }
@@ -900,7 +894,9 @@
         .arg("--socket")
         .arg(add_preserved_fd(&mut preserved_fds, &control_server_socket.as_raw_descriptor()));
 
-    // TODO(b/285855436): Pass dtbo_vendor after --device-tree-overlay crosvm option is supported.
+    if let Some(dtbo_vendor) = &config.dtbo_vendor {
+        command.arg("--device-tree-overlay").arg(add_preserved_fd(&mut preserved_fds, dtbo_vendor));
+    }
 
     append_platform_devices(&mut command, &mut preserved_fds, &config)?;
 
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> {