[opentitanlib] Add OTP hex file generation.

Signed-off-by: Jon Flatley <jflat@google.com>
diff --git a/sw/host/opentitanlib/BUILD b/sw/host/opentitanlib/BUILD
index 626e5c5..7caaad3 100644
--- a/sw/host/opentitanlib/BUILD
+++ b/sw/host/opentitanlib/BUILD
@@ -21,6 +21,13 @@
         "src/io/spi.rs",
         "src/io/uart.rs",
         "src/lib.rs",
+        "src/otp/lc_state.rs",
+        "src/otp/mod.rs",
+        "src/otp/num_de.rs",
+        "src/otp/otp.rs",
+        "src/otp/otp_img.rs",
+        "src/otp/otp_mmap.rs",
+        "src/otp/vmem_serialize.rs",
         "src/spiflash/flash.rs",
         "src/spiflash/mod.rs",
         "src/spiflash/sfdp.rs",
@@ -48,8 +55,8 @@
         "src/util/file.rs",
         "src/util/image.rs",
         "src/util/mod.rs",
-        "src/util/present.rs",
         "src/util/parse_int.rs",
+        "src/util/present.rs",
         "src/util/usb.rs",
         "src/util/voltage.rs",
     ],
diff --git a/sw/host/opentitanlib/Cargo.toml b/sw/host/opentitanlib/Cargo.toml
index b29e2f2..9490a3b 100644
--- a/sw/host/opentitanlib/Cargo.toml
+++ b/sw/host/opentitanlib/Cargo.toml
@@ -35,6 +35,8 @@
 
 serde = { version="1", features=["serde_derive"] }
 serde_json = "1"
+deser-hjson = "1.0.2"
+rand = "0.8.4"
 erased-serde = "0.3.12"
 opentitantool_derive = {path = "opentitantool_derive"}
 
diff --git a/sw/host/opentitanlib/src/lib.rs b/sw/host/opentitanlib/src/lib.rs
index 38e9c55..71ddeab 100644
--- a/sw/host/opentitanlib/src/lib.rs
+++ b/sw/host/opentitanlib/src/lib.rs
@@ -5,6 +5,7 @@
 pub mod app;
 pub mod bootstrap;
 pub mod io;
+pub mod otp;
 pub mod spiflash;
 pub mod transport;
 pub mod util;
