1 /* 2 * QEMU RISC-V PMP (Physical Memory Protection) 3 * 4 * Author: Daire McNamara, daire.mcnamara@emdalo.com 5 * Ivan Griffin, ivan.griffin@emdalo.com 6 * 7 * This provides a RISC-V Physical Memory Protection implementation 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms and conditions of the GNU General Public License, 11 * version 2 or later, as published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 * more details. 17 * 18 * You should have received a copy of the GNU General Public License along with 19 * this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 /* 23 * PMP (Physical Memory Protection) is as-of-yet unused and needs testing. 24 */ 25 26 #include "qemu/osdep.h" 27 #include "qemu/log.h" 28 #include "qapi/error.h" 29 #include "cpu.h" 30 #include "qemu-common.h" 31 32 #ifndef CONFIG_USER_ONLY 33 34 #define RISCV_DEBUG_PMP 0 35 #define PMP_DEBUG(fmt, ...) \ 36 do { \ 37 if (RISCV_DEBUG_PMP) { \ 38 qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ 39 } \ 40 } while (0) 41 42 static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, 43 uint8_t val); 44 static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index); 45 static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index); 46 47 /* 48 * Accessor method to extract address matching type 'a field' from cfg reg 49 */ 50 static inline uint8_t pmp_get_a_field(uint8_t cfg) 51 { 52 uint8_t a = cfg >> 3; 53 return a & 0x3; 54 } 55 56 /* 57 * Check whether a PMP is locked or not. 58 */ 59 static inline int pmp_is_locked(CPURISCVState *env, uint32_t pmp_index) 60 { 61 62 if (env->pmp_state.pmp[pmp_index].cfg_reg & PMP_LOCK) { 63 return 1; 64 } 65 66 /* Top PMP has no 'next' to check */ 67 if ((pmp_index + 1u) >= MAX_RISCV_PMPS) { 68 return 0; 69 } 70 71 /* In TOR mode, need to check the lock bit of the next pmp 72 * (if there is a next) 73 */ 74 const uint8_t a_field = 75 pmp_get_a_field(env->pmp_state.pmp[pmp_index + 1].cfg_reg); 76 if ((env->pmp_state.pmp[pmp_index + 1u].cfg_reg & PMP_LOCK) && 77 (PMP_AMATCH_TOR == a_field)) { 78 return 1; 79 } 80 81 return 0; 82 } 83 84 /* 85 * Count the number of active rules. 86 */ 87 static inline uint32_t pmp_get_num_rules(CPURISCVState *env) 88 { 89 return env->pmp_state.num_rules; 90 } 91 92 /* 93 * Accessor to get the cfg reg for a specific PMP/HART 94 */ 95 static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) 96 { 97 if (pmp_index < MAX_RISCV_PMPS) { 98 return env->pmp_state.pmp[pmp_index].cfg_reg; 99 } 100 101 return 0; 102 } 103 104 105 /* 106 * Accessor to set the cfg reg for a specific PMP/HART 107 * Bounds checks and relevant lock bit. 108 */ 109 static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) 110 { 111 if (pmp_index < MAX_RISCV_PMPS) { 112 if (!pmp_is_locked(env, pmp_index)) { 113 env->pmp_state.pmp[pmp_index].cfg_reg = val; 114 pmp_update_rule(env, pmp_index); 115 } else { 116 qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n"); 117 } 118 } else { 119 qemu_log_mask(LOG_GUEST_ERROR, 120 "ignoring pmpcfg write - out of bounds\n"); 121 } 122 } 123 124 static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) 125 { 126 /* 127 aaaa...aaa0 8-byte NAPOT range 128 aaaa...aa01 16-byte NAPOT range 129 aaaa...a011 32-byte NAPOT range 130 ... 131 aa01...1111 2^XLEN-byte NAPOT range 132 a011...1111 2^(XLEN+1)-byte NAPOT range 133 0111...1111 2^(XLEN+2)-byte NAPOT range 134 1111...1111 Reserved 135 */ 136 if (a == -1) { 137 *sa = 0u; 138 *ea = -1; 139 return; 140 } else { 141 target_ulong t1 = ctz64(~a); 142 target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 2; 143 target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1; 144 *sa = base; 145 *ea = base + range; 146 } 147 } 148 149 150 /* Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea' 151 * end address values. 152 * This function is called relatively infrequently whereas the check that 153 * an address is within a pmp rule is called often, so optimise that one 154 */ 155 static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index) 156 { 157 int i; 158 159 env->pmp_state.num_rules = 0; 160 161 uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg; 162 target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg; 163 target_ulong prev_addr = 0u; 164 target_ulong sa = 0u; 165 target_ulong ea = 0u; 166 167 if (pmp_index >= 1u) { 168 prev_addr = env->pmp_state.pmp[pmp_index - 1].addr_reg; 169 } 170 171 switch (pmp_get_a_field(this_cfg)) { 172 case PMP_AMATCH_OFF: 173 sa = 0u; 174 ea = -1; 175 break; 176 177 case PMP_AMATCH_TOR: 178 sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ 179 ea = (this_addr << 2) - 1u; 180 break; 181 182 case PMP_AMATCH_NA4: 183 sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ 184 ea = (this_addr + 4u) - 1u; 185 break; 186 187 case PMP_AMATCH_NAPOT: 188 pmp_decode_napot(this_addr, &sa, &ea); 189 break; 190 191 default: 192 sa = 0u; 193 ea = 0u; 194 break; 195 } 196 197 env->pmp_state.addr[pmp_index].sa = sa; 198 env->pmp_state.addr[pmp_index].ea = ea; 199 200 for (i = 0; i < MAX_RISCV_PMPS; i++) { 201 const uint8_t a_field = 202 pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); 203 if (PMP_AMATCH_OFF != a_field) { 204 env->pmp_state.num_rules++; 205 } 206 } 207 } 208 209 static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr) 210 { 211 int result = 0; 212 213 if ((addr >= env->pmp_state.addr[pmp_index].sa) 214 && (addr <= env->pmp_state.addr[pmp_index].ea)) { 215 result = 1; 216 } else { 217 result = 0; 218 } 219 220 return result; 221 } 222 223 224 /* 225 * Public Interface 226 */ 227 228 /* 229 * Check if the address has required RWX privs to complete desired operation 230 */ 231 bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, 232 target_ulong size, pmp_priv_t privs) 233 { 234 int i = 0; 235 int ret = -1; 236 target_ulong s = 0; 237 target_ulong e = 0; 238 pmp_priv_t allowed_privs = 0; 239 240 /* Short cut if no rules */ 241 if (0 == pmp_get_num_rules(env)) { 242 return true; 243 } 244 245 /* 1.10 draft priv spec states there is an implicit order 246 from low to high */ 247 for (i = 0; i < MAX_RISCV_PMPS; i++) { 248 s = pmp_is_in_range(env, i, addr); 249 e = pmp_is_in_range(env, i, addr + size); 250 251 /* partially inside */ 252 if ((s + e) == 1) { 253 qemu_log_mask(LOG_GUEST_ERROR, 254 "pmp violation - access is partially inside\n"); 255 ret = 0; 256 break; 257 } 258 259 /* fully inside */ 260 const uint8_t a_field = 261 pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); 262 if ((s + e) == 2) { 263 if (PMP_AMATCH_OFF == a_field) { 264 return 1; 265 } 266 267 allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC; 268 if ((env->priv != PRV_M) || pmp_is_locked(env, i)) { 269 allowed_privs &= env->pmp_state.pmp[i].cfg_reg; 270 } 271 272 if ((privs & allowed_privs) == privs) { 273 ret = 1; 274 break; 275 } else { 276 ret = 0; 277 break; 278 } 279 } 280 } 281 282 /* No rule matched */ 283 if (ret == -1) { 284 if (env->priv == PRV_M) { 285 ret = 1; /* Privileged spec v1.10 states if no PMP entry matches an 286 * M-Mode access, the access succeeds */ 287 } else { 288 ret = 0; /* Other modes are not allowed to succeed if they don't 289 * match a rule, but there are rules. We've checked for 290 * no rule earlier in this function. */ 291 } 292 } 293 294 return ret == 1 ? true : false; 295 } 296 297 298 /* 299 * Handle a write to a pmpcfg CSP 300 */ 301 void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, 302 target_ulong val) 303 { 304 int i; 305 uint8_t cfg_val; 306 307 PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx, 308 env->mhartid, reg_index, val); 309 310 if ((reg_index & 1) && (sizeof(target_ulong) == 8)) { 311 qemu_log_mask(LOG_GUEST_ERROR, 312 "ignoring pmpcfg write - incorrect address\n"); 313 return; 314 } 315 316 for (i = 0; i < sizeof(target_ulong); i++) { 317 cfg_val = (val >> 8 * i) & 0xff; 318 pmp_write_cfg(env, (reg_index * sizeof(target_ulong)) + i, 319 cfg_val); 320 } 321 } 322 323 324 /* 325 * Handle a read from a pmpcfg CSP 326 */ 327 target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index) 328 { 329 int i; 330 target_ulong cfg_val = 0; 331 target_ulong val = 0; 332 333 for (i = 0; i < sizeof(target_ulong); i++) { 334 val = pmp_read_cfg(env, (reg_index * sizeof(target_ulong)) + i); 335 cfg_val |= (val << (i * 8)); 336 } 337 338 PMP_DEBUG("hart " TARGET_FMT_ld ": reg%d, val: 0x" TARGET_FMT_lx, 339 env->mhartid, reg_index, cfg_val); 340 341 return cfg_val; 342 } 343 344 345 /* 346 * Handle a write to a pmpaddr CSP 347 */ 348 void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, 349 target_ulong val) 350 { 351 PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx, 352 env->mhartid, addr_index, val); 353 354 if (addr_index < MAX_RISCV_PMPS) { 355 if (!pmp_is_locked(env, addr_index)) { 356 env->pmp_state.pmp[addr_index].addr_reg = val; 357 pmp_update_rule(env, addr_index); 358 } else { 359 qemu_log_mask(LOG_GUEST_ERROR, 360 "ignoring pmpaddr write - locked\n"); 361 } 362 } else { 363 qemu_log_mask(LOG_GUEST_ERROR, 364 "ignoring pmpaddr write - out of bounds\n"); 365 } 366 } 367 368 369 /* 370 * Handle a read from a pmpaddr CSP 371 */ 372 target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) 373 { 374 PMP_DEBUG("hart " TARGET_FMT_ld ": addr%d, val: 0x" TARGET_FMT_lx, 375 env->mhartid, addr_index, 376 env->pmp_state.pmp[addr_index].addr_reg); 377 if (addr_index < MAX_RISCV_PMPS) { 378 return env->pmp_state.pmp[addr_index].addr_reg; 379 } else { 380 qemu_log_mask(LOG_GUEST_ERROR, 381 "ignoring pmpaddr read - out of bounds\n"); 382 return 0; 383 } 384 } 385 386 #endif 387