Merge "Document subcomponent data" into main
diff --git a/libs/bssl/error/src/lib.rs b/libs/bssl/error/src/lib.rs
index 7f01c6c..82a2d5e 100644
--- a/libs/bssl/error/src/lib.rs
+++ b/libs/bssl/error/src/lib.rs
@@ -91,6 +91,7 @@
     ECDSA_sign,
     ECDSA_size,
     ECDSA_verify,
+    ED25519_verify,
     EVP_AEAD_CTX_new,
     EVP_AEAD_CTX_open,
     EVP_AEAD_CTX_seal,
diff --git a/libs/bssl/src/curve25519.rs b/libs/bssl/src/curve25519.rs
new file mode 100644
index 0000000..499a3d0
--- /dev/null
+++ b/libs/bssl/src/curve25519.rs
@@ -0,0 +1,39 @@
+// 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.
+
+//! Wrappers of the Curve25519 related functions in BoringSSL curve25519.h.
+
+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;
+
+/// Verifies the signature of a message with the given ED25519 public key.
+pub fn ed25519_verify(
+    message: &[u8],
+    signature: &[u8; ED25519_SIGNATURE_LEN],
+    public_key: &[u8; ED25519_PUBLIC_KEY_LEN],
+) -> Result<()> {
+    // SAFETY: The function only reads the parameters within their bounds.
+    let ret = unsafe {
+        bssl_ffi::ED25519_verify(
+            message.as_ptr(),
+            message.len(),
+            signature.as_ptr(),
+            public_key.as_ptr(),
+        )
+    };
+    check_int_result(ret, ApiName::ED25519_verify)
+}
diff --git a/libs/bssl/src/lib.rs b/libs/bssl/src/lib.rs
index a420168..ad51b61 100644
--- a/libs/bssl/src/lib.rs
+++ b/libs/bssl/src/lib.rs
@@ -21,6 +21,7 @@
 mod aead;
 mod cbb;
 mod cbs;
+mod curve25519;
 mod digest;
 mod ec_key;
 mod err;
@@ -36,6 +37,7 @@
 pub use aead::{Aead, AeadContext, AES_GCM_NONCE_LENGTH};
 pub use cbb::CbbFixed;
 pub use cbs::Cbs;
+pub use curve25519::ed25519_verify;
 pub use digest::Digester;
 pub use ec_key::{EcKey, ZVec};
 pub use evp::{PKey, PKeyType};
diff --git a/libs/dice/open_dice/Android.bp b/libs/dice/open_dice/Android.bp
index 646080d..2d0f52c 100644
--- a/libs/dice/open_dice/Android.bp
+++ b/libs/dice/open_dice/Android.bp
@@ -27,12 +27,14 @@
     ],
     visibility: [
         "//packages/modules/Virtualization:__subpackages__",
+        "//system/authgraph/tests:__subpackages__",
     ],
 }
 
 rust_library {
     name: "libdiced_open_dice",
     defaults: ["libdiced_open_dice_defaults"],
+    host_supported: true,
     vendor_available: true,
     rustlibs: [
         "libopen_dice_android_bindgen",
@@ -54,6 +56,7 @@
     ],
     visibility: [
         "//packages/modules/Virtualization:__subpackages__",
+        "//system/authgraph/tests:__subpackages__",
     ],
     apex_available: [
         "//apex_available:platform",
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index d267e2e..37d8ac9 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -26,6 +26,7 @@
         "libpvmfw_avb_nostd",
         "libpvmfw_embedded_key",
         "libpvmfw_fdt_template",
+        "libservice_vm_version",
         "libsmccc",
         "libstatic_assertions",
         "libtinyvec_nostd",
@@ -73,6 +74,7 @@
     srcs: ["src/device_assignment.rs"],
     defaults: ["libpvmfw.test.defaults"],
     rustlibs: [
+        "libhyp",
         "liblibfdt",
         "liblog_rust",
         "libpvmfw_fdt_template",
@@ -84,6 +86,7 @@
         ":test_pvmfw_devices_with_multiple_devices_iommus",
         ":test_pvmfw_devices_with_iommu_sharing",
         ":test_pvmfw_devices_with_iommu_id_conflict",
+        ":test_pvmfw_devices_without_device",
         ":test_pvmfw_devices_without_iommus",
     ],
     // To use libpvmfw_fdt_template for testing
@@ -142,6 +145,13 @@
 }
 
 genrule {
+    name: "test_pvmfw_devices_without_device",
+    defaults: ["test_device_assignment_dts_to_dtb"],
+    srcs: ["testdata/test_pvmfw_devices_without_device.dts"],
+    out: ["test_pvmfw_devices_without_device.dtb"],
+}
+
+genrule {
     name: "test_pvmfw_devices_with_multiple_devices_iommus",
     defaults: ["test_device_assignment_dts_to_dtb"],
     srcs: ["testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts"],
diff --git a/pvmfw/src/config.rs b/pvmfw/src/config.rs
index 4957df2..2fe4ec9 100644
--- a/pvmfw/src/config.rs
+++ b/pvmfw/src/config.rs
@@ -131,6 +131,13 @@
     const COUNT: usize = Self::_VARIANT_COUNT as usize;
 }
 
+#[derive(Default)]
+pub struct Entries<'a> {
+    pub bcc: &'a mut [u8],
+    pub debug_policy: Option<&'a mut [u8]>,
+    pub vm_dtbo: Option<&'a mut [u8]>,
+}
+
 #[repr(packed)]
 #[derive(Clone, Copy, Debug, FromZeroes, FromBytes)]
 struct HeaderEntry {
@@ -260,7 +267,7 @@
     }
 
     /// Get slice containing the platform BCC.
