PowerPC merge: real time TB and decrementer - faster and simpler exception handling (Jocelyn Mayer)

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@841 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2004-05-21 12:59:32 +00:00
parent 4a0fb71e67
commit 9fddaa0c0c
14 changed files with 475 additions and 309 deletions

View File

@ -635,6 +635,7 @@ target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr);
#define CPU_LOG_EXEC (1 << 5) #define CPU_LOG_EXEC (1 << 5)
#define CPU_LOG_PCALL (1 << 6) #define CPU_LOG_PCALL (1 << 6)
#define CPU_LOG_IOPORT (1 << 7) #define CPU_LOG_IOPORT (1 << 7)
#define CPU_LOG_TB_CPU (1 << 8)
/* define log items */ /* define log items */
typedef struct CPULogItem { typedef struct CPULogItem {

View File

@ -241,11 +241,25 @@ int cpu_exec(CPUState *env1)
#endif #endif
} }
#elif defined(TARGET_PPC) #elif defined(TARGET_PPC)
#if 0
if ((interrupt_request & CPU_INTERRUPT_RESET)) {
cpu_ppc_reset(env);
}
#endif
if (msr_ee != 0) {
if ((interrupt_request & CPU_INTERRUPT_HARD)) { if ((interrupt_request & CPU_INTERRUPT_HARD)) {
do_queue_exception(EXCP_EXTERNAL); /* Raise it */
if (check_exception_state(env)) env->exception_index = EXCP_EXTERNAL;
env->error_code = 0;
do_interrupt(env); do_interrupt(env);
env->interrupt_request &= ~CPU_INTERRUPT_HARD; env->interrupt_request &= ~CPU_INTERRUPT_HARD;
} else if ((interrupt_request & CPU_INTERRUPT_TIMER)) {
/* Raise it */
env->exception_index = EXCP_DECR;
env->error_code = 0;
do_interrupt(env);
env->interrupt_request &= ~CPU_INTERRUPT_TIMER;
}
} }
#endif #endif
if (interrupt_request & CPU_INTERRUPT_EXITTB) { if (interrupt_request & CPU_INTERRUPT_EXITTB) {
@ -757,7 +771,7 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
/* we restore the process signal mask as the sigreturn should /* we restore the process signal mask as the sigreturn should
do it (XXX: use sigsetjmp) */ do it (XXX: use sigsetjmp) */
sigprocmask(SIG_SETMASK, old_set, NULL); sigprocmask(SIG_SETMASK, old_set, NULL);
do_queue_exception_err(env->exception_index, env->error_code); do_raise_exception_err(env->exception_index, env->error_code);
} else { } else {
/* activate soft MMU for this block */ /* activate soft MMU for this block */
cpu_resume_from_signal(env, puc); cpu_resume_from_signal(env, puc);

2
exec.c
View File

@ -1132,6 +1132,8 @@ CPULogItem cpu_log_items[] = {
"show interrupts/exceptions in short format" }, "show interrupts/exceptions in short format" },
{ CPU_LOG_EXEC, "exec", { CPU_LOG_EXEC, "exec",
"show trace before each executed TB (lots of logs)" }, "show trace before each executed TB (lots of logs)" },
{ CPU_LOG_TB_CPU, "cpu",
"show CPU state before bloc translation" },
#ifdef TARGET_I386 #ifdef TARGET_I386
{ CPU_LOG_PCALL, "pcall", { CPU_LOG_PCALL, "pcall",
"show protected mode far calls/returns/exceptions" }, "show protected mode far calls/returns/exceptions" },

175
hw/ppc.c
View File

@ -28,6 +28,181 @@ void ppc_prep_init (int ram_size, int vga_ram_size, int boot_device,
const char *kernel_filename, const char *kernel_cmdline, const char *kernel_filename, const char *kernel_cmdline,
const char *initrd_filename); const char *initrd_filename);
/*****************************************************************************/
/* PPC time base and decrementer emulation */
//#define DEBUG_TB
struct ppc_tb_t {
/* Time base management */
int64_t tb_offset; /* Compensation */
uint32_t tb_freq; /* TB frequency */
/* Decrementer management */
uint64_t decr_next; /* Tick for next decr interrupt */
struct QEMUTimer *decr_timer;
};
static inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env)
{
/* TB time in tb periods */
return muldiv64(qemu_get_clock(vm_clock) + tb_env->tb_offset,
tb_env->tb_freq, ticks_per_sec);
}
uint32_t cpu_ppc_load_tbl (CPUState *env)
{
ppc_tb_t *tb_env = env->tb_env;
uint64_t tb;
tb = cpu_ppc_get_tb(tb_env);
#ifdef DEBUG_TB
{
static int last_time;
int now;
now = time(NULL);
if (last_time != now) {
last_time = now;
printf("%s: tb=0x%016lx %d %08lx\n",
__func__, tb, now, tb_env->tb_offset);
}
}
#endif
return tb & 0xFFFFFFFF;
}
uint32_t cpu_ppc_load_tbu (CPUState *env)
{
ppc_tb_t *tb_env = env->tb_env;
uint64_t tb;
tb = cpu_ppc_get_tb(tb_env);
#ifdef DEBUG_TB
printf("%s: tb=0x%016lx\n", __func__, tb);
#endif
return tb >> 32;
}
static void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t value)
{
tb_env->tb_offset = muldiv64(value, ticks_per_sec, tb_env->tb_freq)
- qemu_get_clock(vm_clock);
#ifdef DEBUG_TB
printf("%s: tb=0x%016lx offset=%08x\n", __func__, value);
#endif
}
void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
{
ppc_tb_t *tb_env = env->tb_env;
cpu_ppc_store_tb(tb_env,
((uint64_t)value << 32) | cpu_ppc_load_tbl(env));
}
void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
{
ppc_tb_t *tb_env = env->tb_env;
cpu_ppc_store_tb(tb_env,
((uint64_t)cpu_ppc_load_tbu(env) << 32) | value);
}
uint32_t cpu_ppc_load_decr (CPUState *env)
{
ppc_tb_t *tb_env = env->tb_env;
uint32_t decr;
decr = muldiv64(tb_env->decr_next - qemu_get_clock(vm_clock),
tb_env->tb_freq, ticks_per_sec);
#ifdef DEBUG_TB
printf("%s: 0x%08x\n", __func__, decr);
#endif
return decr;
}
/* When decrementer expires,
* all we need to do is generate or queue a CPU exception
*/
static inline void cpu_ppc_decr_excp (CPUState *env)
{
/* Raise it */
#ifdef DEBUG_TB
printf("raise decrementer exception\n");
#endif
cpu_interrupt(env, CPU_INTERRUPT_TIMER);
}
static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr,
uint32_t value, int is_excp)
{
ppc_tb_t *tb_env = env->tb_env;
uint64_t now, next;
#ifdef DEBUG_TB
printf("%s: 0x%08x => 0x%08x\n", __func__, decr, value);
#endif
now = qemu_get_clock(vm_clock);
next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq);
if (is_excp)
next += tb_env->decr_next - now;
if (next == now)
next++;
tb_env->decr_next = next;
/* Adjust timer */
qemu_mod_timer(tb_env->decr_timer, next);
/* If we set a negative value and the decrementer was positive,
* raise an exception.
*/
if ((value & 0x80000000) && !(decr & 0x80000000))
cpu_ppc_decr_excp(env);
}
void cpu_ppc_store_decr (CPUState *env, uint32_t value)
{
_cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0);
}
static void cpu_ppc_decr_cb (void *opaque)
{
_cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1);
}
/* Set up (once) timebase frequency (in Hz) */
ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq)
{
ppc_tb_t *tb_env;
tb_env = qemu_mallocz(sizeof(ppc_tb_t));
if (tb_env == NULL)
return NULL;
env->tb_env = tb_env;
if (tb_env->tb_freq == 0 || 1) {
tb_env->tb_freq = freq;
/* Create new timer */
tb_env->decr_timer =
qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env);
/* There is a bug in 2.4 kernels:
* if a decrementer exception is pending when it enables msr_ee,
* it's not ready to handle it...
*/
_cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
}
return tb_env;
}
#if 0
/*****************************************************************************/
/* Handle system reset (for now, just stop emulation) */
void cpu_ppc_reset (CPUState *env)
{
printf("Reset asked... Stop emulation\n");
abort();
}
#endif
/*****************************************************************************/
void ppc_init (int ram_size, int vga_ram_size, int boot_device, void ppc_init (int ram_size, int vga_ram_size, int boot_device,
DisplayState *ds, const char **fd_filename, int snapshot, DisplayState *ds, const char **fd_filename, int snapshot,
const char *kernel_filename, const char *kernel_cmdline, const char *kernel_filename, const char *kernel_cmdline,

View File

@ -24,6 +24,9 @@
#include "vl.h" #include "vl.h"
#include "m48t59.h" #include "m48t59.h"
/* XXX: move all TB related stuff in ppc_prep.c and suppress ppc.c ? */
ppc_tb_t *cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq);
//#define HARD_DEBUG_PPC_IO //#define HARD_DEBUG_PPC_IO
//#define DEBUG_PPC_IO //#define DEBUG_PPC_IO
@ -663,7 +666,6 @@ static void VGA_printf (uint8_t *s)
static void VGA_init (void) static void VGA_init (void)
{ {
/* Basic VGA init, inspired by plex86 VGAbios */ /* Basic VGA init, inspired by plex86 VGAbios */
printf("Init VGA...\n");
#if 1 #if 1
/* switch to color mode and enable CPU access 480 lines */ /* switch to color mode and enable CPU access 480 lines */
PPC_io_writeb(PPC_IO_BASE + 0x3C2, 0xC3); PPC_io_writeb(PPC_IO_BASE + 0x3C2, 0xC3);
@ -725,7 +727,6 @@ void PPC_init_hw (/*CPUPPCState *env,*/ uint32_t mem_size,
* if a decrementer exception is pending when it enables msr_ee, * if a decrementer exception is pending when it enables msr_ee,
* it's not ready to handle it... * it's not ready to handle it...
*/ */
env->decr = 0xFFFFFFFF;
p = phys_ram_base + kernel_addr; p = phys_ram_base + kernel_addr;
#if !defined (USE_OPEN_FIRMWARE) #if !defined (USE_OPEN_FIRMWARE)
/* Let's register the whole memory available only in supervisor mode */ /* Let's register the whole memory available only in supervisor mode */
@ -948,6 +949,8 @@ void ppc_prep_init(int ram_size, int vga_ram_size, int boot_device,
} }
} }
cpu_ppc_tb_init(cpu_single_env, 100UL * 1000UL * 1000UL);
/* init basic PC hardware */ /* init basic PC hardware */
vga_initialize(ds, phys_ram_base + ram_size, ram_size, vga_initialize(ds, phys_ram_base + ram_size, ram_size,
vga_ram_size, 0); vga_ram_size, 0);

View File

@ -504,6 +504,49 @@ void cpu_loop (CPUSPARCState *env)
#endif #endif
#ifdef TARGET_PPC #ifdef TARGET_PPC
static inline uint64_t cpu_ppc_get_tb (CPUState *env)
{
/* TO FIX */
return 0;
}
uint32_t cpu_ppc_load_tbl (CPUState *env)
{
return cpu_ppc_get_tb(env) & 0xFFFFFFFF;
}
uint32_t cpu_ppc_load_tbu (CPUState *env)
{
return cpu_ppc_get_tb(env) >> 32;
}
static void cpu_ppc_store_tb (CPUState *env, uint64_t value)
{
/* TO FIX */
}
void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
{
cpu_ppc_store_tb(env, ((uint64_t)value << 32) | cpu_ppc_load_tbl(env));
}
void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
{
cpu_ppc_store_tb(env, ((uint64_t)cpu_ppc_load_tbl(env) << 32) | value);
}
uint32_t cpu_ppc_load_decr (CPUState *env)
{
/* TO FIX */
return -1;
}
void cpu_ppc_store_decr (CPUState *env, uint32_t value)
{
/* TO FIX */
}
void cpu_loop(CPUPPCState *env) void cpu_loop(CPUPPCState *env)
{ {
target_siginfo_t info; target_siginfo_t info;
@ -812,7 +855,7 @@ void cpu_loop(CPUPPCState *env)
abort(); abort();
case EXCP_MTMSR: case EXCP_MTMSR:
/* We reloaded the msr, just go on */ /* We reloaded the msr, just go on */
if (msr_pr) { if (msr_pr == 0) {
fprintf(stderr, "Tried to go into supervisor mode !\n"); fprintf(stderr, "Tried to go into supervisor mode !\n");
if (loglevel) if (loglevel)
fprintf(logfile, "Tried to go into supervisor mode !\n"); fprintf(logfile, "Tried to go into supervisor mode !\n");
@ -842,12 +885,7 @@ void cpu_loop(CPUPPCState *env)
} }
abort(); abort();
} }
if (trapnr < EXCP_PPC_MAX)
env->exceptions &= ~(1 << trapnr);
process_pending_signals(env); process_pending_signals(env);
if (env->exceptions != 0) {
check_exception_state(env);
}
} }
} }
#endif #endif

