|
| 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 | +// Verifies that the mcounteren_writable register properly locks the mcounteren |
| 6 | +// CSR and that the register is write-protected when locked. |
| 7 | + |
| 8 | +#include "sw/device/lib/base/mmio.h" |
| 9 | +#include "sw/device/lib/base/multibits.h" |
| 10 | +#include "sw/device/lib/testing/test_framework/check.h" |
| 11 | +#include "sw/device/lib/testing/test_framework/ottf_main.h" |
| 12 | + |
| 13 | +#include "hw/top/rv_core_ibex_regs.h" |
| 14 | +#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h" |
| 15 | + |
| 16 | +#define MCOUNTERS_DISABLE (0x0) |
| 17 | +#define MCOUNTERS_ENABLE (0x5) |
| 18 | + |
| 19 | +OTTF_DEFINE_TEST_CONFIG(); |
| 20 | + |
| 21 | +// Helper functions for accessing the mcounteren CSR |
| 22 | +static inline uint32_t csr_read_mcounteren(void) { |
| 23 | + uint32_t val; |
| 24 | + asm volatile("csrr %0, mcounteren" : "=r"(val)); |
| 25 | + return val; |
| 26 | +} |
| 27 | + |
| 28 | +static inline void csr_write_mcounteren(uint32_t val) { |
| 29 | + asm volatile("csrw mcounteren, %0" ::"r"(val)); |
| 30 | +} |
| 31 | + |
| 32 | +bool test_main(void) { |
| 33 | + uint32_t mcounteren_val; |
| 34 | + uint32_t mcounteren_writable_val; |
| 35 | + uint32_t regwen; |
| 36 | + |
| 37 | + mmio_region_t ibex_cfg = |
| 38 | + mmio_region_from_addr(TOP_EARLGREY_RV_CORE_IBEX_CFG_BASE_ADDR); |
| 39 | + |
| 40 | + // Initialize mcounteren to a known value for testing. |
| 41 | + csr_write_mcounteren(MCOUNTERS_DISABLE); |
| 42 | + |
| 43 | + // Check defaults |
| 44 | + // MCOUNTEREN_WRITABLE should default to kMultiBitBool4True. |
| 45 | + mcounteren_writable_val = |
| 46 | + mmio_region_read32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET); |
| 47 | + CHECK(mcounteren_writable_val == kMultiBitBool4True, |
| 48 | + "MCOUNTEREN_WRITABLE default should be MuBi4True (0x%x), got 0x%x", |
| 49 | + kMultiBitBool4True, mcounteren_writable_val); |
| 50 | + // REGWEN should be enabled by default. |
| 51 | + regwen = mmio_region_read32( |
| 52 | + ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REGWEN_REG_OFFSET); |
| 53 | + CHECK(regwen == 1, "MCOUNTEREN_WRITABLE_REGWEN default should be 1, got 0x%x", |
| 54 | + regwen); |
| 55 | + |
| 56 | + // Since MCOUNTEREN_WRITABLE is True, writes to mcounteren should succeed. |
| 57 | + csr_write_mcounteren(MCOUNTERS_ENABLE); |
| 58 | + mcounteren_val = csr_read_mcounteren(); |
| 59 | + CHECK(mcounteren_val == MCOUNTERS_ENABLE, |
| 60 | + "mcounteren should be writable when MCOUNTEREN_WRITABLE is True. " |
| 61 | + "Expected 0x%x, got 0x%x", |
| 62 | + MCOUNTERS_ENABLE, mcounteren_val); |
| 63 | + |
| 64 | + // Locking should succeed while REGWEN is enabled. |
| 65 | + mmio_region_write32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET, |
| 66 | + kMultiBitBool4False); |
| 67 | + mcounteren_writable_val = |
| 68 | + mmio_region_read32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET); |
| 69 | + CHECK(mcounteren_writable_val == kMultiBitBool4False, |
| 70 | + "MCOUNTEREN_WRITABLE write should succeed when unlocked, got 0x%x", |
| 71 | + mcounteren_writable_val); |
| 72 | + |
| 73 | + // Since MCOUNTEREN_WRITABLE is now False, writes to mcounteren should be |
| 74 | + // ignored. |
| 75 | + csr_write_mcounteren(MCOUNTERS_DISABLE); |
| 76 | + mcounteren_val = csr_read_mcounteren(); |
| 77 | + CHECK(mcounteren_val == MCOUNTERS_ENABLE, |
| 78 | + "mcounteren writes should be blocked when MCOUNTEREN_WRITABLE is " |
| 79 | + "False. Expected 0x%x, got 0x%x", |
| 80 | + MCOUNTERS_ENABLE, mcounteren_val); |
| 81 | + |
| 82 | + // Unlocking should succeed while REGWEN is enabled. |
| 83 | + mmio_region_write32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET, |
| 84 | + kMultiBitBool4True); |
| 85 | + mcounteren_writable_val = |
| 86 | + mmio_region_read32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET); |
| 87 | + CHECK(mcounteren_writable_val == kMultiBitBool4True, |
| 88 | + "MCOUNTEREN_WRITABLE write should succeed when unlocked, got 0x%x", |
| 89 | + mcounteren_writable_val); |
| 90 | + |
| 91 | + // Ensure mcounteren is writable again after restoring MCOUNTEREN_WRITABLE to |
| 92 | + // True. |
| 93 | + csr_write_mcounteren(MCOUNTERS_DISABLE); |
| 94 | + mcounteren_val = csr_read_mcounteren(); |
| 95 | + CHECK(mcounteren_val == MCOUNTERS_DISABLE, |
| 96 | + "mcounteren should be writable again after restoring config. Expected " |
| 97 | + "0x%x, got 0x%x", |
| 98 | + MCOUNTERS_DISABLE, mcounteren_val); |
| 99 | + |
| 100 | + // Enable mcounteren again. |
| 101 | + csr_write_mcounteren(MCOUNTERS_ENABLE); |
| 102 | + mcounteren_val = csr_read_mcounteren(); |
| 103 | + CHECK(mcounteren_val == MCOUNTERS_ENABLE, |
| 104 | + "mcounteren should be writable again after restoring config. Expected " |
| 105 | + "0x%x, got 0x%x", |
| 106 | + MCOUNTERS_ENABLE, mcounteren_val); |
| 107 | + |
| 108 | + // Lock again, which should succeed. |
| 109 | + mmio_region_write32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET, |
| 110 | + kMultiBitBool4False); |
| 111 | + mcounteren_writable_val = |
| 112 | + mmio_region_read32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET); |
| 113 | + CHECK(mcounteren_writable_val == kMultiBitBool4False, |
| 114 | + "MCOUNTEREN_WRITABLE write should succeed when unlocked, got 0x%x", |
| 115 | + mcounteren_writable_val); |
| 116 | + |
| 117 | + // Clear REGWEN to lock MCOUNTEREN_WRITABLE. |
| 118 | + mmio_region_write32(ibex_cfg, |
| 119 | + RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REGWEN_REG_OFFSET, 0); |
| 120 | + regwen = mmio_region_read32( |
| 121 | + ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REGWEN_REG_OFFSET); |
| 122 | + CHECK(regwen == 0, |
| 123 | + "MCOUNTEREN_WRITABLE_REGWEN should read 0 after clearing, got 0x%x", |
| 124 | + regwen); |
| 125 | + |
| 126 | + // Write to MCOUNTEREN_WRITABLE while locked should have no effect. |
| 127 | + mmio_region_write32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET, |
| 128 | + kMultiBitBool4True); |
| 129 | + mcounteren_writable_val = |
| 130 | + mmio_region_read32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET); |
| 131 | + CHECK(mcounteren_writable_val == kMultiBitBool4False, |
| 132 | + "MCOUNTEREN_WRITABLE should be unchanged when locked, got 0x%x", |
| 133 | + mcounteren_writable_val); |
| 134 | + |
| 135 | + // Since the attempt to flip MCOUNTEREN_WRITABLE to True was |
| 136 | + // ignored, writes to mcounteren should be ignored. |
| 137 | + csr_write_mcounteren(MCOUNTERS_DISABLE); |
| 138 | + mcounteren_val = csr_read_mcounteren(); |
| 139 | + CHECK(mcounteren_val == MCOUNTERS_ENABLE, |
| 140 | + "mcounteren writes should be blocked when MCOUNTEREN_WRITABLE is " |
| 141 | + "False. Expected 0x%x, got 0x%x", |
| 142 | + MCOUNTERS_ENABLE, mcounteren_val); |
| 143 | + |
| 144 | + // Attempt to re-enable REGWEN should fail |
| 145 | + mmio_region_write32(ibex_cfg, |
| 146 | + RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REGWEN_REG_OFFSET, 1); |
| 147 | + regwen = mmio_region_read32( |
| 148 | + ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REGWEN_REG_OFFSET); |
| 149 | + CHECK(regwen == 0, |
| 150 | + "MCOUNTEREN_WRITABLE_REGWEN should remain 0 after attempted " |
| 151 | + "re-enable, got 0x%x", |
| 152 | + regwen); |
| 153 | + |
| 154 | + // Write to MCOUNTEREN_WRITABLE while locked should have no effect. |
| 155 | + mmio_region_write32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET, |
| 156 | + kMultiBitBool4True); |
| 157 | + mcounteren_writable_val = |
| 158 | + mmio_region_read32(ibex_cfg, RV_CORE_IBEX_MCOUNTEREN_WRITABLE_REG_OFFSET); |
| 159 | + CHECK(mcounteren_writable_val == kMultiBitBool4False, |
| 160 | + "MCOUNTEREN_WRITABLE should be unchanged when locked, got 0x%x", |
| 161 | + mcounteren_writable_val); |
| 162 | + |
| 163 | + return true; |
| 164 | +} |
0 commit comments