Module Name: src Committed By: rin Date: Mon May 30 13:58:51 UTC 2022
Modified Files: src/sys/arch/powerpc/include: instr.h Added Files: src/sys/arch/powerpc/powerpc: fix_unaligned.c Log Message: Add routines to fix unaligned memory access for userland process. Mainly intended for 403, which cannot handle unaligned memory access at all (not only ones across page boundaries like 601). For more details, see comments in fix_unaligned.c. To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 src/sys/arch/powerpc/include/instr.h cvs rdiff -u -r0 -r1.1 src/sys/arch/powerpc/powerpc/fix_unaligned.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/powerpc/include/instr.h diff -u src/sys/arch/powerpc/include/instr.h:1.10 src/sys/arch/powerpc/include/instr.h:1.11 --- src/sys/arch/powerpc/include/instr.h:1.10 Sun May 29 11:55:05 2022 +++ src/sys/arch/powerpc/include/instr.h Mon May 30 13:58:51 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: instr.h,v 1.10 2022/05/29 11:55:05 rin Exp $ */ +/* $NetBSD: instr.h,v 1.11 2022/05/30 13:58:51 rin Exp $ */ /* * Copyright (c) 1992, 1993 @@ -318,6 +318,24 @@ union instr { #define OPC31_OR 0x1bc /* + * Opcode 31 sub-types (load/store multiple bytes) + */ +#define OPC31_LWZX 0x017 +#define OPC31_LWZUX 0x037 +#define OPC31_STWX 0x097 +#define OPC31_STWUX 0x0b7 +#define OPC31_LHZX 0x117 +#define OPC31_LHZUX 0x137 +#define OPC31_LHAX 0x157 +#define OPC31_LHAUX 0x177 +#define OPC31_STHX 0x197 +#define OPC31_STHUX 0x1b7 +#define OPC31_LWBRX 0x216 +#define OPC31_STWBRX 0x296 +#define OPC31_LHBRX 0x316 +#define OPC31_STHBRX 0x396 + +/* * Opcode 59 sub-types: */ Added files: Index: src/sys/arch/powerpc/powerpc/fix_unaligned.c diff -u /dev/null src/sys/arch/powerpc/powerpc/fix_unaligned.c:1.1 --- /dev/null Mon May 30 13:58:51 2022 +++ src/sys/arch/powerpc/powerpc/fix_unaligned.c Mon May 30 13:58:51 2022 @@ -0,0 +1,520 @@ +/* $NetBSD: fix_unaligned.c,v 1.1 2022/05/30 13:58:51 rin Exp $ */ + +/* + * Copyright (c) 2022 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Rin Okuyama. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Routines to fix unaligned memory access for userland process. + * + * Intended mainly for PPC_IBM403 at the moment: + * + * - Fetch and decode insn; 403 does not have DSISR. + * + * - Only for integer insn; unaligned floating-point load/store are taken + * care of by FPU emulator. (Support for FPU insn should be trivial.) + * + * Also note: + * + * - For invalid forms, behaviors are undefined and not documented in + * processor manuals. Here, we mimic what described in + * "AIX 7.2 Assembler language reference": + * + * - For "u" variants, ra is not updated if ra == 0 (or rd for load). + * + * - Fix for {l,st}mw is disabled by default. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: fix_unaligned.c,v 1.1 2022/05/30 13:58:51 rin Exp $"); + +#include "opt_ddb.h" +#include "opt_ppcarch.h" + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/evcnt.h> +#include <sys/siginfo.h> +#include <sys/systm.h> + +#include <powerpc/frame.h> +#include <powerpc/instr.h> +#include <powerpc/trap.h> + +#define UA_EVCNT_ATTACH(name) \ + static struct evcnt unaligned_ev_##name = \ + EVCNT_INITIALIZER(EVCNT_TYPE_TRAP, NULL, "unaligned", #name); \ + EVCNT_ATTACH_STATIC(unaligned_ev_##name) + +#define UA_EVCNT_INCR(name) unaligned_ev_##name.ev_count++ + +UA_EVCNT_ATTACH(lwz); +UA_EVCNT_ATTACH(lwzu); +UA_EVCNT_ATTACH(stw); +UA_EVCNT_ATTACH(stwu); +UA_EVCNT_ATTACH(lhz); +UA_EVCNT_ATTACH(lhzu); +UA_EVCNT_ATTACH(lha); +UA_EVCNT_ATTACH(lhau); +UA_EVCNT_ATTACH(sth); +UA_EVCNT_ATTACH(sthu); + +UA_EVCNT_ATTACH(lwzx); +UA_EVCNT_ATTACH(lwzux); +UA_EVCNT_ATTACH(stwx); +UA_EVCNT_ATTACH(stwux); +UA_EVCNT_ATTACH(lhzx); +UA_EVCNT_ATTACH(lhzux); +UA_EVCNT_ATTACH(lhax); +UA_EVCNT_ATTACH(lhaux); +UA_EVCNT_ATTACH(sthx); +UA_EVCNT_ATTACH(sthux); +UA_EVCNT_ATTACH(lwbrx); +UA_EVCNT_ATTACH(stwbrx); +UA_EVCNT_ATTACH(lhbrx); +UA_EVCNT_ATTACH(sthbrx); + +UA_EVCNT_ATTACH(lmw); +UA_EVCNT_ATTACH(stmw); + +UA_EVCNT_ATTACH(isi); +UA_EVCNT_ATTACH(dsi); +UA_EVCNT_ATTACH(unknown); +UA_EVCNT_ATTACH(invalid); + +#if 0 +#define UNALIGNED_DEBUG 1 +#define FIX_UNALIGNED_LSTMW 1 +#endif + +#if defined(UNALIGNED_DEBUG) +int unaligned_debug = 1; +#elif defined(DEBUG) +int unaligned_debug = 0; +#endif + +#if defined(UNALIGNED_DEBUG) || defined(DEBUG) +#define DPRINTF(fmt, args...) \ + do { \ + if (unaligned_debug) \ + printf("%s: " fmt, __func__, ##args); \ + } while (0) +#else +#define DPRINTF(fmt, args...) __nothing +#endif + +#if defined(DDB) && (defined(UNALIGNED_DEBUG) || defined(DEBUG)) +extern vaddr_t opc_disasm(vaddr_t, int); +#define DISASM(tf, insn) \ + do { \ + if (unaligned_debug) \ + opc_disasm((tf)->tf_srr0, (insn)->i_int); \ + } while (0) +#else +#define DISASM(tf, insn) __nothing +#endif + +static bool emul_unaligned(struct trapframe *, ksiginfo_t *, + const union instr *); +static bool do_lst(struct trapframe *, const union instr *, int); +#ifdef FIX_UNALIGNED_LSTMW +static bool do_lstmw(struct trapframe *, const union instr *, int); +#endif + +bool +fix_unaligned(struct trapframe *tf, ksiginfo_t *ksi) +{ + union instr insn; + int ret; + + KSI_INIT_TRAP(ksi); + + ret = ufetch_32((uint32_t *)tf->tf_srr0, (uint32_t *)&insn.i_int); + if (ret) { + UA_EVCNT_INCR(isi); + DPRINTF("EXC_ISI: ret: %d, srr0: 0x%08lx dear: 0x%08lx\n", + ret, tf->tf_srr0, tf->tf_dear); + ksi->ksi_signo = SIGSEGV; + ksi->ksi_trap = EXC_ISI; + ksi->ksi_code = SEGV_MAPERR; + ksi->ksi_addr = (void *)tf->tf_srr0; + return true; + } + + if (emul_unaligned(tf, ksi, &insn)) + return true; + + CTASSERT(sizeof(insn) == 4); /* It was broken before... */ + tf->tf_srr0 += sizeof(insn); + return false; +} + +#define UAF_STORE 0 +#define UAF_LOAD __BIT(0) +#define UAF_HALF __BIT(1) +#define UAF_ALGEBRA __BIT(2) +#define UAF_REVERSE __BIT(3) +#define UAF_UPDATE __BIT(4) + +static bool +emul_unaligned(struct trapframe *tf, ksiginfo_t *ksi, const union instr *insn) +{ + int flags; + + switch (insn->i_any.i_opcd) { + case OPC_LMW: + UA_EVCNT_INCR(lmw); +#ifdef FIX_UNALIGNED_LSTMW + flags = UAF_LOAD; + if (do_lstmw(tf, insn, flags)) + goto fault; + return false; +#else + goto unknown; +#endif + + case OPC_STMW: + UA_EVCNT_INCR(stmw); +#ifdef FIX_UNALIGNED_LSTMW + flags = UAF_STORE; + if (do_lstmw(tf, insn, flags)) + goto fault; + return false; +#else + goto unknown; +#endif + + case OPC_integer_31: + switch (insn->i_x.i_xo) { + case OPC31_LWZX: + UA_EVCNT_INCR(lwzx); + flags = UAF_LOAD; + break; + + case OPC31_LWZUX: + UA_EVCNT_INCR(lwzux); + flags = UAF_LOAD | UAF_UPDATE; + break; + + case OPC31_STWX: + UA_EVCNT_INCR(stwx); + flags = UAF_STORE; + break; + + case OPC31_STWUX: + UA_EVCNT_INCR(stwux); + flags = UAF_STORE | UAF_UPDATE; + break; + + case OPC31_LHZX: + UA_EVCNT_INCR(lhzx); + flags = UAF_LOAD | UAF_HALF; + break; + + case OPC31_LHZUX: + UA_EVCNT_INCR(lhzux); + flags = UAF_LOAD | UAF_HALF | UAF_UPDATE; + break; + + case OPC31_LHAX: + UA_EVCNT_INCR(lhax); + flags = UAF_LOAD | UAF_HALF | UAF_ALGEBRA; + break; + + case OPC31_LHAUX: + UA_EVCNT_INCR(lhaux); + flags = UAF_LOAD | UAF_HALF | UAF_ALGEBRA | UAF_UPDATE; + break; + + case OPC31_STHX: + UA_EVCNT_INCR(sthx); + flags = UAF_STORE | UAF_HALF; + break; + + case OPC31_STHUX: + UA_EVCNT_INCR(sthux); + flags = UAF_STORE | UAF_HALF | UAF_UPDATE; + break; + + case OPC31_LWBRX: + UA_EVCNT_INCR(lwbrx); + flags = UAF_LOAD | UAF_REVERSE; + break; + + case OPC31_STWBRX: + UA_EVCNT_INCR(stwbrx); + flags = UAF_STORE | UAF_REVERSE; + break; + + case OPC31_LHBRX: + UA_EVCNT_INCR(lhbrx); + flags = UAF_LOAD | UAF_HALF | UAF_REVERSE; + break; + + case OPC31_STHBRX: + UA_EVCNT_INCR(sthbrx); + flags = UAF_STORE | UAF_HALF | UAF_REVERSE; + break; + + default: + UA_EVCNT_INCR(unknown); + goto unknown; + } + break; + + case OPC_LWZ: + UA_EVCNT_INCR(lwz); + flags = UAF_LOAD; + break; + + case OPC_LWZU: + UA_EVCNT_INCR(lwzu); + flags = UAF_LOAD | UAF_UPDATE; + break; + + case OPC_STW: + UA_EVCNT_INCR(stw); + flags = UAF_STORE; + break; + + case OPC_STWU: + UA_EVCNT_INCR(stwu); + flags = UAF_STORE | UAF_UPDATE; + break; + + case OPC_LHZ: + UA_EVCNT_INCR(lhz); + flags = UAF_LOAD | UAF_HALF; + break; + + case OPC_LHZU: + UA_EVCNT_INCR(lhzu); + flags = UAF_LOAD | UAF_HALF | UAF_UPDATE; + break; + + case OPC_LHA: + UA_EVCNT_INCR(lha); + flags = UAF_LOAD | UAF_HALF | UAF_ALGEBRA; + break; + + case OPC_LHAU: + UA_EVCNT_INCR(lhau); + flags = UAF_LOAD | UAF_HALF | UAF_ALGEBRA | UAF_UPDATE; + break; + + case OPC_STH: + UA_EVCNT_INCR(sth); + flags = UAF_STORE | UAF_HALF; + break; + + case OPC_STHU: + UA_EVCNT_INCR(sthu); + flags = UAF_STORE | UAF_HALF | UAF_UPDATE; + break; + + default: + UA_EVCNT_INCR(unknown); + unknown: + DPRINTF("unknown: srr0: 0x%08lx dear: 0x%08lx " + "insn: 0x%08x (opcd: 0x%02x, xo: 0x%03x) ", + tf->tf_srr0, tf->tf_dear, + insn->i_int, insn->i_any.i_opcd, insn->i_x.i_xo); + DISASM(tf, insn); + ksi->ksi_signo = SIGBUS; + ksi->ksi_trap = EXC_ALI; + ksi->ksi_addr = (void *)tf->tf_dear; + return true; + } + + if (do_lst(tf, insn, flags)) { +#ifdef FIX_UNALIGNED_LSTMW + fault: +#endif + UA_EVCNT_INCR(dsi); + ksi->ksi_signo = SIGSEGV; + ksi->ksi_trap = EXC_DSI; + ksi->ksi_code = SEGV_MAPERR; + ksi->ksi_addr = (void *)tf->tf_dear; + return true; + } + + return false; +} + +#define SIGN_EXT(u16, algebra) \ + ((u16) | (((algebra) && (u16) >= 0x8000) ? 0xffff0000 : 0)) + +/* + * We support formats D and X, but don't care which; + * fault address is in dear. + */ +static bool +do_lst(struct trapframe *tf, const union instr *insn, int flags) +{ + const bool load = flags & UAF_LOAD, + half = flags & UAF_HALF, + algebra = flags & UAF_ALGEBRA, + reverse = flags & UAF_REVERSE, + update = flags & UAF_UPDATE; + uint8_t * const dear = (uint8_t *)tf->tf_dear; + uint32_t u32; + uint16_t u16; + int rs, ra, ret; + + rs = insn->i_d.i_rs; /* same as i_[dx].i_r[sd] */ + + if (load) { + if (half) + ret = copyin(dear, &u16, sizeof(u16)); + else + ret = copyin(dear, &u32, sizeof(u32)); + } else { + if (half) { + u16 = (uint16_t)tf->tf_fixreg[rs]; + if (reverse) + u16 = bswap16(u16); + ret = copyout(&u16, dear, sizeof(u16)); + } else { + u32 = tf->tf_fixreg[rs]; + if (reverse) + u32 = bswap32(u32); + ret = copyout(&u32, dear, sizeof(u32)); + } + } + + if (ret) + goto fault; + + if (load) { + if (half) + tf->tf_fixreg[rs] = reverse ? + bswap16(u16) : SIGN_EXT(u16, algebra); + else + tf->tf_fixreg[rs] = reverse ? + bswap32(u32) : u32; + } + + if (update) { + ra = insn->i_d.i_ra; /* same as i_x.i_ra */ + /* + * XXX + * ra == 0 (or ra == rd for load) is invalid (undefined). + * Mimic what AIX 7.2 describes. + */ + if (ra == 0 || (load && ra == rs)) { + UA_EVCNT_INCR(invalid); + DPRINTF("invalid: rs: %d ra: %d " + "srr0: 0x%08lx dear: 0x%08x " + "insn: 0x%08x (opcd: 0x%02x xo: 0x%03x) ", + rs, ra, tf->tf_srr0, (uint32_t)dear, + insn->i_int, insn->i_any.i_opcd, insn->i_x.i_xo); + DISASM(tf, insn); + /* XXX discard */ + } else + tf->tf_fixreg[ra] = (__register_t)dear; + } + + return false; + + fault: + DPRINTF("fault: ret: %d srr0: 0x%08lx dear: 0x%08x " + "insn: 0x%08x (opcd: 0x%02x xo: 0x%03x) ", + ret, tf->tf_srr0, (uint32_t)dear, + insn->i_int, insn->i_any.i_opcd, insn->i_x.i_xo); + DISASM(tf, insn); + return true; +} + +#ifdef FIX_UNALIGNED_LSTMW +static bool +do_lstmw(struct trapframe *tf, const union instr *insn, int flags) +{ + const bool load = flags & UAF_LOAD; + const size_t size = sizeof(tf->tf_fixreg[0]); + uint8_t *ea; + uint32_t u32; + int rs, ra, r, ret; + + /* + * XXX + * Can we always assume ea == tf->tf_dear? (True for 403 although...) + */ + rs = insn->i_d.i_rs; + ra = insn->i_d.i_ra; + ea = (uint8_t *)(insn->i_d.i_d + (ra ? tf->tf_fixreg[ra] : 0)); + + for (r = rs; r < 32; r++) { + if (load) + ret = copyin(ea, &u32, size); + else + ret = copyout(&tf->tf_fixreg[r], ea, size); + + if (ret) + goto fault; + + if (load) { + /* + * XXX + * r == ra is invalid (undefined); Mimic what + * AIX 7.2 describes for POWER processors. + */ + if (r == ra) { + UA_EVCNT_INCR(invalid); + DPRINTF("invalid: rs: %d ra: %d r: %d " + "srr0: 0x%08lx dear: 0x%08lx (ea: 0x%08x) " + "insn: 0x%08x (opcd: 0x%02x xo: 0x%03x) ", + rs, ra, r, + tf->tf_srr0, tf->tf_dear, (uint32_t)ea, + insn->i_int, insn->i_any.i_opcd, + insn->i_x.i_xo); + DISASM(tf, insn); + if (r == 0) { + /* XXX load anyway */ + tf->tf_fixreg[r] = u32; + } else { + /* XXX discard */ + } + } else + tf->tf_fixreg[r] = u32; + } + + ea += size; + } + + return false; + + fault: + DPRINTF("fault: ret: %d rs: %d r: %d " + "srr0: 0x%08lx dear: 0x%08lx (ea: 0x%08x) " + "insn: 0x%08x (opcd: 0x%02x xo: 0x%03x) ", + ret, rs, r, tf->tf_srr0, tf->tf_dear, (uint32_t)ea, + insn->i_int, insn->i_any.i_opcd, insn->i_x.i_xo); + DISASM(tf, insn); + return true; +} +#endif /* FIX_UNALIGNED_LSTMW */