View File

@ -589,6 +589,24 @@ static int monitor_get_xer (struct MonitorDef *md)
(cpu_single_env->xer[XER_CA] << XER_CA) | (cpu_single_env->xer[XER_CA] << XER_CA) |
(cpu_single_env->xer[XER_BC] << XER_BC); (cpu_single_env->xer[XER_BC] << XER_BC);
} }
uint32_t cpu_ppc_load_decr (CPUState *env);
static int monitor_get_decr (struct MonitorDef *md)
{
return cpu_ppc_load_decr(cpu_single_env);
}
uint32_t cpu_ppc_load_tbu (CPUState *env);
static int monitor_get_tbu (struct MonitorDef *md)
{
return cpu_ppc_load_tbu(cpu_single_env);
}
uint32_t cpu_ppc_load_tbl (CPUState *env);
static int monitor_get_tbl (struct MonitorDef *md)
{
return cpu_ppc_load_tbl(cpu_single_env);
}
#endif #endif
static MonitorDef monitor_defs[] = { static MonitorDef monitor_defs[] = {
@ -651,12 +669,12 @@ static MonitorDef monitor_defs[] = {
{ "nip|pc", offsetof(CPUState, nip) }, { "nip|pc", offsetof(CPUState, nip) },
{ "lr", offsetof(CPUState, lr) }, { "lr", offsetof(CPUState, lr) },
{ "ctr", offsetof(CPUState, ctr) }, { "ctr", offsetof(CPUState, ctr) },
{ "decr", offsetof(CPUState, decr) }, { "decr", 0, &monitor_get_decr, },
{ "ccr", 0, &monitor_get_ccr, }, { "ccr", 0, &monitor_get_ccr, },
{ "msr", 0, &monitor_get_msr, }, { "msr", 0, &monitor_get_msr, },
{ "xer", 0, &monitor_get_xer, }, { "xer", 0, &monitor_get_xer, },
{ "tbu", offsetof(CPUState, tb[0]) }, { "tbu", 0, &monitor_get_tbu, },
{ "tbl", offsetof(CPUState, tb[1]) }, { "tbl", 0, &monitor_get_tbl, },
{ "sdr1", offsetof(CPUState, sdr1) }, { "sdr1", offsetof(CPUState, sdr1) },
{ "sr0", offsetof(CPUState, sr[0]) }, { "sr0", offsetof(CPUState, sr[0]) },
{ "sr1", offsetof(CPUState, sr[1]) }, { "sr1", offsetof(CPUState, sr[1]) },

View File

@ -78,6 +78,8 @@ enum {
#define PPC_750 (PPC_INTEGER | PPC_FLOAT | PPC_FLOW | PPC_MEM | \ #define PPC_750 (PPC_INTEGER | PPC_FLOAT | PPC_FLOW | PPC_MEM | \
PPC_RES | PPC_CACHE | PPC_MISC | PPC_EXTERN | PPC_SEGMENT) PPC_RES | PPC_CACHE | PPC_MISC | PPC_EXTERN | PPC_SEGMENT)
typedef struct ppc_tb_t ppc_tb_t;
/* Supervisor mode registers */ /* Supervisor mode registers */
/* Machine state register */ /* Machine state register */
#define MSR_POW 18 #define MSR_POW 18
@ -134,10 +136,6 @@ typedef struct CPUPPCState {
/* special purpose registers */ /* special purpose registers */
uint32_t lr; uint32_t lr;
uint32_t ctr; uint32_t ctr;
/* Time base */
uint32_t tb[2];
/* decrementer */
uint32_t decr;
/* BATs */ /* BATs */
uint32_t DBAT[2][8]; uint32_t DBAT[2][8];
uint32_t IBAT[2][8]; uint32_t IBAT[2][8];
@ -154,13 +152,6 @@ typedef struct CPUPPCState {
int error_code; int error_code;
int access_type; /* when a memory exception occurs, the access int access_type; /* when a memory exception occurs, the access
type is stored here */ type is stored here */
#if 0 /* TODO */
uint32_t pending_exceptions; /* For external & decr exception,
* that can be delayed */
#else
uint32_t exceptions; /* exception queue */
uint32_t errors[32];
#endif
int user_mode_only; /* user mode only simulation */ int user_mode_only; /* user mode only simulation */
struct TranslationBlock *current_tb; /* currently executing TB */ struct TranslationBlock *current_tb; /* currently executing TB */
/* soft mmu support */ /* soft mmu support */
@ -178,8 +169,13 @@ typedef struct CPUPPCState {
/* ice debug support */ /* ice debug support */
uint32_t breakpoints[MAX_BREAKPOINTS]; uint32_t breakpoints[MAX_BREAKPOINTS];
int nb_breakpoints; int nb_breakpoints;
int brkstate; int singlestep_enabled; /* XXX: should use CPU single step mode instead */
int singlestep_enabled;
/* Time base and decrementer */
ppc_tb_t *tb_env;
/* Power management */
int power_mode;
/* user data */ /* user data */
void *opaque; void *opaque;
@ -206,10 +202,15 @@ void _store_xer (CPUPPCState *env, uint32_t value);
uint32_t _load_msr (CPUPPCState *env); uint32_t _load_msr (CPUPPCState *env);
void _store_msr (CPUPPCState *env, uint32_t value); void _store_msr (CPUPPCState *env, uint32_t value);
void PPC_init_hw (uint32_t mem_size, /* Time-base and decrementer management */
uint32_t kernel_addr, uint32_t kernel_size, #ifndef NO_CPU_IO_DEFS
uint32_t stack_addr, int boot_device, uint32_t cpu_ppc_load_tbl (CPUPPCState *env);
const unsigned char *initrd_file); uint32_t cpu_ppc_load_tbu (CPUPPCState *env);
void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value);
void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value);
uint32_t cpu_ppc_load_decr (CPUPPCState *env);
void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value);
#endif
#define TARGET_PAGE_BITS 12 #define TARGET_PAGE_BITS 12
#include "cpu-all.h" #include "cpu-all.h"

View File

@ -119,12 +119,8 @@ static inline uint32_t rotl (uint32_t i, int n)
#endif /* !defined(CONFIG_USER_ONLY) */ #endif /* !defined(CONFIG_USER_ONLY) */
int check_exception_state (CPUState *env); void do_raise_exception_err (uint32_t exception, int error_code);
void do_raise_exception (uint32_t exception);
void do_queue_exception_err (uint32_t exception, int error_code);
void do_queue_exception (uint32_t exception);
void do_process_exceptions (void);
void do_check_exception_state (void);
void do_load_cr (void); void do_load_cr (void);
void do_store_cr (uint32_t mask); void do_store_cr (uint32_t mask);

View File

@ -27,49 +27,10 @@
//#define DEBUG_BATS //#define DEBUG_BATS
//#define DEBUG_EXCEPTIONS //#define DEBUG_EXCEPTIONS
extern FILE *logfile, *stdout, *stderr; extern FILE *stdout, *stderr;
void exit (int);
void abort (void); void abort (void);
void cpu_loop_exit(void) /*****************************************************************************/
{
longjmp(env->jmp_env, 1);
}
void do_process_exceptions (void)
{
cpu_loop_exit();
}
int check_exception_state (CPUState *env)
{
int i;
/* Process PPC exceptions */
for (i = 1; i < EXCP_PPC_MAX; i++) {
if (env->exceptions & (1 << i)) {
switch (i) {
case EXCP_EXTERNAL:
case EXCP_DECR:
if (msr_ee == 0)
return 0;
break;
case EXCP_PROGRAM:
if (env->errors[EXCP_PROGRAM] == EXCP_FP &&
msr_fe0 == 0 && msr_fe1 == 0)
return 0;
break;
default:
break;
}
env->exception_index = i;
env->error_code = env->errors[i];
return 1;
}
}
return 0;
}
/*****************************************************************************/ /*****************************************************************************/
/* PPC MMU emulation */ /* PPC MMU emulation */
@ -500,8 +461,7 @@ void tlb_fill(unsigned long addr, int is_write, int is_user, void *retaddr)
cpu_restore_state(tb, env, pc, NULL); cpu_restore_state(tb, env, pc, NULL);
} }
} }
do_queue_exception_err(env->exception_index, env->error_code); do_raise_exception_err(env->exception_index, env->error_code);
do_process_exceptions();
} }
{ {
unsigned long tlb_addrr, tlb_addrw; unsigned long tlb_addrr, tlb_addrw;
@ -701,9 +661,6 @@ void do_interrupt (CPUState *env)
uint32_t msr; uint32_t msr;
int excp = env->exception_index; int excp = env->exception_index;
/* Dequeue PPC exceptions */
if (excp < EXCP_PPC_MAX)
env->exceptions &= ~(1 << excp);
msr = _load_msr(env); msr = _load_msr(env);
#if defined (DEBUG_EXCEPTIONS) #if defined (DEBUG_EXCEPTIONS)
if ((excp == EXCP_PROGRAM || excp == EXCP_DSI) && msr_pr == 1) if ((excp == EXCP_PROGRAM || excp == EXCP_DSI) && msr_pr == 1)
@ -812,7 +769,7 @@ void do_interrupt (CPUState *env)
} }
#endif #endif
/* Requeue it */ /* Requeue it */
do_queue_exception(EXCP_EXTERNAL); do_raise_exception(EXCP_EXTERNAL);
return; return;
} }
goto store_next; goto store_next;
@ -864,7 +821,7 @@ void do_interrupt (CPUState *env)
case EXCP_DECR: case EXCP_DECR:
if (msr_ee == 0) { if (msr_ee == 0) {
/* Requeue it */ /* Requeue it */
do_queue_exception(EXCP_DECR); do_raise_exception(EXCP_DECR);
return; return;
} }
goto store_next; goto store_next;
@ -937,4 +894,5 @@ void do_interrupt (CPUState *env)
T0 = 0; T0 = 0;
#endif #endif
#endif #endif
env->exception_index = -1;
} }

