Software APIs
epmp.h
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 #ifndef OPENTITAN_SW_DEVICE_LIB_RUNTIME_EPMP_H_
6 #define OPENTITAN_SW_DEVICE_LIB_RUNTIME_EPMP_H_
7 
8 #include <stdint.h>
9 
10 /**
11  * Enhanced Physical Memory Protection (EPMP).
12  *
13  * This library is intended for PMP entry configuration management in Machine
14  * mode (M-mode).
15  *
16  * Assumptions (should be initialized in assembly but can be verified using
17  * `epmp_check`):
18  * - Machine Mode Whitelist Policy is enabled (mseccfg.MMWP = 1)
19  * - Machine Mode Lockdown is not set (mseccfg.MML = 0)
20  *
21  * Typically this library will be used with Rule Locking Bypass
22  * (mseccfg.RLB = 1) enabled however this is not a hard requirement and RLB
23  * can be disabled using this library.
24  *
25  * Ibex PMP Documentation:
26  * https://ibex-core.readthedocs.io/en/latest/03_reference/pmp.html
27  */
28 
29 /**
30  * EPMP entry permissions.
31  *
32  * Entries configured with locked permissions may only be modified
33  * if Rule Locking Bypass is set (mseccfg.RLB = 1).
34  *
35  * When Machine Mode Lockdown is disabled (mseccfg.MML = 0) the
36  * combination R=0 W=1 is reserved. This is the assumed state and so
37  * it is not possible to set these values. The combination R=1 W=1
38  * X=1 has also been reserved by this library and may not be used.
39  *
40  * An entry may only be configured with unlocked permissions if the
41  * entry is also configured as OFF.
42  *
43  * Note: permissions may have different meanings when Machine Mode
44  * Lockdown (mseccfg.MML) is set.
45  */
46 typedef enum epmp_perm {
47  /**
48  * Unlocked with R=0, W=0 and X=0.
49  *
50  * Note: full access (R=1, W=1 and X=1) in Machine Mode. Only use
51  * in disabled (OFF) entries.
52  */
53  kEpmpPermUnlocked,
54 
55  /**
56  * Locked with R=0, W=0 and X=0.
57  */
58  kEpmpPermLockedNoAccess,
59 
60  /**
61  * Locked with R=0, W=0 and X=1.
62  */
63  kEpmpPermLockedExecuteOnly,
64 
65  /**
66  * Locked with R=1, W=0 and X=0.
67  */
68  kEpmpPermLockedReadOnly,
69 
70  /**
71  * Locked with R=1, W=0 and X=1.
72  */
73  kEpmpPermLockedReadExecute,
74 
75  /**
76  * Locked with R=1, W=1 and X=0.
77  */
78  kEpmpPermLockedReadWrite,
79 } epmp_perm_t;
80 
81 /**
82  * EPMP constants.
83  */
84 enum {
85  kEpmpNumRegions = 16,
86 };
87 
88 /**
89  * EPMP region specification.
90  *
91  * Provides the start and end addresses of a particular region. These addresses
92  * are byte-aligned (i.e. they are like regular pointers rather than encoded
93  * addresses).
94  *
95  * The `start` address is inclusive and the `end` address is exclusive.
96  */
97 typedef struct epmp_region {
98  uintptr_t start;
99  uintptr_t end;
100 } epmp_region_t;
101 
102 /**
103  * EPMP entry index.
104  */
105 typedef enum epmp_entry {
106  kEpmpEntry0,
107  kEpmpEntry1,
108  kEpmpEntry2,
109  kEpmpEntry3,
110  kEpmpEntry4,
111  kEpmpEntry5,
112  kEpmpEntry6,
113  kEpmpEntry7,
114  kEpmpEntry8,
115  kEpmpEntry9,
116  kEpmpEntry10,
117  kEpmpEntry11,
118  kEpmpEntry12,
119  kEpmpEntry13,
120  kEpmpEntry14,
121  kEpmpEntry15,
122 } epmp_entry_t;
123 
124 /**
125  * EPMP entry count.
126  */
127 typedef enum epmp_entry_count {
128  kEpmpEntryCount0,
129  kEpmpEntryCount1,
130  kEpmpEntryCount2,
131  kEpmpEntryCount3,
132  kEpmpEntryCount4,
133  kEpmpEntryCount5,
134  kEpmpEntryCount6,
135  kEpmpEntryCount7,
136  kEpmpEntryCount8,
137  kEpmpEntryCount9,
138  kEpmpEntryCount10,
139  kEpmpEntryCount11,
140  kEpmpEntryCount12,
141  kEpmpEntryCount13,
142  kEpmpEntryCount14,
143  kEpmpEntryCount15,
144  kEpmpEntryCount16,
145 } epmp_entry_count_t;
146 
147 /**
148  * EPMP generic status codes.
149  *
150  * These error codes can be used by any function. However if a function
151  * requires additional status codes, it must define a set of status codes to
152  * be used exclusively by such function.
153  */
154 typedef enum epmp_result {
155  kEpmpOk = 0,
156  kEpmpError,
157  kEpmpBadArg,
158 } epmp_result_t;
159 
160 /**
161  * Initialize the EPMP library.
162  *
163  * The current state of the EPMP CSRs will be read and stored inside the
164  * library. This function must be called before using the library. The
165  * library can only be initialized once.
166  *
167  * Note: it may be desirable to check the state is as expected. This
168  * can be done using `epmp_get_state`.
169  */
170 epmp_result_t epmp_init(void);
171 
172 /**
173  * Check that the actual EPMP state matches what the library expects
174  * it to be.
175  */
176 epmp_result_t epmp_check(void);
177 
178 /**
179  * Start an EPMP transaction.
180  *
181  * A new transaction will be initialized to expect the given number of
182  * entries to be reconfigured. Only one transaction may be in progress
183  * at a time. Each entry may only be reconfigured once per transaction.
184  * Call `epmp_transaction_end` to finalize the transaction and update
185  * the EPMP control registers.
186  *
187  * @param count Number of entries to be configured.
188  * @returns `epmp_result_t`
189  */
190 epmp_result_t epmp_transaction_start(epmp_entry_count_t count);
191 
192 /**
193  * Finish an EPMP transaction and update EPMP control registers.
194  *
195  * Check that the expected number of regions have been reconfigured and
196  * then update the EPMP control registers. Once this call returns the
197  * transaction will be complete and a new transaction should be
198  * started if further updates are required.
199  *
200  * This call will also perform checks equivalent to an `epmp_check`
201  * call before modifying any registers.
202  *
203  * Updates will occur in the following sequence:
204  *
205  * 1. pmpaddr0-pmpaddr15
206  * 2. pmpcfg0-pmpcfg3
207  *
208  * @param count Number of entries that should have been configured.
209  * @returns `epmp_result_t`
210  */
211 epmp_result_t epmp_transaction_end(epmp_entry_count_t count);
212 
213 /**
214  * EPMP configuration status codes.
215  */
216 typedef enum epmp_entry_configure_result {
217  kEpmpEntryConfigureOk = kEpmpOk,
218  kEpmpEntryConfigureError = kEpmpError,
219  kEpmpEntryConfigureBadArg = kEpmpBadArg,
220 
221  /**
222  * Invalid addresses provided for the selected address mode.
223  */
224  kEpmpEntryConfigureBadRegion,
225 
226  /**
227  * The transaction is in a bad state. This can be caused by:
228  * - Missing call the `epmp_transaction_start`.
229  * - An attempt to configure more entries than was initially specified.
230  * - A prior configuration attempt encountered an error.
231  */
232  kEpmpEntryConfigureBadTransaction,
233 
234  /**
235  * The requested entry is out of range or has previously been configured
236  * in this transaction. Each entry may only be configured once per
237  * transaction.
238  */
239  kEpmpEntryConfigureBadEntry,
240 
241  /**
242  * Encoding the entry would interfere with a different pre-existing entry.
243  *
244  * New entries will be rejected if they:
245  * - Modify the start or end address of an adjacent TOR entry.
246  * - Would result in an address being used in both a NAPOT/NA4 entry and a
247  * TOR entry.
248  */
249  kEpmpEntryConfigureConflict,
250 } epmp_entry_configure_result_t;
251 
252 /**
253  * Disable address matching for a PMP entry.
254  *
255  * The `region` start address will be encoded and configured as the address
256  * register for `entry`. The length of `region` must be 0 (i.e. the `region` end
257  * address must match the start address). If the following entry is configured
258  * using the TOR address mode then the `region` start address must match the
259  * pre-existing address.
260  *
261  * Note: addresses are encoded by dividing them by four. This matches the
262  * address encoding used by the TOR address mode.
263  *
264  * IMPORTANT: the `pmpaddr` and `pmpcfg` control registers will not be
265  * updated until `epmp_transaction_end` is called.
266  *
267  * Example:
268  *
269  * ...
270  * res0 = epmp_transaction_start(kEpmpEntryCount2);
271  * res1 = epmp_entry_configure_off(kEpmpEntry0,
272  * (epmp_region_t){0},
273  * kEpmpPermUnlocked);
274  * res2 = epmp_entry_configure_off(kEpmpEntry1,
275  * (epmp_region_t){ .start = 0x10, .end = 0x10 },
276  * kEpmpPermLockedNoAccess);
277  * res3 = epmp_transaction_end(kEpmpEntryCount2);
278  * ...
279  *
280  * Result:
281  *
282  * Entry | Value of `pmpaddr` | Value of `pmpcfg` |
283  * ======+====================+===================+
284  * 0 | 0x00 (0x00 >> 2) | 0b0000000 |
285  * 1 | 0x04 (0x10 >> 2) | 0b1000000 |
286  *
287  * @param entry Entry index to update (0 <= `entry` < `kEpmpNumRegions`)
288  * @param region Region to encode into `pmpaddr`.
289  * @param permissions Updated permissions to write to pmpcfg for `entry`.
290  * @return `epmp_entry_configure_result_t`.
291  */
292 epmp_entry_configure_result_t epmp_entry_configure_off(epmp_entry_t entry,
293  epmp_region_t region,
294  epmp_perm_t permissions);
295 
296 /**
297  * Configures a PMP entry using the Top Of Range (TOR) address mode.
298  *
299  * The `region` end address will be encoded and configured as the address
300  * register associated with `entry`.
301  *
302  * The `region` start address will be encoded and configured as the address
303  * register associated with the preceding entry if that entry is disabled (i.e.
304  * configured as OFF). If the preceding entry is configured using TOR mode then
305  * its pre-configured end address must match the `region` start address. This
306  * behavior allows adjacent regions configured using TOR to share an address,
307  * removing the need for a disabled entry between them. If configuring entry 0
308  * then the `region` start address must be 0. All other configurations will be
309  * rejected.
310  *
311  * IMPORTANT: the `pmpaddr` and `pmpcfg` control registers will not be
312  * updated until `epmp_transaction_end` is called.
313  *
314  * Example (two adjacent TOR regions + one standalone TOR region):
315  *
316  * ...
317  * res0 = epmp_transaction_start(kEpmpEntryCount4);
318  * res1 = epmp_entry_configure_tor(kEpmpEntry0,
319  * (epmp_region_t){ .start = 0x00, .end = 0x10 },
320  * kEpmpPermLockedReadOnly);
321  * res2 = epmp_entry_configure_tor(kEpmpEntry1,
322  * (epmp_region_t){ .start = 0x10, .end = 0x20 },
323  * kEpmpPermLockedReadOnly);
324  * res3 = epmp_entry_configure_off(kEpmpEntry2,
325  * (epmp_region_t){ .start = 0x00, .end = 0x00 },
326  * kEpmpPermLockedNoAccess);
327  * res4 = epmp_entry_configure_tor(kEpmpEntry3,
328  * (epmp_region_t){ .start = 0x30, .end = 0x40 },
329  * kEpmpPermLockedReadOnly);
330  * res5 = epmp_transaction_end(kEpmpEntryCount4);
331  * ...
332  *
333  * Result:
334  *
335  * Entry | Value of `pmpaddr` | Value of `pmpcfg` |
336  * ======+====================+===================+
337  * 0 | 0x04 (0x10 >> 2) | 0b1001001 |
338  * 1 | 0x08 (0x20 >> 2) | 0b1001001 |
339  * 2 | 0x0c (0x30 >> 2) | 0b1000000 |
340  * 3 | 0x10 (0x40 >> 2) | 0b1001001 |
341  *
342  * @param entry Entry index to update (0 <= `entry` < `kEpmpNumRegions`)
343  * @param region Region start and end addresses. Start address must be 0
344  * for `entry` 0.
345  * @param permissions Updated permissions to write to pmpcfg for `entry`.
346  * @return `epmp_entry_configure_result_t`.
347  */
348 epmp_entry_configure_result_t epmp_entry_configure_tor(epmp_entry_t entry,
349  epmp_region_t region,
350  epmp_perm_t permissions);
351 
352 /**
353  * Configures a PMP entry using the Naturally Aligned 4-byte (NA4) address mode.
354  *
355  * The `region` start address will be encoded and configured as the address in
356  * `entry`. The length of `region` must be exactly four bytes.
357  *
358  * This function will return `kEpmpEntryConfigureBadRegion` if
359  * the PMP granularity is greater than 0.
360  *
361  * IMPORTANT: the `pmpaddr` and `pmpcfg` control registers will not be
362  * updated until `epmp_transaction_end` is called.
363  *
364  * Example:
365  *
366  * ...
367  * res0 = epmp_transaction_start(kEpmpEntryCount1);
368  * res1 = epmp_entry_configure_na4(kEpmpEntry0,
369  * (epmp_region_t){ .start = 0x10, .end = 0x14 },
370  * kEpmpPermLockedReadOnly);
371  * res2 = epmp_transaction_end(kEpmpEntryCount1);
372  * ...
373  *
374  * Result:
375  *
376  * Entry | Value of `pmpaddr` | Value of `pmpcfg` |
377  * ======+====================+===================+
378  * 0 | 0x04 (0x10 >> 2) | 0b1010001 |
379  *
380  * @param entry Entry index to update (0 <= `entry` < `kEpmpNumRegions`)
381  * @param region Region start and end addresses. Must be 4 byte aligned.
382  * @param permissions Updated permissions to write to pmpcfg for `entry`.
383  * @return `epmp_entry_configure_result_t`.
384  */
385 epmp_entry_configure_result_t epmp_entry_configure_na4(epmp_entry_t entry,
386  epmp_region_t region,
387  epmp_perm_t permissions);
388 
389 /**
390  * Configures a PMP entry using the Naturally Aligned Power-Of-Two (NAPOT)
391  * address mode.
392  *
393  * The `region` will be encoded and configured as the address for `entry`.
394  * The length of `region` must be a power of two greater than four and the
395  * `region` (both start and end addresses) must also be aligned to the same
396  * power of two.
397  *
398  * If the PMP granularity (G) is greater than zero then the entire `region`
399  * must also be aligned to `2 ** (2 + G)`.
400  *
401  * IMPORTANT: the `pmpaddr` and `pmpcfg` control registers will not be
402  * updated until `epmp_transaction_end` is called.
403  *
404  * Example:
405  *
406  * ...
407  * res0 = epmp_transaction_start(kEpmpEntryCount2);
408  * res1 = epmp_entry_configure_napot(kEpmpEntry0,
409  * (epmp_region_t){ .start = 0x10, .end = 0x20 },
410  * kEpmpPermLockedReadOnly);
411  * res2 = epmp_entry_configure_napot(kEpmpEntry1,
412  * (epmp_region_t){ .start = 0x50, .end = 0x58 },
413  * kEpmpPermLockedReadWrite);
414  * res3 = epmp_transaction_end(kEpmpEntryCount2);
415  * ...
416  *
417  * Result:
418  *
419  * Entry | Value of `pmpaddr` | Value of `pmpcfg` |
420  * ======+===========================+===================+
421  * 0 | 0x41 ((0x10 >> 2) | 0b01) | 0b1011001 |
422  * 1 | 0x14 ((0x50 >> 2) | 0b00) | 0b1011011 |
423  *
424  * @param entry Entry index to update (0 <= `entry` < `kEpmpNumRegions`)
425  * @param region Region start and end addresses.
426  * @param permissions Updated permissions to write to pmpcfg for `entry`.
427  * @return `epmp_entry_configure_result_t`.
428  */
429 epmp_entry_configure_result_t epmp_entry_configure_napot(
430  epmp_entry_t entry, epmp_region_t region, epmp_perm_t permissions);
431 
432 /**
433  * Disable the Rule Locking Bypass (RLB) feature.
434  *
435  * When enabled (mseccfg.RLB = 1) the Rule Locking Bypass features allows
436  * locked entries to be modified. If any PMP entries are locked and RLB
437  * is disabled (mseccfg.RLB = 0) then it is no longer possible to enable
438  * RLB. RLB will disabled or an error will be returned.
439  *
440  * @return `epmp_result_t`
441  */
442 epmp_result_t epmp_disable_rule_locking_bypass(void);
443 
444 /**
445  * A copy of EPMP control register state for debugging purposes.
446  */
447 typedef struct epmp_debug_state {
448  /**
449  * PMP configuration values (pmp0cfg - pmp15cfg).
450  *
451  * These configuration values are stored in registers pmpcfg0 - pmpcfg3.
452  *
453  * Each 8-bit configuration value is encoded as follows:
454  *
455  * Layout:
456  *
457  * +---+-------+-------+---+---+---+
458  * | L | 0 | A | X | W | R |
459  * +---+-------+-------+---+---+---+
460  * 8 7 6 5 4 3 2 1 0
461  *
462  * Key:
463  *
464  * L = Locked
465  * A = Address-matching Mode (OFF=0, TOR=1, NA4=2, NAPOT=3)
466  * X = Executable
467  * W = Writeable
468  * R = Readable
469  *
470  * Note: the interpretation of these configuration bits depends on
471  * whether Machine Mode Lockdown (mseccfg.MML) is enabled or not.
472  * See the PMP Enhancements specification for more details.
473  */
474  uint8_t pmpcfg[kEpmpNumRegions];
475 
476  /**
477  * PMP address registers (pmpaddr0 - pmpaddr15).
478  *
479  * The way that address register values are interpreted differs
480  * depending on the address-matching mode (A) in the relevant pmpcfg
481  * register(s).
482  */
483  uintptr_t pmpaddr[kEpmpNumRegions];
484 
485  /**
486  * Machine Security Configuration register (mseccfg).
487  *
488  * +---...---+------+------+------+
489  * | 0 | RLB | MMWP | MML |
490  * +---...---+------+------+------+
491  * 63 3 2 1 0
492  *
493  * Key:
494  *
495  * RLB = Rule Locking Bypass
496  * MMWP = Machine Mode Whitelist Policy
497  * MML = Machine Mode Lockdown
498  *
499  * See the PMP Enhancements specification for more details.
500  */
501  uint64_t mseccfg;
503 
504 /**
505  * Get the current PMP configuration.
506  *
507  * Read all the PMP CSRs and return their values.
508  *
509  * @param[out] state Destination to write register values to.
510  * @return `epmp_result_t`
511  */
512 epmp_result_t epmp_debug_get_state(epmp_debug_state_t *state);
513 
514 /**
515  * Read the current staged register state.
516  *
517  * The staged state consists of values that have been configured as
518  * part of an ongoing transaction. It therefore only makes sense to call
519  * this function after `epmp_transaction_start` and before
520  * `epmp_transaction_end`.
521  *
522  * @param[out] state Destination to write register values to.
523  * @return `epmp_result_t`
524  */
525 epmp_result_t epmp_debug_get_transaction_state(epmp_debug_state_t *state);
526 
527 #endif // OPENTITAN_SW_DEVICE_LIB_RUNTIME_EPMP_H_