X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=target-ppc%2Fhelper.c;h=55fe2b22419b6583a744d2d4222ad9a8762475fa;hb=6d463de2b3e261e95f224767605eef02acbd2701;hp=87e54111e2f5ea15cba6eaae3cd701ab882ac8eb;hpb=79aceca54a8f12a70e23f418ae584e85093c8907;p=qemu diff --git a/target-ppc/helper.c b/target-ppc/helper.c index 87e5411..55fe2b2 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -18,55 +18,583 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "exec.h" +#if defined (USE_OPEN_FIRMWARE) +#include +#include "of.h" +#endif -extern FILE *logfile; +//#define DEBUG_MMU +//#define DEBUG_BATS +//#define DEBUG_EXCEPTIONS -void cpu_loop_exit(void) +/*****************************************************************************/ +/* PPC MMU emulation */ +int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, + int is_user, int is_softmmu); + +/* Perform BAT hit & translation */ +static int get_bat (CPUState *env, uint32_t *real, int *prot, + uint32_t virtual, int rw, int type) { - longjmp(env->jmp_env, 1); + uint32_t *BATlt, *BATut, *BATu, *BATl; + uint32_t base, BEPIl, BEPIu, bl; + int i; + int ret = -1; + +#if defined (DEBUG_BATS) + if (loglevel > 0) { + fprintf(logfile, "%s: %cBAT v 0x%08x\n", __func__, + type == ACCESS_CODE ? 'I' : 'D', virtual); + } +#endif + switch (type) { + case ACCESS_CODE: + BATlt = env->IBAT[1]; + BATut = env->IBAT[0]; + break; + default: + BATlt = env->DBAT[1]; + BATut = env->DBAT[0]; + break; + } +#if defined (DEBUG_BATS) + if (loglevel > 0) { + fprintf(logfile, "%s...: %cBAT v 0x%08x\n", __func__, + type == ACCESS_CODE ? 'I' : 'D', virtual); + } +#endif + base = virtual & 0xFFFC0000; + for (i = 0; i < 4; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + bl = (*BATu & 0x00001FFC) << 15; +#if defined (DEBUG_BATS) + if (loglevel > 0) { + fprintf(logfile, "%s: %cBAT%d v 0x%08x BATu 0x%08x BATl 0x%08x\n", + __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, + *BATu, *BATl); + } +#endif + if ((virtual & 0xF0000000) == BEPIu && + ((virtual & 0x0FFE0000) & ~bl) == BEPIl) { + /* BAT matches */ + if ((msr_pr == 0 && (*BATu & 0x00000002)) || + (msr_pr == 1 && (*BATu & 0x00000001))) { + /* Get physical address */ + *real = (*BATl & 0xF0000000) | + ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) | + (virtual & 0x0001F000); + if (*BATl & 0x00000001) + *prot = PAGE_READ; + if (*BATl & 0x00000002) + *prot = PAGE_WRITE | PAGE_READ; +#if defined (DEBUG_BATS) + if (loglevel > 0) { + fprintf(logfile, "BAT %d match: r 0x%08x prot=%c%c\n", + i, *real, *prot & PAGE_READ ? 'R' : '-', + *prot & PAGE_WRITE ? 'W' : '-'); + } +#endif + ret = 0; + break; + } + } + } + if (ret < 0) { +#if defined (DEBUG_BATS) + printf("no BAT match for 0x%08x:\n", virtual); + for (i = 0; i < 4; i++) { + BATu = &BATut[i]; + BATl = &BATlt[i]; + BEPIu = *BATu & 0xF0000000; + BEPIl = *BATu & 0x0FFE0000; + bl = (*BATu & 0x00001FFC) << 15; + printf("%s: %cBAT%d v 0x%08x BATu 0x%08x BATl 0x%08x \n\t" + "0x%08x 0x%08x 0x%08x\n", + __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual, + *BATu, *BATl, BEPIu, BEPIl, bl); + } +#endif + } + /* No hit */ + return ret; } -/* shortcuts to generate exceptions */ -void raise_exception_err (int exception_index, int error_code) +/* PTE table lookup */ +static int find_pte (uint32_t *RPN, int *prot, uint32_t base, uint32_t va, + int h, int key, int rw) { - env->exception_index = exception_index; - env->error_code = error_code; + uint32_t pte0, pte1, keep = 0, access = 0; + int i, good = -1, store = 0; + int ret = -1; /* No entry found */ - cpu_loop_exit(); + for (i = 0; i < 8; i++) { + pte0 = ldl_raw(phys_ram_base + base + (i * 8)); + pte1 = ldl_raw(phys_ram_base + base + (i * 8) + 4); +#if defined (DEBUG_MMU) + if (loglevel > 0) { + fprintf(logfile, "Load pte from 0x%08x => 0x%08x 0x%08x " + "%d %d %d 0x%08x\n", base + (i * 8), pte0, pte1, + pte0 >> 31, h, (pte0 >> 6) & 1, va); + } +#endif + /* Check validity and table match */ + if (pte0 & 0x80000000 && (h == ((pte0 >> 6) & 1))) { + /* Check vsid & api */ + if ((pte0 & 0x7FFFFFBF) == va) { + if (good == -1) { + good = i; + keep = pte1; + } else { + /* All matches should have equal RPN, WIMG & PP */ + if ((keep & 0xFFFFF07B) != (pte1 & 0xFFFFF07B)) { + if (loglevel > 0) + fprintf(logfile, "Bad RPN/WIMG/PP\n"); + return -1; + } + } + /* Check access rights */ + if (key == 0) { + access = PAGE_READ; + if ((pte1 & 0x00000003) != 0x3) + access |= PAGE_WRITE; + } else { + switch (pte1 & 0x00000003) { + case 0x0: + access = 0; + break; + case 0x1: + case 0x3: + access = PAGE_READ; + break; + case 0x2: + access = PAGE_READ | PAGE_WRITE; + break; + } + } + if (ret < 0) { + if ((rw == 0 && (access & PAGE_READ)) || + (rw == 1 && (access & PAGE_WRITE))) { +#if defined (DEBUG_MMU) + if (loglevel > 0) + fprintf(logfile, "PTE access granted !\n"); +#endif + good = i; + keep = pte1; + ret = 0; + } else { + /* Access right violation */ + ret = -2; +#if defined (DEBUG_MMU) + if (loglevel > 0) + fprintf(logfile, "PTE access rejected\n"); +#endif + } + *prot = access; + } + } + } + } + if (good != -1) { + *RPN = keep & 0xFFFFF000; +#if defined (DEBUG_MMU) + if (loglevel > 0) { + fprintf(logfile, "found PTE at addr 0x%08x prot=0x%01x ret=%d\n", + *RPN, *prot, ret); + } +#endif + /* Update page flags */ + if (!(keep & 0x00000100)) { + /* Access flag */ + keep |= 0x00000100; + store = 1; + } + if (!(keep & 0x00000080)) { + if (rw && ret == 0) { + /* Change flag */ + keep |= 0x00000080; + store = 1; + } else { + /* Force page fault for first write access */ + *prot &= ~PAGE_WRITE; + } + } + if (store) { + stl_raw(phys_ram_base + base + (good * 8) + 4, keep); + } + } + + return ret; +} + +static inline uint32_t get_pgaddr (uint32_t sdr1, uint32_t hash, uint32_t mask) +{ + return (sdr1 & 0xFFFF0000) | (hash & mask); } -void raise_exception (int exception_index) +/* Perform segment based translation */ +static int get_segment (CPUState *env, uint32_t *real, int *prot, + uint32_t virtual, int rw, int type) { - env->exception_index = exception_index; - env->error_code = 0; + uint32_t pg_addr, sdr, ptem, vsid, pgidx; + uint32_t hash, mask; + uint32_t sr; + int key; + int ret = -1, ret2; - cpu_loop_exit(); + sr = env->sr[virtual >> 28]; +#if defined (DEBUG_MMU) + if (loglevel > 0) { + fprintf(logfile, "Check segment v=0x%08x %d 0x%08x nip=0x%08x " + "lr=0x%08x ir=%d dr=%d pr=%d %d t=%d\n", + virtual, virtual >> 28, sr, env->nip, + env->lr, msr_ir, msr_dr, msr_pr, rw, type); + } +#endif + key = (((sr & 0x20000000) && msr_pr == 1) || + ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0; + if ((sr & 0x80000000) == 0) { +#if defined (DEBUG_MMU) + if (loglevel > 0) + fprintf(logfile, "pte segment: key=%d n=0x%08x\n", + key, sr & 0x10000000); +#endif + /* Check if instruction fetch is allowed, if needed */ + if (type != ACCESS_CODE || (sr & 0x10000000) == 0) { + /* Page address translation */ + vsid = sr & 0x00FFFFFF; + pgidx = (virtual >> 12) & 0xFFFF; + sdr = env->sdr1; + hash = ((vsid ^ pgidx) & 0x0007FFFF) << 6; + mask = ((sdr & 0x000001FF) << 16) | 0xFFC0; + pg_addr = get_pgaddr(sdr, hash, mask); + ptem = (vsid << 7) | (pgidx >> 10); +#if defined (DEBUG_MMU) + if (loglevel > 0) { + fprintf(logfile, "0 sdr1=0x%08x vsid=0x%06x api=0x%04x " + "hash=0x%07x pg_addr=0x%08x\n", sdr, vsid, pgidx, hash, + pg_addr); + } +#endif + /* Primary table lookup */ + ret = find_pte(real, prot, pg_addr, ptem, 0, key, rw); + if (ret < 0) { + /* Secondary table lookup */ + hash = (~hash) & 0x01FFFFC0; + pg_addr = get_pgaddr(sdr, hash, mask); +#if defined (DEBUG_MMU) + if (virtual != 0xEFFFFFFF && loglevel > 0) { + fprintf(logfile, "1 sdr1=0x%08x vsid=0x%06x api=0x%04x " + "hash=0x%05x pg_addr=0x%08x\n", sdr, vsid, pgidx, + hash, pg_addr); + } +#endif + ret2 = find_pte(real, prot, pg_addr, ptem, 1, key, rw); + if (ret2 != -1) + ret = ret2; + } + } else { +#if defined (DEBUG_MMU) + if (loglevel > 0) + fprintf(logfile, "No access allowed\n"); +#endif + ret = -3; + } + } else { +#if defined (DEBUG_MMU) + if (loglevel > 0) + fprintf(logfile, "direct store...\n"); +#endif + /* Direct-store segment : absolutely *BUGGY* for now */ + switch (type) { + case ACCESS_INT: + /* Integer load/store : only access allowed */ + break; + case ACCESS_CODE: + /* No code fetch is allowed in direct-store areas */ + return -4; + case ACCESS_FLOAT: + /* Floating point load/store */ + return -4; + case ACCESS_RES: + /* lwarx, ldarx or srwcx. */ + return -4; + case ACCESS_CACHE: + /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */ + /* Should make the instruction do no-op. + * As it already do no-op, it's quite easy :-) + */ + *real = virtual; + return 0; + case ACCESS_EXT: + /* eciwx or ecowx */ + return -4; + default: + if (logfile) { + fprintf(logfile, "ERROR: instruction should not need " + "address translation\n"); + } + printf("ERROR: instruction should not need " + "address translation\n"); + return -4; + } + if ((rw == 1 || key != 1) && (rw == 0 || key != 0)) { + *real = virtual; + ret = 2; + } else { + ret = -2; + } + } + + return ret; } -/* Helpers for "fat" micro operations */ -uint32_t do_load_cr (void) +int get_physical_address (CPUState *env, uint32_t *physical, int *prot, + uint32_t address, int rw, int access_type) { - return (env->crf[0] << 28) | - (env->crf[1] << 24) | - (env->crf[2] << 20) | - (env->crf[3] << 16) | - (env->crf[4] << 12) | - (env->crf[5] << 8) | - (env->crf[6] << 4) | - (env->crf[7] << 0); + int ret; +#if 0 + if (loglevel > 0) { + fprintf(logfile, "%s\n", __func__); + } +#endif + if ((access_type == ACCESS_CODE && msr_ir == 0) || + (access_type != ACCESS_CODE && msr_dr == 0)) { + /* No address translation */ + *physical = address & ~0xFFF; + *prot = PAGE_READ | PAGE_WRITE; + ret = 0; + } else { + /* Try to find a BAT */ + ret = get_bat(env, physical, prot, address, rw, access_type); + if (ret < 0) { + /* We didn't match any BAT entry */ + ret = get_segment(env, physical, prot, address, rw, access_type); + } + } +#if 0 + if (loglevel > 0) { + fprintf(logfile, "%s address %08x => %08x\n", + __func__, address, *physical); + } +#endif + return ret; } -void do_store_cr (uint32_t crn, uint32_t value) +#if defined(CONFIG_USER_ONLY) +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +{ + return addr; +} +#else +target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) { - int i, sh; + uint32_t phys_addr; + int prot; + + if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0) + return -1; + return phys_addr; +} +#endif + +#if !defined(CONFIG_USER_ONLY) + +#define MMUSUFFIX _mmu +#define GETPC() (__builtin_return_address(0)) + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" - for (i = 0, sh = 7; i < 8; i++, sh --) { - if (crn & (1 << sh)) - env->crf[i] = (value >> (sh * 4)) & 0xF; +/* try to fill the TLB and return an exception if error. If retaddr is + NULL, it means that the function was called in C code (i.e. not + from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +void tlb_fill(unsigned long addr, int is_write, int is_user, void *retaddr) +{ + TranslationBlock *tb; + CPUState *saved_env; + unsigned long pc; + int ret; + + /* XXX: hack to restore env in all cases, even if not called from + generated code */ + saved_env = env; + env = cpu_single_env; + { + unsigned long tlb_addrr, tlb_addrw; + int index; + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_addrr = env->tlb_read[is_user][index].address; + tlb_addrw = env->tlb_write[is_user][index].address; +#if 0 + if (loglevel) { + fprintf(logfile, + "%s 1 %p %p idx=%d addr=0x%08lx tbl_addr=0x%08lx 0x%08lx " + "(0x%08lx 0x%08lx)\n", __func__, env, + &env->tlb_read[is_user][index], index, addr, + tlb_addrr, tlb_addrw, addr & TARGET_PAGE_MASK, + tlb_addrr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); + } +#endif + } + ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, is_user, 1); + if (ret) { + if (retaddr) { + /* now we have a real cpu fault */ + pc = (unsigned long)retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc, NULL); + } + } + do_raise_exception_err(env->exception_index, env->error_code); + } + { + unsigned long tlb_addrr, tlb_addrw; + int index; + index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + tlb_addrr = env->tlb_read[is_user][index].address; + tlb_addrw = env->tlb_write[is_user][index].address; +#if 0 + printf("%s 2 %p %p idx=%d addr=0x%08lx tbl_addr=0x%08lx 0x%08lx " + "(0x%08lx 0x%08lx)\n", __func__, env, + &env->tlb_read[is_user][index], index, addr, + tlb_addrr, tlb_addrw, addr & TARGET_PAGE_MASK, + tlb_addrr & (TARGET_PAGE_MASK | TLB_INVALID_MASK)); +#endif } + env = saved_env; } -uint32_t do_load_xer (void) +void cpu_ppc_init_mmu(CPUState *env) +{ + /* Nothing to do: all translation are disabled */ +} +#endif + +/* Perform address translation */ +int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, + int is_user, int is_softmmu) +{ + uint32_t physical; + int prot; + int exception = 0, error_code = 0; + int access_type; + int ret = 0; + +// printf("%s 0\n", __func__); + access_type = env->access_type; + if (env->user_mode_only) { + /* user mode only emulation */ + ret = -2; + goto do_fault; + } + /* NASTY BUG workaround */ + if (access_type == ACCESS_CODE && rw) { + printf("%s: ERROR WRITE CODE ACCESS\n", __func__); + access_type = ACCESS_INT; + } + ret = get_physical_address(env, &physical, &prot, + address, rw, access_type); + if (ret == 0) { + ret = tlb_set_page(env, address & ~0xFFF, physical, prot, + is_user, is_softmmu); + } else if (ret < 0) { + do_fault: +#if defined (DEBUG_MMU) + if (loglevel > 0) + cpu_ppc_dump_state(env, logfile, 0); +#endif + if (access_type == ACCESS_CODE) { + exception = EXCP_ISI; + switch (ret) { + case -1: + /* No matches in page tables */ + error_code = EXCP_ISI_TRANSLATE; + break; + case -2: + /* Access rights violation */ + error_code = EXCP_ISI_PROT; + break; + case -3: + /* No execute protection violation */ + error_code = EXCP_ISI_NOEXEC; + break; + case -4: + /* Direct store exception */ + /* No code fetch is allowed in direct-store areas */ + error_code = EXCP_ISI_DIRECT; + break; + } + } else { + exception = EXCP_DSI; + switch (ret) { + case -1: + /* No matches in page tables */ + error_code = EXCP_DSI_TRANSLATE; + break; + case -2: + /* Access rights violation */ + error_code = EXCP_DSI_PROT; + break; + case -4: + /* Direct store exception */ + switch (access_type) { + case ACCESS_FLOAT: + /* Floating point load/store */ + exception = EXCP_ALIGN; + error_code = EXCP_ALIGN_FP; + break; + case ACCESS_RES: + /* lwarx, ldarx or srwcx. */ + exception = EXCP_DSI; + error_code = EXCP_DSI_NOTSUP | EXCP_DSI_DIRECT; + break; + case ACCESS_EXT: + /* eciwx or ecowx */ + exception = EXCP_DSI; + error_code = EXCP_DSI_NOTSUP | EXCP_DSI_DIRECT | + EXCP_DSI_ECXW; + break; + default: + printf("DSI: invalid exception (%d)\n", ret); + exception = EXCP_PROGRAM; + error_code = EXCP_INVAL | EXCP_INVAL_INVAL; + break; + } + } + if (rw) + error_code |= EXCP_DSI_STORE; + /* Store fault address */ + env->spr[DAR] = address; + } +#if 0 + printf("%s: set exception to %d %02x\n", + __func__, exception, error_code); +#endif + env->exception_index = exception; + env->error_code = error_code; + ret = 1; + } + + return ret; +} + +uint32_t _load_xer (CPUState *env) { return (xer_so << XER_SO) | (xer_ov << XER_OV) | @@ -74,7 +602,7 @@ uint32_t do_load_xer (void) (xer_bc << XER_BC); } -void do_store_xer (uint32_t value) +void _store_xer (CPUState *env, uint32_t value) { xer_so = (value >> XER_SO) & 0x01; xer_ov = (value >> XER_OV) & 0x01; @@ -82,7 +610,7 @@ void do_store_xer (uint32_t value) xer_bc = (value >> XER_BC) & 0x1f; } -uint32_t do_load_msr (void) +uint32_t _load_msr (CPUState *env) { return (msr_pow << MSR_POW) | (msr_ile << MSR_ILE) | @@ -101,162 +629,268 @@ uint32_t do_load_msr (void) (msr_le << MSR_LE); } -void do_store_msr (uint32_t msr_value) -{ - msr_pow = (msr_value >> MSR_POW) & 0x03; - msr_ile = (msr_value >> MSR_ILE) & 0x01; - msr_ee = (msr_value >> MSR_EE) & 0x01; - msr_pr = (msr_value >> MSR_PR) & 0x01; - msr_fp = (msr_value >> MSR_FP) & 0x01; - msr_me = (msr_value >> MSR_ME) & 0x01; - msr_fe0 = (msr_value >> MSR_FE0) & 0x01; - msr_se = (msr_value >> MSR_SE) & 0x01; - msr_be = (msr_value >> MSR_BE) & 0x01; - msr_fe1 = (msr_value >> MSR_FE1) & 0x01; - msr_ip = (msr_value >> MSR_IP) & 0x01; - msr_ir = (msr_value >> MSR_IR) & 0x01; - msr_dr = (msr_value >> MSR_DR) & 0x01; - msr_ri = (msr_value >> MSR_RI) & 0x01; - msr_le = (msr_value >> MSR_LE) & 0x01; -} - -/* The 32 MSB of the target fpr are undefined. They'll be zero... */ -uint32_t do_load_fpscr (void) +void _store_msr (CPUState *env, uint32_t value) { - return (fpscr_fx << FPSCR_FX) | - (fpscr_fex << FPSCR_FEX) | - (fpscr_vx << FPSCR_VX) | - (fpscr_ox << FPSCR_OX) | - (fpscr_ux << FPSCR_UX) | - (fpscr_zx << FPSCR_ZX) | - (fpscr_xx << FPSCR_XX) | - (fpscr_vsxnan << FPSCR_VXSNAN) | - (fpscr_vxisi << FPSCR_VXISI) | - (fpscr_vxidi << FPSCR_VXIDI) | - (fpscr_vxzdz << FPSCR_VXZDZ) | - (fpscr_vximz << FPSCR_VXIMZ) | - (fpscr_fr << FPSCR_FR) | - (fpscr_fi << FPSCR_FI) | - (fpscr_fprf << FPSCR_FPRF) | - (fpscr_vxsoft << FPSCR_VXSOFT) | - (fpscr_vxsqrt << FPSCR_VXSQRT) | - (fpscr_oe << FPSCR_OE) | - (fpscr_ue << FPSCR_UE) | - (fpscr_ze << FPSCR_ZE) | - (fpscr_xe << FPSCR_XE) | - (fpscr_ni << FPSCR_NI) | - (fpscr_rn << FPSCR_RN); -} - -/* We keep only 32 bits of input... */ -/* For now, this is COMPLETELY BUGGY ! */ -void do_store_fpscr (uint8_t mask, uint32_t fp) -{ - int i; - - for (i = 0; i < 7; i++) { - if ((mask & (1 << i)) == 0) - fp &= ~(0xf << (4 * i)); +#if 0 // TRY + if (((value >> MSR_IR) & 0x01) != msr_ir || + ((value >> MSR_DR) & 0x01) != msr_dr) + { + /* Flush all tlb when changing translation mode or privilege level */ + tlb_flush(env, 1); } - if ((mask & 80) != 0) - fpscr_fx = (fp >> FPSCR_FX) & 0x01; - fpscr_fex = (fp >> FPSCR_FEX) & 0x01; - fpscr_vx = (fp >> FPSCR_VX) & 0x01; - fpscr_ox = (fp >> FPSCR_OX) & 0x01; - fpscr_ux = (fp >> FPSCR_UX) & 0x01; - fpscr_zx = (fp >> FPSCR_ZX) & 0x01; - fpscr_xx = (fp >> FPSCR_XX) & 0x01; - fpscr_vsxnan = (fp >> FPSCR_VXSNAN) & 0x01; - fpscr_vxisi = (fp >> FPSCR_VXISI) & 0x01; - fpscr_vxidi = (fp >> FPSCR_VXIDI) & 0x01; - fpscr_vxzdz = (fp >> FPSCR_VXZDZ) & 0x01; - fpscr_vximz = (fp >> FPSCR_VXIMZ) & 0x01; - fpscr_fr = (fp >> FPSCR_FR) & 0x01; - fpscr_fi = (fp >> FPSCR_FI) & 0x01; - fpscr_fprf = (fp >> FPSCR_FPRF) & 0x1F; - fpscr_vxsoft = (fp >> FPSCR_VXSOFT) & 0x01; - fpscr_vxsqrt = (fp >> FPSCR_VXSQRT) & 0x01; - fpscr_oe = (fp >> FPSCR_OE) & 0x01; - fpscr_ue = (fp >> FPSCR_UE) & 0x01; - fpscr_ze = (fp >> FPSCR_ZE) & 0x01; - fpscr_xe = (fp >> FPSCR_XE) & 0x01; - fpscr_ni = (fp >> FPSCR_NI) & 0x01; - fpscr_rn = (fp >> FPSCR_RN) & 0x03; +#endif + msr_pow = (value >> MSR_POW) & 0x03; + msr_ile = (value >> MSR_ILE) & 0x01; + msr_ee = (value >> MSR_EE) & 0x01; + msr_pr = (value >> MSR_PR) & 0x01; + msr_fp = (value >> MSR_FP) & 0x01; + msr_me = (value >> MSR_ME) & 0x01; + msr_fe0 = (value >> MSR_FE0) & 0x01; + msr_se = (value >> MSR_SE) & 0x01; + msr_be = (value >> MSR_BE) & 0x01; + msr_fe1 = (value >> MSR_FE1) & 0x01; + msr_ip = (value >> MSR_IP) & 0x01; + msr_ir = (value >> MSR_IR) & 0x01; + msr_dr = (value >> MSR_DR) & 0x01; + msr_ri = (value >> MSR_RI) & 0x01; + msr_le = (value >> MSR_LE) & 0x01; } -int32_t do_sraw(int32_t value, uint32_t shift) +void do_interrupt (CPUState *env) { - int32_t ret; +#if defined (CONFIG_USER_ONLY) + env->exception_index |= 0x100; +#else + uint32_t msr; + int excp = env->exception_index; - xer_ca = 0; - if (shift & 0x20) { - ret = (-1) * ((uint32_t)value >> 31); - if (ret < 0) - xer_ca = 1; - } else { - ret = value >> (shift & 0x1f); - if (ret < 0 && (value & ((1 << shift) - 1)) != 0) - xer_ca = 1; + msr = _load_msr(env); +#if defined (DEBUG_EXCEPTIONS) + if ((excp == EXCP_PROGRAM || excp == EXCP_DSI) && msr_pr == 1) + { + if (loglevel > 0) { + fprintf(logfile, "Raise exception at 0x%08x => 0x%08x (%02x)\n", + env->nip, excp << 8, env->error_code); } - - return ret; -} - -void do_lmw (int reg, uint32_t src) -{ - for (; reg <= 31; reg++, src += 4) - ugpr(reg) = ld32(src); -} - -void do_stmw (int reg, uint32_t dest) -{ - for (; reg <= 31; reg++, dest += 4) - st32(dest, ugpr(reg)); -} - -void do_lsw (uint32_t reg, int count, uint32_t src) -{ - uint32_t tmp; - int sh; - - for (; count > 3; count -= 4, src += 4) { - if (reg == 32) - reg = 0; - ugpr(reg++) = ld32(src); + if (loglevel > 0) + cpu_ppc_dump_state(env, logfile, 0); } - if (count > 0) { - for (sh = 24, tmp = 0; count > 0; count--, src++, sh -= 8) { - if (reg == 32) - reg = 0; - tmp |= ld8(src) << sh; - if (sh == 0) { - sh = 32; - ugpr(reg++) = tmp; - tmp = 0; - } +#endif + /* Generate informations in save/restore registers */ + switch (excp) { + case EXCP_OFCALL: +#if defined (USE_OPEN_FIRMWARE) + env->gpr[3] = OF_client_entry((void *)env->gpr[3]); +#endif + return; + case EXCP_RTASCALL: +#if defined (USE_OPEN_FIRMWARE) + printf("RTAS call !\n"); + env->gpr[3] = RTAS_entry((void *)env->gpr[3]); + printf("RTAS call done\n"); +#endif + return; + case EXCP_NONE: + /* Do nothing */ +#if defined (DEBUG_EXCEPTIONS) + printf("%s: escape EXCP_NONE\n", __func__); +#endif + return; + case EXCP_RESET: + if (msr_ip) + excp += 0xFFC00; + goto store_next; + case EXCP_MACHINE_CHECK: + if (msr_me == 0) { + cpu_abort(env, "Machine check exception while not allowed\n"); } - ugpr(reg) = tmp; - } -} - -void do_stsw (uint32_t reg, int count, uint32_t dest) -{ - int sh; - - for (; count > 3; count -= 4, dest += 4) { - if (reg == 32) - reg = 0; - st32(dest, ugpr(reg++)); + msr_me = 0; + break; + case EXCP_DSI: + /* Store exception cause */ + /* data location address has been stored + * when the fault has been detected + */ + msr &= ~0xFFFF0000; + env->spr[DSISR] = 0; + if (env->error_code & EXCP_DSI_TRANSLATE) + env->spr[DSISR] |= 0x40000000; + else if (env->error_code & EXCP_DSI_PROT) + env->spr[DSISR] |= 0x08000000; + else if (env->error_code & EXCP_DSI_NOTSUP) { + env->spr[DSISR] |= 0x80000000; + if (env->error_code & EXCP_DSI_DIRECT) + env->spr[DSISR] |= 0x04000000; + } + if (env->error_code & EXCP_DSI_STORE) + env->spr[DSISR] |= 0x02000000; + if ((env->error_code & 0xF) == EXCP_DSI_DABR) + env->spr[DSISR] |= 0x00400000; + if (env->error_code & EXCP_DSI_ECXW) + env->spr[DSISR] |= 0x00100000; +#if defined (DEBUG_EXCEPTIONS) + if (loglevel) { + fprintf(logfile, "DSI exception: DSISR=0x%08x, DAR=0x%08x\n", + env->spr[DSISR], env->spr[DAR]); + } else { + printf("DSI exception: DSISR=0x%08x, DAR=0x%08x nip=0x%08x\n", + env->spr[DSISR], env->spr[DAR], env->nip); + } +#endif + goto store_next; + case EXCP_ISI: + /* Store exception cause */ + msr &= ~0xFFFF0000; + if (env->error_code == EXCP_ISI_TRANSLATE) + msr |= 0x40000000; + else if (env->error_code == EXCP_ISI_NOEXEC || + env->error_code == EXCP_ISI_GUARD || + env->error_code == EXCP_ISI_DIRECT) + msr |= 0x10000000; + else + msr |= 0x08000000; +#if defined (DEBUG_EXCEPTIONS) + if (loglevel) { + fprintf(logfile, "ISI exception: msr=0x%08x, nip=0x%08x\n", + msr, env->nip); + } else { + printf("ISI exception: msr=0x%08x, nip=0x%08x tbl:0x%08x\n", + msr, env->nip, env->spr[V_TBL]); + } +#endif + goto store_next; + case EXCP_EXTERNAL: + if (msr_ee == 0) { +#if defined (DEBUG_EXCEPTIONS) + if (loglevel > 0) { + fprintf(logfile, "Skipping hardware interrupt\n"); } - if (count > 0) { - for (sh = 24; count > 0; count--, dest++, sh -= 8) { - if (reg == 32) - reg = 0; - st8(dest, (ugpr(reg) >> sh) & 0xFF); - if (sh == 0) { - sh = 32; - reg++; +#endif + /* Requeue it */ + do_raise_exception(EXCP_EXTERNAL); + return; } + goto store_next; + case EXCP_ALIGN: + /* Store exception cause */ + /* Get rS/rD and rA from faulting opcode */ + env->spr[DSISR] |= + (ldl_code((void *)(env->nip - 4)) & 0x03FF0000) >> 16; + /* data location address has been stored + * when the fault has been detected + */ + goto store_current; + case EXCP_PROGRAM: + msr &= ~0xFFFF0000; + switch (env->error_code & ~0xF) { + case EXCP_FP: + if (msr_fe0 == 0 && msr_fe1 == 0) { +#if defined (DEBUG_EXCEPTIONS) + printf("Ignore floating point exception\n"); +#endif + return; + } + msr |= 0x00100000; + /* Set FX */ + env->fpscr[7] |= 0x8; + /* Finally, update FEX */ + if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) & + ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3))) + env->fpscr[7] |= 0x4; + break; + case EXCP_INVAL: + // printf("Invalid instruction at 0x%08x\n", env->nip); + msr |= 0x00080000; + break; + case EXCP_PRIV: + msr |= 0x00040000; + break; + case EXCP_TRAP: + msr |= 0x00020000; + break; + default: + /* Should never occur */ + break; + } + msr |= 0x00010000; + goto store_current; + case EXCP_NO_FP: + goto store_current; + case EXCP_DECR: + if (msr_ee == 0) { + /* Requeue it */ + do_raise_exception(EXCP_DECR); + return; } + goto store_next; + case EXCP_SYSCALL: +#if defined (DEBUG_EXCEPTIONS) + if (msr_pr) { + if (loglevel) { + fprintf(logfile, "syscall %d 0x%08x 0x%08x 0x%08x 0x%08x\n", + env->gpr[0], env->gpr[3], env->gpr[4], + env->gpr[5], env->gpr[6]); + } else { + printf("syscall %d from 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + env->gpr[0], env->nip, env->gpr[3], env->gpr[4], + env->gpr[5], env->gpr[6]); + } + } +#endif + goto store_next; + case EXCP_TRACE: + goto store_next; + case EXCP_FP_ASSIST: + goto store_next; + case EXCP_MTMSR: + /* Nothing to do */ + return; + case EXCP_BRANCH: + /* Nothing to do */ + return; + case EXCP_RFI: + /* Restore user-mode state */ + tb_flush(env); +#if defined (DEBUG_EXCEPTIONS) + if (msr_pr == 1) + printf("Return from exception => 0x%08x\n", (uint32_t)env->nip); +#endif + return; + store_current: + /* SRR0 is set to current instruction */ + env->spr[SRR0] = (uint32_t)env->nip - 4; + break; + store_next: + /* SRR0 is set to next instruction */ + env->spr[SRR0] = (uint32_t)env->nip; + break; } + env->spr[SRR1] = msr; + /* reload MSR with correct bits */ + msr_pow = 0; + msr_ee = 0; + msr_pr = 0; + msr_fp = 0; + msr_fe0 = 0; + msr_se = 0; + msr_be = 0; + msr_fe1 = 0; + msr_ir = 0; + msr_dr = 0; + msr_ri = 0; + msr_le = msr_ile; + /* Jump to handler */ + env->nip = excp << 8; + env->exception_index = EXCP_NONE; + /* Invalidate all TLB as we may have changed translation mode */ + tlb_flush(env, 1); + /* ensure that no TB jump will be modified as + the program flow was changed */ +#ifdef __sparc__ + tmp_T0 = 0; +#else + T0 = 0; +#endif +#endif + env->exception_index = -1; }