View File

@ -208,32 +208,28 @@ PPC_OP(set_T2)
} }
/* Generate exceptions */ /* Generate exceptions */
PPC_OP(queue_exception_err) PPC_OP(raise_exception_err)
{ {
do_queue_exception_err(PARAM(1), PARAM(2)); do_raise_exception_err(PARAM(1), PARAM(2));
} }
PPC_OP(queue_exception) PPC_OP(raise_exception)
{ {
do_queue_exception(PARAM(1)); do_raise_exception(PARAM(1));
} }
PPC_OP(process_exceptions) PPC_OP(update_nip)
{ {
env->nip = PARAM(1); env->nip = PARAM(1);
if (env->exceptions != 0) {
do_check_exception_state();
}
} }
PPC_OP(debug) PPC_OP(debug)
{ {
env->nip = PARAM(1); env->nip = PARAM(1);
env->brkstate = 1;
#if defined (DEBUG_OP) #if defined (DEBUG_OP)
dump_state(); dump_state();
#endif #endif
do_queue_exception(EXCP_DEBUG); do_raise_exception(EXCP_DEBUG);
RETURN(); RETURN();
} }
@ -364,58 +360,38 @@ PPC_OP(store_ctr)
RETURN(); RETURN();
} }
/* Update time base */ PPC_OP(load_tbl)
PPC_OP(update_tb)
{ {
T0 = regs->tb[0]; T0 = cpu_ppc_load_tbl(regs);
T1 = T0;
T0 += PARAM(1);
#if defined (DEBUG_OP)
dump_update_tb(PARAM(1));
#endif
if (T0 < T1) {
T1 = regs->tb[1] + 1;
regs->tb[1] = T1;
}
regs->tb[0] = T0;
RETURN(); RETURN();
} }
PPC_OP(load_tb) PPC_OP(load_tbu)
{ {
T0 = regs->tb[PARAM(1)]; T0 = cpu_ppc_load_tbu(regs);
RETURN(); RETURN();
} }
PPC_OP(store_tb) PPC_OP(store_tbl)
{ {
regs->tb[PARAM(1)] = T0; cpu_ppc_store_tbl(regs, T0);
#if defined (DEBUG_OP)
dump_store_tb(PARAM(1));
#endif
RETURN(); RETURN();
} }
/* Update decrementer */ PPC_OP(store_tbu)
PPC_OP(update_decr)
{ {
T0 = regs->decr; cpu_ppc_store_tbu(regs, T0);
T1 = T0;
T0 -= PARAM(1);
regs->decr = T0;
if (PARAM(1) > T1) {
do_queue_exception(EXCP_DECR);
}
RETURN(); RETURN();
} }
PPC_OP(load_decr)
{
T0 = cpu_ppc_load_decr(regs);
}
PPC_OP(store_decr) PPC_OP(store_decr)
{ {
T1 = regs->decr; cpu_ppc_store_decr(regs, T0);
regs->decr = T0;
if (Ts0 < 0 && Ts1 > 0) {
do_queue_exception(EXCP_DECR);
}
RETURN(); RETURN();
} }
@ -1471,17 +1447,14 @@ PPC_OP(fneg)
/* Return from interrupt */ /* Return from interrupt */
PPC_OP(rfi) PPC_OP(rfi)
{ {
regs->nip = regs->spr[SRR0] & ~0x00000003;
T0 = regs->spr[SRR1] & ~0xFFFF0000; T0 = regs->spr[SRR1] & ~0xFFFF0000;
do_store_msr(); do_store_msr();
do_tlbia();
#if defined (DEBUG_OP) #if defined (DEBUG_OP)
dump_rfi(); dump_rfi();
#endif #endif
regs->nip = regs->spr[SRR0] & ~0x00000003; // do_tlbia();
do_queue_exception(EXCP_RFI); do_raise_exception(EXCP_RFI);
if (env->exceptions != 0) {
do_check_exception_state();
}
RETURN(); RETURN();
} }
@ -1493,7 +1466,7 @@ PPC_OP(tw)
(Ts0 == Ts1 && (PARAM(1) & 0x04)) || (Ts0 == Ts1 && (PARAM(1) & 0x04)) ||
(T0 < T1 && (PARAM(1) & 0x02)) || (T0 < T1 && (PARAM(1) & 0x02)) ||
(T0 > T1 && (PARAM(1) & 0x01))) (T0 > T1 && (PARAM(1) & 0x01)))
do_queue_exception_err(EXCP_PROGRAM, EXCP_TRAP); do_raise_exception_err(EXCP_PROGRAM, EXCP_TRAP);
RETURN(); RETURN();
} }
@ -1504,7 +1477,7 @@ PPC_OP(twi)
(Ts0 == SPARAM(1) && (PARAM(2) & 0x04)) || (Ts0 == SPARAM(1) && (PARAM(2) & 0x04)) ||
(T0 < (uint32_t)SPARAM(1) && (PARAM(2) & 0x02)) || (T0 < (uint32_t)SPARAM(1) && (PARAM(2) & 0x02)) ||
(T0 > (uint32_t)SPARAM(1) && (PARAM(2) & 0x01))) (T0 > (uint32_t)SPARAM(1) && (PARAM(2) & 0x01)))
do_queue_exception_err(EXCP_PROGRAM, EXCP_TRAP); do_raise_exception_err(EXCP_PROGRAM, EXCP_TRAP);
RETURN(); RETURN();
} }