-    pub fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>, Option<&mut [u8]>) {
+    pub fn get_entries(&mut self) -> Entries<'_> {
         // This assumes that the blobs are in-order w.r.t. the entries.
         let bcc_range = self.get_entry_range(Entry::Bcc);
         let dp_range = self.get_entry_range(Entry::DebugPolicy);
@@ -270,16 +277,17 @@
             info!("Found VM DTBO at {:?}", vm_dtbo_range);
         }
 
-        // SAFETY: When instantiate, ranges are validated to be in the body range without
+        // SAFETY: When instantiated, ranges are validated to be in the body range without
         // overlapping.
-        unsafe {
+        let (bcc, debug_policy, vm_dtbo) = unsafe {
             let ptr = self.body.as_mut_ptr() as usize;
             (
                 Self::from_raw_range_mut(ptr, bcc_range.unwrap()),
                 dp_range.map(|dp_range| Self::from_raw_range_mut(ptr, dp_range)),
                 vm_dtbo_range.map(|vm_dtbo_range| Self::from_raw_range_mut(ptr, vm_dtbo_range)),
             )
-        }
+        };
+        Entries { bcc, debug_policy, vm_dtbo }
     }
 
     fn get_entry_range(&self, entry: Entry) -> Option<NonEmptyRange> {
diff --git a/pvmfw/src/device_assignment.rs b/pvmfw/src/device_assignment.rs
index 14f1fe5..19ace5f 100644
--- a/pvmfw/src/device_assignment.rs
+++ b/pvmfw/src/device_assignment.rs
@@ -27,7 +27,9 @@
 use core::ffi::CStr;
 use core::iter::Iterator;
 use core::mem;
-use libfdt::{Fdt, FdtError, FdtNode, Phandle};
+use hyp::DeviceAssigningHypervisor;
+use libfdt::{Fdt, FdtError, FdtNode, Phandle, Reg};
+use log::error;
 
 // TODO(b/308694211): Use cstr! from vmbase instead.
 macro_rules! cstr {
@@ -47,10 +49,12 @@
 /// Errors in device assignment.
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub enum DeviceAssignmentError {
-    // Invalid VM DTBO
+    /// Invalid VM DTBO
     InvalidDtbo,
     /// Invalid __symbols__
     InvalidSymbols,
+    /// Invalid <reg>
+    InvalidReg,
     /// Invalid <interrupts>
     InvalidInterrupts,
     /// Invalid <iommus>
@@ -83,6 +87,7 @@
                 f,
                 "Invalid property in /__symbols__. Must point to valid assignable device node."
             ),
+            Self::InvalidReg => write!(f, "Invalid <reg>"),
             Self::InvalidInterrupts => write!(f, "Invalid <interrupts>"),
             Self::InvalidIommus => write!(f, "Invalid <iommus>"),
             Self::InvalidPvIommu => write!(f, "Invalid pvIOMMU node"),
@@ -214,6 +219,36 @@
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
 struct Vsid(u32);
 
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+struct DeviceReg {
+    addr: u64,
+    size: u64,
+}
+
+impl TryFrom<Reg<u64>> for DeviceReg {
+    type Error = DeviceAssignmentError;
+
+    fn try_from(reg: Reg<u64>) -> Result<Self> {
+        Ok(Self { addr: reg.addr, size: reg.size.ok_or(DeviceAssignmentError::InvalidReg)? })
+    }
+}
+
+fn parse_node_reg(node: &FdtNode) -> Result<Vec<DeviceReg>> {
+    node.reg()?
+        .ok_or(DeviceAssignmentError::InvalidReg)?
+        .map(DeviceReg::try_from)
+        .collect::<Result<Vec<_>>>()
+}
+
+fn to_be_bytes(reg: &[DeviceReg]) -> Vec<u8> {
+    let mut reg_cells = vec![];
+    for x in reg {
+        reg_cells.extend_from_slice(&x.addr.to_be_bytes());
+        reg_cells.extend_from_slice(&x.size.to_be_bytes());
+    }
+    reg_cells
+}
+
 /// Assigned device information parsed from crosvm DT.
 /// Keeps everything in the owned data because underlying FDT will be reused for platform DT.
 #[derive(Debug, Eq, PartialEq)]
@@ -223,7 +258,7 @@
     // DTBO node path of the assigned device (e.g. "/fragment@rng/__overlay__/rng")
     dtbo_node_path: CString,
     // <reg> property from the crosvm DT
-    reg: Vec<u8>,
+    reg: Vec<DeviceReg>,
     // <interrupts> property from the crosvm DT
     interrupts: Vec<u8>,
     // Parsed <iommus> property from the crosvm DT. Tuple of PvIommu and vSID.
@@ -231,6 +266,22 @@
 }
 
 impl AssignedDeviceInfo {
+    fn parse_reg(
+        node: &FdtNode,
+        hypervisor: &dyn DeviceAssigningHypervisor,
+    ) -> Result<Vec<DeviceReg>> {
+        let device_reg = parse_node_reg(node)?;
+        // TODO(b/277993056): Valid the result back with physical reg
+        for reg in &device_reg {
+            hypervisor.get_phys_mmio_token(reg.addr, reg.size).map_err(|e| {
+                let name = node.name();
+                error!("Failed to validate device <reg>, error={e:?}, name={name:?}, reg={reg:?}");
+                DeviceAssignmentError::InvalidReg
+            })?;
+        }
+        Ok(device_reg)
+    }
+
     fn parse_interrupts(node: &FdtNode) -> Result<Vec<u8>> {
         // Validation: Validate if interrupts cell numbers are multiple of #interrupt-cells.
         // We can't know how many interrupts would exist.
@@ -250,6 +301,7 @@
     fn parse_iommus(
         node: &FdtNode,
         pviommus: &BTreeMap<Phandle, PvIommu>,
+        hypervisor: &dyn DeviceAssigningHypervisor,
     ) -> Result<Vec<(PvIommu, Vsid)>> {
         let mut iommus = vec![];
         let Some(mut cells) = node.getprop_cells(cstr!("iommus"))? else {
@@ -266,6 +318,15 @@
             };
             let vsid = Vsid(cell);
 
+            // TODO(b/277993056): Valid the result back with phys iommu id and sid..
+            hypervisor
+                .get_phys_iommu_token(pviommu.id.into(), vsid.0.into())
+                .map_err(|e| {
+                    let name = node.name().unwrap_or_default();
+                    error!("Failed to validate device <iommus>, error={e:?}, name={name:?}, pviommu={pviommu:?}, vsid={:?}", vsid.0);
+                    DeviceAssignmentError::InvalidIommus
+                })?;
+
             iommus.push((*pviommu, vsid));
         }
         Ok(iommus)
@@ -276,27 +337,21 @@
         vm_dtbo: &VmDtbo,
         dtbo_node_path: &CStr,
         pviommus: &BTreeMap<Phandle, PvIommu>,
+        hypervisor: &dyn DeviceAssigningHypervisor,
     ) -> Result<Option<Self>> {
         let node_path = vm_dtbo.locate_overlay_target_path(dtbo_node_path)?;
 
         let Some(node) = fdt.node(&node_path)? else { return Ok(None) };
 
-        // TODO(b/277993056): Validate reg with HVC, and keep reg with FdtNode::reg()
-        let reg = node.getprop(cstr!("reg")).unwrap().unwrap();
+        let reg = Self::parse_reg(&node, hypervisor)?;
         let interrupts = Self::parse_interrupts(&node)?;
-        let iommus = Self::parse_iommus(&node, pviommus)?;
-        Ok(Some(Self {
-            node_path,
-            dtbo_node_path: dtbo_node_path.into(),
-            reg: reg.to_vec(),
-            interrupts,
-            iommus,
-        }))
+        let iommus = Self::parse_iommus(&node, pviommus, hypervisor)?;
+        Ok(Some(Self { node_path, dtbo_node_path: dtbo_node_path.into(), reg, interrupts, iommus }))
     }
 
     fn patch(&self, fdt: &mut Fdt, pviommu_phandles: &BTreeMap<PvIommu, Phandle>) -> Result<()> {
         let mut dst = fdt.node_mut(&self.node_path)?.unwrap();
-        dst.setprop(cstr!("reg"), &self.reg)?;
+        dst.setprop(cstr!("reg"), &to_be_bytes(&self.reg))?;
         dst.setprop(cstr!("interrupts"), &self.interrupts)?;
         let mut iommus = Vec::with_capacity(8 * self.iommus.len());
         for (pviommu, vsid) in &self.iommus {
@@ -339,7 +394,11 @@
     /// Parses fdt and vm_dtbo, and creates new DeviceAssignmentInfo
     // TODO(b/277993056): Parse __local_fixups__
     // TODO(b/277993056): Parse __fixups__
-    pub fn parse(fdt: &Fdt, vm_dtbo: &VmDtbo) -> Result<Option<Self>> {
+    pub fn parse(
+        fdt: &Fdt,
+        vm_dtbo: &VmDtbo,
+        hypervisor: &dyn DeviceAssigningHypervisor,
+    ) -> Result<Option<Self>> {
         let Some(symbols_node) = vm_dtbo.as_ref().symbols()? else {
             // /__symbols__ should contain all assignable devices.
             // If empty, then nothing can be assigned.
@@ -359,7 +418,7 @@
             let dtbo_node_path = CStr::from_bytes_with_nul(symbol_prop_value)
                 .or(Err(DeviceAssignmentError::InvalidSymbols))?;
             let assigned_device =
-                AssignedDeviceInfo::parse(fdt, vm_dtbo, dtbo_node_path, &pviommus)?;
+                AssignedDeviceInfo::parse(fdt, vm_dtbo, dtbo_node_path, &pviommus, hypervisor)?;
             if let Some(assigned_device) = assigned_device {
                 assigned_devices.push(assigned_device);
             } else {
@@ -446,19 +505,42 @@
 #[cfg(test)]
 mod tests {
     use super::*;
-    use alloc::collections::BTreeSet;
+    use alloc::collections::{BTreeMap, BTreeSet};
     use std::fs;
 
     const VM_DTBO_FILE_PATH: &str = "test_pvmfw_devices_vm_dtbo.dtbo";
     const VM_DTBO_WITHOUT_SYMBOLS_FILE_PATH: &str =
         "test_pvmfw_devices_vm_dtbo_without_symbols.dtbo";
     const FDT_WITHOUT_IOMMUS_FILE_PATH: &str = "test_pvmfw_devices_without_iommus.dtb";
+    const FDT_WITHOUT_DEVICE_FILE_PATH: &str = "test_pvmfw_devices_without_device.dtb";
     const FDT_FILE_PATH: &str = "test_pvmfw_devices_with_rng.dtb";
     const FDT_WITH_MULTIPLE_DEVICES_IOMMUS_FILE_PATH: &str =
         "test_pvmfw_devices_with_multiple_devices_iommus.dtb";
     const FDT_WITH_IOMMU_SHARING: &str = "test_pvmfw_devices_with_iommu_sharing.dtb";
     const FDT_WITH_IOMMU_ID_CONFLICT: &str = "test_pvmfw_devices_with_iommu_id_conflict.dtb";
 
+    #[derive(Debug, Default)]
+    struct MockHypervisor {
+        mmio_tokens: BTreeMap<(u64, u64), u64>,
+        iommu_tokens: BTreeMap<(u64, u64), (u64, u64)>,
+    }
+
+    impl DeviceAssigningHypervisor for MockHypervisor {
+        fn get_phys_mmio_token(&self, base_ipa: u64, size: u64) -> hyp::Result<u64> {
+            Ok(*self.mmio_tokens.get(&(base_ipa, size)).ok_or(hyp::Error::KvmError(
+                hyp::KvmError::InvalidParameter,
+                0xc6000012, /* VENDOR_HYP_KVM_DEV_REQ_MMIO_FUNC_ID */
+            ))?)
+        }
+
+        fn get_phys_iommu_token(&self, pviommu_id: u64, vsid: u64) -> hyp::Result<(u64, u64)> {
+            Ok(*self.iommu_tokens.get(&(pviommu_id, vsid)).ok_or(hyp::Error::KvmError(
+                hyp::KvmError::InvalidParameter,
+                0xc6000013, /* VENDOR_HYP_KVM_DEV_REQ_DMA_FUNC_ID */
+            ))?)
+        }
+    }
+
     #[derive(Debug, Eq, PartialEq)]
     struct AssignedDeviceNode {
         path: CString,
@@ -473,8 +555,7 @@
                 return Err(FdtError::NotFound.into());
             };
 
-            // TODO(b/277993056): Replace DeviceAssignmentError::Internal
-            let reg = node.getprop(cstr!("reg"))?.ok_or(DeviceAssignmentError::Internal)?;
+            let reg = node.getprop(cstr!("reg"))?.ok_or(DeviceAssignmentError::InvalidReg)?;
             let interrupts = node
                 .getprop(cstr!("interrupts"))?
                 .ok_or(DeviceAssignmentError::InvalidInterrupts)?;
@@ -524,6 +605,12 @@
         v
     }
 
+    impl From<[u64; 2]> for DeviceReg {
+        fn from(fdt_cells: [u64; 2]) -> Self {
+            DeviceReg { addr: fdt_cells[0], size: fdt_cells[1] }
+        }
+    }
+
     #[test]
     fn device_info_new_without_symbols() {
         let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
@@ -531,7 +618,20 @@
         let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
         let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
 
-        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap();
+        let hypervisor: MockHypervisor = Default::default();
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap();
+        assert_eq!(device_info, None);
+    }
+
+    #[test]
+    fn device_info_new_without_device() {
+        let mut fdt_data = fs::read(FDT_WITHOUT_DEVICE_FILE_PATH).unwrap();
+        let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
+        let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
+        let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
+
+        let hypervisor: MockHypervisor = Default::default();
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap();
         assert_eq!(device_info, None);
     }
 
@@ -542,12 +642,16 @@
         let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
         let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
 
-        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+        let hypervisor = MockHypervisor {
+            mmio_tokens: [((0x9, 0xFF), 0x300)].into(),
+            iommu_tokens: BTreeMap::new(),
+        };
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
 
         let expected = [AssignedDeviceInfo {
             node_path: CString::new("/backlight").unwrap(),
             dtbo_node_path: cstr!("/fragment@backlight/__overlay__/backlight").into(),
-            reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
+            reg: vec![[0x9, 0xFF].into()],
             interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
             iommus: vec![],
         }];
@@ -562,12 +666,16 @@
         let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
         let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
 
-        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+        let hypervisor = MockHypervisor {
+            mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
+            iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
+        };
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
 
         let expected = [AssignedDeviceInfo {
             node_path: CString::new("/rng").unwrap(),
             dtbo_node_path: cstr!("/fragment@rng/__overlay__/rng").into(),
-            reg: into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]),
+            reg: vec![[0x9, 0xFF].into()],
             interrupts: into_fdt_prop(vec![0x0, 0xF, 0x4]),
             iommus: vec![(PvIommu { id: 0x4 }, Vsid(0xFF0))],
         }];
@@ -583,7 +691,8 @@
         let fdt = Fdt::create_empty_tree(&mut fdt_data).unwrap();
         let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
 
-        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap();
+        let hypervisor: MockHypervisor = Default::default();
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap();
         assert_eq!(device_info, None);
     }
 
@@ -594,7 +703,11 @@
         let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
         let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
 
-        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+        let hypervisor = MockHypervisor {
+            mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
+            iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
+        };
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
         device_info.filter(vm_dtbo).unwrap();
 
         let vm_dtbo = vm_dtbo.as_mut();
@@ -624,7 +737,11 @@
         let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
         let platform_dt = Fdt::create_empty_tree(data.as_mut_slice()).unwrap();
 
-        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+        let hypervisor = MockHypervisor {
+            mmio_tokens: [((0x9, 0xFF), 0x300)].into(),
+            iommu_tokens: BTreeMap::new(),
+        };
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
         device_info.filter(vm_dtbo).unwrap();
 
         // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
@@ -669,7 +786,11 @@
         let platform_dt = Fdt::from_mut_slice(&mut platform_dt_data).unwrap();
         platform_dt.unpack().unwrap();
 
-        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+        let hypervisor = MockHypervisor {
+            mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
+            iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
+        };
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
         device_info.filter(vm_dtbo).unwrap();
 
         // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
@@ -703,7 +824,21 @@
         let platform_dt = Fdt::from_mut_slice(&mut platform_dt_data).unwrap();
         platform_dt.unpack().unwrap();
 
-        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+        let hypervisor = MockHypervisor {
+            mmio_tokens: [
+                ((0x9, 0xFF), 0x12F00000),
+                ((0x100, 0x1000), 0xF00000),
+                ((0x200, 0x1000), 0xF10000),
+            ]
+            .into(),
+            iommu_tokens: [
+                ((0x4, 0xFF0), (0x12E40000, 3)),
+                ((0x40, 0xFFA), (0x40000, 0x4)),
+                ((0x50, 0xFFB), (0x50000, 0x5)),
+            ]
+            .into(),
+        };
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
         device_info.filter(vm_dtbo).unwrap();
 
         // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
@@ -721,7 +856,7 @@
             },
             AssignedDeviceNode {
                 path: CString::new("/light").unwrap(),
-                reg: into_fdt_prop(vec![0x100, 0x9]),
+                reg: into_fdt_prop(vec![0x0, 0x100, 0x0, 0x1000, 0x0, 0x200, 0x0, 0x1000]),
                 interrupts: into_fdt_prop(vec![0x0, 0xF, 0x5]),
                 iommus: vec![0x40, 0xFFA, 0x50, 0xFFB],
             },
@@ -746,7 +881,11 @@
         let platform_dt = Fdt::from_mut_slice(&mut platform_dt_data).unwrap();
         platform_dt.unpack().unwrap();
 
-        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo).unwrap().unwrap();
+        let hypervisor = MockHypervisor {
+            mmio_tokens: [((0x9, 0xFF), 0x12F00000), ((0x100, 0x9), 0x12000000)].into(),
+            iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 3))].into(),
+        };
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor).unwrap().unwrap();
         device_info.filter(vm_dtbo).unwrap();
 
         // SAFETY: Damaged VM DTBO wouldn't be used after this unsafe block.
