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 31 #ifndef CONFIG_USER_ONLY 32 33 #define RISCV_DEBUG_PMP 0 34 #define PMP_DEBUG(fmt, ...) \ 35 do { \ 36 if (RISCV_DEBUG_PMP) { \ 37 qemu_log_mask(LOG_TRACE, "%s: " fmt "\n", __func__, ##__VA_ARGS__);\ 38 } \ 39 } while (0) 40 41 static void pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, 42 uint8_t val); 43 static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index); 44 static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index); 45 46 /* 47 * Accessor method to extract address matching type 'a field' from cfg reg 48 */ 49 static inline uint8_t pmp_get_a_field(uint8_t cfg) 50 { 51 uint8_t a = cfg >> 3; 52 return a & 0x3; 53 } 54 55 /* 56 * Check whether a PMP is locked or not. 57 */ 58 static inline int pmp_is_locked(CPURISCVState *env, uint32_t pmp_index) 59 { 60 61 if (env->pmp_state.pmp[pmp_index].cfg_reg & PMP_LOCK) { 62 return 1; 63 } 64 65 /* Top PMP has no 'next' to check */ 66 if ((pmp_index + 1u) >= MAX_RISCV_PMPS) { 67 return 0; 68 } 69 70 /* In TOR mode, need to check the lock bit of the next pmp 71 * (if there is a next) 72 */ 73 const uint8_t a_field = 74 pmp_get_a_field(env->pmp_state.pmp[pmp_index + 1].cfg_reg); 75 if ((env->pmp_state.pmp[pmp_index + 1u].cfg_reg & PMP_LOCK) && 76 (PMP_AMATCH_TOR == a_field)) { 77 return 1; 78 } 79 80 return 0; 81 } 82 83 /* 84 * Count the number of active rules. 85 */ 86 static inline uint32_t pmp_get_num_rules(CPURISCVState *env) 87 { 88 return env->pmp_state.num_rules; 89 } 90 91 /* 92 * Accessor to get the cfg reg for a specific PMP/HART 93 */ 94 static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) 95 { 96 if (pmp_index < MAX_RISCV_PMPS) { 97 return env->pmp_state.pmp[pmp_index].cfg_reg; 98 } 99 100 return 0; 101 } 102 103 104 /* 105 * Accessor to set the cfg reg for a specific PMP/HART 106 * Bounds checks and relevant lock bit. 107 */ 108 static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) 109 { 110 if (pmp_index < MAX_RISCV_PMPS) { 111 if (!pmp_is_locked(env, pmp_index)) { 112 env->pmp_state.pmp[pmp_index].cfg_reg = val; 113 pmp_update_rule(env, pmp_index); 114 } else { 115 qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n"); 116 } 117 } else { 118 qemu_log_mask(LOG_GUEST_ERROR, 119 "ignoring pmpcfg write - out of bounds\n"); 120 } 121 } 122 123 static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) 124 { 125 /* 126 aaaa...aaa0 8-byte NAPOT range 127 aaaa...aa01 16-byte NAPOT range 128 aaaa...a011 32-byte NAPOT range 129 ... 130 aa01...1111 2^XLEN-byte NAPOT range 131 a011...1111 2^(XLEN+1)-byte NAPOT range 132 0111...1111 2^(XLEN+2)-byte NAPOT range 133 1111...1111 Reserved 134 */ 135 if (a == -1) { 136 *sa = 0u; 137 *ea = -1; 138 return; 139 } else { 140 target_ulong t1 = ctz64(~a); 141 target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 2; 142 target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1; 143 *sa = base; 144 *ea = base + range; 145 } 146 } 147 148 149 /* Convert cfg/addr reg values here into simple 'sa' --> start address and 'ea' 150 * end address values. 151 * This function is called relatively infrequently whereas the check that 152 * an address is within a pmp rule is called often, so optimise that one 153 */ 154 static void pmp_update_rule(CPURISCVState *env, uint32_t pmp_index) 155 { 156 int i; 157 158 env->pmp_state.num_rules = 0; 159 160 uint8_t this_cfg = env->pmp_state.pmp[pmp_index].cfg_reg; 161 target_ulong this_addr = env->pmp_state.pmp[pmp_index].addr_reg; 162 target_ulong prev_addr = 0u; 163 target_ulong sa = 0u; 164 target_ulong ea = 0u; 165 166 if (pmp_index >= 1u) { 167 prev_addr = env->pmp_state.pmp[pmp_index - 1].addr_reg; 168 } 169 170 switch (pmp_get_a_field(this_cfg)) { 171 case PMP_AMATCH_OFF: 172 sa = 0u; 173 ea = -1; 174 break; 175 176 case PMP_AMATCH_TOR: 177 sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ 178 ea = (this_addr << 2) - 1u; 179 break; 180 181 case PMP_AMATCH_NA4: 182 sa = this_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ 183 ea = (this_addr + 4u) - 1u; 184 break; 185 186 case PMP_AMATCH_NAPOT: 187 pmp_decode_napot(this_addr, &sa, &ea); 188 break; 189 190 default: 191 sa = 0u; 192 ea = 0u; 193 break; 194 } 195 196 env->pmp_state.addr[pmp_index].sa = sa; 197 env->pmp_state.addr[pmp_index].ea = ea; 198 199 for (i = 0; i < MAX_RISCV_PMPS; i++) { 200 const uint8_t a_field = 201 pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); 202 if (PMP_AMATCH_OFF != a_field) { 203 env->pmp_state.num_rules++; 204 } 205 } 206 } 207 208 static int pmp_is_in_range(CPURISCVState *env, int pmp_index, target_ulong addr) 209 { 210 int result = 0; 211 212 if ((addr >= env->pmp_state.addr[pmp_index].sa) 213 && (addr <= env->pmp_state.addr[pmp_index].ea)) { 214 result = 1; 215 } else { 216 result = 0; 217 } 218 219 return result; 220 } 221 222 223 /* 224 * Public Interface 225 */ 226 227 /* 228 * Check if the address has required RWX privs to complete desired operation 229 */ 230 bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, 231 target_ulong size, pmp_priv_t privs, target_ulong mode) 232 { 233 int i = 0; 234 int ret = -1; 235 target_ulong s = 0; 236 target_ulong e = 0; 237 pmp_priv_t allowed_privs = 0; 238 239 /* Short cut if no rules */ 240 if (0 == pmp_get_num_rules(env)) { 241 return true; 242 } 243 244 /* 1.10 draft priv spec states there is an implicit order 245 from low to high */ 246 for (i = 0; i < MAX_RISCV_PMPS; i++) { 247 s = pmp_is_in_range(env, i, addr); 248 e = pmp_is_in_range(env, i, addr + size - 1); 249 250 /* partially inside */ 251 if ((s + e) == 1) { 252 qemu_log_mask(LOG_GUEST_ERROR, 253 "pmp violation - access is partially inside\n"); 254 ret = 0; 255 break; 256 } 257 258 /* fully inside */ 259 const uint8_t a_field = 260 pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); 261 262 /* 263 * If the PMP entry is not off and the address is in range, do the priv 264 * check 265 */ 266 if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) { 267 allowed_privs = PMP_READ | PMP_WRITE | PMP_EXEC; 268 if ((mode != 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 (mode == 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