Software APIs
epmp_unittest.cc
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 
5 #include <array>
6 #include <cstring>
7 #include <vector>
8 
9 #include "gtest/gtest.h"
11 #include "sw/device/lib/base/csr.h"
12 #include "sw/device/silicon_creator/lib/base/mock_csr.h"
13 #include "sw/device/silicon_creator/lib/epmp_state.h"
14 #include "sw/device/silicon_creator/testing/rom_test.h"
15 
16 namespace epmp_unittest {
17 namespace {
18 
19 /**
20  * Representation of the hardware PMP control register state.
21  */
22 struct PmpCsrs {
23  /**
24  * The unpacked PMP configuration (`pmpNcfg`) registers, one entry for each
25  * PMP entry.
26  */
27  std::array<uint8_t, kEpmpNumRegions> pmpcfg = {};
28 
29  /**
30  * The PMP address registers, one for each PMP entry.
31  */
32  std::array<uint32_t, kEpmpNumRegions> pmpaddr = {};
33 
34  /**
35  * The Machine Security Configuration register.
36  */
37  uint64_t mseccfg = 0;
38 
39  /**
40  * Pack the individual (`pmpNcfg`) PMP entry configuration register
41  * values into hardware register (`pmpcfgN`) values.
42  *
43  * @returns An array representing the packed `pmpcfgN` values.
44  */
45  std::array<uint32_t, kEpmpNumRegions / 4> PackCfg() const {
46  // There are four 8-bit `pmpNcfg` values packed into each `pmpcfgN`
47  // register.
48  std::array<uint32_t, kEpmpNumRegions / 4> packed;
49  std::memcpy(&packed, &pmpcfg, sizeof(pmpcfg));
50  return packed;
51  }
52 
53  /**
54  * Generate a state that is a snapshot of the current PMP control register
55  * values.
56  *
57  * @returns An `epmp_state_t` value.
58  */
59  epmp_state_t State() const {
60  epmp_state_t state = {
61  .mseccfg = static_cast<uint32_t>(mseccfg),
62  };
63  std::memcpy(&state.pmpcfg, &pmpcfg, sizeof(pmpcfg));
64  std::memcpy(&state.pmpaddr, &pmpaddr, sizeof(pmpaddr));
65  return state;
66  }
67 };
68 
69 /**
70  * Map from PMP config register index to CSR_REG_PMPCFGN value.
71  */
72 constexpr std::array<uint32_t, 4> kPmpcfgMap = {
73  CSR_REG_PMPCFG0,
74  CSR_REG_PMPCFG1,
75  CSR_REG_PMPCFG2,
76  CSR_REG_PMPCFG3,
77 };
78 
79 /**
80  * Map from PMP address register index to CSR_REG_PMPADDRN value.
81  */
82 constexpr std::array<uint32_t, 16> kPmpaddrMap = {
83  CSR_REG_PMPADDR0, CSR_REG_PMPADDR1, CSR_REG_PMPADDR2, CSR_REG_PMPADDR3,
84  CSR_REG_PMPADDR4, CSR_REG_PMPADDR5, CSR_REG_PMPADDR6, CSR_REG_PMPADDR7,
85  CSR_REG_PMPADDR8, CSR_REG_PMPADDR9, CSR_REG_PMPADDR10, CSR_REG_PMPADDR11,
86  CSR_REG_PMPADDR12, CSR_REG_PMPADDR13, CSR_REG_PMPADDR14, CSR_REG_PMPADDR15,
87 };
88 
89 /**
90  * Helper function to call EXPECT_CSR_READ on all pmpcfg registers
91  * returning the values provided.
92  */
93 void ExpectPmpcfgRead(const PmpCsrs &expect) {
94  auto packed = expect.PackCfg();
95  for (size_t i = 0; i < packed.size(); ++i) {
96  EXPECT_CSR_READ(kPmpcfgMap.at(i), packed.at(i));
97  }
98 }
99 
100 /**
101  * Helper function to call EXPECT_CSR_READ on all pmpaddr registers
102  * returning the values provided.
103  */
104 void ExpectPmpaddrRead(const PmpCsrs &expect) {
105  for (size_t i = 0; i < expect.pmpaddr.size(); ++i) {
106  EXPECT_CSR_READ(kPmpaddrMap.at(i), expect.pmpaddr.at(i));
107  }
108 }
109 
110 /**
111  * Helper function to call EXPECT_CSR_READ on the mseccfg register
112  * returning the value provided.
113  */
114 void ExpectMseccfgRead(const PmpCsrs &expect) {
115  EXPECT_CSR_READ(CSR_REG_MSECCFG, static_cast<uint32_t>(expect.mseccfg));
116  EXPECT_CSR_READ(CSR_REG_MSECCFGH,
117  static_cast<uint32_t>(expect.mseccfg >> 32));
118 }
119 
120 /**
121  * Helper function to call EXPECT_CSR_READ as necessary for an
122  * internal `read_state` call with the expected values provided.
123  */
124 void ExpectReadState(const PmpCsrs &expect) {
125  ExpectPmpaddrRead(expect);
126  ExpectPmpcfgRead(expect);
127  ExpectMseccfgRead(expect);
128 }
129 
130 TEST(EpmpCheckTest, Default) {
131  mock_csr::MockCsr csr;
132 
133  ExpectReadState({});
134  memset((void *)&epmp_state, 0, sizeof(epmp_state));
135  EXPECT_EQ(epmp_state_check(), kErrorOk);
136 }
137 
138 TEST(EpmpCheckTest, ErrorPmpaddr) {
139  mock_csr::MockCsr csr;
140 
141  PmpCsrs bad;
142  bad.pmpaddr[15] ^= 1 << 31;
143 
144  ExpectReadState(bad);
145  memset((void *)&epmp_state, 0, sizeof(epmp_state));
146  EXPECT_EQ(epmp_state_check(), kErrorEpmpBadCheck);
147 }
148 
149 TEST(EpmpCheckTest, ErrorPmpcfg) {
150  mock_csr::MockCsr csr;
151 
152  PmpCsrs bad;
153  bad.pmpcfg[0] ^= 1;
154 
155  ExpectReadState(bad);
156  memset((void *)&epmp_state, 0, sizeof(epmp_state));
157  EXPECT_EQ(epmp_state_check(), kErrorEpmpBadCheck);
158 }
159 
160 TEST(EpmpCheckTest, ErrorMseccfg) {
161  mock_csr::MockCsr csr;
162 
163  PmpCsrs bad;
164  bad.mseccfg ^= 1;
165 
166  ExpectReadState(bad);
167  memset((void *)&epmp_state, 0, sizeof(epmp_state));
168  EXPECT_EQ(epmp_state_check(), kErrorEpmpBadCheck);
169 }
170 
171 TEST(EpmpTorTest, Entry0) {
172  mock_csr::MockCsr csr;
173  epmp_region_t region = {
174  .start = 0,
175  .end = 0,
176  };
177 
178  // Configure registers for entry 0 (base address implicitly 0).
179  PmpCsrs csrs;
180  csrs.pmpcfg[0] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
181  csrs.pmpaddr[0] = region.end >> 2;
182  memset((void *)&epmp_state, 0, sizeof(epmp_state));
183  epmp_state_configure_tor(0, region, kEpmpPermLockedReadOnly);
184 
185  ExpectReadState(csrs);
186  EXPECT_EQ(epmp_state_check(), kErrorOk);
187 }
188 
189 TEST(EpmpTorTest, Entry1) {
190  mock_csr::MockCsr csr;
191  epmp_region_t region = {
192  .start = 0x120,
193  .end = 0x140,
194  };
195 
196  // Configure registers for entry 1, with base address in entry 0.
197  PmpCsrs csrs;
198  csrs.pmpaddr[0] = region.start >> 2;
199  csrs.pmpcfg[1] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
200  csrs.pmpaddr[1] = region.end >> 2;
201  memset((void *)&epmp_state, 0, sizeof(epmp_state));
202  epmp_state_configure_tor(1, region, kEpmpPermLockedReadOnly);
203 
204  ExpectReadState(csrs);
205  EXPECT_EQ(epmp_state_check(), kErrorOk);
206 }
207 
208 TEST(EpmpTorTest, Entry15) {
209  mock_csr::MockCsr csr;
210  epmp_region_t region = {
211  .start = 0x120,
212  .end = 0x140,
213  };
214 
215  // Configure registers for entry 15, with base address in entry 14.
216  PmpCsrs csrs;
217  csrs.pmpaddr[14] = region.start >> 2;
218  csrs.pmpcfg[15] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
219  csrs.pmpaddr[15] = region.end >> 2;
220 
221  memset((void *)&epmp_state, 0, sizeof(epmp_state));
222  epmp_state_configure_tor(15, region, kEpmpPermLockedReadOnly);
223 
224  ExpectReadState(csrs);
225  EXPECT_EQ(epmp_state_check(), kErrorOk);
226 }
227 
228 TEST(EpmpTorTest, SharedAddress) {
229  mock_csr::MockCsr csr;
230 
231  // Adjacent regions for entries.
232  epmp_region_t region1 = {
233  .start = 0x120,
234  .end = 0x140,
235  };
236  epmp_region_t region2 = {
237  .start = 0x140,
238  .end = 0x180,
239  };
240 
241  // Configure registers for entries 1 and 2, with base address in entry 0.
242  PmpCsrs csrs;
243  csrs.pmpaddr[0] = region1.start >> 2;
244  csrs.pmpcfg[1] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R | EPMP_CFG_X;
245  csrs.pmpaddr[1] = region1.end >> 2;
246  csrs.pmpcfg[2] = EPMP_CFG_L | EPMP_CFG_A_TOR | EPMP_CFG_R;
247  csrs.pmpaddr[2] = region2.end >> 2;
248 
249  memset((void *)&epmp_state, 0, sizeof(epmp_state));
250  epmp_state_configure_tor(1, region1, kEpmpPermLockedReadExecute);
251  epmp_state_configure_tor(2, region2, kEpmpPermLockedReadOnly);
252 
253  ExpectReadState(csrs);
254  EXPECT_EQ(epmp_state_check(), kErrorOk);
255 }
256 
257 TEST(EpmpNa4Test, Entry0) {
258  mock_csr::MockCsr csr;
259  epmp_region_t region = {
260  .start = 0xF4,
261  .end = 0xF8,
262  };
263 
264  // Configure registers for entry 0.
265  PmpCsrs csrs;
266  csrs.pmpcfg[0] = EPMP_CFG_L | EPMP_CFG_A_NA4 | EPMP_CFG_R | EPMP_CFG_X;
267  csrs.pmpaddr[0] = region.start >> 2;
268 
269  memset((void *)&epmp_state, 0, sizeof(epmp_state));
270  epmp_state_configure_na4(0, region, kEpmpPermLockedReadExecute);
271 
272  ExpectReadState(csrs);
273  EXPECT_EQ(epmp_state_check(), kErrorOk);
274 }
275 
276 TEST(EpmpNa4Test, Entry15) {
277  mock_csr::MockCsr csr;
278  epmp_region_t region = {
279  .start = 0x0,
280  .end = 0x4,
281  };
282 
283  // Configure registers for entry 15.
284  PmpCsrs csrs;
285  csrs.pmpcfg[15] = EPMP_CFG_L | EPMP_CFG_A_NA4 | EPMP_CFG_R | EPMP_CFG_W;
286  csrs.pmpaddr[15] = region.start >> 2;
287 
288  memset((void *)&epmp_state, 0, sizeof(epmp_state));
289  epmp_state_configure_na4(15, region, kEpmpPermLockedReadWrite);
290 
291  ExpectReadState(csrs);
292  EXPECT_EQ(epmp_state_check(), kErrorOk);
293 }
294 
295 /**
296  * Encode a region using the NAPOT address mode. This does not
297  * do any validation of the values provided.
298  *
299  * Note: other address modes just require the address to be right
300  * shifted by 2.
301  */
302 uint32_t Napot(epmp_region_t region) {
303  return (region.start >> 2) | ((region.end - region.start - 1) >> 3);
304 }
305 
306 TEST(EpmpNapotTest, Entry0) {
307  mock_csr::MockCsr csr;
308  epmp_region_t region = {
309  .start = 0xFF00,
310  .end = 0xFF10,
311  };
312 
313  // Configure registers for entry 0.
314  PmpCsrs csrs;
315  csrs.pmpcfg[0] = EPMP_CFG_L | EPMP_CFG_A_NAPOT | EPMP_CFG_R | EPMP_CFG_X;
316  csrs.pmpaddr[0] = Napot(region);
317 
318  memset((void *)&epmp_state, 0, sizeof(epmp_state));
319  epmp_state_configure_napot(0, region, kEpmpPermLockedReadExecute);
320 
321  ExpectReadState(csrs);
322  EXPECT_EQ(epmp_state_check(), kErrorOk);
323 }
324 
325 TEST(EpmpNapotTest, Entry15) {
326  mock_csr::MockCsr csr;
327  epmp_region_t region = {
328  .start = 0x0,
329  .end = 0x8,
330  };
331 
332  // Configure registers for entry 15.
333  PmpCsrs csrs;
334  csrs.pmpcfg[15] = EPMP_CFG_L | EPMP_CFG_A_NAPOT | EPMP_CFG_R | EPMP_CFG_W;
335  csrs.pmpaddr[15] = Napot(region);
336 
337  memset((void *)&epmp_state, 0, sizeof(epmp_state));
338  epmp_state_configure_napot(15, region, kEpmpPermLockedReadWrite);
339 
340  ExpectReadState(csrs);
341  EXPECT_EQ(epmp_state_check(), kErrorOk);
342 }
343 
344 } // namespace
345 } // namespace epmp_unittest