diff --git a/sys/arm64/arm64/freebsd32_machdep.c b/sys/arm64/arm64/freebsd32_machdep.c index 5fadef74df87..44f35f1b2abf 100644 --- a/sys/arm64/arm64/freebsd32_machdep.c +++ b/sys/arm64/arm64/freebsd32_machdep.c @@ -1,460 +1,464 @@ /*- * Copyright (c) 2018 Olivier Houchard * Copyright (c) 2017 Nuxi, https://nuxi.nl/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include +#include #include #include #include #include #ifdef VFP #include #endif #include #include #include #include #include #include _Static_assert(sizeof(mcontext32_t) == 208, "mcontext32_t size incorrect"); _Static_assert(sizeof(ucontext32_t) == 260, "ucontext32_t size incorrect"); _Static_assert(sizeof(struct siginfo32) == 64, "struct siginfo32 size incorrect"); extern void freebsd32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask); +SYSCTL_NODE(_compat, OID_AUTO, arm, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, + "32-bit mode"); + /* * The first two fields of a ucontext_t are the signal mask and the machine * context. The next field is uc_link; we want to avoid destroying the link * when copying out contexts. */ #define UC32_COPY_SIZE offsetof(ucontext32_t, uc_link) /* * Stubs for machine dependent 32-bits system calls. */ int freebsd32_sysarch(struct thread *td, struct freebsd32_sysarch_args *uap) { int error; #define ARM_SYNC_ICACHE 0 #define ARM_DRAIN_WRITEBUF 1 #define ARM_SET_TP 2 #define ARM_GET_TP 3 #define ARM_GET_VFPSTATE 4 switch(uap->op) { case ARM_SET_TP: WRITE_SPECIALREG(tpidr_el0, uap->parms); WRITE_SPECIALREG(tpidrro_el0, uap->parms); return 0; case ARM_SYNC_ICACHE: { struct { uint32_t addr; uint32_t size; } args; if ((error = copyin(uap->parms, &args, sizeof(args))) != 0) return (error); if ((uint64_t)args.addr + (uint64_t)args.size > 0xffffffff) return (EINVAL); cpu_icache_sync_range_checked(args.addr, args.size); return 0; } case ARM_GET_VFPSTATE: { mcontext32_vfp_t mcontext_vfp; struct { uint32_t mc_vfp_size; uint32_t mc_vfp; } args; if ((error = copyin(uap->parms, &args, sizeof(args))) != 0) return (error); if (args.mc_vfp_size != sizeof(mcontext_vfp)) return (EINVAL); #ifdef VFP get_fpcontext32(td, &mcontext_vfp); #else bzero(&mcontext_vfp, sizeof(mcontext_vfp)); #endif error = copyout(&mcontext_vfp, (void *)(uintptr_t)args.mc_vfp, sizeof(mcontext_vfp)); return error; } } return (EINVAL); } #ifdef VFP void get_fpcontext32(struct thread *td, mcontext32_vfp_t *mcp) { struct pcb *pcb; int i; KASSERT(td == curthread || TD_IS_SUSPENDED(td) || P_SHOULDSTOP(td->td_proc), ("not suspended thread %p", td)); memset(mcp, 0, sizeof(*mcp)); pcb = td->td_pcb; if ((pcb->pcb_fpflags & PCB_FP_STARTED) != 0) { /* * If we have just been running VFP instructions we will * need to save the state to memcpy it below. */ if (td == curthread) vfp_save_state(td, pcb); KASSERT(pcb->pcb_fpusaved == &pcb->pcb_fpustate, ("Called get_fpcontext32 while the kernel is using the VFP")); KASSERT((pcb->pcb_fpflags & ~PCB_FP_USERMASK) == 0, ("Non-userspace FPU flags set in get_fpcontext32")); for (i = 0; i < 32; i++) mcp->mcv_reg[i] = (uint64_t)pcb->pcb_fpustate.vfp_regs[i]; mcp->mcv_fpscr = VFP_FPSCR_FROM_SRCR(pcb->pcb_fpustate.vfp_fpcr, pcb->pcb_fpustate.vfp_fpsr); } } void set_fpcontext32(struct thread *td, mcontext32_vfp_t *mcp) { struct pcb *pcb; int i; critical_enter(); pcb = td->td_pcb; if (td == curthread) vfp_discard(td); for (i = 0; i < 32; i++) pcb->pcb_fpustate.vfp_regs[i] = mcp->mcv_reg[i]; pcb->pcb_fpustate.vfp_fpsr = VFP_FPSR_FROM_FPSCR(mcp->mcv_fpscr); pcb->pcb_fpustate.vfp_fpcr = VFP_FPSR_FROM_FPSCR(mcp->mcv_fpscr); critical_exit(); } #endif static void get_mcontext32(struct thread *td, mcontext32_t *mcp, int flags) { struct trapframe *tf; int i; tf = td->td_frame; if ((flags & GET_MC_CLEAR_RET) != 0) { mcp->mc_gregset[0] = 0; mcp->mc_gregset[16] = tf->tf_spsr & ~PSR_C; } else { mcp->mc_gregset[0] = tf->tf_x[0]; mcp->mc_gregset[16] = tf->tf_spsr; } for (i = 1; i < 15; i++) mcp->mc_gregset[i] = tf->tf_x[i]; mcp->mc_gregset[15] = tf->tf_elr; mcp->mc_vfp_size = 0; mcp->mc_vfp_ptr = 0; memset(mcp->mc_spare, 0, sizeof(mcp->mc_spare)); } static int set_mcontext32(struct thread *td, mcontext32_t *mcp) { struct trapframe *tf; mcontext32_vfp_t mc_vfp; uint32_t spsr; int i; tf = td->td_frame; spsr = mcp->mc_gregset[16]; /* * There is no PSR_SS in the 32-bit kernel so ignore it if it's set * as we will set it later if needed. */ if ((spsr & ~(PSR_SETTABLE_32 | PSR_SS)) != (tf->tf_spsr & ~(PSR_SETTABLE_32 | PSR_SS))) return (EINVAL); spsr &= PSR_SETTABLE_32; spsr |= tf->tf_spsr & ~PSR_SETTABLE_32; if ((td->td_dbgflags & TDB_STEP) != 0) { spsr |= PSR_SS; td->td_pcb->pcb_flags |= PCB_SINGLE_STEP; WRITE_SPECIALREG(mdscr_el1, READ_SPECIALREG(mdscr_el1) | MDSCR_SS); } for (i = 0; i < 15; i++) tf->tf_x[i] = mcp->mc_gregset[i]; tf->tf_elr = mcp->mc_gregset[15]; tf->tf_spsr = spsr; #ifdef VFP if (mcp->mc_vfp_size == sizeof(mc_vfp) && mcp->mc_vfp_ptr != 0) { if (copyin((void *)(uintptr_t)mcp->mc_vfp_ptr, &mc_vfp, sizeof(mc_vfp)) != 0) return (EFAULT); set_fpcontext32(td, &mc_vfp); } #endif return (0); } #define UC_COPY_SIZE offsetof(ucontext32_t, uc_link) int freebsd32_getcontext(struct thread *td, struct freebsd32_getcontext_args *uap) { ucontext32_t uc; int ret; if (uap->ucp == NULL) ret = EINVAL; else { memset(&uc, 0, sizeof(uc)); get_mcontext32(td, &uc.uc_mcontext, GET_MC_CLEAR_RET); PROC_LOCK(td->td_proc); uc.uc_sigmask = td->td_sigmask; PROC_UNLOCK(td->td_proc); ret = copyout(&uc, uap->ucp, UC_COPY_SIZE); } return (ret); } int freebsd32_setcontext(struct thread *td, struct freebsd32_setcontext_args *uap) { ucontext32_t uc; int ret; if (uap->ucp == NULL) ret = EINVAL; else { ret = copyin(uap->ucp, &uc, UC_COPY_SIZE); if (ret == 0) { ret = set_mcontext32(td, &uc.uc_mcontext); if (ret == 0) kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); } } return (ret); } int freebsd32_sigreturn(struct thread *td, struct freebsd32_sigreturn_args *uap) { ucontext32_t uc; int error; if (uap == NULL) return (EFAULT); if (copyin(uap->sigcntxp, &uc, sizeof(uc))) return (EFAULT); error = set_mcontext32(td, &uc.uc_mcontext); if (error != 0) return (0); /* Restore signal mask. */ kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); return (EJUSTRETURN); } int freebsd32_swapcontext(struct thread *td, struct freebsd32_swapcontext_args *uap) { ucontext32_t uc; int ret; if (uap->oucp == NULL || uap->ucp == NULL) ret = EINVAL; else { bzero(&uc, sizeof(uc)); get_mcontext32(td, &uc.uc_mcontext, GET_MC_CLEAR_RET); PROC_LOCK(td->td_proc); uc.uc_sigmask = td->td_sigmask; PROC_UNLOCK(td->td_proc); ret = copyout(&uc, uap->oucp, UC32_COPY_SIZE); if (ret == 0) { ret = copyin(uap->ucp, &uc, UC32_COPY_SIZE); if (ret == 0) { ret = set_mcontext32(td, &uc.uc_mcontext); kern_sigprocmask(td, SIG_SETMASK, &uc.uc_sigmask, NULL, 0); } } } return (ret); } void freebsd32_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) { struct thread *td; struct proc *p; struct trapframe *tf; struct sigframe32 *fp, frame; struct sigacts *psp; struct siginfo32 siginfo; struct sysentvec *sysent; int onstack; int sig; siginfo_to_siginfo32(&ksi->ksi_info, &siginfo); td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); sig = ksi->ksi_signo; psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); tf = td->td_frame; onstack = sigonstack(tf->tf_x[13]); CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, catcher, sig); /* Allocate and validate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !(onstack) && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct sigframe32 *)((uintptr_t)td->td_sigstk.ss_sp + td->td_sigstk.ss_size); #if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else fp = (struct sigframe32 *)td->td_frame->tf_x[13]; /* make room on the stack */ fp--; /* make the stack aligned */ fp = (struct sigframe32 *)((unsigned long)(fp) &~ (8 - 1)); /* Populate the siginfo frame. */ get_mcontext32(td, &frame.sf_uc.uc_mcontext, 0); #ifdef VFP get_fpcontext32(td, &frame.sf_vfp); frame.sf_uc.uc_mcontext.mc_vfp_size = sizeof(fp->sf_vfp); frame.sf_uc.uc_mcontext.mc_vfp_ptr = (uint32_t)(uintptr_t)&fp->sf_vfp; #else frame.sf_uc.uc_mcontext.mc_vfp_size = 0; frame.sf_uc.uc_mcontext.mc_vfp_ptr = (uint32_t)NULL; #endif frame.sf_si = siginfo; frame.sf_uc.uc_sigmask = *mask; frame.sf_uc.uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK ) ? ((onstack) ? SS_ONSTACK : 0) : SS_DISABLE; frame.sf_uc.uc_stack.ss_sp = (uintptr_t)td->td_sigstk.ss_sp; frame.sf_uc.uc_stack.ss_size = td->td_sigstk.ss_size; mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(td->td_proc); /* Copy the sigframe out to the user's stack. */ if (copyout(&frame, fp, sizeof(*fp)) != 0) { /* Process has trashed its stack. Kill it. */ CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp); PROC_LOCK(p); sigexit(td, SIGILL); } /* * Build context to run handler in. We invoke the handler * directly, only returning via the trampoline. Note the * trampoline version numbers are coordinated with machine- * dependent code in libc. */ tf->tf_x[0] = sig; tf->tf_x[1] = (register_t)&fp->sf_si; tf->tf_x[2] = (register_t)&fp->sf_uc; /* the trampoline uses r5 as the uc address */ tf->tf_x[5] = (register_t)&fp->sf_uc; tf->tf_elr = (register_t)catcher; tf->tf_x[13] = (register_t)fp; sysent = p->p_sysent; if (PROC_HAS_SHP(p)) tf->tf_x[14] = (register_t)PROC_SIGCODE(p); else tf->tf_x[14] = (register_t)(PROC_PS_STRINGS(p) - *(sysent->sv_szsigcode)); /* Set the mode to enter in the signal handler */ if ((register_t)catcher & 1) tf->tf_spsr |= PSR_T; else tf->tf_spsr &= ~PSR_T; /* Clear the single step flag while in the signal handler */ if ((td->td_pcb->pcb_flags & PCB_SINGLE_STEP) != 0) { td->td_pcb->pcb_flags &= ~PCB_SINGLE_STEP; WRITE_SPECIALREG(mdscr_el1, READ_SPECIALREG(mdscr_el1) & ~MDSCR_SS); isb(); } CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_x[14], tf->tf_x[13]); PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } #ifdef COMPAT_43 /* * Mirror the osigreturn definition in kern_sig.c for !i386 platforms. This * mirrors what's connected to the FreeBSD/arm syscall. */ int ofreebsd32_sigreturn(struct thread *td, struct ofreebsd32_sigreturn_args *uap) { return (nosys(td, (struct nosys_args *)uap)); } #endif diff --git a/sys/arm64/arm64/undefined.c b/sys/arm64/arm64/undefined.c index 1feb242db060..7f436aaef6e5 100644 --- a/sys/arm64/arm64/undefined.c +++ b/sys/arm64/arm64/undefined.c @@ -1,177 +1,339 @@ /*- * Copyright (c) 2017 Andrew Turner * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include +#include #include +#include #include +#define _MD_WANT_SWAPWORD +#include +#include #include #include +#include +#include + +/* Low bit masked off */ +#define INSN_COND(insn) ((insn >> 28) & ~0x1) +#define INSN_COND_INVERTED(insn) ((insn >> 28) & 0x1) +#define INSN_COND_EQ 0x00 /* NE */ +#define INSN_COND_CS 0x02 /* CC */ +#define INSN_COND_MI 0x04 /* PL */ +#define INSN_COND_VS 0x06 /* VC */ +#define INSN_COND_HI 0x08 /* LS */ +#define INSN_COND_GE 0x0a /* LT */ +#define INSN_COND_GT 0x0c /* LE */ +#define INSN_COND_AL 0x0e /* Always */ + MALLOC_DEFINE(M_UNDEF, "undefhandler", "Undefined instruction handler data"); +#ifdef COMPAT_FREEBSD32 +#ifndef EMUL_SWP +#define EMUL_SWP 0 +#endif + +SYSCTL_DECL(_compat_arm); + +static bool compat32_emul_swp = EMUL_SWP; +SYSCTL_BOOL(_compat_arm, OID_AUTO, emul_swp, + CTLFLAG_RWTUN | CTLFLAG_MPSAFE, &compat32_emul_swp, 0, + "Enable SWP/SWPB emulation"); +#endif + struct undef_handler { LIST_ENTRY(undef_handler) uh_link; undef_handler_t uh_handler; }; /* * Create two undefined instruction handler lists, one for userspace, one for * the kernel. This allows us to handle instructions that will trap */ LIST_HEAD(, undef_handler) undef_handlers[2]; /* * Work around a bug in QEMU prior to 2.5.1 where reading unknown ID * registers would raise an exception when they should return 0. */ static int id_aa64mmfr2_handler(vm_offset_t va, uint32_t insn, struct trapframe *frame, uint32_t esr) { int reg; #define MRS_ID_AA64MMFR2_EL0_MASK (MRS_MASK | 0x000fffe0) #define MRS_ID_AA64MMFR2_EL0_VALUE (MRS_VALUE | 0x00080740) /* mrs xn, id_aa64mfr2_el1 */ if ((insn & MRS_ID_AA64MMFR2_EL0_MASK) == MRS_ID_AA64MMFR2_EL0_VALUE) { reg = MRS_REGISTER(insn); frame->tf_elr += INSN_SIZE; if (reg < nitems(frame->tf_x)) { frame->tf_x[reg] = 0; } else if (reg == 30) { frame->tf_lr = 0; } /* If reg is 32 then write to xzr, i.e. do nothing */ return (1); } return (0); } +static bool +arm_cond_match(uint32_t insn, struct trapframe *frame) +{ + uint64_t spsr; + uint32_t cond; + bool invert; + bool match; + + /* + * Generally based on the function of the same name in NetBSD, though + * condition bits left in their original position rather than shifting + * over the low bit that indicates inversion for quicker sanity checking + * against spec. + */ + spsr = frame->tf_spsr; + cond = INSN_COND(insn); + invert = INSN_COND_INVERTED(insn); + + switch (cond) { + case INSN_COND_EQ: + match = (spsr & PSR_Z) != 0; + break; + case INSN_COND_CS: + match = (spsr & PSR_C) != 0; + break; + case INSN_COND_MI: + match = (spsr & PSR_N) != 0; + break; + case INSN_COND_VS: + match = (spsr & PSR_V) != 0; + break; + case INSN_COND_HI: + match = (spsr & (PSR_C | PSR_Z)) == PSR_C; + break; + case INSN_COND_GE: + match = (!(spsr & PSR_N) == !(spsr & PSR_V)); + break; + case INSN_COND_GT: + match = !(spsr & PSR_Z) && (!(spsr & PSR_N) == !(spsr & PSR_V)); + break; + case INSN_COND_AL: + match = true; + break; + } + + return (!match != !invert); +} + #ifdef COMPAT_FREEBSD32 /* arm32 GDB breakpoints */ #define GDB_BREAKPOINT 0xe6000011 #define GDB5_BREAKPOINT 0xe7ffdefe static int gdb_trapper(vm_offset_t va, uint32_t insn, struct trapframe *frame, uint32_t esr) { struct thread *td = curthread; if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) { if (SV_PROC_FLAG(td->td_proc, SV_ILP32) && va < VM_MAXUSER_ADDRESS) { ksiginfo_t ksi; ksiginfo_init_trap(&ksi); ksi.ksi_signo = SIGTRAP; ksi.ksi_code = TRAP_BRKPT; ksi.ksi_addr = (void *)va; trapsignal(td, &ksi); return 1; } } return 0; } + +static int +swp_emulate(vm_offset_t va, uint32_t insn, struct trapframe *frame, + uint32_t esr) +{ + ksiginfo_t ksi; + struct thread *td; + vm_offset_t vaddr; + uint64_t *regs; + uint32_t val; + int attempts, error, Rn, Rd, Rm; + bool is_swpb; + + td = curthread; + + /* + * swp, swpb only; there are no Thumb swp/swpb instructions so we can + * safely bail out if we're in Thumb mode. + */ + if (!compat32_emul_swp || !SV_PROC_FLAG(td->td_proc, SV_ILP32) || + (frame->tf_spsr & PSR_T) != 0) + return (0); + else if ((insn & 0x0fb00ff0) != 0x01000090) + return (0); + else if (!arm_cond_match(insn, frame)) + goto next; /* Handled, but does nothing */ + + Rn = (insn & 0xf0000) >> 16; + Rd = (insn & 0xf000) >> 12; + Rm = (insn & 0xf); + + regs = frame->tf_x; + vaddr = regs[Rn] & 0xffffffff; + val = regs[Rm]; + + /* Enforce alignment for swp. */ + is_swpb = (insn & 0x00400000) != 0; + if (!is_swpb && (vaddr & 3) != 0) + goto fault; + + attempts = 0; + + do { + if (is_swpb) { + uint8_t bval; + + bval = val; + error = swapueword8((void *)vaddr, &bval); + val = bval; + } else { + error = swapueword32((void *)vaddr, &val); + } + + if (error == -1) + goto fault; + + /* + * Avoid potential DoS, e.g., on CPUs that don't implement + * global monitors. + */ + if (error != 0 && (++attempts % 5) == 0) + maybe_yield(); + } while (error != 0); + + regs[Rd] = val; + +next: + /* No thumb SWP/SWPB */ + frame->tf_elr += 4; //INSN_SIZE; + + return (1); +fault: + ksiginfo_init_trap(&ksi); + ksi.ksi_signo = SIGSEGV; + ksi.ksi_code = SEGV_MAPERR; + ksi.ksi_addr = (void *)va; + trapsignal(td, &ksi); + + return (1); +} #endif void undef_init(void) { LIST_INIT(&undef_handlers[0]); LIST_INIT(&undef_handlers[1]); install_undef_handler(false, id_aa64mmfr2_handler); #ifdef COMPAT_FREEBSD32 install_undef_handler(true, gdb_trapper); + install_undef_handler(true, swp_emulate); #endif } void * install_undef_handler(bool user, undef_handler_t func) { struct undef_handler *uh; uh = malloc(sizeof(*uh), M_UNDEF, M_WAITOK); uh->uh_handler = func; LIST_INSERT_HEAD(&undef_handlers[user ? 0 : 1], uh, uh_link); return (uh); } void remove_undef_handler(void *handle) { struct undef_handler *uh; uh = handle; LIST_REMOVE(uh, uh_link); free(handle, M_UNDEF); } int undef_insn(u_int el, struct trapframe *frame) { struct undef_handler *uh; uint32_t insn; int ret; KASSERT(el < 2, ("Invalid exception level %u", el)); if (el == 0) { ret = fueword32((uint32_t *)frame->tf_elr, &insn); if (ret != 0) panic("Unable to read userspace faulting instruction"); } else { insn = *(uint32_t *)frame->tf_elr; } LIST_FOREACH(uh, &undef_handlers[el], uh_link) { ret = uh->uh_handler(frame->tf_elr, insn, frame, frame->tf_esr); if (ret) return (1); } return (0); } diff --git a/sys/conf/options.arm64 b/sys/conf/options.arm64 index 26c7c87e49e2..0d2a5f177754 100644 --- a/sys/conf/options.arm64 +++ b/sys/conf/options.arm64 @@ -1,38 +1,40 @@ # $FreeBSD$ ARM64 opt_global.h # for cpu ARM64 to work INTRNG opt_global.h SOCDEV_PA opt_global.h THUNDERX_PASS_1_1_ERRATA opt_global.h VFP opt_global.h LINUX_BOOT_ABI opt_global.h LSE_ATOMICS opt_global.h # Per-thread stack smashing protection support # Needs clang >= 13 PERTHREAD_SSP opt_global.h # Binary compatibility COMPAT_FREEBSD32 opt_global.h +# Emulate SWP/SWPB for COMPAT_FREEBSD32 +EMUL_SWP opt_global.h # EFI Runtime services support EFIRT opt_efirt.h # SoC Support SOC_ALLWINNER_A64 opt_soc.h SOC_ALLWINNER_H5 opt_soc.h SOC_ALLWINNER_H6 opt_soc.h SOC_BRCM_BCM2837 opt_soc.h SOC_BRCM_BCM2838 opt_soc.h SOC_BRCM_NS2 opt_soc.h SOC_CAVM_THUNDERX opt_soc.h SOC_FREESCALE_IMX8 opt_soc.h SOC_HISI_HI6220 opt_soc.h SOC_INTEL_STRATIX10 opt_soc.h SOC_MARVELL_8K opt_soc.h SOC_NVIDIA_TEGRA210 opt_soc.h SOC_NXP_LS opt_soc.h SOC_ROCKCHIP_RK3328 opt_soc.h SOC_ROCKCHIP_RK3399 opt_soc.h SOC_ROCKCHIP_RK3568 opt_soc.h SOC_XILINX_ZYNQ opt_soc.h