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