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