opentitanlib/ownership/
flash.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 anyhow::Result;
6use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
7use serde::{Deserialize, Serialize};
8use serde_annotate::Annotate;
9use std::io::{Read, Write};
10
11use super::GlobalFlags;
12use super::misc::{TlvHeader, TlvTag};
13use crate::chip::boolean::MultiBitBool4;
14
15/// Describes the proprerties of a flash region.
16#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize, Annotate)]
17pub struct FlashFlags {
18    /// Read operations are allowed in this region.
19    #[serde(default)]
20    pub read: bool,
21    /// Program operations are allowed in this region.
22    #[serde(default)]
23    pub program: bool,
24    /// Erase operations are allowed in this region.
25    #[serde(default)]
26    pub erase: bool,
27    /// Scrambling is enabled in this region.
28    #[serde(default)]
29    pub scramble: bool,
30    /// ECC memory correction is enabled in this region.
31    #[serde(default)]
32    pub ecc: bool,
33    /// The high endurance feature is enabled in this region.
34    #[serde(default)]
35    pub high_endurance: bool,
36    /// Forbid program and erase operations when in the active flash side.
37    #[serde(default)]
38    pub protect_when_active: bool,
39    /// Lock the configuration of this region.
40    #[serde(default)]
41    pub lock: bool,
42}
43
44impl FlashFlags {
45    const TRUE: u64 = MultiBitBool4::True.0 as u64;
46    const FALSE: u64 = MultiBitBool4::False.0 as u64;
47
48    /// A basic set of flash properties.
49    pub fn basic() -> Self {
50        FlashFlags {
51            read: true,
52            program: true,
53            erase: true,
54            scramble: true,
55            ecc: true,
56            high_endurance: true,
57            ..Default::default()
58        }
59    }
60
61    /// A set of flash properties appropriate for the ROM_EXT region.
62    pub fn rom_ext() -> Self {
63        Self {
64            read: true,
65            program: true,
66            erase: true,
67            protect_when_active: true,
68            ..Default::default()
69        }
70    }
71
72    /// A set of flash properties appropriate for the owner firmware region.
73    pub fn firmware() -> Self {
74        Self {
75            read: true,
76            program: true,
77            erase: true,
78            scramble: true,
79            ecc: true,
80            protect_when_active: true,
81            ..Default::default()
82        }
83    }
84
85    /// A set of flash properties appropriate for the owner filesystem region.
86    pub fn filesystem() -> Self {
87        Self {
88            read: true,
89            program: true,
90            erase: true,
91            high_endurance: true,
92            ..Default::default()
93        }
94    }
95
96    /// A set of flash properties appropriate for owner info pages.
97    pub fn info_page() -> Self {
98        Self {
99            read: true,
100            program: true,
101            erase: true,
102            scramble: true,
103            ecc: true,
104            ..Default::default()
105        }
106    }
107}
108
109impl From<u64> for FlashFlags {
110    fn from(flags: u64) -> Self {
111        #[rustfmt::skip]
112        let value = Self {
113            // First 32-bit word: access/protection flags.
114            read:                 flags & 0xF == Self::TRUE,
115            program:              (flags >> 4) & 0xF == Self::TRUE,
116            erase:                (flags >> 8) & 0xF == Self::TRUE,
117            protect_when_active:  (flags >> 24) & 0xF == Self::TRUE,
118            lock:                 (flags >> 28) & 0xF == Self::TRUE,
119
120            // Second 32-bit word: flash properties.
121            scramble:             (flags >> 32) & 0xF == Self::TRUE,
122            ecc:                  (flags >> 36) & 0xF == Self::TRUE,
123            high_endurance:       (flags >> 40) & 0xF == Self::TRUE,
124        };
125        value
126    }
127}
128
129impl From<FlashFlags> for u64 {
130    fn from(flags: FlashFlags) -> u64 {
131        #[rustfmt::skip]
132        let value =
133            // First 32-bit word: access/protection flags.
134            if flags.read                 { FlashFlags::TRUE } else { FlashFlags::FALSE } |
135            if flags.program              { FlashFlags::TRUE } else { FlashFlags::FALSE } << 4 |
136            if flags.erase                { FlashFlags::TRUE } else { FlashFlags::FALSE } << 8 |
137            if flags.protect_when_active  { FlashFlags::TRUE } else { FlashFlags::FALSE } << 24 |
138            if flags.lock                 { FlashFlags::TRUE } else { FlashFlags::FALSE } << 28 |
139
140            // Second 32-bit word: flash properties.
141            if flags.scramble             { FlashFlags::TRUE } else { FlashFlags::FALSE } << 32 |
142            if flags.ecc                  { FlashFlags::TRUE } else { FlashFlags::FALSE } << 36 |
143            if flags.high_endurance       { FlashFlags::TRUE } else { FlashFlags::FALSE } << 40 ;
144        value
145    }
146}
147
148/// Describes a region to which a set of flags apply.
149#[derive(Debug, Default, Serialize, Deserialize, Annotate)]
150pub struct OwnerFlashRegion {
151    /// The start of the region (in pages).
152    pub start: u16,
153    /// The size of the region (in pages).
154    pub size: u16,
155    #[serde(flatten)]
156    pub flags: FlashFlags,
157}
158
159impl OwnerFlashRegion {
160    const SIZE: usize = 12;
161    pub fn new(start: u16, size: u16, flags: FlashFlags) -> Self {
162        Self { start, size, flags }
163    }
164    pub fn read(src: &mut impl Read, crypt: u64) -> Result<Self> {
165        let start = src.read_u16::<LittleEndian>()?;
166        let size = src.read_u16::<LittleEndian>()?;
167        let flags = FlashFlags::from(src.read_u64::<LittleEndian>()? ^ crypt);
168        Ok(Self { start, size, flags })
169    }
170    pub fn write(&self, dest: &mut impl Write, crypt: u64) -> Result<()> {
171        dest.write_u16::<LittleEndian>(self.start)?;
172        dest.write_u16::<LittleEndian>(self.size)?;
173        dest.write_u64::<LittleEndian>(u64::from(self.flags) ^ crypt)?;
174        Ok(())
175    }
176}
177
178/// Describes the overall flash configuration for data pages.
179#[derive(Debug, Serialize, Deserialize, Annotate)]
180pub struct OwnerFlashConfig {
181    /// Header identifying this struct.
182    #[serde(
183        skip_serializing_if = "GlobalFlags::not_debug",
184        default = "OwnerFlashConfig::default_header"
185    )]
186    pub header: TlvHeader,
187    /// A list of flash region configurations.
188    pub config: Vec<OwnerFlashRegion>,
189}
190
191impl Default for OwnerFlashConfig {
192    fn default() -> Self {
193        Self {
194            header: Self::default_header(),
195            config: Vec::new(),
196        }
197    }
198}
199
200impl OwnerFlashConfig {
201    const BASE_SIZE: usize = 8;
202
203    pub fn default_header() -> TlvHeader {
204        TlvHeader::new(TlvTag::FlashConfig, 0, "0.0")
205    }
206    pub fn basic() -> Self {
207        Self {
208            header: TlvHeader::new(TlvTag::FlashConfig, 0, "0.0"),
209            config: vec![
210                OwnerFlashRegion::new(0, 32, FlashFlags::rom_ext()),
211                OwnerFlashRegion::new(32, 192, FlashFlags::firmware()),
212                OwnerFlashRegion::new(224, 32, FlashFlags::filesystem()),
213                OwnerFlashRegion::new(256, 32, FlashFlags::rom_ext()),
214                OwnerFlashRegion::new(256 + 32, 192, FlashFlags::firmware()),
215                OwnerFlashRegion::new(256 + 224, 32, FlashFlags::filesystem()),
216            ],
217        }
218    }
219    pub fn read(src: &mut impl Read, header: TlvHeader) -> Result<Self> {
220        let config_len = (header.length - Self::BASE_SIZE) / OwnerFlashRegion::SIZE;
221        let mut config = Vec::new();
222        for i in 0..config_len {
223            let crypt = 0x1111_1111_1111_1111u64 * (i as u64);
224            config.push(OwnerFlashRegion::read(src, crypt)?)
225        }
226        Ok(Self { header, config })
227    }
228    pub fn write(&self, dest: &mut impl Write) -> Result<()> {
229        let header = TlvHeader::new(
230            TlvTag::FlashConfig,
231            Self::BASE_SIZE + self.config.len() * OwnerFlashRegion::SIZE,
232            "0.0",
233        );
234        header.write(dest)?;
235        for (i, config) in self.config.iter().enumerate() {
236            let crypt = 0x1111_1111_1111_1111u64 * (i as u64);
237            config.write(dest, crypt)?;
238        }
239        Ok(())
240    }
241}
242
243#[cfg(test)]
244mod test {
245    use super::*;
246    use crate::util::hexdump::{hexdump_parse, hexdump_string};
247
248    #[rustfmt::skip]
249    const OWNER_FLASH_CONFIG_BIN: &str =
250r#"00000000: 46 4c 53 48 2c 00 00 00 00 00 00 00 96 09 00 99  FLSH,...........
25100000010: 69 09 00 00 01 00 02 00 77 18 11 88 88 18 11 11  i.......w.......
25200000020: 03 00 05 00 44 24 22 bb 44 24 22 22              ....D$".D$""
253"#;
254
255    const OWNER_FLASH_CONFIG_JSON: &str = r#"{
256  config: [
257    {
258      start: 0,
259      size: 0,
260      read: true,
261      program: false,
262      erase: false,
263      scramble: false,
264      ecc: true,
265      high_endurance: false,
266      protect_when_active: false,
267      lock: false
268    },
269    {
270      start: 1,
271      size: 2,
272      read: true,
273      program: true,
274      erase: false,
275      scramble: false,
276      ecc: false,
277      high_endurance: false,
278      protect_when_active: false,
279      lock: false
280    },
281    {
282      start: 3,
283      size: 5,
284      read: true,
285      program: true,
286      erase: true,
287      scramble: true,
288      ecc: true,
289      high_endurance: true,
290      protect_when_active: false,
291      lock: false
292    }
293  ]
294}"#;
295
296    #[test]
297    fn test_owner_flash_config_write() -> Result<()> {
298        let ofr = OwnerFlashConfig {
299            header: TlvHeader::default(),
300            config: vec![
301                OwnerFlashRegion::new(
302                    0,
303                    0,
304                    FlashFlags {
305                        read: true,
306                        ecc: true,
307                        ..Default::default()
308                    },
309                ),
310                OwnerFlashRegion::new(
311                    1,
312                    2,
313                    FlashFlags {
314                        read: true,
315                        program: true,
316                        ..Default::default()
317                    },
318                ),
319                OwnerFlashRegion::new(3, 5, FlashFlags::basic()),
320            ],
321        };
322        let mut bin = Vec::new();
323        ofr.write(&mut bin)?;
324        eprintln!("{}", hexdump_string(&bin)?);
325        assert_eq!(hexdump_string(&bin)?, OWNER_FLASH_CONFIG_BIN);
326        Ok(())
327    }
328
329    #[test]
330    fn test_owner_flash_config_read() -> Result<()> {
331        let buf = hexdump_parse(OWNER_FLASH_CONFIG_BIN)?;
332        let mut cur = std::io::Cursor::new(&buf);
333        let header = TlvHeader::read(&mut cur)?;
334        let ofr = OwnerFlashConfig::read(&mut cur, header)?;
335        let doc = serde_annotate::serialize(&ofr)?.to_json5().to_string();
336        assert_eq!(doc, OWNER_FLASH_CONFIG_JSON);
337        Ok(())
338    }
339}