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/exec-all.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 } 124