View File

@ -31,31 +31,38 @@
/*****************************************************************************/ /*****************************************************************************/
/* Exceptions processing helpers */ /* Exceptions processing helpers */
void do_queue_exception_err (uint32_t exception, int error_code) void cpu_loop_exit(void)
{ {
/* Queue real PPC exceptions */ longjmp(env->jmp_env, 1);
if (exception < EXCP_PPC_MAX) { }
env->exceptions |= 1 << exception;
env->errors[exception] = error_code; void do_raise_exception_err (uint32_t exception, int error_code)
} else { {
/* Preserve compatibility with qemu core */ #if 0
env->exceptions |= 1; printf("Raise exception %3x code : %d\n", exception, error_code);
#endif
switch (exception) {
case EXCP_EXTERNAL:
case EXCP_DECR:
printf("DECREMENTER & EXTERNAL exceptions should be hard interrupts !\n");
if (msr_ee == 0)
return;
break;
case EXCP_PROGRAM:
if (error_code == EXCP_FP && msr_fe0 == 0 && msr_fe1 == 0)
return;
break;
default:
break;
}
env->exception_index = exception; env->exception_index = exception;
env->error_code = error_code; env->error_code = error_code;
}
}
void do_queue_exception (uint32_t exception)
{
do_queue_exception_err(exception, 0);
}
void do_check_exception_state (void)
{
if ((env->exceptions & 1) == 1 || check_exception_state(env)) {
env->exceptions &= ~1;
cpu_loop_exit(); cpu_loop_exit();
} }
void do_raise_exception (uint32_t exception)
{
do_raise_exception_err(exception, 0);
} }
/*****************************************************************************/ /*****************************************************************************/
@ -125,13 +132,6 @@ void do_store_msr (void)
/* Flush all tlb when changing translation mode or privilege level */ /* Flush all tlb when changing translation mode or privilege level */
do_tlbia(); do_tlbia();
} }
#if 0
if ((T0 >> MSR_IP) & 0x01) {
printf("Halting CPU. Stop emulation\n");
do_queue_exception(EXCP_HLT);
cpu_loop_exit();
}
#endif
msr_pow = (T0 >> MSR_POW) & 0x03; msr_pow = (T0 >> MSR_POW) & 0x03;
msr_ile = (T0 >> MSR_ILE) & 0x01; msr_ile = (T0 >> MSR_ILE) & 0x01;
msr_ee = (T0 >> MSR_EE) & 0x01; msr_ee = (T0 >> MSR_EE) & 0x01;

View File

