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