| 1 | /* |
| 2 | * MIPS emulation helpers for qemu. |
| 3 | * |
| 4 | * Copyright (c) 2004-2005 Jocelyn Mayer |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2.1 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| 18 | * |
| 19 | */ |
| 20 | |
| 21 | #include "qemu/osdep.h" |
| 22 | #include "cpu.h" |
| 23 | #include "internal.h" |
| 24 | #include "exec/helper-proto.h" |
| 25 | #include "exec/memop.h" |
| 26 | #include "fpu_helper.h" |
| 27 | #include "qemu/crc32c.h" |
| 28 | #include "qemu/timer.h" |
| 29 | #include <zlib.h> |
| 30 | |
| 31 | static inline target_ulong bitswap(target_ulong v) |
| 32 | { |
| 33 | v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | |
| 34 | ((v & (target_ulong)0x5555555555555555ULL) << 1); |
| 35 | v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) | |
| 36 | ((v & (target_ulong)0x3333333333333333ULL) << 2); |
| 37 | v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) | |
| 38 | ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4); |
| 39 | return v; |
| 40 | } |
| 41 | |
| 42 | #ifdef TARGET_MIPS64 |
| 43 | target_ulong helper_dbitswap(target_ulong rt) |
| 44 | { |
| 45 | return bitswap(rt); |
| 46 | } |
| 47 | #endif |
| 48 | |
| 49 | target_ulong helper_bitswap(target_ulong rt) |
| 50 | { |
| 51 | return (int32_t)bitswap(rt); |
| 52 | } |
| 53 | |
| 54 | target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx, |
| 55 | uint32_t stripe) |
| 56 | { |
| 57 | int i; |
| 58 | uint64_t tmp0 = ((uint64_t)rs) << 32 | ((uint64_t)rs & 0xffffffff); |
| 59 | uint64_t tmp1 = tmp0; |
| 60 | for (i = 0; i <= 46; i++) { |
| 61 | int s; |
| 62 | if (i & 0x8) { |
| 63 | s = shift; |
| 64 | } else { |
| 65 | s = shiftx; |
| 66 | } |
| 67 | |
| 68 | if (stripe != 0 && !(i & 0x4)) { |
| 69 | s = ~s; |
| 70 | } |
| 71 | if (s & 0x10) { |
| 72 | if (tmp0 & (1LL << (i + 16))) { |
| 73 | tmp1 |= 1LL << i; |
| 74 | } else { |
| 75 | tmp1 &= ~(1LL << i); |
| 76 | } |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | uint64_t tmp2 = tmp1; |
| 81 | for (i = 0; i <= 38; i++) { |
| 82 | int s; |
| 83 | if (i & 0x4) { |
| 84 | s = shift; |
| 85 | } else { |
| 86 | s = shiftx; |
| 87 | } |
| 88 | |
| 89 | if (s & 0x8) { |
| 90 | if (tmp1 & (1LL << (i + 8))) { |
| 91 | tmp2 |= 1LL << i; |
| 92 | } else { |
| 93 | tmp2 &= ~(1LL << i); |
| 94 | } |
| 95 | } |
| 96 | } |
| 97 | |
| 98 | uint64_t tmp3 = tmp2; |
| 99 | for (i = 0; i <= 34; i++) { |
| 100 | int s; |
| 101 | if (i & 0x2) { |
| 102 | s = shift; |
| 103 | } else { |
| 104 | s = shiftx; |
| 105 | } |
| 106 | if (s & 0x4) { |
| 107 | if (tmp2 & (1LL << (i + 4))) { |
| 108 | tmp3 |= 1LL << i; |
| 109 | } else { |
| 110 | tmp3 &= ~(1LL << i); |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | uint64_t tmp4 = tmp3; |
| 116 | for (i = 0; i <= 32; i++) { |
| 117 | int s; |
| 118 | if (i & 0x1) { |
| 119 | s = shift; |
| 120 | } else { |
| 121 | s = shiftx; |
| 122 | } |
| 123 | if (s & 0x2) { |
| 124 | if (tmp3 & (1LL << (i + 2))) { |
| 125 | tmp4 |= 1LL << i; |
| 126 | } else { |
| 127 | tmp4 &= ~(1LL << i); |
| 128 | } |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | uint64_t tmp5 = tmp4; |
| 133 | for (i = 0; i <= 31; i++) { |
| 134 | int s; |
| 135 | s = shift; |
| 136 | if (s & 0x1) { |
| 137 | if (tmp4 & (1LL << (i + 1))) { |
| 138 | tmp5 |= 1LL << i; |
| 139 | } else { |
| 140 | tmp5 &= ~(1LL << i); |
| 141 | } |
| 142 | } |
| 143 | } |
| 144 | |
| 145 | return (int64_t)(int32_t)(uint32_t)tmp5; |
| 146 | } |
| 147 | |
| 148 | /* these crc32 functions are based on target/loongarch/tcg/op_helper.c */ |
| 149 | target_ulong helper_crc32(target_ulong val, target_ulong m, uint32_t sz) |
| 150 | { |
| 151 | uint8_t buf[8]; |
| 152 | target_ulong mask = ((sz * 8) == 64) ? |
| 153 | (target_ulong) -1ULL : |
| 154 | ((1ULL << (sz * 8)) - 1); |
| 155 | |
| 156 | m &= mask; |
| 157 | stq_le_p(buf, m); |
| 158 | return (int32_t) (crc32(val ^ 0xffffffff, buf, sz) ^ 0xffffffff); |
| 159 | } |
| 160 | |
| 161 | target_ulong helper_crc32c(target_ulong val, target_ulong m, uint32_t sz) |
| 162 | { |
| 163 | uint8_t buf[8]; |
| 164 | target_ulong mask = ((sz * 8) == 64) ? |
| 165 | (target_ulong) -1ULL : |
| 166 | ((1ULL << (sz * 8)) - 1); |
| 167 | m &= mask; |
| 168 | stq_le_p(buf, m); |
| 169 | return (int32_t) (crc32c(val, buf, sz) ^ 0xffffffff); |
| 170 | } |
| 171 | |
| 172 | void helper_fork(target_ulong arg1, target_ulong arg2) |
| 173 | { |
| 174 | /* |
| 175 | * arg1 = rt, arg2 = rs |
| 176 | * TODO: store to TC register |
| 177 | */ |
| 178 | } |
| 179 | |
| 180 | target_ulong helper_yield(CPUMIPSState *env, target_ulong arg) |
| 181 | { |
| 182 | target_long arg1 = arg; |
| 183 | |
| 184 | if (arg1 < 0) { |
| 185 | /* No scheduling policy implemented. */ |
| 186 | if (arg1 != -2) { |
| 187 | if (env->CP0_VPEControl & (1 << CP0VPECo_YSI) && |
| 188 | env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) { |
| 189 | env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); |
| 190 | env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT; |
| 191 | do_raise_exception(env, EXCP_THREAD, GETPC()); |
| 192 | } |
| 193 | } |
| 194 | } else if (arg1 == 0) { |
| 195 | if (0) { |
| 196 | /* TODO: TC underflow */ |
| 197 | env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); |
| 198 | do_raise_exception(env, EXCP_THREAD, GETPC()); |
| 199 | } else { |
| 200 | /* TODO: Deallocate TC */ |
| 201 | } |
| 202 | } else if (arg1 > 0) { |
| 203 | /* Yield qualifier inputs not implemented. */ |
| 204 | env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); |
| 205 | env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT; |
| 206 | do_raise_exception(env, EXCP_THREAD, GETPC()); |
| 207 | } |
| 208 | return env->CP0_YQMask; |
| 209 | } |
| 210 | |
| 211 | static inline void check_hwrena(CPUMIPSState *env, int reg, uintptr_t pc) |
| 212 | { |
| 213 | if ((env->hflags & MIPS_HFLAG_CP0) || (env->CP0_HWREna & (1u << reg))) { |
| 214 | return; |
| 215 | } |
| 216 | do_raise_exception(env, EXCP_RI, pc); |
| 217 | } |
| 218 | |
| 219 | target_ulong helper_rdhwr_cpunum(CPUMIPSState *env) |
| 220 | { |
| 221 | check_hwrena(env, 0, GETPC()); |
| 222 | return env->CP0_EBase & 0x3ff; |
| 223 | } |
| 224 | |
| 225 | target_ulong helper_rdhwr_synci_step(CPUMIPSState *env) |
| 226 | { |
| 227 | check_hwrena(env, 1, GETPC()); |
| 228 | return env->SYNCI_Step; |
| 229 | } |
| 230 | |
| 231 | target_ulong helper_rdhwr_cc(CPUMIPSState *env) |
| 232 | { |
| 233 | check_hwrena(env, 2, GETPC()); |
| 234 | #ifdef CONFIG_USER_ONLY |
| 235 | return env->CP0_Count; |
| 236 | #else |
| 237 | return (int32_t)cpu_mips_get_count(env); |
| 238 | #endif |
| 239 | } |
| 240 | |
| 241 | target_ulong helper_rdhwr_ccres(CPUMIPSState *env) |
| 242 | { |
| 243 | check_hwrena(env, 3, GETPC()); |
| 244 | return env->CCRes; |
| 245 | } |
| 246 | |
| 247 | target_ulong helper_rdhwr_performance(CPUMIPSState *env) |
| 248 | { |
| 249 | check_hwrena(env, 4, GETPC()); |
| 250 | return env->CP0_Performance0; |
| 251 | } |
| 252 | |
| 253 | target_ulong helper_rdhwr_xnp(CPUMIPSState *env) |
| 254 | { |
| 255 | check_hwrena(env, 5, GETPC()); |
| 256 | return (env->CP0_Config5 >> CP0C5_XNP) & 1; |
| 257 | } |
| 258 | |
| 259 | target_ulong helper_rdhwr_chord(CPUMIPSState *env) |
| 260 | { |
| 261 | check_hwrena(env, 30, GETPC()); |
| 262 | return env->octeon_crypto.chord; |
| 263 | } |
| 264 | |
| 265 | target_ulong helper_rdhwr_cvmcount(CPUMIPSState *env) |
| 266 | { |
| 267 | check_hwrena(env, 31, GETPC()); |
| 268 | #ifdef CONFIG_USER_ONLY |
| 269 | return cpu_get_host_ticks(); |
| 270 | #else |
| 271 | return (uint32_t)cpu_mips_get_count(env); |
| 272 | #endif |
| 273 | } |
| 274 | |
| 275 | void helper_pmon(CPUMIPSState *env, int function) |
| 276 | { |
| 277 | function /= 2; |
| 278 | switch (function) { |
| 279 | case 2: /* TODO: char inbyte(int waitflag); */ |
| 280 | if (env->active_tc.gpr[4] == 0) { |
| 281 | env->active_tc.gpr[2] = -1; |
| 282 | } |
| 283 | /* Fall through */ |
| 284 | case 11: /* TODO: char inbyte (void); */ |
| 285 | env->active_tc.gpr[2] = -1; |
| 286 | break; |
| 287 | case 3: |
| 288 | case 12: |
| 289 | printf("%c", (char)(env->active_tc.gpr[4] & 0xFF)); |
| 290 | break; |
| 291 | case 17: |
| 292 | break; |
| 293 | case 158: |
| 294 | { |
| 295 | unsigned char *fmt = (void *)(uintptr_t)env->active_tc.gpr[4]; |
| 296 | printf("%s", fmt); |
| 297 | } |
| 298 | break; |
| 299 | } |
| 300 | } |
| 301 | |
| 302 | #ifdef TARGET_MIPS64 |
| 303 | target_ulong helper_lcsr_cpucfg(CPUMIPSState *env, target_ulong rs) |
| 304 | { |
| 305 | switch (rs) { |
| 306 | case 0: |
| 307 | return env->CP0_PRid; |
| 308 | case 1: |
| 309 | return env->lcsr_cpucfg1; |
| 310 | case 2: |
| 311 | return env->lcsr_cpucfg2; |
| 312 | default: |
| 313 | return 0; |
| 314 | } |
| 315 | } |
| 316 | #endif |
| 317 | |
| 318 | #if !defined(CONFIG_USER_ONLY) |
| 319 | |
| 320 | void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr, |
| 321 | MMUAccessType access_type, |
| 322 | int mmu_idx, uintptr_t retaddr) |
| 323 | { |
| 324 | CPUMIPSState *env = cpu_env(cs); |
| 325 | int error_code = 0; |
| 326 | int excp; |
| 327 | |
| 328 | if (!(env->hflags & MIPS_HFLAG_DM)) { |
| 329 | env->CP0_BadVAddr = addr; |
| 330 | } |
| 331 | |
| 332 | if (access_type == MMU_DATA_STORE) { |
| 333 | excp = EXCP_AdES; |
| 334 | } else { |
| 335 | excp = EXCP_AdEL; |
| 336 | if (access_type == MMU_INST_FETCH) { |
| 337 | error_code |= EXCP_INST_NOTAVAIL; |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | do_raise_exception_err(env, excp, error_code, retaddr); |
| 342 | } |
| 343 | |
| 344 | void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, |
| 345 | vaddr addr, unsigned size, |
| 346 | MMUAccessType access_type, |
| 347 | int mmu_idx, MemTxAttrs attrs, |
| 348 | MemTxResult response, uintptr_t retaddr) |
| 349 | { |
| 350 | MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cs); |
| 351 | CPUMIPSState *env = cpu_env(cs); |
| 352 | |
| 353 | if (access_type == MMU_INST_FETCH) { |
| 354 | do_raise_exception(env, EXCP_IBE, retaddr); |
| 355 | } else if (!mcc->no_data_aborts) { |
| 356 | do_raise_exception(env, EXCP_DBE, retaddr); |
| 357 | } |
| 358 | } |
| 359 | #endif /* !CONFIG_USER_ONLY */ |