/*
* Architecture-specific unaligned trap handling.
*
* Copyright (C) 1999-2002, 2004 Hewlett-Packard Co
* Stephane Eranian <eranian@hpl.hp.com>
* David Mosberger-Tang <davidm@hpl.hp.com>
*
* 2002/12/09 Fix rotating register handling (off-by-1 error, missing fr-rotation). Fix
* get_rse_reg() to not leak kernel bits to user-level (reading an out-of-frame
* stacked register returns an undefined value; it does NOT trigger a
* "rsvd register fault").
* 2001/10/11 Fix unaligned access to rotating registers in s/w pipelined loops.
* 2001/08/13 Correct size of extended floats (float_fsz) from 16 to 10 bytes.
* 2001/01/17 Add support emulation of unaligned kernel accesses.
*/
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/ratelimit.h>
#include <asm/intrinsics.h>
#include <asm/processor.h>
#include <asm/rse.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
extern int die_if_kernel(char *str, struct pt_regs *regs, long err);
#undef DEBUG_UNALIGNED_TRAP
#ifdef DEBUG_UNALIGNED_TRAP
# define DPRINT(a...) do { printk("%s %u: ", __func__, __LINE__); printk (a); } while (0)
# define DDUMP(str,vp,len) dump(str, vp, len)
static void
dump (const char *str, void *vp, size_t len)
{
unsigned char *cp = vp;
int i;
printk("%s", str);
for (i = 0; i < len; ++i)
printk (" %02x", *cp++);
printk("\n");
}
#else
# define DPRINT(a...)
# define DDUMP(str,vp,len)
#endif
#define IA64_FIRST_STACKED_GR 32
#define IA64_FIRST_ROTATING_FR 32
#define SIGN_EXT9 0xffffffffffffff00ul
/*
* sysctl settable hook which tells the kernel whether to honor the
* IA64_THREAD_UAC_NOPRINT prctl. Because this is user settable, we want
* to allow the super user to enable/disable this for security reasons
* (i.e. don't allow attacker to fill up logs with unaligned accesses).
*/
int no_unaligned_warning;
int unaligned_dump_stack;
/*
* For M-unit:
*
* opcode | m | x6 |
* --------|------|---------|
* [40-37] | [36] | [35:30] |
* --------|------|---------|
* 4 | 1 | 6 | = 11 bits
* --------------------------
* However bits [31:30] are not directly useful to distinguish between
* load/store so we can use [35:32] instead, which gives the following
* mask ([40:32]) using 9 bits. The 'e' comes from the fact that we defer
* checking the m-bit until later in the load/store emulation.
*/
#define IA64_OPCODE_MASK 0x1ef
#define IA64_OPCODE_SHIFT 32
/*
* Table C-28 Integer Load/Store
*
* We ignore [35:32]= 0x6, 0x7, 0xE, 0xF
*
* ld8.fill, st8.fill MUST be aligned because the RNATs are based on
* the address (bits [8:3]), so we must failed.
*/
#define LD_OP 0x080
#define LDS_OP 0x081
#define LDA_OP 0x082
#define LDSA_OP 0x083
#define LDBIAS_OP 0x084
#define LDACQ_OP 0x085
/* 0x086, 0x087 are not relevant */
#define LDCCLR_OP 0x088
#define LDCNC_OP 0x089
#define LDCCLRACQ_OP 0x08a
#define ST_OP 0x08c
#define STREL_OP 0x08d
/* 0x08e,0x8f are not relevant */
/*
* Table C-29 Integer Load +Reg
*
* we use the ld->m (bit [36:36]) field to determine whether or not we have
* a load/store of this form.
*/
/*
* Table C-30 Integer Load/Store +Imm
*
* We ignore [35:32]= 0x6, 0x7, 0xE, 0xF
*
* ld8.fill, st8.fill must be aligned because the Nat register are based on
* the address, so we must fail and the program must be fixed.
*/
#define LD_IMM_OP 0x0a0
#define LDS_IMM_OP 0x0a1
#define LDA_IMM_OP 0x0a2
#define LDSA_IMM_OP 0x0a3
#define LDBIAS_IMM_OP 0x0a4
#define LDACQ_IMM_OP 0x0a5
/* 0x0a6, 0xa7 are not relevant */
#define LDCCLR_IMM_OP 0x0a8
#define LDCNC_IMM_OP 0x0a9
#define LDCCLRACQ_IMM_OP 0x0aa
#define ST_IMM_OP 0x0ac
#define STREL_IMM_OP 0x0ad
/* 0x0ae,0xaf are not relevant */
/*
* Table C-32 Floating-point Load/Store
*/
#define LDF_OP 0x0c0
#define LDFS_OP 0x0c1
#define LDFA_OP 0x0c2
#define LDFSA_OP 0x0c3
/* 0x0c6 is irrelevant */
#define LDFCCLR_OP 0x0c8
#define LDFCNC_OP 0x0c9
/* 0x0cb is irrelevant */
#define STF_OP 0x0cc
/*
* Table C-33 Floating-point Load +Reg
*
* we use the ld->m (bit [36:36]) field to determine whether or not we have
* a load/store of this form.
*/
/*
* Table C-34 Floating-point Load/Store +Imm
*/
#define LDF_IMM_OP 0x0e0
#define LDFS_IMM_OP 0x0e1
#define LDFA_IMM_OP 0x0e2
#define LDFSA_IMM_OP 0x0e3
/* 0x0e6 is irrelevant */
#define LDFCCLR_IMM_OP 0x0e8
#define LDFCNC_IMM_OP 0x0e9
#define STF_IMM_OP 0x0ec
typedef struct {
unsigned long qp:6; /* [0:5] */
unsigned long r1:7; /* [6:12] */
unsigned long imm:7; /* [13:19] */
unsigned long r3:7; /* [20:26] */
unsigned long x:1; /* [27:27] */
unsigned long hint:2; /* [28:29] */
unsigned long x6_sz:2; /* [30:31] */
unsigned long x6_op:4; /* [32:35], x6 = x6_sz|x6_op */
unsigned long m:1; /* [36:36] */
unsigned long op:4; /* [37:40] */
unsigned long pad:23; /* [41:63] */
} load_store_t;
typedef enum {
UPD_IMMEDIATE, /* ldXZ r1=[r3],imm(9) */
UPD_REG /* ldXZ r1=[r3],r2 */
} update_t;
/*
* We use tables to keep track of the offsets of registers in the saved state.
* This way we save having big switch/case statements.
*
* We use bit 0 to indicate switch_stack or pt_regs.
* The offset is simply shifted by 1 bit.
* A 2-byte value should be enough to hold any kind of offset
*
* In case the calling convention changes (and thus pt_regs/switch_stack)
* simply use RSW instead of RPT or vice-versa.
*/
#define RPO(x) ((size_t) &((struct pt_regs *)0)->x)
#define RSO(x) ((size_t) &((struct switch_stack *)0)->x)
#define RPT(x) (RPO(x) << 1)
#define RSW(x) (1| RSO(x)<<1)
#define GR_OFFS(x) (gr_info[x]>>1)
#define GR_IN_SW(x) (gr_info[x] & 0x1)
#define FR_OFFS(x) (fr_info[x]>>1)
#define FR_IN_SW(x) (fr_info[x] & 0x1)
static u16 gr_info[32]={
0, /* r0 is read-only : WE SHOULD NEVER GET THIS */
RPT(r1), RPT(r2), RPT(r3),
RSW(r4), RSW(r5), RSW(r6), RSW(r7),
RPT(r8), RPT(r9), RPT(r10), RPT(r11),
RPT(r12), RPT(r13), RPT(r14), RPT(r15),
RPT(r16), RPT(r17), RPT(r18), RPT(r19),
RPT(r20), RPT(r21), RPT(r22), RPT(r23),
RPT(r24), RPT(r25), RPT(r26), RPT(r27),
RPT(r28), RPT(r29), RPT(r30), RPT(r31)
};
static u16 fr_info[32]={
0, /* constant : WE SHOULD NEVER GET THIS */
0, /* constant : WE SHOULD NEVER GET THIS */
RSW(f2), RSW(f3), RSW(f4), RSW(f5),
RPT(f6), RPT(f7), RPT(f8), RPT(f9),
RPT(f10), RPT(f11),
RSW(f12), RSW(f13), RSW(f14),
RSW(f15), RSW(f16), RSW(f17), RSW(f18), RSW(f19),
RSW(f20), RSW(f21), RSW(f22), RSW(f23)
|