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
209c092804SMarkus Armbruster #ifndef TARGET_ARCH_CPU_H
219c092804SMarkus 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
target_cpu_init(CPUARMState * env,struct target_pt_regs * regs)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
target_cpu_loop(CPUARMState * env)40*166a4b6eSPhilippe Mathieu-Daudé static inline G_NORETURN 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
target_cpu_clone_regs(CPUARMState * env,target_ulong newsp)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
target_cpu_reset(CPUArchState * env)209bab6ccc5SWarner Losh static inline void target_cpu_reset(CPUArchState *env)
210ca5d32a3SWarner Losh {
211ca5d32a3SWarner Losh }
212ca5d32a3SWarner Losh
2139c092804SMarkus Armbruster #endif /* TARGET_ARCH_CPU_H */
214