1ca5d32a3SWarner Losh /* 2ca5d32a3SWarner Losh * arm cpu init and loop 3ca5d32a3SWarner Losh * 4ca5d32a3SWarner Losh * Copyright (c) 2013 Stacey D. Son 5ca5d32a3SWarner Losh * 6ca5d32a3SWarner Losh * This program is free software; you can redistribute it and/or modify 7ca5d32a3SWarner Losh * it under the terms of the GNU General Public License as published by 8ca5d32a3SWarner Losh * the Free Software Foundation; either version 2 of the License, or 9ca5d32a3SWarner Losh * (at your option) any later version. 10ca5d32a3SWarner Losh * 11ca5d32a3SWarner Losh * This program is distributed in the hope that it will be useful, 12ca5d32a3SWarner Losh * but WITHOUT ANY WARRANTY; without even the implied warranty of 13ca5d32a3SWarner Losh * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14ca5d32a3SWarner Losh * GNU General Public License for more details. 15ca5d32a3SWarner Losh * 16ca5d32a3SWarner Losh * You should have received a copy of the GNU General Public License 17ca5d32a3SWarner Losh * along with this program; if not, see <http://www.gnu.org/licenses/>. 18ca5d32a3SWarner Losh */ 19ca5d32a3SWarner Losh 20*9c092804SMarkus Armbruster #ifndef TARGET_ARCH_CPU_H 21*9c092804SMarkus Armbruster #define TARGET_ARCH_CPU_H 22ca5d32a3SWarner Losh 23ca5d32a3SWarner Losh #include "target_arch.h" 242bd010c4SWarner Losh #include "signal-common.h" 25ca5d32a3SWarner Losh 26ca5d32a3SWarner Losh #define TARGET_DEFAULT_CPU_MODEL "any" 27ca5d32a3SWarner Losh 28ca5d32a3SWarner Losh static inline void target_cpu_init(CPUARMState *env, 29ca5d32a3SWarner Losh struct target_pt_regs *regs) 30ca5d32a3SWarner Losh { 31ca5d32a3SWarner Losh int i; 32ca5d32a3SWarner Losh 33ca5d32a3SWarner Losh cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, 34ca5d32a3SWarner Losh CPSRWriteByInstr); 35ca5d32a3SWarner Losh for (i = 0; i < 16; i++) { 36ca5d32a3SWarner Losh env->regs[i] = regs->uregs[i]; 37ca5d32a3SWarner Losh } 38ca5d32a3SWarner Losh } 39ca5d32a3SWarner Losh 4006efe3bfSWarner Losh static inline void target_cpu_loop(CPUARMState *env) 4106efe3bfSWarner Losh { 4267ccbe79SWarner Losh int trapnr, si_signo, si_code; 4306efe3bfSWarner Losh CPUState *cs = env_cpu(env); 4406efe3bfSWarner Losh 4506efe3bfSWarner Losh for (;;) { 4606efe3bfSWarner Losh cpu_exec_start(cs); 4706efe3bfSWarner Losh trapnr = cpu_exec(cs); 4806efe3bfSWarner Losh cpu_exec_end(cs); 4906efe3bfSWarner Losh process_queued_cpu_work(cs); 5006efe3bfSWarner Losh switch (trapnr) { 5170985aecSWarner Losh case EXCP_UDEF: 525e02ded1SWarner Losh case EXCP_NOCP: 535e02ded1SWarner Losh case EXCP_INVSTATE: 545e02ded1SWarner Losh /* 555e02ded1SWarner Losh * See arm/arm/undefined.c undefinedinstruction(); 565e02ded1SWarner Losh * 575e02ded1SWarner Losh * A number of details aren't emulated (they likely don't matter): 585e02ded1SWarner Losh * o Misaligned PC generates ILL_ILLADR (these can't come from qemu) 595e02ded1SWarner Losh * o Thumb-2 instructions generate ILLADR 605e02ded1SWarner Losh * o Both modes implement coprocessor instructions, which we don't 615e02ded1SWarner Losh * do here. FreeBSD just implements them for the VFP coprocessor 625e02ded1SWarner Losh * and special kernel breakpoints, trace points, dtrace, etc. 635e02ded1SWarner Losh */ 645e02ded1SWarner Losh force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC, env->regs[15]); 6570985aecSWarner Losh break; 668d450c9aSWarner Losh case EXCP_SWI: 678d450c9aSWarner Losh { 688d450c9aSWarner Losh int ret; 698d450c9aSWarner Losh abi_ulong params = get_sp_from_cpustate(env); 70e555e709SWarner Losh int32_t syscall_nr = env->regs[7]; 718d450c9aSWarner Losh int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; 728d450c9aSWarner Losh 73c0b93df3SWarner Losh /* See arm/arm/syscall.c cpu_fetch_syscall_args() */ 748d450c9aSWarner Losh if (syscall_nr == TARGET_FREEBSD_NR_syscall) { 758d450c9aSWarner Losh syscall_nr = env->regs[0]; 768d450c9aSWarner Losh arg1 = env->regs[1]; 778d450c9aSWarner Losh arg2 = env->regs[2]; 788d450c9aSWarner Losh arg3 = env->regs[3]; 798d450c9aSWarner Losh get_user_s32(arg4, params); 808d450c9aSWarner Losh params += sizeof(int32_t); 818d450c9aSWarner Losh get_user_s32(arg5, params); 828d450c9aSWarner Losh params += sizeof(int32_t); 838d450c9aSWarner Losh get_user_s32(arg6, params); 848d450c9aSWarner Losh params += sizeof(int32_t); 858d450c9aSWarner Losh get_user_s32(arg7, params); 868d450c9aSWarner Losh arg8 = 0; 878d450c9aSWarner Losh } else if (syscall_nr == TARGET_FREEBSD_NR___syscall) { 888d450c9aSWarner Losh syscall_nr = env->regs[0]; 898d450c9aSWarner Losh arg1 = env->regs[2]; 908d450c9aSWarner Losh arg2 = env->regs[3]; 918d450c9aSWarner Losh get_user_s32(arg3, params); 928d450c9aSWarner Losh params += sizeof(int32_t); 938d450c9aSWarner Losh get_user_s32(arg4, params); 948d450c9aSWarner Losh params += sizeof(int32_t); 958d450c9aSWarner Losh get_user_s32(arg5, params); 968d450c9aSWarner Losh params += sizeof(int32_t); 978d450c9aSWarner Losh get_user_s32(arg6, params); 988d450c9aSWarner Losh arg7 = 0; 998d450c9aSWarner Losh arg8 = 0; 1008d450c9aSWarner Losh } else { 1018d450c9aSWarner Losh arg1 = env->regs[0]; 1028d450c9aSWarner Losh arg2 = env->regs[1]; 1038d450c9aSWarner Losh arg3 = env->regs[2]; 1048d450c9aSWarner Losh arg4 = env->regs[3]; 1058d450c9aSWarner Losh get_user_s32(arg5, params); 1068d450c9aSWarner Losh params += sizeof(int32_t); 1078d450c9aSWarner Losh get_user_s32(arg6, params); 1088d450c9aSWarner Losh params += sizeof(int32_t); 1098d450c9aSWarner Losh get_user_s32(arg7, params); 1108d450c9aSWarner Losh params += sizeof(int32_t); 1118d450c9aSWarner Losh get_user_s32(arg8, params); 1128d450c9aSWarner Losh } 1138d450c9aSWarner Losh ret = do_freebsd_syscall(env, syscall_nr, arg1, arg2, arg3, 1148d450c9aSWarner Losh arg4, arg5, arg6, arg7, arg8); 1158d450c9aSWarner Losh /* 1168d450c9aSWarner Losh * Compare to arm/arm/vm_machdep.c 1178d450c9aSWarner Losh * cpu_set_syscall_retval() 1188d450c9aSWarner Losh */ 1198d450c9aSWarner Losh if (-TARGET_EJUSTRETURN == ret) { 1208d450c9aSWarner Losh /* 1218d450c9aSWarner Losh * Returning from a successful sigreturn syscall. 1228d450c9aSWarner Losh * Avoid clobbering register state. 1238d450c9aSWarner Losh */ 1248d450c9aSWarner Losh break; 1258d450c9aSWarner Losh } 1268d450c9aSWarner Losh if (-TARGET_ERESTART == ret) { 1278d450c9aSWarner Losh env->regs[15] -= env->thumb ? 2 : 4; 1288d450c9aSWarner Losh break; 1298d450c9aSWarner Losh } 1308d450c9aSWarner Losh if ((unsigned int)ret >= (unsigned int)(-515)) { 1318d450c9aSWarner Losh ret = -ret; 1328d450c9aSWarner Losh cpsr_write(env, CPSR_C, CPSR_C, CPSRWriteByInstr); 1338d450c9aSWarner Losh env->regs[0] = ret; 1348d450c9aSWarner Losh } else { 1358d450c9aSWarner Losh cpsr_write(env, 0, CPSR_C, CPSRWriteByInstr); 1368d450c9aSWarner Losh env->regs[0] = ret; /* XXX need to handle lseek()? */ 1378d450c9aSWarner Losh /* env->regs[1] = 0; */ 1388d450c9aSWarner Losh } 1398d450c9aSWarner Losh } 1408d450c9aSWarner Losh break; 14170985aecSWarner Losh case EXCP_INTERRUPT: 14270985aecSWarner Losh /* just indicate that signals should be handled asap */ 14370985aecSWarner Losh break; 144ef1412bdSWarner Losh case EXCP_PREFETCH_ABORT: 145ef1412bdSWarner Losh case EXCP_DATA_ABORT: 14667ccbe79SWarner Losh /* 14767ccbe79SWarner Losh * See arm/arm/trap-v6.c prefetch_abort_handler() and 14867ccbe79SWarner Losh * data_abort_handler() 14967ccbe79SWarner Losh * 15067ccbe79SWarner Losh * However, FreeBSD maps these to a generic value and then uses that 15167ccbe79SWarner Losh * to maybe fault in pages in vm/vm_fault.c:vm_fault_trap(). I 15267ccbe79SWarner Losh * believe that the indirection maps the same as Linux, but haven't 15367ccbe79SWarner Losh * chased down every single possible indirection. 15467ccbe79SWarner Losh */ 15567ccbe79SWarner Losh 15667ccbe79SWarner Losh /* For user-only we don't set TTBCR_EAE, so look at the FSR. */ 15767ccbe79SWarner Losh switch (env->exception.fsr & 0x1f) { 15867ccbe79SWarner Losh case 0x1: /* Alignment */ 15967ccbe79SWarner Losh si_signo = TARGET_SIGBUS; 16067ccbe79SWarner Losh si_code = TARGET_BUS_ADRALN; 16167ccbe79SWarner Losh break; 16267ccbe79SWarner Losh case 0x3: /* Access flag fault, level 1 */ 16367ccbe79SWarner Losh case 0x6: /* Access flag fault, level 2 */ 16467ccbe79SWarner Losh case 0x9: /* Domain fault, level 1 */ 16567ccbe79SWarner Losh case 0xb: /* Domain fault, level 2 */ 16667ccbe79SWarner Losh case 0xd: /* Permission fault, level 1 */ 16767ccbe79SWarner Losh case 0xf: /* Permission fault, level 2 */ 16867ccbe79SWarner Losh si_signo = TARGET_SIGSEGV; 16967ccbe79SWarner Losh si_code = TARGET_SEGV_ACCERR; 17067ccbe79SWarner Losh break; 17167ccbe79SWarner Losh case 0x5: /* Translation fault, level 1 */ 17267ccbe79SWarner Losh case 0x7: /* Translation fault, level 2 */ 17367ccbe79SWarner Losh si_signo = TARGET_SIGSEGV; 17467ccbe79SWarner Losh si_code = TARGET_SEGV_MAPERR; 17567ccbe79SWarner Losh break; 17667ccbe79SWarner Losh default: 17767ccbe79SWarner Losh g_assert_not_reached(); 17867ccbe79SWarner Losh } 17967ccbe79SWarner Losh force_sig_fault(si_signo, si_code, env->exception.vaddress); 180ef1412bdSWarner Losh break; 18170985aecSWarner Losh case EXCP_DEBUG: 182a3ed97ceSWarner Losh case EXCP_BKPT: 183a3ed97ceSWarner Losh force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->regs[15]); 18470985aecSWarner Losh break; 18570985aecSWarner Losh case EXCP_YIELD: 18670985aecSWarner Losh /* nothing to do here for user-mode, just resume guest code */ 18770985aecSWarner Losh break; 188c0d2691cSWarner Losh case EXCP_ATOMIC: 189c0d2691cSWarner Losh cpu_exec_step_atomic(cs); 190c0d2691cSWarner Losh break; 19106efe3bfSWarner Losh default: 19206efe3bfSWarner Losh fprintf(stderr, "qemu: unhandled CPU exception 0x%x - aborting\n", 19306efe3bfSWarner Losh trapnr); 19406efe3bfSWarner Losh cpu_dump_state(cs, stderr, 0); 19506efe3bfSWarner Losh abort(); 19606efe3bfSWarner Losh } /* switch() */ 19706efe3bfSWarner Losh process_pending_signals(env); 19806efe3bfSWarner Losh } /* for (;;) */ 19906efe3bfSWarner Losh } 20006efe3bfSWarner Losh 201e17d4c9aSWarner Losh static inline void target_cpu_clone_regs(CPUARMState *env, target_ulong newsp) 202e17d4c9aSWarner Losh { 203e17d4c9aSWarner Losh if (newsp) { 204e17d4c9aSWarner Losh env->regs[13] = newsp; 205e17d4c9aSWarner Losh } 206e17d4c9aSWarner Losh env->regs[0] = 0; 207e17d4c9aSWarner Losh } 208e17d4c9aSWarner Losh 209bab6ccc5SWarner Losh static inline void target_cpu_reset(CPUArchState *env) 210ca5d32a3SWarner Losh { 211ca5d32a3SWarner Losh } 212ca5d32a3SWarner Losh 213*9c092804SMarkus Armbruster #endif /* TARGET_ARCH_CPU_H */ 214