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 "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 48static bool check_plv(DisasContext *ctx) 49{ 50 if (ctx->plv == MMU_PLV_USER) { 51 generate_exception(ctx, EXCCODE_IPE); 52 return true; 53 } 54 return false; 55} 56 57static bool set_csr_trans_func(unsigned int csr_num, GenCSRRead readfn, 58 GenCSRWrite writefn) 59{ 60 CSRInfo *csr; 61 62 csr = get_csr(csr_num); 63 if (!csr) { 64 return false; 65 } 66 67 csr->readfn = (GenCSRFunc)readfn; 68 csr->writefn = (GenCSRFunc)writefn; 69 return true; 70} 71 72#define SET_CSR_FUNC(NAME, read, write) \ 73 set_csr_trans_func(LOONGARCH_CSR_##NAME, read, write) 74 75void loongarch_csr_translate_init(void) 76{ 77 SET_CSR_FUNC(STLBPS, NULL, gen_helper_csrwr_stlbps); 78 SET_CSR_FUNC(ESTAT, NULL, gen_helper_csrwr_estat); 79 SET_CSR_FUNC(ASID, NULL, gen_helper_csrwr_asid); 80 SET_CSR_FUNC(PGD, gen_helper_csrrd_pgd, NULL); 81 SET_CSR_FUNC(PWCL, NULL, gen_helper_csrwr_pwcl); 82 SET_CSR_FUNC(CPUID, gen_helper_csrrd_cpuid, NULL); 83 SET_CSR_FUNC(TCFG, NULL, gen_helper_csrwr_tcfg); 84 SET_CSR_FUNC(TVAL, gen_helper_csrrd_tval, NULL); 85 SET_CSR_FUNC(TICLR, NULL, gen_helper_csrwr_ticlr); 86} 87#undef SET_CSR_FUNC 88 89static bool check_csr_flags(DisasContext *ctx, const CSRInfo *csr, bool write) 90{ 91 if ((csr->flags & CSRFL_READONLY) && write) { 92 return false; 93 } 94 if ((csr->flags & CSRFL_IO) && translator_io_start(&ctx->base)) { 95 ctx->base.is_jmp = DISAS_EXIT_UPDATE; 96 } else if ((csr->flags & CSRFL_EXITTB) && write) { 97 ctx->base.is_jmp = DISAS_EXIT_UPDATE; 98 } 99 return true; 100} 101 102static bool trans_csrrd(DisasContext *ctx, arg_csrrd *a) 103{ 104 TCGv dest; 105 const CSRInfo *csr; 106 GenCSRRead readfn; 107 108 if (check_plv(ctx)) { 109 return false; 110 } 111 csr = get_csr(a->csr); 112 if (csr == NULL) { 113 /* CSR is undefined: read as 0. */ 114 dest = tcg_constant_tl(0); 115 } else { 116 check_csr_flags(ctx, csr, false); 117 dest = gpr_dst(ctx, a->rd, EXT_NONE); 118 readfn = (GenCSRRead)csr->readfn; 119 if (readfn) { 120 readfn(dest, tcg_env); 121 } else { 122 tcg_gen_ld_tl(dest, tcg_env, csr->offset); 123 } 124 } 125 gen_set_gpr(a->rd, dest, EXT_NONE); 126 return true; 127} 128 129static bool trans_csrwr(DisasContext *ctx, arg_csrwr *a) 130{ 131 TCGv dest, src1; 132 const CSRInfo *csr; 133 GenCSRWrite writefn; 134 135 if (check_plv(ctx)) { 136 return false; 137 } 138 csr = get_csr(a->csr); 139 if (csr == NULL) { 140 /* CSR is undefined: write ignored, read old_value as 0. */ 141 gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); 142 return true; 143 } 144 if (!check_csr_flags(ctx, csr, true)) { 145 /* CSR is readonly: trap. */ 146 return false; 147 } 148 src1 = gpr_src(ctx, a->rd, EXT_NONE); 149 writefn = (GenCSRWrite)csr->writefn; 150 if (writefn) { 151 dest = gpr_dst(ctx, a->rd, EXT_NONE); 152 writefn(dest, tcg_env, src1); 153 } else { 154 dest = tcg_temp_new(); 155 tcg_gen_ld_tl(dest, tcg_env, csr->offset); 156 tcg_gen_st_tl(src1, tcg_env, csr->offset); 157 } 158 gen_set_gpr(a->rd, dest, EXT_NONE); 159 return true; 160} 161 162static bool trans_csrxchg(DisasContext *ctx, arg_csrxchg *a) 163{ 164 TCGv src1, mask, oldv, newv, temp; 165 const CSRInfo *csr; 166 GenCSRWrite writefn; 167 168 if (check_plv(ctx)) { 169 return false; 170 } 171 csr = get_csr(a->csr); 172 if (csr == NULL) { 173 /* CSR is undefined: write ignored, read old_value as 0. */ 174 gen_set_gpr(a->rd, tcg_constant_tl(0), EXT_NONE); 175 return true; 176 } 177 178 if (!check_csr_flags(ctx, csr, true)) { 179 /* CSR is readonly: trap. */ 180 return false; 181 } 182 183 /* So far only readonly csrs have readfn. */ 184 assert(csr->readfn == NULL); 185 186 src1 = gpr_src(ctx, a->rd, EXT_NONE); 187 mask = gpr_src(ctx, a->rj, EXT_NONE); 188 oldv = tcg_temp_new(); 189 newv = tcg_temp_new(); 190 temp = tcg_temp_new(); 191 192 tcg_gen_ld_tl(oldv, tcg_env, csr->offset); 193 tcg_gen_and_tl(newv, src1, mask); 194 tcg_gen_andc_tl(temp, oldv, mask); 195 tcg_gen_or_tl(newv, newv, temp); 196 197 writefn = (GenCSRWrite)csr->writefn; 198 if (writefn) { 199 writefn(oldv, tcg_env, newv); 200 } else { 201 tcg_gen_st_tl(newv, tcg_env, csr->offset); 202 } 203 gen_set_gpr(a->rd, oldv, EXT_NONE); 204 return true; 205} 206 207static bool gen_iocsrrd(DisasContext *ctx, arg_rr *a, 208 void (*func)(TCGv, TCGv_ptr, TCGv)) 209{ 210 TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); 211 TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); 212 213 if (check_plv(ctx)) { 214 return false; 215 } 216 func(dest, tcg_env, src1); 217 return true; 218} 219 220static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a, 221 void (*func)(TCGv_ptr, TCGv, TCGv)) 222{ 223 TCGv val = gpr_src(ctx, a->rd, EXT_NONE); 224 TCGv addr = gpr_src(ctx, a->rj, EXT_NONE); 225 226 if (check_plv(ctx)) { 227 return false; 228 } 229 func(tcg_env, addr, val); 230 return true; 231} 232 233TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b) 234TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h) 235TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w) 236TRANS(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d) 237TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b) 238TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h) 239TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w) 240TRANS(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d) 241 242static void check_mmu_idx(DisasContext *ctx) 243{ 244 if (ctx->mem_idx != MMU_DA_IDX) { 245 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); 246 ctx->base.is_jmp = DISAS_EXIT; 247 } 248} 249 250static bool trans_tlbsrch(DisasContext *ctx, arg_tlbsrch *a) 251{ 252 if (check_plv(ctx)) { 253 return false; 254 } 255 gen_helper_tlbsrch(tcg_env); 256 return true; 257} 258 259static bool trans_tlbrd(DisasContext *ctx, arg_tlbrd *a) 260{ 261 if (check_plv(ctx)) { 262 return false; 263 } 264 gen_helper_tlbrd(tcg_env); 265 return true; 266} 267 268static bool trans_tlbwr(DisasContext *ctx, arg_tlbwr *a) 269{ 270 if (check_plv(ctx)) { 271 return false; 272 } 273 gen_helper_tlbwr(tcg_env); 274 check_mmu_idx(ctx); 275 return true; 276} 277 278static bool trans_tlbfill(DisasContext *ctx, arg_tlbfill *a) 279{ 280 if (check_plv(ctx)) { 281 return false; 282 } 283 gen_helper_tlbfill(tcg_env); 284 check_mmu_idx(ctx); 285 return true; 286} 287 288static bool trans_tlbclr(DisasContext *ctx, arg_tlbclr *a) 289{ 290 if (check_plv(ctx)) { 291 return false; 292 } 293 gen_helper_tlbclr(tcg_env); 294 check_mmu_idx(ctx); 295 return true; 296} 297 298static bool trans_tlbflush(DisasContext *ctx, arg_tlbflush *a) 299{ 300 if (check_plv(ctx)) { 301 return false; 302 } 303 gen_helper_tlbflush(tcg_env); 304 check_mmu_idx(ctx); 305 return true; 306} 307 308static bool trans_invtlb(DisasContext *ctx, arg_invtlb *a) 309{ 310 TCGv rj = gpr_src(ctx, a->rj, EXT_NONE); 311 TCGv rk = gpr_src(ctx, a->rk, EXT_NONE); 312 313 if (check_plv(ctx)) { 314 return false; 315 } 316 317 switch (a->imm) { 318 case 0: 319 case 1: 320 gen_helper_invtlb_all(tcg_env); 321 break; 322 case 2: 323 gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(1)); 324 break; 325 case 3: 326 gen_helper_invtlb_all_g(tcg_env, tcg_constant_i32(0)); 327 break; 328 case 4: 329 gen_helper_invtlb_all_asid(tcg_env, rj); 330 break; 331 case 5: 332 gen_helper_invtlb_page_asid(tcg_env, rj, rk); 333 break; 334 case 6: 335 gen_helper_invtlb_page_asid_or_g(tcg_env, rj, rk); 336 break; 337 default: 338 return false; 339 } 340 ctx->base.is_jmp = DISAS_STOP; 341 return true; 342} 343 344static bool trans_cacop(DisasContext *ctx, arg_cacop *a) 345{ 346 /* Treat the cacop as a nop */ 347 if (check_plv(ctx)) { 348 return false; 349 } 350 return true; 351} 352 353static bool trans_ldpte(DisasContext *ctx, arg_ldpte *a) 354{ 355 TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); 356 TCGv src1 = gpr_src(ctx, a->rj, EXT_NONE); 357 358 if (!avail_LSPW(ctx)) { 359 return true; 360 } 361 362 if (check_plv(ctx)) { 363 return false; 364 } 365 gen_helper_ldpte(tcg_env, src1, tcg_constant_tl(a->imm), mem_idx); 366 return true; 367} 368 369static bool trans_lddir(DisasContext *ctx, arg_lddir *a) 370{ 371 TCGv_i32 mem_idx = tcg_constant_i32(ctx->mem_idx); 372 TCGv src = gpr_src(ctx, a->rj, EXT_NONE); 373 TCGv dest = gpr_dst(ctx, a->rd, EXT_NONE); 374 375 if (!avail_LSPW(ctx)) { 376 return true; 377 } 378 379 if (check_plv(ctx)) { 380 return false; 381 } 382 gen_helper_lddir(dest, tcg_env, src, tcg_constant_tl(a->imm), mem_idx); 383 return true; 384} 385 386static bool trans_ertn(DisasContext *ctx, arg_ertn *a) 387{ 388 if (check_plv(ctx)) { 389 return false; 390 } 391 gen_helper_ertn(tcg_env); 392 ctx->base.is_jmp = DISAS_EXIT; 393 return true; 394} 395 396static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a) 397{ 398 if (check_plv(ctx)) { 399 return false; 400 } 401 generate_exception(ctx, EXCCODE_DBP); 402 return true; 403} 404 405static bool trans_idle(DisasContext *ctx, arg_idle *a) 406{ 407 if (check_plv(ctx)) { 408 return false; 409 } 410 411 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next + 4); 412 gen_helper_idle(tcg_env); 413 ctx->base.is_jmp = DISAS_NORETURN; 414 return true; 415} 416#endif 417