trans_privileged.c.inc (12374B)
1 /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 /* 3 * Copyright (c) 2021 Loongson Technology Corporation Limited 4 * 5 * LoongArch translation routines for the privileged instructions. 6 */ 7 8 #include "cpu-csr.h" 9 10 #ifdef CONFIG_USER_ONLY 11 12 #define GEN_FALSE_TRANS(name) \ 13 static bool trans_##name(DisasContext *ctx, arg_##name * a) \ 14 { \ 15 return false; \ 16 } 17 18 GEN_FALSE_TRANS(csrrd) 19 GEN_FALSE_TRANS(csrwr) 20 GEN_FALSE_TRANS(csrxchg) 21 GEN_FALSE_TRANS(iocsrrd_b) 22 GEN_FALSE_TRANS(iocsrrd_h) 23 GEN_FALSE_TRANS(iocsrrd_w) 24 GEN_FALSE_TRANS(iocsrrd_d) 25 GEN_FALSE_TRANS(iocsrwr_b) 26 GEN_FALSE_TRANS(iocsrwr_h) 27 GEN_FALSE_TRANS(iocsrwr_w) 28 GEN_FALSE_TRANS(iocsrwr_d) 29 GEN_FALSE_TRANS(tlbsrch) 30 GEN_FALSE_TRANS(tlbrd) 31 GEN_FALSE_TRANS(tlbwr) 32 GEN_FALSE_TRANS(tlbfill) 33 GEN_FALSE_TRANS(tlbclr) 34 GEN_FALSE_TRANS(tlbflush) 35 GEN_FALSE_TRANS(invtlb) 36 GEN_FALSE_TRANS(cacop) 37 GEN_FALSE_TRANS(ldpte) 38 GEN_FALSE_TRANS(lddir) 39 GEN_FALSE_TRANS(ertn) 40 GEN_FALSE_TRANS(dbcl) 41 GEN_FALSE_TRANS(idle) 42 43 #else 44 45 typedef void (*GenCSRRead)(TCGv dest, TCGv_ptr env); 46 typedef void (*GenCSRWrite)(TCGv dest, TCGv_ptr env, TCGv src); 47 48 typedef struct { 49 int offset; 50 int flags; 51 GenCSRRead readfn; 52 GenCSRWrite writefn; 53 } CSRInfo; 54 55 enum { 56 CSRFL_READONLY = (1 << 0), 57 CSRFL_EXITTB = (1 << 1), 58 CSRFL_IO = (1 << 2), 59 }; 60 61 #define CSR_OFF_FUNCS(NAME, FL, RD, WR) \ 62 [LOONGARCH_CSR_##NAME] = { \ 63 .offset = offsetof(CPULoongArchState, CSR_##NAME), \ 64 .flags = FL, .readfn = RD, .writefn = WR \ 65 } 66 67 #define CSR_OFF_ARRAY(NAME, N) \ 68 [LOONGARCH_CSR_##NAME(N)] = { \ 69 .offset = offsetof(CPULoongArchState, CSR_##NAME[N]), \ 70 .flags = 0, .readfn = NULL, .writefn = NULL \ 71 } 72 73 #define CSR_OFF_FLAGS(NAME, FL) \ 74 CSR_OFF_FUNCS(NAME, FL, NULL, NULL) 75 76 #define CSR_OFF(NAME) \ 77 CSR_OFF_FLAGS(NAME, 0) 78 79 static const CSRInfo csr_info[] = { 80 CSR_OFF_FLAGS(CRMD, CSRFL_EXITTB), 81 CSR_OFF(PRMD), 82 CSR_OFF_FLAGS(EUEN, CSRFL_EXITTB), 83 CSR_OFF_FLAGS(MISC, CSRFL_READONLY), 84 CSR_OFF(ECFG), 85 CSR_OFF_FUNCS(ESTAT, CSRFL_EXITTB, NULL, gen_helper_csrwr_estat), 86 CSR_OFF(ERA), 87 CSR_OFF(BADV), 88 CSR_OFF_FLAGS(BADI, CSRFL_READONLY), 89 CSR_OFF(EENTRY), 90 CSR_OFF(TLBIDX), 91 CSR_OFF(TLBEHI), 92 CSR_OFF(TLBELO0), 93 CSR_OFF(TLBELO1), 94 CSR_OFF_FUNCS(ASID, CSRFL_EXITTB, NULL, gen_helper_csrwr_asid), 95 CSR_OFF(PGDL), 96 CSR_OFF(PGDH), 97 CSR_OFF_FUNCS(PGD, CSRFL_READONLY, gen_helper_csrrd_pgd, NULL), 98 CSR_OFF(PWCL), 99 CSR_OFF(PWCH), 100 CSR_OFF(STLBPS), 101 CSR_OFF(RVACFG), 102 [LOONGARCH_CSR_CPUID] = { 103 .offset = (int)offsetof(CPUState, cpu_index) 104 - (int)offsetof(LoongArchCPU, env), 105 .flags = CSRFL_READONLY, 106 .readfn = NULL, 107 .writefn = NULL 108 }, 109 CSR_OFF_FLAGS(PRCFG1, CSRFL_READONLY), 110 CSR_OFF_FLAGS(PRCFG2, CSRFL_READONLY), 111 CSR_OFF_FLAGS(PRCFG3, CSRFL_READONLY), 112 CSR_OFF_ARRAY(SAVE, 0), 113 CSR_OFF_ARRAY(SAVE, 1), 114 CSR_OFF_ARRAY(SAVE, 2), 115 CSR_OFF_ARRAY(SAVE, 3), 116 CSR_OFF_ARRAY(SAVE, 4), 117 CSR_OFF_ARRAY(SAVE, 5), 118 CSR_OFF_ARRAY(SAVE, 6), 119 CSR_OFF_ARRAY(SAVE, 7), 120 CSR_OFF_ARRAY(SAVE, 8), 121 CSR_OFF_ARRAY(SAVE, 9), 122 CSR_OFF_ARRAY(SAVE, 10), 123 CSR_OFF_ARRAY(SAVE, 11), 124 CSR_OFF_ARRAY(SAVE, 12), 125 CSR_OFF_ARRAY(SAVE, 13), 126 CSR_OFF_ARRAY(SAVE, 14), 127 CSR_OFF_ARRAY(SAVE, 15), 128 CSR_OFF(TID), 129 CSR_OFF_FUNCS(TCFG, CSRFL_IO, NULL, gen_helper_csrwr_tcfg), 130 CSR_OFF_FUNCS(TVAL, CSRFL_READONLY | CSRFL_IO, gen_helper_csrrd_tval, NULL), 131 CSR_OFF(CNTC), 132 CSR_OFF_FUNCS(TICLR, CSRFL_IO, NULL, gen_helper_csrwr_ticlr), 133 CSR_OFF(LLBCTL), 134 CSR_OFF(IMPCTL1), 135 CSR_OFF(IMPCTL2), 136 CSR_OFF(TLBRENTRY), 137 CSR_OFF(TLBRBADV), 138 CSR_OFF(TLBRERA), 139 CSR_OFF(TLBRSAVE), 140 CSR_OFF(TLBRELO0), 141 CSR_OFF(TLBRELO1), 142 CSR_OFF(TLBREHI), 143 CSR_OFF(TLBRPRMD), 144 CSR_OFF(MERRCTL), 145 CSR_OFF(MERRINFO1), 146 CSR_OFF(MERRINFO2), 147 CSR_OFF(MERRENTRY), 148 CSR_OFF(MERRERA), 149 CSR_OFF(MERRSAVE), 150 CSR_OFF(CTAG), 151 CSR_OFF_ARRAY(DMW, 0), 152 CSR_OFF_ARRAY(DMW, 1), 153 CSR_OFF_ARRAY(DMW, 2), 154 CSR_OFF_ARRAY(DMW, 3), 155 CSR_OFF(DBG), 156 CSR_OFF(DERA), 157 CSR_OFF(DSAVE), 158 }; 159 160 static bool check_plv(DisasContext *ctx) 161 { 162 if (ctx->plv == MMU_PLV_USER) { 163 generate_exception(ctx, EXCCODE_IPE); 164 return true; 165 } 166 return false; 167 } 168 169 static const CSRInfo *get_csr(unsigned csr_num) 170 { 171 const CSRInfo *csr; 172 173 if (csr_num >= ARRAY_SIZE(csr_info)) { 174 return NULL; 175 } 176 csr = &csr_info[csr_num]; 177 if (csr->offset == 0) { 178 return NULL; 179 } 180 return csr; 181 } 182 183 static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write) 184 { 185 if ((csr->flags & CSRFL_READONLY) && write) { 186 return false; 187 } 188 if ((csr->flags & CSRFL_IO) && 189 (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT)) { 190 gen_io_start(); 191 ctx->base.is_jmp = DISAS_EXIT_UPDATE; 192 } else if ((csr->flags & CSRFL_EXITTB) && write) { 193 ctx->base.is_jmp = DISAS_EXIT_UPDATE; 194 } 195 return true; 196 } 197 198 static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a) 199 { 200 TCGv dest; 201 const CSRInfo *csr; 202 203 if (check_plv(ctx)) { 204 return false; 205 } 206 csr = get_csr(a->csr); 207 if (csr == NULL) { 208 /* CSR is undefined: read as 0. */ 209 dest = tcg_constant_tl(0); 210 } else { 211 check_csr_flags(ctx, csr, false); 212 dest = gpr_dst(ctx, a->rd, EXT_NONE); 213 if (csr->readfn) { 214 csr->readfn(dest, cpu_env); 215 } else { 216 tcg_gen_ld_tl(dest, cpu_env, csr->offset); 217 } 218 } 219 gen_set_gpr(a->rd, dest, EXT_NONE); 220 return true; 221 } 222 223 static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a) 224 { 225 TCGv dest, src1; 226 const CSRInfo *csr; 227 228 if (check_plv(ctx)) { 229 return false; 230 } 231 csr = get_csr(a->csr); 232 if (csr == NULL) { 233 /* CSR is undefined: write ignored, read old_value as 0. */ 234 gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); 235 return true; 236 } 237 if (!check_csr_flags(ctx, csr, true)) { 238 /* CSR is readonly: trap. */ 239 return false; 240 } 241 src1 = gpr_src(ctx, a->rd, EXT_NONE); 242 if (csr->writefn) { 243 dest = gpr_dst(ctx, a->rd, EXT_NONE); 244 csr->writefn(dest, cpu_env, src1); 245 } else { 246 dest = temp_new(ctx); 247 tcg_gen_ld_tl(dest, cpu_env, csr->offset); 248 tcg_gen_st_tl(src1, cpu_env, csr->offset); 249 } 250 gen_set_gpr(a->rd, dest, EXT_NONE); 251 return true; 252 } 253 254 static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a) 255 { 256 TCGv src1, mask, oldv, newv, temp; 257 const CSRInfo *csr; 258 259 if (check_plv(ctx)) { 260 return false; 261 } 262 csr = get_csr(a->csr); 263 if (csr == NULL) { 264 /* CSR is undefined: write ignored, read old_value as 0. */ 265 gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); 266 return true; 267 } 268 269 if (!check_csr_flags(ctx, csr, true)) { 270 /* CSR is readonly: trap. */ 271 return false; 272 } 273 274 /* So far only readonly csrs have readfn. */ 275 assert(csr->readfn == NULL); 276 277 src1 = gpr_src(ctx, a->rd, EXT_NONE); 278 mask = gpr_src(ctx, a->rj, EXT_NONE); 279 oldv = tcg_temp_new(); 280 newv = tcg_temp_new(); 281 temp = tcg_temp_new(); 282 283 tcg_gen_ld_tl(oldv, cpu_env, csr->offset); 284 tcg_gen_and_tl(newv, src1, mask); 285 tcg_gen_andc_tl(temp, oldv, mask); 286 tcg_gen_or_tl(newv, newv, temp); 287 288 if (csr->writefn) { 289 csr->writefn(oldv, cpu_env, newv); 290 } else { 291 tcg_gen_st_tl(newv, cpu_env, csr->offset); 292 } 293 gen_set_gpr(a->rd, oldv, EXT_NONE); 294 295 tcg_temp_free(temp); 296 tcg_temp_free(newv); 297 tcg_temp_free(oldv); 298 return true; 299 } 300 301 static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a, 302 void (*func)(TCGv, TCGv_ptr, TCGv)) 303 { 304 TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); 305 TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); 306 307 if (check_plv(ctx)) { 308 return false; 309 } 310 func(dest, cpu_env, src1); 311 return true; 312 } 313 314 static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a, 315 void (*func)(TCGv_ptr, TCGv, TCGv)) 316 { 317 TCGv val = gpr_src(ctx, a->rd, EXT_NONE); 318 TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); 319 320 if (check_plv(ctx)) { 321 return false; 322 } 323 func(cpu_env, addr, val); 324 return true; 325 } 326 327 TRANS(iocsrrd_b, gen_iocsrrd, gen_helper_iocsrrd_b) 328 TRANS(iocsrrd_h, gen_iocsrrd, gen_helper_iocsrrd_h) 329 TRANS(iocsrrd_w, gen_iocsrrd, gen_helper_iocsrrd_w) 330 TRANS(iocsrrd_d, gen_iocsrrd, gen_helper_iocsrrd_d) 331 TRANS(iocsrwr_b, gen_iocsrwr, gen_helper_iocsrwr_b) 332 TRANS(iocsrwr_h, gen_iocsrwr, gen_helper_iocsrwr_h) 333 TRANS(iocsrwr_w, gen_iocsrwr, gen_helper_iocsrwr_w) 334 TRANS(iocsrwr_d, gen_iocsrwr, gen_helper_iocsrwr_d) 335 336 static void check_mmu_idx(DisasContext *ctx) 337 { 338 if (ctx->mem_idx != MMU_IDX_DA) { 339 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); 340 ctx->base.is_jmp = DISAS_EXIT; 341 } 342 } 343 344 static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a) 345 { 346 if (check_plv(ctx)) { 347 return false; 348 } 349 gen_helper_tlbsrch(cpu_env); 350 return true; 351 } 352 353 static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a) 354 { 355 if (check_plv(ctx)) { 356 return false; 357 } 358 gen_helper_tlbrd(cpu_env); 359 return true; 360 } 361 362 static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a) 363 { 364 if (check_plv(ctx)) { 365 return false; 366 } 367 gen_helper_tlbwr(cpu_env); 368 check_mmu_idx(ctx); 369 return true; 370 } 371 372 static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a) 373 { 374 if (check_plv(ctx)) { 375 return false; 376 } 377 gen_helper_tlbfill(cpu_env); 378 check_mmu_idx(ctx); 379 return true; 380 } 381 382 static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a) 383 { 384 if (check_plv(ctx)) { 385 return false; 386 } 387 gen_helper_tlbclr(cpu_env); 388 check_mmu_idx(ctx); 389 return true; 390 } 391 392 static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a) 393 { 394 if (check_plv(ctx)) { 395 return false; 396 } 397 gen_helper_tlbflush(cpu_env); 398 check_mmu_idx(ctx); 399 return true; 400 } 401 402 static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a) 403 { 404 TCGv rj = gpr_src(ctx, a->rj, EXT_NONE); 405 TCGv rk = gpr_src(ctx, a->rk, EXT_NONE); 406 407 if (check_plv(ctx)) { 408 return false; 409 } 410 411 switch (a->imm) { 412 case 0: 413 case 1: 414 gen_helper_invtlb_all(cpu_env); 415 break; 416 case 2: 417 gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(1)); 418 break; 419 case 3: 420 gen_helper_invtlb_all_g(cpu_env, tcg_constant_i32(0)); 421 break; 422 case 4: 423 gen_helper_invtlb_all_asid(cpu_env, rj); 424 break; 425 case 5: 426 gen_helper_invtlb_page_asid(cpu_env, rj, rk); 427 break; 428 case 6: 429 gen_helper_invtlb_page_asid_or_g(cpu_env, rj, rk); 430 break; 431 default: 432 return false; 433 } 434 ctx->base.is_jmp = DISAS_STOP; 435 return true; 436 } 437 438 static bool trans_cacop(DisasContext *ctx, arg_cacop *a) 439 { 440 /* Treat the cacop as a nop */ 441 if (check_plv(ctx)) { 442 return false; 443 } 444 return true; 445 } 446 447 static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a) 448 { 449 TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); 450 TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); 451 452 if (check_plv(ctx)) { 453 return false; 454 } 455 gen_helper_ldpte(cpu_env, src1, tcg_constant_tl(a->imm), mem_idx); 456 return true; 457 } 458 459 static bool trans_lddir(DisasContext *ctx, arg_lddir *a) 460 { 461 TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); 462 TCGv src = gpr_src(ctx, a->rj, EXT_NONE); 463 TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); 464 465 if (check_plv(ctx)) { 466 return false; 467 } 468 gen_helper_lddir(dest, cpu_env, src, tcg_constant_tl(a->imm), mem_idx); 469 return true; 470 } 471 472 static bool trans_ertn(DisasContext *ctx, arg_ertn *a) 473 { 474 if (check_plv(ctx)) { 475 return false; 476 } 477 gen_helper_ertn(cpu_env); 478 ctx->base.is_jmp = DISAS_EXIT; 479 return true; 480 } 481 482 static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a) 483 { 484 if (check_plv(ctx)) { 485 return false; 486 } 487 generate_exception(ctx, EXCCODE_DBP); 488 return true; 489 } 490 491 static bool trans_idle(DisasContext *ctx, arg_idle *a) 492 { 493 if (check_plv(ctx)) { 494 return false; 495 } 496 497 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); 498 gen_helper_idle(cpu_env); 499 ctx->base.is_jmp = DISAS_NORETURN; 500 return true; 501 } 502 #endif