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 76UA_EVCNT_ATTACH(lwz); 77UA_EVCNT_ATTACH(lwzu); 78UA_EVCNT_ATTACH(stw); 79UA_EVCNT_ATTACH(stwu); 80UA_EVCNT_ATTACH(lhz); 81UA_EVCNT_ATTACH(lhzu); 82UA_EVCNT_ATTACH(lha); 83UA_EVCNT_ATTACH(lhau); 84UA_EVCNT_ATTACH(sth); 85UA_EVCNT_ATTACH(sthu); 86 87UA_EVCNT_ATTACH(lwzx); 88UA_EVCNT_ATTACH(lwzux); 89UA_EVCNT_ATTACH(stwx); 90UA_EVCNT_ATTACH(stwux); 91UA_EVCNT_ATTACH(lhzx); 92UA_EVCNT_ATTACH(lhzux); 93UA_EVCNT_ATTACH(lhax); 94UA_EVCNT_ATTACH(lhaux); 95UA_EVCNT_ATTACH(sthx); 96UA_EVCNT_ATTACH(sthux); 97UA_EVCNT_ATTACH(lwbrx); 98UA_EVCNT_ATTACH(stwbrx); 99UA_EVCNT_ATTACH(lhbrx); 100UA_EVCNT_ATTACH(sthbrx); 101 102UA_EVCNT_ATTACH(lmw); 103UA_EVCNT_ATTACH(stmw); 104 105UA_EVCNT_ATTACH(isi); 106UA_EVCNT_ATTACH(dsi); 107UA_EVCNT_ATTACH(unknown); 108UA_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) 116int unaligned_debug = 1; 117#elif defined(DEBUG) 118int 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)) 132extern 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 142static bool emul_unaligned(struct trapframe *, ksiginfo_t *, 143 const union instr *); 144static bool do_lst(struct trapframe *, const union instr *, int); 145#ifdef FIX_UNALIGNED_LSTMW 146static bool do_lstmw(struct trapframe *, const union instr *, int); 147#endif 148 149bool 150fix_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 184static bool 185emul_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 */ 376static bool 377do_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 454static bool 455do_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