| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| #ifndef OPENTITAN_SW_DEVICE_LIB_RUNTIME_PMP_H_ | |
| #define OPENTITAN_SW_DEVICE_LIB_RUNTIME_PMP_H_ | |
| #include <stdbool.h> | |
| #include <stdint.h> | |
| #include "sw/device/lib/base/macros.h" | |
| /** | |
| * RISC-V PMP address matching modes. | |
| * | |
| * PMP address matching modes, how protected regions are formed and | |
| * implementation defined granularity information is described in: | |
| * "Volume II: RISC-V Privileged Architectures V20190608-Priv-MSU-Ratified", | |
| * "3.6.1 Physical Memory Protection CSRs", "Address Matching". | |
| * | |
| * PMP regions matching logic and prioritisation is described in the | |
| * "Priority and Matching Logic" paragraph of the section "3.6.1". | |
| * | |
| * Note that granularity is implementation defined (default G=0, making the | |
| * minimal PMP region size as 2^(G+2) = 4 bytes), and with G >= 1, the | |
| * NA4 is not available. Granularity also determines the minimal region size in | |
| * TOR and NAPOT modes. | |
| */ | |
| /** | |
| * RV32 PMP CSR definitions. | |
| * | |
| * The values for these defines have been taken from the: | |
| * "Volume II: RISC-V Privileged Architectures V20190608-Priv-MSU-Ratified", | |
| * "3.6.1 Physical Memory Protection CSRs". | |
| */ | |
| #define PMP_REGIONS_NUM 16 | |
| /** | |
| * Ibex PMP address granularity. | |
| * | |
| * Please see: | |
| * https://ibex-core.readthedocs.io/en/latest/03_reference/pmp.html#pmp-granularity | |
| */ | |
| #define PMP_GRANULARITY_IBEX 0 | |
| /** | |
| * RISC-V PMP address alignment and granularity. | |
| * | |
| * The values have been taken from the: | |
| * "Volume II: RISC-V Privileged Architectures V20190608-Priv-MSU-Ratified", | |
| * "3.6.1 Physical Memory Protection CSRs", section "Address Matching". | |
| * | |
| * "PMP mechanism supports regions as small as four bytes, platforms may | |
| * specify coarser PMP regions." | |
| */ | |
| #define PMP_ADDRESS_MIN_ALIGNMENT 4 | |
| #define PMP_ADDRESS_MIN_ALIGNMENT_NAPOT 8 | |
| #define PMP_ADDRESS_ALIGNMENT (4 << PMP_GRANULARITY_IBEX) | |
| #define PMP_ADDRESS_ALIGNMENT_MASK (PMP_ADDRESS_ALIGNMENT - 1) | |
| #define PMP_ADDRESS_ALIGNMENT_INVERTED_MASK (~PMP_ADDRESS_ALIGNMENT_MASK) | |
| #define PMP_ADDRESS_SHIFT 2 | |
| #define PMP_ADDRESS_SHIFT_NAPOT 3 | |
| /** | |
| * PMP region access permissions. | |
| * | |
| * PMP permissions are described in: | |
| * "Volume II: RISC-V Privileged Architectures V20190608-Priv-MSU-Ratified", | |
| * "3.6.1 Physical Memory Protection CSRs", "Locking and Privilege Mode". | |
| * | |
| * Note that "The combination R=0 and W=1 is reserved for future use". | |
| * | |
| * When a region is configured with `kPmpRegionLockUnlocked` then these | |
| * permissions only apply to RISC-V "U" and "S" privilege modes, and have no | |
| * effect in the "M" mode. | |
| */ | |
| typedef enum pmp_region_permissions { | |
| kPmpRegionPermissionsNone = 0, /**< No access permitted .*/ | |
| kPmpRegionPermissionsReadOnly, /**< Only read access. */ | |
| kPmpRegionPermissionsExecuteOnly, /**< Only execute access. */ | |
| kPmpRegionPermissionsReadExecute, /**< Read and execute access. */ | |
| kPmpRegionPermissionsReadWrite, /**< Read and write access. */ | |
| kPmpRegionPermissionsReadWriteExecute, /**< Read, write and execute access. */ | |
| } pmp_region_permissions_t; | |
| /** | |
| * PMP region locking. | |
| * | |
| * PMP locking is described in: | |
| * "Volume II: RISC-V Privileged Architectures V20190608-Priv-MSU-Ratified", | |
| * "3.6.1 Physical Memory Protection CSRs", "Locking and Privilege Mode". | |
| * | |
| * When a region is configured with `kPmpRegionLockLocked`, this region cannot | |
| * be accessed even by the machine "M" privilege code, and can be only unlocked | |
| * by the system reset. Additionally it also forces the | |
| * `pmp_region_permissions_t` access restrictions for the corresponding region | |
| * in machine "M" mode, which otherwise are ignored. | |
| */ | |
| typedef enum pmp_region_lock { | |
| kPmpRegionLockUnlocked = 0, /**< The region is unlocked. */ | |
| kPmpRegionLockLocked, /**< The region is locked. */ | |
| } pmp_region_lock_t; | |
| /** | |
| * PMP region index is used to identify one of `PMP_REGIONS_NUM` PMP regions. | |
| */ | |
| typedef uint32_t pmp_region_index_t; | |
| /** | |
| * PMP region configuration information. | |
| */ | |
| typedef struct pmp_region_config { | |
| pmp_region_lock_t lock; /**< Region lock for "M" privilege mode.*/ | |
| pmp_region_permissions_t permissions; /**< Region access permissions. */ | |
| } pmp_region_config_t; | |
| /** | |
| * PMP generic status codes. | |
| * | |
| * These error codes can be used by any function. However if a function | |
| * requires additional status codes, it must define a set of status codes to | |
| * be used exclusively by such function. | |
| */ | |
| typedef enum pmp_result { | |
| kPmpOk = 0, /**< Success. */ | |
| kPmpError, /**< General error. */ | |
| kPmpBadArg, /**< Input parameter is not valid. */ | |
| } pmp_result_t; | |
| /** | |
| * PMP region access and address configuration result. | |
| */ | |
| typedef enum pmp_region_configure_result { | |
| kPmpRegionConfigureOk = kPmpOk, | |
| kPmpRegionConfigureError = kPmpError, | |
| kPmpRegionConfigureBadArg = kPmpBadArg, | |
| /** | |
| * The requested region is invalid. | |
| */ | |
| kPmpRegionConfigureBadRegion, | |
| /** | |
| * The requested address is invalid. | |
| */ | |
| kPmpRegionConfigureBadAddress, | |
| /** | |
| * The requested region was not configured correctly. From the "Volume II: | |
| * RISC-V Privileged Architectures V20190608-Priv-MSU-Ratified": | |
| * "Implementations will not raise an exception on writes of unsupported | |
| * values to a WARL field. Implementations can return any legal value on the | |
| * read of a WARL field when the last write was of an illegal value, but the | |
| * legal value returned should deterministically depend on the illegal | |
| * written value and the value of the field prior to the write" | |
| */ | |
| kPmpRegionConfigureWarlError, | |
| } pmp_region_configure_result_t; | |
| /** | |
| * Disables PMP address matching. | |
| * | |
| * Address matching is disabled for `region`, however the corresponding | |
| * pmpaddr can still be used as a TOR range start address. For example, when | |
| * pmpcfgN is set to TOR, but pmpcfgN-1 is set to OFF, then pmpaddrN-1 can | |
| * be used as TOR range start address. | |
| * | |
| * Please see the PMP specification reference and general information at the | |
| * top of this header file. | |
| * | |
| * Note: this function will clear PMP `region` configuration. | |
| * | |
| * @param region PMP region to configure and set address for. | |
| * @param address System address. | |
| * @return `pmp_region_configure_result_t`. | |
| */ | |
| OT_WARN_UNUSED_RESULT | |
| pmp_region_configure_result_t pmp_region_configure_off( | |
| pmp_region_index_t region, uintptr_t address); | |
| /** | |
| * PMP region access and address NA4 configuration result. | |
| */ | |
| typedef enum pmp_region_configure_na4_result { | |
| kPmpRegionConfigureNa4Ok = kPmpRegionConfigureOk, | |
| kPmpRegionConfigureNa4Error = kPmpRegionConfigureError, | |
| kPmpRegionConfigureNa4BadArg = kPmpRegionConfigureBadArg, | |
| kPmpRegionConfigureNa4BadRegion = kPmpRegionConfigureBadRegion, | |
| kPmpRegionConfigureNa4BadAddress = kPmpRegionConfigureBadAddress, | |
| kPmpRegionConfigureNa4WarlError = kPmpRegionConfigureWarlError, | |
| /** | |
| * NA4 is not available with granularity "G" > 0. Please see: | |
| * "Volume II: RISC-V Privileged Architectures V20190608-Priv-MSU-Ratified", | |
| * "3.6.1 Physical Memory Protection CSRs", "Table 3.10" and section | |
| * "Address Matching". | |
| */ | |
| kPmpRegionConfigureNa4Unavailable, | |
| } pmp_region_configure_na4_result_t; | |
| /** | |
| * Configures PMP address matching to Naturally aligned four-byte (NA4). | |
| * | |
| * When pmpcfgN is set to NA4 then the protected region range is formed from | |
| * pmpaddrN and extends to the next 4 bytes. | |
| * | |
| * Please see the PMP specification reference and general information at the | |
| * top of this header file. | |
| * | |
| * @param region PMP region to configure and set the address for. | |
| * @param config PMP region configuration information. | |
| * @param address Range start system address. | |
| * @return `pmp_region_configure_na4_result_t`. | |
| */ | |
| OT_WARN_UNUSED_RESULT | |
| pmp_region_configure_na4_result_t pmp_region_configure_na4( | |
| pmp_region_index_t region, const pmp_region_config_t config, | |
| uintptr_t address); | |
| /** | |
| * PMP region access and address NAPOT configuration result. | |
| */ | |
| typedef enum pmp_region_configure_napot_result { | |
| kPmpRegionConfigureNapotOk = kPmpRegionConfigureOk, | |
| kPmpRegionConfigureNapotError = kPmpRegionConfigureError, | |
| kPmpRegionConfigureNapotBadArg = kPmpRegionConfigureBadArg, | |
| kPmpRegionConfigureNapotBadRegion = kPmpRegionConfigureBadRegion, | |
| kPmpRegionConfigureNapotBadAddress = kPmpRegionConfigureBadAddress, | |
| kPmpRegionConfigureNapotWarlError = kPmpRegionConfigureWarlError, | |
| /** | |
| * NAPOT size must be at least 8bytes and Power Of Two. NAPOT address | |
| * must be aligned to the size. Please see: | |
| * "Volume II: RISC-V Privileged Architectures V20190608-Priv-MSU-Ratified", | |
| * "3.6.1 Physical Memory Protection CSRs", "Table 3.10" and section | |
| * "Address Matching". | |
| */ | |
| kPmpRegionConfigureNapotBadSize, | |
| } pmp_region_configure_napot_result_t; | |
| /** | |
| * Configures PMP address matching to Naturally aligned power-of-two (NAPOT). | |
| * | |
| * Size ≥ 8 bytes. When pmpcfgN is set to NAPOT then the protected region range | |
| * is formed from a single encoded pmpaddrN address. NAPOT ranges make use of | |
| * the low-order bits of the associated address register to encode the size of | |
| * the range, please see the table "3.10" of the "Address Matching" section of | |
| * the specification mentioned above. | |
| * | |
| * Example: NAPOT encoded address of yyyy011 describes the | |
| * yyyy00000 .. yyyy11111 range. The index of the first "0", determines the | |
| * range size ( 2^(3 + index) ). | |
| * | |
| * Please see the PMP specification reference and general information at the | |
| * top of this header file. | |
| * | |
| * @param region PMP region to configure and set the address for. | |
| * @param config PMP region configuration information. | |
| * @param address Range start system address (must be aligned to `size`). | |
| * @param size Address range size (must be power-of-two). | |
| * @return `pmp_region_configure_napot_result_t`. | |
| */ | |
| OT_WARN_UNUSED_RESULT | |
| pmp_region_configure_napot_result_t pmp_region_configure_napot( | |
| pmp_region_index_t region, const pmp_region_config_t config, | |
| uintptr_t address, uint32_t size); | |
| /** | |
| * Configures PMP address matching to Top Of the Range (TOR). | |
| * | |
| * When pmpcfgN is set to TOR, the protected address range is formed from the | |
| * pmpaddrN-1 and pmpaddrN registers. Note that the combination of pmpcfgN set | |
| * to TOR and pmpcfgN-1 set to NAPOT does not form any meaningful range for TOR | |
| * addressing mode. pmpcfgN TOR mode will take the bottom of the range | |
| * pmpaddrN-1 address as is, without decoding the NAPOT address. | |
| * | |
| * Please see the PMP specification reference and general information at the | |
| * top of this header file. | |
| * | |
| * @param region_end PMP region to configure and set the address for. | |
| * @param config PMP region configuration information. | |
| * @param address_start Bottom of the range system address. Must be | |
| * 0 for the "0" region. | |
| * @param address_end Top of the range system address. | |
| * @return `pmp_region_configure_result_t`. | |
| */ | |
| OT_WARN_UNUSED_RESULT | |
| pmp_region_configure_result_t pmp_region_configure_tor( | |
| pmp_region_index_t region_end, const pmp_region_config_t config, | |
| uintptr_t address_start, uintptr_t address_end); | |
| /** | |
| * Queries the lock status for the requested region. | |
| * | |
| * @param region PMP region to query the lock information for. | |
| * @param lock State of the PMP region (locked/unlocked). | |
| * @return `pmp_region_configure_result_t`. | |
| */ | |
| OT_WARN_UNUSED_RESULT | |
| pmp_region_configure_result_t pmp_region_lock_status_get( | |
| pmp_region_index_t region, pmp_region_lock_t *lock); | |
| #endif // OPENTITAN_SW_DEVICE_LIB_RUNTIME_PMP_H_ |