@@ -764,7 +903,7 @@
             },
             AssignedDeviceNode {
                 path: CString::new("/led").unwrap(),
-                reg: into_fdt_prop(vec![0x100, 0x9]),
+                reg: into_fdt_prop(vec![0x0, 0x100, 0x0, 0x9]),
                 interrupts: into_fdt_prop(vec![0x0, 0xF, 0x5]),
                 iommus: vec![0x4, 0xFF0],
             },
@@ -786,8 +925,44 @@
         let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
         let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
 
-        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo);
+        let hypervisor = MockHypervisor {
+            mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
+            iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
+        };
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
 
         assert_eq!(device_info, Err(DeviceAssignmentError::DuplicatedPvIommuIds));
     }
+
+    #[test]
+    fn device_info_invalid_reg() {
+        let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
+        let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
+        let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
+        let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
+
+        let hypervisor = MockHypervisor {
+            mmio_tokens: BTreeMap::new(),
+            iommu_tokens: [((0x4, 0xFF0), (0x12E40000, 0x3))].into(),
+        };
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
+
+        assert_eq!(device_info, Err(DeviceAssignmentError::InvalidReg));
+    }
+
+    #[test]
+    fn device_info_invalid_iommus() {
+        let mut fdt_data = fs::read(FDT_FILE_PATH).unwrap();
+        let mut vm_dtbo_data = fs::read(VM_DTBO_FILE_PATH).unwrap();
+        let fdt = Fdt::from_mut_slice(&mut fdt_data).unwrap();
+        let vm_dtbo = VmDtbo::from_mut_slice(&mut vm_dtbo_data).unwrap();
+
+        let hypervisor = MockHypervisor {
+            mmio_tokens: [((0x9, 0xFF), 0x12F00000)].into(),
+            iommu_tokens: BTreeMap::new(),
+        };
+        let device_info = DeviceAssignmentInfo::parse(fdt, vm_dtbo, &hypervisor);
+
+        assert_eq!(device_info, Err(DeviceAssignmentError::InvalidIommus));
+    }
 }
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index ed73bc9..f4078a3 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -212,7 +212,7 @@
         RebootReason::InvalidConfig
     })?;
 