diff --git a/sw/host/opentitanlib/src/otp/lc_state.rs b/sw/host/opentitanlib/src/otp/lc_state.rs
new file mode 100644
index 0000000..813d8da
--- /dev/null
+++ b/sw/host/opentitanlib/src/otp/lc_state.rs
@@ -0,0 +1,110 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+use anyhow::{bail, Result};
+use serde::Deserialize;
+use std::fs;
+use std::path::Path;
+
+/// SECDED matrix used for ECC in OTP.
+#[derive(Deserialize, Debug)]
+pub struct LcSecded {
+    /// The number of bits of data covered by ECC.
+    data_width: usize,
+    /// The number of ECC bits.
+    ecc_width: usize,
+    /// ECC matrix used for computing ECC bits.
+    ecc_matrix: Vec<Vec<u8>>,
+}
+
+/// The internal representation of lc_ctrl_state, used in OTP operations.
+#[derive(Deserialize, Debug)]
+pub struct LcState {
+    secded: LcSecded,
+}
+
+impl LcSecded {
+    pub fn new(in_file: &Path) -> Result<LcSecded> {
+        let json_text = fs::read_to_string(in_file)?;
+        let res: LcState = deser_hjson::from_str(&json_text)?;
+        if res.secded.ecc_matrix.len() != res.secded.ecc_width {
+            bail!("Bad ecc matrix length {}", res.secded.ecc_matrix.len());
+        }
+        Ok(res.secded)
+    }
+
+    fn bit_index(data: &[u8], index: usize) -> bool {
+        let byte = index / 8;
+        let bit = index % 8;
+        data[byte] & (1 << bit) != 0
+    }
+
+    pub fn ecc_encode(&self, mut data: Vec<u8>) -> Result<Vec<u8>> {
+        if data.len() * 8 != self.data_width {
+            bail!("Bad data length for ecc {}", data.len() * 8);
+        }
+        let data_len = data.len();
+        data.resize(data_len + self.ecc_byte_len(), 0);
+        for (i, matrix) in self.ecc_matrix.iter().enumerate() {
+            let mut bit = false;
+            for j in matrix {
+                bit ^= Self::bit_index(&data, *j as usize);
+            }
+            if bit {
+                let byte = i / 8 + data_len;
+                let bit = i % 8;
+                data[byte] |= 1 << bit;
+            }
+        }
+
+        Ok(data)
+    }
+
+    pub fn ecc_byte_len(&self) -> usize {
+        if self.ecc_width == 0 {
+            0
+        } else {
+            (self.ecc_width - 1) / 8 + 1
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use anyhow::Result;
+    use deser_hjson::from_str;
+    use std::fs::read_to_string;
+
+    #[test]
+    fn test_lc_state_deserialize() -> Result<()> {
+        let _: LcState = from_str(&read_to_string("tests/lc_ctrl_state.hjson")?)?;
+        Ok(())
+    }
+
+    #[test]
+    fn test_ecc_encode() {
+        let secded = LcSecded {
+            data_width: 16,
+            ecc_width: 6,
+            ecc_matrix: vec![
+                vec![0, 1, 3, 4, 6, 8, 10, 11, 13, 15], // ECC bit 0
+                vec![0, 2, 3, 5, 6, 9, 10, 12, 13],     // ECC bit 1
+                vec![1, 2, 3, 7, 8, 9, 10, 14, 15],     // ECC bit 2
+                vec![4, 5, 6, 7, 8, 9, 10],             // ECC bit 3
+                vec![11, 12, 13, 14, 15],               // ECC bit 4
+                vec![
+                    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+                ], // Parity bit
+            ],
+        };
+
+        let zero: Vec<u8> = vec![0, 0];
+        let a5a5: Vec<u8> = vec![0xa5, 0xa5];
+        let fcc5: Vec<u8> = vec![0xfc, 0xc5];
+        assert_eq!(vec![0u8, 0, 0], secded.ecc_encode(zero).unwrap());
+        assert_eq!(vec![0xa5u8, 0xa5, 0x27], secded.ecc_encode(a5a5).unwrap());
+        assert_eq!(vec![0x0fcu8, 0xc5, 0x06], secded.ecc_encode(fcc5).unwrap())
+    }
+}
diff --git a/sw/host/opentitanlib/src/otp/mod.rs b/sw/host/opentitanlib/src/otp/mod.rs
new file mode 100644
index 0000000..8465483
--- /dev/null
+++ b/sw/host/opentitanlib/src/otp/mod.rs
@@ -0,0 +1,37 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+pub mod lc_state;
+pub mod otp;
+pub mod otp_img;
+pub mod otp_mmap;
+pub mod vmem_serialize;
+
+mod num_de;
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use anyhow::Result;
+    use std::path::Path;
+
+    #[test]
+    fn test_vmem_serialize() -> Result<()> {
+        let mut otp_mmap = otp_mmap::OtpMap::new(Path::new("tests/otp_ctrl_mmap.hjson"))?;
+        let mut otp_img = otp_img::OtpImg::new(Path::new("tests/otp_ctrl_img_dev.hjson"))?;
+        let lc_state = lc_state::LcSecded::new(Path::new("tests/lc_ctrl_state.hjson"))?;
+        let vmem = otp_mmap.make_vmem(&mut otp_img)?;
+        let keys = otp_mmap.generate_keys(&otp_img);
+        let result = vmem.generate(keys, &lc_state)?;
+        let expected = std::fs::read_to_string(Path::new("tests/output.vmem"))?;
+        let expected = expected
+            .split("\n")
+            .filter(|s| !s.is_empty())
+            .collect::<Vec<&str>>();
+
+        assert_eq!(result, expected);
+
+        Ok(())
+    }
+}
diff --git a/sw/host/opentitanlib/src/otp/num_de.rs b/sw/host/opentitanlib/src/otp/num_de.rs
new file mode 100644
index 0000000..d621e9e
--- /dev/null
+++ b/sw/host/opentitanlib/src/otp/num_de.rs
@@ -0,0 +1,228 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+/// Deserialization utilities for certain values in OTP HJSON files.
+///
+/// The OTP HJSON files have some strange values:
+///
+/// Integers, sometimes wrapped in strings, with inconsistent formatting and meta values, such as:
+///   - value: "0x739"
+///   - key_size: "16"
+///   - seed: "10556718629619452145"
+///   - seed: 01931961561863975174  // This is a decimal integer, not octal.
+///   - value: "<random>"
+///
+/// Additionally, some values have sizes defined within the config files themselves, such as the
+/// keys. This module exists to handle these peculiar cases.
+use anyhow::Result;
+use rand::RngCore;
+use serde::de::{self, Deserializer, Unexpected};
+use serde::ser::Serializer;
+use serde::{Deserialize, Serialize};
+use std::any::type_name;
+use std::fmt;
+use std::marker::PhantomData;
+use std::ops::Deref;
+
+use crate::util::parse_int::{ParseInt, ParseIntError};
+
+pub fn _serialize<S, T>(_r: T, _ser: S) -> Result<S::Ok, S::Error>
+where
+    S: Serializer,
+    T: Serialize + Copy,
+{
+    unimplemented!();
+}
+
+/// Deserialize numeric types from HJSON config files.
+pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
+where
+    D: Deserializer<'de>,
+    T: ParseInt,
+{
+    struct Visitor<U>(PhantomData<U>);
+
+    impl<'a, U: ParseInt> de::Visitor<'a> for Visitor<U> {
+        type Value = U;
+
+        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+            formatter.write_fmt(format_args!("a string that parses to {}", type_name::<U>()))
+        }
+
+        fn visit_string<E>(self, mut name: String) -> Result<Self::Value, E>
+        where
+            E: de::Error,
+        {
+            if name.starts_with("false") {
+                name = "0".to_owned()
+            } else if name.starts_with("true") {
+                name = "1".to_owned()
+            }
+
+            let trimmed = if name.starts_with("0x") {
+                &name
+            } else {
+                let trimmed = name[0..name.len() - 1].trim_start_matches('0');
+                &name[name.len() - trimmed.len() - 1..]
+            };
+
+            match U::from_str(trimmed) {
+                Ok(value) => Ok(value),
+                Err(_) => Err(de::Error::invalid_value(Unexpected::Str(trimmed), &self)),
+            }
+        }
+
+        fn visit_str<E>(self, name: &str) -> Result<Self::Value, E>
+        where
+            E: de::Error,
+        {
+            self.visit_string(name.to_owned())
+        }
+
+        fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
+        where
+            E: de::Error,
+        {
+            if v {
+                self.visit_str("1")
+            } else {
+                self.visit_str("0")
+            }
+        }
+    }
+
+    deserializer.deserialize_string(Visitor {
+        0: PhantomData::<T>,
+    })
+}
+
+/// Placeholder type for values that cannot be resolved during deserialization.
+#[derive(Debug, PartialEq, Clone)]
+enum DeferredInit {
+    Initialized(Vec<u8>),
+    Random,
+}
+
+#[derive(Debug, Clone, Deserialize)]
+pub struct DeferredValue(#[serde(with = "self")] DeferredInit);
+
+impl DeferredValue {
+    pub fn resolve(&self, size: usize, rng: &mut dyn RngCore) -> Vec<u8> {
+        match self.0.clone() {
+            DeferredInit::Initialized(mut vec) => {
+                vec.resize(size, 0);
+                vec
+            }
+            DeferredInit::Random => {
+                let mut vec = vec![0u8; size];
+                rng.fill_bytes(&mut vec);
+                vec
+            }
+        }
+    }
+
+    pub fn is_initialized(&self) -> bool {
+        matches!(self.0, DeferredInit::Initialized(_))
+    }
+}
+
+impl ParseInt for DeferredInit {
+    type FromStrRadixErr = ParseIntError;
+
+    fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
+        Ok(DeferredInit::Initialized(Vec::<u8>::from_str_radix(
+            src, radix,
+        )?))
+    }
+
+    fn from_str(src: &str) -> Result<Self, ParseIntError> {
+        if src == "<random>" {
+            Ok(DeferredInit::Random)
+        } else {
+            Ok(DeferredInit::Initialized(Vec::<u8>::from_str(src)?))
+        }
+    }
+}
+
+impl Deref for DeferredValue {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        match &self.0 {
+            DeferredInit::Initialized(val) => val,
+            _ => panic!("Value has not been initialized"),
+        }
+    }
+}
+
+/// Wrapper type to force deserialization assuming octal encoding.
+#[derive(Deserialize, Debug)]
+pub struct OctEncoded<T: ParseInt>(#[serde(with = "self")] T);
+
+/// Wrapper type to force deserialization assuming decimal encoding.
+#[derive(Deserialize, Debug)]
+pub struct DecEncoded<T: ParseInt>(#[serde(with = "self")] T);
+
+/// Wrapper type to force deserialization assuming hexadecimal encoding.
+#[derive(Deserialize, Debug)]
+pub struct HexEncoded<T: ParseInt>(#[serde(with = "self")] T);
+
+macro_rules! impl_parse_int_enc {
+    ($ty:ident, $radix:expr) => {
+        impl<T: ParseInt> std::ops::Deref for $ty<T> {
+            type Target = T;
+
+            fn deref(&self) -> &Self::Target {
+                &self.0
+            }
+        }
+
+        impl<T: ParseInt> ParseInt for $ty<T> {
+            type FromStrRadixErr = T::FromStrRadixErr;
+
+            fn from_str_radix(src: &str, radix: u32) -> Result<Self, T::FromStrRadixErr> {
+                Ok(Self(T::from_str_radix(src, radix)?))
+            }
+
+            fn from_str(src: &str) -> Result<Self, ParseIntError> {
+                Self::from_str_radix(src, $radix).map_err(|e| e.into())
+            }
+        }
+    };
+}
+
+impl_parse_int_enc!(OctEncoded, 8);
+impl_parse_int_enc!(DecEncoded, 10);
+impl_parse_int_enc!(HexEncoded, 16);
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use serde::Deserialize;
+
+    #[test]
+    fn de_u8() -> Result<()> {
+        #[derive(Debug, Deserialize)]
+        struct TestData {
+            #[serde(with = "super")]
+            oct: OctEncoded<u8>,
+            #[serde(with = "super")]
+            dec: DecEncoded<u8>,
+            #[serde(with = "super")]
+            hex: HexEncoded<u8>,
+        }
+
+        let data: TestData = deser_hjson::from_str(stringify!(
+        {
+            oct: "77",
+            dec: "77",
+            hex: "77"
+        }))?;
+
+        assert_eq!(*data.oct, 63);
+        assert_eq!(*data.dec, 77);
+        assert_eq!(*data.hex, 119);
+        Ok(())
+    }
+}
diff --git a/sw/host/opentitanlib/src/otp/otp.rs b/sw/host/opentitanlib/src/otp/otp.rs
new file mode 100644
index 0000000..c11fea0
--- /dev/null
+++ b/sw/host/opentitanlib/src/otp/otp.rs
@@ -0,0 +1,44 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::util::parse_int::{ParseInt, ParseIntError};
+
+use anyhow::Result;
+
+impl ParseInt for Vec<u8> {
+    type FromStrRadixErr = ParseIntError;
+
+    fn from_str_radix(src: &str, radix: u32) -> Result<Self, ParseIntError> {
+        let mut bytes = vec![];
+        for digit_bytes in src.as_bytes().rchunks(2) {
+            let digits = std::str::from_utf8(digit_bytes).unwrap();
+            bytes.push(u8::from_str_radix(digits, radix)?);
+        }
+        Ok(bytes)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::convert::TryInto;
+    #[test]
+    fn byte_field_test() {
+        assert_eq!(Vec::from_str("0x1"), Ok(vec![0x1]));
+        assert_eq!(
+            Vec::from_str("0x4b4b4b4b4b4ba5a5"),
+            Ok(vec![0xa5, 0xa5, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b, 0x4b])
+        );
+        assert_eq!(
+            u64::from_ne_bytes(
+                Vec::from_str("0x4b4b4b4b4b4ba5a5")
+                    .unwrap()
+                    .try_into()
+                    .unwrap()
+            ),
+            u64::from_str("0x4b4b4b4b4b4ba5a5").unwrap()
+        );
+        assert!(Vec::from_str("-1").is_err());
+    }
+}
diff --git a/sw/host/opentitanlib/src/otp/otp_img.rs b/sw/host/opentitanlib/src/otp/otp_img.rs
new file mode 100644
index 0000000..fe05775
--- /dev/null
+++ b/sw/host/opentitanlib/src/otp/otp_img.rs
@@ -0,0 +1,60 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+use serde::Deserialize;
+
+use std::path::Path;
+
+use crate::otp::num_de::{DecEncoded, DeferredValue};
+use anyhow::Result;
+use rand::rngs::StdRng;
+use rand::SeedableRng;
+
+const OTP_IMG_SEED_DIVERSIFIER: u64 = 1941661965323525198146u128 as u64;
+
+#[derive(Deserialize, Debug)]
+pub struct OtpImgItem {
+    pub name: String,
+    pub value: DeferredValue,
+}
+
+#[derive(Deserialize, Debug)]
+pub struct OtpImgPartition {
+    pub name: String,
+    pub items: Option<Vec<OtpImgItem>>,
+}
+
+#[derive(Deserialize, Debug)]
+pub struct OtpImg {
+    pub seed: DecEncoded<u64>,
+    pub partitions: Vec<OtpImgPartition>,
+}
+
+impl OtpImgPartition {
+    pub fn get_item(&mut self, name: &str) -> Option<&mut OtpImgItem> {
+        self.items
+            .as_mut()
+            .and_then(|items| items.iter_mut().find(|i| i.name == name))
+    }
+}
+
+impl OtpImg {
+    pub fn new(in_file: &Path) -> Result<OtpImg> {
+        let json_text = std::fs::read_to_string(in_file)?;
+        let res: OtpImg = deser_hjson::from_str(&json_text)?;
+        Ok(res)
+    }
+
+    pub fn get_partition(&mut self, name: &str) -> Option<&mut OtpImgPartition> {
+        self.partitions.iter_mut().find(|p| p.name == name)
+    }
+
+    pub fn partition(&self) -> &[OtpImgPartition] {
+        &self.partitions
+    }
+
+    pub fn get_rng(&self) -> StdRng {
+        StdRng::seed_from_u64(OTP_IMG_SEED_DIVERSIFIER + *self.seed)
+    }
+}
diff --git a/sw/host/opentitanlib/src/otp/otp_mmap.rs b/sw/host/opentitanlib/src/otp/otp_mmap.rs
new file mode 100644
index 0000000..e6960c3
--- /dev/null
+++ b/sw/host/opentitanlib/src/otp/otp_mmap.rs
@@ -0,0 +1,207 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::otp::num_de::{self, DeferredValue};
+use crate::otp::otp_img::OtpImg;
+use crate::otp::vmem_serialize::*;
+
+use anyhow::{anyhow, bail, Result};
+use serde::Deserialize;
+use std::collections::HashMap;
+use std::convert::TryInto;
+use std::fs;
+use std::path::Path;
+
+#[derive(Deserialize, Debug)]
+struct OtpMapConfig {
+    #[serde(with = "num_de")]
+    width: usize,
+    #[serde(with = "num_de")]
+    depth: usize,
+}
+
+#[derive(Deserialize, Debug)]
+struct OtpMapKey {
+    name: String,
+    value: DeferredValue,
+}
+
+#[derive(Deserialize, Debug)]
+struct OtpMapDigest {
+    name: String,
+    iv_value: DeferredValue,
+    cnst_value: DeferredValue,
+}
+
+#[derive(Deserialize, Debug)]
+struct OtpMapScrambling {
+    #[serde(with = "num_de")]
+    key_size: usize,
+    #[serde(with = "num_de")]
+    iv_size: usize,
+    #[serde(with = "num_de")]
+    cnst_size: usize,
+    keys: Vec<OtpMapKey>,
+    digests: Vec<OtpMapDigest>,
+}
+
+#[derive(Deserialize, Debug)]
+struct OtpMapItem {
+    name: String,
+    #[serde(with = "num_de")]
+    size: usize,
+    #[serde(default)]
+    isdigest: bool,
+    inv_default: Option<DeferredValue>,
+}
+
+#[derive(Deserialize, Debug)]
+pub struct OtpMapPartition {
+    name: String,
+    secret: bool,
+    #[serde(default, with = "num_de")]
+    size: usize,
+    sw_digest: bool,
+    hw_digest: bool,
+    key_sel: String,
+    items: Vec<OtpMapItem>,
+}
+
+#[derive(Deserialize, Debug)]
+pub struct OtpMap {
+    seed: String,
+    otp: OtpMapConfig,
+    scrambling: OtpMapScrambling,
+    partitions: Vec<OtpMapPartition>,
+}
+
+impl OtpMap {
+    pub fn new(in_file: &Path) -> Result<OtpMap> {
+        let json_text = fs::read_to_string(in_file)?;
+        let res: OtpMap = deser_hjson::from_str(&json_text)?;
+        Ok(res)
+    }
+
+    pub fn generate_keys(&self, img: &OtpImg) -> HashMap<String, Vec<u8>> {
+        let mut rng = img.get_rng();
+        let mut map = HashMap::new();
+        for key in &self.scrambling.keys {
+            let value = key.value.resolve(self.scrambling.key_size, &mut rng);
+            map.insert(key.name.clone(), value);
+        }
+        map
+    }
+
+    pub fn make_vmem(&mut self, img: &mut OtpImg) -> Result<VmemImage> {
+        // Seeded RNG needed for "<random>" values.
+        let mut rng = img.get_rng();
+        let mut vmem_partitions = Vec::<VmemPartition>::new();
+        for partition in &self.partitions {
+            let key_name = match partition.key_sel.as_str() {
+                "NoKey" => None,
+                key => Some(key.to_owned()),
+            };
+
+            let digest_type = if !partition.sw_digest && !partition.hw_digest {
+                DigestType::Unlocked
+            } else if partition.sw_digest && !partition.hw_digest {
+                DigestType::Software
+            } else if !partition.sw_digest && partition.hw_digest {
+                // Extra information needed to compute HW digests.
+                let iv_size = self.scrambling.iv_size;
+                let cnst_size = self.scrambling.cnst_size;
+                let digest_info = self
+                    .scrambling
+                    .digests
+                    .iter_mut()
+                    .find(|v| v.name == "CnstyDigest")
+                    .ok_or(anyhow!("Couldn't find digest info"))?;
+
+                const IV_SIZE: usize = std::mem::size_of::<DigestIV>();
+                const CNST_SIZE: usize = std::mem::size_of::<DigestCnst>();
+                let iv_value: [u8; IV_SIZE] = digest_info
+                    .iv_value
+                    .resolve(iv_size, &mut rng)
+                    .try_into()
+                    .map_err(|_| anyhow!("Bad IV size {}", iv_size))?;
+                let cnst_value: [u8; CNST_SIZE] = digest_info
+                    .cnst_value
+                    .resolve(cnst_size, &mut rng)
+                    .try_into()
+                    .map_err(|_| anyhow!("Bad scrambling constant size {}", cnst_size))?;
+                DigestType::Hardware(
+                    DigestIV::from_ne_bytes(iv_value),
+                    DigestCnst::from_ne_bytes(cnst_value),
+                )
+            } else {
+                bail!("Invalid digest configuration");
+            };
+
+            let mut vmem_partition = VmemPartition::new(
+                partition.name.clone(),
+                partition.size,
+                digest_type,
+                key_name,
+            );
+
+            // Fetch the img definition for partition, this contains the associated values for
+            // paritition items.
+            let mut img_partition = img.get_partition(&partition.name);
+
+            let mut offset = 0usize;
+
+            // Resolve all values and convert to Vmem representation.
+            for item in &partition.items {
+                let img_item_value = match &mut img_partition {
+                    Some(v) => {
+                        let item_value = v.get_item(&item.name);
+                        match item_value {
+                            Some(v) => v.value.resolve(item.size, &mut rng),
+                            None => vec![0u8; item.size],
+                        }
+                    }
+                    None => vec![0u8; item.size],
+                };
+                let vmem_item = VmemItem::new(img_item_value, offset, item.name.clone());
+                offset += item.size;
+                vmem_partition.push_item(vmem_item);
+            }
+            if partition.size == 0 {
+                const SCRAMBLE_BLOCK_WIDTH: usize = 8;
+                const DIGEST_SIZE: usize = 8;
+                let mut size = SCRAMBLE_BLOCK_WIDTH
+                    * ((offset + SCRAMBLE_BLOCK_WIDTH - 1) / SCRAMBLE_BLOCK_WIDTH);
+                if partition.hw_digest || partition.sw_digest {
+                    size += DIGEST_SIZE;
+                }
+                vmem_partition.set_size(size);
+            }
+            vmem_partitions.push(vmem_partition);
+        }
+        Ok(VmemImage::new(
+            vmem_partitions,
+            self.otp.width,
+            self.otp.depth,
+        ))
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use std::fs::read_to_string;
+
+    #[test]
+    fn test_mmap_deserialize() {
+        let _: OtpMap =
+            deser_hjson::from_str(&read_to_string("tests/otp_ctrl_mmap.hjson").unwrap()).unwrap();
+    }
+
+    #[test]
+    fn test_img_deserialize() {
+        let _: OtpImg =
+            deser_hjson::from_str(&read_to_string("tests/otp_ctrl_img_dev.hjson").unwrap())
+                .unwrap();
+    }
+}
diff --git a/sw/host/opentitanlib/src/otp/vmem_serialize.rs b/sw/host/opentitanlib/src/otp/vmem_serialize.rs
new file mode 100644
index 0000000..077b46a
--- /dev/null
+++ b/sw/host/opentitanlib/src/otp/vmem_serialize.rs
@@ -0,0 +1,279 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+use crate::otp::lc_state::LcSecded;
+use crate::util::present::Present;
+
+use std::collections::HashMap;
+use std::convert::TryInto;
+use std::fmt::Write;
+
+use anyhow::{anyhow, bail, ensure, Result};
+
+use zerocopy::AsBytes;
+
+enum ItemType {
+    Bytes(Vec<u8>),
+    Unvalued(usize),
+}
+
+/// The hex representation of an OTP item.
+pub struct VmemItem {
+    value: ItemType,
+    offset: usize,
+    name: String,
+}
+
+impl VmemItem {
+    pub fn new(bytes: Vec<u8>, offset: usize, name: String) -> VmemItem {
+        VmemItem {
+            value: ItemType::Bytes(bytes),
+            offset,
+            name,
+        }
+    }
+
+    pub fn new_unvalued(size: usize, offset: usize, name: String) -> VmemItem {
+        VmemItem {
+            value: ItemType::Unvalued(size),
+            offset,
+            name,
+        }
+    }
+
+    pub fn size(&self) -> usize {
+        match &self.value {
+            ItemType::Bytes(b) => b.len(),
+            ItemType::Unvalued(size) => *size,
+        }
+    }
+}
+
+pub type DigestIV = u64;
+pub type DigestCnst = u128;
+
+/// Digest information for an OTP partition.
+#[derive(PartialEq)]
+pub enum DigestType {
+    Unlocked,
+    Software,
+    Hardware(DigestIV, DigestCnst),
+}
+
+/// The hex representation of an OTP partition.
+pub struct VmemPartition {
+    /// Items associated with this partition.
+    items: Vec<VmemItem>,
+    /// The name of this partition.
+    /// Used in annotations.
+    name: String,
+    /// The type of digest used for this partition.
+    /// For software digests, the value of the digest is provided and appended to the list of
+    /// items. For hardware digests, we must compute the digest value and append to the list of
+    /// items.
+    digest_type: DigestType,
+    /// Partition size.
+    size: usize,
+    /// The key name for this parition.
+    /// If specified, the serializer will attempt to scramble this parition using the key named in
+    /// this field.
+    key_name: Option<String>,
+}
+
+impl VmemPartition {
+    pub fn new(
+        name: String,
+        size: usize,
+        digest_type: DigestType,
+        key_name: Option<String>,
+    ) -> VmemPartition {
+        VmemPartition {
+            items: Vec::new(),
+            name,
+            digest_type,
+            size,
+            key_name,
+        }
+    }
+
+    /// Set the size of the partition.
+    ///
+    /// For partitions that don't specify their size, this is used to set the size of the partition
+    /// including the digest.
+    pub fn set_size(&mut self, size: usize) {
+        self.size = size;
+    }
+
+    /// Add an item to this partition.
+    pub fn push_item(&mut self, item: VmemItem) {
+        self.items.push(item);
+    }
+
+    /// Produces a tuple containing OTP HEX lines with annotations.
+    fn write_to_buffer(&self, keys: &HashMap<String, Vec<u8>>) -> Result<(Vec<u8>, Vec<String>)> {
+        if self.size % 8 != 0 {
+            bail!("Partition {} must be 64-bit alligned", self.name);
+        }
+
+        let mut defined = vec![false; self.size];
+        let mut annotations: Vec<String> = vec!["unallocated".to_owned(); self.size];
+
+        let mut data_bytes: Vec<u8> = vec![0; self.size];
+
+        for item in &self.items {
+            let end = item.offset + item.size();
+            annotations[item.offset..end].fill(format!("{}: {}", self.name, item.name).to_string());
+            let defined = &mut defined[item.offset..end];
+            if let Some(collision) = defined.iter().position(|defined| *defined) {
+                bail!(
+                    "Unexpected item collision with item {} at 0x{:x}",
+                    item.name,
+                    collision
+                );
+            }
+            defined.fill(true);
+            if let ItemType::Bytes(bytes) = &item.value {
+                data_bytes[item.offset..end].copy_from_slice(bytes);
+            }
+        }
+
+        let mut data_blocks = Vec::<u64>::new();
+        let mut data_blocks_defined = Vec::<bool>::new();
+        for (k, chunk) in data_bytes.chunks(8).enumerate() {
+            data_blocks.push(u64::from_le_bytes(chunk.try_into().unwrap()));
+            let byte_offset = k * 8;
+            data_blocks_defined.push(
+                defined[byte_offset..byte_offset + 8]
+                    .iter()
+                    .fold(false, |a, &b| a || b),
+            );
+        }
+
+        if let Some(key_name) = &self.key_name {
+            let key = keys
+                .get(key_name)
+                .ok_or_else(|| anyhow!("Key not found {}", key_name))?;
+
+            let cipher = Present::try_new(key.clone())?;
+
+            for i in 0..data_blocks.len() {
+                if data_blocks_defined[i] {
+                    data_blocks[i] = cipher.encrypt_block(data_blocks[i]);
+                }
+            }
+        }
+
+        if let DigestType::Hardware(iv, fin_const) = self.digest_type {
+            ensure!(
+                matches!(data_blocks.last(), None | Some(0)),
+                "Digest of partition {} cannot be overridden manually",
+                self.name
+            );
+            let last = data_blocks.len() - 1;
+            data_blocks[last] = present_digest_64(&data_blocks[0..last], iv, fin_const);
+        }
+
+        let data = data_blocks.as_bytes().to_vec();
+
+        if data.len() != self.size {
+            Err(anyhow!("Partition {} size mismatch", self.name))
+        } else {
+            Ok((data, annotations))
+        }
+    }
+}
+
+pub struct VmemImage {
+    partitions: Vec<VmemPartition>,
+    width: usize,
+    depth: usize,
+}
+
+impl VmemImage {
+    pub fn new(partitions: Vec<VmemPartition>, width: usize, depth: usize) -> VmemImage {
+        VmemImage {
+            partitions,
+            width,
+            depth,
+        }
+    }
+    pub fn generate(
+        &self,
+        keys: HashMap<String, Vec<u8>>,
+        secded: &LcSecded,
+    ) -> Result<Vec<String>> {
+        let mut data: Vec<u8> = vec![0; self.width * self.depth];
+        let mut annotations: Vec<String> = vec![Default::default(); data.len()];
+        let mut offset = 0;
+        for partition in &self.partitions {
+            let (part_data, part_annotation) = partition.write_to_buffer(&keys)?;
+            let end = offset + partition.size;
+            if end > data.len() {
+                bail!(
+                    "Partition {} out of bounds, ends at 0x{:x}",
+                    partition.name,
+                    end
+                );
+            }
+            data[offset..end].clone_from_slice(&part_data);
+            annotations[offset..end].clone_from_slice(&part_annotation);
+            offset += partition.size;
+        }
+
+        let width_ecc = self.width + secded.ecc_byte_len();
+        let num_words = data.len() / self.width;
+
+        let mut output = vec![format!(
+            "// OTP memory hexfile with {} x {}bit layout",
+            self.depth,
+            width_ecc * 8
+        )];
+
+        for i in 0..num_words {
+            let mut word = Vec::<u8>::new();
+            let mut word_annotation = Vec::<String>::new();
+            for j in 0..self.width {
+                let idx = i * self.width + j;
+                word.push(data[idx]);
+                if !word_annotation.contains(&annotations[idx]) {
+                    word_annotation.push(annotations[idx].clone());
+                }
+            }
+            let word_with_ecc = secded.ecc_encode(word)?;
+            let mut word_str = String::new();
+            for byte in word_with_ecc.iter().rev() {
+                write!(word_str, "{:02x}", byte)?;
+            }
+            output.push(format!(
+                "{} // {:06x}: {}",
+                word_str,
+                i * self.width,
+                word_annotation.join(", ")
+            ));
+        }
+
+        Ok(output)
+    }
+}
+
+fn present_digest_64(message: &[u64], iv: DigestIV, fin_const: DigestCnst) -> u64 {
+    let mut state = iv;
+    for i in (0..message.len() + 2).step_by(2) {
+        let b128: [u8; 16] = if i + 1 < message.len() {
+            (message[i] as u128) << 64 | message[i + 1] as u128
+        } else if i < message.len() {
+            (message[i] as u128) << 64 | message[i] as u128
+        } else {
+            fin_const
+        }
+        .as_bytes()
+        .try_into()
+        .unwrap();
+
+        let cipher = Present::new_128(&b128);
+        state ^= cipher.encrypt_block(state);
+    }
+
+    state
+}
diff --git a/sw/host/opentitanlib/src/util/mod.rs b/sw/host/opentitanlib/src/util/mod.rs
index 4d655de..1c3c7bf 100644
--- a/sw/host/opentitanlib/src/util/mod.rs
+++ b/sw/host/opentitanlib/src/util/mod.rs
@@ -7,8 +7,8 @@
 pub mod file;
 pub mod image;
 pub mod parse_int;
-pub mod usb;
 pub mod present;
+pub mod usb;
 pub mod voltage;
 
 /// The `collection` macro provides syntax for hash and set literals.
diff --git a/sw/host/opentitanlib/src/util/parse_int.rs b/sw/host/opentitanlib/src/util/parse_int.rs
index 1308013..6da3d48 100644
--- a/sw/host/opentitanlib/src/util/parse_int.rs
+++ b/sw/host/opentitanlib/src/util/parse_int.rs
@@ -73,6 +73,8 @@
 impl_parse_int!(u32);
 impl_parse_int!(i64);
 impl_parse_int!(u64);
+impl_parse_int!(i128);
+impl_parse_int!(u128);
 impl_parse_int!(isize);
 impl_parse_int!(usize);
 
diff --git a/sw/host/opentitanlib/tests/lc_ctrl_state.hjson b/sw/host/opentitanlib/tests/lc_ctrl_state.hjson
new file mode 100644
index 0000000..c463f41
--- /dev/null
+++ b/sw/host/opentitanlib/tests/lc_ctrl_state.hjson
@@ -0,0 +1,104 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Use the gen-lc-state-enc.py script to regenerate the SV package:
+//
+// $ ./util/design/gen-lc-state-enc.py
+
+{
+    // Seed to be used for generation of partition item default values.
+    // Can be overridden on the command line with the --seed switch.
+    seed: 10167336684108184581
+
+    // Secded used in prim_generic_otp.sv
+    // SECDED matrix used for ECC in OTP
+    secded : {
+        data_width : 16,
+        ecc_width  : 6,
+        ecc_matrix : [
+            // This is a standard extended Hamming code for 16bit
+            [0, 1, 3, 4, 6, 8, 10, 11, 13, 15], // ECC bit 0
+            [0, 2, 3, 5, 6, 9, 10, 12, 13],     // ECC bit 1
+            [1, 2, 3, 7, 8, 9, 10, 14, 15],     // ECC bit 2
+            [4, 5, 6, 7, 8, 9, 10],             // ECC bit 3
+            [11, 12, 13, 14, 15],               // ECC bit 4
+            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], // Parity bit
+        ]
+    }
+
+    // Additional Hamming weight and distance requirements.
+    // Note that the life cycle words are 16 + 6 = 22 bits wide total.
+    min_hw       : 5,
+    max_hw       : 17,
+    min_hd       : 5
+
+    // LC token size in bit
+    token_size   : 128
+    tokens       : [
+        {
+            name:  "AllZeroToken"
+            value: "0x0"
+        }
+        {
+            name:  "RndCnstRawUnlockToken"
+            value: "<random>"
+        }
+    ]
+
+    // Life cycle state definition.
+    // From least to most significant OTP word in ascending order.
+    lc_state : {
+        RAW            : [ '0',  '0',  '0',  '0', '0',  '0',  '0',   '0',  '0',  '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',],
+        TEST_UNLOCKED0 : ['B0', 'A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_LOCKED0   : ['B0', 'B1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_UNLOCKED1 : ['B0', 'B1', 'B2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_LOCKED1   : ['B0', 'B1', 'B2', 'B3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_UNLOCKED2 : ['B0', 'B1', 'B2', 'B3', 'B4', 'A5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_LOCKED2   : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'A6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_UNLOCKED3 : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'A7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_LOCKED3   : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'A8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_UNLOCKED4 : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'A9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_LOCKED4   : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'A10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_UNLOCKED5 : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'A11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_LOCKED5   : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'A12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_UNLOCKED6 : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'A13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_LOCKED6   : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'B13', 'A14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        TEST_UNLOCKED7 : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'B13', 'B14', 'A15', 'A16', 'A17', 'A18', 'A19',],
+        DEV            : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'B13', 'B14', 'B15', 'A16', 'A17', 'A18', 'A19',],
+        PROD           : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'B13', 'B14', 'A15', 'B16', 'A17', 'A18', 'A19',],
+        PROD_END       : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'B13', 'B14', 'A15', 'A16', 'B17', 'A18', 'A19',],
+        RMA            : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'A17', 'B18', 'B19',],
+        SCRAP          : ['B0', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'B10', 'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17', 'B18', 'B19',]
+    }
+
+    // Life cycle state transition counter definition.
+    // From least to most significant OTP word in ascending order.
+    lc_cnt : {
+        0   : [ '0',  '0',  '0',  '0', '0',  '0',  '0',   '0',  '0',  '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0',   '0'],
+        1   : ['D0', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        2   : ['D0', 'D1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        3   : ['D0', 'D1', 'D2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        4   : ['D0', 'D1', 'D2', 'D3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        5   : ['D0', 'D1', 'D2', 'D3', 'D4', 'C5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        6   : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'C6', 'C7', 'C8', 'C9', 'C10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        7   : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'C7', 'C8', 'C9', 'C10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        8   : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'C8', 'C9', 'C10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        9   : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'C9', 'C10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        10  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'C10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        11  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'C11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        12  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'C12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        13  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'C13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        14  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'C14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        15  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'C15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        16  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'C16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        17  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'C17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        18  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17', 'C18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        19  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17', 'D18', 'C19', 'C20', 'C21', 'C22', 'C23'],
+        20  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17', 'D18', 'D19', 'C20', 'C21', 'C22', 'C23'],
+        21  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17', 'D18', 'D19', 'D20', 'C21', 'C22', 'C23'],
+        22  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17', 'D18', 'D19', 'D20', 'D21', 'C22', 'C23'],
+        23  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17', 'D18', 'D19', 'D20', 'D21', 'D22', 'C23'],
+        24  : ['D0', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'D10', 'D11', 'D12', 'D13', 'D14', 'D15', 'D16', 'D17', 'D18', 'D19', 'D20', 'D21', 'D22', 'D23'],
+    }
+}
diff --git a/sw/host/opentitanlib/tests/otp_ctrl_img_dev.hjson b/sw/host/opentitanlib/tests/otp_ctrl_img_dev.hjson
new file mode 100644
index 0000000..82cc173
--- /dev/null
+++ b/sw/host/opentitanlib/tests/otp_ctrl_img_dev.hjson
@@ -0,0 +1,129 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Use the gen-otp-img.py script to convert this configuration into
+// a hex file for preloading the OTP in FPGA synthesis or simulation.
+//
+
+{
+    // Seed to be used for generation of partition randomized values.
+    // Can be overridden on the command line with the --seed switch.
+    seed: 01931961561863975174
+
+    // The partition and item names must correspond with the OTP memory map.
+    partitions: [
+        {
+            name:  "CREATOR_SW_CFG",
+            items: [
+                {
+                    name:  "CREATOR_SW_CFG_DIGEST",
+                    value: "0x0",
+                },
+                {
+                    name: "CREATOR_SW_CFG_USE_SW_RSA_VERIFY",
+                    // Use software mod_exp implementation for signature
+                    // verification. See the definition of `hardened_bool_t` in
+                    // sw/device/lib/base/hardened.h.
+                    value: "0x739",
+                },
+                {
+                    name: "CREATOR_SW_CFG_KEY_IS_VALID",
+                    // Mark the first two keys as valid and remaining as
+                    // invalid since we have currently only two keys. See the
+                    // definition of `hardened_byte_bool_t` in
+                    // sw/device/lib/base/hardened.h.
+                    value: "0x4b4b4b4b4b4ba5a5",
+                }
+            ],
+        }
+        {
+            name:  "OWNER_SW_CFG",
+            items: [
+                {
+                    name:  "OWNER_SW_CFG_DIGEST",
+                    value: "0x0",
+                }
+            ],
+        }
+        {
+            name:  "HW_CFG",
+            // If set to true, this computes the HW digest value
+            // and locks the partition.
+            lock:  "True",
+            items: [
+                {
+                    name:  "DEVICE_ID",
+                    value: "<random>",
+                },
+                {
+                    name:  "EN_CSRNG_SW_APP_READ",
+                    value: "0x0",
+                },
+                {
+                    name:  "EN_ENTROPY_SRC_FW_READ",
+                    value: "0x1",
+                },
+            ],
+        }
+        {
+            name:  "SECRET0",
+            lock:  "True",
+            items: [
+                {
+                    name:  "TEST_UNLOCK_TOKEN",
+                    value: "<random>",
+                }
+                {
+                    name:  "TEST_EXIT_TOKEN",
+                    value: "<random>",
+                }
+            ],
+        }
+        {
+            name:  "SECRET1",
+            lock:  "True",
+            items: [
+                {
+                    name:  "FLASH_ADDR_KEY_SEED",
+                    value: "<random>",
+                }
+                {
+                    name:  "FLASH_DATA_KEY_SEED",
+                    value: "<random>",
+                }
+                {
+                    name:  "SRAM_DATA_KEY_SEED",
+                    value: "<random>",
+                }
+            ],
+        }
+        {
+            name:  "SECRET2",
+            lock:  "False",
+            items: [
+                {
+                    name:  "RMA_TOKEN",
+                    value: "<random>",
+                }
+                {
+                    name:  "CREATOR_ROOT_KEY_SHARE0",
+                    value: "<random>",
+                }
+                {
+                    name:  "CREATOR_ROOT_KEY_SHARE1",
+                    value: "<random>",
+                }
+            ],
+        }
+        {
+            name:  "LIFE_CYCLE",
+            // Can be one of the following strings:
+            // RAW, TEST_UNLOCKED0-3, TEST_LOCKED0-2, DEV, PROD, PROD_END, RMA, SCRAP
+            state: "DEV",
+            // Can range from 0 to 16.
+            // Note that a value of 0 is only permissible in RAW state.
+            count: "5"
+        }
+    ]
+}
diff --git a/sw/host/opentitanlib/tests/otp_ctrl_mmap.hjson b/sw/host/opentitanlib/tests/otp_ctrl_mmap.hjson
new file mode 100644
index 0000000..fe14f1d
--- /dev/null
+++ b/sw/host/opentitanlib/tests/otp_ctrl_mmap.hjson
@@ -0,0 +1,414 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// Use the gen-otp-mmap.py script to update dependent files (like documentation
+// tables the comportable hjson and metadata SV package):
+//
+// $ ./util/design/gen-otp-mmap.py
+//
+// Make sure to regenerate the CSRs after converting the memory map:
+//
+// $ cd ${PROJ_ROOT}
+// $ make -C hw regs
+//
+
+{
+    // Seed to be used for generation of partition item default values.
+    // Can be overridden on the command line with the --seed switch.
+    seed: "10556718629619452145"
+
+    otp: {
+        width: "2", // bytes
+        depth: "1024"
+    }
+
+    // Definition of scrambling and digest constants and keys.
+    scrambling: {
+        key_size:  "16",
+        iv_size:   "8",
+        cnst_size: "16",
+        keys: [
+            {
+                name:  "Secret0Key",
+                value: "<random>",
+            }
+            {
+                name:  "Secret1Key",
+                value: "<random>",
+            }
+            {
+                name:  "Secret2Key",
+                value: "<random>",
+            }
+        ]
+        digests: [
+            // This is the consistency digest used by all partitions.
+            {
+                name:       "CnstyDigest",
+                iv_value:   "<random>",
+                cnst_value: "<random>",
+            }
+            // The other digest configurations below are used for
+            // key derivation and token hashing.
+            {
+                name:       "FlashDataKey",
+                iv_value:   "<random>",
+                cnst_value: "<random>",
+            }
+            {
+                name:       "FlashAddrKey",
+                iv_value:   "<random>",
+                cnst_value: "<random>",
+            }
+            {
+                name:       "SramDataKey",
+                iv_value:   "<random>",
+                cnst_value: "<random>",
+            }
+        ]
+    }
+
+    // The enumeration order below defines the address map of the OTP controller,
+    // if the offsets are not defined explicitly via the "offset" key.
+    // Note that the digest items are added automatically to the address map.
+    partitions: [
+        {
+            name:       "VENDOR_TEST",
+            variant:    "Unbuffered",
+            size:       "64", // in bytes
+            secret:     false,
+            sw_digest:  true,
+            hw_digest:  false,
+            write_lock: "Digest",
+            read_lock:  "CSR",
+            key_sel:    "NoKey",
+            ecc_fatal:  false, // Do not send out a fatal alert upon detection of uncorrectable ECC errors.
+            bkout_type: false, // Do not generate a breakout type for this partition.
+            items: [
+                {
+                    name: "SCRATCH",
+                    size: "56"
+                }
+            ],
+            desc: '''Vendor test partition for OTP smoke checks
+            during manufacturing. The OTP wrapper control logic inside prim_otp is allowed
+            to read/write to this region. ECC uncorrectable errors seen on the functional
+            prim_otp interface will not lead to an alert for this partition.
+            '''
+        }
+        {
+            name:       "CREATOR_SW_CFG",
+            variant:    "Unbuffered",
+            absorb:     true,
+            size:       "768", // in bytes
+            secret:     false,
+            sw_digest:  true,
+            hw_digest:  false,
+            write_lock: "Digest",
+            read_lock:  "CSR",
+            key_sel:    "NoKey",
+            ecc_fatal:  true, // Uncorrectable ECC errors trigger a fatal alert.
+            bkout_type: false, // Do not generate a breakout type for this partition.
+            items: [
+                {
+                    name: "CREATOR_SW_CFG_AST_CFG",
+                    size: "128"
+                }
+                {
+                    name: "CREATOR_SW_CFG_AST_INIT_EN",
+                    size: "4"
+                }
+                {
+                    name: "CREATOR_SW_CFG_ROM_EXT_SKU",
+                    size: "4"
+                }
+                {
+                    name: "CREATOR_SW_CFG_USE_SW_RSA_VERIFY",
+                    size: "4"
+                }
+                {
+                    name: "CREATOR_SW_CFG_KEY_IS_VALID",
+                    size: "8"
+                }
+                {
+                    name: "CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG",
+                    size: "4"
+                }
+                {
+                    name: "CREATOR_SW_CFG_FLASH_INFO_BOOT_DATA_CFG",
+                    size: "4"
+                }
+                {
+                    name: "CREATOR_SW_CFG_RNG_EN",
+                    size: "4"
+                }
+            ],
+            desc: '''Software configuration partition for
+            device-specific calibration data (Clock, LDO,
+            RNG, device identity).
+            '''
+        }
+        {
+            name:       "OWNER_SW_CFG",
+            variant:    "Unbuffered",
+            absorb:     true,
+            size:       "768", // in bytes
+            secret:     false,
+            sw_digest:  true,
+            hw_digest:  false,
+            write_lock: "Digest",
+            read_lock:  "CSR",
+            key_sel:    "NoKey",
+            ecc_fatal:  true,
+            bkout_type: false,
+            items: [
+                {
+                    name: "ROM_ERROR_REPORTING",
+                    size: "4"
+                }
+                {
+                    name: "ROM_BOOTSTRAP_EN",
+                    size: "4"
+                }
+                {
+                    name: "ROM_FAULT_RESPONSE",
+                    size: "4"
+                }
+                {
+                    name: "ROM_ALERT_CLASS_EN",
+                    size: "4"
+                }
+                {
+                    name: "ROM_ALERT_ESCALATION",
+                    size: "4"
+                }
+                {
+                    name: "ROM_ALERT_CLASSIFICATION",
+                    size: "320"
+                }
+                {
+                    name: "ROM_LOCAL_ALERT_CLASSIFICATION",
+                    size: "64"
+                }
+                {
+                    name: "ROM_ALERT_ACCUM_THRESH",
+                    size: "16"
+                }
+                {
+                    name: "ROM_ALERT_TIMEOUT_CYCLES",
+                    size: "16"
+                }
+                {
+                    name: "ROM_ALERT_PHASE_CYCLES",
+                    size: "64"
+                }
+            ],
+            desc: '''Software configuration partition for
+            data that changes software behavior, specifically
+            in the ROM. E.g., enabling defensive features in
+            ROM or selecting failure modes if verification fails.
+            '''
+        }
+        {
+            name:       "HW_CFG",
+            variant:    "Buffered",
+            secret:     false,
+            sw_digest:  false,
+            hw_digest:  true,
+            write_lock: "Digest",
+            read_lock:  "None",
+            key_sel:    "NoKey",
+            ecc_fatal:  true,
+            bkout_type: true,
+            items: [
+                {
+                    name:        "DEVICE_ID",
+                    size:        "32",
+                    // Default value to be output in case partition has not
+                    // initialized or is in error state. If not specified,
+                    // a value of '0 will be used.
+                    inv_default: "<random>",
+                },
+                {
+                    name:        "MANUF_STATE",
+                    size:        "32",
+                    inv_default: "<random>",
+                },
+                {
+                    name:        "EN_SRAM_IFETCH",
+                    size:        "1",
+                    ismubi:      true,
+                    inv_default: false
+                },
+                {
+                    name:        "EN_CSRNG_SW_APP_READ",
+                    size:        "1",
+                    ismubi:      true,
+                    inv_default: false
+                },
+                {
+                    name:        "EN_ENTROPY_SRC_FW_READ",
+                    size:        "1",
+                    ismubi:      true,
+                    inv_default: false
+                },
+                {
+                    name:        "EN_ENTROPY_SRC_FW_OVER",
+                    size:        "1",
+                    ismubi:      true,
+                    inv_default: false
+                },
+            ],
+            desc: '''
+            EN_SRAM_IFETCH: Enable / disable execute from SRAM CSR switch.
+            EN_CSRNG_SW_APP_READ: This input efuse is used to enable access
+            to the NIST internal state per instance.
+            EN_ENTROPY_SRC_FW_READ: This input efuse is used to enable access
+            to the ENTROPY_DATA register directly.
+            EN_ENTROPY_SRC_FW_OVER: This input efuse is used to enable access
+            to the firmware override FIFO and other related functions.
+            '''
+        }
+        {
+            name:       "SECRET0",
+            variant:    "Buffered",
+            secret:     true,
+            sw_digest:  false,
+            hw_digest:  true,
+            write_lock: "Digest",
+            read_lock:  "Digest",
+            key_sel:    "Secret0Key",
+            ecc_fatal:  true,
+            bkout_type: false,
+            items: [
+                {
+                    name: "TEST_UNLOCK_TOKEN",
+                    // This will generate a random default to be output in
+                    // case partition has not initialized or is in error state.
+                    // If not specified, a value of '0 will be used.
+                    inv_default: "<random>",
+                    size: "16"
+                }
+                {
+                    name: "TEST_EXIT_TOKEN",
+                    inv_default: "<random>",
+                    size: "16"
+                }
+            ],
+            desc: '''Test unlock tokens.
+            '''
+        }
+        {
+            name:       "SECRET1",
+            variant:    "Buffered",
+            secret:     true,
+            sw_digest:  false,
+            hw_digest:  true,
+            write_lock: "Digest",
+            read_lock:  "Digest",
+            key_sel:    "Secret1Key",
+            ecc_fatal:  true,
+            bkout_type: false,
+            items: [
+                {
+                    name: "FLASH_ADDR_KEY_SEED",
+                    inv_default: "<random>",
+                    size: "32"
+                }
+                {
+                    name: "FLASH_DATA_KEY_SEED",
+                    inv_default: "<random>",
+                    size: "32"
+                }
+                {
+                    name: "SRAM_DATA_KEY_SEED",
+                    inv_default: "<random>",
+                    size: "16"
+                }
+            ],
+            desc: '''SRAM and FLASH scrambling key roots
+            used for scrambling key derivation.
+            '''
+        }
+        {
+            name:       "SECRET2",
+            variant:    "Buffered",
+            secret:     true,
+            sw_digest:  false,
+            hw_digest:  true,
+            write_lock: "Digest",
+            read_lock:  "Digest",
+            key_sel:    "Secret2Key",
+            ecc_fatal:  true,
+            bkout_type: false,
+            items: [
+                {
+                    name: "RMA_TOKEN",
+                    inv_default: "<random>",
+                    size: "16"
+                }
+                {
+                    name: "CREATOR_ROOT_KEY_SHARE0",
+                    inv_default: "<random>",
+                    size: "32"
+                }
+                {
+                    name: "CREATOR_ROOT_KEY_SHARE1",
+                    inv_default: "<random>",
+                    size: "32"
+                }
+            ],
+            desc: '''RMA unlock token and creator root key.
+            '''
+        }
+        {
+            name:       "LIFE_CYCLE",
+            variant:    "LifeCycle",
+            secret:     false,
+            sw_digest:  false,
+            hw_digest:  false,
+            write_lock: "None",
+            read_lock:  "None",
+            key_sel:    "NoKey",
+            ecc_fatal:  true,
+            bkout_type: false,
+            items: [
+                // The life cycle transition count is specified
+                // first such that any programming attempt of the life cycle
+                // partition through the LCI will always write the transition
+                // counter words first when programming an updated state vector.
+                // This is an additional safeguard, to the sequencing in the
+                // life cycle controller to ensure that the counter is always written
+                // before any state update. I.e., the life cycle controller
+                // already splits the counter and state updates into two
+                // supsequent requests through the LCI, where the first request
+                // only contains the updated transition counter, and the second
+                // request the updated transition counter and state.
+                {
+                    name: "LC_TRANSITION_CNT",
+                    inv_default: "<random>",
+                    size: "48"
+                }
+                {
+                    name: "LC_STATE",
+                    inv_default: "<random>",
+                    size: "40"
+                }
+            ],
+            desc: '''Life-cycle related bits. This
+            partition cannot be locked as the life cycle
+            state needs to be able to advance to RMA in-field.
+            Note that while this partition is not marked secret
+            (i.e. it is not scrambled) it is not readable
+            nor writeable via the DAI. Only the LC controller
+            can access this partition, and even via the LC
+            controller it is not possible to read the
+            raw manufacturing life cycle state in encoded form,
+            since that encoding is considered a netlist secret.
+            The LC controller only exposes a decoded version of
+            this state.
+            '''
+        }
+    ]
+}
diff --git a/sw/host/opentitanlib/tests/output.vmem b/sw/host/opentitanlib/tests/output.vmem
new file mode 100644
index 0000000..0a5bf0b
--- /dev/null
+++ b/sw/host/opentitanlib/tests/output.vmem
@@ -0,0 +1,1025 @@
+// OTP memory hexfile with 1024 x 24bit layout
+000000 // 000000: VENDOR_TEST: SCRATCH
+000000 // 000002: VENDOR_TEST: SCRATCH
+000000 // 000004: VENDOR_TEST: SCRATCH
+000000 // 000006: VENDOR_TEST: SCRATCH
+000000 // 000008: VENDOR_TEST: SCRATCH
+000000 // 00000a: VENDOR_TEST: SCRATCH
+000000 // 00000c: VENDOR_TEST: SCRATCH
+000000 // 00000e: VENDOR_TEST: SCRATCH
+000000 // 000010: VENDOR_TEST: SCRATCH
+000000 // 000012: VENDOR_TEST: SCRATCH
+000000 // 000014: VENDOR_TEST: SCRATCH
+000000 // 000016: VENDOR_TEST: SCRATCH
+000000 // 000018: VENDOR_TEST: SCRATCH
+000000 // 00001a: VENDOR_TEST: SCRATCH
+000000 // 00001c: VENDOR_TEST: SCRATCH
+000000 // 00001e: VENDOR_TEST: SCRATCH
+000000 // 000020: VENDOR_TEST: SCRATCH
+000000 // 000022: VENDOR_TEST: SCRATCH
+000000 // 000024: VENDOR_TEST: SCRATCH
+000000 // 000026: VENDOR_TEST: SCRATCH
+000000 // 000028: VENDOR_TEST: SCRATCH
+000000 // 00002a: VENDOR_TEST: SCRATCH
+000000 // 00002c: VENDOR_TEST: SCRATCH
+000000 // 00002e: VENDOR_TEST: SCRATCH
+000000 // 000030: VENDOR_TEST: SCRATCH
+000000 // 000032: VENDOR_TEST: SCRATCH
+000000 // 000034: VENDOR_TEST: SCRATCH
+000000 // 000036: VENDOR_TEST: SCRATCH
+000000 // 000038: unallocated
+000000 // 00003a: unallocated
+000000 // 00003c: unallocated
+000000 // 00003e: unallocated
+000000 // 000040: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000042: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000044: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000046: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000048: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00004a: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00004c: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00004e: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000050: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000052: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000054: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000056: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000058: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00005a: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00005c: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00005e: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000060: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000062: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000064: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000066: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000068: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00006a: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00006c: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00006e: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000070: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000072: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000074: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000076: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000078: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00007a: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00007c: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00007e: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000080: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000082: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000084: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000086: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000088: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00008a: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00008c: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00008e: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000090: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000092: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000094: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000096: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 000098: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00009a: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00009c: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 00009e: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000a0: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000a2: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000a4: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000a6: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000a8: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000aa: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000ac: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000ae: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000b0: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000b2: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000b4: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000b6: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000b8: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000ba: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000bc: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000be: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_CFG
+000000 // 0000c0: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_INIT_EN
+000000 // 0000c2: CREATOR_SW_CFG: CREATOR_SW_CFG_AST_INIT_EN
+000000 // 0000c4: CREATOR_SW_CFG: CREATOR_SW_CFG_ROM_EXT_SKU
+000000 // 0000c6: CREATOR_SW_CFG: CREATOR_SW_CFG_ROM_EXT_SKU
+0b0739 // 0000c8: CREATOR_SW_CFG: CREATOR_SW_CFG_USE_SW_RSA_VERIFY
+000000 // 0000ca: CREATOR_SW_CFG: CREATOR_SW_CFG_USE_SW_RSA_VERIFY
+27a5a5 // 0000cc: CREATOR_SW_CFG: CREATOR_SW_CFG_KEY_IS_VALID
+0c4b4b // 0000ce: CREATOR_SW_CFG: CREATOR_SW_CFG_KEY_IS_VALID
+0c4b4b // 0000d0: CREATOR_SW_CFG: CREATOR_SW_CFG_KEY_IS_VALID
+0c4b4b // 0000d2: CREATOR_SW_CFG: CREATOR_SW_CFG_KEY_IS_VALID
+000000 // 0000d4: CREATOR_SW_CFG: CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG
+000000 // 0000d6: CREATOR_SW_CFG: CREATOR_SW_CFG_FLASH_DATA_DEFAULT_CFG
+000000 // 0000d8: CREATOR_SW_CFG: CREATOR_SW_CFG_FLASH_INFO_BOOT_DATA_CFG
+000000 // 0000da: CREATOR_SW_CFG: CREATOR_SW_CFG_FLASH_INFO_BOOT_DATA_CFG
+000000 // 0000dc: CREATOR_SW_CFG: CREATOR_SW_CFG_RNG_EN
+000000 // 0000de: CREATOR_SW_CFG: CREATOR_SW_CFG_RNG_EN
+000000 // 0000e0: unallocated
+000000 // 0000e2: unallocated
+000000 // 0000e4: unallocated
+000000 // 0000e6: unallocated
+000000 // 0000e8: unallocated
+000000 // 0000ea: unallocated
+000000 // 0000ec: unallocated
+000000 // 0000ee: unallocated
+000000 // 0000f0: unallocated
+000000 // 0000f2: unallocated
+000000 // 0000f4: unallocated
+000000 // 0000f6: unallocated
+000000 // 0000f8: unallocated
+000000 // 0000fa: unallocated
+000000 // 0000fc: unallocated
+000000 // 0000fe: unallocated
+000000 // 000100: unallocated
+000000 // 000102: unallocated
+000000 // 000104: unallocated
+000000 // 000106: unallocated
+000000 // 000108: unallocated
+000000 // 00010a: unallocated
+000000 // 00010c: unallocated
+000000 // 00010e: unallocated
+000000 // 000110: unallocated
+000000 // 000112: unallocated
+000000 // 000114: unallocated
+000000 // 000116: unallocated
+000000 // 000118: unallocated
+000000 // 00011a: unallocated
+000000 // 00011c: unallocated
+000000 // 00011e: unallocated
+000000 // 000120: unallocated
+000000 // 000122: unallocated
+000000 // 000124: unallocated
+000000 // 000126: unallocated
+000000 // 000128: unallocated
+000000 // 00012a: unallocated
+000000 // 00012c: unallocated
+000000 // 00012e: unallocated
+000000 // 000130: unallocated
+000000 // 000132: unallocated
+000000 // 000134: unallocated
+000000 // 000136: unallocated
+000000 // 000138: unallocated
+000000 // 00013a: unallocated
+000000 // 00013c: unallocated
+000000 // 00013e: unallocated
+000000 // 000140: unallocated
+000000 // 000142: unallocated
+000000 // 000144: unallocated
+000000 // 000146: unallocated
+000000 // 000148: unallocated
+000000 // 00014a: unallocated
+000000 // 00014c: unallocated
+000000 // 00014e: unallocated
+000000 // 000150: unallocated
+000000 // 000152: unallocated
+000000 // 000154: unallocated
+000000 // 000156: unallocated
+000000 // 000158: unallocated
+000000 // 00015a: unallocated
+000000 // 00015c: unallocated
+000000 // 00015e: unallocated
+000000 // 000160: unallocated
+000000 // 000162: unallocated
+000000 // 000164: unallocated
+000000 // 000166: unallocated
+000000 // 000168: unallocated
+000000 // 00016a: unallocated
+000000 // 00016c: unallocated
+000000 // 00016e: unallocated
+000000 // 000170: unallocated
+000000 // 000172: unallocated
+000000 // 000174: unallocated
+000000 // 000176: unallocated
+000000 // 000178: unallocated
+000000 // 00017a: unallocated
+000000 // 00017c: unallocated
+000000 // 00017e: unallocated
+000000 // 000180: unallocated
+000000 // 000182: unallocated
+000000 // 000184: unallocated
+000000 // 000186: unallocated
+000000 // 000188: unallocated
+000000 // 00018a: unallocated
+000000 // 00018c: unallocated
+000000 // 00018e: unallocated
+000000 // 000190: unallocated
+000000 // 000192: unallocated
+000000 // 000194: unallocated
+000000 // 000196: unallocated
+000000 // 000198: unallocated
+000000 // 00019a: unallocated
+000000 // 00019c: unallocated
+000000 // 00019e: unallocated
+000000 // 0001a0: unallocated
+000000 // 0001a2: unallocated
+000000 // 0001a4: unallocated
+000000 // 0001a6: unallocated
+000000 // 0001a8: unallocated
+000000 // 0001aa: unallocated
+000000 // 0001ac: unallocated
+000000 // 0001ae: unallocated
+000000 // 0001b0: unallocated
+000000 // 0001b2: unallocated
+000000 // 0001b4: unallocated
+000000 // 0001b6: unallocated
+000000 // 0001b8: unallocated
+000000 // 0001ba: unallocated
+000000 // 0001bc: unallocated
+000000 // 0001be: unallocated
+000000 // 0001c0: unallocated
+000000 // 0001c2: unallocated
+000000 // 0001c4: unallocated
+000000 // 0001c6: unallocated
+000000 // 0001c8: unallocated
+000000 // 0001ca: unallocated
+000000 // 0001cc: unallocated
+000000 // 0001ce: unallocated
+000000 // 0001d0: unallocated
+000000 // 0001d2: unallocated
+000000 // 0001d4: unallocated
+000000 // 0001d6: unallocated
+000000 // 0001d8: unallocated
+000000 // 0001da: unallocated
+000000 // 0001dc: unallocated
+000000 // 0001de: unallocated
+000000 // 0001e0: unallocated
+000000 // 0001e2: unallocated
+000000 // 0001e4: unallocated
+000000 // 0001e6: unallocated
+000000 // 0001e8: unallocated
+000000 // 0001ea: unallocated
+000000 // 0001ec: unallocated
+000000 // 0001ee: unallocated
+000000 // 0001f0: unallocated
+000000 // 0001f2: unallocated
+000000 // 0001f4: unallocated
+000000 // 0001f6: unallocated
+000000 // 0001f8: unallocated
+000000 // 0001fa: unallocated
+000000 // 0001fc: unallocated
+000000 // 0001fe: unallocated
+000000 // 000200: unallocated
+000000 // 000202: unallocated
+000000 // 000204: unallocated
+000000 // 000206: unallocated
+000000 // 000208: unallocated
+000000 // 00020a: unallocated
+000000 // 00020c: unallocated
+000000 // 00020e: unallocated
+000000 // 000210: unallocated
+000000 // 000212: unallocated
+000000 // 000214: unallocated
+000000 // 000216: unallocated
+000000 // 000218: unallocated
+000000 // 00021a: unallocated
+000000 // 00021c: unallocated
+000000 // 00021e: unallocated
+000000 // 000220: unallocated
+000000 // 000222: unallocated
+000000 // 000224: unallocated
+000000 // 000226: unallocated
+000000 // 000228: unallocated
+000000 // 00022a: unallocated
+000000 // 00022c: unallocated
+000000 // 00022e: unallocated
+000000 // 000230: unallocated
+000000 // 000232: unallocated
+000000 // 000234: unallocated
+000000 // 000236: unallocated
+000000 // 000238: unallocated
+000000 // 00023a: unallocated
+000000 // 00023c: unallocated
+000000 // 00023e: unallocated
+000000 // 000240: unallocated
+000000 // 000242: unallocated
+000000 // 000244: unallocated
+000000 // 000246: unallocated
+000000 // 000248: unallocated
+000000 // 00024a: unallocated
+000000 // 00024c: unallocated
+000000 // 00024e: unallocated
+000000 // 000250: unallocated
+000000 // 000252: unallocated
+000000 // 000254: unallocated
+000000 // 000256: unallocated
+000000 // 000258: unallocated
+000000 // 00025a: unallocated
+000000 // 00025c: unallocated
+000000 // 00025e: unallocated
+000000 // 000260: unallocated
+000000 // 000262: unallocated
+000000 // 000264: unallocated
+000000 // 000266: unallocated
+000000 // 000268: unallocated
+000000 // 00026a: unallocated
+000000 // 00026c: unallocated
+000000 // 00026e: unallocated
+000000 // 000270: unallocated
+000000 // 000272: unallocated
+000000 // 000274: unallocated
+000000 // 000276: unallocated
+000000 // 000278: unallocated
+000000 // 00027a: unallocated
+000000 // 00027c: unallocated
+000000 // 00027e: unallocated
+000000 // 000280: unallocated
+000000 // 000282: unallocated
+000000 // 000284: unallocated
+000000 // 000286: unallocated
+000000 // 000288: unallocated
+000000 // 00028a: unallocated
+000000 // 00028c: unallocated
+000000 // 00028e: unallocated
+000000 // 000290: unallocated
+000000 // 000292: unallocated
+000000 // 000294: unallocated
+000000 // 000296: unallocated
+000000 // 000298: unallocated
+000000 // 00029a: unallocated
+000000 // 00029c: unallocated
+000000 // 00029e: unallocated
+000000 // 0002a0: unallocated
+000000 // 0002a2: unallocated
+000000 // 0002a4: unallocated
+000000 // 0002a6: unallocated
+000000 // 0002a8: unallocated
+000000 // 0002aa: unallocated
+000000 // 0002ac: unallocated
+000000 // 0002ae: unallocated
+000000 // 0002b0: unallocated
+000000 // 0002b2: unallocated
+000000 // 0002b4: unallocated
+000000 // 0002b6: unallocated
+000000 // 0002b8: unallocated
+000000 // 0002ba: unallocated
+000000 // 0002bc: unallocated
+000000 // 0002be: unallocated
+000000 // 0002c0: unallocated
+000000 // 0002c2: unallocated
+000000 // 0002c4: unallocated
+000000 // 0002c6: unallocated
+000000 // 0002c8: unallocated
+000000 // 0002ca: unallocated
+000000 // 0002cc: unallocated
+000000 // 0002ce: unallocated
+000000 // 0002d0: unallocated
+000000 // 0002d2: unallocated
+000000 // 0002d4: unallocated
+000000 // 0002d6: unallocated
+000000 // 0002d8: unallocated
+000000 // 0002da: unallocated
+000000 // 0002dc: unallocated
+000000 // 0002de: unallocated
+000000 // 0002e0: unallocated
+000000 // 0002e2: unallocated
+000000 // 0002e4: unallocated
+000000 // 0002e6: unallocated
+000000 // 0002e8: unallocated
+000000 // 0002ea: unallocated
+000000 // 0002ec: unallocated
+000000 // 0002ee: unallocated
+000000 // 0002f0: unallocated
+000000 // 0002f2: unallocated
+000000 // 0002f4: unallocated
+000000 // 0002f6: unallocated
+000000 // 0002f8: unallocated
+000000 // 0002fa: unallocated
+000000 // 0002fc: unallocated
+000000 // 0002fe: unallocated
+000000 // 000300: unallocated
+000000 // 000302: unallocated
+000000 // 000304: unallocated
+000000 // 000306: unallocated
+000000 // 000308: unallocated
+000000 // 00030a: unallocated
+000000 // 00030c: unallocated
+000000 // 00030e: unallocated
+000000 // 000310: unallocated
+000000 // 000312: unallocated
+000000 // 000314: unallocated
+000000 // 000316: unallocated
+000000 // 000318: unallocated
+000000 // 00031a: unallocated
+000000 // 00031c: unallocated
+000000 // 00031e: unallocated
+000000 // 000320: unallocated
+000000 // 000322: unallocated
+000000 // 000324: unallocated
+000000 // 000326: unallocated
+000000 // 000328: unallocated
+000000 // 00032a: unallocated
+000000 // 00032c: unallocated
+000000 // 00032e: unallocated
+000000 // 000330: unallocated
+000000 // 000332: unallocated
+000000 // 000334: unallocated
+000000 // 000336: unallocated
+000000 // 000338: unallocated
+000000 // 00033a: unallocated
+000000 // 00033c: unallocated
+000000 // 00033e: unallocated
+000000 // 000340: OWNER_SW_CFG: ROM_ERROR_REPORTING
+000000 // 000342: OWNER_SW_CFG: ROM_ERROR_REPORTING
+000000 // 000344: OWNER_SW_CFG: ROM_BOOTSTRAP_EN
+000000 // 000346: OWNER_SW_CFG: ROM_BOOTSTRAP_EN
+000000 // 000348: OWNER_SW_CFG: ROM_FAULT_RESPONSE
+000000 // 00034a: OWNER_SW_CFG: ROM_FAULT_RESPONSE
+000000 // 00034c: OWNER_SW_CFG: ROM_ALERT_CLASS_EN
+000000 // 00034e: OWNER_SW_CFG: ROM_ALERT_CLASS_EN
+000000 // 000350: OWNER_SW_CFG: ROM_ALERT_ESCALATION
+000000 // 000352: OWNER_SW_CFG: ROM_ALERT_ESCALATION
+000000 // 000354: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000356: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000358: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00035a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00035c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00035e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000360: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000362: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000364: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000366: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000368: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00036a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00036c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00036e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000370: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000372: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000374: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000376: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000378: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00037a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00037c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00037e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000380: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000382: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000384: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000386: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000388: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00038a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00038c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00038e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000390: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000392: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000394: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000396: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000398: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00039a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00039c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00039e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003a0: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003a2: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003a4: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003a6: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003a8: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003aa: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003ac: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003ae: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003b0: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003b2: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003b4: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003b6: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003b8: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003ba: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003bc: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003be: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003c0: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003c2: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003c4: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003c6: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003c8: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003ca: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003cc: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003ce: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003d0: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003d2: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003d4: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003d6: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003d8: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003da: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003dc: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003de: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003e0: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003e2: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003e4: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003e6: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003e8: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003ea: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003ec: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003ee: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003f0: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003f2: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003f4: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003f6: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003f8: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003fa: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003fc: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 0003fe: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000400: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000402: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000404: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000406: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000408: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00040a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00040c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00040e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000410: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000412: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000414: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000416: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000418: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00041a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00041c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00041e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000420: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000422: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000424: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000426: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000428: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00042a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00042c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00042e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000430: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000432: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000434: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000436: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000438: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00043a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00043c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00043e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000440: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000442: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000444: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000446: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000448: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00044a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00044c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00044e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000450: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000452: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000454: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000456: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000458: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00045a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00045c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00045e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000460: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000462: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000464: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000466: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000468: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00046a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00046c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00046e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000470: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000472: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000474: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000476: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000478: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00047a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00047c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00047e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000480: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000482: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000484: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000486: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000488: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00048a: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00048c: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 00048e: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000490: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000492: OWNER_SW_CFG: ROM_ALERT_CLASSIFICATION
+000000 // 000494: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 000496: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 000498: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 00049a: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 00049c: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 00049e: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004a0: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004a2: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004a4: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004a6: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004a8: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004aa: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004ac: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004ae: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004b0: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004b2: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004b4: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004b6: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004b8: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004ba: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004bc: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004be: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004c0: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004c2: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004c4: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004c6: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004c8: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004ca: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004cc: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004ce: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004d0: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004d2: OWNER_SW_CFG: ROM_LOCAL_ALERT_CLASSIFICATION
+000000 // 0004d4: OWNER_SW_CFG: ROM_ALERT_ACCUM_THRESH
+000000 // 0004d6: OWNER_SW_CFG: ROM_ALERT_ACCUM_THRESH
+000000 // 0004d8: OWNER_SW_CFG: ROM_ALERT_ACCUM_THRESH
+000000 // 0004da: OWNER_SW_CFG: ROM_ALERT_ACCUM_THRESH
+000000 // 0004dc: OWNER_SW_CFG: ROM_ALERT_ACCUM_THRESH
+000000 // 0004de: OWNER_SW_CFG: ROM_ALERT_ACCUM_THRESH
+000000 // 0004e0: OWNER_SW_CFG: ROM_ALERT_ACCUM_THRESH
+000000 // 0004e2: OWNER_SW_CFG: ROM_ALERT_ACCUM_THRESH
+000000 // 0004e4: OWNER_SW_CFG: ROM_ALERT_TIMEOUT_CYCLES
+000000 // 0004e6: OWNER_SW_CFG: ROM_ALERT_TIMEOUT_CYCLES
+000000 // 0004e8: OWNER_SW_CFG: ROM_ALERT_TIMEOUT_CYCLES
+000000 // 0004ea: OWNER_SW_CFG: ROM_ALERT_TIMEOUT_CYCLES
+000000 // 0004ec: OWNER_SW_CFG: ROM_ALERT_TIMEOUT_CYCLES
+000000 // 0004ee: OWNER_SW_CFG: ROM_ALERT_TIMEOUT_CYCLES
+000000 // 0004f0: OWNER_SW_CFG: ROM_ALERT_TIMEOUT_CYCLES
+000000 // 0004f2: OWNER_SW_CFG: ROM_ALERT_TIMEOUT_CYCLES
+000000 // 0004f4: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 0004f6: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 0004f8: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 0004fa: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 0004fc: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 0004fe: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000500: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000502: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000504: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000506: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000508: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 00050a: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 00050c: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 00050e: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000510: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000512: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000514: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000516: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000518: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 00051a: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 00051c: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 00051e: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000520: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000522: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000524: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000526: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000528: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 00052a: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 00052c: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 00052e: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000530: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000532: OWNER_SW_CFG: ROM_ALERT_PHASE_CYCLES
+000000 // 000534: unallocated
+000000 // 000536: unallocated
+000000 // 000538: unallocated
+000000 // 00053a: unallocated
+000000 // 00053c: unallocated
+000000 // 00053e: unallocated
+000000 // 000540: unallocated
+000000 // 000542: unallocated
+000000 // 000544: unallocated
+000000 // 000546: unallocated
+000000 // 000548: unallocated
+000000 // 00054a: unallocated
+000000 // 00054c: unallocated
+000000 // 00054e: unallocated
+000000 // 000550: unallocated
+000000 // 000552: unallocated
+000000 // 000554: unallocated
+000000 // 000556: unallocated
+000000 // 000558: unallocated
+000000 // 00055a: unallocated
+000000 // 00055c: unallocated
+000000 // 00055e: unallocated
+000000 // 000560: unallocated
+000000 // 000562: unallocated
+000000 // 000564: unallocated
+000000 // 000566: unallocated
+000000 // 000568: unallocated
+000000 // 00056a: unallocated
+000000 // 00056c: unallocated
+000000 // 00056e: unallocated
+000000 // 000570: unallocated
+000000 // 000572: unallocated
+000000 // 000574: unallocated
+000000 // 000576: unallocated
+000000 // 000578: unallocated
+000000 // 00057a: unallocated
+000000 // 00057c: unallocated
+000000 // 00057e: unallocated
+000000 // 000580: unallocated
+000000 // 000582: unallocated
+000000 // 000584: unallocated
+000000 // 000586: unallocated
+000000 // 000588: unallocated
+000000 // 00058a: unallocated
+000000 // 00058c: unallocated
+000000 // 00058e: unallocated
+000000 // 000590: unallocated
+000000 // 000592: unallocated
+000000 // 000594: unallocated
+000000 // 000596: unallocated
+000000 // 000598: unallocated
+000000 // 00059a: unallocated
+000000 // 00059c: unallocated
+000000 // 00059e: unallocated
+000000 // 0005a0: unallocated
+000000 // 0005a2: unallocated
+000000 // 0005a4: unallocated
+000000 // 0005a6: unallocated
+000000 // 0005a8: unallocated
+000000 // 0005aa: unallocated
+000000 // 0005ac: unallocated
+000000 // 0005ae: unallocated
+000000 // 0005b0: unallocated
+000000 // 0005b2: unallocated
+000000 // 0005b4: unallocated
+000000 // 0005b6: unallocated
+000000 // 0005b8: unallocated
+000000 // 0005ba: unallocated
+000000 // 0005bc: unallocated
+000000 // 0005be: unallocated
+000000 // 0005c0: unallocated
+000000 // 0005c2: unallocated
+000000 // 0005c4: unallocated
+000000 // 0005c6: unallocated
+000000 // 0005c8: unallocated
+000000 // 0005ca: unallocated
+000000 // 0005cc: unallocated
+000000 // 0005ce: unallocated
+000000 // 0005d0: unallocated
+000000 // 0005d2: unallocated
+000000 // 0005d4: unallocated
+000000 // 0005d6: unallocated
+000000 // 0005d8: unallocated
+000000 // 0005da: unallocated
+000000 // 0005dc: unallocated
+000000 // 0005de: unallocated
+000000 // 0005e0: unallocated
+000000 // 0005e2: unallocated
+000000 // 0005e4: unallocated
+000000 // 0005e6: unallocated
+000000 // 0005e8: unallocated
+000000 // 0005ea: unallocated
+000000 // 0005ec: unallocated
+000000 // 0005ee: unallocated
+000000 // 0005f0: unallocated
+000000 // 0005f2: unallocated
+000000 // 0005f4: unallocated
+000000 // 0005f6: unallocated
+000000 // 0005f8: unallocated
+000000 // 0005fa: unallocated
+000000 // 0005fc: unallocated
+000000 // 0005fe: unallocated
+000000 // 000600: unallocated
+000000 // 000602: unallocated
+000000 // 000604: unallocated
+000000 // 000606: unallocated
+000000 // 000608: unallocated
+000000 // 00060a: unallocated
+000000 // 00060c: unallocated
+000000 // 00060e: unallocated
+000000 // 000610: unallocated
+000000 // 000612: unallocated
+000000 // 000614: unallocated
+000000 // 000616: unallocated
+000000 // 000618: unallocated
+000000 // 00061a: unallocated
+000000 // 00061c: unallocated
+000000 // 00061e: unallocated
+000000 // 000620: unallocated
+000000 // 000622: unallocated
+000000 // 000624: unallocated
+000000 // 000626: unallocated
+000000 // 000628: unallocated
+000000 // 00062a: unallocated
+000000 // 00062c: unallocated
+000000 // 00062e: unallocated
+000000 // 000630: unallocated
+000000 // 000632: unallocated
+000000 // 000634: unallocated
+000000 // 000636: unallocated
+000000 // 000638: unallocated
+000000 // 00063a: unallocated
+000000 // 00063c: unallocated
+000000 // 00063e: unallocated
+00e8a5 // 000640: HW_CFG: DEVICE_ID
+05492f // 000642: HW_CFG: DEVICE_ID
+383aa7 // 000644: HW_CFG: DEVICE_ID
+0f3231 // 000646: HW_CFG: DEVICE_ID
+195db1 // 000648: HW_CFG: DEVICE_ID
+311749 // 00064a: HW_CFG: DEVICE_ID
+1687dc // 00064c: HW_CFG: DEVICE_ID
+30fc8d // 00064e: HW_CFG: DEVICE_ID
+0b1a6f // 000650: HW_CFG: DEVICE_ID
+31242f // 000652: HW_CFG: DEVICE_ID
+2b8bb1 // 000654: HW_CFG: DEVICE_ID
+014c1d // 000656: HW_CFG: DEVICE_ID
+139e3f // 000658: HW_CFG: DEVICE_ID
+389b8b // 00065a: HW_CFG: DEVICE_ID
+145d67 // 00065c: HW_CFG: DEVICE_ID
+1b9be6 // 00065e: HW_CFG: DEVICE_ID
+000000 // 000660: HW_CFG: MANUF_STATE
+000000 // 000662: HW_CFG: MANUF_STATE
+000000 // 000664: HW_CFG: MANUF_STATE
+000000 // 000666: HW_CFG: MANUF_STATE
+000000 // 000668: HW_CFG: MANUF_STATE
+000000 // 00066a: HW_CFG: MANUF_STATE
+000000 // 00066c: HW_CFG: MANUF_STATE
+000000 // 00066e: HW_CFG: MANUF_STATE
+000000 // 000670: HW_CFG: MANUF_STATE
+000000 // 000672: HW_CFG: MANUF_STATE
+000000 // 000674: HW_CFG: MANUF_STATE
+000000 // 000676: HW_CFG: MANUF_STATE
+000000 // 000678: HW_CFG: MANUF_STATE
+000000 // 00067a: HW_CFG: MANUF_STATE
+000000 // 00067c: HW_CFG: MANUF_STATE
+000000 // 00067e: HW_CFG: MANUF_STATE
+000000 // 000680: HW_CFG: EN_SRAM_IFETCH, HW_CFG: EN_CSRNG_SW_APP_READ
+230001 // 000682: HW_CFG: EN_ENTROPY_SRC_FW_READ, HW_CFG: EN_ENTROPY_SRC_FW_OVER
+000000 // 000684: unallocated
+000000 // 000686: unallocated
+31848f // 000688: unallocated
+18410c // 00068a: unallocated
+2ca155 // 00068c: unallocated
+2e51cd // 00068e: unallocated
+1c0f67 // 000690: SECRET0: TEST_UNLOCK_TOKEN
+3a154c // 000692: SECRET0: TEST_UNLOCK_TOKEN
+01df22 // 000694: SECRET0: TEST_UNLOCK_TOKEN
+37b22f // 000696: SECRET0: TEST_UNLOCK_TOKEN
+1a39c7 // 000698: SECRET0: TEST_UNLOCK_TOKEN
+1f5d11 // 00069a: SECRET0: TEST_UNLOCK_TOKEN
+021ea4 // 00069c: SECRET0: TEST_UNLOCK_TOKEN
+0802cc // 00069e: SECRET0: TEST_UNLOCK_TOKEN
+0cf7a4 // 0006a0: SECRET0: TEST_EXIT_TOKEN
+25c0ad // 0006a2: SECRET0: TEST_EXIT_TOKEN
+0a2f9b // 0006a4: SECRET0: TEST_EXIT_TOKEN
+1444b7 // 0006a6: SECRET0: TEST_EXIT_TOKEN
+33cdc4 // 0006a8: SECRET0: TEST_EXIT_TOKEN
+0d63b3 // 0006aa: SECRET0: TEST_EXIT_TOKEN
+34e60f // 0006ac: SECRET0: TEST_EXIT_TOKEN
+1b6d25 // 0006ae: SECRET0: TEST_EXIT_TOKEN
+3ed201 // 0006b0: unallocated
+24baea // 0006b2: unallocated
+1720c1 // 0006b4: unallocated
+30cf12 // 0006b6: unallocated
+26c3ad // 0006b8: SECRET1: FLASH_ADDR_KEY_SEED
+18d7ac // 0006ba: SECRET1: FLASH_ADDR_KEY_SEED
+12f85b // 0006bc: SECRET1: FLASH_ADDR_KEY_SEED
+265414 // 0006be: SECRET1: FLASH_ADDR_KEY_SEED
+024efd // 0006c0: SECRET1: FLASH_ADDR_KEY_SEED
+30cea8 // 0006c2: SECRET1: FLASH_ADDR_KEY_SEED
+12b032 // 0006c4: SECRET1: FLASH_ADDR_KEY_SEED
+2e4fab // 0006c6: SECRET1: FLASH_ADDR_KEY_SEED
+3cfe79 // 0006c8: SECRET1: FLASH_ADDR_KEY_SEED
+303ec4 // 0006ca: SECRET1: FLASH_ADDR_KEY_SEED
+0d3156 // 0006cc: SECRET1: FLASH_ADDR_KEY_SEED
+0cbd2b // 0006ce: SECRET1: FLASH_ADDR_KEY_SEED
+399d75 // 0006d0: SECRET1: FLASH_ADDR_KEY_SEED
+0fc5eb // 0006d2: SECRET1: FLASH_ADDR_KEY_SEED
+211da3 // 0006d4: SECRET1: FLASH_ADDR_KEY_SEED
+161185 // 0006d6: SECRET1: FLASH_ADDR_KEY_SEED
+257fe7 // 0006d8: SECRET1: FLASH_DATA_KEY_SEED
+396cf9 // 0006da: SECRET1: FLASH_DATA_KEY_SEED
+2ca570 // 0006dc: SECRET1: FLASH_DATA_KEY_SEED
+3e0d1e // 0006de: SECRET1: FLASH_DATA_KEY_SEED
+0bec0f // 0006e0: SECRET1: FLASH_DATA_KEY_SEED
+2fbffa // 0006e2: SECRET1: FLASH_DATA_KEY_SEED
+20c7d1 // 0006e4: SECRET1: FLASH_DATA_KEY_SEED
+0dbc9a // 0006e6: SECRET1: FLASH_DATA_KEY_SEED
+10991a // 0006e8: SECRET1: FLASH_DATA_KEY_SEED
+1f5a91 // 0006ea: SECRET1: FLASH_DATA_KEY_SEED
+356e57 // 0006ec: SECRET1: FLASH_DATA_KEY_SEED
+231831 // 0006ee: SECRET1: FLASH_DATA_KEY_SEED
+0debe0 // 0006f0: SECRET1: FLASH_DATA_KEY_SEED
+0297ee // 0006f2: SECRET1: FLASH_DATA_KEY_SEED
+3d8762 // 0006f4: SECRET1: FLASH_DATA_KEY_SEED
+258bae // 0006f6: SECRET1: FLASH_DATA_KEY_SEED
+227f7a // 0006f8: SECRET1: SRAM_DATA_KEY_SEED
+1b767f // 0006fa: SECRET1: SRAM_DATA_KEY_SEED
+047b6d // 0006fc: SECRET1: SRAM_DATA_KEY_SEED
+0f7eb2 // 0006fe: SECRET1: SRAM_DATA_KEY_SEED
+34a973 // 000700: SECRET1: SRAM_DATA_KEY_SEED
+11b484 // 000702: SECRET1: SRAM_DATA_KEY_SEED
+0a9687 // 000704: SECRET1: SRAM_DATA_KEY_SEED
+1b86b0 // 000706: SECRET1: SRAM_DATA_KEY_SEED
+11fdc2 // 000708: unallocated
+1b68e0 // 00070a: unallocated
+24eea0 // 00070c: unallocated
+1da8d9 // 00070e: unallocated
+2d62ad // 000710: SECRET2: RMA_TOKEN
+0ddfff // 000712: SECRET2: RMA_TOKEN
+360dfb // 000714: SECRET2: RMA_TOKEN
+1683f9 // 000716: SECRET2: RMA_TOKEN
+3b749e // 000718: SECRET2: RMA_TOKEN
+3d0e44 // 00071a: SECRET2: RMA_TOKEN
+3e7764 // 00071c: SECRET2: RMA_TOKEN
+2d06b1 // 00071e: SECRET2: RMA_TOKEN
+2861ae // 000720: SECRET2: CREATOR_ROOT_KEY_SHARE0
+15cb59 // 000722: SECRET2: CREATOR_ROOT_KEY_SHARE0
+0d3266 // 000724: SECRET2: CREATOR_ROOT_KEY_SHARE0
+1c14c4 // 000726: SECRET2: CREATOR_ROOT_KEY_SHARE0
+2294ef // 000728: SECRET2: CREATOR_ROOT_KEY_SHARE0
+1a170e // 00072a: SECRET2: CREATOR_ROOT_KEY_SHARE0
+1bac30 // 00072c: SECRET2: CREATOR_ROOT_KEY_SHARE0
+375cb0 // 00072e: SECRET2: CREATOR_ROOT_KEY_SHARE0
+3daffd // 000730: SECRET2: CREATOR_ROOT_KEY_SHARE0
+3d46e2 // 000732: SECRET2: CREATOR_ROOT_KEY_SHARE0
+1b5b13 // 000734: SECRET2: CREATOR_ROOT_KEY_SHARE0
+310a8a // 000736: SECRET2: CREATOR_ROOT_KEY_SHARE0
+2c8c63 // 000738: SECRET2: CREATOR_ROOT_KEY_SHARE0
+059536 // 00073a: SECRET2: CREATOR_ROOT_KEY_SHARE0
+3d71cd // 00073c: SECRET2: CREATOR_ROOT_KEY_SHARE0
+360808 // 00073e: SECRET2: CREATOR_ROOT_KEY_SHARE0
+3c7733 // 000740: SECRET2: CREATOR_ROOT_KEY_SHARE1
+3526f1 // 000742: SECRET2: CREATOR_ROOT_KEY_SHARE1
+0462e7 // 000744: SECRET2: CREATOR_ROOT_KEY_SHARE1
+0b976c // 000746: SECRET2: CREATOR_ROOT_KEY_SHARE1
+22bea0 // 000748: SECRET2: CREATOR_ROOT_KEY_SHARE1
+0e48b9 // 00074a: SECRET2: CREATOR_ROOT_KEY_SHARE1
+09c5de // 00074c: SECRET2: CREATOR_ROOT_KEY_SHARE1
+38468b // 00074e: SECRET2: CREATOR_ROOT_KEY_SHARE1
+17741e // 000750: SECRET2: CREATOR_ROOT_KEY_SHARE1
+2ade10 // 000752: SECRET2: CREATOR_ROOT_KEY_SHARE1
+04eefd // 000754: SECRET2: CREATOR_ROOT_KEY_SHARE1
+0391df // 000756: SECRET2: CREATOR_ROOT_KEY_SHARE1
+39fd4c // 000758: SECRET2: CREATOR_ROOT_KEY_SHARE1
+1615fa // 00075a: SECRET2: CREATOR_ROOT_KEY_SHARE1
+3ead88 // 00075c: SECRET2: CREATOR_ROOT_KEY_SHARE1
+1422ee // 00075e: SECRET2: CREATOR_ROOT_KEY_SHARE1
+319ecc // 000760: unallocated
+09f19b // 000762: unallocated
+3a6f5a // 000764: unallocated
+29367c // 000766: unallocated
+000000 // 000768: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 00076a: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 00076c: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 00076e: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000770: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000772: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000774: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000776: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000778: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 00077a: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 00077c: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 00077e: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000780: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000782: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000784: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000786: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000788: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 00078a: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 00078c: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 00078e: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000790: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000792: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000794: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000796: LIFE_CYCLE: LC_TRANSITION_CNT
+000000 // 000798: LIFE_CYCLE: LC_STATE
+000000 // 00079a: LIFE_CYCLE: LC_STATE
+000000 // 00079c: LIFE_CYCLE: LC_STATE
+000000 // 00079e: LIFE_CYCLE: LC_STATE
+000000 // 0007a0: LIFE_CYCLE: LC_STATE
+000000 // 0007a2: LIFE_CYCLE: LC_STATE
+000000 // 0007a4: LIFE_CYCLE: LC_STATE
+000000 // 0007a6: LIFE_CYCLE: LC_STATE
+000000 // 0007a8: LIFE_CYCLE: LC_STATE
+000000 // 0007aa: LIFE_CYCLE: LC_STATE
+000000 // 0007ac: LIFE_CYCLE: LC_STATE
+000000 // 0007ae: LIFE_CYCLE: LC_STATE
+000000 // 0007b0: LIFE_CYCLE: LC_STATE
+000000 // 0007b2: LIFE_CYCLE: LC_STATE
+000000 // 0007b4: LIFE_CYCLE: LC_STATE
+000000 // 0007b6: LIFE_CYCLE: LC_STATE
+000000 // 0007b8: LIFE_CYCLE: LC_STATE
+000000 // 0007ba: LIFE_CYCLE: LC_STATE
+000000 // 0007bc: LIFE_CYCLE: LC_STATE
+000000 // 0007be: LIFE_CYCLE: LC_STATE
+000000 // 0007c0: 
+000000 // 0007c2: 
+000000 // 0007c4: 
+000000 // 0007c6: 
+000000 // 0007c8: 
+000000 // 0007ca: 
+000000 // 0007cc: 
+000000 // 0007ce: 
+000000 // 0007d0: 
+000000 // 0007d2: 
+000000 // 0007d4: 
+000000 // 0007d6: 
+000000 // 0007d8: 
+000000 // 0007da: 
+000000 // 0007dc: 
+000000 // 0007de: 
+000000 // 0007e0: 
+000000 // 0007e2: 
+000000 // 0007e4: 
+000000 // 0007e6: 
+000000 // 0007e8: 
+000000 // 0007ea: 
+000000 // 0007ec: 
+000000 // 0007ee: 
+000000 // 0007f0: 
+000000 // 0007f2: 
+000000 // 0007f4: 
+000000 // 0007f6: 
+000000 // 0007f8: 
+000000 // 0007fa: 
+000000 // 0007fc: 
+000000 // 0007fe: 
diff --git a/third_party/cargo/crates.bzl b/third_party/cargo/crates.bzl
index 1e3d07b..24be8f4 100644
--- a/third_party/cargo/crates.bzl
+++ b/third_party/cargo/crates.bzl
@@ -15,6 +15,7 @@
         "anyhow": "@raze__anyhow__1_0_46//:anyhow",
         "bitflags": "@raze__bitflags__1_3_2//:bitflags",
         "byteorder": "@raze__byteorder__1_4_3//:byteorder",
+        "deser-hjson": "@raze__deser_hjson__1_0_2//:deser_hjson",
         "erased-serde": "@raze__erased_serde__0_3_16//:erased_serde",
         "hex": "@raze__hex__0_4_3//:hex",
         "lazy_static": "@raze__lazy_static__1_4_0//:lazy_static",
@@ -23,6 +24,7 @@
         "num-bigint-dig": "@raze__num_bigint_dig__0_7_0//:num_bigint_dig",
         "num-traits": "@raze__num_traits__0_2_14//:num_traits",
         "num_enum": "@raze__num_enum__0_5_4//:num_enum",
+        "rand": "@raze__rand__0_8_4//:rand",
         "regex": "@raze__regex__1_5_4//:regex",
         "rusb": "@raze__rusb__0_8_1//:rusb",
         "safe-ftdi": "@raze__safe_ftdi__0_3_0//:safe_ftdi",
@@ -393,6 +395,15 @@
 
     maybe(
         http_archive,
+        name = "raze__deser_hjson__1_0_2",
+        url = "https://crates.io/api/v1/crates/deser-hjson/1.0.2/download",
+        type = "tar.gz",
+        strip_prefix = "deser-hjson-1.0.2",
+        build_file = Label("//third_party/cargo/remote:BUILD.deser-hjson-1.0.2.bazel"),
+    )
+
+    maybe(
+        http_archive,
         name = "raze__directories__4_0_1",
         url = "https://crates.io/api/v1/crates/directories/4.0.1/download",
         type = "tar.gz",
@@ -453,6 +464,16 @@
 
     maybe(
         http_archive,
+        name = "raze__goblin__0_0_24",
+        url = "https://crates.io/api/v1/crates/goblin/0.0.24/download",
+        type = "tar.gz",
+        sha256 = "e3fa261d919c1ae9d1e4533c4a2f99e10938603c4208d56c05bec7a872b661b0",
+        strip_prefix = "goblin-0.0.24",
+        build_file = Label("//third_party/cargo/remote:BUILD.goblin-0.0.24.bazel"),
+    )
+
+    maybe(
+        http_archive,
         name = "raze__heck__0_3_3",
         url = "https://crates.io/api/v1/crates/heck/0.3.3/download",
         type = "tar.gz",
@@ -734,6 +755,16 @@
 
     maybe(
         http_archive,
+        name = "raze__plain__0_2_3",
+        url = "https://crates.io/api/v1/crates/plain/0.2.3/download",
+        type = "tar.gz",
+        sha256 = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6",
+        strip_prefix = "plain-0.2.3",
+        build_file = Label("//third_party/cargo/remote:BUILD.plain-0.2.3.bazel"),
+    )
+
+    maybe(
+        http_archive,
         name = "raze__ppv_lite86__0_2_16",
         url = "https://crates.io/api/v1/crates/ppv-lite86/0.2.16/download",
         type = "tar.gz",
@@ -840,6 +871,15 @@
 
     maybe(
         http_archive,
+        name = "raze__rand_hc__0_3_1",
+        url = "https://crates.io/api/v1/crates/rand_hc/0.3.1/download",
+        type = "tar.gz",
+        strip_prefix = "rand_hc-0.3.1",
+        build_file = Label("//third_party/cargo/remote:BUILD.rand_hc-0.3.1.bazel"),
+    )
+
+    maybe(
+        http_archive,
         name = "raze__raw_tty__0_1_0",
         url = "https://crates.io/api/v1/crates/raw_tty/0.1.0/download",
         type = "tar.gz",
@@ -929,6 +969,26 @@
 
     maybe(
         http_archive,
+        name = "raze__scroll__0_9_2",
+        url = "https://crates.io/api/v1/crates/scroll/0.9.2/download",
+        type = "tar.gz",
+        sha256 = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383",
+        strip_prefix = "scroll-0.9.2",
+        build_file = Label("//third_party/cargo/remote:BUILD.scroll-0.9.2.bazel"),
+    )
+
+    maybe(
+        http_archive,
+        name = "raze__scroll_derive__0_9_5",
+        url = "https://crates.io/api/v1/crates/scroll_derive/0.9.5/download",
+        type = "tar.gz",
+        sha256 = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb",
+        strip_prefix = "scroll_derive-0.9.5",
+        build_file = Label("//third_party/cargo/remote:BUILD.scroll_derive-0.9.5.bazel"),
+    )
+
+    maybe(
+        http_archive,
         name = "raze__semver__0_9_0",
         url = "https://crates.io/api/v1/crates/semver/0.9.0/download",
         type = "tar.gz",
@@ -999,11 +1059,11 @@
 
     maybe(
         http_archive,
-        name = "raze__smallvec__1_7_0",
-        url = "https://crates.io/api/v1/crates/smallvec/1.7.0/download",
+        name = "raze__smallvec__1_8_0",
+        url = "https://crates.io/api/v1/crates/smallvec/1.8.0/download",
         type = "tar.gz",
-        strip_prefix = "smallvec-1.7.0",
-        build_file = Label("//third_party/cargo/remote:BUILD.smallvec-1.7.0.bazel"),
+        strip_prefix = "smallvec-1.8.0",
+        build_file = Label("//third_party/cargo/remote:BUILD.smallvec-1.8.0.bazel"),
     )
 
     maybe(
diff --git a/third_party/cargo/remote/BUILD.deser-hjson-1.0.2.bazel b/third_party/cargo/remote/BUILD.deser-hjson-1.0.2.bazel
new file mode 100644
index 0000000..1490eff
--- /dev/null
+++ b/third_party/cargo/remote/BUILD.deser-hjson-1.0.2.bazel
@@ -0,0 +1,74 @@
+"""
+@generated
+cargo-raze crate build file.
+
+DO NOT EDIT! Replaced on runs of cargo-raze
+"""
+
+# buildifier: disable=load
+load("@bazel_skylib//lib:selects.bzl", "selects")
+
+# buildifier: disable=load
+load(
+    "@rules_rust//rust:defs.bzl",
+    "rust_binary",
+    "rust_library",
+    "rust_proc_macro",
+    "rust_test",
+)
+
+package(default_visibility = [
+    # Public for visibility by "@raze__crate__version//" targets.
+    #
+    # Prefer access through "//third_party/cargo", which limits external
+    # visibility to explicit Cargo.toml dependencies.
+    "//visibility:public",
+])
+
+licenses([
+    "notice",  # MIT from expression "MIT"
+])
+
+# Generated Targets
+
+# Unsupported target "parse" with type "bench" omitted
+
+rust_library(
+    name = "deser_hjson",
+    srcs = glob(["**/*.rs"]),
+    crate_features = [
+    ],
+    crate_root = "src/lib.rs",
+    data = [],
+    edition = "2018",
+    rustc_flags = [
+        "--cap-lints=allow",
+    ],
+    tags = [
+        "cargo-raze",
+        "manual",
+    ],
+    version = "1.0.2",
+    # buildifier: leave-alone
+    deps = [
+        "@raze__serde__1_0_130//:serde",
+    ],
+)
+
+# Unsupported target "bad_format" with type "test" omitted
+
+# Unsupported target "crlf" with type "test" omitted
+
+# Unsupported target "enum" with type "test" omitted
+
+# Unsupported target "guess" with type "test" omitted
+
+# Unsupported target "mix" with type "test" omitted
+
+# Unsupported target "serde-error" with type "test" omitted
+
+# Unsupported target "spacing" with type "test" omitted
+
+# Unsupported target "strings" with type "test" omitted
+
+# Unsupported target "trailing_chars" with type "test" omitted
diff --git a/third_party/cargo/remote/BUILD.goblin-0.0.24.bazel b/third_party/cargo/remote/BUILD.goblin-0.0.24.bazel
new file mode 100644
index 0000000..7cfc627
--- /dev/null
+++ b/third_party/cargo/remote/BUILD.goblin-0.0.24.bazel
@@ -0,0 +1,88 @@
+"""
+@generated
+cargo-raze crate build file.
+
+DO NOT EDIT! Replaced on runs of cargo-raze
+"""
+
+# buildifier: disable=load
+load("@bazel_skylib//lib:selects.bzl", "selects")
+
+# buildifier: disable=load
+load(
+    "@rules_rust//rust:defs.bzl",
+    "rust_binary",
+    "rust_library",
+    "rust_proc_macro",
+    "rust_test",
+)
+
+package(default_visibility = [
+    # Public for visibility by "@raze__crate__version//" targets.
+    #
+    # Prefer access through "//third_party/cargo", which limits external
+    # visibility to explicit Cargo.toml dependencies.
+    "//visibility:public",
+])
+
+licenses([
+    "notice",  # MIT from expression "MIT"
+])
+
+# Generated Targets
+
+# Unsupported target "ar" with type "example" omitted
+
+# Unsupported target "automagic" with type "example" omitted
+
+# Unsupported target "dotnet_pe_analysis" with type "example" omitted
+
+# Unsupported target "dyldinfo" with type "example" omitted
+
+# Unsupported target "lipo" with type "example" omitted
+
+# Unsupported target "rdr" with type "example" omitted
+
+# Unsupported target "scroll" with type "example" omitted
+
+rust_library(
+    name = "goblin",
+    srcs = glob(["**/*.rs"]),
+    crate_features = [
+        "alloc",
+        "archive",
+        "default",
+        "elf32",
+        "elf64",
+        "endian_fd",
+        "log",
+        "mach32",
+        "mach64",
+        "pe32",
+        "pe64",
+        "std",
+    ],
+    crate_root = "src/lib.rs",
+    data = [],
+    edition = "2018",
+    rustc_flags = [
+        "--cap-lints=allow",
+    ],
+    tags = [
+        "cargo-raze",
+        "manual",
+    ],
+    version = "0.0.24",
+    # buildifier: leave-alone
+    deps = [
+        "@raze__log__0_4_14//:log",
+        "@raze__plain__0_2_3//:plain",
+        "@raze__scroll__0_9_2//:scroll",
+    ],
+)
+
+# Unsupported target "archive" with type "test" omitted
+
+# Unsupported target "compare_dyldinfos" with type "test" omitted
+
+# Unsupported target "macho" with type "test" omitted
diff --git a/third_party/cargo/remote/BUILD.num-bigint-dig-0.7.0.bazel b/third_party/cargo/remote/BUILD.num-bigint-dig-0.7.0.bazel
index cec2e16..9016e1c 100644
--- a/third_party/cargo/remote/BUILD.num-bigint-dig-0.7.0.bazel
+++ b/third_party/cargo/remote/BUILD.num-bigint-dig-0.7.0.bazel
@@ -100,7 +100,7 @@
         "@raze__num_traits__0_2_14//:num_traits",
         "@raze__rand__0_8_4//:rand",
         "@raze__serde__1_0_130//:serde",
-        "@raze__smallvec__1_7_0//:smallvec",
+        "@raze__smallvec__1_8_0//:smallvec",
     ],
 )
 
diff --git a/third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel b/third_party/cargo/remote/BUILD.plain-0.2.3.bazel
similarity index 82%
copy from third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel
copy to third_party/cargo/remote/BUILD.plain-0.2.3.bazel
index 0567011..650a097 100644
--- a/third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel
+++ b/third_party/cargo/remote/BUILD.plain-0.2.3.bazel
@@ -31,17 +31,14 @@
 
 # Generated Targets
 
-# Unsupported target "bench" with type "bench" omitted
-
 rust_library(
-    name = "smallvec",
+    name = "plain",
     srcs = glob(["**/*.rs"]),
     crate_features = [
-        "write",
     ],
     crate_root = "src/lib.rs",
     data = [],
-    edition = "2018",
+    edition = "2015",
     rustc_flags = [
         "--cap-lints=allow",
     ],
@@ -49,10 +46,8 @@
         "cargo-raze",
         "manual",
     ],
-    version = "1.7.0",
+    version = "0.2.3",
     # buildifier: leave-alone
     deps = [
     ],
 )
-
-# Unsupported target "macro" with type "test" omitted
diff --git a/third_party/cargo/remote/BUILD.rand-0.8.4.bazel b/third_party/cargo/remote/BUILD.rand-0.8.4.bazel
index 62ea154..8ed0a62 100644
--- a/third_party/cargo/remote/BUILD.rand-0.8.4.bazel
+++ b/third_party/cargo/remote/BUILD.rand-0.8.4.bazel
@@ -38,10 +38,13 @@
     },
     crate_features = [
         "alloc",
+        "default",
         "getrandom",
         "libc",
         "rand_chacha",
+        "rand_hc",
         "std",
+        "std_rng",
     ],
     crate_root = "src/lib.rs",
     data = [],
diff --git a/third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel b/third_party/cargo/remote/BUILD.rand_hc-0.3.1.bazel
similarity index 84%
copy from third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel
copy to third_party/cargo/remote/BUILD.rand_hc-0.3.1.bazel
index 0567011..5fa17f7 100644
--- a/third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel
+++ b/third_party/cargo/remote/BUILD.rand_hc-0.3.1.bazel
@@ -31,13 +31,10 @@
 
 # Generated Targets
 
-# Unsupported target "bench" with type "bench" omitted
-
 rust_library(
-    name = "smallvec",
+    name = "rand_hc",
     srcs = glob(["**/*.rs"]),
     crate_features = [
-        "write",
     ],
     crate_root = "src/lib.rs",
     data = [],
@@ -49,10 +46,9 @@
         "cargo-raze",
         "manual",
     ],
-    version = "1.7.0",
+    version = "0.3.1",
     # buildifier: leave-alone
     deps = [
+        "@raze__rand_core__0_6_3//:rand_core",
     ],
 )
-
-# Unsupported target "macro" with type "test" omitted
diff --git a/third_party/cargo/remote/BUILD.scroll-0.9.2.bazel b/third_party/cargo/remote/BUILD.scroll-0.9.2.bazel
new file mode 100644
index 0000000..a2f3823
--- /dev/null
+++ b/third_party/cargo/remote/BUILD.scroll-0.9.2.bazel
@@ -0,0 +1,101 @@
+"""
+@generated
+cargo-raze crate build file.
+
+DO NOT EDIT! Replaced on runs of cargo-raze
+"""
+
+# buildifier: disable=load
+load("@bazel_skylib//lib:selects.bzl", "selects")
+
+# buildifier: disable=load
+load(
+    "@rules_rust//rust:defs.bzl",
+    "rust_binary",
+    "rust_library",
+    "rust_proc_macro",
+    "rust_test",
+)
+
+package(default_visibility = [
+    # Public for visibility by "@raze__crate__version//" targets.
+    #
+    # Prefer access through "//third_party/cargo", which limits external
+    # visibility to explicit Cargo.toml dependencies.
+    "//visibility:public",
+])
+
+licenses([
+    "notice",  # MIT from expression "MIT"
+])
+
+# Generated Targets
+# buildifier: disable=out-of-order-load
+# buildifier: disable=load-on-top
+load(
+    "@rules_rust//cargo:cargo_build_script.bzl",
+    "cargo_build_script",
+)
+
+cargo_build_script(
+    name = "scroll_build_script",
+    srcs = glob(["**/*.rs"]),
+    build_script_env = {
+    },
+    crate_features = [
+        "derive",
+        "scroll_derive",
+        "std",
+    ],
+    crate_root = "build.rs",
+    data = glob(["**"]),
+    edition = "2015",
+    rustc_flags = [
+        "--cap-lints=allow",
+    ],
+    tags = [
+        "cargo-raze",
+        "manual",
+    ],
+    version = "0.9.2",
+    visibility = ["//visibility:private"],
+    deps = [
+        "@raze__rustc_version__0_2_3//:rustc_version",
+    ],
+)
+
+# Unsupported target "bench" with type "bench" omitted
+
+# Unsupported target "data_ctx" with type "example" omitted
+
+rust_library(
+    name = "scroll",
+    srcs = glob(["**/*.rs"]),
+    crate_features = [
+        "derive",
+        "scroll_derive",
+        "std",
+    ],
+    crate_root = "src/lib.rs",
+    data = [],
+    edition = "2015",
+    proc_macro_deps = [
+        "@raze__scroll_derive__0_9_5//:scroll_derive",
+    ],
+    rustc_flags = [
+        "--cap-lints=allow",
+    ],
+    tags = [
+        "cargo-raze",
+        "manual",
+    ],
+    version = "0.9.2",
+    # buildifier: leave-alone
+    deps = [
+        ":scroll_build_script",
+    ],
+)
+
+# Unsupported target "api" with type "test" omitted
+
+# Unsupported target "readme" with type "test" omitted
diff --git a/third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel b/third_party/cargo/remote/BUILD.scroll_derive-0.9.5.bazel
similarity index 70%
copy from third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel
copy to third_party/cargo/remote/BUILD.scroll_derive-0.9.5.bazel
index 0567011..5b0720a 100644
--- a/third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel
+++ b/third_party/cargo/remote/BUILD.scroll_derive-0.9.5.bazel
@@ -26,22 +26,21 @@
 ])
 
 licenses([
-    "notice",  # MIT from expression "MIT OR Apache-2.0"
+    "notice",  # MIT from expression "MIT"
 ])
 
 # Generated Targets
 
-# Unsupported target "bench" with type "bench" omitted
+# Unsupported target "main" with type "example" omitted
 
-rust_library(
-    name = "smallvec",
+rust_proc_macro(
+    name = "scroll_derive",
     srcs = glob(["**/*.rs"]),
     crate_features = [
-        "write",
     ],
     crate_root = "src/lib.rs",
     data = [],
-    edition = "2018",
+    edition = "2015",
     rustc_flags = [
         "--cap-lints=allow",
     ],
@@ -49,10 +48,13 @@
         "cargo-raze",
         "manual",
     ],
-    version = "1.7.0",
+    version = "0.9.5",
     # buildifier: leave-alone
     deps = [
+        "@raze__proc_macro2__0_4_30//:proc_macro2",
+        "@raze__quote__0_6_13//:quote",
+        "@raze__syn__0_15_44//:syn",
     ],
 )
 
-# Unsupported target "macro" with type "test" omitted
+# Unsupported target "tests" with type "test" omitted
diff --git a/third_party/cargo/remote/BUILD.serde-1.0.130.bazel b/third_party/cargo/remote/BUILD.serde-1.0.130.bazel
index 3ebb55a..75c98f5 100644
--- a/third_party/cargo/remote/BUILD.serde-1.0.130.bazel
+++ b/third_party/cargo/remote/BUILD.serde-1.0.130.bazel
@@ -44,6 +44,7 @@
     },
     crate_features = [
         "default",
+        "derive",
         "serde_derive",
         "std",
     ],
@@ -68,6 +69,7 @@
     srcs = glob(["**/*.rs"]),
     crate_features = [
         "default",
+        "derive",
         "serde_derive",
         "std",
     ],
diff --git a/third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel b/third_party/cargo/remote/BUILD.smallvec-1.8.0.bazel
similarity index 97%
rename from third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel
rename to third_party/cargo/remote/BUILD.smallvec-1.8.0.bazel
index 0567011..3883e5d 100644
--- a/third_party/cargo/remote/BUILD.smallvec-1.7.0.bazel
+++ b/third_party/cargo/remote/BUILD.smallvec-1.8.0.bazel
@@ -49,7 +49,7 @@
         "cargo-raze",
         "manual",
     ],
-    version = "1.7.0",
+    version = "1.8.0",
     # buildifier: leave-alone
     deps = [
     ],