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