-    let (bcc_slice, debug_policy, vm_dtbo) = appended.get_entries();
+    let config_entries = appended.get_entries();
 
     // Up to this point, we were using the built-in static (from .rodata) page tables.
     MEMORY.lock().replace(MemoryTracker::new(
@@ -222,13 +222,19 @@
         Some(memory::appended_payload_range()),
     ));
 
-    let slices = MemorySlices::new(fdt, payload, payload_size, vm_dtbo)?;
+    let slices = MemorySlices::new(fdt, payload, payload_size, config_entries.vm_dtbo)?;
 
     // This wrapper allows main() to be blissfully ignorant of platform details.
-    let next_bcc = crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, debug_policy)?;
+    let next_bcc = crate::main(
+        slices.fdt,
+        slices.kernel,
+        slices.ramdisk,
+        config_entries.bcc,
+        config_entries.debug_policy,
+    )?;
 
     // Writable-dirty regions will be flushed when MemoryTracker is dropped.
-    bcc_slice.zeroize();
+    config_entries.bcc.zeroize();
 
     info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
     MEMORY.lock().as_mut().unwrap().mmio_unmap_all().map_err(|e| {
@@ -432,10 +438,10 @@
         }
     }
 
-    fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>, Option<&mut [u8]>) {
+    fn get_entries(&mut self) -> config::Entries<'_> {
         match self {
-            Self::Config(ref mut cfg) => cfg.get_entries(),
-            Self::LegacyBcc(ref mut bcc) => (bcc, None, None),
+            Self::Config(cfg) => cfg.get_entries(),
+            Self::LegacyBcc(bcc) => config::Entries { bcc, ..Default::default() },
         }
     }
 }
