blob: 843bd1491eac94e0bf48873db2f28f3a880bcaef [file] [log] [blame]
/*
* 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, see <http://www.gnu.org/licenses/>.
*/
#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 explicitly check that the TB is valid and the FOE bit is reset */
static void update_itb (void)
{
/* 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 (void);
{
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 mmu_idx, int rw)
{
uint64_t pteaddr, pte, pfn;
uint8_t gh;
int ure, uwe, kre, kwe, foE, foR, foW, v, ret, ar, is_user;
/* XXX: TOFIX */
is_user = mmu_idx == MMU_USER_IDX;
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 mmu_idx, 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, mmu_idx, 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 mmu_idx, 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, mmu_idx, rw);
break;
case 1:
/* seg1: 2 levels of PTE */
ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits,
virtual, mmu_idx, rw);
break;
case 2:
/* kernel segment */
if (mmu_idx != 0) {
ret = 2;
} else {
*physp = virtual;
}
break;
case 3:
/* seg1: TB mapped */
ret = paddr_from_pte(physp, zbitsp, protp, ptebase, page_bits,
virtual, mmu_idx, 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 mmu_idx, int is_softmmu)
{
uint64_t physical, page_size, end;
int prot, zbits, ret;
#if defined(CONFIG_USER_ONLY)
ret = 2;
#else
ret = virtual_to_physical(env, &physical, &zbits, &prot,
address, mmu_idx, rw);
#endif
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,
mmu_idx, 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_long ret;
switch (palcode) {
case 0x80:
/* BPT */
qemu_log("BPT\n");
/* FIXME: Sends SIGTRAP, si_code=TRAP_BRKPT. */
exit(1);
case 0x81:
/* BUGCHK */
qemu_log("BUGCHK\n");
/* FIXME: Sends SIGTRAP, si_code=SI_FAULT. */
exit(1);
case 0x83:
/* CALLSYS */
qemu_log("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]);
if (ret >= 0) {
env->ir[IR_A3] = 0;
env->ir[IR_V0] = ret;
} else {
env->ir[IR_A3] = 1;
env->ir[IR_V0] = -ret;
}
break;
case 0x86:
/* IMB */
qemu_log("IMB\n");
/* ??? We can probably elide the code using page_unprotect that is
checking for self-modifying code. Instead we could simply call
tb_flush here. Until we work out the changes required to turn
off the extra write protection, this can be a no-op. */
break;
case 0x9E:
/* RDUNIQUE */
qemu_log("RDUNIQUE: " TARGET_FMT_lx "\n", env->unique);
/* Handled in the translator for usermode. */
abort();
case 0x9F:
/* WRUNIQUE */
qemu_log("WRUNIQUE: " TARGET_FMT_lx "\n", env->ir[IR_A0]);
/* Handled in the translator for usermode. */
abort();
case 0xAA:
/* GENTRAP */
qemu_log("GENTRAP: " TARGET_FMT_lx "\n", env->ir[IR_A0]);
/* FIXME: This is supposed to send a signal:
SIGFPE:
GEN_INTOVF, GEN_INTDIV, GEN_FLTOVF, GEN_FLTDIV,
GEN_FLTUND, GEN_FLTINV, GEN_FLTINE, GEN_ROPRAND
SIGTRAP:
others
with various settings of si_code. */
exit(1);
default:
qemu_log("%s: unhandled palcode %02x\n", __func__, palcode);
exit(1);
}
}
#endif