1 /*- 2 * Copyright (c) 2017 Andrew Turner 3 * All rights reserved. 4 * 5 * This software was developed by SRI International and the University of 6 * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 7 * ("CTSRD"), as part of the DARPA CRASH research programme. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/malloc.h> 36 #include <sys/proc.h> 37 #include <sys/queue.h> 38 #include <sys/signal.h> 39 #include <sys/signalvar.h> 40 #include <sys/sysctl.h> 41 #include <sys/sysent.h> 42 43 #include <machine/atomic.h> 44 #include <machine/frame.h> 45 #define _MD_WANT_SWAPWORD 46 #include <machine/md_var.h> 47 #include <machine/pcb.h> 48 #include <machine/undefined.h> 49 #include <machine/vmparam.h> 50 51 #include <vm/vm.h> 52 #include <vm/vm_extern.h> 53 54 /* Low bit masked off */ 55 #define INSN_COND(insn) ((insn >> 28) & ~0x1) 56 #define INSN_COND_INVERTED(insn) ((insn >> 28) & 0x1) 57 #define INSN_COND_EQ 0x00 /* NE */ 58 #define INSN_COND_CS 0x02 /* CC */ 59 #define INSN_COND_MI 0x04 /* PL */ 60 #define INSN_COND_VS 0x06 /* VC */ 61 #define INSN_COND_HI 0x08 /* LS */ 62 #define INSN_COND_GE 0x0a /* LT */ 63 #define INSN_COND_GT 0x0c /* LE */ 64 #define INSN_COND_AL 0x0e /* Always */ 65 66 MALLOC_DEFINE(M_UNDEF, "undefhandler", "Undefined instruction handler data"); 67 68 #ifdef COMPAT_FREEBSD32 69 #ifndef EMUL_SWP 70 #define EMUL_SWP 0 71 #endif 72 73 SYSCTL_DECL(_compat_arm); 74 75 static bool compat32_emul_swp = EMUL_SWP; 76 SYSCTL_BOOL(_compat_arm, OID_AUTO, emul_swp, 77 CTLFLAG_RWTUN | CTLFLAG_MPSAFE, &compat32_emul_swp, 0, 78 "Enable SWP/SWPB emulation"); 79 #endif 80 81 struct undef_handler { 82 LIST_ENTRY(undef_handler) uh_link; 83 undef_handler_t uh_handler; 84 }; 85 86 /* 87 * Create two undefined instruction handler lists, one for userspace, one for 88 * the kernel. This allows us to handle instructions that will trap 89 */ 90 LIST_HEAD(, undef_handler) undef_handlers[2]; 91 92 /* 93 * Work around a bug in QEMU prior to 2.5.1 where reading unknown ID 94 * registers would raise an exception when they should return 0. 95 */ 96 static int 97 id_aa64mmfr2_handler(vm_offset_t va, uint32_t insn, struct trapframe *frame, 98 uint32_t esr) 99 { 100 int reg; 101 102 #define MRS_ID_AA64MMFR2_EL0_MASK (MRS_MASK | 0x000fffe0) 103 #define MRS_ID_AA64MMFR2_EL0_VALUE (MRS_VALUE | 0x00080740) 104 105 /* mrs xn, id_aa64mfr2_el1 */ 106 if ((insn & MRS_ID_AA64MMFR2_EL0_MASK) == MRS_ID_AA64MMFR2_EL0_VALUE) { 107 reg = MRS_REGISTER(insn); 108 109 frame->tf_elr += INSN_SIZE; 110 if (reg < nitems(frame->tf_x)) { 111 frame->tf_x[reg] = 0; 112 } else if (reg == 30) { 113 frame->tf_lr = 0; 114 } 115 /* If reg is 32 then write to xzr, i.e. do nothing */ 116 117 return (1); 118 } 119 return (0); 120 } 121 122 static bool 123 arm_cond_match(uint32_t insn, struct trapframe *frame) 124 { 125 uint64_t spsr; 126 uint32_t cond; 127 bool invert; 128 bool match; 129 130 /* 131 * Generally based on the function of the same name in NetBSD, though 132 * condition bits left in their original position rather than shifting 133 * over the low bit that indicates inversion for quicker sanity checking 134 * against spec. 135 */ 136 spsr = frame->tf_spsr; 137 cond = INSN_COND(insn); 138 invert = INSN_COND_INVERTED(insn); 139 140 switch (cond) { 141 case INSN_COND_EQ: 142 match = (spsr & PSR_Z) != 0; 143 break; 144 case INSN_COND_CS: 145 match = (spsr & PSR_C) != 0; 146 break; 147 case INSN_COND_MI: 148 match = (spsr & PSR_N) != 0; 149 break; 150 case INSN_COND_VS: 151 match = (spsr & PSR_V) != 0; 152 break; 153 case INSN_COND_HI: 154 match = (spsr & (PSR_C | PSR_Z)) == PSR_C; 155 break; 156 case INSN_COND_GE: 157 match = (!(spsr & PSR_N) == !(spsr & PSR_V)); 158 break; 159 case INSN_COND_GT: 160 match = !(spsr & PSR_Z) && (!(spsr & PSR_N) == !(spsr & PSR_V)); 161 break; 162 case INSN_COND_AL: 163 match = true; 164 break; 165 default: 166 __assert_unreachable(); 167 } 168 169 return (match != invert); 170 } 171 172 #ifdef COMPAT_FREEBSD32 173 /* arm32 GDB breakpoints */ 174 #define GDB_BREAKPOINT 0xe6000011 175 #define GDB5_BREAKPOINT 0xe7ffdefe 176 static int 177 gdb_trapper(vm_offset_t va, uint32_t insn, struct trapframe *frame, 178 uint32_t esr) 179 { 180 struct thread *td = curthread; 181 182 if (insn == GDB_BREAKPOINT || insn == GDB5_BREAKPOINT) { 183 if (SV_PROC_FLAG(td->td_proc, SV_ILP32) && 184 va < VM_MAXUSER_ADDRESS) { 185 ksiginfo_t ksi; 186 187 ksiginfo_init_trap(&ksi); 188 ksi.ksi_signo = SIGTRAP; 189 ksi.ksi_code = TRAP_BRKPT; 190 ksi.ksi_addr = (void *)va; 191 trapsignal(td, &ksi); 192 return 1; 193 } 194 } 195 return 0; 196 } 197 198 static int 199 swp_emulate(vm_offset_t va, uint32_t insn, struct trapframe *frame, 200 uint32_t esr) 201 { 202 ksiginfo_t ksi; 203 struct thread *td; 204 vm_offset_t vaddr; 205 uint64_t *regs; 206 uint32_t val; 207 int attempts, error, Rn, Rd, Rm; 208 bool is_swpb; 209 210 td = curthread; 211 212 /* 213 * swp, swpb only; there are no Thumb swp/swpb instructions so we can 214 * safely bail out if we're in Thumb mode. 215 */ 216 if (!compat32_emul_swp || !SV_PROC_FLAG(td->td_proc, SV_ILP32) || 217 (frame->tf_spsr & PSR_T) != 0) 218 return (0); 219 else if ((insn & 0x0fb00ff0) != 0x01000090) 220 return (0); 221 else if (!arm_cond_match(insn, frame)) 222 goto next; /* Handled, but does nothing */ 223 224 Rn = (insn & 0xf0000) >> 16; 225 Rd = (insn & 0xf000) >> 12; 226 Rm = (insn & 0xf); 227 228 regs = frame->tf_x; 229 vaddr = regs[Rn] & 0xffffffff; 230 val = regs[Rm]; 231 232 /* Enforce alignment for swp. */ 233 is_swpb = (insn & 0x00400000) != 0; 234 if (!is_swpb && (vaddr & 3) != 0) 235 goto fault; 236 237 attempts = 0; 238 239 do { 240 if (is_swpb) { 241 uint8_t bval; 242 243 bval = val; 244 error = swapueword8((void *)vaddr, &bval); 245 val = bval; 246 } else { 247 error = swapueword32((void *)vaddr, &val); 248 } 249 250 if (error == -1) 251 goto fault; 252 253 /* 254 * Avoid potential DoS, e.g., on CPUs that don't implement 255 * global monitors. 256 */ 257 if (error != 0 && (++attempts % 5) == 0) 258 maybe_yield(); 259 } while (error != 0); 260 261 regs[Rd] = val; 262 263 next: 264 /* No thumb SWP/SWPB */ 265 frame->tf_elr += 4; //INSN_SIZE; 266 267 return (1); 268 fault: 269 ksiginfo_init_trap(&ksi); 270 ksi.ksi_signo = SIGSEGV; 271 ksi.ksi_code = SEGV_MAPERR; 272 ksi.ksi_addr = (void *)va; 273 trapsignal(td, &ksi); 274 275 return (1); 276 } 277 #endif 278 279 void 280 undef_init(void) 281 { 282 283 LIST_INIT(&undef_handlers[0]); 284 LIST_INIT(&undef_handlers[1]); 285 286 install_undef_handler(false, id_aa64mmfr2_handler); 287 #ifdef COMPAT_FREEBSD32 288 install_undef_handler(true, gdb_trapper); 289 install_undef_handler(true, swp_emulate); 290 #endif 291 } 292 293 void * 294 install_undef_handler(bool user, undef_handler_t func) 295 { 296 struct undef_handler *uh; 297 298 uh = malloc(sizeof(*uh), M_UNDEF, M_WAITOK); 299 uh->uh_handler = func; 300 LIST_INSERT_HEAD(&undef_handlers[user ? 0 : 1], uh, uh_link); 301 302 return (uh); 303 } 304 305 void 306 remove_undef_handler(void *handle) 307 { 308 struct undef_handler *uh; 309 310 uh = handle; 311 LIST_REMOVE(uh, uh_link); 312 free(handle, M_UNDEF); 313 } 314 315 int 316 undef_insn(u_int el, struct trapframe *frame) 317 { 318 struct undef_handler *uh; 319 uint32_t insn; 320 int ret; 321 322 KASSERT(el < 2, ("Invalid exception level %u", el)); 323 324 if (el == 0) { 325 ret = fueword32((uint32_t *)frame->tf_elr, &insn); 326 if (ret != 0) 327 panic("Unable to read userspace faulting instruction"); 328 } else { 329 insn = *(uint32_t *)frame->tf_elr; 330 } 331 332 LIST_FOREACH(uh, &undef_handlers[el], uh_link) { 333 ret = uh->uh_handler(frame->tf_elr, insn, frame, frame->tf_esr); 334 if (ret) 335 return (1); 336 } 337 338 return (0); 339 } 340