1use anyhow::{Result, ensure};
6use thiserror::Error;
7
8pub mod constants {
9 pub const EPMP_CFG_LOCKED: u8 = 1 << 7;
11 pub const EPMP_CFG_EXEC: u8 = 1 << 2;
13 pub const EPMP_CFG_WRITE: u8 = 1 << 1;
15 pub const EPMP_CFG_READ: u8 = 1 << 0;
17 pub const EPMP_CFG_UNLOCKED: u8 = 0;
19 pub const EPMP_CFG_LOCKED_NONE: u8 = EPMP_CFG_LOCKED;
20 pub const EPMP_CFG_LRO: u8 = EPMP_CFG_LOCKED | EPMP_CFG_READ;
21 pub const EPMP_CFG_LRW: u8 = EPMP_CFG_LOCKED | EPMP_CFG_READ | EPMP_CFG_WRITE;
22 pub const EPMP_CFG_LRX: u8 = EPMP_CFG_LOCKED | EPMP_CFG_READ | EPMP_CFG_EXEC;
23 pub const EPMP_CFG_LRWX: u8 = EPMP_CFG_LOCKED | EPMP_CFG_READ | EPMP_CFG_WRITE | EPMP_CFG_EXEC;
24
25 pub const EPMP_CFG_RO: u8 = EPMP_CFG_READ;
26 pub const EPMP_CFG_RW: u8 = EPMP_CFG_READ | EPMP_CFG_WRITE;
27 pub const EPMP_CFG_RX: u8 = EPMP_CFG_READ | EPMP_CFG_EXEC;
28 pub const EPMP_CFG_RWX: u8 = EPMP_CFG_READ | EPMP_CFG_WRITE | EPMP_CFG_EXEC;
29
30 pub const EPMP_MSECCFG_MML: u32 = 1;
32 pub const EPMP_MSECCFG_MMWP: u32 = 2;
35 pub const EPMP_MSECCFG_RLB: u32 = 4;
37}
38
39#[derive(Debug, Error)]
40pub enum EpmpError {
41 #[error("Invalid raw EPMP length (cfg={0}, addr={1})")]
42 InvalidLength(usize, usize),
43}
44
45#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
47#[repr(u8)]
48pub enum EpmpRegionKind {
49 #[default]
50 Off = 0,
51 Tor = 1,
52 Na4 = 2,
53 Napot = 3,
54}
55
56const EPMP_ADDR_SHIFT: u8 = 3;
57const EPMP_ADDR_MASK: u8 = 3;
58
59#[derive(Clone, Debug, Default, PartialEq, Eq)]
61pub struct EpmpAddressRange(pub u64, pub u64);
62
63impl EpmpAddressRange {
64 pub fn start(&self) -> u64 {
65 self.0
66 }
67 pub fn end(&self) -> u64 {
68 self.1
69 }
70}
71
72#[derive(Clone, Debug, Default, PartialEq, Eq)]
74pub struct EpmpEntry {
75 pub cfg: u8,
77 pub kind: EpmpRegionKind,
79 pub range: EpmpAddressRange,
81}
82
83#[derive(Clone, Debug, Default)]
84pub struct Epmp {
85 pub entry: Vec<EpmpEntry>,
86}
87
88impl Epmp {
89 pub fn from_raw_rv32(pmpcfg: &[u32], pmpaddr: &[u32]) -> Result<Self> {
91 ensure!(
92 pmpcfg.len() * 4 == pmpaddr.len(),
93 EpmpError::InvalidLength(pmpcfg.len(), pmpaddr.len())
94 );
95 let pmpcfg = pmpcfg
96 .iter()
97 .flat_map(|word| word.to_le_bytes())
98 .collect::<Vec<u8>>();
99 let mut entry = Vec::with_capacity(pmpaddr.len());
100 for i in 0..pmpaddr.len() {
101 let region_kind = match (pmpcfg[i] >> EPMP_ADDR_SHIFT) & EPMP_ADDR_MASK {
102 0 => EpmpRegionKind::Off,
103 1 => EpmpRegionKind::Tor,
104 2 => EpmpRegionKind::Na4,
105 3 => EpmpRegionKind::Napot,
106 _ => unreachable!(),
107 };
108 let addr = match region_kind {
109 EpmpRegionKind::Off => EpmpAddressRange(0, 0),
110 EpmpRegionKind::Tor => {
111 if i == 0 {
112 EpmpAddressRange(0, (pmpaddr[i] as u64) << 2)
113 } else {
114 EpmpAddressRange((pmpaddr[i - 1] as u64) << 2, (pmpaddr[i] as u64) << 2)
115 }
116 }
117 EpmpRegionKind::Na4 => {
118 EpmpAddressRange((pmpaddr[i] as u64) << 2, ((pmpaddr[i] as u64) << 2) + 4)
119 }
120 EpmpRegionKind::Napot => {
121 let size = pmpaddr[i].trailing_ones();
122 let addr = ((pmpaddr[i] & (pmpaddr[i] + 1)) as u64) << 2;
123 let length = (1 << (size + 3)) as u64;
124 EpmpAddressRange(addr, addr + length)
125 }
126 };
127 entry.push(EpmpEntry {
128 cfg: pmpcfg[i] & !(EPMP_ADDR_MASK << EPMP_ADDR_SHIFT),
129 kind: region_kind,
130 range: addr,
131 });
132 }
133 Ok(Epmp { entry })
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::constants::*;
140 use super::*;
141
142 const PMPCFG: [u32; 4] = [0x998d00, 0x1f998d, 0x8b000000, 0x9b909f00];
147 const PMPADDR: [u32; 16] = [
148 0x2000, 0x3411, 0x2fff, 0x8000100, 0x8001203, 0x801ffff, 0x2fff, 0x0, 0x0, 0x0, 0x10000000,
149 0x14000000, 0x0, 0x41ff, 0x4007000, 0x4003fff,
150 ];
151
152 #[test]
153 fn test_epmp_decode() -> Result<()> {
154 let epmp = Epmp::from_raw_rv32(&PMPCFG, &PMPADDR)?;
155
156 assert!(matches!(
159 epmp.entry[0],
160 EpmpEntry {
161 cfg: 0,
162 kind: EpmpRegionKind::Off,
163 range: EpmpAddressRange(0, 0)
164 }
165 ));
166 assert!(matches!(
168 epmp.entry[1],
169 EpmpEntry {
170 cfg: EPMP_CFG_LRX,
171 kind: EpmpRegionKind::Tor,
172 range: EpmpAddressRange(0x8000, 0xd044)
173 }
174 ));
175 assert!(matches!(
177 epmp.entry[2],
178 EpmpEntry {
179 cfg: EPMP_CFG_LRO,
180 kind: EpmpRegionKind::Napot,
181 range: EpmpAddressRange(0x8000, 0x10000)
182 }
183 ));
184 assert!(matches!(
187 epmp.entry[6],
188 EpmpEntry {
189 cfg: EPMP_CFG_RWX,
190 kind: EpmpRegionKind::Napot,
191 range: EpmpAddressRange(0x8000, 0x10000)
192 }
193 ));
194
195 assert!(matches!(
197 epmp.entry[13],
198 EpmpEntry {
199 cfg: EPMP_CFG_LRWX,
200 kind: EpmpRegionKind::Napot,
201 range: EpmpAddressRange(0x10000, 0x11000)
202 }
203 ));
204
205 eprintln!("entry14 = {:?}", epmp.entry[14]);
207 assert!(matches!(
208 epmp.entry[14],
209 EpmpEntry {
210 cfg: EPMP_CFG_LOCKED_NONE,
211 kind: EpmpRegionKind::Na4,
212 range: EpmpAddressRange(0x1001_c000, 0x1001_c004)
213 }
214 ));
215 assert!(matches!(
217 epmp.entry[15],
218 EpmpEntry {
219 cfg: EPMP_CFG_LRW,
220 kind: EpmpRegionKind::Napot,
221 range: EpmpAddressRange(0x1000_0000, 0x1002_0000)
222 }
223 ));
224 Ok(())
225 }
226}