| 1 | /* |
| 2 | * QEMU MIPS CPU |
| 3 | * |
| 4 | * Copyright (c) 2012 SUSE LINUX Products GmbH |
| 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 |
| 18 | * <http://www.gnu.org/licenses/lgpl-2.1.html> |
| 19 | */ |
| 20 | |
| 21 | #include "qemu/osdep.h" |
| 22 | #include "cpu.h" |
| 23 | #include "internal.h" |
| 24 | #include "exec/cputlb.h" |
| 25 | |
| 26 | /* Called for updates to CP0_Status. */ |
| 27 | void sync_c0_status(CPUMIPSState *env, CPUMIPSState *cpu, int tc) |
| 28 | { |
| 29 | int32_t tcstatus, *tcst; |
| 30 | uint32_t v = cpu->CP0_Status; |
| 31 | uint32_t cu, mx, asid, ksu; |
| 32 | uint32_t mask = ((1 << CP0TCSt_TCU3) |
| 33 | | (1 << CP0TCSt_TCU2) |
| 34 | | (1 << CP0TCSt_TCU1) |
| 35 | | (1 << CP0TCSt_TCU0) |
| 36 | | (1 << CP0TCSt_TMX) |
| 37 | | (3 << CP0TCSt_TKSU) |
| 38 | | (0xff << CP0TCSt_TASID)); |
| 39 | |
| 40 | cu = (v >> CP0St_CU0) & 0xf; |
| 41 | mx = (v >> CP0St_MX) & 0x1; |
| 42 | ksu = (v >> CP0St_KSU) & 0x3; |
| 43 | asid = env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask; |
| 44 | |
| 45 | tcstatus = cu << CP0TCSt_TCU0; |
| 46 | tcstatus |= mx << CP0TCSt_TMX; |
| 47 | tcstatus |= ksu << CP0TCSt_TKSU; |
| 48 | tcstatus |= asid; |
| 49 | |
| 50 | if (tc == cpu->current_tc) { |
| 51 | tcst = &cpu->active_tc.CP0_TCStatus; |
| 52 | } else { |
| 53 | tcst = &cpu->tcs[tc].CP0_TCStatus; |
| 54 | } |
| 55 | |
| 56 | *tcst &= ~mask; |
| 57 | *tcst |= tcstatus; |
| 58 | compute_hflags(cpu); |
| 59 | } |
| 60 | |
| 61 | void cpu_mips_store_status(CPUMIPSState *env, target_ulong val) |
| 62 | { |
| 63 | uint32_t mask = env->CP0_Status_rw_bitmask; |
| 64 | target_ulong old = env->CP0_Status; |
| 65 | |
| 66 | if (env->insn_flags & ISA_MIPS_R6) { |
| 67 | bool has_supervisor = extract32(mask, CP0St_KSU, 2) == 0x3; |
| 68 | #if defined(TARGET_MIPS64) |
| 69 | uint32_t ksux = (1 << CP0St_KX) & val; |
| 70 | ksux |= (ksux >> 1) & val; /* KX = 0 forces SX to be 0 */ |
| 71 | ksux |= (ksux >> 1) & val; /* SX = 0 forces UX to be 0 */ |
| 72 | val = (val & ~(7 << CP0St_UX)) | ksux; |
| 73 | #endif |
| 74 | if (has_supervisor && extract32(val, CP0St_KSU, 2) == 0x3) { |
| 75 | mask &= ~(3 << CP0St_KSU); |
| 76 | } |
| 77 | mask &= ~(((1 << CP0St_SR) | (1 << CP0St_NMI)) & val); |
| 78 | } |
| 79 | |
| 80 | env->CP0_Status = (old & ~mask) | (val & mask); |
| 81 | #if defined(TARGET_MIPS64) |
| 82 | if ((env->CP0_Status ^ old) & (old & (7 << CP0St_UX))) { |
| 83 | /* Access to at least one of the 64-bit segments has been disabled */ |
| 84 | tlb_flush(env_cpu(env)); |
| 85 | } |
| 86 | #endif |
| 87 | if (ase_mt_available(env)) { |
| 88 | sync_c0_status(env, env, env->current_tc); |
| 89 | } else { |
| 90 | compute_hflags(env); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | void cpu_mips_store_cause(CPUMIPSState *env, target_ulong val) |
| 95 | { |
| 96 | uint32_t mask = 0x00C00300; |
| 97 | uint32_t old = env->CP0_Cause; |
| 98 | int i; |
| 99 | |
| 100 | if (env->insn_flags & ISA_MIPS_R2) { |
| 101 | mask |= 1 << CP0Ca_DC; |
| 102 | } |
| 103 | if (env->insn_flags & ISA_MIPS_R6) { |
| 104 | mask &= ~((1 << CP0Ca_WP) & val); |
| 105 | } |
| 106 | |
| 107 | env->CP0_Cause = (env->CP0_Cause & ~mask) | (val & mask); |
| 108 | |
| 109 | if ((old ^ env->CP0_Cause) & (1 << CP0Ca_DC)) { |
| 110 | if (env->CP0_Cause & (1 << CP0Ca_DC)) { |
| 111 | cpu_mips_stop_count(env); |
| 112 | } else { |
| 113 | cpu_mips_start_count(env); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /* Set/reset software interrupts */ |
| 118 | for (i = 0 ; i < 2 ; i++) { |
| 119 | if ((old ^ env->CP0_Cause) & (1 << (CP0Ca_IP + i))) { |
| 120 | cpu_mips_soft_irq(env, i, env->CP0_Cause & (1 << (CP0Ca_IP + i))); |
| 121 | } |
| 122 | } |
| 123 | } |