提交 d5a103cd 编写于 作者: R Richard Henderson

target-s390: Reorg exception handling

Make the user path more like the system path.  Prepare for more kinds
of runtime exceptions.  Rename ILC to ILEN to make it clear that we
want to pass around a full instruction length, rather than a "code"
that happens to be stored one bit left in a larger field.
Signed-off-by: NRichard Henderson <rth@twiddle.net>
上级 3fde06f5
......@@ -2933,71 +2933,115 @@ void cpu_loop(CPUAlphaState *env)
#ifdef TARGET_S390X
void cpu_loop(CPUS390XState *env)
{
int trapnr;
int trapnr, n, sig;
target_siginfo_t info;
target_ulong addr;
while (1) {
trapnr = cpu_s390x_exec (env);
trapnr = cpu_s390x_exec(env);
switch (trapnr) {
case EXCP_INTERRUPT:
/* just indicate that signals should be handled asap */
/* Just indicate that signals should be handled asap. */
break;
case EXCP_DEBUG:
{
int sig;
sig = gdb_handlesig (env, TARGET_SIGTRAP);
if (sig) {
info.si_signo = sig;
info.si_errno = 0;
info.si_code = TARGET_TRAP_BRKPT;
queue_signal(env, info.si_signo, &info);
}
case EXCP_SVC:
n = env->int_svc_code;
if (!n) {
/* syscalls > 255 */
n = env->regs[1];
}
env->psw.addr += env->int_svc_ilen;
env->regs[2] = do_syscall(env, n, env->regs[2], env->regs[3],
env->regs[4], env->regs[5],
env->regs[6], env->regs[7], 0, 0);
break;
case EXCP_SVC:
{
int n = env->int_svc_code;
if (!n) {
/* syscalls > 255 */
n = env->regs[1];
}
env->psw.addr += env->int_svc_ilc;
env->regs[2] = do_syscall(env, n,
env->regs[2],
env->regs[3],
env->regs[4],
env->regs[5],
env->regs[6],
env->regs[7],
0, 0);
case EXCP_DEBUG:
sig = gdb_handlesig(env, TARGET_SIGTRAP);
if (sig) {
n = TARGET_TRAP_BRKPT;
goto do_signal_pc;
}
break;
case EXCP_ADDR:
{
info.si_signo = SIGSEGV;
info.si_errno = 0;
case EXCP_PGM:
n = env->int_pgm_code;
switch (n) {
case PGM_OPERATION:
case PGM_PRIVILEGED:
sig = SIGILL;
n = TARGET_ILL_ILLOPC;
goto do_signal_pc;
case PGM_PROTECTION:
case PGM_ADDRESSING:
sig = SIGSEGV;
/* XXX: check env->error_code */
info.si_code = TARGET_SEGV_MAPERR;
info._sifields._sigfault._addr = env->__excp_addr;
queue_signal(env, info.si_signo, &info);
n = TARGET_SEGV_MAPERR;
addr = env->__excp_addr;
goto do_signal;
case PGM_EXECUTE:
case PGM_SPECIFICATION:
case PGM_SPECIAL_OP:
case PGM_OPERAND:
do_sigill_opn:
sig = SIGILL;
n = TARGET_ILL_ILLOPN;
goto do_signal_pc;
case PGM_FIXPT_OVERFLOW:
sig = SIGFPE;
n = TARGET_FPE_INTOVF;
goto do_signal_pc;
case PGM_FIXPT_DIVIDE:
sig = SIGFPE;
n = TARGET_FPE_INTDIV;
goto do_signal_pc;
case PGM_DATA:
n = (env->fpc >> 8) & 0xff;
if (n == 0xff) {
/* compare-and-trap */
goto do_sigill_opn;
} else {
/* An IEEE exception, simulated or otherwise. */
if (n & 0x80) {
n = TARGET_FPE_FLTINV;
} else if (n & 0x40) {
n = TARGET_FPE_FLTDIV;
} else if (n & 0x20) {
n = TARGET_FPE_FLTOVF;
} else if (n & 0x10) {
n = TARGET_FPE_FLTUND;
} else if (n & 0x08) {
n = TARGET_FPE_FLTRES;
} else {
/* ??? Quantum exception; BFP, DFP error. */
goto do_sigill_opn;
}
sig = SIGFPE;
goto do_signal_pc;
}
default:
fprintf(stderr, "Unhandled program exception: %#x\n", n);
cpu_dump_state(env, stderr, fprintf, 0);
exit(1);
}
break;
case EXCP_SPEC:
{
fprintf(stderr,"specification exception insn 0x%08x%04x\n", ldl(env->psw.addr), lduw(env->psw.addr + 4));
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_code = TARGET_ILL_ILLOPC;
info._sifields._sigfault._addr = env->__excp_addr;
queue_signal(env, info.si_signo, &info);
}
do_signal_pc:
addr = env->psw.addr;
do_signal:
info.si_signo = sig;
info.si_errno = 0;
info.si_code = n;
info._sifields._sigfault._addr = addr;
queue_signal(env, info.si_signo, &info);
break;
default:
printf ("Unhandled trap: 0x%x\n", trapnr);
fprintf(stderr, "Unhandled trap: 0x%x\n", trapnr);
cpu_dump_state(env, stderr, fprintf, 0);
exit (1);
exit(1);
}
process_pending_signals (env);
}
......
......@@ -16,7 +16,7 @@ struct target_pt_regs {
target_psw_t psw;
abi_ulong gprs[TARGET_NUM_GPRS];
abi_ulong orig_gpr2;
unsigned short ilc;
unsigned short ilen;
unsigned short trap;
};
......
......@@ -79,10 +79,10 @@ typedef struct CPUS390XState {
uint64_t psa;
uint32_t int_pgm_code;
uint32_t int_pgm_ilc;
uint32_t int_pgm_ilen;
uint32_t int_svc_code;
uint32_t int_svc_ilc;
uint32_t int_svc_ilen;
uint64_t cregs[16]; /* control registers */
......@@ -253,25 +253,31 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc,
((env->psw.mask & PSW_MASK_32) ? FLAG_MASK_32 : 0);
}
static inline int get_ilc(uint8_t opc)
/* While the PoO talks about ILC (a number between 1-3) what is actually
stored in LowCore is shifted left one bit (an even between 2-6). As
this is the actual length of the insn and therefore more useful, that
is what we want to pass around and manipulate. To make sure that we
have applied this distinction universally, rename the "ILC" to "ILEN". */
static inline int get_ilen(uint8_t opc)
{
switch (opc >> 6) {
case 0:
return 1;
return 2;
case 1:
case 2:
return 2;
case 3:
return 3;
return 4;
default:
return 6;
}
return 0;
}
#define ILC_LATER 0x20
#define ILC_LATER_INC 0x21
#define ILC_LATER_INC_2 0x22
#ifndef CONFIG_USER_ONLY
/* In several cases of runtime exceptions, we havn't recorded the true
instruction length. Use these codes when raising exceptions in order
to re-compute the length by examining the insn in memory. */
#define ILEN_LATER 0x20
#define ILEN_LATER_INC 0x21
#endif
S390CPU *cpu_s390x_init(const char *cpu_model);
void s390x_translate_init(void);
......@@ -352,21 +358,10 @@ static inline void cpu_set_tls(CPUS390XState *env, target_ulong newtls)
#include "exec/exec-all.h"
#ifdef CONFIG_USER_ONLY
#define EXCP_OPEX 1 /* operation exception (sigill) */
#define EXCP_SVC 2 /* supervisor call (syscall) */
#define EXCP_ADDR 5 /* addressing exception */
#define EXCP_SPEC 6 /* specification exception */
#else
#define EXCP_EXT 1 /* external interrupt */
#define EXCP_SVC 2 /* supervisor call (syscall) */
#define EXCP_PGM 3 /* program interruption */
#endif /* CONFIG_USER_ONLY */
#define INTERRUPT_EXT (1 << 0)
#define INTERRUPT_TOD (1 << 1)
#define INTERRUPT_CPUTIMER (1 << 2)
......@@ -532,9 +527,9 @@ typedef struct LowCore
uint32_t ext_params; /* 0x080 */
uint16_t cpu_addr; /* 0x084 */
uint16_t ext_int_code; /* 0x086 */
uint16_t svc_ilc; /* 0x088 */
uint16_t svc_ilen; /* 0x088 */
uint16_t svc_code; /* 0x08a */
uint16_t pgm_ilc; /* 0x08c */
uint16_t pgm_ilen; /* 0x08c */
uint16_t pgm_code; /* 0x08e */
uint32_t data_exc_code; /* 0x090 */
uint16_t mon_class_num; /* 0x094 */
......@@ -924,6 +919,6 @@ uint32_t set_cc_nz_f32(float32 v);
uint32_t set_cc_nz_f64(float64 v);
/* misc_helper.c */
void program_interrupt(CPUS390XState *env, uint32_t code, int ilc);
void program_interrupt(CPUS390XState *env, uint32_t code, int ilen);
#endif
......@@ -99,10 +99,10 @@ void do_interrupt(CPUS390XState *env)
int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong address,
int rw, int mmu_idx)
{
/* fprintf(stderr, "%s: address 0x%lx rw %d mmu_idx %d\n",
__func__, address, rw, mmu_idx); */
env->exception_index = EXCP_ADDR;
/* FIXME: find out how this works on a real machine */
env->exception_index = EXCP_PGM;
env->int_pgm_code = PGM_ADDRESSING;
/* On real machines this value is dropped into LowMem. Since this
is userland, simply put this someplace that cpu_loop can find it. */
env->__excp_addr = address;
return 1;
}
......@@ -111,11 +111,11 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong address,
/* Ensure to exit the TB after this call! */
static void trigger_pgm_exception(CPUS390XState *env, uint32_t code,
uint32_t ilc)
uint32_t ilen)
{
env->exception_index = EXCP_PGM;
env->int_pgm_code = code;
env->int_pgm_ilc = ilc;
env->int_pgm_ilen = ilen;
}
static int trans_bits(CPUS390XState *env, uint64_t mode)
......@@ -143,30 +143,30 @@ static int trans_bits(CPUS390XState *env, uint64_t mode)
static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr,
uint64_t mode)
{
int ilc = ILC_LATER_INC_2;
int ilen = ILEN_LATER_INC;
int bits = trans_bits(env, mode) | 4;
DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
trigger_pgm_exception(env, PGM_PROTECTION, ilc);
trigger_pgm_exception(env, PGM_PROTECTION, ilen);
}
static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr,
uint32_t type, uint64_t asc, int rw)
{
int ilc = ILC_LATER;
int ilen = ILEN_LATER;
int bits = trans_bits(env, asc);
/* Code accesses have an undefined ilc. */
if (rw == 2) {
/* code has is undefined ilc */
ilc = 2;
ilen = 2;
}
DPRINTF("%s: vaddr=%016" PRIx64 " bits=%d\n", __func__, vaddr, bits);
stq_phys(env->psa + offsetof(LowCore, trans_exc_code), vaddr | bits);
trigger_pgm_exception(env, type, ilc);
trigger_pgm_exception(env, type, ilen);
}
static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
......@@ -406,7 +406,7 @@ int cpu_s390x_handle_mmu_fault(CPUS390XState *env, target_ulong orig_vaddr,
if (raddr > (ram_size + virtio_size)) {
DPRINTF("%s: aaddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__,
(uint64_t)aaddr, (uint64_t)ram_size);
trigger_pgm_exception(env, PGM_ADDRESSING, ILC_LATER);
trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_LATER);
return 1;
}
......@@ -480,9 +480,9 @@ static void do_svc_interrupt(CPUS390XState *env)
lowcore = cpu_physical_memory_map(env->psa, &len, 1);
lowcore->svc_code = cpu_to_be16(env->int_svc_code);
lowcore->svc_ilc = cpu_to_be16(env->int_svc_ilc);
lowcore->svc_ilen = cpu_to_be16(env->int_svc_ilen);
lowcore->svc_old_psw.mask = cpu_to_be64(get_psw_mask(env));
lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + (env->int_svc_ilc));
lowcore->svc_old_psw.addr = cpu_to_be64(env->psw.addr + env->int_svc_ilen);
mask = be64_to_cpu(lowcore->svc_new_psw.mask);
addr = be64_to_cpu(lowcore->svc_new_psw.addr);
......@@ -496,28 +496,26 @@ static void do_program_interrupt(CPUS390XState *env)
uint64_t mask, addr;
LowCore *lowcore;
hwaddr len = TARGET_PAGE_SIZE;
int ilc = env->int_pgm_ilc;
int ilen = env->int_pgm_ilen;
switch (ilc) {
case ILC_LATER:
ilc = get_ilc(cpu_ldub_code(env, env->psw.addr));
break;
case ILC_LATER_INC:
ilc = get_ilc(cpu_ldub_code(env, env->psw.addr));
env->psw.addr += ilc * 2;
switch (ilen) {
case ILEN_LATER:
ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
break;
case ILC_LATER_INC_2:
ilc = get_ilc(cpu_ldub_code(env, env->psw.addr)) * 2;
env->psw.addr += ilc;
case ILEN_LATER_INC:
ilen = get_ilen(cpu_ldub_code(env, env->psw.addr));
env->psw.addr += ilen;
break;
default:
assert(ilen == 2 || ilen == 4 || ilen == 6);
}
qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilc=%d\n",
__func__, env->int_pgm_code, ilc);
qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n",
__func__, env->int_pgm_code, ilen);
lowcore = cpu_physical_memory_map(env->psa, &len, 1);
lowcore->pgm_ilc = cpu_to_be16(ilc);
lowcore->pgm_ilen = cpu_to_be16(ilen);
lowcore->pgm_code = cpu_to_be16(env->int_pgm_code);
lowcore->program_old_psw.mask = cpu_to_be64(get_psw_mask(env));
lowcore->program_old_psw.addr = cpu_to_be64(env->psw.addr);
......@@ -527,7 +525,7 @@ static void do_program_interrupt(CPUS390XState *env)
cpu_physical_memory_unmap(lowcore, len, 1, len);
DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__,
env->int_pgm_code, ilc, env->psw.mask,
env->int_pgm_code, ilen, env->psw.mask,
env->psw.addr);
load_psw(env, mask, addr);
......
......@@ -594,7 +594,7 @@ uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
env->psw.addr = ret - 4;
env->int_svc_code = (insn | v1) & 0xff;
env->int_svc_ilc = 4;
env->int_svc_ilen = 4;
helper_exception(env, EXCP_SVC);
} else if ((insn & 0xff00) == 0xbf00) {
uint32_t insn2, r1, r3, b2, d2;
......
......@@ -41,7 +41,7 @@
#define HELPER_LOG(x...)
#endif
/* raise an exception */
/* Raise an exception statically from a TB. */
void HELPER(exception)(CPUS390XState *env, uint32_t excp)
{
HELPER_LOG("%s: exception %d\n", __func__, excp);
......@@ -50,7 +50,7 @@ void HELPER(exception)(CPUS390XState *env, uint32_t excp)
}
#ifndef CONFIG_USER_ONLY
void program_interrupt(CPUS390XState *env, uint32_t code, int ilc)
void program_interrupt(CPUS390XState *env, uint32_t code, int ilen)
{
qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n",
env->psw.addr);
......@@ -61,7 +61,7 @@ void program_interrupt(CPUS390XState *env, uint32_t code, int ilc)
#endif
} else {
env->int_pgm_code = code;
env->int_pgm_ilc = ilc;
env->int_pgm_ilen = ilen;
env->exception_index = EXCP_PGM;
cpu_loop_exit(env);
}
......@@ -105,7 +105,7 @@ uint64_t HELPER(diag)(CPUS390XState *env, uint32_t num, uint64_t mem,
}
if (r) {
program_interrupt(env, PGM_OPERATION, ILC_LATER_INC);
program_interrupt(env, PGM_OPERATION, ILEN_LATER_INC);
}
return r;
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册