2012-05-30 06:23:22 +02:00
|
|
|
/*
|
|
|
|
* PowerPC exception emulation helpers for QEMU.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2003-2007 Jocelyn Mayer
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
2016-01-26 19:16:58 +01:00
|
|
|
#include "qemu/osdep.h"
|
2012-05-30 06:23:22 +02:00
|
|
|
#include "cpu.h"
|
2014-04-08 07:31:41 +02:00
|
|
|
#include "exec/helper-proto.h"
|
2016-03-15 13:18:37 +01:00
|
|
|
#include "exec/exec-all.h"
|
2014-03-28 19:42:10 +01:00
|
|
|
#include "exec/cpu_ldst.h"
|
2012-05-30 06:23:22 +02:00
|
|
|
|
|
|
|
#include "helper_regs.h"
|
|
|
|
|
|
|
|
//#define DEBUG_OP
|
2015-11-13 13:34:23 +01:00
|
|
|
//#define DEBUG_SOFTWARE_TLB
|
2012-05-30 06:23:22 +02:00
|
|
|
//#define DEBUG_EXCEPTIONS
|
|
|
|
|
2012-05-30 06:23:25 +02:00
|
|
|
#ifdef DEBUG_EXCEPTIONS
|
|
|
|
# define LOG_EXCP(...) qemu_log(__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
# define LOG_EXCP(...) do { } while (0)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* PowerPC Hypercall emulation */
|
|
|
|
|
2012-05-03 06:03:45 +02:00
|
|
|
void (*cpu_ppc_hypercall)(PowerPCCPU *);
|
2012-05-30 06:23:25 +02:00
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Exception processing */
|
|
|
|
#if defined(CONFIG_USER_ONLY)
|
2013-02-02 10:57:51 +01:00
|
|
|
void ppc_cpu_do_interrupt(CPUState *cs)
|
2012-05-30 06:23:25 +02:00
|
|
|
{
|
2013-02-02 10:57:51 +01:00
|
|
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
|
|
|
CPUPPCState *env = &cpu->env;
|
|
|
|
|
2013-08-26 08:31:06 +02:00
|
|
|
cs->exception_index = POWERPC_EXCP_NONE;
|
2012-05-30 06:23:25 +02:00
|
|
|
env->error_code = 0;
|
|
|
|
}
|
|
|
|
|
2014-09-13 18:45:32 +02:00
|
|
|
static void ppc_hw_interrupt(CPUPPCState *env)
|
2012-05-30 06:23:25 +02:00
|
|
|
{
|
2013-08-26 08:31:06 +02:00
|
|
|
CPUState *cs = CPU(ppc_env_get_cpu(env));
|
|
|
|
|
|
|
|
cs->exception_index = POWERPC_EXCP_NONE;
|
2012-05-30 06:23:25 +02:00
|
|
|
env->error_code = 0;
|
|
|
|
}
|
|
|
|
#else /* defined(CONFIG_USER_ONLY) */
|
|
|
|
static inline void dump_syscall(CPUPPCState *env)
|
|
|
|
{
|
|
|
|
qemu_log_mask(CPU_LOG_INT, "syscall r0=%016" PRIx64 " r3=%016" PRIx64
|
|
|
|
" r4=%016" PRIx64 " r5=%016" PRIx64 " r6=%016" PRIx64
|
|
|
|
" nip=" TARGET_FMT_lx "\n",
|
|
|
|
ppc_dump_gpr(env, 0), ppc_dump_gpr(env, 3),
|
|
|
|
ppc_dump_gpr(env, 4), ppc_dump_gpr(env, 5),
|
|
|
|
ppc_dump_gpr(env, 6), env->nip);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Note that this function should be greatly optimized
|
|
|
|
* when called with a constant excp, from ppc_hw_interrupt
|
|
|
|
*/
|
2012-05-03 05:55:58 +02:00
|
|
|
static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp)
|
2012-05-30 06:23:25 +02:00
|
|
|
{
|
2013-08-26 08:31:06 +02:00
|
|
|
CPUState *cs = CPU(cpu);
|
2012-05-03 05:55:58 +02:00
|
|
|
CPUPPCState *env = &cpu->env;
|
2012-05-30 06:23:25 +02:00
|
|
|
target_ulong msr, new_msr, vector;
|
2016-06-21 23:48:48 +02:00
|
|
|
int srr0, srr1, asrr0, asrr1, lev, ail;
|
|
|
|
bool lpes0;
|
2012-05-30 06:23:25 +02:00
|
|
|
|
|
|
|
qemu_log_mask(CPU_LOG_INT, "Raise exception at " TARGET_FMT_lx
|
|
|
|
" => %08x (%02x)\n", env->nip, excp, env->error_code);
|
|
|
|
|
|
|
|
/* new srr1 value excluding must-be-zero bits */
|
2012-12-21 17:15:41 +01:00
|
|
|
if (excp_model == POWERPC_EXCP_BOOKE) {
|
|
|
|
msr = env->msr;
|
|
|
|
} else {
|
|
|
|
msr = env->msr & ~0x783f0000ULL;
|
|
|
|
}
|
2012-05-30 06:23:25 +02:00
|
|
|
|
2016-06-21 23:48:48 +02:00
|
|
|
/* new interrupt handler msr preserves existing HV and ME unless
|
|
|
|
* explicitly overriden
|
|
|
|
*/
|
|
|
|
new_msr = env->msr & (((target_ulong)1 << MSR_ME) | MSR_HVB);
|
2012-05-30 06:23:25 +02:00
|
|
|
|
|
|
|
/* target registers */
|
|
|
|
srr0 = SPR_SRR0;
|
|
|
|
srr1 = SPR_SRR1;
|
|
|
|
asrr0 = -1;
|
|
|
|
asrr1 = -1;
|
|
|
|
|
2016-06-21 23:48:55 +02:00
|
|
|
/* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */
|
|
|
|
if (env->in_pm_state) {
|
|
|
|
env->in_pm_state = false;
|
|
|
|
|
|
|
|
/* Pretend to be returning from doze always as we don't lose state */
|
|
|
|
msr |= (0x1ull << (63 - 47));
|
|
|
|
|
|
|
|
/* Non-machine check are routed to 0x100 with a wakeup cause
|
|
|
|
* encoded in SRR1
|
|
|
|
*/
|
|
|
|
if (excp != POWERPC_EXCP_MCHECK) {
|
|
|
|
switch (excp) {
|
|
|
|
case POWERPC_EXCP_RESET:
|
|
|
|
msr |= 0x4ull << (63 - 45);
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_EXTERNAL:
|
|
|
|
msr |= 0x8ull << (63 - 45);
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_DECR:
|
|
|
|
msr |= 0x6ull << (63 - 45);
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_SDOOR:
|
|
|
|
msr |= 0x5ull << (63 - 45);
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_SDOOR_HV:
|
|
|
|
msr |= 0x3ull << (63 - 45);
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_HV_MAINT:
|
|
|
|
msr |= 0xaull << (63 - 45);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
cpu_abort(cs, "Unsupported exception %d in Power Save mode\n",
|
|
|
|
excp);
|
|
|
|
}
|
|
|
|
excp = POWERPC_EXCP_RESET;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-03 19:57:50 +02:00
|
|
|
/* Exception targetting modifiers
|
2016-06-21 23:48:48 +02:00
|
|
|
*
|
|
|
|
* LPES0 is supported on POWER7/8
|
|
|
|
* LPES1 is not supported (old iSeries mode)
|
|
|
|
*
|
|
|
|
* On anything else, we behave as if LPES0 is 1
|
|
|
|
* (externals don't alter MSR:HV)
|
2016-04-03 19:57:50 +02:00
|
|
|
*
|
|
|
|
* AIL is initialized here but can be cleared by
|
|
|
|
* selected exceptions
|
|
|
|
*/
|
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
if (excp_model == POWERPC_EXCP_POWER7 ||
|
|
|
|
excp_model == POWERPC_EXCP_POWER8) {
|
2016-06-21 23:48:48 +02:00
|
|
|
lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
|
2016-04-03 19:57:50 +02:00
|
|
|
if (excp_model == POWERPC_EXCP_POWER8) {
|
|
|
|
ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT;
|
|
|
|
} else {
|
|
|
|
ail = 0;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
#endif /* defined(TARGET_PPC64) */
|
|
|
|
{
|
2016-06-21 23:48:48 +02:00
|
|
|
lpes0 = true;
|
2016-04-03 19:57:50 +02:00
|
|
|
ail = 0;
|
|
|
|
}
|
|
|
|
|
2016-06-21 23:48:51 +02:00
|
|
|
/* Hypervisor emulation assistance interrupt only exists on server
|
|
|
|
* arch 2.05 server or later. We also don't want to generate it if
|
|
|
|
* we don't have HVB in msr_mask (PAPR mode).
|
|
|
|
*/
|
|
|
|
if (excp == POWERPC_EXCP_HV_EMU
|
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
&& !((env->mmu_model & POWERPC_MMU_64) && (env->msr_mask & MSR_HVB))
|
|
|
|
#endif /* defined(TARGET_PPC64) */
|
|
|
|
|
|
|
|
) {
|
|
|
|
excp = POWERPC_EXCP_PROGRAM;
|
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:25 +02:00
|
|
|
switch (excp) {
|
|
|
|
case POWERPC_EXCP_NONE:
|
|
|
|
/* Should never happen */
|
|
|
|
return;
|
|
|
|
case POWERPC_EXCP_CRITICAL: /* Critical input */
|
|
|
|
switch (excp_model) {
|
|
|
|
case POWERPC_EXCP_40x:
|
|
|
|
srr0 = SPR_40x_SRR2;
|
|
|
|
srr1 = SPR_40x_SRR3;
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_BOOKE:
|
|
|
|
srr0 = SPR_BOOKE_CSRR0;
|
|
|
|
srr1 = SPR_BOOKE_CSRR1;
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_G2:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
goto excp_invalid;
|
|
|
|
}
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_MCHECK: /* Machine check exception */
|
|
|
|
if (msr_me == 0) {
|
|
|
|
/* Machine check exception is not enabled.
|
|
|
|
* Enter checkstop state.
|
|
|
|
*/
|
2015-11-13 13:16:27 +01:00
|
|
|
fprintf(stderr, "Machine check while not allowed. "
|
|
|
|
"Entering checkstop state\n");
|
|
|
|
if (qemu_log_separate()) {
|
2012-05-30 06:23:25 +02:00
|
|
|
qemu_log("Machine check while not allowed. "
|
|
|
|
"Entering checkstop state\n");
|
|
|
|
}
|
2013-01-17 18:51:17 +01:00
|
|
|
cs->halted = 1;
|
|
|
|
cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
|
2012-05-30 06:23:25 +02:00
|
|
|
}
|
2016-06-21 23:48:48 +02:00
|
|
|
new_msr |= (target_ulong)MSR_HVB;
|
2016-04-03 19:57:50 +02:00
|
|
|
ail = 0;
|
2012-05-30 06:23:25 +02:00
|
|
|
|
|
|
|
/* machine check exceptions don't have ME set */
|
|
|
|
new_msr &= ~((target_ulong)1 << MSR_ME);
|
|
|
|
|
|
|
|
/* XXX: should also have something loaded in DAR / DSISR */
|
|
|
|
switch (excp_model) {
|
|
|
|
case POWERPC_EXCP_40x:
|
|
|
|
srr0 = SPR_40x_SRR2;
|
|
|
|
srr1 = SPR_40x_SRR3;
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_BOOKE:
|
2012-12-21 17:15:41 +01:00
|
|
|
/* FIXME: choose one or the other based on CPU type */
|
2012-05-30 06:23:25 +02:00
|
|
|
srr0 = SPR_BOOKE_MCSRR0;
|
|
|
|
srr1 = SPR_BOOKE_MCSRR1;
|
|
|
|
asrr0 = SPR_BOOKE_CSRR0;
|
|
|
|
asrr1 = SPR_BOOKE_CSRR1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_DSI: /* Data storage exception */
|
|
|
|
LOG_EXCP("DSI exception: DSISR=" TARGET_FMT_lx" DAR=" TARGET_FMT_lx
|
|
|
|
"\n", env->spr[SPR_DSISR], env->spr[SPR_DAR]);
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_ISI: /* Instruction storage exception */
|
|
|
|
LOG_EXCP("ISI exception: msr=" TARGET_FMT_lx ", nip=" TARGET_FMT_lx
|
|
|
|
"\n", msr, env->nip);
|
|
|
|
msr |= env->error_code;
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_EXTERNAL: /* External input */
|
2013-11-15 14:46:38 +01:00
|
|
|
cs = CPU(cpu);
|
|
|
|
|
2016-06-21 23:48:48 +02:00
|
|
|
if (!lpes0) {
|
2012-05-30 06:23:25 +02:00
|
|
|
new_msr |= (target_ulong)MSR_HVB;
|
2016-06-21 23:48:48 +02:00
|
|
|
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
|
|
|
srr0 = SPR_HSRR0;
|
|
|
|
srr1 = SPR_HSRR1;
|
2012-05-30 06:23:25 +02:00
|
|
|
}
|
2013-01-04 11:21:04 +01:00
|
|
|
if (env->mpic_proxy) {
|
|
|
|
/* IACK the IRQ on delivery */
|
2013-11-15 14:46:38 +01:00
|
|
|
env->spr[SPR_BOOKE_EPR] = ldl_phys(cs->as, env->mpic_iack);
|
2013-01-04 11:21:04 +01:00
|
|
|
}
|
2012-05-30 06:23:25 +02:00
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_ALIGN: /* Alignment exception */
|
|
|
|
/* XXX: this is false */
|
|
|
|
/* Get rS/rD and rA from faulting opcode */
|
2012-05-30 06:23:40 +02:00
|
|
|
env->spr[SPR_DSISR] |= (cpu_ldl_code(env, (env->nip - 4))
|
|
|
|
& 0x03FF0000) >> 16;
|
2015-07-02 06:44:06 +02:00
|
|
|
goto store_next;
|
2012-05-30 06:23:25 +02:00
|
|
|
case POWERPC_EXCP_PROGRAM: /* Program exception */
|
|
|
|
switch (env->error_code & ~0xF) {
|
|
|
|
case POWERPC_EXCP_FP:
|
|
|
|
if ((msr_fe0 == 0 && msr_fe1 == 0) || msr_fp == 0) {
|
|
|
|
LOG_EXCP("Ignore floating point exception\n");
|
2013-08-26 08:31:06 +02:00
|
|
|
cs->exception_index = POWERPC_EXCP_NONE;
|
2012-05-30 06:23:25 +02:00
|
|
|
env->error_code = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
msr |= 0x00100000;
|
|
|
|
if (msr_fe0 == msr_fe1) {
|
|
|
|
goto store_next;
|
|
|
|
}
|
|
|
|
msr |= 0x00010000;
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_INVAL:
|
|
|
|
LOG_EXCP("Invalid instruction at " TARGET_FMT_lx "\n", env->nip);
|
|
|
|
msr |= 0x00080000;
|
|
|
|
env->spr[SPR_BOOKE_ESR] = ESR_PIL;
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_PRIV:
|
|
|
|
msr |= 0x00040000;
|
|
|
|
env->spr[SPR_BOOKE_ESR] = ESR_PPR;
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_TRAP:
|
|
|
|
msr |= 0x00020000;
|
|
|
|
env->spr[SPR_BOOKE_ESR] = ESR_PTR;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Should never occur */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Invalid program exception %d. Aborting\n",
|
2012-05-30 06:23:25 +02:00
|
|
|
env->error_code);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto store_current;
|
2016-06-21 23:48:51 +02:00
|
|
|
case POWERPC_EXCP_HV_EMU:
|
|
|
|
srr0 = SPR_HSRR0;
|
|
|
|
srr1 = SPR_HSRR1;
|
|
|
|
new_msr |= (target_ulong)MSR_HVB;
|
|
|
|
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
|
|
|
goto store_current;
|
2012-05-30 06:23:25 +02:00
|
|
|
case POWERPC_EXCP_FPU: /* Floating-point unavailable exception */
|
|
|
|
goto store_current;
|
|
|
|
case POWERPC_EXCP_SYSCALL: /* System call exception */
|
|
|
|
dump_syscall(env);
|
|
|
|
lev = env->error_code;
|
2016-06-21 23:48:48 +02:00
|
|
|
|
|
|
|
/* "PAPR mode" built-in hypercall emulation */
|
2012-05-30 06:23:25 +02:00
|
|
|
if ((lev == 1) && cpu_ppc_hypercall) {
|
2012-05-03 06:03:45 +02:00
|
|
|
cpu_ppc_hypercall(cpu);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-06-21 23:48:48 +02:00
|
|
|
if (lev == 1) {
|
2012-05-30 06:23:25 +02:00
|
|
|
new_msr |= (target_ulong)MSR_HVB;
|
|
|
|
}
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_APU: /* Auxiliary processor unavailable */
|
|
|
|
goto store_current;
|
|
|
|
case POWERPC_EXCP_DECR: /* Decrementer exception */
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_FIT: /* Fixed-interval timer interrupt */
|
|
|
|
/* FIT on 4xx */
|
|
|
|
LOG_EXCP("FIT exception\n");
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_WDT: /* Watchdog timer interrupt */
|
|
|
|
LOG_EXCP("WDT exception\n");
|
|
|
|
switch (excp_model) {
|
|
|
|
case POWERPC_EXCP_BOOKE:
|
|
|
|
srr0 = SPR_BOOKE_CSRR0;
|
|
|
|
srr1 = SPR_BOOKE_CSRR1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_DTLB: /* Data TLB error */
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_ITLB: /* Instruction TLB error */
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_DEBUG: /* Debug interrupt */
|
|
|
|
switch (excp_model) {
|
|
|
|
case POWERPC_EXCP_BOOKE:
|
2012-12-21 17:15:41 +01:00
|
|
|
/* FIXME: choose one or the other based on CPU type */
|
2012-05-30 06:23:25 +02:00
|
|
|
srr0 = SPR_BOOKE_DSRR0;
|
|
|
|
srr1 = SPR_BOOKE_DSRR1;
|
|
|
|
asrr0 = SPR_BOOKE_CSRR0;
|
|
|
|
asrr1 = SPR_BOOKE_CSRR1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Debug exception is not implemented yet !\n");
|
2012-05-30 06:23:25 +02:00
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */
|
|
|
|
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
|
|
|
|
goto store_current;
|
|
|
|
case POWERPC_EXCP_EFPDI: /* Embedded floating-point data interrupt */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Embedded floating point data exception "
|
2012-05-30 06:23:25 +02:00
|
|
|
"is not implemented yet !\n");
|
|
|
|
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_EFPRI: /* Embedded floating-point round interrupt */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Embedded floating point round exception "
|
2012-05-30 06:23:25 +02:00
|
|
|
"is not implemented yet !\n");
|
|
|
|
env->spr[SPR_BOOKE_ESR] = ESR_SPV;
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_EPERFM: /* Embedded performance monitor interrupt */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs,
|
2012-05-30 06:23:25 +02:00
|
|
|
"Performance counter exception is not implemented yet !\n");
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_DOORI: /* Embedded doorbell interrupt */
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_DOORCI: /* Embedded doorbell critical interrupt */
|
|
|
|
srr0 = SPR_BOOKE_CSRR0;
|
|
|
|
srr1 = SPR_BOOKE_CSRR1;
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_RESET: /* System reset exception */
|
|
|
|
if (msr_pow) {
|
|
|
|
/* indicate that we resumed from power save mode */
|
|
|
|
msr |= 0x10000;
|
|
|
|
} else {
|
|
|
|
new_msr &= ~((target_ulong)1 << MSR_ME);
|
|
|
|
}
|
|
|
|
|
2016-06-21 23:48:48 +02:00
|
|
|
new_msr |= (target_ulong)MSR_HVB;
|
2016-04-03 19:57:50 +02:00
|
|
|
ail = 0;
|
2012-05-30 06:23:25 +02:00
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_DSEG: /* Data segment exception */
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_ISEG: /* Instruction segment exception */
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_HDECR: /* Hypervisor decrementer exception */
|
|
|
|
srr0 = SPR_HSRR0;
|
|
|
|
srr1 = SPR_HSRR1;
|
|
|
|
new_msr |= (target_ulong)MSR_HVB;
|
|
|
|
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_TRACE: /* Trace exception */
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_HDSI: /* Hypervisor data storage exception */
|
|
|
|
srr0 = SPR_HSRR0;
|
|
|
|
srr1 = SPR_HSRR1;
|
|
|
|
new_msr |= (target_ulong)MSR_HVB;
|
|
|
|
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_HISI: /* Hypervisor instruction storage exception */
|
|
|
|
srr0 = SPR_HSRR0;
|
|
|
|
srr1 = SPR_HSRR1;
|
|
|
|
new_msr |= (target_ulong)MSR_HVB;
|
|
|
|
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_HDSEG: /* Hypervisor data segment exception */
|
|
|
|
srr0 = SPR_HSRR0;
|
|
|
|
srr1 = SPR_HSRR1;
|
|
|
|
new_msr |= (target_ulong)MSR_HVB;
|
|
|
|
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */
|
|
|
|
srr0 = SPR_HSRR0;
|
|
|
|
srr1 = SPR_HSRR1;
|
|
|
|
new_msr |= (target_ulong)MSR_HVB;
|
|
|
|
new_msr |= env->msr & ((target_ulong)1 << MSR_RI);
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_VPU: /* Vector unavailable exception */
|
|
|
|
goto store_current;
|
2013-10-22 13:06:17 +02:00
|
|
|
case POWERPC_EXCP_VSXU: /* VSX unavailable exception */
|
|
|
|
goto store_current;
|
2014-06-04 14:50:56 +02:00
|
|
|
case POWERPC_EXCP_FU: /* Facility unavailable exception */
|
|
|
|
goto store_current;
|
2012-05-30 06:23:25 +02:00
|
|
|
case POWERPC_EXCP_PIT: /* Programmable interval timer interrupt */
|
|
|
|
LOG_EXCP("PIT exception\n");
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_IO: /* IO error exception */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "601 IO error exception is not implemented yet !\n");
|
2012-05-30 06:23:25 +02:00
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_RUNM: /* Run mode exception */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "601 run mode exception is not implemented yet !\n");
|
2012-05-30 06:23:25 +02:00
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_EMUL: /* Emulation trap exception */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "602 emulation trap exception "
|
2012-05-30 06:23:25 +02:00
|
|
|
"is not implemented yet !\n");
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_IFTLB: /* Instruction fetch TLB error */
|
|
|
|
switch (excp_model) {
|
|
|
|
case POWERPC_EXCP_602:
|
|
|
|
case POWERPC_EXCP_603:
|
|
|
|
case POWERPC_EXCP_603E:
|
|
|
|
case POWERPC_EXCP_G2:
|
|
|
|
goto tlb_miss_tgpr;
|
|
|
|
case POWERPC_EXCP_7x5:
|
|
|
|
goto tlb_miss;
|
|
|
|
case POWERPC_EXCP_74xx:
|
|
|
|
goto tlb_miss_74xx;
|
|
|
|
default:
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Invalid instruction TLB miss exception\n");
|
2012-05-30 06:23:25 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_DLTLB: /* Data load TLB miss */
|
|
|
|
switch (excp_model) {
|
|
|
|
case POWERPC_EXCP_602:
|
|
|
|
case POWERPC_EXCP_603:
|
|
|
|
case POWERPC_EXCP_603E:
|
|
|
|
case POWERPC_EXCP_G2:
|
|
|
|
goto tlb_miss_tgpr;
|
|
|
|
case POWERPC_EXCP_7x5:
|
|
|
|
goto tlb_miss;
|
|
|
|
case POWERPC_EXCP_74xx:
|
|
|
|
goto tlb_miss_74xx;
|
|
|
|
default:
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Invalid data load TLB miss exception\n");
|
2012-05-30 06:23:25 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_DSTLB: /* Data store TLB miss */
|
|
|
|
switch (excp_model) {
|
|
|
|
case POWERPC_EXCP_602:
|
|
|
|
case POWERPC_EXCP_603:
|
|
|
|
case POWERPC_EXCP_603E:
|
|
|
|
case POWERPC_EXCP_G2:
|
|
|
|
tlb_miss_tgpr:
|
|
|
|
/* Swap temporary saved registers with GPRs */
|
|
|
|
if (!(new_msr & ((target_ulong)1 << MSR_TGPR))) {
|
|
|
|
new_msr |= (target_ulong)1 << MSR_TGPR;
|
|
|
|
hreg_swap_gpr_tgpr(env);
|
|
|
|
}
|
|
|
|
goto tlb_miss;
|
|
|
|
case POWERPC_EXCP_7x5:
|
|
|
|
tlb_miss:
|
|
|
|
#if defined(DEBUG_SOFTWARE_TLB)
|
|
|
|
if (qemu_log_enabled()) {
|
|
|
|
const char *es;
|
|
|
|
target_ulong *miss, *cmp;
|
|
|
|
int en;
|
|
|
|
|
|
|
|
if (excp == POWERPC_EXCP_IFTLB) {
|
|
|
|
es = "I";
|
|
|
|
en = 'I';
|
|
|
|
miss = &env->spr[SPR_IMISS];
|
|
|
|
cmp = &env->spr[SPR_ICMP];
|
|
|
|
} else {
|
|
|
|
if (excp == POWERPC_EXCP_DLTLB) {
|
|
|
|
es = "DL";
|
|
|
|
} else {
|
|
|
|
es = "DS";
|
|
|
|
}
|
|
|
|
en = 'D';
|
|
|
|
miss = &env->spr[SPR_DMISS];
|
|
|
|
cmp = &env->spr[SPR_DCMP];
|
|
|
|
}
|
|
|
|
qemu_log("6xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
|
|
|
|
TARGET_FMT_lx " H1 " TARGET_FMT_lx " H2 "
|
|
|
|
TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
|
|
|
|
env->spr[SPR_HASH1], env->spr[SPR_HASH2],
|
|
|
|
env->error_code);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
msr |= env->crf[0] << 28;
|
|
|
|
msr |= env->error_code; /* key, D/I, S/L bits */
|
|
|
|
/* Set way using a LRU mechanism */
|
|
|
|
msr |= ((env->last_way + 1) & (env->nb_ways - 1)) << 17;
|
|
|
|
break;
|
|
|
|
case POWERPC_EXCP_74xx:
|
|
|
|
tlb_miss_74xx:
|
|
|
|
#if defined(DEBUG_SOFTWARE_TLB)
|
|
|
|
if (qemu_log_enabled()) {
|
|
|
|
const char *es;
|
|
|
|
target_ulong *miss, *cmp;
|
|
|
|
int en;
|
|
|
|
|
|
|
|
if (excp == POWERPC_EXCP_IFTLB) {
|
|
|
|
es = "I";
|
|
|
|
en = 'I';
|
|
|
|
miss = &env->spr[SPR_TLBMISS];
|
|
|
|
cmp = &env->spr[SPR_PTEHI];
|
|
|
|
} else {
|
|
|
|
if (excp == POWERPC_EXCP_DLTLB) {
|
|
|
|
es = "DL";
|
|
|
|
} else {
|
|
|
|
es = "DS";
|
|
|
|
}
|
|
|
|
en = 'D';
|
|
|
|
miss = &env->spr[SPR_TLBMISS];
|
|
|
|
cmp = &env->spr[SPR_PTEHI];
|
|
|
|
}
|
|
|
|
qemu_log("74xx %sTLB miss: %cM " TARGET_FMT_lx " %cC "
|
|
|
|
TARGET_FMT_lx " %08x\n", es, en, *miss, en, *cmp,
|
|
|
|
env->error_code);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
msr |= env->error_code; /* key bit */
|
|
|
|
break;
|
|
|
|
default:
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Invalid data store TLB miss exception\n");
|
2012-05-30 06:23:25 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_FPA: /* Floating-point assist exception */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Floating point assist exception "
|
2012-05-30 06:23:25 +02:00
|
|
|
"is not implemented yet !\n");
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_DABR: /* Data address breakpoint */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "DABR exception is not implemented yet !\n");
|
2012-05-30 06:23:25 +02:00
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_IABR: /* Instruction address breakpoint */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "IABR exception is not implemented yet !\n");
|
2012-05-30 06:23:25 +02:00
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_SMI: /* System management interrupt */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "SMI exception is not implemented yet !\n");
|
2012-05-30 06:23:25 +02:00
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_THERM: /* Thermal interrupt */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Thermal management exception "
|
2012-05-30 06:23:25 +02:00
|
|
|
"is not implemented yet !\n");
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_PERFM: /* Embedded performance monitor interrupt */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs,
|
2012-05-30 06:23:25 +02:00
|
|
|
"Performance counter exception is not implemented yet !\n");
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_VPUA: /* Vector assist exception */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "VPU assist exception is not implemented yet !\n");
|
2012-05-30 06:23:25 +02:00
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_SOFTP: /* Soft patch exception */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs,
|
2012-05-30 06:23:25 +02:00
|
|
|
"970 soft-patch exception is not implemented yet !\n");
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_MAINT: /* Maintenance exception */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs,
|
2012-05-30 06:23:25 +02:00
|
|
|
"970 maintenance exception is not implemented yet !\n");
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_MEXTBR: /* Maskable external breakpoint */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Maskable external exception "
|
2012-05-30 06:23:25 +02:00
|
|
|
"is not implemented yet !\n");
|
|
|
|
goto store_next;
|
|
|
|
case POWERPC_EXCP_NMEXTBR: /* Non maskable external breakpoint */
|
|
|
|
/* XXX: TODO */
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Non maskable external exception "
|
2012-05-30 06:23:25 +02:00
|
|
|
"is not implemented yet !\n");
|
|
|
|
goto store_next;
|
|
|
|
default:
|
|
|
|
excp_invalid:
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Invalid PowerPC exception %d. Aborting\n", excp);
|
2012-05-30 06:23:25 +02:00
|
|
|
break;
|
|
|
|
store_current:
|
|
|
|
/* save current instruction location */
|
|
|
|
env->spr[srr0] = env->nip - 4;
|
|
|
|
break;
|
|
|
|
store_next:
|
|
|
|
/* save next instruction location */
|
|
|
|
env->spr[srr0] = env->nip;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Save MSR */
|
|
|
|
env->spr[srr1] = msr;
|
2016-06-21 23:48:48 +02:00
|
|
|
|
|
|
|
/* Sanity check */
|
|
|
|
if (!(env->msr_mask & MSR_HVB) && (srr0 == SPR_HSRR0)) {
|
|
|
|
cpu_abort(cs, "Trying to deliver HV exception %d with "
|
|
|
|
"no HV support\n", excp);
|
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:25 +02:00
|
|
|
/* If any alternate SRR register are defined, duplicate saved values */
|
|
|
|
if (asrr0 != -1) {
|
|
|
|
env->spr[asrr0] = env->spr[srr0];
|
|
|
|
}
|
|
|
|
if (asrr1 != -1) {
|
|
|
|
env->spr[asrr1] = env->spr[srr1];
|
|
|
|
}
|
2014-06-04 14:51:05 +02:00
|
|
|
|
2016-06-21 23:48:48 +02:00
|
|
|
/* Sort out endianness of interrupt, this differs depending on the
|
|
|
|
* CPU, the HV mode, etc...
|
|
|
|
*/
|
2013-08-07 02:47:01 +02:00
|
|
|
#ifdef TARGET_PPC64
|
2016-06-21 23:48:48 +02:00
|
|
|
if (excp_model == POWERPC_EXCP_POWER7) {
|
|
|
|
if (!(new_msr & MSR_HVB) && (env->spr[SPR_LPCR] & LPCR_ILE)) {
|
|
|
|
new_msr |= (target_ulong)1 << MSR_LE;
|
|
|
|
}
|
|
|
|
} else if (excp_model == POWERPC_EXCP_POWER8) {
|
|
|
|
if (new_msr & MSR_HVB) {
|
|
|
|
if (env->spr[SPR_HID0] & HID0_HILE) {
|
|
|
|
new_msr |= (target_ulong)1 << MSR_LE;
|
|
|
|
}
|
|
|
|
} else if (env->spr[SPR_LPCR] & LPCR_ILE) {
|
2013-08-07 02:47:01 +02:00
|
|
|
new_msr |= (target_ulong)1 << MSR_LE;
|
|
|
|
}
|
|
|
|
} else if (msr_ile) {
|
|
|
|
new_msr |= (target_ulong)1 << MSR_LE;
|
|
|
|
}
|
|
|
|
#else
|
2012-05-30 06:23:25 +02:00
|
|
|
if (msr_ile) {
|
|
|
|
new_msr |= (target_ulong)1 << MSR_LE;
|
|
|
|
}
|
2013-08-07 02:47:01 +02:00
|
|
|
#endif
|
2012-05-30 06:23:25 +02:00
|
|
|
|
|
|
|
/* Jump to handler */
|
|
|
|
vector = env->excp_vectors[excp];
|
|
|
|
if (vector == (target_ulong)-1ULL) {
|
2013-09-03 17:38:47 +02:00
|
|
|
cpu_abort(cs, "Raised an exception without defined vector %d\n",
|
2012-05-30 06:23:25 +02:00
|
|
|
excp);
|
|
|
|
}
|
|
|
|
vector |= env->excp_prefix;
|
2016-04-03 19:57:50 +02:00
|
|
|
|
|
|
|
/* AIL only works if there is no HV transition and we are running with
|
|
|
|
* translations enabled
|
|
|
|
*/
|
2016-06-21 23:48:48 +02:00
|
|
|
if (!((msr >> MSR_IR) & 1) || !((msr >> MSR_DR) & 1) ||
|
|
|
|
((new_msr & MSR_HVB) && !(msr & MSR_HVB))) {
|
2016-04-03 19:57:50 +02:00
|
|
|
ail = 0;
|
|
|
|
}
|
|
|
|
/* Handle AIL */
|
|
|
|
if (ail) {
|
|
|
|
new_msr |= (1 << MSR_IR) | (1 << MSR_DR);
|
|
|
|
switch(ail) {
|
|
|
|
case AIL_0001_8000:
|
|
|
|
vector |= 0x18000;
|
|
|
|
break;
|
|
|
|
case AIL_C000_0000_0000_4000:
|
|
|
|
vector |= 0xc000000000004000ull;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
cpu_abort(cs, "Invalid AIL combination %d\n", ail);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:25 +02:00
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
if (excp_model == POWERPC_EXCP_BOOKE) {
|
2012-06-20 21:20:29 +02:00
|
|
|
if (env->spr[SPR_BOOKE_EPCR] & EPCR_ICM) {
|
|
|
|
/* Cat.64-bit: EPCR.ICM is copied to MSR.CM */
|
2012-05-30 06:23:25 +02:00
|
|
|
new_msr |= (target_ulong)1 << MSR_CM;
|
2012-06-20 21:20:29 +02:00
|
|
|
} else {
|
|
|
|
vector = (uint32_t)vector;
|
2012-05-30 06:23:25 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!msr_isf && !(env->mmu_model & POWERPC_MMU_64)) {
|
|
|
|
vector = (uint32_t)vector;
|
|
|
|
} else {
|
|
|
|
new_msr |= (target_ulong)1 << MSR_SF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2016-06-03 14:11:18 +02:00
|
|
|
/* We don't use hreg_store_msr here as already have treated
|
|
|
|
* any special case that could occur. Just store MSR and update hflags
|
|
|
|
*
|
|
|
|
* Note: We *MUST* not use hreg_store_msr() as-is anyway because it
|
|
|
|
* will prevent setting of the HV bit which some exceptions might need
|
|
|
|
* to do.
|
2012-05-30 06:23:25 +02:00
|
|
|
*/
|
|
|
|
env->msr = new_msr & env->msr_mask;
|
|
|
|
hreg_compute_hflags(env);
|
|
|
|
env->nip = vector;
|
|
|
|
/* Reset exception state */
|
2013-08-26 08:31:06 +02:00
|
|
|
cs->exception_index = POWERPC_EXCP_NONE;
|
2012-05-30 06:23:25 +02:00
|
|
|
env->error_code = 0;
|
ppc: Do some batching of TCG tlb flushes
On ppc64 especially, we flush the tlb on any slbie or tlbie instruction.
However, those instructions often come in bursts of 3 or more (context
switch will favor a series of slbie's for example to an slbia if the
SLB has less than a certain number of entries in it, and tlbie's can
happen in a series, with PAPR, H_BULK_REMOVE can remove up to 4 entries
at a time.
Doing a tlb_flush() each time is a waste of time. We end up doing a memset
of the whole TLB, reloading it for the next instruction, memset'ing again,
etc...
Those instructions don't have to take effect immediately. For slbie, they
can wait for the next context synchronizing event. For tlbie, the next
tlbsync.
This implements batching by keeping a flag that indicates that we have a
TLB in need of flushing. We check it on interrupts, rfi's, isync's and
tlbsync and flush the TLB if needed.
This reduces the number of tlb_flush() on a boot to a ubuntu installer
first dialog screen from roughly 360K down to 36K.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: added a 'CPUPPCState *' variable in h_remove() and
h_bulk_remove() ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
[dwg: removed spurious whitespace change, use 0/1 not true/false
consistently, since tlb_need_flush has int type]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2016-05-03 18:03:25 +02:00
|
|
|
|
|
|
|
/* Any interrupt is context synchronizing, check if TCG TLB
|
|
|
|
* needs a delayed flush on ppc64
|
|
|
|
*/
|
|
|
|
check_tlb_flush(env);
|
2012-05-30 06:23:25 +02:00
|
|
|
}
|
|
|
|
|
2013-02-02 10:57:51 +01:00
|
|
|
void ppc_cpu_do_interrupt(CPUState *cs)
|
2012-05-30 06:23:25 +02:00
|
|
|
{
|
2013-02-02 10:57:51 +01:00
|
|
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
|
|
|
CPUPPCState *env = &cpu->env;
|
2012-05-03 05:55:58 +02:00
|
|
|
|
2013-08-26 08:31:06 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, cs->exception_index);
|
2012-05-30 06:23:25 +02:00
|
|
|
}
|
|
|
|
|
2014-09-13 18:45:32 +02:00
|
|
|
static void ppc_hw_interrupt(CPUPPCState *env)
|
2012-05-30 06:23:25 +02:00
|
|
|
{
|
2012-05-03 05:55:58 +02:00
|
|
|
PowerPCCPU *cpu = ppc_env_get_cpu(env);
|
2012-05-30 06:23:25 +02:00
|
|
|
#if 0
|
2013-01-17 18:51:17 +01:00
|
|
|
CPUState *cs = CPU(cpu);
|
|
|
|
|
2012-05-30 06:23:25 +02:00
|
|
|
qemu_log_mask(CPU_LOG_INT, "%s: %p pending %08x req %08x me %d ee %d\n",
|
2013-01-17 18:51:17 +01:00
|
|
|
__func__, env, env->pending_interrupts,
|
|
|
|
cs->interrupt_request, (int)msr_me, (int)msr_ee);
|
2012-05-30 06:23:25 +02:00
|
|
|
#endif
|
|
|
|
/* External reset */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Machine check exception */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_MCHECK);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#if 0 /* TODO */
|
|
|
|
/* External debug exception */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DEBUG);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
2016-06-27 08:55:19 +02:00
|
|
|
/* Hypervisor decrementer exception */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) {
|
|
|
|
/* LPCR will be clear when not supported so this will work */
|
|
|
|
bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE);
|
|
|
|
if ((msr_ee != 0 || msr_hv == 0) && hdice) {
|
|
|
|
/* HDEC clears on delivery */
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2016-06-27 08:55:17 +02:00
|
|
|
/* Extermal interrupt can ignore MSR:EE under some circumstances */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) {
|
|
|
|
bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0);
|
|
|
|
if (msr_ee != 0 || (env->has_hv_mode && msr_hv == 0 && !lpes0)) {
|
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2012-05-30 06:23:25 +02:00
|
|
|
if (msr_ce != 0) {
|
|
|
|
/* External critical interrupt */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) {
|
|
|
|
/* Taking a critical external interrupt does not clear the external
|
|
|
|
* critical interrupt status
|
|
|
|
*/
|
|
|
|
#if 0
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CEXT);
|
|
|
|
#endif
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_CRITICAL);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (msr_ee != 0) {
|
|
|
|
/* Watchdog timer on embedded PowerPC */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_WDT);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_CDOORBELL)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CDOORBELL);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORCI);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Fixed interval timer on embedded PowerPC */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_FIT);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Programmable interval timer on embedded PowerPC */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_PIT);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Decrementer exception */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) {
|
2014-04-06 01:32:06 +02:00
|
|
|
if (ppc_decr_clear_on_delivery(env)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR);
|
|
|
|
}
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DECR);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_DOORBELL)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DOORBELL);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_DOORI);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_PERFM)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PERFM);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_PERFM);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* Thermal interrupt */
|
|
|
|
if (env->pending_interrupts & (1 << PPC_INTERRUPT_THERM)) {
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_THERM);
|
2012-05-03 05:55:58 +02:00
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_THERM);
|
2012-05-30 06:23:25 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-08-20 14:16:36 +02:00
|
|
|
|
|
|
|
void ppc_cpu_do_system_reset(CPUState *cs)
|
|
|
|
{
|
|
|
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
|
|
|
CPUPPCState *env = &cpu->env;
|
|
|
|
|
|
|
|
powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_RESET);
|
|
|
|
}
|
2012-05-30 06:23:25 +02:00
|
|
|
#endif /* !CONFIG_USER_ONLY */
|
|
|
|
|
2014-09-13 18:45:32 +02:00
|
|
|
bool ppc_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
|
|
|
|
{
|
|
|
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
|
|
|
CPUPPCState *env = &cpu->env;
|
|
|
|
|
|
|
|
if (interrupt_request & CPU_INTERRUPT_HARD) {
|
|
|
|
ppc_hw_interrupt(env);
|
|
|
|
if (env->pending_interrupts == 0) {
|
|
|
|
cs->interrupt_request &= ~CPU_INTERRUPT_HARD;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:25 +02:00
|
|
|
#if defined(DEBUG_OP)
|
|
|
|
static void cpu_dump_rfi(target_ulong RA, target_ulong msr)
|
|
|
|
{
|
|
|
|
qemu_log("Return from exception at " TARGET_FMT_lx " with flags "
|
|
|
|
TARGET_FMT_lx "\n", RA, msr);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-05-30 06:23:22 +02:00
|
|
|
/*****************************************************************************/
|
|
|
|
/* Exceptions processing helpers */
|
|
|
|
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_raise_exception_err(CPUPPCState *env, uint32_t exception,
|
|
|
|
uint32_t error_code)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2013-08-26 08:31:06 +02:00
|
|
|
CPUState *cs = CPU(ppc_env_get_cpu(env));
|
|
|
|
|
2012-05-30 06:23:22 +02:00
|
|
|
#if 0
|
|
|
|
printf("Raise exception %3x code : %d\n", exception, error_code);
|
|
|
|
#endif
|
2013-08-26 08:31:06 +02:00
|
|
|
cs->exception_index = exception;
|
2012-05-30 06:23:22 +02:00
|
|
|
env->error_code = error_code;
|
2013-08-27 17:52:12 +02:00
|
|
|
cpu_loop_exit(cs);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_raise_exception(CPUPPCState *env, uint32_t exception)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2012-05-30 06:23:23 +02:00
|
|
|
helper_raise_exception_err(env, exception, 0);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if !defined(CONFIG_USER_ONLY)
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_store_msr(CPUPPCState *env, target_ulong val)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2013-01-17 18:51:17 +01:00
|
|
|
CPUState *cs;
|
|
|
|
|
2012-05-30 06:23:22 +02:00
|
|
|
val = hreg_store_msr(env, val, 0);
|
|
|
|
if (val != 0) {
|
2013-01-17 18:51:17 +01:00
|
|
|
cs = CPU(ppc_env_get_cpu(env));
|
|
|
|
cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
|
2012-05-30 06:23:23 +02:00
|
|
|
helper_raise_exception(env, val);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-21 23:48:55 +02:00
|
|
|
#if defined(TARGET_PPC64)
|
|
|
|
void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn)
|
|
|
|
{
|
|
|
|
CPUState *cs;
|
|
|
|
|
|
|
|
cs = CPU(ppc_env_get_cpu(env));
|
|
|
|
cs->halted = 1;
|
|
|
|
env->in_pm_state = true;
|
|
|
|
|
2016-06-27 08:55:19 +02:00
|
|
|
/* The architecture specifies that HDEC interrupts are
|
|
|
|
* discarded in PM states
|
|
|
|
*/
|
|
|
|
env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR);
|
|
|
|
|
2016-06-21 23:48:55 +02:00
|
|
|
/* Technically, nap doesn't set EE, but if we don't set it
|
|
|
|
* then ppc_hw_interrupt() won't deliver. We could add some
|
|
|
|
* other tests there based on LPCR but it's simpler to just
|
|
|
|
* whack EE in. It will be cleared by the 0x100 at wakeup
|
|
|
|
* anyway. It will still be observable by the guest in SRR1
|
|
|
|
* but this doesn't seem to be a problem.
|
|
|
|
*/
|
|
|
|
env->msr |= (1ull << MSR_EE);
|
|
|
|
helper_raise_exception(env, EXCP_HLT);
|
|
|
|
}
|
|
|
|
#endif /* defined(TARGET_PPC64) */
|
|
|
|
|
2016-06-21 23:48:46 +02:00
|
|
|
static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2013-01-17 18:51:17 +01:00
|
|
|
CPUState *cs = CPU(ppc_env_get_cpu(env));
|
|
|
|
|
2016-06-21 23:48:46 +02:00
|
|
|
/* MSR:POW cannot be set by any form of rfi */
|
|
|
|
msr &= ~(1ULL << MSR_POW);
|
|
|
|
|
2012-05-30 06:23:22 +02:00
|
|
|
#if defined(TARGET_PPC64)
|
2016-06-21 23:48:46 +02:00
|
|
|
/* Switching to 32-bit ? Crop the nip */
|
|
|
|
if (!msr_is_64bit(env, msr)) {
|
2012-05-30 06:23:22 +02:00
|
|
|
nip = (uint32_t)nip;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
nip = (uint32_t)nip;
|
|
|
|
#endif
|
|
|
|
/* XXX: beware: this is false if VLE is supported */
|
|
|
|
env->nip = nip & ~((target_ulong)0x00000003);
|
|
|
|
hreg_store_msr(env, msr, 1);
|
|
|
|
#if defined(DEBUG_OP)
|
|
|
|
cpu_dump_rfi(env->nip, env->msr);
|
|
|
|
#endif
|
|
|
|
/* No need to raise an exception here,
|
|
|
|
* as rfi is always the last insn of a TB
|
|
|
|
*/
|
2013-01-17 18:51:17 +01:00
|
|
|
cs->interrupt_request |= CPU_INTERRUPT_EXITTB;
|
ppc: Do some batching of TCG tlb flushes
On ppc64 especially, we flush the tlb on any slbie or tlbie instruction.
However, those instructions often come in bursts of 3 or more (context
switch will favor a series of slbie's for example to an slbia if the
SLB has less than a certain number of entries in it, and tlbie's can
happen in a series, with PAPR, H_BULK_REMOVE can remove up to 4 entries
at a time.
Doing a tlb_flush() each time is a waste of time. We end up doing a memset
of the whole TLB, reloading it for the next instruction, memset'ing again,
etc...
Those instructions don't have to take effect immediately. For slbie, they
can wait for the next context synchronizing event. For tlbie, the next
tlbsync.
This implements batching by keeping a flag that indicates that we have a
TLB in need of flushing. We check it on interrupts, rfi's, isync's and
tlbsync and flush the TLB if needed.
This reduces the number of tlb_flush() on a boot to a ubuntu installer
first dialog screen from roughly 360K down to 36K.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
[clg: added a 'CPUPPCState *' variable in h_remove() and
h_bulk_remove() ]
Signed-off-by: Cédric Le Goater <clg@kaod.org>
[dwg: removed spurious whitespace change, use 0/1 not true/false
consistently, since tlb_need_flush has int type]
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2016-05-03 18:03:25 +02:00
|
|
|
|
|
|
|
/* Context synchronizing: check if TCG TLB needs flush */
|
|
|
|
check_tlb_flush(env);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_rfi(CPUPPCState *env)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2016-06-21 23:48:46 +02:00
|
|
|
do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1] & 0xfffffffful);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
|
2016-06-21 23:48:46 +02:00
|
|
|
#define MSR_BOOK3S_MASK
|
2012-05-30 06:23:22 +02:00
|
|
|
#if defined(TARGET_PPC64)
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_rfid(CPUPPCState *env)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2016-06-21 23:48:46 +02:00
|
|
|
/* The architeture defines a number of rules for which bits
|
|
|
|
* can change but in practice, we handle this in hreg_store_msr()
|
|
|
|
* which will be called by do_rfi(), so there is no need to filter
|
|
|
|
* here
|
|
|
|
*/
|
|
|
|
do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1]);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_hrfid(CPUPPCState *env)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2016-06-21 23:48:46 +02:00
|
|
|
do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Embedded PowerPC specific helpers */
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_40x_rfci(CPUPPCState *env)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2016-06-21 23:48:46 +02:00
|
|
|
do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3]);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_rfci(CPUPPCState *env)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2016-06-21 23:48:46 +02:00
|
|
|
do_rfi(env, env->spr[SPR_BOOKE_CSRR0], env->spr[SPR_BOOKE_CSRR1]);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_rfdi(CPUPPCState *env)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2012-12-21 17:15:41 +01:00
|
|
|
/* FIXME: choose CSRR1 or DSRR1 based on cpu type */
|
2016-06-21 23:48:46 +02:00
|
|
|
do_rfi(env, env->spr[SPR_BOOKE_DSRR0], env->spr[SPR_BOOKE_DSRR1]);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_rfmci(CPUPPCState *env)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2012-12-21 17:15:41 +01:00
|
|
|
/* FIXME: choose CSRR1 or MCSRR1 based on cpu type */
|
2016-06-21 23:48:46 +02:00
|
|
|
do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], env->spr[SPR_BOOKE_MCSRR1]);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
|
|
|
|
uint32_t flags)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
|
|
|
if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) ||
|
|
|
|
((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) ||
|
|
|
|
((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) ||
|
|
|
|
((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) ||
|
|
|
|
((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) {
|
2012-05-30 06:23:23 +02:00
|
|
|
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
|
|
|
|
POWERPC_EXCP_TRAP);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(TARGET_PPC64)
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2,
|
|
|
|
uint32_t flags)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
|
|
|
if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) ||
|
|
|
|
((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) ||
|
|
|
|
((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) ||
|
|
|
|
((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) ||
|
|
|
|
((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) {
|
2012-05-30 06:23:23 +02:00
|
|
|
helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
|
|
|
|
POWERPC_EXCP_TRAP);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* PowerPC 601 specific instructions (POWER bridge) */
|
|
|
|
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_rfsvc(CPUPPCState *env)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
2016-06-21 23:48:46 +02:00
|
|
|
do_rfi(env, env->lr, env->ctr & 0x0000FFFF);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Embedded.Processor Control */
|
|
|
|
static int dbell2irq(target_ulong rb)
|
|
|
|
{
|
|
|
|
int msg = rb & DBELL_TYPE_MASK;
|
|
|
|
int irq = -1;
|
|
|
|
|
|
|
|
switch (msg) {
|
|
|
|
case DBELL_TYPE_DBELL:
|
|
|
|
irq = PPC_INTERRUPT_DOORBELL;
|
|
|
|
break;
|
|
|
|
case DBELL_TYPE_DBELL_CRIT:
|
|
|
|
irq = PPC_INTERRUPT_CDOORBELL;
|
|
|
|
break;
|
|
|
|
case DBELL_TYPE_G_DBELL:
|
|
|
|
case DBELL_TYPE_G_DBELL_CRIT:
|
|
|
|
case DBELL_TYPE_G_DBELL_MC:
|
|
|
|
/* XXX implement */
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return irq;
|
|
|
|
}
|
|
|
|
|
2012-05-30 06:23:23 +02:00
|
|
|
void helper_msgclr(CPUPPCState *env, target_ulong rb)
|
2012-05-30 06:23:22 +02:00
|
|
|
{
|
|
|
|
int irq = dbell2irq(rb);
|
|
|
|
|
|
|
|
if (irq < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
env->pending_interrupts &= ~(1 << irq);
|
|
|
|
}
|
|
|
|
|
|
|
|
void helper_msgsnd(target_ulong rb)
|
|
|
|
{
|
|
|
|
int irq = dbell2irq(rb);
|
|
|
|
int pir = rb & DBELL_PIRTAG_MASK;
|
2013-05-29 22:29:20 +02:00
|
|
|
CPUState *cs;
|
2012-05-30 06:23:22 +02:00
|
|
|
|
|
|
|
if (irq < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-24 23:50:24 +02:00
|
|
|
CPU_FOREACH(cs) {
|
2013-05-29 22:29:20 +02:00
|
|
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
|
|
|
CPUPPCState *cenv = &cpu->env;
|
|
|
|
|
2012-05-30 06:23:22 +02:00
|
|
|
if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) {
|
|
|
|
cenv->pending_interrupts |= 1 << irq;
|
2013-05-29 22:29:20 +02:00
|
|
|
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
|
2012-05-30 06:23:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|