Merge "[test] Test attestation request in e2e test rialto_test" into main
diff --git a/apex/sign_virt_apex.py b/apex/sign_virt_apex.py
index 8257aae..029ac76 100644
--- a/apex/sign_virt_apex.py
+++ b/apex/sign_virt_apex.py
@@ -27,6 +27,7 @@
 - lpmake, lpunpack, simg2img, img2simg, initrd_bootconfig
 """
 import argparse
+import builtins
 import hashlib
 import os
 import re
@@ -282,7 +283,7 @@
         avb_version_bc = re.search(
             r"androidboot.vbmeta.avb_version = \"([^\"]*)\"", bootconfigs).group(1)
         if avb_version_curr != avb_version_bc:
-            raise Exception(f'AVB version mismatch between current & one & \
+            raise builtins.Exception(f'AVB version mismatch between current & one & \
                 used to build bootconfigs:{avb_version_curr}&{avb_version_bc}')
 
     def calc_vbmeta_digest():
@@ -430,21 +431,32 @@
 
     # unpacked files (will be unpacked from super.img below)
     system_a_img = os.path.join(unpack_dir.name, 'system_a.img')
+    vendor_a_img = os.path.join(unpack_dir.name, 'vendor_a.img')
 
     # re-sign super.img
     # 1. unpack super.img
-    # 2. resign system
-    # 3. repack super.img out of resigned system
+    # 2. resign system and vendor (if exists)
+    # 3. repack super.img out of resigned system and vendor (if exists)
     UnpackSuperImg(args, files['super.img'], unpack_dir.name)
     system_a_f = Async(AddHashTreeFooter, args, key, system_a_img)
     partitions = {"system_a": system_a_img}
+    images = [system_a_img]
+    images_f = [system_a_f]
+
+    # if vendor_a.img exists, resign it
+    if os.path.exists(vendor_a_img):
+        partitions.update({'vendor_a': vendor_a_img})
+        images.append(vendor_a_img)
+        vendor_a_f = Async(AddHashTreeFooter, args, key, vendor_a_img)
+        images_f.append(vendor_a_f)
+
     Async(MakeSuperImage, args, partitions,
-          files['super.img'], wait=[system_a_f])
+          files['super.img'], wait=images_f)
 
     # re-generate vbmeta from re-signed system_a.img
     vbmeta_f = Async(MakeVbmetaImage, args, key, files['vbmeta.img'],
-                     images=[system_a_img],
-                     wait=[system_a_f])
+                     images=images,
+                     wait=images_f)
 
     vbmeta_bc_f = None
     if not args.do_not_update_bootconfigs:
diff --git a/compos/src/compsvc_main.rs b/compos/src/compsvc_main.rs
index b0fc323..128d581 100644
--- a/compos/src/compsvc_main.rs
+++ b/compos/src/compsvc_main.rs
@@ -54,10 +54,7 @@
     // SAFETY: We hold a strong pointer, so the raw pointer remains valid. The bindgen AIBinder
     // is the same type as sys::AIBinder. It is safe for on_ready to be invoked at any time, with
     // any parameter.
-    unsafe {
-        AVmPayload_runVsockRpcServer(service, COMPOS_VSOCK_PORT, Some(on_ready), param);
-    }
-    Ok(())
+    unsafe { AVmPayload_runVsockRpcServer(service, COMPOS_VSOCK_PORT, Some(on_ready), param) }
 }
 
 extern "C" fn on_ready(_param: *mut c_void) {
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index b811730..b369390 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -504,6 +504,18 @@
 
         fdt_err_or_option(ret)?.map(|offset| FdtProperty::new(self.fdt, offset)).transpose()
     }
+
+    /// Returns the phandle
+    pub fn get_phandle(&self) -> Result<Option<Phandle>> {
+        // This rewrites the fdt_get_phandle() because it doesn't return error code.
+        if let Some(prop) = self.getprop_u32(cstr!("phandle"))? {
+            Ok(Some(prop.try_into()?))
+        } else if let Some(prop) = self.getprop_u32(cstr!("linux,phandle"))? {
+            Ok(Some(prop.try_into()?))
+        } else {
+            Ok(None)
+        }
+    }
 }
 
 impl<'a> PartialEq for FdtNode<'a> {
@@ -1012,9 +1024,20 @@
 
     /// Returns a node with the phandle
     pub fn node_with_phandle(&self, phandle: Phandle) -> Result<Option<FdtNode>> {
-        // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
+        let offset = self.node_offset_with_phandle(phandle)?;
+        Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
+    }
+
+    /// Returns a mutable node with the phandle
+    pub fn node_mut_with_phandle(&mut self, phandle: Phandle) -> Result<Option<FdtNodeMut>> {
+        let offset = self.node_offset_with_phandle(phandle)?;
+        Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
+    }
+
+    fn node_offset_with_phandle(&self, phandle: Phandle) -> Result<Option<c_int>> {
+        // SAFETY: Accesses are constrained to the DT totalsize.
         let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_phandle(self.as_ptr(), phandle.0) };
-        Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self, offset }))
+        fdt_err_or_option(ret)
     }
 
     /// Returns the mutable root node of the tree.
diff --git a/libs/libfdt/tests/api_test.rs b/libs/libfdt/tests/api_test.rs
index bc306ad..d76b1a4 100644
--- a/libs/libfdt/tests/api_test.rs
+++ b/libs/libfdt/tests/api_test.rs
@@ -16,6 +16,7 @@
 
 //! Integration tests of the library libfdt.
 
+use core::ffi::CStr;
 use libfdt::{Fdt, FdtError, FdtNodeMut, Phandle};
 use std::ffi::CString;
 use std::fs;
@@ -106,11 +107,11 @@
     let data = fs::read(TEST_TREE_WITH_NO_MEMORY_NODE_PATH).unwrap();
     let fdt = Fdt::from_slice(&data).unwrap();
     let root = fdt.root().unwrap();
-    let expected = [cstr!("cpus"), cstr!("randomnode"), cstr!("chosen")];
+    let expected = [Ok(cstr!("cpus")), Ok(cstr!("randomnode")), Ok(cstr!("chosen"))];
 
-    for (node, name) in root.subnodes().unwrap().zip(expected) {
-        assert_eq!(node.name(), Ok(name));
-    }
+    let root_subnodes = root.subnodes().unwrap();
+    let subnode_names: Vec<_> = root_subnodes.map(|node| node.name()).collect();
+    assert_eq!(subnode_names, expected);
 }
 
 #[test]
@@ -119,18 +120,19 @@
     let fdt = Fdt::from_slice(&data).unwrap();
     let root = fdt.root().unwrap();
     let one_be = 0x1_u32.to_be_bytes();
-    let expected = [
-        (cstr!("model"), b"MyBoardName\0".as_ref()),
-        (cstr!("compatible"), b"MyBoardName\0MyBoardFamilyName\0".as_ref()),
-        (cstr!("#address-cells"), &one_be),
-        (cstr!("#size-cells"), &one_be),
-        (cstr!("empty_prop"), &[]),
+    type Result<T> = core::result::Result<T, FdtError>;
+    let expected: Vec<(Result<&CStr>, Result<&[u8]>)> = vec![
+        (Ok(cstr!("model")), Ok(b"MyBoardName\0".as_ref())),
+        (Ok(cstr!("compatible")), Ok(b"MyBoardName\0MyBoardFamilyName\0".as_ref())),
+        (Ok(cstr!("#address-cells")), Ok(&one_be)),
+        (Ok(cstr!("#size-cells")), Ok(&one_be)),
+        (Ok(cstr!("empty_prop")), Ok(&[])),
     ];
 
     let properties = root.properties().unwrap();
-    for (prop, (name, value)) in properties.zip(expected.into_iter()) {
-        assert_eq!((prop.name(), prop.value()), (Ok(name), Ok(value)));
-    }
+    let subnode_properties: Vec<_> = properties.map(|prop| (prop.name(), prop.value())).collect();
+
+    assert_eq!(subnode_properties, expected);
 }
 
 #[test]
@@ -138,12 +140,16 @@
     let data = fs::read(TEST_TREE_WITH_NO_MEMORY_NODE_PATH).unwrap();
     let fdt = Fdt::from_slice(&data).unwrap();
     let node = fdt.node(cstr!("/cpus/PowerPC,970@1")).unwrap().unwrap();
-    let expected = [cstr!(""), cstr!("cpus"), cstr!("PowerPC,970@1")];
+    let expected = vec![Ok(cstr!("")), Ok(cstr!("cpus")), Ok(cstr!("PowerPC,970@1"))];
 
-    for (depth, name) in expected.into_iter().enumerate() {
-        let supernode = node.supernode_at_depth(depth).unwrap();
-        assert_eq!(supernode.name(), Ok(name));
+    let mut supernode_names = vec![];
+    let mut depth = 0;
+    while let Ok(supernode) = node.supernode_at_depth(depth) {
+        supernode_names.push(supernode.name());
+        depth += 1;
     }
+
+    assert_eq!(supernode_names, expected);
 }
 
 #[test]
@@ -200,6 +206,40 @@
 }
 
 #[test]
+fn node_mut_with_phandle() {
+    let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+    let fdt = Fdt::from_mut_slice(&mut data).unwrap();
+
+    // Test linux,phandle
+    let phandle = Phandle::new(0xFF).unwrap();
+    let node: FdtNodeMut = fdt.node_mut_with_phandle(phandle).unwrap().unwrap();
+    assert_eq!(node.as_node().name(), Ok(cstr!("node_zz")));
+
+    // Test phandle
+    let phandle = Phandle::new(0x22).unwrap();
+    let node: FdtNodeMut = fdt.node_mut_with_phandle(phandle).unwrap().unwrap();
+    assert_eq!(node.as_node().name(), Ok(cstr!("node_abc")));
+}
+
+#[test]
+fn node_get_phandle() {
+    let data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
+    let fdt = Fdt::from_slice(&data).unwrap();
+
+    // Test linux,phandle
+    let node = fdt.node(cstr!("/node_z/node_zz")).unwrap().unwrap();
+    assert_eq!(node.get_phandle(), Ok(Phandle::new(0xFF)));
+
+    // Test phandle
+    let node = fdt.node(cstr!("/node_a/node_ab/node_abc")).unwrap().unwrap();
+    assert_eq!(node.get_phandle(), Ok(Phandle::new(0x22)));
+
+    // Test no phandle
+    let node = fdt.node(cstr!("/node_b")).unwrap().unwrap();
+    assert_eq!(node.get_phandle(), Ok(None));
+}
+
+#[test]
 fn node_nop() {
     let mut data = fs::read(TEST_TREE_PHANDLE_PATH).unwrap();
     let fdt = Fdt::from_mut_slice(&mut data).unwrap();
diff --git a/microdroid/Android.bp b/microdroid/Android.bp
index 42ff4b0..4e735e6 100644
--- a/microdroid/Android.bp
+++ b/microdroid/Android.bp
@@ -243,7 +243,35 @@
         "echo ro.product.cpu.abi=arm64-v8a) > $(out)",
 }
 
-logical_partition {
+// Need to keep microdroid_vendor for the release configurations that don't
+// have RELEASE_AVF_ENABLE_VENDOR_MODULES build flag enabled.
+android_filesystem {
+    name: "microdroid_vendor",
+    partition_name: "vendor",
+    use_avb: true,
+    avb_private_key: ":microdroid_sign_key",
+    avb_algorithm: "SHA256_RSA4096",
+    avb_hash_algorithm: "sha256",
+    file_contexts: ":microdroid_vendor_file_contexts.gen",
+    // For deterministic output, use fake_timestamp, hard-coded uuid
+    fake_timestamp: "1611569676",
+    // python -c "import uuid; print(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com/avf/microdroid/vendor'))"
+    uuid: "156d40d7-8d8e-5c99-8913-ec82de549a70",
+}
+
+soong_config_module_type {
+    name: "flag_aware_microdroid_super_partition",
+    module_type: "logical_partition",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "release_avf_enable_vendor_modules",
+    ],
+    properties: [
+        "default_group",
+    ],
+}
+
+flag_aware_microdroid_super_partition {
     name: "microdroid_super",
     sparse: true,
     size: "auto",
@@ -253,6 +281,16 @@
             filesystem: ":microdroid",
         },
     ],
+    soong_config_variables: {
+        release_avf_enable_vendor_modules: {
+            default_group: [
+                {
+                    name: "vendor_a",
+                    filesystem: ":microdroid_vendor",
+                },
+            ],
+        },
+    },
 }
 
 android_filesystem {
@@ -330,13 +368,30 @@
     srcs: [":avb_testkey_rsa4096"],
 }
 
-vbmeta {
+soong_config_module_type {
+    name: "flag_aware_microdroid_vbmeta",
+    module_type: "vbmeta",
+    config_namespace: "ANDROID",
+    bool_variables: [
+        "release_avf_enable_vendor_modules",
+    ],
+    properties: [
+        "partitions",
+    ],
+}
+
+flag_aware_microdroid_vbmeta {
     name: "microdroid_vbmeta",
     partition_name: "vbmeta",
     private_key: ":microdroid_sign_key",
     partitions: [
         "microdroid",
     ],
+    soong_config_variables: {
+        release_avf_enable_vendor_modules: {
+            partitions: ["microdroid_vendor"],
+        },
+    },
 }
 
 prebuilt_etc {
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 946ed85..b7b5900 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -224,7 +224,7 @@
     srcs: [":pvmfw_platform.dts.preprocessed"],
     out: ["lib.rs"],
     tools: ["dtc"],
-    cmd: "$(location dtc) -I dts -O dtb -o $(genDir)/compiled.dtbo $(in) && " +
+    cmd: "$(location dtc) -@ -I dts -O dtb -o $(genDir)/compiled.dtbo $(in) && " +
         "(" +
         "    echo '#![no_std]';" +
         "    echo '#![allow(missing_docs)]';" +
diff --git a/pvmfw/platform.dts b/pvmfw/platform.dts
index cb8e30d..4a269c3 100644
--- a/pvmfw/platform.dts
+++ b/pvmfw/platform.dts
@@ -261,4 +261,64 @@
 		clock-frequency = <10>;
 		timeout-sec = <8>;
 	};
+
+	pviommu_0: pviommu0 {
+		compatible = "pkvm,pviommu";
+		id = <PLACEHOLDER>;
+		#iommu-cells = <0>;
+	};
+
+	pviommu_1: pviommu1 {
+		compatible = "pkvm,pviommu";
+		id = <PLACEHOLDER>;
+		#iommu-cells = <0>;
+	};
+
+	pviommu_2: pviommu2 {
+		compatible = "pkvm,pviommu";
+		id = <PLACEHOLDER>;
+		#iommu-cells = <0>;
+	};
+
+	pviommu_3: pviommu3 {
+		compatible = "pkvm,pviommu";
+		id = <PLACEHOLDER>;
+		#iommu-cells = <0>;
+	};
+
+	pviommu_4: pviommu4 {
+		compatible = "pkvm,pviommu";
+		id = <PLACEHOLDER>;
+		#iommu-cells = <0>;
+	};
+
+	pviommu_5: pviommu5 {
+		compatible = "pkvm,pviommu";
+		id = <PLACEHOLDER>;
+		#iommu-cells = <0>;
+	};
+
+	pviommu_6: pviommu6 {
+		compatible = "pkvm,pviommu";
+		id = <PLACEHOLDER>;
+		#iommu-cells = <0>;
+	};
+
+	pviommu_7: pviommu7 {
+		compatible = "pkvm,pviommu";
+		id = <PLACEHOLDER>;
+		#iommu-cells = <0>;
+	};
+
+	pviommu_8: pviommu8 {
+		compatible = "pkvm,pviommu";
+		id = <PLACEHOLDER>;
+		#iommu-cells = <0>;
+	};
+
+	pviommu_9: pviommu9 {
+		compatible = "pkvm,pviommu";
+		id = <PLACEHOLDER>;
+		#iommu-cells = <0>;
+	};
 };
diff --git a/pvmfw/src/device_assignment.rs b/pvmfw/src/device_assignment.rs
index 7eae09f..a92b418 100644
--- a/pvmfw/src/device_assignment.rs
+++ b/pvmfw/src/device_assignment.rs
@@ -401,17 +401,28 @@
         unsafe {
             platform_dt.apply_overlay(vm_dtbo.as_mut()).unwrap();
         }
+        device_info.patch(platform_dt).unwrap();
 
-        let rng_node = platform_dt.node(cstr!("/rng")).unwrap().unwrap();
-        let expected: Vec<(&CStr, Vec<u8>)> = vec![
-            (cstr!("android,rng,ignore-gctrl-reset"), Vec::<u8>::new()),
-            (cstr!("compatible"), b"android,rng\0".to_vec()),
-            (cstr!("reg"), into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF])),
-            (cstr!("interrupts"), into_fdt_prop(vec![0x0, 0xF, 0x4])),
+        type FdtResult<T> = libfdt::Result<T>;
+        let expected: Vec<(FdtResult<&CStr>, FdtResult<Vec<u8>>)> = vec![
+            (Ok(cstr!("android,rng,ignore-gctrl-reset")), Ok(Vec::new())),
+            (Ok(cstr!("compatible")), Ok(Vec::from(*b"android,rng\0"))),
+            (Ok(cstr!("interrupts")), Ok(into_fdt_prop(vec![0x0, 0xF, 0x4]))),
+            (Ok(cstr!("reg")), Ok(into_fdt_prop(vec![0x0, 0x9, 0x0, 0xFF]))),
         ];
 
-        for (prop, (prop_name, prop_value)) in rng_node.properties().unwrap().zip(expected) {
-            assert_eq!((prop.name(), prop.value()), (Ok(prop_name), Ok(prop_value.as_slice())));
-        }
+        let rng_node = platform_dt.node(cstr!("/rng")).unwrap().unwrap();
+        let mut properties: Vec<_> = rng_node
+            .properties()
+            .unwrap()
+            .map(|prop| (prop.name(), prop.value().map(|x| x.into())))
+            .collect();
+        properties.sort_by(|a, b| {
+            let lhs = a.0.unwrap_or_default();
+            let rhs = b.0.unwrap_or_default();
+            lhs.partial_cmp(rhs).unwrap()
+        });
+
+        assert_eq!(properties, expected);
     }
 }
diff --git a/secretkeeper/dice_policy/Android.bp b/secretkeeper/dice_policy/Android.bp
index a7ac5b9..4f1e8b6 100644
--- a/secretkeeper/dice_policy/Android.bp
+++ b/secretkeeper/dice_policy/Android.bp
@@ -13,7 +13,9 @@
         "libanyhow",
         "libciborium",
         "libcoset",
+        "libnum_traits",
     ],
+    proc_macros: ["libnum_derive"],
 }
 
 rust_library {
diff --git a/secretkeeper/dice_policy/src/lib.rs b/secretkeeper/dice_policy/src/lib.rs
index 327b8a4..2e91305 100644
--- a/secretkeeper/dice_policy/src/lib.rs
+++ b/secretkeeper/dice_policy/src/lib.rs
@@ -57,16 +57,20 @@
 //!
 //! value = bool / int / tstr / bstr
 
-use anyhow::{anyhow, bail, Context, Result};
+use anyhow::{anyhow, bail, ensure, Context, Result};
 use ciborium::Value;
 use coset::{AsCborValue, CoseSign1};
+use num_derive::FromPrimitive;
+use num_traits::FromPrimitive;
 use std::borrow::Cow;
+use std::iter::zip;
 
 const DICE_POLICY_VERSION: u64 = 1;
 
 /// Constraint Types supported in Dice policy.
+#[repr(u16)]
 #[non_exhaustive]
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, FromPrimitive, PartialEq)]
 pub enum ConstraintType {
     /// Enforce exact match criteria, indicating the policy should match
     /// if the dice chain has exact same specified values.
@@ -133,6 +137,7 @@
     ///    ];
     ///
     /// 2. For hypothetical (and highly simplified) dice chain:
+    ///
     ///    [ROT_KEY, [{1 : 'a', 2 : {200 : 5, 201 : 'b'}}]]
     ///    The following can be used
     ///    constraint_spec =[
@@ -140,13 +145,7 @@
     ///     ConstraintSpec(ConstraintType::GreaterOrEqual, vec![2, 200]),// matches any value >= 5
     ///    ];
     pub fn from_dice_chain(dice_chain: &[u8], constraint_spec: &[ConstraintSpec]) -> Result<Self> {
-        // TODO(b/298217847): Check if the given dice chain adheres to Explicit-key DiceCertChain
-        // format and if not, convert it before policy construction.
-        let dice_chain = value_from_bytes(dice_chain).context("Unable to decode top-level CBOR")?;
-        let dice_chain = match dice_chain {
-            Value::Array(array) if array.len() >= 2 => array,
-            _ => bail!("Expected an array of at least length 2, found: {:?}", dice_chain),
-        };
+        let dice_chain = deserialize_dice_chain(dice_chain)?;
         let mut constraints_list: Vec<NodeConstraints> = Vec::with_capacity(dice_chain.len());
         let mut it = dice_chain.into_iter();
 
@@ -167,6 +166,61 @@
             node_constraints_list: constraints_list.into_boxed_slice(),
         })
     }
+
+    /// Dice chain policy verifier - Compare the input dice chain against this Dice policy.
+    /// The method returns Ok() if the dice chain meets the constraints set in Dice policy,
+    /// otherwise returns error in case of mismatch.
+    /// TODO(b/291238565) Create a separate error module for DicePolicy mismatches.
+    pub fn matches_dice_chain(&self, dice_chain: &[u8]) -> Result<()> {
+        let dice_chain = deserialize_dice_chain(dice_chain)?;
+        ensure!(
+            dice_chain.len() == self.node_constraints_list.len(),
+            format!(
+                "Dice chain size({}) does not match policy({})",
+                dice_chain.len(),
+                self.node_constraints_list.len()
+            )
+        );
+
+        for (n, (dice_node, node_constraints)) in
+            zip(dice_chain, self.node_constraints_list.iter()).enumerate()
+        {
+            let dice_node_payload = if n == 0 {
+                dice_node
+            } else {
+                cbor_value_from_cose_sign(dice_node)
+                    .with_context(|| format!("Unable to get Cose payload at: {}", n))?
+            };
+            check_constraints_on_node(node_constraints, &dice_node_payload)
+                .context(format!("Mismatch found at {}", n))?;
+        }
+        Ok(())
+    }
+}
+
+fn check_constraints_on_node(node_constraints: &NodeConstraints, dice_node: &Value) -> Result<()> {
+    for constraint in node_constraints.0.iter() {
+        check_constraint_on_node(constraint, dice_node)?;
+    }
+    Ok(())
+}
+
+fn check_constraint_on_node(constraint: &Constraint, dice_node: &Value) -> Result<()> {
+    let Constraint(cons_type, path, value_in_constraint) = constraint;
+    let value_in_node = lookup_value_in_nested_map(dice_node, path)?;
+    match ConstraintType::from_u16(*cons_type).ok_or(anyhow!("Unexpected Constraint type"))? {
+        ConstraintType::ExactMatch => ensure!(value_in_node == *value_in_constraint),
+        ConstraintType::GreaterOrEqual => {
+            let value_in_node = value_in_node
+                .as_integer()
+                .ok_or(anyhow!("Mismatch type: expected a cbor integer"))?;
+            let value_min = value_in_constraint
+                .as_integer()
+                .ok_or(anyhow!("Mismatch type: expected a cbor integer"))?;
+            ensure!(value_in_node >= value_min);
+        }
+    };
+    Ok(())
 }
 
 // Take the payload of a dice node & construct the constraints on it.
@@ -231,6 +285,17 @@
         Some(payload) => Ok(value_from_bytes(&payload)?),
     }
 }
+fn deserialize_dice_chain(dice_chain_bytes: &[u8]) -> Result<Vec<Value>> {
+    // TODO(b/298217847): Check if the given dice chain adheres to Explicit-key DiceCertChain
+    // format and if not, convert it.
+    let dice_chain =
+        value_from_bytes(dice_chain_bytes).context("Unable to decode top-level CBOR")?;
+    let dice_chain = match dice_chain {
+        Value::Array(array) if array.len() >= 2 => array,
+        _ => bail!("Expected an array of at least length 2, found: {:?}", dice_chain),
+    };
+    Ok(dice_chain)
+}
 
 /// Decodes the provided binary CBOR-encoded value and returns a
 /// ciborium::Value struct wrapped in Result.
@@ -266,38 +331,29 @@
         constraint_spec: Vec<ConstraintSpec>,
         // The expected dice policy if above constraint_spec is applied to input_dice.
         expected_dice_policy: DicePolicy,
+        // Another dice chain, which is almost same as the input_dice, but (roughly) imitates
+        // an 'updated' one, ie, some int entries are higher than corresponding entry
+        // in input_chain.
+        updated_input_dice: Vec<u8>,
     }
 
     impl TestArtifacts {
         // Get an example instance of TestArtifacts. This uses a hard coded, hypothetical
         // chain of certificates & a list of constraint_spec on this.
         fn get_example() -> Self {
-            const EXAMPLE_NUM: i64 = 59765;
+            const EXAMPLE_NUM_1: i64 = 59765;
+            const EXAMPLE_NUM_2: i64 = 59766;
             const EXAMPLE_STRING: &str = "testing_dice_policy";
+            const UNCONSTRAINED_STRING: &str = "unconstrained_string";
+            const ANOTHER_UNCONSTRAINED_STRING: &str = "another_unconstrained_string";
 
             let rot_key = CoseKey::default().to_cbor_value().unwrap();
-            let nested_payload = cbor!({
-                100 => EXAMPLE_NUM
-            })
-            .unwrap();
-            let payload = cbor!({
-                1 => EXAMPLE_STRING,
-                2 => "some_other_example_string",
-                3 => Value::Bytes(value_to_bytes(&nested_payload).unwrap()),
-            })
-            .unwrap();
-            let payload = value_to_bytes(&payload).unwrap();
-            let dice_node = CoseSign1 {
-                protected: ProtectedHeader::default(),
-                unprotected: Header::default(),
-                payload: Some(payload),
-                signature: b"ddef".to_vec(),
-            }
-            .to_cbor_value()
-            .unwrap();
-            let input_dice = Value::Array([rot_key.clone(), dice_node].to_vec());
-
-            let input_dice = value_to_bytes(&input_dice).unwrap();
+            let input_dice = Self::get_dice_chain_helper(
+                rot_key.clone(),
+                EXAMPLE_NUM_1,
+                EXAMPLE_STRING,
+                UNCONSTRAINED_STRING,
+            );
 
             // Now construct constraint_spec on the input dice, note this will use the keys
             // which are also hardcoded within the get_dice_chain_helper.
@@ -305,7 +361,7 @@
             let constraint_spec = vec![
                 ConstraintSpec::new(ConstraintType::ExactMatch, vec![1]).unwrap(),
                 // Notice how key "2" is (deliberately) absent in ConstraintSpec
-                // so policy should not constraint it.
+                // so policy should not constrain it.
                 ConstraintSpec::new(ConstraintType::GreaterOrEqual, vec![3, 100]).unwrap(),
             ];
             let expected_dice_policy = DicePolicy {
@@ -325,12 +381,53 @@
                         Constraint(
                             ConstraintType::GreaterOrEqual as u16,
                             vec![3, 100],
-                            Value::from(EXAMPLE_NUM),
+                            Value::from(EXAMPLE_NUM_1),
                         ),
                     ])),
                 ]),
             };
-            Self { input_dice, constraint_spec, expected_dice_policy }
+
+            let updated_input_dice = Self::get_dice_chain_helper(
+                rot_key.clone(),
+                EXAMPLE_NUM_2,
+                EXAMPLE_STRING,
+                ANOTHER_UNCONSTRAINED_STRING,
+            );
+            Self { input_dice, constraint_spec, expected_dice_policy, updated_input_dice }
+        }
+
+        // Helper method method to generate a dice chain with a given rot_key.
+        // Other arguments are ad-hoc values in the nested map. Callers use these to
+        // construct appropriate constrains in dice policies.
+        fn get_dice_chain_helper(
+            rot_key: Value,
+            version: i64,
+            constrained_string: &str,
+            unconstrained_string: &str,
+        ) -> Vec<u8> {
+            let nested_payload = cbor!({
+                100 => version
+            })
+            .unwrap();
+
+            let payload = cbor!({
+                1 => constrained_string,
+                2 => unconstrained_string,
+                3 => Value::Bytes(value_to_bytes(&nested_payload).unwrap()),
+            })
+            .unwrap();
+            let payload = value_to_bytes(&payload).unwrap();
+            let dice_node = CoseSign1 {
+                protected: ProtectedHeader::default(),
+                unprotected: Header::default(),
+                payload: Some(payload),
+                signature: b"ddef".to_vec(),
+            }
+            .to_cbor_value()
+            .unwrap();
+            let input_dice = Value::Array([rot_key.clone(), dice_node].to_vec());
+
+            value_to_bytes(&input_dice).unwrap()
         }
     }
 
@@ -344,6 +441,43 @@
         assert_eq!(policy, example.expected_dice_policy);
     }
 
+    test!(policy_matches_original_dice_chain);
+    fn policy_matches_original_dice_chain() {
+        let example = TestArtifacts::get_example();
+        assert!(
+            DicePolicy::from_dice_chain(&example.input_dice, &example.constraint_spec)
+                .unwrap()
+                .matches_dice_chain(&example.input_dice)
+                .is_ok(),
+            "The dice chain did not match the policy constructed out of it!"
+        );
+    }
+
+    test!(policy_matches_updated_dice_chain);
+    fn policy_matches_updated_dice_chain() {
+        let example = TestArtifacts::get_example();
+        assert!(
+            DicePolicy::from_dice_chain(&example.input_dice, &example.constraint_spec)
+                .unwrap()
+                .matches_dice_chain(&example.updated_input_dice)
+                .is_ok(),
+            "The updated dice chain did not match the original policy!"
+        );
+    }
+
+    test!(policy_mismatch_downgraded_dice_chain);
+    fn policy_mismatch_downgraded_dice_chain() {
+        let example = TestArtifacts::get_example();
+        assert!(
+            DicePolicy::from_dice_chain(&example.updated_input_dice, &example.constraint_spec)
+                .unwrap()
+                .matches_dice_chain(&example.input_dice)
+                .is_err(),
+            "The (downgraded) dice chain matched the policy constructed out of the 'updated'\
+            dice chain!!"
+        );
+    }
+
     test!(policy_dice_size_is_same);
     fn policy_dice_size_is_same() {
         // This is the number of certs in compos bcc (including the first ROT)
diff --git a/vm_payload/include/vm_payload.h b/vm_payload/include/vm_payload.h
index 951b57f..78cd80d 100644
--- a/vm_payload/include/vm_payload.h
+++ b/vm_payload/include/vm_payload.h
@@ -19,7 +19,6 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
-#include <stdnoreturn.h>
 #include <sys/cdefs.h>
 
 #include "vm_main.h"
@@ -81,9 +80,9 @@
  * callback will be called at most once.
  * \param param parameter to be passed to the `on_ready` callback.
  */
-noreturn void AVmPayload_runVsockRpcServer(AIBinder* _Nonnull service, uint32_t port,
-                                           void (*_Nullable on_ready)(void* _Nullable param),
-                                           void* _Nullable param);
+__attribute__((noreturn)) void AVmPayload_runVsockRpcServer(
+        AIBinder* _Nonnull service, uint32_t port,
+        void (*_Nullable on_ready)(void* _Nullable param), void* _Nullable param);
 
 /**
  * Returns all or part of a 32-byte secret that is bound to this unique VM