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