185199Sdfr/*- 2121415Smarcel * Copyright (c) 2003 Marcel Moolenaar 385199Sdfr * Copyright (c) 2001 Doug Rabson 485199Sdfr * All rights reserved. 585199Sdfr * 685199Sdfr * Redistribution and use in source and binary forms, with or without 785199Sdfr * modification, are permitted provided that the following conditions 885199Sdfr * are met: 985199Sdfr * 1. Redistributions of source code must retain the above copyright 1085199Sdfr * notice, this list of conditions and the following disclaimer. 1185199Sdfr * 2. Redistributions in binary form must reproduce the above copyright 1285199Sdfr * notice, this list of conditions and the following disclaimer in the 1385199Sdfr * documentation and/or other materials provided with the distribution. 1485199Sdfr * 1585199Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1685199Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1785199Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1885199Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1985199Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2085199Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2185199Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2285199Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2385199Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2485199Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2585199Sdfr * SUCH DAMAGE. 2685199Sdfr * 2785199Sdfr * $FreeBSD: releng/10.2/sys/ia64/ia64/unaligned.c 219741 2011-03-18 15:36:28Z marcel $ 2885199Sdfr */ 2985199Sdfr 3085199Sdfr#include <sys/param.h> 3185199Sdfr#include <sys/systm.h> 3285199Sdfr#include <sys/kernel.h> 3385199Sdfr#include <sys/proc.h> 34115378Smarcel#include <sys/sysctl.h> 3585199Sdfr#include <vm/vm.h> 3685199Sdfr#include <vm/vm_extern.h> 3785199Sdfr#include <machine/frame.h> 38121415Smarcel#include <machine/md_var.h> 39121415Smarcel#include <ia64/disasm/disasm.h> 4085199Sdfr 41139554Smarcelstatic int ia64_unaligned_print = 0; 42139554SmarcelSYSCTL_INT(_debug, OID_AUTO, unaligned_print, CTLFLAG_RW, 43115378Smarcel &ia64_unaligned_print, 0, "warn about unaligned accesses"); 44115378Smarcel 45139554Smarcelstatic int ia64_unaligned_test = 0; 46139554SmarcelSYSCTL_INT(_debug, OID_AUTO, unaligned_test, CTLFLAG_RW, 47139554Smarcel &ia64_unaligned_test, 0, "test emulation when PSR.ac is set"); 48115378Smarcel 49121415Smarcelstatic void * 50121415Smarcelfpreg_ptr(mcontext_t *mc, int fr) 5185199Sdfr{ 52121415Smarcel union _ia64_fpreg *p; 5385199Sdfr 54121415Smarcel if (fr <= 1 || fr >= 128) 55121415Smarcel return (NULL); 56121415Smarcel if (fr >= 32) { 57121415Smarcel p = &mc->mc_high_fp.fr32; 58121415Smarcel fr -= 32; 59121415Smarcel } else if (fr >= 16) { 60121415Smarcel p = &mc->mc_preserved_fp.fr16; 61121415Smarcel fr -= 16; 62121415Smarcel } else if (fr >= 6) { 63121415Smarcel p = &mc->mc_scratch_fp.fr6; 64121415Smarcel fr -= 6; 65121415Smarcel } else { 66121415Smarcel p = &mc->mc_preserved_fp.fr2; 67121415Smarcel fr -= 2; 68121415Smarcel } 69121415Smarcel return ((void*)(p + fr)); 7085199Sdfr} 7185199Sdfr 72121415Smarcelstatic void * 73121415Smarcelgreg_ptr(mcontext_t *mc, int gr) 7485199Sdfr{ 75121415Smarcel uint64_t *p; 76133888Sarun int nslots; 7785199Sdfr 78121415Smarcel if (gr <= 0 || gr >= 32 + (mc->mc_special.cfm & 0x7f)) 79121415Smarcel return (NULL); 80121415Smarcel if (gr >= 32) { 81133888Sarun nslots = IA64_CFM_SOF(mc->mc_special.cfm) - gr + 32; 82133888Sarun p = (void *)ia64_bsp_adjust(mc->mc_special.bspstore, -nslots); 83121415Smarcel gr = 0; 84121415Smarcel } else if (gr >= 14) { 85121415Smarcel p = &mc->mc_scratch.gr14; 86121415Smarcel gr -= 14; 87121415Smarcel } else if (gr == 13) { 88121415Smarcel p = &mc->mc_special.tp; 89121415Smarcel gr = 0; 90121415Smarcel } else if (gr == 12) { 91121415Smarcel p = &mc->mc_special.sp; 92121415Smarcel gr = 0; 93121415Smarcel } else if (gr >= 8) { 94121415Smarcel p = &mc->mc_scratch.gr8; 95121415Smarcel gr -= 8; 96121415Smarcel } else if (gr >= 4) { 97121415Smarcel p = &mc->mc_preserved.gr4; 98121415Smarcel gr -= 4; 99121415Smarcel } else if (gr >= 2) { 100121415Smarcel p = &mc->mc_scratch.gr2; 101121415Smarcel gr -= 2; 102121415Smarcel } else { 103121415Smarcel p = &mc->mc_special.gp; 104121415Smarcel gr = 0; 105121415Smarcel } 106121415Smarcel return ((void*)(p + gr)); 10785199Sdfr} 10885199Sdfr 109121449Smarcelstatic uint64_t 110121449Smarcelrdreg(uint64_t *addr) 111121449Smarcel{ 112219741Smarcel if ((uintptr_t)addr < VM_MAXUSER_ADDRESS) 113121449Smarcel return (fuword(addr)); 114121449Smarcel return (*addr); 115121449Smarcel} 116121449Smarcel 117121449Smarcelstatic void 118121449Smarcelwrreg(uint64_t *addr, uint64_t val) 119121449Smarcel{ 120219741Smarcel if ((uintptr_t)addr < VM_MAXUSER_ADDRESS) 121121449Smarcel suword(addr, val); 122121449Smarcel else 123121449Smarcel *addr = val; 124121449Smarcel} 125121449Smarcel 12685199Sdfrstatic int 127121415Smarcelfixup(struct asm_inst *i, mcontext_t *mc, uint64_t va) 12885199Sdfr{ 129121415Smarcel union { 130121415Smarcel double d; 131139554Smarcel long double e; 132121415Smarcel uint64_t i; 133139554Smarcel float s; 134121415Smarcel } buf; 135121415Smarcel void *reg; 136121415Smarcel uint64_t postinc; 13785199Sdfr 138121415Smarcel switch (i->i_op) { 139122162Smarcel case ASM_OP_LD2: 140122162Smarcel copyin((void*)va, (void*)&buf.i, 2); 141122162Smarcel reg = greg_ptr(mc, (int)i->i_oper[1].o_value); 142122162Smarcel if (reg == NULL) 143122162Smarcel return (EINVAL); 144122162Smarcel wrreg(reg, buf.i & 0xffffU); 145122162Smarcel break; 146121933Smarcel case ASM_OP_LD4: 147121933Smarcel copyin((void*)va, (void*)&buf.i, 4); 148121933Smarcel reg = greg_ptr(mc, (int)i->i_oper[1].o_value); 149121933Smarcel if (reg == NULL) 150121933Smarcel return (EINVAL); 151121933Smarcel wrreg(reg, buf.i & 0xffffffffU); 152121933Smarcel break; 153121415Smarcel case ASM_OP_LD8: 154121415Smarcel copyin((void*)va, (void*)&buf.i, 8); 155121415Smarcel reg = greg_ptr(mc, (int)i->i_oper[1].o_value); 156121415Smarcel if (reg == NULL) 157115084Smarcel return (EINVAL); 158121449Smarcel wrreg(reg, buf.i); 159121415Smarcel break; 160121415Smarcel case ASM_OP_LDFD: 161139554Smarcel copyin((void*)va, (void*)&buf.d, sizeof(buf.d)); 162121415Smarcel reg = fpreg_ptr(mc, (int)i->i_oper[1].o_value); 163121415Smarcel if (reg == NULL) 164121415Smarcel return (EINVAL); 165139554Smarcel __asm("ldfd f6=%1;; stf.spill %0=f6" : "=m"(*(double *)reg) : 166139554Smarcel "m"(buf.d) : "f6"); 167121415Smarcel break; 168139554Smarcel case ASM_OP_LDFE: 169139554Smarcel copyin((void*)va, (void*)&buf.e, sizeof(buf.e)); 170139554Smarcel reg = fpreg_ptr(mc, (int)i->i_oper[1].o_value); 171139554Smarcel if (reg == NULL) 172139554Smarcel return (EINVAL); 173139554Smarcel __asm("ldfe f6=%1;; stf.spill %0=f6" : 174139554Smarcel "=m"(*(long double *)reg) : "m"(buf.e) : "f6"); 175139554Smarcel break; 176139554Smarcel case ASM_OP_LDFS: 177139554Smarcel copyin((void*)va, (void*)&buf.s, sizeof(buf.s)); 178139554Smarcel reg = fpreg_ptr(mc, (int)i->i_oper[1].o_value); 179139554Smarcel if (reg == NULL) 180139554Smarcel return (EINVAL); 181139554Smarcel __asm("ldfs f6=%1;; stf.spill %0=f6" : "=m"(*(float *)reg) : 182139554Smarcel "m"(buf.s) : "f6"); 183139554Smarcel break; 184122162Smarcel case ASM_OP_ST2: 185122162Smarcel reg = greg_ptr(mc, (int)i->i_oper[2].o_value); 186122162Smarcel if (reg == NULL) 187122162Smarcel return (EINVAL); 188122162Smarcel buf.i = rdreg(reg); 189122162Smarcel copyout((void*)&buf.i, (void*)va, 2); 190122162Smarcel break; 191122162Smarcel case ASM_OP_ST4: 192122162Smarcel reg = greg_ptr(mc, (int)i->i_oper[2].o_value); 193122162Smarcel if (reg == NULL) 194122162Smarcel return (EINVAL); 195122162Smarcel buf.i = rdreg(reg); 196122162Smarcel copyout((void*)&buf.i, (void*)va, 4); 197122162Smarcel break; 198122162Smarcel case ASM_OP_ST8: 199122162Smarcel reg = greg_ptr(mc, (int)i->i_oper[2].o_value); 200122162Smarcel if (reg == NULL) 201122162Smarcel return (EINVAL); 202122162Smarcel buf.i = rdreg(reg); 203122162Smarcel copyout((void*)&buf.i, (void*)va, 8); 204122162Smarcel break; 205139554Smarcel case ASM_OP_STFD: 206139554Smarcel reg = fpreg_ptr(mc, (int)i->i_oper[2].o_value); 207139554Smarcel if (reg == NULL) 208139554Smarcel return (EINVAL); 209139554Smarcel __asm("ldf.fill f6=%1;; stfd %0=f6" : "=m"(buf.d) : 210139554Smarcel "m"(*(double *)reg) : "f6"); 211139554Smarcel copyout((void*)&buf.d, (void*)va, sizeof(buf.d)); 212139554Smarcel break; 213139554Smarcel case ASM_OP_STFE: 214139554Smarcel reg = fpreg_ptr(mc, (int)i->i_oper[2].o_value); 215139554Smarcel if (reg == NULL) 216139554Smarcel return (EINVAL); 217139554Smarcel __asm("ldf.fill f6=%1;; stfe %0=f6" : "=m"(buf.e) : 218139554Smarcel "m"(*(long double *)reg) : "f6"); 219139554Smarcel copyout((void*)&buf.e, (void*)va, sizeof(buf.e)); 220139554Smarcel break; 221139554Smarcel case ASM_OP_STFS: 222139554Smarcel reg = fpreg_ptr(mc, (int)i->i_oper[2].o_value); 223139554Smarcel if (reg == NULL) 224139554Smarcel return (EINVAL); 225139554Smarcel __asm("ldf.fill f6=%1;; stfs %0=f6" : "=m"(buf.s) : 226139554Smarcel "m"(*(float *)reg) : "f6"); 227139554Smarcel copyout((void*)&buf.s, (void*)va, sizeof(buf.s)); 228139554Smarcel break; 229121415Smarcel default: 230121415Smarcel return (ENOENT); 23185199Sdfr } 23285199Sdfr 233121415Smarcel /* Handle post-increment. */ 234121415Smarcel if (i->i_oper[3].o_type == ASM_OPER_GREG) { 235121415Smarcel reg = greg_ptr(mc, (int)i->i_oper[3].o_value); 236121415Smarcel if (reg == NULL) 237115084Smarcel return (EINVAL); 238121449Smarcel postinc = rdreg(reg); 239121415Smarcel } else 240121415Smarcel postinc = (i->i_oper[3].o_type == ASM_OPER_IMM) 241121415Smarcel ? i->i_oper[3].o_value : 0; 242121415Smarcel if (postinc != 0) { 243140891Smarcel if (i->i_oper[1].o_type == ASM_OPER_MEM) 244140891Smarcel reg = greg_ptr(mc, (int)i->i_oper[1].o_value); 245140891Smarcel else 246140891Smarcel reg = greg_ptr(mc, (int)i->i_oper[2].o_value); 247121415Smarcel if (reg == NULL) 248121415Smarcel return (EINVAL); 249121449Smarcel postinc += rdreg(reg); 250121449Smarcel wrreg(reg, postinc); 25185199Sdfr } 252115084Smarcel return (0); 25385199Sdfr} 25485199Sdfr 25585199Sdfrint 256121415Smarcelunaligned_fixup(struct trapframe *tf, struct thread *td) 25785199Sdfr{ 258121415Smarcel mcontext_t context; 259121415Smarcel struct asm_bundle bundle; 260121415Smarcel int error, slot; 26185199Sdfr 262121415Smarcel slot = ((tf->tf_special.psr & IA64_PSR_RI) == IA64_PSR_RI_0) ? 0 : 263121415Smarcel ((tf->tf_special.psr & IA64_PSR_RI) == IA64_PSR_RI_1) ? 1 : 2; 26485199Sdfr 265121415Smarcel if (ia64_unaligned_print) { 266121415Smarcel uprintf("pid %d (%s): unaligned access: va=0x%lx, pc=0x%lx\n", 267121415Smarcel td->td_proc->p_pid, td->td_proc->p_comm, 268121415Smarcel tf->tf_special.ifa, tf->tf_special.iip + slot); 26985199Sdfr } 27085199Sdfr 27185199Sdfr /* 272139554Smarcel * If PSR.ac is set, the process wants to be signalled about mis- 273139554Smarcel * aligned loads and stores. Send it a SIGBUS. In order for us to 274139554Smarcel * test the emulation of misaligned loads and stores, we have a 275139554Smarcel * sysctl that tells us that we must emulate the load or store, 276139554Smarcel * instead of sending the signal. We need the sysctl because if 277139554Smarcel * PSR.ac is not set, the CPU may (and likely will) deal with the 278139554Smarcel * misaligned load or store itself. As such, we won't get the 279139554Smarcel * exception. 28085199Sdfr */ 281139554Smarcel if ((tf->tf_special.psr & IA64_PSR_AC) && !ia64_unaligned_test) 282121415Smarcel return (SIGBUS); 28385199Sdfr 284121415Smarcel if (!asm_decode(tf->tf_special.iip, &bundle)) 285121415Smarcel return (SIGILL); 28685199Sdfr 287121415Smarcel get_mcontext(td, &context, 0); 28885199Sdfr 289121415Smarcel error = fixup(bundle.b_inst + slot, &context, tf->tf_special.ifa); 290121415Smarcel if (error == ENOENT) { 291121415Smarcel printf("unhandled misaligned memory access:\n\t"); 292121415Smarcel asm_print_inst(&bundle, slot, tf->tf_special.iip); 293121415Smarcel return (SIGILL); 294121415Smarcel } else if (error != 0) 295121415Smarcel return (SIGBUS); 29685199Sdfr 297121415Smarcel set_mcontext(td, &context); 29885199Sdfr 299121415Smarcel /* Advance to the next instruction. */ 300121415Smarcel if (slot == 2) { 301121415Smarcel tf->tf_special.psr &= ~IA64_PSR_RI; 302121415Smarcel tf->tf_special.iip += 16; 303121415Smarcel } else 304121415Smarcel tf->tf_special.psr += IA64_PSR_RI_1; 30585685Sdfr 306121415Smarcel return (0); 30785199Sdfr} 308