xref: /openbmc/qemu/target/ppc/helper_regs.c (revision 2da8a6bcdca72c7a79a9c732133eaeb9452242cc)
18a05fd9aSRichard Henderson /*
28a05fd9aSRichard Henderson  *  PowerPC emulation special registers manipulation helpers for qemu.
38a05fd9aSRichard Henderson  *
48a05fd9aSRichard Henderson  *  Copyright (c) 2003-2007 Jocelyn Mayer
58a05fd9aSRichard Henderson  *
68a05fd9aSRichard Henderson  * This library is free software; you can redistribute it and/or
78a05fd9aSRichard Henderson  * modify it under the terms of the GNU Lesser General Public
88a05fd9aSRichard Henderson  * License as published by the Free Software Foundation; either
98a05fd9aSRichard Henderson  * version 2.1 of the License, or (at your option) any later version.
108a05fd9aSRichard Henderson  *
118a05fd9aSRichard Henderson  * This library is distributed in the hope that it will be useful,
128a05fd9aSRichard Henderson  * but WITHOUT ANY WARRANTY; without even the implied warranty of
138a05fd9aSRichard Henderson  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
148a05fd9aSRichard Henderson  * Lesser General Public License for more details.
158a05fd9aSRichard Henderson  *
168a05fd9aSRichard Henderson  * You should have received a copy of the GNU Lesser General Public
178a05fd9aSRichard Henderson  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
188a05fd9aSRichard Henderson  */
198a05fd9aSRichard Henderson 
208a05fd9aSRichard Henderson #include "qemu/osdep.h"
212df4fe7aSRichard Henderson #include "cpu.h"
228a05fd9aSRichard Henderson #include "qemu/main-loop.h"
238a05fd9aSRichard Henderson #include "exec/exec-all.h"
248a05fd9aSRichard Henderson #include "sysemu/kvm.h"
258a05fd9aSRichard Henderson #include "helper_regs.h"
268a05fd9aSRichard Henderson 
278a05fd9aSRichard Henderson /* Swap temporary saved registers with GPRs */
288a05fd9aSRichard Henderson void hreg_swap_gpr_tgpr(CPUPPCState *env)
298a05fd9aSRichard Henderson {
308a05fd9aSRichard Henderson     target_ulong tmp;
318a05fd9aSRichard Henderson 
328a05fd9aSRichard Henderson     tmp = env->gpr[0];
338a05fd9aSRichard Henderson     env->gpr[0] = env->tgpr[0];
348a05fd9aSRichard Henderson     env->tgpr[0] = tmp;
358a05fd9aSRichard Henderson     tmp = env->gpr[1];
368a05fd9aSRichard Henderson     env->gpr[1] = env->tgpr[1];
378a05fd9aSRichard Henderson     env->tgpr[1] = tmp;
388a05fd9aSRichard Henderson     tmp = env->gpr[2];
398a05fd9aSRichard Henderson     env->gpr[2] = env->tgpr[2];
408a05fd9aSRichard Henderson     env->tgpr[2] = tmp;
418a05fd9aSRichard Henderson     tmp = env->gpr[3];
428a05fd9aSRichard Henderson     env->gpr[3] = env->tgpr[3];
438a05fd9aSRichard Henderson     env->tgpr[3] = tmp;
448a05fd9aSRichard Henderson }
458a05fd9aSRichard Henderson 
46*2da8a6bcSRichard Henderson static uint32_t hreg_compute_hflags_value(CPUPPCState *env)
478a05fd9aSRichard Henderson {
482df4fe7aSRichard Henderson     target_ulong msr = env->msr;
492df4fe7aSRichard Henderson     uint32_t ppc_flags = env->flags;
502df4fe7aSRichard Henderson     uint32_t hflags = 0;
512df4fe7aSRichard Henderson     uint32_t msr_mask;
528a05fd9aSRichard Henderson 
532df4fe7aSRichard Henderson     /* Some bits come straight across from MSR. */
542df4fe7aSRichard Henderson     QEMU_BUILD_BUG_ON(MSR_LE != HFLAGS_LE);
552df4fe7aSRichard Henderson     QEMU_BUILD_BUG_ON(MSR_PR != HFLAGS_PR);
562df4fe7aSRichard Henderson     QEMU_BUILD_BUG_ON(MSR_DR != HFLAGS_DR);
572df4fe7aSRichard Henderson     QEMU_BUILD_BUG_ON(MSR_FP != HFLAGS_FP);
582df4fe7aSRichard Henderson     msr_mask = ((1 << MSR_LE) | (1 << MSR_PR) |
59d764184dSRichard Henderson                 (1 << MSR_DR) | (1 << MSR_FP));
6018285046SRichard Henderson 
612df4fe7aSRichard Henderson     if (ppc_flags & POWERPC_FLAG_HID0_LE) {
6218285046SRichard Henderson         /*
6318285046SRichard Henderson          * Note that MSR_LE is not set in env->msr_mask for this cpu,
642df4fe7aSRichard Henderson          * and so will never be set in msr.
6518285046SRichard Henderson          */
6618285046SRichard Henderson         uint32_t le = extract32(env->spr[SPR_HID0], 3, 1);
672df4fe7aSRichard Henderson         hflags |= le << MSR_LE;
6818285046SRichard Henderson     }
692df4fe7aSRichard Henderson 
707da31f26SRichard Henderson     if (ppc_flags & POWERPC_FLAG_DE) {
717da31f26SRichard Henderson         target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0];
727da31f26SRichard Henderson         if (dbcr0 & DBCR0_ICMP) {
737da31f26SRichard Henderson             hflags |= 1 << HFLAGS_SE;
747da31f26SRichard Henderson         }
757da31f26SRichard Henderson         if (dbcr0 & DBCR0_BRT) {
767da31f26SRichard Henderson             hflags |= 1 << HFLAGS_BE;
777da31f26SRichard Henderson         }
787da31f26SRichard Henderson     } else {
792df4fe7aSRichard Henderson         if (ppc_flags & POWERPC_FLAG_BE) {
802df4fe7aSRichard Henderson             QEMU_BUILD_BUG_ON(MSR_BE != HFLAGS_BE);
812df4fe7aSRichard Henderson             msr_mask |= 1 << MSR_BE;
822df4fe7aSRichard Henderson         }
832df4fe7aSRichard Henderson         if (ppc_flags & POWERPC_FLAG_SE) {
842df4fe7aSRichard Henderson             QEMU_BUILD_BUG_ON(MSR_SE != HFLAGS_SE);
852df4fe7aSRichard Henderson             msr_mask |= 1 << MSR_SE;
862df4fe7aSRichard Henderson         }
877da31f26SRichard Henderson     }
882df4fe7aSRichard Henderson 
892df4fe7aSRichard Henderson     if (msr_is_64bit(env, msr)) {
902df4fe7aSRichard Henderson         hflags |= 1 << HFLAGS_64;
912df4fe7aSRichard Henderson     }
922df4fe7aSRichard Henderson     if ((ppc_flags & POWERPC_FLAG_SPE) && (msr & (1 << MSR_SPE))) {
932df4fe7aSRichard Henderson         hflags |= 1 << HFLAGS_SPE;
942df4fe7aSRichard Henderson     }
952df4fe7aSRichard Henderson     if (ppc_flags & POWERPC_FLAG_VRE) {
962df4fe7aSRichard Henderson         QEMU_BUILD_BUG_ON(MSR_VR != HFLAGS_VR);
972df4fe7aSRichard Henderson         msr_mask |= 1 << MSR_VR;
982df4fe7aSRichard Henderson     }
990e6bac3eSRichard Henderson     if (ppc_flags & POWERPC_FLAG_VSX) {
1000e6bac3eSRichard Henderson         QEMU_BUILD_BUG_ON(MSR_VSX != HFLAGS_VSX);
1010e6bac3eSRichard Henderson         msr_mask |= 1 << MSR_VSX;
1022df4fe7aSRichard Henderson     }
1032df4fe7aSRichard Henderson     if ((ppc_flags & POWERPC_FLAG_TM) && (msr & (1ull << MSR_TM))) {
1042df4fe7aSRichard Henderson         hflags |= 1 << HFLAGS_TM;
1052df4fe7aSRichard Henderson     }
106f03de3b4SRichard Henderson     if (env->spr[SPR_LPCR] & LPCR_GTSE) {
107f03de3b4SRichard Henderson         hflags |= 1 << HFLAGS_GTSE;
108f03de3b4SRichard Henderson     }
1092df4fe7aSRichard Henderson 
1102df4fe7aSRichard Henderson #ifndef CONFIG_USER_ONLY
1112df4fe7aSRichard Henderson     if (!env->has_hv_mode || (msr & (1ull << MSR_HV))) {
1122df4fe7aSRichard Henderson         hflags |= 1 << HFLAGS_HV;
1132df4fe7aSRichard Henderson     }
114d764184dSRichard Henderson 
115d764184dSRichard Henderson     /*
116d764184dSRichard Henderson      * This is our encoding for server processors. The architecture
117d764184dSRichard Henderson      * specifies that there is no such thing as userspace with
118d764184dSRichard Henderson      * translation off, however it appears that MacOS does it and some
119d764184dSRichard Henderson      * 32-bit CPUs support it. Weird...
120d764184dSRichard Henderson      *
121d764184dSRichard Henderson      *   0 = Guest User space virtual mode
122d764184dSRichard Henderson      *   1 = Guest Kernel space virtual mode
123d764184dSRichard Henderson      *   2 = Guest User space real mode
124d764184dSRichard Henderson      *   3 = Guest Kernel space real mode
125d764184dSRichard Henderson      *   4 = HV User space virtual mode
126d764184dSRichard Henderson      *   5 = HV Kernel space virtual mode
127d764184dSRichard Henderson      *   6 = HV User space real mode
128d764184dSRichard Henderson      *   7 = HV Kernel space real mode
129d764184dSRichard Henderson      *
130d764184dSRichard Henderson      * For BookE, we need 8 MMU modes as follow:
131d764184dSRichard Henderson      *
132d764184dSRichard Henderson      *  0 = AS 0 HV User space
133d764184dSRichard Henderson      *  1 = AS 0 HV Kernel space
134d764184dSRichard Henderson      *  2 = AS 1 HV User space
135d764184dSRichard Henderson      *  3 = AS 1 HV Kernel space
136d764184dSRichard Henderson      *  4 = AS 0 Guest User space
137d764184dSRichard Henderson      *  5 = AS 0 Guest Kernel space
138d764184dSRichard Henderson      *  6 = AS 1 Guest User space
139d764184dSRichard Henderson      *  7 = AS 1 Guest Kernel space
140d764184dSRichard Henderson      */
141d764184dSRichard Henderson     unsigned immu_idx, dmmu_idx;
142d764184dSRichard Henderson     dmmu_idx = msr & (1 << MSR_PR) ? 0 : 1;
143d764184dSRichard Henderson     if (env->mmu_model & POWERPC_MMU_BOOKE) {
144d764184dSRichard Henderson         dmmu_idx |= msr & (1 << MSR_GS) ? 4 : 0;
145d764184dSRichard Henderson         immu_idx = dmmu_idx;
146d764184dSRichard Henderson         immu_idx |= msr & (1 << MSR_IS) ? 2 : 0;
147d764184dSRichard Henderson         dmmu_idx |= msr & (1 << MSR_DS) ? 2 : 0;
148d764184dSRichard Henderson     } else {
149d764184dSRichard Henderson         dmmu_idx |= msr & (1ull << MSR_HV) ? 4 : 0;
150d764184dSRichard Henderson         immu_idx = dmmu_idx;
151d764184dSRichard Henderson         immu_idx |= msr & (1 << MSR_IR) ? 0 : 2;
152d764184dSRichard Henderson         dmmu_idx |= msr & (1 << MSR_DR) ? 0 : 2;
153d764184dSRichard Henderson     }
154d764184dSRichard Henderson     hflags |= immu_idx << HFLAGS_IMMU_IDX;
155d764184dSRichard Henderson     hflags |= dmmu_idx << HFLAGS_DMMU_IDX;
1562df4fe7aSRichard Henderson #endif
1572df4fe7aSRichard Henderson 
158*2da8a6bcSRichard Henderson     return hflags | (msr & msr_mask);
1598a05fd9aSRichard Henderson }
1608a05fd9aSRichard Henderson 
161*2da8a6bcSRichard Henderson void hreg_compute_hflags(CPUPPCState *env)
162*2da8a6bcSRichard Henderson {
163*2da8a6bcSRichard Henderson     env->hflags = hreg_compute_hflags_value(env);
164*2da8a6bcSRichard Henderson }
165*2da8a6bcSRichard Henderson 
166*2da8a6bcSRichard Henderson #ifdef CONFIG_DEBUG_TCG
167*2da8a6bcSRichard Henderson void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc,
168*2da8a6bcSRichard Henderson                           target_ulong *cs_base, uint32_t *flags)
169*2da8a6bcSRichard Henderson {
170*2da8a6bcSRichard Henderson     uint32_t hflags_current = env->hflags;
171*2da8a6bcSRichard Henderson     uint32_t hflags_rebuilt;
172*2da8a6bcSRichard Henderson 
173*2da8a6bcSRichard Henderson     *pc = env->nip;
174*2da8a6bcSRichard Henderson     *cs_base = 0;
175*2da8a6bcSRichard Henderson     *flags = hflags_current;
176*2da8a6bcSRichard Henderson 
177*2da8a6bcSRichard Henderson     hflags_rebuilt = hreg_compute_hflags_value(env);
178*2da8a6bcSRichard Henderson     if (unlikely(hflags_current != hflags_rebuilt)) {
179*2da8a6bcSRichard Henderson         cpu_abort(env_cpu(env),
180*2da8a6bcSRichard Henderson                   "TCG hflags mismatch (current:0x%08x rebuilt:0x%08x)\n",
181*2da8a6bcSRichard Henderson                   hflags_current, hflags_rebuilt);
182*2da8a6bcSRichard Henderson     }
183*2da8a6bcSRichard Henderson }
184*2da8a6bcSRichard Henderson #endif
185*2da8a6bcSRichard Henderson 
1868a05fd9aSRichard Henderson void cpu_interrupt_exittb(CPUState *cs)
1878a05fd9aSRichard Henderson {
1888a05fd9aSRichard Henderson     if (!kvm_enabled()) {
1898a05fd9aSRichard Henderson         return;
1908a05fd9aSRichard Henderson     }
1918a05fd9aSRichard Henderson 
1928a05fd9aSRichard Henderson     if (!qemu_mutex_iothread_locked()) {
1938a05fd9aSRichard Henderson         qemu_mutex_lock_iothread();
1948a05fd9aSRichard Henderson         cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
1958a05fd9aSRichard Henderson         qemu_mutex_unlock_iothread();
1968a05fd9aSRichard Henderson     } else {
1978a05fd9aSRichard Henderson         cpu_interrupt(cs, CPU_INTERRUPT_EXITTB);
1988a05fd9aSRichard Henderson     }
1998a05fd9aSRichard Henderson }
2008a05fd9aSRichard Henderson 
2018a05fd9aSRichard Henderson int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv)
2028a05fd9aSRichard Henderson {
2038a05fd9aSRichard Henderson     int excp;
2048a05fd9aSRichard Henderson #if !defined(CONFIG_USER_ONLY)
2058a05fd9aSRichard Henderson     CPUState *cs = env_cpu(env);
2068a05fd9aSRichard Henderson #endif
2078a05fd9aSRichard Henderson 
2088a05fd9aSRichard Henderson     excp = 0;
2098a05fd9aSRichard Henderson     value &= env->msr_mask;
2108a05fd9aSRichard Henderson #if !defined(CONFIG_USER_ONLY)
2118a05fd9aSRichard Henderson     /* Neither mtmsr nor guest state can alter HV */
2128a05fd9aSRichard Henderson     if (!alter_hv || !(env->msr & MSR_HVB)) {
2138a05fd9aSRichard Henderson         value &= ~MSR_HVB;
2148a05fd9aSRichard Henderson         value |= env->msr & MSR_HVB;
2158a05fd9aSRichard Henderson     }
2168a05fd9aSRichard Henderson     if (((value >> MSR_IR) & 1) != msr_ir ||
2178a05fd9aSRichard Henderson         ((value >> MSR_DR) & 1) != msr_dr) {
2188a05fd9aSRichard Henderson         cpu_interrupt_exittb(cs);
2198a05fd9aSRichard Henderson     }
2208a05fd9aSRichard Henderson     if ((env->mmu_model & POWERPC_MMU_BOOKE) &&
2218a05fd9aSRichard Henderson         ((value >> MSR_GS) & 1) != msr_gs) {
2228a05fd9aSRichard Henderson         cpu_interrupt_exittb(cs);
2238a05fd9aSRichard Henderson     }
2248a05fd9aSRichard Henderson     if (unlikely((env->flags & POWERPC_FLAG_TGPR) &&
2258a05fd9aSRichard Henderson                  ((value ^ env->msr) & (1 << MSR_TGPR)))) {
2268a05fd9aSRichard Henderson         /* Swap temporary saved registers with GPRs */
2278a05fd9aSRichard Henderson         hreg_swap_gpr_tgpr(env);
2288a05fd9aSRichard Henderson     }
2298a05fd9aSRichard Henderson     if (unlikely((value >> MSR_EP) & 1) != msr_ep) {
2308a05fd9aSRichard Henderson         /* Change the exception prefix on PowerPC 601 */
2318a05fd9aSRichard Henderson         env->excp_prefix = ((value >> MSR_EP) & 1) * 0xFFF00000;
2328a05fd9aSRichard Henderson     }
2338a05fd9aSRichard Henderson     /*
2348a05fd9aSRichard Henderson      * If PR=1 then EE, IR and DR must be 1
2358a05fd9aSRichard Henderson      *
2368a05fd9aSRichard Henderson      * Note: We only enforce this on 64-bit server processors.
2378a05fd9aSRichard Henderson      * It appears that:
2388a05fd9aSRichard Henderson      * - 32-bit implementations supports PR=1 and EE/DR/IR=0 and MacOS
2398a05fd9aSRichard Henderson      *   exploits it.
2408a05fd9aSRichard Henderson      * - 64-bit embedded implementations do not need any operation to be
2418a05fd9aSRichard Henderson      *   performed when PR is set.
2428a05fd9aSRichard Henderson      */
2438a05fd9aSRichard Henderson     if (is_book3s_arch2x(env) && ((value >> MSR_PR) & 1)) {
2448a05fd9aSRichard Henderson         value |= (1 << MSR_EE) | (1 << MSR_DR) | (1 << MSR_IR);
2458a05fd9aSRichard Henderson     }
2468a05fd9aSRichard Henderson #endif
2478a05fd9aSRichard Henderson     env->msr = value;
2488a05fd9aSRichard Henderson     hreg_compute_hflags(env);
2498a05fd9aSRichard Henderson #if !defined(CONFIG_USER_ONLY)
2508a05fd9aSRichard Henderson     if (unlikely(msr_pow == 1)) {
2518a05fd9aSRichard Henderson         if (!env->pending_interrupts && (*env->check_pow)(env)) {
2528a05fd9aSRichard Henderson             cs->halted = 1;
2538a05fd9aSRichard Henderson             excp = EXCP_HALTED;
2548a05fd9aSRichard Henderson         }
2558a05fd9aSRichard Henderson     }
2568a05fd9aSRichard Henderson #endif
2578a05fd9aSRichard Henderson 
2588a05fd9aSRichard Henderson     return excp;
2598a05fd9aSRichard Henderson }
2608a05fd9aSRichard Henderson 
2618a05fd9aSRichard Henderson #ifndef CONFIG_USER_ONLY
2628a05fd9aSRichard Henderson void check_tlb_flush(CPUPPCState *env, bool global)
2638a05fd9aSRichard Henderson {
2648a05fd9aSRichard Henderson     CPUState *cs = env_cpu(env);
2658a05fd9aSRichard Henderson 
2668a05fd9aSRichard Henderson     /* Handle global flushes first */
2678a05fd9aSRichard Henderson     if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) {
2688a05fd9aSRichard Henderson         env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH;
2698a05fd9aSRichard Henderson         env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
2708a05fd9aSRichard Henderson         tlb_flush_all_cpus_synced(cs);
2718a05fd9aSRichard Henderson         return;
2728a05fd9aSRichard Henderson     }
2738a05fd9aSRichard Henderson 
2748a05fd9aSRichard Henderson     /* Then handle local ones */
2758a05fd9aSRichard Henderson     if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) {
2768a05fd9aSRichard Henderson         env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH;
2778a05fd9aSRichard Henderson         tlb_flush(cs);
2788a05fd9aSRichard Henderson     }
2798a05fd9aSRichard Henderson }
2808a05fd9aSRichard Henderson #endif
281