6ebbf39000
allowing support of more than 2 mmu access modes. Add backward compatibility is_user variable in targets code when needed. Implement per target cpu_mmu_index function, avoiding duplicated code and #ifdef TARGET_xxx in softmmu core functions. Implement per target mmu modes definitions. As an example, add PowerPC hypervisor mode definition and Alpha executive and kernel modes definitions. Optimize PowerPC case, precomputing mmu_idx when MSR register changes and using the same definition in code translation code. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3384 c046a42c-6fe2-441c-8c8c-71466251a162
1102 lines
27 KiB
C
1102 lines
27 KiB
C
/*
|
|
* 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 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 (env->user_mode_only) {
|
|
ret = 2;
|
|
} else {
|
|
ret = virtual_to_physical(env, &physical, &zbits, &prot,
|
|
address, mmu_idx, 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,
|
|
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_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
|