diff --git a/pvmfw/src/fdt.rs b/pvmfw/src/fdt.rs
index 7a89b75..2cd1061 100644
--- a/pvmfw/src/fdt.rs
+++ b/pvmfw/src/fdt.rs
@@ -15,8 +15,7 @@
 //! High-level FDT functions.
 
 use crate::bootargs::BootArgsIterator;
-use crate::device_assignment::DeviceAssignmentInfo;
-use crate::device_assignment::VmDtbo;
+use crate::device_assignment::{DeviceAssignmentInfo, VmDtbo};
 use crate::helpers::GUEST_PAGE_SIZE;
 use crate::Box;
 use crate::RebootReason;
@@ -721,10 +720,19 @@
     validate_swiotlb_info(&swiotlb_info, &memory_range)?;
 
     let device_assignment = match vm_dtbo {
-        Some(vm_dtbo) => DeviceAssignmentInfo::parse(fdt, vm_dtbo).map_err(|e| {
-            error!("Failed to parse device assignment from DT and VM DTBO: {e}");
-            RebootReason::InvalidFdt
-        })?,
+        Some(vm_dtbo) => {
+            if let Some(hypervisor) = hyp::get_device_assigner() {
+                DeviceAssignmentInfo::parse(fdt, vm_dtbo, hypervisor).map_err(|e| {
+                    error!("Failed to parse device assignment from DT and VM DTBO: {e}");
+                    RebootReason::InvalidFdt
+                })?
+            } else {
+                warn!(
+                    "Device assignment is ignored because device assigning hypervisor is missing"
+                );
+                None
+            }
+        }
         None => None,
     };
 
