opentitanlib/util/
usr_access.rs1use anyhow::Result;
6use serde::{Deserialize, Serialize};
7use std::convert::TryInto;
8use thiserror::Error;
9
10use chrono::{Datelike, Timelike, Utc};
11use crc::Crc;
12
13use crate::util::bitfield::BitField;
14
15const SYNC_WORD: [u8; 4] = [0xAA, 0x99, 0x55, 0x66];
16const REG_ADDR_USR_ACCESS: u32 = 0x0d;
17const REG_ADDR_CRC: u32 = 0x00;
18const NOOP: [u8; 4] = [0x20, 0x00, 0x00, 0x00];
19const TYPE_FIELD: BitField = BitField {
20 offset: 29,
21 size: 3,
22};
23const OP_FIELD: BitField = BitField {
24 offset: 27,
25 size: 2,
26};
27const TYPE1_ADDR_FIELD: BitField = BitField {
28 offset: 13,
29 size: 14,
30};
31const TYPE1_WORDS_FIELD: BitField = BitField {
32 offset: 0,
33 size: 11,
34};
35const TYPE2_WORDS_FIELD: BitField = BitField {
36 offset: 0,
37 size: 27,
38};
39
40#[derive(Error, Debug, Serialize, Deserialize)]
41pub enum Error {
42 #[error("Command {0:#x?} not found in bitstream")]
43 CommandNotFound(u32),
44}
45
46#[derive(PartialEq)]
47enum BitstreamOp {
48 Nop = 0,
49 Read = 1,
50 Write = 2,
51 Reserved,
52}
53
54struct BitstreamTypeOnePacketHeader {
55 value: u32,
57 offset: usize,
59}
60
61impl BitstreamTypeOnePacketHeader {
62 fn op(&self) -> BitstreamOp {
63 match OP_FIELD.extract(self.value) {
64 0 => BitstreamOp::Nop,
65 1 => BitstreamOp::Read,
66 2 => BitstreamOp::Write,
67 _ => BitstreamOp::Reserved,
68 }
69 }
70
71 fn address(&self) -> u32 {
72 TYPE1_ADDR_FIELD.extract(self.value)
73 }
74
75 fn data_size(&self) -> usize {
76 usize::try_from(4 * TYPE1_WORDS_FIELD.extract(self.value)).unwrap()
77 }
78}
79
80struct BitstreamTypeOneHeaders<'a> {
82 bitstream: &'a [u8],
83 offset: usize,
84 have_sync: bool,
85}
86
87impl<'a> BitstreamTypeOneHeaders<'a> {
88 fn from_bitstream(bitstream: &[u8]) -> BitstreamTypeOneHeaders<'_> {
89 BitstreamTypeOneHeaders {
90 bitstream,
91 offset: 0,
92 have_sync: false,
93 }
94 }
95
96 fn find_next_sync(&self) -> Option<usize> {
100 self.bitstream[self.offset..]
101 .windows(4)
102 .position(|word| *word == SYNC_WORD)
103 .map(|offset| self.offset + offset)
104 }
105}
106
107impl<'a> std::iter::Iterator for BitstreamTypeOneHeaders<'a> {
108 type Item = BitstreamTypeOnePacketHeader;
109
110 fn next(&mut self) -> Option<Self::Item> {
114 while self.offset + 4 < self.bitstream.len() {
115 if !self.have_sync {
116 if let Some(sync_offset) = self.find_next_sync() {
117 self.offset = sync_offset + 4;
118 self.have_sync = true;
119 } else {
120 self.offset = self.bitstream.len();
121 return None;
122 }
123 }
124 let header = &self.bitstream[self.offset..self.offset + 4];
125 let header = u32::from_be_bytes(header.try_into().ok()?);
126 let header = match TYPE_FIELD.extract(header) {
127 1 => {
128 let x = BitstreamTypeOnePacketHeader {
129 value: header,
130 offset: self.offset,
131 };
132 self.offset += 4 + x.data_size();
133 Some(x)
134 }
135 2 => {
136 let data_bytes = 4 * TYPE2_WORDS_FIELD.extract(header);
137 self.offset += usize::try_from(4 + data_bytes).unwrap();
138 None
139 }
140 _ => {
141 log::info!("Bitstream lost sync at {:#x}", self.offset);
142 self.have_sync = false;
143 None
144 }
145 };
146 if header.is_some() {
147 return header;
148 }
149 }
150 None
151 }
152}
153
154fn cmd_from_parts(op: BitstreamOp, address: u32) -> u32 {
157 TYPE_FIELD.emplace(1)
158 | OP_FIELD.emplace(op as u32)
159 | TYPE1_ADDR_FIELD.emplace(address)
160 | TYPE1_WORDS_FIELD.emplace(1)
161}
162
163fn remove_crc(bitstream: &mut [u8]) {
179 let crc_headers: Vec<BitstreamTypeOnePacketHeader> =
180 BitstreamTypeOneHeaders::from_bitstream(bitstream)
181 .filter(|x| (x.op() == BitstreamOp::Write) && (x.address() == REG_ADDR_CRC))
182 .collect();
183 for header in crc_headers.iter() {
184 log::info!(
185 "Replaced WRITE_CRC_REG command at {:#x} with NOOP",
186 header.offset
187 );
188 bitstream[header.offset..header.offset + 4].copy_from_slice(&NOOP);
189 bitstream[header.offset + 4..header.offset + 8].copy_from_slice(&NOOP);
190 }
191}
192
193pub fn usr_access_get(bitstream: &[u8]) -> Result<u32> {
194 if let Some(header) = BitstreamTypeOneHeaders::from_bitstream(bitstream)
195 .find(|x| (x.op() == BitstreamOp::Write) && (x.address() == REG_ADDR_USR_ACCESS))
196 {
197 let operand = &bitstream[header.offset + 4..header.offset + 8];
198 let usr_access = u32::from_be_bytes(operand.try_into()?);
199 log::info!("Bitstream file USR_ACCESS value: {:#x}", usr_access);
200 return Ok(usr_access);
201 }
202 Err(Error::CommandNotFound(cmd_from_parts(BitstreamOp::Write, REG_ADDR_USR_ACCESS)).into())
203}
204
205pub fn usr_access_timestamp() -> u32 {
216 let now = Utc::now();
217 now.day() << 27
218 | now.month() << 23
219 | now.year_ce().1.checked_sub(2000u32).unwrap() << 17
220 | now.hour() << 12
221 | now.minute() << 6
222 | now.second()
223}
224
225pub fn usr_access_crc32(bitstream: &mut [u8]) -> Result<u32> {
227 usr_access_set(bitstream, 0)?; Ok(Crc::<u32>::new(&crc::CRC_32_ISO_HDLC).checksum(bitstream))
229}
230
231pub fn usr_access_set(bitstream: &mut [u8], val: u32) -> Result<()> {
232 if let Some(header) = BitstreamTypeOneHeaders::from_bitstream(bitstream)
233 .find(|x| (x.op() == BitstreamOp::Write) && (x.address() == REG_ADDR_USR_ACCESS))
234 {
235 let operand = &mut bitstream[header.offset + 4..header.offset + 8];
236 let usr_access = u32::from_be_bytes(operand.try_into()?);
237 log::info!("Bitstream file old USR_ACCESS value: {:#x}", usr_access);
238 operand.copy_from_slice(&val.to_be_bytes());
239 log::info!("Bitstream file new USR_ACCESS value: {:#x}", val);
240 remove_crc(bitstream);
241 return Ok(());
242 }
243 Err(Error::CommandNotFound(cmd_from_parts(BitstreamOp::Write, REG_ADDR_USR_ACCESS)).into())
244}