1 /* $NetBSD: fix_unaligned.c,v 1.2 2022/06/02 00:32:14 rin Exp $ */ 2 3 /* 4 * Copyright (c) 2022 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Rin Okuyama. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Routines to fix unaligned memory access for userland process. 34 * 35 * Intended mainly for PPC_IBM403 at the moment: 36 * 37 * - Fetch and decode insn; 403 does not have DSISR. 38 * 39 * - Only for integer insn; unaligned floating-point load/store are taken 40 * care of by FPU emulator. (Support for FPU insn should be trivial.) 41 * 42 * Also note: 43 * 44 * - For invalid forms, behaviors are undefined and not documented in 45 * processor manuals. Here, we mimic what described in 46 * "AIX 7.2 Assembler language reference": 47 * 48 * - For "u" variants, ra is not updated if ra == 0 (or rd for load). 49 * 50 * - Fix for {l,st}mw is disabled by default. 51 */ 52 53 #include <sys/cdefs.h> 54 __KERNEL_RCSID(0, "$NetBSD: fix_unaligned.c,v 1.2 2022/06/02 00:32:14 rin Exp $"); 55 56 #include "opt_ddb.h" 57 #include "opt_ppcarch.h" 58 59 #include <sys/param.h> 60 #include <sys/types.h> 61 #include <sys/evcnt.h> 62 #include <sys/siginfo.h> 63 #include <sys/systm.h> 64 65 #include <powerpc/frame.h> 66 #include <powerpc/instr.h> 67 #include <powerpc/trap.h> 68 69 #define UA_EVCNT_ATTACH(name) \ 70 static struct evcnt unaligned_ev_##name = \ 71 EVCNT_INITIALIZER(EVCNT_TYPE_TRAP, NULL, "unaligned", #name); \ 72 EVCNT_ATTACH_STATIC(unaligned_ev_##name) 73 74 #define UA_EVCNT_INCR(name) unaligned_ev_##name.ev_count++ 75 76 UA_EVCNT_ATTACH(lwz); 77 UA_EVCNT_ATTACH(lwzu); 78 UA_EVCNT_ATTACH(stw); 79 UA_EVCNT_ATTACH(stwu); 80 UA_EVCNT_ATTACH(lhz); 81 UA_EVCNT_ATTACH(lhzu); 82 UA_EVCNT_ATTACH(lha); 83 UA_EVCNT_ATTACH(lhau); 84 UA_EVCNT_ATTACH(sth); 85 UA_EVCNT_ATTACH(sthu); 86 87 UA_EVCNT_ATTACH(lwzx); 88 UA_EVCNT_ATTACH(lwzux); 89 UA_EVCNT_ATTACH(stwx); 90 UA_EVCNT_ATTACH(stwux); 91 UA_EVCNT_ATTACH(lhzx); 92 UA_EVCNT_ATTACH(lhzux); 93 UA_EVCNT_ATTACH(lhax); 94 UA_EVCNT_ATTACH(lhaux); 95 UA_EVCNT_ATTACH(sthx); 96 UA_EVCNT_ATTACH(sthux); 97 UA_EVCNT_ATTACH(lwbrx); 98 UA_EVCNT_ATTACH(stwbrx); 99 UA_EVCNT_ATTACH(lhbrx); 100 UA_EVCNT_ATTACH(sthbrx); 101 102 UA_EVCNT_ATTACH(lmw); 103 UA_EVCNT_ATTACH(stmw); 104 105 UA_EVCNT_ATTACH(isi); 106 UA_EVCNT_ATTACH(dsi); 107 UA_EVCNT_ATTACH(unknown); 108 UA_EVCNT_ATTACH(invalid); 109 110 #if 0 111 #define UNALIGNED_DEBUG 1 112 #define FIX_UNALIGNED_LSTMW 1 113 #endif 114 115 #if defined(UNALIGNED_DEBUG) 116 int unaligned_debug = 1; 117 #elif defined(DEBUG) 118 int unaligned_debug = 0; 119 #endif 120 121 #if defined(UNALIGNED_DEBUG) || defined(DEBUG) 122 #define DPRINTF(fmt, args...) \ 123 do { \ 124 if (unaligned_debug) \ 125 printf("%s: " fmt, __func__, ##args); \ 126 } while (0) 127 #else 128 #define DPRINTF(fmt, args...) __nothing 129 #endif 130 131 #if defined(DDB) && (defined(UNALIGNED_DEBUG) || defined(DEBUG)) 132 extern vaddr_t opc_disasm(vaddr_t, int); 133 #define DISASM(tf, insn) \ 134 do { \ 135 if (unaligned_debug) \ 136 opc_disasm((tf)->tf_srr0, (insn)->i_int); \ 137 } while (0) 138 #else 139 #define DISASM(tf, insn) __nothing 140 #endif 141 142 static bool emul_unaligned(struct trapframe *, ksiginfo_t *, 143 const union instr *); 144 static bool do_lst(struct trapframe *, const union instr *, int); 145 #ifdef FIX_UNALIGNED_LSTMW 146 static bool do_lstmw(struct trapframe *, const union instr *, int); 147 #endif 148 149 bool 150 fix_unaligned(struct trapframe *tf, ksiginfo_t *ksi) 151 { 152 union instr insn; 153 int ret; 154 155 KSI_INIT_TRAP(ksi); 156 157 ret = ufetch_32((uint32_t *)tf->tf_srr0, (uint32_t *)&insn.i_int); 158 if (ret) { 159 UA_EVCNT_INCR(isi); 160 DPRINTF("EXC_ISI: ret: %d, srr0: 0x%08lx dear: 0x%08lx\n", 161 ret, tf->tf_srr0, tf->tf_dear); 162 ksi->ksi_signo = SIGSEGV; 163 ksi->ksi_trap = EXC_ISI; 164 ksi->ksi_code = SEGV_MAPERR; 165 ksi->ksi_addr = (void *)tf->tf_srr0; 166 return true; 167 } 168 169 if (emul_unaligned(tf, ksi, &insn)) 170 return true; 171 172 CTASSERT(sizeof(insn) == 4); /* It was broken before... */ 173 tf->tf_srr0 += sizeof(insn); 174 return false; 175 } 176 177 #define UAF_STORE 0 178 #define UAF_LOAD __BIT(0) 179 #define UAF_HALF __BIT(1) 180 #define UAF_ALGEBRA __BIT(2) 181 #define UAF_REVERSE __BIT(3) 182 #define UAF_UPDATE __BIT(4) 183 184 static bool 185 emul_unaligned(struct trapframe *tf, ksiginfo_t *ksi, const union instr *insn) 186 { 187 int flags; 188 189 switch (insn->i_any.i_opcd) { 190 case OPC_LWZ: 191 UA_EVCNT_INCR(lwz); 192 flags = UAF_LOAD; 193 break; 194 195 case OPC_LWZU: 196 UA_EVCNT_INCR(lwzu); 197 flags = UAF_LOAD | UAF_UPDATE; 198 break; 199 200 case OPC_STW: 201 UA_EVCNT_INCR(stw); 202 flags = UAF_STORE; 203 break; 204 205 case OPC_STWU: 206 UA_EVCNT_INCR(stwu); 207 flags = UAF_STORE | UAF_UPDATE; 208 break; 209 210 case OPC_LHZ: 211 UA_EVCNT_INCR(lhz); 212 flags = UAF_LOAD | UAF_HALF; 213 break; 214 215 case OPC_LHZU: 216 UA_EVCNT_INCR(lhzu); 217 flags = UAF_LOAD | UAF_HALF | UAF_UPDATE; 218 break; 219 220 case OPC_LHA: 221 UA_EVCNT_INCR(lha); 222 flags = UAF_LOAD | UAF_HALF | UAF_ALGEBRA; 223 break; 224 225 case OPC_LHAU: 226 UA_EVCNT_INCR(lhau); 227 flags = UAF_LOAD | UAF_HALF | UAF_ALGEBRA | UAF_UPDATE; 228 break; 229 230 case OPC_STH: 231 UA_EVCNT_INCR(sth); 232 flags = UAF_STORE | UAF_HALF; 233 break; 234 235 case OPC_STHU: 236 UA_EVCNT_INCR(sthu); 237 flags = UAF_STORE | UAF_HALF | UAF_UPDATE; 238 break; 239 240 case OPC_integer_31: 241 switch (insn->i_x.i_xo) { 242 case OPC31_LWZX: 243 UA_EVCNT_INCR(lwzx); 244 flags = UAF_LOAD; 245 break; 246 247 case OPC31_LWZUX: 248 UA_EVCNT_INCR(lwzux); 249 flags = UAF_LOAD | UAF_UPDATE; 250 break; 251 252 case OPC31_STWX: 253 UA_EVCNT_INCR(stwx); 254 flags = UAF_STORE; 255 break; 256 257 case OPC31_STWUX: 258 UA_EVCNT_INCR(stwux); 259 flags = UAF_STORE | UAF_UPDATE; 260 break; 261 262 case OPC31_LHZX: 263 UA_EVCNT_INCR(lhzx); 264 flags = UAF_LOAD | UAF_HALF; 265 break; 266 267 case OPC31_LHZUX: 268 UA_EVCNT_INCR(lhzux); 269 flags = UAF_LOAD | UAF_HALF | UAF_UPDATE; 270 break; 271 272 case OPC31_LHAX: 273 UA_EVCNT_INCR(lhax); 274 flags = UAF_LOAD | UAF_HALF | UAF_ALGEBRA; 275 break; 276 277 case OPC31_LHAUX: 278 UA_EVCNT_INCR(lhaux); 279 flags = UAF_LOAD | UAF_HALF | UAF_ALGEBRA | UAF_UPDATE; 280 break; 281 282 case OPC31_STHX: 283 UA_EVCNT_INCR(sthx); 284 flags = UAF_STORE | UAF_HALF; 285 break; 286 287 case OPC31_STHUX: 288 UA_EVCNT_INCR(sthux); 289 flags = UAF_STORE | UAF_HALF | UAF_UPDATE; 290 break; 291 292 case OPC31_LWBRX: 293 UA_EVCNT_INCR(lwbrx); 294 flags = UAF_LOAD | UAF_REVERSE; 295 break; 296 297 case OPC31_STWBRX: 298 UA_EVCNT_INCR(stwbrx); 299 flags = UAF_STORE | UAF_REVERSE; 300 break; 301 302 case OPC31_LHBRX: 303 UA_EVCNT_INCR(lhbrx); 304 flags = UAF_LOAD | UAF_HALF | UAF_REVERSE; 305 break; 306 307 case OPC31_STHBRX: 308 UA_EVCNT_INCR(sthbrx); 309 flags = UAF_STORE | UAF_HALF | UAF_REVERSE; 310 break; 311 312 default: 313 UA_EVCNT_INCR(unknown); 314 goto unknown; 315 } 316 break; 317 318 case OPC_LMW: 319 UA_EVCNT_INCR(lmw); 320 #ifdef FIX_UNALIGNED_LSTMW 321 flags = UAF_LOAD; 322 if (do_lstmw(tf, insn, flags)) 323 goto fault; 324 return false; 325 #else 326 goto unknown; 327 #endif 328 329 case OPC_STMW: 330 UA_EVCNT_INCR(stmw); 331 #ifdef FIX_UNALIGNED_LSTMW 332 flags = UAF_STORE; 333 if (do_lstmw(tf, insn, flags)) 334 goto fault; 335 return false; 336 #else 337 goto unknown; 338 #endif 339 340 default: 341 UA_EVCNT_INCR(unknown); 342 unknown: 343 DPRINTF("unknown: srr0: 0x%08lx dear: 0x%08lx " 344 "insn: 0x%08x (opcd: 0x%02x, xo: 0x%03x) ", 345 tf->tf_srr0, tf->tf_dear, 346 insn->i_int, insn->i_any.i_opcd, insn->i_x.i_xo); 347 DISASM(tf, insn); 348 ksi->ksi_signo = SIGBUS; 349 ksi->ksi_trap = EXC_ALI; 350 ksi->ksi_addr = (void *)tf->tf_dear; 351 return true; 352 } 353 354 if (do_lst(tf, insn, flags)) { 355 #ifdef FIX_UNALIGNED_LSTMW 356 fault: 357 #endif 358 UA_EVCNT_INCR(dsi); 359 ksi->ksi_signo = SIGSEGV; 360 ksi->ksi_trap = EXC_DSI; 361 ksi->ksi_code = SEGV_MAPERR; 362 ksi->ksi_addr = (void *)tf->tf_dear; 363 return true; 364 } 365 366 return false; 367 } 368 369 #define SIGN_EXT(u16, algebra) \ 370 ((u16) | (((algebra) && (u16) >= 0x8000) ? 0xffff0000 : 0)) 371 372 /* 373 * We support formats D and X, but don't care which; 374 * fault address is in dear. 375 */ 376 static bool 377 do_lst(struct trapframe *tf, const union instr *insn, int flags) 378 { 379 const bool load = flags & UAF_LOAD, 380 half = flags & UAF_HALF, 381 algebra = flags & UAF_ALGEBRA, 382 reverse = flags & UAF_REVERSE, 383 update = flags & UAF_UPDATE; 384 uint8_t * const dear = (uint8_t *)tf->tf_dear; 385 uint32_t u32; 386 uint16_t u16; 387 int rs, ra, ret; 388 389 rs = insn->i_d.i_rs; /* same as i_[dx].i_r[sd] */ 390 391 if (load) { 392 if (half) 393 ret = copyin(dear, &u16, sizeof(u16)); 394 else 395 ret = copyin(dear, &u32, sizeof(u32)); 396 } else { 397 if (half) { 398 u16 = (uint16_t)tf->tf_fixreg[rs]; 399 if (reverse) 400 u16 = bswap16(u16); 401 ret = copyout(&u16, dear, sizeof(u16)); 402 } else { 403 u32 = tf->tf_fixreg[rs]; 404 if (reverse) 405 u32 = bswap32(u32); 406 ret = copyout(&u32, dear, sizeof(u32)); 407 } 408 } 409 410 if (ret) 411 goto fault; 412 413 if (load) { 414 if (half) 415 tf->tf_fixreg[rs] = reverse ? 416 bswap16(u16) : SIGN_EXT(u16, algebra); 417 else 418 tf->tf_fixreg[rs] = reverse ? 419 bswap32(u32) : u32; 420 } 421 422 if (update) { 423 ra = insn->i_d.i_ra; /* same as i_x.i_ra */ 424 /* 425 * XXX 426 * ra == 0 (or ra == rd for load) is invalid (undefined). 427 * Mimic what AIX 7.2 describes. 428 */ 429 if (ra == 0 || (load && ra == rs)) { 430 UA_EVCNT_INCR(invalid); 431 DPRINTF("invalid: rs: %d ra: %d " 432 "srr0: 0x%08lx dear: 0x%08x " 433 "insn: 0x%08x (opcd: 0x%02x xo: 0x%03x) ", 434 rs, ra, tf->tf_srr0, (uint32_t)dear, 435 insn->i_int, insn->i_any.i_opcd, insn->i_x.i_xo); 436 DISASM(tf, insn); 437 /* XXX discard */ 438 } else 439 tf->tf_fixreg[ra] = (__register_t)dear; 440 } 441 442 return false; 443 444 fault: 445 DPRINTF("fault: ret: %d srr0: 0x%08lx dear: 0x%08x " 446 "insn: 0x%08x (opcd: 0x%02x xo: 0x%03x) ", 447 ret, tf->tf_srr0, (uint32_t)dear, 448 insn->i_int, insn->i_any.i_opcd, insn->i_x.i_xo); 449 DISASM(tf, insn); 450 return true; 451 } 452 453 #ifdef FIX_UNALIGNED_LSTMW 454 static bool 455 do_lstmw(struct trapframe *tf, const union instr *insn, int flags) 456 { 457 const bool load = flags & UAF_LOAD; 458 const size_t size = sizeof(tf->tf_fixreg[0]); 459 uint8_t *ea; 460 uint32_t u32; 461 int rs, ra, r, ret; 462 463 /* 464 * XXX 465 * Can we always assume ea == tf->tf_dear? (True for 403 although...) 466 */ 467 rs = insn->i_d.i_rs; 468 ra = insn->i_d.i_ra; 469 ea = (uint8_t *)(insn->i_d.i_d + (ra ? tf->tf_fixreg[ra] : 0)); 470 471 for (r = rs; r < 32; r++) { 472 if (load) 473 ret = copyin(ea, &u32, size); 474 else 475 ret = copyout(&tf->tf_fixreg[r], ea, size); 476 477 if (ret) 478 goto fault; 479 480 if (load) { 481 /* 482 * XXX 483 * r == ra is invalid (undefined); Mimic what 484 * AIX 7.2 describes for POWER processors. 485 */ 486 if (r == ra) { 487 UA_EVCNT_INCR(invalid); 488 DPRINTF("invalid: rs: %d ra: %d r: %d " 489 "srr0: 0x%08lx dear: 0x%08lx (ea: 0x%08x) " 490 "insn: 0x%08x (opcd: 0x%02x xo: 0x%03x) ", 491 rs, ra, r, 492 tf->tf_srr0, tf->tf_dear, (uint32_t)ea, 493 insn->i_int, insn->i_any.i_opcd, 494 insn->i_x.i_xo); 495 DISASM(tf, insn); 496 if (r == 0) { 497 /* XXX load anyway */ 498 tf->tf_fixreg[r] = u32; 499 } else { 500 /* XXX discard */ 501 } 502 } else 503 tf->tf_fixreg[r] = u32; 504 } 505 506 ea += size; 507 } 508 509 return false; 510 511 fault: 512 DPRINTF("fault: ret: %d rs: %d r: %d " 513 "srr0: 0x%08lx dear: 0x%08lx (ea: 0x%08x) " 514 "insn: 0x%08x (opcd: 0x%02x xo: 0x%03x) ", 515 ret, rs, r, tf->tf_srr0, tf->tf_dear, (uint32_t)ea, 516 insn->i_int, insn->i_any.i_opcd, insn->i_x.i_xo); 517 DISASM(tf, insn); 518 return true; 519 } 520 #endif /* FIX_UNALIGNED_LSTMW */ 521