@ -97,8 +97,7 @@ PPC_OP(glue(lswx, MEMSUFFIX))
if (T1 > 0) { if (T1 > 0) {
if ((PARAM(1) < PARAM(2) && (PARAM(1) + T1) > PARAM(2)) || if ((PARAM(1) < PARAM(2) && (PARAM(1) + T1) > PARAM(2)) ||
(PARAM(1) < PARAM(3) && (PARAM(1) + T1) > PARAM(3))) { (PARAM(1) < PARAM(3) && (PARAM(1) + T1) > PARAM(3))) {
do_queue_exception_err(EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_LSWX); do_raise_exception_err(EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_LSWX);
do_process_exceptions();
} else { } else {
glue(do_lsw, MEMSUFFIX)(PARAM(1)); glue(do_lsw, MEMSUFFIX)(PARAM(1));
} }
@ -138,8 +137,7 @@ PPC_LDF_OP(fs, ldfl);
PPC_OP(glue(lwarx, MEMSUFFIX)) PPC_OP(glue(lwarx, MEMSUFFIX))
{ {
if (T0 & 0x03) { if (T0 & 0x03) {
do_queue_exception(EXCP_ALIGN); do_raise_exception(EXCP_ALIGN);
do_process_exceptions();
} else { } else {
T1 = glue(ldl, MEMSUFFIX)((void *)T0); T1 = glue(ldl, MEMSUFFIX)((void *)T0);
regs->reserve = T0; regs->reserve = T0;
@ -151,8 +149,7 @@ PPC_OP(glue(lwarx, MEMSUFFIX))
PPC_OP(glue(stwcx, MEMSUFFIX)) PPC_OP(glue(stwcx, MEMSUFFIX))
{ {
if (T0 & 0x03) { if (T0 & 0x03) {
do_queue_exception(EXCP_ALIGN); do_raise_exception(EXCP_ALIGN);
do_process_exceptions();
} else { } else {
if (regs->reserve != T0) { if (regs->reserve != T0) {
env->crf[0] = xer_ov; env->crf[0] = xer_ov;

View File

@ -30,6 +30,7 @@
//#define DO_SINGLE_STEP //#define DO_SINGLE_STEP
//#define DO_STEP_FLUSH //#define DO_STEP_FLUSH
//#define DEBUG_DISAS //#define DEBUG_DISAS
//#define PPC_DEBUG_DISAS
enum { enum {
#define DEF(s, n, copy_size) INDEX_op_ ## s, #define DEF(s, n, copy_size) INDEX_op_ ## s,
@ -135,10 +136,6 @@ typedef struct DisasContext {
uint32_t nip; uint32_t nip;
uint32_t opcode; uint32_t opcode;
uint32_t exception; uint32_t exception;
/* Time base offset */
uint32_t tb_offset;
/* Decrementer offset */
uint32_t decr_offset;
/* Execution mode */ /* Execution mode */
#if !defined(CONFIG_USER_ONLY) #if !defined(CONFIG_USER_ONLY)
int supervisor; int supervisor;
@ -156,21 +153,26 @@ typedef struct opc_handler_t {
void (*handler)(DisasContext *ctx); void (*handler)(DisasContext *ctx);
} opc_handler_t; } opc_handler_t;
#define RET_EXCP(excp, error) \ #define RET_EXCP(ctx, excp, error) \
do { \ do { \
gen_op_queue_exception_err(excp, error); \ if ((ctx)->exception == EXCP_NONE) { \
ctx->exception = excp; \ gen_op_update_nip((ctx)->nip); \
return; \ } \
gen_op_raise_exception_err((excp), (error)); \
ctx->exception = (excp); \
} while (0) } while (0)
#define RET_INVAL() \ #define RET_INVAL(ctx) \
RET_EXCP(EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_INVAL) RET_EXCP((ctx), EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_INVAL)
#define RET_PRIVOPC() \ #define RET_PRIVOPC(ctx) \
RET_EXCP(EXCP_PROGRAM, EXCP_INVAL | EXCP_PRIV_OPC) RET_EXCP((ctx), EXCP_PROGRAM, EXCP_INVAL | EXCP_PRIV_OPC)
#define RET_PRIVREG() \ #define RET_PRIVREG(ctx) \
RET_EXCP(EXCP_PROGRAM, EXCP_INVAL | EXCP_PRIV_REG) RET_EXCP((ctx), EXCP_PROGRAM, EXCP_INVAL | EXCP_PRIV_REG)
#define RET_MTMSR(ctx) \
RET_EXCP((ctx), EXCP_MTMSR, 0)
#define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \ #define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \
static void gen_##name (DisasContext *ctx); \ static void gen_##name (DisasContext *ctx); \
@ -312,29 +314,26 @@ GEN_OPCODE_MARK(start);
/* Invalid instruction */ /* Invalid instruction */
GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE) GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE)
{ {
RET_INVAL(); RET_INVAL(ctx);
} }
/* Special opcode to stop emulation */ /* Special opcode to stop emulation */
GEN_HANDLER(stop, 0x06, 0x00, 0xFF, 0x03FFFFC1, PPC_COMMON) GEN_HANDLER(stop, 0x06, 0x00, 0xFF, 0x03FFFFC1, PPC_COMMON)
{ {
gen_op_queue_exception(EXCP_HLT); RET_EXCP(ctx, EXCP_HLT, 0);
ctx->exception = EXCP_HLT;
} }
/* Special opcode to call open-firmware */ /* Special opcode to call open-firmware */
GEN_HANDLER(of_enter, 0x06, 0x01, 0xFF, 0x03FFFFC1, PPC_COMMON) GEN_HANDLER(of_enter, 0x06, 0x01, 0xFF, 0x03FFFFC1, PPC_COMMON)
{ {
gen_op_queue_exception(EXCP_OFCALL); RET_EXCP(ctx, EXCP_OFCALL, 0);
ctx->exception = EXCP_OFCALL;
} }
/* Special opcode to call RTAS */ /* Special opcode to call RTAS */
GEN_HANDLER(rtas_enter, 0x06, 0x02, 0xFF, 0x03FFFFC1, PPC_COMMON) GEN_HANDLER(rtas_enter, 0x06, 0x02, 0xFF, 0x03FFFFC1, PPC_COMMON)
{ {
printf("RTAS entry point !\n"); printf("RTAS entry point !\n");
gen_op_queue_exception(EXCP_RTASCALL); RET_EXCP(ctx, EXCP_RTASCALL, 0);
ctx->exception = EXCP_RTASCALL;
} }
static opc_handler_t invalid_handler = { static opc_handler_t invalid_handler = {
@ -1010,7 +1009,8 @@ GEN_HANDLER(l##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
uint32_t simm = SIMM(ctx->opcode); \ uint32_t simm = SIMM(ctx->opcode); \
if (rA(ctx->opcode) == 0 || \ if (rA(ctx->opcode) == 0 || \
rA(ctx->opcode) == rD(ctx->opcode)) { \ rA(ctx->opcode) == rD(ctx->opcode)) { \
RET_INVAL(); \ RET_INVAL(ctx); \
return; \
} \ } \
gen_op_load_gpr_T0(rA(ctx->opcode)); \ gen_op_load_gpr_T0(rA(ctx->opcode)); \
if (simm != 0) \ if (simm != 0) \
@ -1025,7 +1025,8 @@ GEN_HANDLER(l##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_INTEGER) \
{ \ { \
if (rA(ctx->opcode) == 0 || \ if (rA(ctx->opcode) == 0 || \
rA(ctx->opcode) == rD(ctx->opcode)) { \ rA(ctx->opcode) == rD(ctx->opcode)) { \
RET_INVAL(); \ RET_INVAL(ctx); \
return; \
} \ } \
gen_op_load_gpr_T0(rA(ctx->opcode)); \ gen_op_load_gpr_T0(rA(ctx->opcode)); \
gen_op_load_gpr_T1(rB(ctx->opcode)); \ gen_op_load_gpr_T1(rB(ctx->opcode)); \
@ -1086,7 +1087,8 @@ GEN_HANDLER(st##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
{ \ { \
uint32_t simm = SIMM(ctx->opcode); \ uint32_t simm = SIMM(ctx->opcode); \
if (rA(ctx->opcode) == 0) { \ if (rA(ctx->opcode) == 0) { \
RET_INVAL(); \ RET_INVAL(ctx); \
return; \
} \ } \
gen_op_load_gpr_T0(rA(ctx->opcode)); \ gen_op_load_gpr_T0(rA(ctx->opcode)); \
if (simm != 0) \ if (simm != 0) \
@ -1100,7 +1102,8 @@ GEN_HANDLER(st##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
GEN_HANDLER(st##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_INTEGER) \ GEN_HANDLER(st##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_INTEGER) \
{ \ { \
if (rA(ctx->opcode) == 0) { \ if (rA(ctx->opcode) == 0) { \
RET_INVAL(); \ RET_INVAL(ctx); \
return; \
} \ } \
gen_op_load_gpr_T0(rA(ctx->opcode)); \ gen_op_load_gpr_T0(rA(ctx->opcode)); \
gen_op_load_gpr_T1(rB(ctx->opcode)); \ gen_op_load_gpr_T1(rB(ctx->opcode)); \
@ -1236,7 +1239,8 @@ GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_INTEGER)
nr = nb / 4; nr = nb / 4;
if (((start + nr) > 32 && start <= ra && (start + nr - 32) > ra) || if (((start + nr) > 32 && start <= ra && (start + nr - 32) > ra) ||
((start + nr) <= 32 && start <= ra && (start + nr) > ra)) { ((start + nr) <= 32 && start <= ra && (start + nr) > ra)) {
RET_EXCP(EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_LSWX); RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_LSWX);
return;
} }
if (ra == 0) { if (ra == 0) {
gen_op_set_T0(0); gen_op_set_T0(0);
@ -1376,7 +1380,8 @@ GEN_HANDLER(l##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
uint32_t simm = SIMM(ctx->opcode); \ uint32_t simm = SIMM(ctx->opcode); \
if (rA(ctx->opcode) == 0 || \ if (rA(ctx->opcode) == 0 || \
rA(ctx->opcode) == rD(ctx->opcode)) { \ rA(ctx->opcode) == rD(ctx->opcode)) { \
RET_INVAL(); \ RET_INVAL(ctx); \
return; \
} \ } \
gen_op_load_gpr_T0(rA(ctx->opcode)); \ gen_op_load_gpr_T0(rA(ctx->opcode)); \
if (simm != 0) \ if (simm != 0) \
@ -1391,7 +1396,8 @@ GEN_HANDLER(l##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_INTEGER) \
{ \ { \
if (rA(ctx->opcode) == 0 || \ if (rA(ctx->opcode) == 0 || \
rA(ctx->opcode) == rD(ctx->opcode)) { \ rA(ctx->opcode) == rD(ctx->opcode)) { \
RET_INVAL(); \ RET_INVAL(ctx); \
return; \
} \ } \
gen_op_load_gpr_T0(rA(ctx->opcode)); \ gen_op_load_gpr_T0(rA(ctx->opcode)); \
gen_op_load_gpr_T1(rB(ctx->opcode)); \ gen_op_load_gpr_T1(rB(ctx->opcode)); \
@ -1448,7 +1454,8 @@ GEN_HANDLER(st##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
{ \ { \
uint32_t simm = SIMM(ctx->opcode); \ uint32_t simm = SIMM(ctx->opcode); \
if (rA(ctx->opcode) == 0) { \ if (rA(ctx->opcode) == 0) { \
RET_INVAL(); \ RET_INVAL(ctx); \
return; \
} \ } \
gen_op_load_gpr_T0(rA(ctx->opcode)); \ gen_op_load_gpr_T0(rA(ctx->opcode)); \
if (simm != 0) \ if (simm != 0) \
@ -1462,7 +1469,8 @@ GEN_HANDLER(st##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
GEN_HANDLER(st##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_INTEGER) \ GEN_HANDLER(st##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_INTEGER) \
{ \ { \
if (rA(ctx->opcode) == 0) { \ if (rA(ctx->opcode) == 0) { \
RET_INVAL(); \ RET_INVAL(ctx); \
return; \
} \ } \
gen_op_load_gpr_T0(rA(ctx->opcode)); \ gen_op_load_gpr_T0(rA(ctx->opcode)); \
gen_op_load_gpr_T1(rB(ctx->opcode)); \ gen_op_load_gpr_T1(rB(ctx->opcode)); \
@ -1502,7 +1510,7 @@ GEN_STFS(fs, 0x14);
/* stfiwx */ /* stfiwx */
GEN_HANDLER(stfiwx, 0x1F, 0x17, 0x1E, 0x00000001, PPC_FLOAT) GEN_HANDLER(stfiwx, 0x1F, 0x17, 0x1E, 0x00000001, PPC_FLOAT)
{ {
RET_INVAL(); RET_INVAL(ctx);
} }
/*** Branch ***/ /*** Branch ***/
@ -1512,9 +1520,6 @@ GEN_HANDLER(b, 0x12, 0xFF, 0xFF, 0x00000000, PPC_FLOW)
{ {
uint32_t li = s_ext24(LI(ctx->opcode)), target; uint32_t li = s_ext24(LI(ctx->opcode)), target;
gen_op_update_tb(ctx->tb_offset);
gen_op_update_decr(ctx->decr_offset);
gen_op_process_exceptions(ctx->nip - 4);
if (AA(ctx->opcode) == 0) if (AA(ctx->opcode) == 0)
target = ctx->nip + li - 4; target = ctx->nip + li - 4;
else else
@ -1538,10 +1543,6 @@ static inline void gen_bcond(DisasContext *ctx, int type)
uint32_t mask; uint32_t mask;
uint32_t li; uint32_t li;
gen_op_update_tb(ctx->tb_offset);
gen_op_update_decr(ctx->decr_offset);
gen_op_process_exceptions(ctx->nip - 4);
if ((bo & 0x4) == 0) if ((bo & 0x4) == 0)
gen_op_dec_ctr(); gen_op_dec_ctr();
switch(type) { switch(type) {
@ -1683,14 +1684,15 @@ GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER)
GEN_HANDLER(rfi, 0x13, 0x12, 0xFF, 0x03FF8001, PPC_FLOW) GEN_HANDLER(rfi, 0x13, 0x12, 0xFF, 0x03FF8001, PPC_FLOW)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVOPC(); RET_PRIVOPC(ctx);
#else #else
/* Restore CPU state */ /* Restore CPU state */
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVOPC(); RET_PRIVOPC(ctx);
return;
} }
gen_op_rfi(); gen_op_rfi();
ctx->exception = EXCP_RFI; RET_EXCP(ctx, EXCP_RFI, 0);
#endif #endif
} }
@ -1698,11 +1700,10 @@ GEN_HANDLER(rfi, 0x13, 0x12, 0xFF, 0x03FF8001, PPC_FLOW)
GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFFFFD, PPC_FLOW) GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFFFFD, PPC_FLOW)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
gen_op_queue_exception(EXCP_SYSCALL_USER); RET_EXCP(ctx, EXCP_SYSCALL_USER, 0);
#else #else
gen_op_queue_exception(EXCP_SYSCALL); RET_EXCP(ctx, EXCP_SYSCALL, 0);
#endif #endif
ctx->exception = EXCP_SYSCALL;
} }
/*** Trap ***/ /*** Trap ***/
@ -1770,10 +1771,11 @@ GEN_HANDLER(mfcr, 0x1F, 0x13, 0x00, 0x001FF801, PPC_MISC)
GEN_HANDLER(mfmsr, 0x1F, 0x13, 0x02, 0x001FF801, PPC_MISC) GEN_HANDLER(mfmsr, 0x1F, 0x13, 0x02, 0x001FF801, PPC_MISC)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVREG(); RET_PRIVREG(ctx);
#else #else
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVREG(); RET_PRIVREG(ctx);
return;
} }
gen_op_load_msr(); gen_op_load_msr();
gen_op_store_T0_gpr(rD(ctx->opcode)); gen_op_store_T0_gpr(rD(ctx->opcode));
@ -1792,11 +1794,11 @@ GEN_HANDLER(mfspr, 0x1F, 0x13, 0x0A, 0x00000001, PPC_MISC)
#endif #endif
{ {
case -1: case -1:
RET_EXCP(EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR); RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR);
break; return;
case 0: case 0:
RET_PRIVREG(); RET_PRIVREG(ctx);
break; return;
default: default:
break; break;
} }
@ -1910,19 +1912,13 @@ GEN_HANDLER(mfspr, 0x1F, 0x13, 0x0A, 0x00000001, PPC_MISC)
gen_op_load_sdr1(); gen_op_load_sdr1();
break; break;
case V_TBL: case V_TBL:
gen_op_update_tb(ctx->tb_offset); gen_op_load_tbl();
ctx->tb_offset = 0;
/* TBL is still in T0 */
break; break;
case V_TBU: case V_TBU:
gen_op_update_tb(ctx->tb_offset); gen_op_load_tbu();
ctx->tb_offset = 0;
gen_op_load_tb(1);
break; break;
case DECR: case DECR:
gen_op_update_decr(ctx->decr_offset); gen_op_load_decr();
ctx->decr_offset = 0;
/* decr is still in T0 */
break; break;
default: default:
gen_op_load_spr(sprn); gen_op_load_spr(sprn);
@ -1939,18 +1935,16 @@ GEN_HANDLER(mftb, 0x1F, 0x13, 0x0B, 0x00000001, PPC_MISC)
/* We need to update the time base before reading it */ /* We need to update the time base before reading it */
switch (sprn) { switch (sprn) {
case V_TBL: case V_TBL:
gen_op_update_tb(ctx->tb_offset);
/* TBL is still in T0 */ /* TBL is still in T0 */
gen_op_load_tbl();
break; break;
case V_TBU: case V_TBU:
gen_op_update_tb(ctx->tb_offset); gen_op_load_tbu();
gen_op_load_tb(1);
break; break;
default: default:
RET_INVAL(); RET_INVAL(ctx);
break; return;
} }
ctx->tb_offset = 0;
gen_op_store_T0_gpr(rD(ctx->opcode)); gen_op_store_T0_gpr(rD(ctx->opcode));
} }
@ -1965,15 +1959,16 @@ GEN_HANDLER(mtcrf, 0x1F, 0x10, 0x04, 0x00100801, PPC_MISC)
GEN_HANDLER(mtmsr, 0x1F, 0x12, 0x04, 0x001FF801, PPC_MISC) GEN_HANDLER(mtmsr, 0x1F, 0x12, 0x04, 0x001FF801, PPC_MISC)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVREG(); RET_PRIVREG(ctx);
#else #else
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVREG(); RET_PRIVREG(ctx);
return;
} }
gen_op_load_gpr_T0(rS(ctx->opcode)); gen_op_load_gpr_T0(rS(ctx->opcode));
gen_op_store_msr(); gen_op_store_msr();
/* Must stop the translation as machine state (may have) changed */ /* Must stop the translation as machine state (may have) changed */
ctx->exception = EXCP_MTMSR; RET_MTMSR(ctx);
#endif #endif
} }
@ -1995,10 +1990,10 @@ GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000001, PPC_MISC)
#endif #endif
{ {
case -1: case -1:
RET_EXCP(EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR); RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR);
break; break;
case 0: case 0:
RET_PRIVREG(); RET_PRIVREG(ctx);
break; break;
default: default:
break; break;
@ -2147,16 +2142,13 @@ GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000001, PPC_MISC)
gen_op_tlbia(); gen_op_tlbia();
break; break;
case O_TBL: case O_TBL:
gen_op_store_tb(0); gen_op_store_tbl();
ctx->tb_offset = 0;
break; break;
case O_TBU: case O_TBU:
gen_op_store_tb(1); gen_op_store_tbu();
ctx->tb_offset = 0;
break; break;
case DECR: case DECR:
gen_op_store_decr(); gen_op_store_decr();
ctx->decr_offset = 0;
break; break;
default: default:
gen_op_store_spr(sprn); gen_op_store_spr(sprn);
@ -2186,10 +2178,11 @@ GEN_HANDLER(dcbf, 0x1F, 0x16, 0x02, 0x03E00001, PPC_CACHE)
GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE) GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVOPC(); RET_PRIVOPC(ctx);
#else #else
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVOPC(); RET_PRIVOPC(ctx);
return;
} }
if (rA(ctx->opcode) == 0) { if (rA(ctx->opcode) == 0) {
gen_op_load_gpr_T0(rB(ctx->opcode)); gen_op_load_gpr_T0(rB(ctx->opcode));
@ -2274,10 +2267,11 @@ GEN_HANDLER(dcba, 0x1F, 0x16, 0x07, 0x03E00001, PPC_CACHE_OPT)
GEN_HANDLER(mfsr, 0x1F, 0x13, 0x12, 0x0010F801, PPC_SEGMENT) GEN_HANDLER(mfsr, 0x1F, 0x13, 0x12, 0x0010F801, PPC_SEGMENT)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVREG(); RET_PRIVREG(ctx);
#else #else
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVREG(); RET_PRIVREG(ctx);
return;
} }
gen_op_load_sr(SR(ctx->opcode)); gen_op_load_sr(SR(ctx->opcode));
gen_op_store_T0_gpr(rD(ctx->opcode)); gen_op_store_T0_gpr(rD(ctx->opcode));
@ -2288,10 +2282,11 @@ GEN_HANDLER(mfsr, 0x1F, 0x13, 0x12, 0x0010F801, PPC_SEGMENT)
GEN_HANDLER(mfsrin, 0x1F, 0x13, 0x14, 0x001F0001, PPC_SEGMENT) GEN_HANDLER(mfsrin, 0x1F, 0x13, 0x14, 0x001F0001, PPC_SEGMENT)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVREG(); RET_PRIVREG(ctx);
#else #else
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVREG(); RET_PRIVREG(ctx);
return;
} }
gen_op_load_gpr_T1(rB(ctx->opcode)); gen_op_load_gpr_T1(rB(ctx->opcode));
gen_op_load_srin(); gen_op_load_srin();
@ -2303,14 +2298,18 @@ GEN_HANDLER(mfsrin, 0x1F, 0x13, 0x14, 0x001F0001, PPC_SEGMENT)
GEN_HANDLER(mtsr, 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT) GEN_HANDLER(mtsr, 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVREG(); RET_PRIVREG(ctx);
#else #else
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVREG(); RET_PRIVREG(ctx);
return;
} }
gen_op_load_gpr_T0(rS(ctx->opcode)); gen_op_load_gpr_T0(rS(ctx->opcode));
gen_op_store_sr(SR(ctx->opcode)); gen_op_store_sr(SR(ctx->opcode));
#if 0
gen_op_tlbia(); gen_op_tlbia();
RET_MTMSR(ctx);
#endif
#endif #endif
} }
@ -2318,10 +2317,11 @@ GEN_HANDLER(mtsr, 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT)
GEN_HANDLER(mtsrin, 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT) GEN_HANDLER(mtsrin, 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVREG(); RET_PRIVREG(ctx);
#else #else
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVREG(); RET_PRIVREG(ctx);
return;
} }
gen_op_load_gpr_T0(rS(ctx->opcode)); gen_op_load_gpr_T0(rS(ctx->opcode));
gen_op_load_gpr_T1(rB(ctx->opcode)); gen_op_load_gpr_T1(rB(ctx->opcode));
@ -2336,10 +2336,13 @@ GEN_HANDLER(mtsrin, 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT)
GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_OPT) GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_OPT)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVOPC(); RET_PRIVOPC(ctx);
#else #else
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVOPC(); if (loglevel)
fprintf(logfile, "%s: ! supervisor\n", __func__);
RET_PRIVOPC(ctx);
return;
} }
gen_op_tlbia(); gen_op_tlbia();
#endif #endif
@ -2349,10 +2352,11 @@ GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_OPT)
GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x03FF0001, PPC_MEM) GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x03FF0001, PPC_MEM)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVOPC(); RET_PRIVOPC(ctx);
#else #else
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVOPC(); RET_PRIVOPC(ctx);
return;
} }
gen_op_load_gpr_T0(rB(ctx->opcode)); gen_op_load_gpr_T0(rB(ctx->opcode));
gen_op_tlbie(); gen_op_tlbie();
@ -2363,10 +2367,11 @@ GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x03FF0001, PPC_MEM)
GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM) GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM)
{ {
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
RET_PRIVOPC(); RET_PRIVOPC(ctx);
#else #else
if (!ctx->supervisor) { if (!ctx->supervisor) {
RET_PRIVOPC(); RET_PRIVOPC(ctx);
return;
} }
/* This has no effect: it should ensure that all previous /* This has no effect: it should ensure that all previous
* tlbie have completed * tlbie have completed
@ -2916,7 +2921,8 @@ void cpu_ppc_dump_state(CPUPPCState *env, FILE *f, int flags)
fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' '); fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' ');
} }
fprintf(f, " ] "); fprintf(f, " ] ");
fprintf(f, "TB: 0x%08x %08x\n", env->tb[1], env->tb[0]); fprintf(f, "TB: 0x%08x %08x\n", cpu_ppc_load_tbu(env),
cpu_ppc_load_tbl(env));
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
if ((i & 3) == 0) if ((i & 3) == 0)
fprintf(f, "FPR%02d:", i); fprintf(f, "FPR%02d:", i);
@ -2924,8 +2930,8 @@ void cpu_ppc_dump_state(CPUPPCState *env, FILE *f, int flags)
if ((i & 3) == 3) if ((i & 3) == 3)
fprintf(f, "\n"); fprintf(f, "\n");
} }
fprintf(f, "SRR0 0x%08x SRR1 0x%08x DECR=0x%08x excp:0x%08x\n", fprintf(f, "SRR0 0x%08x SRR1 0x%08x DECR=0x%08x\n",
env->spr[SRR0], env->spr[SRR1], env->decr, env->exceptions); env->spr[SRR0], env->spr[SRR1], cpu_ppc_load_decr(env));
fprintf(f, "reservation 0x%08x\n", env->reserve); fprintf(f, "reservation 0x%08x\n", env->reserve);
fflush(f); fflush(f);
} }
@ -2952,7 +2958,6 @@ CPUPPCState *cpu_ppc_init(void)
// env->spr[PVR] = 0x00083100; /* MPC755 (G3 embedded) */ // env->spr[PVR] = 0x00083100; /* MPC755 (G3 embedded) */
// env->spr[PVR] = 0x00070100; /* IBM 750FX */ // env->spr[PVR] = 0x00070100; /* IBM 750FX */
#endif #endif
env->decr = 0xFFFFFFFF;
if (create_ppc_proc(ppc_opcodes, env->spr[PVR]) < 0) if (create_ppc_proc(ppc_opcodes, env->spr[PVR]) < 0)
return NULL; return NULL;
init_spr_rights(env->spr[PVR]); init_spr_rights(env->spr[PVR]);
@ -2976,14 +2981,13 @@ void cpu_ppc_close(CPUPPCState *env)
} }
/*****************************************************************************/ /*****************************************************************************/
void raise_exception_err (int exception_index, int error_code);
int print_insn_powerpc (FILE *out, unsigned long insn, unsigned memaddr, int print_insn_powerpc (FILE *out, unsigned long insn, unsigned memaddr,
int dialect); int dialect);
int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb, int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
int search_pc) int search_pc)
{ {
DisasContext ctx; DisasContext ctx, *ctxp = &ctx;
opc_handler_t **table, *handler; opc_handler_t **table, *handler;
uint32_t pc_start; uint32_t pc_start;
uint16_t *gen_opc_end; uint16_t *gen_opc_end;
@ -2994,8 +2998,6 @@ int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
gen_opparam_ptr = gen_opparam_buf; gen_opparam_ptr = gen_opparam_buf;
ctx.nip = pc_start; ctx.nip = pc_start;
ctx.tb_offset = 0;
ctx.decr_offset = 0;
ctx.tb = tb; ctx.tb = tb;
ctx.exception = EXCP_NONE; ctx.exception = EXCP_NONE;
#if defined(CONFIG_USER_ONLY) #if defined(CONFIG_USER_ONLY)
@ -3023,26 +3025,22 @@ int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
gen_opc_instr_start[lj] = 1; gen_opc_instr_start[lj] = 1;
} }
} }
#if defined DEBUG_DISAS #if defined PPC_DEBUG_DISAS
if (loglevel > 0) { if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "----------------\n"); fprintf(logfile, "----------------\n");
fprintf(logfile, "nip=%08x super=%d ir=%d\n", fprintf(logfile, "nip=%08x super=%d ir=%d\n",
ctx.nip, 1 - msr_pr, msr_ir); ctx.nip, 1 - msr_pr, msr_ir);
} }
#endif #endif
ctx.opcode = ldl_code((void *)ctx.nip); ctx.opcode = ldl_code((void *)ctx.nip);
#if defined DEBUG_DISAS #if defined PPC_DEBUG_DISAS
if (loglevel > 0) { if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "translate opcode %08x (%02x %02x %02x)\n", fprintf(logfile, "translate opcode %08x (%02x %02x %02x)\n",
ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode), ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode),
opc3(ctx.opcode)); opc3(ctx.opcode));
} }
#endif #endif
ctx.nip += 4; ctx.nip += 4;
ctx.tb_offset++;
/* Check decrementer exception */
if (++ctx.decr_offset == env->decr + 1)
ctx.exception = EXCP_DECR;
table = ppc_opcodes; table = ppc_opcodes;
handler = table[opc1(ctx.opcode)]; handler = table[opc1(ctx.opcode)];
if (is_indirect_opcode(handler)) { if (is_indirect_opcode(handler)) {
@ -3098,26 +3096,17 @@ int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
(ctx.nip & 0xFC) != 0x04) && (ctx.nip & 0xFC) != 0x04) &&
ctx.exception != EXCP_SYSCALL && ctx.exception != EXCP_RFI && ctx.exception != EXCP_SYSCALL && ctx.exception != EXCP_RFI &&
ctx.exception != EXCP_TRAP)) { ctx.exception != EXCP_TRAP)) {
#if !defined(CONFIG_USER_ONLY) RET_EXCP(ctxp, EXCP_TRACE, 0);
gen_op_queue_exception(EXCP_TRACE);
#endif
if (ctx.exception == EXCP_NONE) {
ctx.exception = EXCP_TRACE;
}
} }
/* if we reach a page boundary, stop generation */ /* if we reach a page boundary, stop generation */
if ((ctx.nip & (TARGET_PAGE_SIZE - 1)) == 0) { if ((ctx.nip & (TARGET_PAGE_SIZE - 1)) == 0) {
RET_EXCP(ctxp, EXCP_BRANCH, 0);
}
}
if (ctx.exception == EXCP_NONE) { if (ctx.exception == EXCP_NONE) {
gen_op_b((long)ctx.tb, ctx.nip); gen_op_b((unsigned long)ctx.tb, ctx.nip);
ctx.exception = EXCP_BRANCH; } else if (ctx.exception != EXCP_BRANCH) {
} gen_op_set_T0(0);
}
}
/* In case of branch, this has already been done *BEFORE* the branch */
if (ctx.exception != EXCP_BRANCH && ctx.exception != EXCP_RFI) {
gen_op_update_tb(ctx.tb_offset);
gen_op_update_decr(ctx.decr_offset);
gen_op_process_exceptions(ctx.nip);
} }
#if 1 #if 1
/* TO BE FIXED: T0 hasn't got a proper value, which makes tb_add_jump /* TO BE FIXED: T0 hasn't got a proper value, which makes tb_add_jump
@ -3144,15 +3133,16 @@ int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
} }
env->access_type = ACCESS_INT; env->access_type = ACCESS_INT;
#ifdef DEBUG_DISAS #ifdef DEBUG_DISAS
if (loglevel > 0) { if (loglevel & CPU_LOG_TB_CPU) {
fprintf(logfile, "---------------- excp: %04x\n", ctx.exception); fprintf(logfile, "---------------- excp: %04x\n", ctx.exception);
cpu_ppc_dump_state(env, logfile, 0); cpu_ppc_dump_state(env, logfile, 0);
}
if (loglevel & CPU_LOG_TB_IN_ASM) {
fprintf(logfile, "IN: %s\n", lookup_symbol((void *)pc_start)); fprintf(logfile, "IN: %s\n", lookup_symbol((void *)pc_start));
#if defined(CONFIG_USER_ONLY)
disas(logfile, (void *)pc_start, ctx.nip - pc_start, 0, 0); disas(logfile, (void *)pc_start, ctx.nip - pc_start, 0, 0);
#endif
fprintf(logfile, "\n"); fprintf(logfile, "\n");
}
if (loglevel & CPU_LOG_TB_OP) {
fprintf(logfile, "OP:\n"); fprintf(logfile, "OP:\n");
dump_ops(gen_opc_buf, gen_opparam_buf); dump_ops(gen_opc_buf, gen_opparam_buf);
fprintf(logfile, "\n"); fprintf(logfile, "\n");