mirror of
				https://git.proxmox.com/git/qemu
				synced 2025-10-26 19:59:48 +00:00 
			
		
		
		
	 6049f4f831
			
		
	
	
		6049f4f831
		
	
	
	
	
		
			
			Move userland PALcode handling into linux-user main loop so that we can send signals from there. This also makes alpha_palcode.c system-level only, so don't build it for userland. Add defines for GENTRAP PALcall mapping to signals. Signed-off-by: Richard Henderson <rth@twiddle.net> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
		
			
				
	
	
		
			1048 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1048 lines
		
	
	
		
			25 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, see <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| #include <stdint.h>
 | |
| #include <stdlib.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| #include "cpu.h"
 | |
| #include "exec-all.h"
 | |
| 
 | |
| /* 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;
 | |
| 
 | |
|     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
 |