opentitanlib/otp/
otp_img.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use crate::util::parse_int::ParseInt;
6
7use std::fmt;
8use std::path::Path;
9
10use anyhow::{Result, anyhow, bail};
11
12use serde::de::{self, Unexpected};
13use serde::{Deserialize, Serialize};
14
15use serde_annotate::Annotate;
16
17#[derive(Annotate, Serialize, Debug, PartialEq, Eq)]
18#[serde(untagged)]
19pub enum OtpImgValue {
20    Word(u64),
21    Bool(bool),
22    Sequence(Vec<u32>),
23    #[serde(serialize_with = "serialize_random")]
24    Random,
25}
26
27fn serialize_random<S>(serializer: S) -> Result<S::Ok, S::Error>
28where
29    S: serde::Serializer,
30{
31    serializer.serialize_str("<random>")
32}
33
34impl<'de> Deserialize<'de> for OtpImgValue {
35    fn deserialize<D>(deserializer: D) -> Result<OtpImgValue, D::Error>
36    where
37        D: serde::Deserializer<'de>,
38    {
39        struct Visitor;
40
41        impl<'a> de::Visitor<'a> for Visitor {
42            type Value = OtpImgValue;
43
44            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
45                formatter.write_str("an OtpImgValue")
46            }
47
48            fn visit_str<E>(self, val: &str) -> Result<Self::Value, E>
49            where
50                E: de::Error,
51            {
52                Ok(match val {
53                    "<random>" => OtpImgValue::Random,
54                    "true" => OtpImgValue::Bool(true),
55                    "false" => OtpImgValue::Bool(false),
56                    _ => OtpImgValue::Word(
57                        u64::from_str(val)
58                            .map_err(|_| de::Error::invalid_value(Unexpected::Str(val), &self))?,
59                    ),
60                })
61            }
62
63            fn visit_u64<E>(self, val: u64) -> Result<Self::Value, E>
64            where
65                E: de::Error,
66            {
67                Ok(OtpImgValue::Word(val))
68            }
69
70            fn visit_bool<E>(self, val: bool) -> Result<Self::Value, E>
71            where
72                E: de::Error,
73            {
74                Ok(OtpImgValue::Bool(val))
75            }
76
77            fn visit_seq<A>(self, mut val: A) -> Result<Self::Value, A::Error>
78            where
79                A: de::SeqAccess<'a>,
80            {
81                let mut res = Vec::<u32>::new();
82                while let Ok(Some(v)) = val.next_element::<String>() {
83                    res.push(
84                        u32::from_str(&v)
85                            .map_err(|_| de::Error::invalid_value(Unexpected::Str(&v), &self))?,
86                    )
87                }
88                Ok(OtpImgValue::Sequence(res))
89            }
90        }
91        deserializer.deserialize_any(Visitor {})
92    }
93}
94
95#[derive(Annotate, Serialize, Deserialize, Debug, PartialEq, Eq)]
96pub struct OtpImgItem {
97    pub name: String,
98    pub value: OtpImgValue,
99}
100
101#[derive(Annotate, Serialize, Deserialize, Debug, PartialEq, Eq)]
102pub struct OtpImgPartition {
103    pub name: String,
104    pub items: Option<Vec<OtpImgItem>>,
105}
106
107#[derive(Annotate, Serialize, Deserialize, Debug, PartialEq, Eq)]
108pub struct OtpImg {
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub seed: Option<u64>,
111    // FIXME: Needed to get `OtpImgValue` serailization to emit hex values.
112    // See: https://github.com/cfrantz/serde-annotate/issues/5.
113    #[annotate(format = hex)]
114    pub partitions: Vec<OtpImgPartition>,
115}
116
117pub trait OtpRead {
118    fn read32(&self, name: &str) -> Result<u32> {
119        self.read32_offset(name, 0)
120    }
121
122    fn read32_offset(&self, name: &str, offset: usize) -> Result<u32>;
123}
124
125impl OtpRead for OtpImg {
126    fn read32_offset(&self, name: &str, offset: usize) -> Result<u32> {
127        if !offset.is_multiple_of(4) {
128            bail!("offset not word aligned");
129        }
130        Ok(
131            // Flatten all partitions and find the value that matches `name`.
132            match self
133                .partitions
134                .iter()
135                .filter_map(|p| p.items.as_ref())
136                .flatten()
137                .find(|v| v.name == name)
138                .map(|item| &item.value)
139            {
140                Some(OtpImgValue::Word(v)) => {
141                    if offset == 0 {
142                        (v & u32::MAX as u64).try_into().unwrap()
143                    } else if offset == 4 {
144                        (v >> 32).try_into().unwrap()
145                    } else {
146                        bail!("invalid OTP address {} + {:#08x}", name, offset)
147                    }
148                }
149                Some(OtpImgValue::Sequence(v)) => *v
150                    .get(offset / 4)
151                    .ok_or_else(|| anyhow!("invalid OTP address {} + {:#08x}", name, offset))?,
152                None => bail!("undefined OTP word {}", name),
153                Some(x) => bail!("invalid OTP word {} = {:?}", name, x),
154            },
155        )
156    }
157}
158
159impl OtpImg {
160    pub fn from_file(in_file: &Path) -> Result<OtpImg> {
161        use std::str::FromStr;
162        Self::from_str(&std::fs::read_to_string(in_file)?)
163    }
164}
165
166impl std::str::FromStr for OtpImg {
167    type Err = anyhow::Error;
168
169    fn from_str(json_text: &str) -> Result<OtpImg> {
170        let res: OtpImg = deser_hjson::from_str(json_text)?;
171        Ok(res)
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178    use std::str::FromStr;
179    use std::sync::LazyLock;
180
181    use serde_annotate::serialize;
182
183    const TEST_OTP_JSON: &str = r#"
184        {
185            partitions: [
186                {
187                    name:  "CREATOR_SW_CFG",
188                    items: [
189                        {
190                            name:  "CREATOR_SW_CFG_DIGEST",
191                            value: 0,
192                        },
193                        {
194                            name: "CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN",
195                            value: "0x4b4b4b4b4b4ba5a5",
196                        },
197                        {
198                            name:  "CREATOR_RANDOM",
199                            value: "<random>",
200                        },
201                        {
202                            name:  "CREATOR_SEQ",
203                            value: [
204                                "0xab",
205                                "0xcd",
206                                "0xef",
207                            ],
208                        },
209                    ]
210                }
211            ]
212        }"#;
213
214    static TEST_OTP: LazyLock<OtpImg> = LazyLock::new(|| OtpImg {
215        seed: None,
216        partitions: vec![OtpImgPartition {
217            name: "CREATOR_SW_CFG".to_owned(),
218            items: Some(vec![
219                OtpImgItem {
220                    name: "CREATOR_SW_CFG_DIGEST".to_owned(),
221                    value: OtpImgValue::Word(0x0),
222                },
223                OtpImgItem {
224                    name: "CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN".to_owned(),
225                    value: OtpImgValue::Word(0x4b4b4b4b4b4ba5a5),
226                },
227                OtpImgItem {
228                    name: "CREATOR_RANDOM".to_owned(),
229                    value: OtpImgValue::Random,
230                },
231                OtpImgItem {
232                    name: "CREATOR_SEQ".to_owned(),
233                    value: OtpImgValue::Sequence(vec![0xab, 0xcd, 0xef]),
234                },
235            ]),
236        }],
237    });
238
239    #[test]
240    fn test_deser() {
241        let res = OtpImg::from_str(TEST_OTP_JSON).unwrap();
242        assert_eq!(res, *TEST_OTP);
243    }
244
245    #[test]
246    fn test_ser() {
247        let json = serialize(&*TEST_OTP).unwrap().to_hjson().to_string();
248        let json_str = "{
249  partitions: [
250    {
251      name: \"CREATOR_SW_CFG\",
252      items: [
253        {
254          name: \"CREATOR_SW_CFG_DIGEST\",
255          value: 0
256        },
257        {
258          name: \"CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN\",
259          value: \"5425512962855773605\"
260        },
261        {
262          name: \"CREATOR_RANDOM\",
263          value: \"<random>\"
264        },
265        {
266          name: \"CREATOR_SEQ\",
267          value: [
268            171,
269            205,
270            239
271          ]
272        }
273      ]
274    }
275  ]
276}";
277        assert_eq!(json_str, json);
278    }
279
280    #[test]
281    fn test_otp_read() {
282        let otp = OtpImg::from_str(TEST_OTP_JSON).unwrap();
283        assert_eq!(otp.read32("CREATOR_SW_CFG_DIGEST").unwrap(), 0x0);
284        assert_eq!(
285            otp.read32("CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN").unwrap(),
286            0x4b4ba5a5
287        );
288        assert_eq!(
289            otp.read32_offset("CREATOR_SW_CFG_SIGVERIFY_RSA_KEY_EN", 4)
290                .unwrap(),
291            0x4b4b4b4b
292        );
293        assert!(otp.read32("CREATOR_RANDOM").is_err());
294        assert_eq!(otp.read32_offset("CREATOR_SEQ", 0).unwrap(), 0xab);
295        assert_eq!(otp.read32_offset("CREATOR_SEQ", 4).unwrap(), 0xcd);
296        assert_eq!(otp.read32_offset("CREATOR_SEQ", 8).unwrap(), 0xef);
297    }
298}