freebsd-nq/sys/ia64/ia64/unaligned.c
Marcel Moolenaar 51e25af386 Add support for unaligned ld2, st2, st4 and st8. While here, make
sure we handle stacked registers properly by taking into account
that:
1. bspstore points after the frame (due to cover),
2. we need to adjust for intermediate NaT collections.
2003-11-06 04:26:40 +00:00

261 lines
6.6 KiB
C

/*-
* Copyright (c) 2003 Marcel Moolenaar
* Copyright (c) 2001 Doug Rabson
* All rights reserved.
*
* 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 AUTHOR 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 AUTHOR 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.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/sysctl.h>
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <machine/frame.h>
#include <machine/md_var.h>
#include <ia64/disasm/disasm.h>
static int ia64_unaligned_print = 1; /* warn about unaligned accesses. */
static int ia64_unaligned_sigbus = 0; /* SIGBUS on all unaligned accesses. */
SYSCTL_INT(_machdep, OID_AUTO, unaligned_print, CTLFLAG_RW,
&ia64_unaligned_print, 0, "warn about unaligned accesses");
SYSCTL_INT(_machdep, OID_AUTO, unaligned_sigbus, CTLFLAG_RW,
&ia64_unaligned_sigbus, 0, "do not SIGBUS on fixed-up accesses");
static void *
fpreg_ptr(mcontext_t *mc, int fr)
{
union _ia64_fpreg *p;
if (fr <= 1 || fr >= 128)
return (NULL);
if (fr >= 32) {
p = &mc->mc_high_fp.fr32;
fr -= 32;
} else if (fr >= 16) {
p = &mc->mc_preserved_fp.fr16;
fr -= 16;
} else if (fr >= 6) {
p = &mc->mc_scratch_fp.fr6;
fr -= 6;
} else {
p = &mc->mc_preserved_fp.fr2;
fr -= 2;
}
return ((void*)(p + fr));
}
static void *
greg_ptr(mcontext_t *mc, int gr)
{
uint64_t *p;
int bias, nslots;
if (gr <= 0 || gr >= 32 + (mc->mc_special.cfm & 0x7f))
return (NULL);
if (gr >= 32) {
p = (void*)mc->mc_special.bspstore;
nslots = (mc->mc_special.cfm & 0x7f) - gr + 32;
bias = (0x1f8 - (mc->mc_special.bspstore & 0x1f8)) >> 3;
nslots += (nslots + bias) / 63;
p -= nslots;
gr = 0;
} else if (gr >= 14) {
p = &mc->mc_scratch.gr14;
gr -= 14;
} else if (gr == 13) {
p = &mc->mc_special.tp;
gr = 0;
} else if (gr == 12) {
p = &mc->mc_special.sp;
gr = 0;
} else if (gr >= 8) {
p = &mc->mc_scratch.gr8;
gr -= 8;
} else if (gr >= 4) {
p = &mc->mc_preserved.gr4;
gr -= 4;
} else if (gr >= 2) {
p = &mc->mc_scratch.gr2;
gr -= 2;
} else {
p = &mc->mc_special.gp;
gr = 0;
}
return ((void*)(p + gr));
}
static uint64_t
rdreg(uint64_t *addr)
{
if ((uintptr_t)addr < VM_MAX_ADDRESS)
return (fuword(addr));
return (*addr);
}
static void
wrreg(uint64_t *addr, uint64_t val)
{
if ((uintptr_t)addr < VM_MAX_ADDRESS)
suword(addr, val);
else
*addr = val;
}
static int
fixup(struct asm_inst *i, mcontext_t *mc, uint64_t va)
{
union {
double d;
uint64_t i;
} buf;
void *reg;
uint64_t postinc;
switch (i->i_op) {
case ASM_OP_LD2:
copyin((void*)va, (void*)&buf.i, 2);
reg = greg_ptr(mc, (int)i->i_oper[1].o_value);
if (reg == NULL)
return (EINVAL);
wrreg(reg, buf.i & 0xffffU);
break;
case ASM_OP_LD4:
copyin((void*)va, (void*)&buf.i, 4);
reg = greg_ptr(mc, (int)i->i_oper[1].o_value);
if (reg == NULL)
return (EINVAL);
wrreg(reg, buf.i & 0xffffffffU);
break;
case ASM_OP_LD8:
copyin((void*)va, (void*)&buf.i, 8);
reg = greg_ptr(mc, (int)i->i_oper[1].o_value);
if (reg == NULL)
return (EINVAL);
wrreg(reg, buf.i);
break;
case ASM_OP_LDFD:
copyin((void*)va, (void*)&buf.d, 8);
reg = fpreg_ptr(mc, (int)i->i_oper[1].o_value);
if (reg == NULL)
return (EINVAL);
spillfd((void*)&buf.d, reg);
break;
case ASM_OP_ST2:
reg = greg_ptr(mc, (int)i->i_oper[2].o_value);
if (reg == NULL)
return (EINVAL);
buf.i = rdreg(reg);
copyout((void*)&buf.i, (void*)va, 2);
break;
case ASM_OP_ST4:
reg = greg_ptr(mc, (int)i->i_oper[2].o_value);
if (reg == NULL)
return (EINVAL);
buf.i = rdreg(reg);
copyout((void*)&buf.i, (void*)va, 4);
break;
case ASM_OP_ST8:
reg = greg_ptr(mc, (int)i->i_oper[2].o_value);
if (reg == NULL)
return (EINVAL);
buf.i = rdreg(reg);
copyout((void*)&buf.i, (void*)va, 8);
break;
default:
return (ENOENT);
}
/* Handle post-increment. */
if (i->i_oper[3].o_type == ASM_OPER_GREG) {
reg = greg_ptr(mc, (int)i->i_oper[3].o_value);
if (reg == NULL)
return (EINVAL);
postinc = rdreg(reg);
} else
postinc = (i->i_oper[3].o_type == ASM_OPER_IMM)
? i->i_oper[3].o_value : 0;
if (postinc != 0) {
reg = greg_ptr(mc, (int)i->i_oper[3].o_value);
if (reg == NULL)
return (EINVAL);
postinc += rdreg(reg);
wrreg(reg, postinc);
}
return (0);
}
int
unaligned_fixup(struct trapframe *tf, struct thread *td)
{
mcontext_t context;
struct asm_bundle bundle;
int error, slot;
slot = ((tf->tf_special.psr & IA64_PSR_RI) == IA64_PSR_RI_0) ? 0 :
((tf->tf_special.psr & IA64_PSR_RI) == IA64_PSR_RI_1) ? 1 : 2;
if (ia64_unaligned_print) {
uprintf("pid %d (%s): unaligned access: va=0x%lx, pc=0x%lx\n",
td->td_proc->p_pid, td->td_proc->p_comm,
tf->tf_special.ifa, tf->tf_special.iip + slot);
}
/*
* If PSR.ac is set, then the process wants to know about misaligned
* loads and stores. Send it a SIGBUS so that it can deal with them.
* We also send a SIGBUS if configured to do so.
*/
if ((tf->tf_special.psr & IA64_PSR_AC) || ia64_unaligned_sigbus)
return (SIGBUS);
if (!asm_decode(tf->tf_special.iip, &bundle))
return (SIGILL);
get_mcontext(td, &context, 0);
error = fixup(bundle.b_inst + slot, &context, tf->tf_special.ifa);
if (error == ENOENT) {
printf("unhandled misaligned memory access:\n\t");
asm_print_inst(&bundle, slot, tf->tf_special.iip);
return (SIGILL);
} else if (error != 0)
return (SIGBUS);
set_mcontext(td, &context);
/* Advance to the next instruction. */
if (slot == 2) {
tf->tf_special.psr &= ~IA64_PSR_RI;
tf->tf_special.iip += 16;
} else
tf->tf_special.psr += IA64_PSR_RI_1;
return (0);
}