diff options
-rw-r--r-- | hw/alpha_palcode.c | 1099 |
1 files changed, 1099 insertions, 0 deletions
diff --git a/hw/alpha_palcode.c b/hw/alpha_palcode.c new file mode 100644 index 0000000000..0fe9d969fb --- /dev/null +++ b/hw/alpha_palcode.c @@ -0,0 +1,1099 @@ +/* + * Alpha emulation - PALcode emulation for qemu. + * + * Copyright (c) 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> + +#include "qemu.h" +#include "cpu.h" +#include "exec-all.h" + +#if !defined (CONFIG_USER_ONLY) +/* Shared handlers */ +static void pal_reset (CPUState *env); +/* Console handlers */ +static void pal_console_call (CPUState *env, uint32_t palcode); +/* OpenVMS handlers */ +static void pal_openvms_call (CPUState *env, uint32_t palcode); +/* UNIX / Linux handlers */ +static void pal_unix_call (CPUState *env, uint32_t palcode); + +pal_handler_t pal_handlers[] = { + /* Console handler */ + { + .reset = &pal_reset, + .call_pal = &pal_console_call, + }, + /* OpenVMS handler */ + { + .reset = &pal_reset, + .call_pal = &pal_openvms_call, + }, + /* UNIX / Linux handler */ + { + .reset = &pal_reset, + .call_pal = &pal_unix_call, + }, +}; + +#if 0 +/* One must explicitely check that the TB is valid and the FOE bit is reset */ +static void update_itb () +{ + /* This writes into a temp register, not the actual one */ + mtpr(TB_TAG); + mtpr(TB_CTL); + /* This commits the TB update */ + mtpr(ITB_PTE); +} + +static void update_dtb (); +{ + mtpr(TB_CTL); + /* This write into a temp register, not the actual one */ + mtpr(TB_TAG); + /* This commits the TB update */ + mtpr(DTB_PTE); +} +#endif + +static void pal_reset (CPUState *env) +{ +} + +static void do_swappal (CPUState *env, uint64_t palid) +{ + pal_handler_t *pal_handler; + int status; + + status = 0; + switch (palid) { + case 0 ... 2: + pal_handler = &pal_handlers[palid]; + env->pal_handler = pal_handler; + env->ipr[IPR_PAL_BASE] = -1ULL; + (*pal_handler->reset)(env); + break; + case 3 ... 255: + /* Unknown identifier */ + env->ir[0] = 1; + return; + default: + /* We were given the entry point address */ + env->pal_handler = NULL; + env->ipr[IPR_PAL_BASE] = palid; + env->pc = env->ipr[IPR_PAL_BASE]; + cpu_loop_exit(); + } +} + +static void pal_console_call (CPUState *env, uint32_t palcode) +{ + uint64_t palid; + + if (palcode < 0x00000080) { + /* Privileged palcodes */ + if (!(env->ps >> 3)) { + /* TODO: generate privilege exception */ + } + } + switch (palcode) { + case 0x00000000: + /* HALT */ + /* REQUIRED */ + break; + case 0x00000001: + /* CFLUSH */ + break; + case 0x00000002: + /* DRAINA */ + /* REQUIRED */ + /* Implemented as no-op */ + break; + case 0x00000009: + /* CSERVE */ + /* REQUIRED */ + break; + case 0x0000000A: + /* SWPPAL */ + /* REQUIRED */ + palid = env->ir[16]; + do_swappal(env, palid); + break; + case 0x00000080: + /* BPT */ + /* REQUIRED */ + break; + case 0x00000081: + /* BUGCHK */ + /* REQUIRED */ + break; + case 0x00000086: + /* IMB */ + /* REQUIRED */ + /* Implemented as no-op */ + break; + case 0x0000009E: + /* RDUNIQUE */ + /* REQUIRED */ + break; + case 0x0000009F: + /* WRUNIQUE */ + /* REQUIRED */ + break; + case 0x000000AA: + /* GENTRAP */ + /* REQUIRED */ + break; + default: + break; + } +} + +static void pal_openvms_call (CPUState *env, uint32_t palcode) +{ + uint64_t palid, val, oldval; + + if (palcode < 0x00000080) { + /* Privileged palcodes */ + if (!(env->ps >> 3)) { + /* TODO: generate privilege exception */ + } + } + switch (palcode) { + case 0x00000000: + /* HALT */ + /* REQUIRED */ + break; + case 0x00000001: + /* CFLUSH */ + break; + case 0x00000002: + /* DRAINA */ + /* REQUIRED */ + /* Implemented as no-op */ + break; + case 0x00000003: + /* LDQP */ + break; + case 0x00000004: + /* STQP */ + break; + case 0x00000005: + /* SWPCTX */ + break; + case 0x00000006: + /* MFPR_ASN */ + if (cpu_alpha_mfpr(env, IPR_ASN, &val) == 0) + env->ir[0] = val; + break; + case 0x00000007: + /* MTPR_ASTEN */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_ASTEN, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000008: + /* MTPR_ASTSR */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_ASTSR, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000009: + /* CSERVE */ + /* REQUIRED */ + break; + case 0x0000000A: + /* SWPPAL */ + /* REQUIRED */ + palid = env->ir[16]; + do_swappal(env, palid); + break; + case 0x0000000B: + /* MFPR_FEN */ + if (cpu_alpha_mfpr(env, IPR_FEN, &val) == 0) + env->ir[0] = val; + break; + case 0x0000000C: + /* MTPR_FEN */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_FEN, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000000D: + /* MTPR_IPIR */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_IPIR, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000000E: + /* MFPR_IPL */ + if (cpu_alpha_mfpr(env, IPR_IPL, &val) == 0) + env->ir[0] = val; + break; + case 0x0000000F: + /* MTPR_IPL */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_IPL, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000010: + /* MFPR_MCES */ + if (cpu_alpha_mfpr(env, IPR_MCES, &val) == 0) + env->ir[0] = val; + break; + case 0x00000011: + /* MTPR_MCES */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_MCES, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000012: + /* MFPR_PCBB */ + if (cpu_alpha_mfpr(env, IPR_PCBB, &val) == 0) + env->ir[0] = val; + break; + case 0x00000013: + /* MFPR_PRBR */ + if (cpu_alpha_mfpr(env, IPR_PRBR, &val) == 0) + env->ir[0] = val; + break; + case 0x00000014: + /* MTPR_PRBR */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_PRBR, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000015: + /* MFPR_PTBR */ + if (cpu_alpha_mfpr(env, IPR_PTBR, &val) == 0) + env->ir[0] = val; + break; + case 0x00000016: + /* MFPR_SCBB */ + if (cpu_alpha_mfpr(env, IPR_SCBB, &val) == 0) + env->ir[0] = val; + break; + case 0x00000017: + /* MTPR_SCBB */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_SCBB, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000018: + /* MTPR_SIRR */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_SIRR, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000019: + /* MFPR_SISR */ + if (cpu_alpha_mfpr(env, IPR_SISR, &val) == 0) + env->ir[0] = val; + break; + case 0x0000001A: + /* MFPR_TBCHK */ + if (cpu_alpha_mfpr(env, IPR_TBCHK, &val) == 0) + env->ir[0] = val; + break; + case 0x0000001B: + /* MTPR_TBIA */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_TBIA, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000001C: + /* MTPR_TBIAP */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_TBIAP, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000001D: + /* MTPR_TBIS */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_TBIS, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000001E: + /* MFPR_ESP */ + if (cpu_alpha_mfpr(env, IPR_ESP, &val) == 0) + env->ir[0] = val; + break; + case 0x0000001F: + /* MTPR_ESP */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_ESP, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000020: + /* MFPR_SSP */ + if (cpu_alpha_mfpr(env, IPR_SSP, &val) == 0) + env->ir[0] = val; + break; + case 0x00000021: + /* MTPR_SSP */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_SSP, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000022: + /* MFPR_USP */ + if (cpu_alpha_mfpr(env, IPR_USP, &val) == 0) + env->ir[0] = val; + break; + case 0x00000023: + /* MTPR_USP */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_USP, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000024: + /* MTPR_TBISD */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_TBISD, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000025: + /* MTPR_TBISI */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_TBISI, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000026: + /* MFPR_ASTEN */ + if (cpu_alpha_mfpr(env, IPR_ASTEN, &val) == 0) + env->ir[0] = val; + break; + case 0x00000027: + /* MFPR_ASTSR */ + if (cpu_alpha_mfpr(env, IPR_ASTSR, &val) == 0) + env->ir[0] = val; + break; + case 0x00000029: + /* MFPR_VPTB */ + if (cpu_alpha_mfpr(env, IPR_VPTB, &val) == 0) + env->ir[0] = val; + break; + case 0x0000002A: + /* MTPR_VPTB */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_VPTB, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000002B: + /* MTPR_PERFMON */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000002E: + /* MTPR_DATFX */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_DATFX, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000003E: + /* WTINT */ + break; + case 0x0000003F: + /* MFPR_WHAMI */ + if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) + env->ir[0] = val; + break; + case 0x00000080: + /* BPT */ + /* REQUIRED */ + break; + case 0x00000081: + /* BUGCHK */ + /* REQUIRED */ + break; + case 0x00000082: + /* CHME */ + break; + case 0x00000083: + /* CHMK */ + break; + case 0x00000084: + /* CHMS */ + break; + case 0x00000085: + /* CHMU */ + break; + case 0x00000086: + /* IMB */ + /* REQUIRED */ + /* Implemented as no-op */ + break; + case 0x00000087: + /* INSQHIL */ + break; + case 0x00000088: + /* INSQTIL */ + break; + case 0x00000089: + /* INSQHIQ */ + break; + case 0x0000008A: + /* INSQTIQ */ + break; + case 0x0000008B: + /* INSQUEL */ + break; + case 0x0000008C: + /* INSQUEQ */ + break; + case 0x0000008D: + /* INSQUEL/D */ + break; + case 0x0000008E: + /* INSQUEQ/D */ + break; + case 0x0000008F: + /* PROBER */ + break; + case 0x00000090: + /* PROBEW */ + break; + case 0x00000091: + /* RD_PS */ + break; + case 0x00000092: + /* REI */ + break; + case 0x00000093: + /* REMQHIL */ + break; + case 0x00000094: + /* REMQTIL */ + break; + case 0x00000095: + /* REMQHIQ */ + break; + case 0x00000096: + /* REMQTIQ */ + break; + case 0x00000097: + /* REMQUEL */ + break; + case 0x00000098: + /* REMQUEQ */ + break; + case 0x00000099: + /* REMQUEL/D */ + break; + case 0x0000009A: + /* REMQUEQ/D */ + break; + case 0x0000009B: + /* SWASTEN */ + break; + case 0x0000009C: + /* WR_PS_SW */ + break; + case 0x0000009D: + /* RSCC */ + break; + case 0x0000009E: + /* READ_UNQ */ + /* REQUIRED */ + break; + case 0x0000009F: + /* WRITE_UNQ */ + /* REQUIRED */ + break; + case 0x000000A0: + /* AMOVRR */ + break; + case 0x000000A1: + /* AMOVRM */ + break; + case 0x000000A2: + /* INSQHILR */ + break; + case 0x000000A3: + /* INSQTILR */ + break; + case 0x000000A4: + /* INSQHIQR */ + break; + case 0x000000A5: + /* INSQTIQR */ + break; + case 0x000000A6: + /* REMQHILR */ + break; + case 0x000000A7: + /* REMQTILR */ + break; + case 0x000000A8: + /* REMQHIQR */ + break; + case 0x000000A9: + /* REMQTIQR */ + break; + case 0x000000AA: + /* GENTRAP */ + /* REQUIRED */ + break; + case 0x000000AE: + /* CLRFEN */ + break; + default: + break; + } +} + +static void pal_unix_call (CPUState *env, uint32_t palcode) +{ + uint64_t palid, val, oldval; + + if (palcode < 0x00000080) { + /* Privileged palcodes */ + if (!(env->ps >> 3)) { + /* TODO: generate privilege exception */ + } + } + switch (palcode) { + case 0x00000000: + /* HALT */ + /* REQUIRED */ + break; + case 0x00000001: + /* CFLUSH */ + break; + case 0x00000002: + /* DRAINA */ + /* REQUIRED */ + /* Implemented as no-op */ + break; + case 0x00000009: + /* CSERVE */ + /* REQUIRED */ + break; + case 0x0000000A: + /* SWPPAL */ + /* REQUIRED */ + palid = env->ir[16]; + do_swappal(env, palid); + break; + case 0x0000000D: + /* WRIPIR */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_IPIR, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000010: + /* RDMCES */ + if (cpu_alpha_mfpr(env, IPR_MCES, &val) == 0) + env->ir[0] = val; + break; + case 0x00000011: + /* WRMCES */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_MCES, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000002B: + /* WRFEN */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000002D: + /* WRVPTPTR */ + break; + case 0x00000030: + /* SWPCTX */ + break; + case 0x00000031: + /* WRVAL */ + break; + case 0x00000032: + /* RDVAL */ + break; + case 0x00000033: + /* TBI */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_TBIS, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000034: + /* WRENT */ + break; + case 0x00000035: + /* SWPIPL */ + break; + case 0x00000036: + /* RDPS */ + break; + case 0x00000037: + /* WRKGP */ + break; + case 0x00000038: + /* WRUSP */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_USP, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x00000039: + /* WRPERFMON */ + val = env->ir[16]; + if (cpu_alpha_mtpr(env, IPR_PERFMON, val, &oldval) == 1) + env->ir[0] = val; + break; + case 0x0000003A: + /* RDUSP */ + if (cpu_alpha_mfpr(env, IPR_USP, &val) == 0) + env->ir[0] = val; + break; + case 0x0000003C: + /* WHAMI */ + if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) + env->ir[0] = val; + break; + case 0x0000003D: + /* RETSYS */ + break; + case 0x0000003E: + /* WTINT */ + break; + case 0x0000003F: + /* RTI */ + if (cpu_alpha_mfpr(env, IPR_WHAMI, &val) == 0) + env->ir[0] = val; + break; + case 0x00000080: + /* BPT */ + /* REQUIRED */ + break; + case 0x00000081: + /* BUGCHK */ + /* REQUIRED */ + break; + case 0x00000083: + /* CALLSYS */ + break; + case 0x00000086: + /* IMB */ + /* REQUIRED */ + /* Implemented as no-op */ + break; + case 0x00000092: + /* URTI */ + break; + case 0x0000009E: + /* RDUNIQUE */ + /* REQUIRED */ + break; + case 0x0000009F: + /* WRUNIQUE */ + /* REQUIRED */ + break; + case 0x000000AA: + /* GENTRAP */ + /* REQUIRED */ + break; + case 0x000000AE: + /* CLRFEN */ + break; + default: + break; + } +} + +void call_pal (CPUState *env) +{ + pal_handler_t *pal_handler = env->pal_handler; + + switch (env->exception_index) { + case EXCP_RESET: + (*pal_handler->reset)(env); + break; + case EXCP_MCHK: + (*pal_handler->machine_check)(env); + break; + case EXCP_ARITH: + (*pal_handler->arithmetic)(env); + break; + case EXCP_INTERRUPT: + (*pal_handler->interrupt)(env); + break; + case EXCP_DFAULT: + (*pal_handler->dfault)(env); + break; + case EXCP_DTB_MISS_PAL: + (*pal_handler->dtb_miss_pal)(env); + break; + case EXCP_DTB_MISS_NATIVE: + (*pal_handler->dtb_miss_native)(env); + break; + case EXCP_UNALIGN: + (*pal_handler->unalign)(env); + break; + case EXCP_ITB_MISS: + (*pal_handler->itb_miss)(env); + break; + case EXCP_ITB_ACV: + (*pal_handler->itb_acv)(env); + break; + case EXCP_OPCDEC: + (*pal_handler->opcdec)(env); + break; + case EXCP_FEN: + (*pal_handler->fen)(env); + break; + default: + if (env->exception_index >= EXCP_CALL_PAL && + env->exception_index < EXCP_CALL_PALP) { + /* Unprivileged PAL call */ + (*pal_handler->call_pal) + (env, (env->exception_index - EXCP_CALL_PAL) >> 6); + } else if (env->exception_index >= EXCP_CALL_PALP && + env->exception_index < EXCP_CALL_PALE) { + /* Privileged PAL call */ + (*pal_handler->call_pal) + (env, ((env->exception_index - EXCP_CALL_PALP) >> 6) + 0x80); + } else { + /* Should never happen */ + } + break; + } + env->ipr[IPR_EXC_ADDR] &= ~1; +} + +void pal_init (CPUState *env) +{ + do_swappal(env, 0); +} + +#if 0 +static uint64_t get_ptebase (CPUState *env, uint64_t vaddr) +{ + uint64_t virbnd, ptbr; + + if ((env->features & FEATURE_VIRBND)) { + cpu_alpha_mfpr(env, IPR_VIRBND, &virbnd); + if (vaddr >= virbnd) + cpu_alpha_mfpr(env, IPR_SYSPTBR, &ptbr); + else + cpu_alpha_mfpr(env, IPR_PTBR, &ptbr); + } else { + cpu_alpha_mfpr(env, IPR_PTBR, &ptbr); + } + + return ptbr; +} + +static int get_page_bits (CPUState *env) +{ + /* XXX */ + return 13; +} + +static int get_pte (uint64_t *pfnp, int *zbitsp, int *protp, + uint64_t ptebase, int page_bits, uint64_t level, + int is_user, int rw) +{ + uint64_t pteaddr, pte, pfn; + uint8_t gh; + int ure, uwe, kre, kwe, foE, foR, foW, v, ret, ar; + + pteaddr = (ptebase << page_bits) + (8 * level); + pte = ldq_raw(pteaddr); + /* Decode all interresting PTE fields */ + pfn = pte >> 32; + uwe = (pte >> 13) & 1; + kwe = (pte >> 12) & 1; + ure = (pte >> 9) & 1; + kre = (pte >> 8) & 1; + gh = (pte >> 5) & 3; + foE = (pte >> 3) & 1; + foW = (pte >> 2) & 1; + foR = (pte >> 1) & 1; + v = pte & 1; + ret = 0; + if (!v) + ret = 0x1; + /* Check access rights */ + ar = 0; + if (is_user) { + if (ure) + ar |= PAGE_READ; + if (uwe) + ar |= PAGE_WRITE; + if (rw == 1 && !uwe) + ret |= 0x2; + if (rw != 1 && !ure) + ret |= 0x2; + } else { + if (kre) + ar |= PAGE_READ; + if (kwe) + ar |= PAGE_WRITE; + if (rw == 1 && !kwe) + ret |= 0x2; + if (rw != 1 && !kre) + ret |= 0x2; + } + if (rw == 0 && foR) + ret |= 0x4; + if (rw == 2 && foE) + ret |= 0x8; + if (rw == 1 && foW) + ret |= 0xC; + *pfnp = pfn; + if (zbitsp != NULL) + *zbitsp = page_bits + (3 * gh); + if (protp != NULL) + *protp = ar; + + return ret; +} + +static int paddr_from_pte (uint64_t *paddr, int *zbitsp, int *prot, + uint64_t ptebase, int page_bits, + uint64_t vaddr, int is_user, int rw) +{ + uint64_t pfn, page_mask, lvl_mask, level1, level2, level3; + int lvl_bits, ret; + + page_mask = (1ULL << page_bits) - 1ULL; + lvl_bits = page_bits - 3; + lvl_mask = (1ULL << lvl_bits) - 1ULL; + level3 = (vaddr >> page_bits) & lvl_mask; + level2 = (vaddr >> (page_bits + lvl_bits)) & lvl_mask; + level1 = (vaddr >> (page_bits + (2 * lvl_bits))) & lvl_mask; + /* Level 1 PTE */ + ret = get_pte(&pfn, NULL, NULL, ptebase, page_bits, level1, 0, 0); + switch (ret) { + case 3: + /* Access violation */ + return 2; + case 2: + /* translation not valid */ + return 1; + default: + /* OK */ + break; + } + /* Level 2 PTE */ + ret = get_pte(&pfn, NULL, NULL, pfn, page_bits, level2, 0, 0); + switch (ret) { + case 3: + /* Access violation */ + return 2; + case 2: + /* translation not valid */ + return 1; + default: + /* OK */ + break; + } + /* Level 3 PTE */ + ret = get_pte(&pfn, zbitsp, prot, pfn, page_bits, level3, is_user, rw); + if (ret & 0x1) { + /* Translation not valid */ + ret = 1; + } else if (ret & 2) { + /* Access violation */ + ret = 2; + } else { + switch (ret & 0xC) { + case 0: + /* OK */ + ret = 0; + break; + case 0x4: + /* Fault on read */ + ret = 3; + break; + case 0x8: + /* Fault on execute */ + ret = 4; + break; + case 0xC: + /* Fault on write */ + ret = 5; + break; + } + } + *paddr = (pfn << page_bits) | (vaddr & page_mask); + + return 0; +} + +static int virtual_to_physical (CPUState *env, uint64_t *physp, + int *zbitsp, int *protp, + uint64_t virtual, int is_user, int rw) +{ + uint64_t sva, ptebase; + int seg, page_bits, ret; + + sva = ((int64_t)(virtual << (64 - VA_BITS))) >> (64 - VA_BITS); + if (sva != virtual) + seg = -1; + else + seg = sva >> (VA_BITS - 2); + virtual &= ~(0xFFFFFC0000000000ULL << (VA_BITS - 43)); + ptebase = get_ptebase(env, virtual); + page_bits = get_page_bits(env); + ret = 0; + switch (seg) { + case 0: + /* seg1: 3 levels of PTE */ + ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, + virtual, is_user, rw); + break; + case 1: + /* seg1: 2 levels of PTE */ + ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, + virtual, is_user, rw); + break; + case 2: + /* kernel segment */ + if (is_user) { + ret = 2; + } else { + *physp = virtual; + } + break; + case 3: + /* seg1: TB mapped */ + ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits, + virtual, is_user, rw); + break; + default: + ret = 1; + break; + } + + return ret; +} + +/* XXX: code provision */ +int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, + int is_user, int is_softmmu) +{ + uint64_t physical, page_size, end; + int prot, zbits, ret; + + if (env->user_mode_only) { + ret = 2; + } else { + ret = virtual_to_physical(env, &physical, &zbits, &prot, + address, is_user, rw); + } + switch (ret) { + case 0: + /* No fault */ + page_size = 1ULL << zbits; + address &= ~(page_size - 1); + for (end = physical + page_size; physical < end; physical += 0x1000) { + ret = tlb_set_page(env, address, physical, prot, + is_user, is_softmmu); + address += 0x1000; + } + break; +#if 0 + case 1: + env->exception_index = EXCP_DFAULT; + env->ipr[IPR_EXC_ADDR] = address; + ret = 1; + break; + case 2: + env->exception_index = EXCP_ACCESS_VIOLATION; + env->ipr[IPR_EXC_ADDR] = address; + ret = 1; + break; + case 3: + env->exception_index = EXCP_FAULT_ON_READ; + env->ipr[IPR_EXC_ADDR] = address; + ret = 1; + break; + case 4: + env->exception_index = EXCP_FAULT_ON_EXECUTE; + env->ipr[IPR_EXC_ADDR] = address; + ret = 1; + case 5: + env->exception_index = EXCP_FAULT_ON_WRITE; + env->ipr[IPR_EXC_ADDR] = address; + ret = 1; +#endif + default: + /* Should never happen */ + env->exception_index = EXCP_MCHK; + env->ipr[IPR_EXC_ADDR] = address; + ret = 1; + break; + } + + return ret; +} +#endif + +#else /* !defined (CONFIG_USER_ONLY) */ +void pal_init (CPUState *env) +{ +} + +void call_pal (CPUState *env, int palcode) +{ + target_ulong ret; + + printf("%s: palcode %02x\n", __func__, palcode); + if (logfile != NULL) + fprintf(logfile, "%s: palcode %02x\n", __func__, palcode); + switch (palcode) { + case 0x83: + /* CALLSYS */ + printf("CALLSYS n " TARGET_FMT_ld "\n", env->ir[0]); + if (logfile != NULL) + fprintf(logfile, "CALLSYS n " TARGET_FMT_ld "\n", env->ir[0]); + ret = do_syscall(env, env->ir[IR_V0], env->ir[IR_A0], env->ir[IR_A1], + env->ir[IR_A2], env->ir[IR_A3], env->ir[IR_A4], + env->ir[IR_A5]); + env->ir[IR_A3] = ret; + if (ret > (target_ulong)(-515)) { + env->ir[IR_V0] = 1; + } else { + env->ir[IR_V0] = 0; + } + break; + case 0x9E: + /* RDUNIQUE */ + env->ir[IR_V0] = env->unique; + printf("RDUNIQUE: " TARGET_FMT_lx "\n", env->unique); + break; + case 0x9F: + /* WRUNIQUE */ + env->unique = env->ir[IR_A0]; + printf("WRUNIQUE: " TARGET_FMT_lx "\n", env->unique); + break; + default: + printf("%s: unhandled palcode %02x\n", __func__, palcode); + if (logfile != NULL) + fprintf(logfile, "%s: unhandled palcode %02x\n", + __func__, palcode); + exit(1); + } +} +#endif |