diff --git a/pvmfw/src/instance.rs b/pvmfw/src/instance.rs
index f2cd6a3..a998bfb 100644
--- a/pvmfw/src/instance.rs
+++ b/pvmfw/src/instance.rs
@@ -141,6 +141,17 @@
             let decrypted = aead.open(&mut entry, payload).map_err(Error::FailedOpen)?;
 
             let body = EntryBody::read_from(decrypted).unwrap();
+            if dice_inputs.rkp_vm_marker {
+                // The RKP VM is allowed to run if it has passed the verified boot check and
+                // contains the expected version in its AVB footer.
+                // The comparison below with the previous boot information is skipped to enable the
+                // simultaneous update of the pvmfw and RKP VM.
+                // For instance, when both the pvmfw and RKP VM are updated, the code hash of the
+                // RKP VM will differ from the one stored in the instance image. In this case, the
+                // RKP VM is still allowed to run.
+                // This ensures that the updated RKP VM will retain the same CDIs in the next stage.
+                return Ok((false, body.salt));
+            }
             if body.code_hash != dice_inputs.code_hash {
                 Err(Error::RecordedCodeHashMismatch)
             } else if body.auth_hash != dice_inputs.auth_hash {
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 8aa5274..1d55a84 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -115,6 +115,17 @@
 
     if verified_boot_data.has_capability(Capability::RemoteAttest) {
         info!("Service VM capable of remote attestation detected");
+        if service_vm_version::VERSION != verified_boot_data.rollback_index {
+            // For RKP VM, we only boot if the version in the AVB footer of its kernel matches
+            // the one embedded in pvmfw at build time.
+            // This prevents the pvmfw from booting a roll backed RKP VM.
+            error!(
+                "Service VM version mismatch: expected {}, found {}",
+                service_vm_version::VERSION,
+                verified_boot_data.rollback_index
+            );
+            return Err(RebootReason::InvalidPayload);
+        }
     }
 
     if verified_boot_data.has_capability(Capability::SecretkeeperProtection) {
diff --git a/pvmfw/testdata/test_pvmfw_devices_vm_dtbo.dts b/pvmfw/testdata/test_pvmfw_devices_vm_dtbo.dts
index da08694..691d15a 100644
--- a/pvmfw/testdata/test_pvmfw_devices_vm_dtbo.dts
+++ b/pvmfw/testdata/test_pvmfw_devices_vm_dtbo.dts
@@ -21,7 +21,7 @@
 			light {
 				compatible = "android,light";
 				version = <0x1 0x2>;
-				android,pvmfw,phy-reg = <0x0 0xF00000 0x1000>;
+				android,pvmfw,phy-reg = <0x0 0xF00000 0x1000>, <0x0 0xF10000 0x1000>;
 				android,pvmfw,phy-iommu = <0x0 0x40000>, <0x0 0x50000>;
 				android,pvmfw,phy-sid = <4>, <5>;
 			};
diff --git a/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts b/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts
index 70b633c..a9e30be 100644
--- a/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts
+++ b/pvmfw/testdata/test_pvmfw_devices_with_iommu_id_conflict.dts
@@ -27,7 +27,7 @@
 
     light@70000000 {
         compatible = "android,light";
-        reg = <0x100 0x9>;
+        reg = <0x0 0x100 0x0 0x100>, <0x0 0x200 0x0 0x100>;
         interrupts = <0x0 0xF 0x5>;
         iommus = <&pviommu_a 0xA>, <&pviommu_b 0xB>;
     };
diff --git a/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts b/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts
index 7c6d2f2..78ff868 100644
--- a/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts
+++ b/pvmfw/testdata/test_pvmfw_devices_with_iommu_sharing.dts
@@ -14,8 +14,8 @@
     };
 
     led@70000000 {
-        compatible = "android,light";
-        reg = <0x100 0x9>;
+        compatible = "android,led";
+        reg = <0x0 0x100 0x0 0x9>;
         interrupts = <0x0 0xF 0x5>;
         iommus = <&pviommu_0 0xFF0>;
     };
diff --git a/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts b/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts
index 76c99c9..ca7e7f3 100644
--- a/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts
+++ b/pvmfw/testdata/test_pvmfw_devices_with_multiple_devices_iommus.dts
@@ -20,7 +20,7 @@
 
     light@70000000 {
         compatible = "android,light";
-        reg = <0x100 0x9>;
+        reg = <0x0 0x100 0x0 0x1000>, <0x0 0x200 0x0 0x1000>;
         interrupts = <0x0 0xF 0x5>;
         iommus = <&pviommu_a 0xFFA>, <&pviommu_b 0xFFB>;
     };
diff --git a/pvmfw/testdata/test_pvmfw_devices_without_device.dts b/pvmfw/testdata/test_pvmfw_devices_without_device.dts
new file mode 100644
index 0000000..ee0be3a
--- /dev/null
+++ b/pvmfw/testdata/test_pvmfw_devices_without_device.dts
@@ -0,0 +1,7 @@
+/dts-v1/;
+/plugin/;
+
+/include/ "test_crosvm_dt_base.dtsi"
+
+/ {
+};
diff --git a/rialto/Android.bp b/rialto/Android.bp
index bbb5e54..5e7fe1f 100644
--- a/rialto/Android.bp
+++ b/rialto/Android.bp
@@ -63,6 +63,28 @@
     srcs: [":avb_testkey_rsa4096"],
 }
 
+// Both SERVICE_VM_VERSION and SERVICE_VM_VERSION_STRING should represent the
+// same version number for the service VM.
+SERVICE_VM_VERSION = 1
+SERVICE_VM_VERSION_STRING = "1"
+
+genrule {
+    name: "service_vm_version_rs",
+    out: ["lib.rs"],
+    cmd: "(" +
+        "    echo '#![no_std]';" +
+        "    echo '#![allow(missing_docs)]';" +
+        "    echo 'pub const VERSION: u64 = " + SERVICE_VM_VERSION_STRING + ";'" +
+        ") > $(out)",
+}
+
+rust_library_rlib {
+    name: "libservice_vm_version",
+    crate_name: "service_vm_version",
+    defaults: ["vmbase_rlib_defaults"],
+    srcs: [":service_vm_version_rs"],
+}
+
 avb_add_hash_footer {
     name: "rialto_signed",
     src: ":empty_file",
@@ -70,6 +92,7 @@
     partition_name: "boot",
     private_key: ":rialto_sign_key",
     salt: rialto_salt,
+    rollback_index: SERVICE_VM_VERSION,
     props: [
         {
             name: "com.android.virt.cap",
diff --git a/service_vm/kernel/extract_microdroid_kernel_hashes.py b/service_vm/kernel/extract_microdroid_kernel_hashes.py
index 538c787..148e8be 100644
--- a/service_vm/kernel/extract_microdroid_kernel_hashes.py
+++ b/service_vm/kernel/extract_microdroid_kernel_hashes.py
@@ -5,6 +5,10 @@
 - initrd_debug hash
 
 The hashes are written to stdout as a Rust file.
+
+In unsupportive environments such as x86, when the kernel is just an empty file,
+the output Rust file has the same hash constant fields for compatibility
+reasons, but all of them are empty.
 """
 #!/usr/bin/env python3
 
@@ -21,20 +25,27 @@
     avbtool = args[0]
     kernel_image_path = args[1]
     hashes = collect_hashes(avbtool, kernel_image_path)
-    assert hashes.keys() == {PARTITION_NAME_BOOT,
-                             PARTITION_NAME_INITRD_NORMAL,
-                             PARTITION_NAME_INITRD_DEBUG}, hashes.keys()
 
     print("//! This file is generated by extract_microdroid_kernel_hashes.py.")
     print("//! It contains the hashes of the kernel and initrds.\n")
     print("#![no_std]\n#![allow(missing_docs)]\n")
 
+    # Microdroid's kernel is just an empty file in unsupportive environments
+    # such as x86, in this case the hashes should be empty.
+    if hashes.keys() != {PARTITION_NAME_BOOT,
+                         PARTITION_NAME_INITRD_NORMAL,
+                         PARTITION_NAME_INITRD_DEBUG}:
+        print("/// The kernel is empty, no hashes are available.")
+        hashes[PARTITION_NAME_BOOT] = ""
+        hashes[PARTITION_NAME_INITRD_NORMAL] = ""
+        hashes[PARTITION_NAME_INITRD_DEBUG] = ""
+
     print("pub const KERNEL_HASH: &[u8] = &["
           f"{format_hex_string(hashes[PARTITION_NAME_BOOT])}];\n")
     print("pub const INITRD_NORMAL_HASH: &[u8] = &["
           f"{format_hex_string(hashes[PARTITION_NAME_INITRD_NORMAL])}];\n")
     print("pub const INITRD_DEBUG_HASH: &[u8] = &["
-          f"{format_hex_string(hashes[PARTITION_NAME_INITRD_DEBUG])}];\n")
+          f"{format_hex_string(hashes[PARTITION_NAME_INITRD_DEBUG])}];")
 
 def collect_hashes(avbtool: str, kernel_image_path: str) -> Dict[str, str]:
     """Collects the hashes from the AVB footer of the kernel image."""
diff --git a/service_vm/requests/src/client_vm.rs b/service_vm/requests/src/client_vm.rs
index 5b4735d..c2f39e7 100644
--- a/service_vm/requests/src/client_vm.rs
+++ b/service_vm/requests/src/client_vm.rs
@@ -35,6 +35,7 @@
 
 type Result<T> = result::Result<T, RequestProcessingError>;
 
+const DICE_CDI_LEAF_SIGNATURE_INDEX: usize = 0;
 const ATTESTATION_KEY_SIGNATURE_INDEX: usize = 1;
 
 pub(super) fn request_attestation(
@@ -72,8 +73,9 @@
     let aad = &[];
 
     // Verifies the first signature with the leaf private key in the DICE chain.
-    // TODO(b/310931749): Verify the first signature with CDI_Leaf_Pub of
-    // the DICE chain in `cose_sign`.
+    cose_sign.verify_signature(DICE_CDI_LEAF_SIGNATURE_INDEX, aad, |signature, message| {
+        client_vm_dice_chain.microdroid_payload().subject_public_key.verify(signature, message)
+    })?;
 
     // Verifies the second signature with the public key in the CSR payload.
     let ec_public_key = EcKey::from_cose_public_key_slice(&csr_payload.public_key)?;
diff --git a/service_vm/requests/src/dice.rs b/service_vm/requests/src/dice.rs
index 8c804da..657e482 100644
--- a/service_vm/requests/src/dice.rs
+++ b/service_vm/requests/src/dice.rs
@@ -16,15 +16,19 @@
 
 use alloc::string::String;
 use alloc::vec::Vec;
+use bssl_avf::{ed25519_verify, Digester, EcKey};
 use cbor_util::{
-    cbor_value_type, value_to_array, value_to_byte_array, value_to_bytes, value_to_map,
-    value_to_num, value_to_text,
+    cbor_value_type, get_label_value, get_label_value_as_bytes, value_to_array,
+    value_to_byte_array, value_to_bytes, value_to_map, value_to_num, value_to_text,
 };
 use ciborium::value::Value;
 use core::cell::OnceCell;
 use core::result;
 use coset::{
-    self, iana, AsCborValue, CborSerializable, CoseError, CoseKey, CoseSign1, KeyOperation,
+    self,
+    iana::{self, EnumI64},
+    Algorithm, AsCborValue, CborSerializable, CoseError, CoseKey, CoseSign1, KeyOperation, KeyType,
+    Label,
 };
 use diced_open_dice::{DiceMode, HASH_SIZE};
 use log::error;
@@ -58,7 +62,7 @@
 /// ]
 #[derive(Debug, Clone)]
 pub(crate) struct ClientVmDiceChain {
-    pub(crate) payloads: Vec<DiceChainEntryPayload>,
+    payloads: Vec<DiceChainEntryPayload>,
 }
 
 impl ClientVmDiceChain {
@@ -130,7 +134,7 @@
         &self.payloads[self.payloads.len() - 2]
     }
 
-    fn microdroid_payload(&self) -> &DiceChainEntryPayload {
+    pub(crate) fn microdroid_payload(&self) -> &DiceChainEntryPayload {
         &self.payloads[self.payloads.len() - 1]
     }
 
@@ -194,15 +198,69 @@
     }
 }
 
+impl PublicKey {
+    /// Verifies the signature of the provided message with the public key.
+    ///
+    /// This function supports the following key/algorithm types as specified in
+    /// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
+    /// generateCertificateRequestV2.cddl:
+    ///
+    /// PubKeyEd25519 / PubKeyECDSA256 / PubKeyECDSA384
+    pub(crate) fn verify(&self, signature: &[u8], message: &[u8]) -> Result<()> {
+        match &self.0.kty {
+            KeyType::Assigned(iana::KeyType::EC2) => {
+                let public_key = EcKey::from_cose_public_key(&self.0)?;
+                let Some(Algorithm::Assigned(alg)) = self.0.alg else {
+                    error!("Invalid algorithm in COSE key {:?}", self.0.alg);
+                    return Err(RequestProcessingError::InvalidDiceChain);
+                };
+                let digester = match alg {
+                    iana::Algorithm::ES256 => Digester::sha256(),
+                    iana::Algorithm::ES384 => Digester::sha384(),
+                    _ => {
+                        error!("Unsupported algorithm in EC2 key: {:?}", alg);
+                        return Err(RequestProcessingError::InvalidDiceChain);
+                    }
+                };
+                let digest = digester.digest(message)?;
+                Ok(public_key.ecdsa_verify(signature, &digest)?)
+            }
+            KeyType::Assigned(iana::KeyType::OKP) => {
+                let curve_type =
+                    get_label_value(&self.0, Label::Int(iana::OkpKeyParameter::Crv.to_i64()))?;
+                if curve_type != &Value::from(iana::EllipticCurve::Ed25519.to_i64()) {
+                    error!("Unsupported curve type in OKP COSE key: {:?}", curve_type);
+                    return Err(RequestProcessingError::OperationUnimplemented);
+                }
+                let x = get_label_value_as_bytes(
+                    &self.0,
+                    Label::Int(iana::OkpKeyParameter::X.to_i64()),
+                )?;
+                let public_key = x.try_into().map_err(|_| {
+                    error!("Invalid ED25519 public key size: {}", x.len());
+                    RequestProcessingError::InvalidDiceChain
+                })?;
+                let signature = signature.try_into().map_err(|_| {
+                    error!("Invalid ED25519 signature size: {}", signature.len());
+                    RequestProcessingError::InvalidDiceChain
+                })?;
+                Ok(ed25519_verify(message, signature, public_key)?)
+            }
+            kty => {
+                error!("Unsupported key type in COSE key: {:?}", kty);
+                Err(RequestProcessingError::OperationUnimplemented)
+            }
+        }
+    }
+}
+
 /// Represents a partially decoded `DiceChainEntryPayload`. The whole payload is defined in:
 ///
 /// hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/
 /// generateCertificateRequestV2.cddl
 #[derive(Debug, Clone)]
 pub(crate) struct DiceChainEntryPayload {
-    /// TODO(b/310931749): Verify the DICE chain entry using the subject public key.
-    #[allow(dead_code)]
-    subject_public_key: PublicKey,
+    pub(crate) subject_public_key: PublicKey,
     mode: DiceMode,
     pub(crate) code_hash: [u8; HASH_SIZE],
     pub(crate) authority_hash: [u8; HASH_SIZE],
@@ -214,10 +272,13 @@
     /// extracts payload from the value.
     fn validate_cose_signature_and_extract_payload(
         value: Value,
-        _authority_public_key: &PublicKey,
+        authority_public_key: &PublicKey,
     ) -> Result<Self> {
         let cose_sign1 = CoseSign1::from_cbor_value(value)?;
-        // TODO(b/310931749): Verify the DICE chain entry using `authority_public_key`.
+        let aad = &[]; // AAD is not used in DICE chain entry.
+        cose_sign1.verify_signature(aad, |signature, message| {
+            authority_public_key.verify(signature, message)
+        })?;
 
         let payload = cose_sign1.payload.ok_or_else(|| {
             error!("No payload found in the DICE chain entry");
diff --git a/virtualizationservice/aidl/Android.bp b/virtualizationservice/aidl/Android.bp
index 91d91aa..c69fe8f 100644
--- a/virtualizationservice/aidl/Android.bp
+++ b/virtualizationservice/aidl/Android.bp
@@ -61,7 +61,7 @@
     unstable: true,
     backend: {
         java: {
-            sdk_version: "module_current",
+            enabled: false,
         },
         rust: {
             enabled: true,