📄 unaligned.c
字号:
/* * Architecture-specific unaligned trap handling. * * Copyright (C) 1999-2000 Hewlett-Packard Co * Copyright (C) 1999-2000 Stephane Eranian <eranian@hpl.hp.com> */#include <linux/kernel.h>#include <linux/sched.h>#include <linux/smp_lock.h>#include <asm/uaccess.h>#include <asm/rse.h>#include <asm/processor.h>#include <asm/unaligned.h>extern void die_if_kernel(char *str, struct pt_regs *regs, long err) __attribute__ ((noreturn));#undef DEBUG_UNALIGNED_TRAP#ifdef DEBUG_UNALIGNED_TRAP#define DPRINT(a) { printk("%s, line %d: ", __FUNCTION__, __LINE__); printk a;}#else#define DPRINT(a)#endif#define IA64_FIRST_STACKED_GR 32#define IA64_FIRST_ROTATING_FR 32#define SIGN_EXT9 __IA64_UL(0xffffffffffffff00)/* * 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 0x1ef00000000/* * 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 0x08000000000#define LDS_OP 0x08100000000#define LDA_OP 0x08200000000#define LDSA_OP 0x08300000000#define LDBIAS_OP 0x08400000000#define LDACQ_OP 0x08500000000/* 0x086, 0x087 are not relevant */#define LDCCLR_OP 0x08800000000#define LDCNC_OP 0x08900000000#define LDCCLRACQ_OP 0x08a00000000#define ST_OP 0x08c00000000#define STREL_OP 0x08d00000000/* 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 0x0a000000000#define LDS_IMM_OP 0x0a100000000#define LDA_IMM_OP 0x0a200000000#define LDSA_IMM_OP 0x0a300000000#define LDBIAS_IMM_OP 0x0a400000000#define LDACQ_IMM_OP 0x0a500000000/* 0x0a6, 0xa7 are not relevant */#define LDCCLR_IMM_OP 0x0a800000000#define LDCNC_IMM_OP 0x0a900000000#define LDCCLRACQ_IMM_OP 0x0aa00000000#define ST_IMM_OP 0x0ac00000000#define STREL_IMM_OP 0x0ad00000000/* 0x0ae,0xaf are not relevant *//* * Table C-32 Floating-point Load/Store */#define LDF_OP 0x0c000000000#define LDFS_OP 0x0c100000000#define LDFA_OP 0x0c200000000#define LDFSA_OP 0x0c300000000/* 0x0c6 is irrelevant */#define LDFCCLR_OP 0x0c800000000#define LDFCNC_OP 0x0c900000000/* 0x0cb is irrelevant */#define STF_OP 0x0cc00000000/* * 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 0x0e000000000#define LDFS_IMM_OP 0x0e100000000#define LDFA_IMM_OP 0x0e200000000#define LDFSA_IMM_OP 0x0e300000000/* 0x0e6 is irrelevant */#define LDFCCLR_IMM_OP 0x0e800000000#define LDFCNC_IMM_OP 0x0e900000000#define STF_IMM_OP 0x0ec00000000typedef 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), RSW(f10), RSW(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), RSW(f24), RSW(f25), RSW(f26), RSW(f27), RSW(f28), RSW(f29), RSW(f30), RSW(f31)};/* Invalidate ALAT entry for integer register REGNO. */static voidinvala_gr (int regno){# define F(reg) case reg: __asm__ __volatile__ ("invala.e r%0" :: "i"(reg)); break switch (regno) { F( 0); F( 1); F( 2); F( 3); F( 4); F( 5); F( 6); F( 7); F( 8); F( 9); F( 10); F( 11); F( 12); F( 13); F( 14); F( 15); F( 16); F( 17); F( 18); F( 19); F( 20); F( 21); F( 22); F( 23); F( 24); F( 25); F( 26); F( 27); F( 28); F( 29); F( 30); F( 31); F( 32); F( 33); F( 34); F( 35); F( 36); F( 37); F( 38); F( 39); F( 40); F( 41); F( 42); F( 43); F( 44); F( 45); F( 46); F( 47); F( 48); F( 49); F( 50); F( 51); F( 52); F( 53); F( 54); F( 55); F( 56); F( 57); F( 58); F( 59); F( 60); F( 61); F( 62); F( 63); F( 64); F( 65); F( 66); F( 67); F( 68); F( 69); F( 70); F( 71); F( 72); F( 73); F( 74); F( 75); F( 76); F( 77); F( 78); F( 79); F( 80); F( 81); F( 82); F( 83); F( 84); F( 85); F( 86); F( 87); F( 88); F( 89); F( 90); F( 91); F( 92); F( 93); F( 94); F( 95); F( 96); F( 97); F( 98); F( 99); F(100); F(101); F(102); F(103); F(104); F(105); F(106); F(107); F(108); F(109); F(110); F(111); F(112); F(113); F(114); F(115); F(116); F(117); F(118); F(119); F(120); F(121); F(122); F(123); F(124); F(125); F(126); F(127); }# undef F}/* Invalidate ALAT entry for floating-point register REGNO. */static voidinvala_fr (int regno){# define F(reg) case reg: __asm__ __volatile__ ("invala.e f%0" :: "i"(reg)); break switch (regno) { F( 0); F( 1); F( 2); F( 3); F( 4); F( 5); F( 6); F( 7); F( 8); F( 9); F( 10); F( 11); F( 12); F( 13); F( 14); F( 15); F( 16); F( 17); F( 18); F( 19); F( 20); F( 21); F( 22); F( 23); F( 24); F( 25); F( 26); F( 27); F( 28); F( 29); F( 30); F( 31); F( 32); F( 33); F( 34); F( 35); F( 36); F( 37); F( 38); F( 39); F( 40); F( 41); F( 42); F( 43); F( 44); F( 45); F( 46); F( 47); F( 48); F( 49); F( 50); F( 51); F( 52); F( 53); F( 54); F( 55); F( 56); F( 57); F( 58); F( 59); F( 60); F( 61); F( 62); F( 63); F( 64); F( 65); F( 66); F( 67); F( 68); F( 69); F( 70); F( 71); F( 72); F( 73); F( 74); F( 75); F( 76); F( 77); F( 78); F( 79); F( 80); F( 81); F( 82); F( 83); F( 84); F( 85); F( 86); F( 87); F( 88); F( 89); F( 90); F( 91); F( 92); F( 93); F( 94); F( 95); F( 96); F( 97); F( 98); F( 99); F(100); F(101); F(102); F(103); F(104); F(105); F(106); F(107); F(108); F(109); F(110); F(111); F(112); F(113); F(114); F(115); F(116); F(117); F(118); F(119); F(120); F(121); F(122); F(123); F(124); F(125); F(126); F(127); }# undef F}static voidset_rse_reg(struct pt_regs *regs, unsigned long r1, unsigned long val, int nat){ struct switch_stack *sw = (struct switch_stack *)regs - 1; unsigned long *kbs = ((unsigned long *)current) + IA64_RBS_OFFSET/8; unsigned long on_kbs; unsigned long *bsp, *bspstore, *addr, *ubs_end, *slot; unsigned long rnats; long nlocals; /* * cr_ifs=[rv:ifm], ifm=[....:sof(6)] * nlocal=number of locals (in+loc) register of the faulting function */ nlocals = (regs->cr_ifs) & 0x7f; DPRINT(("sw.bsptore=%lx pt.bspstore=%lx\n", sw->ar_bspstore, regs->ar_bspstore)); DPRINT(("cr.ifs=%lx sof=%ld sol=%ld\n", regs->cr_ifs, regs->cr_ifs &0x7f, (regs->cr_ifs>>7)&0x7f)); on_kbs = ia64_rse_num_regs(kbs, (unsigned long *)sw->ar_bspstore); bspstore = (unsigned long *)regs->ar_bspstore; DPRINT(("rse_slot_num=0x%lx\n",ia64_rse_slot_num((unsigned long *)sw->ar_bspstore))); DPRINT(("kbs=%p nlocals=%ld\n", (void *) kbs, nlocals)); DPRINT(("bspstore next rnat slot %p\n", (void *) ia64_rse_rnat_addr((unsigned long *)sw->ar_bspstore))); DPRINT(("on_kbs=%ld rnats=%ld\n", on_kbs, ((sw->ar_bspstore-(unsigned long)kbs)>>3) - on_kbs)); /* * See get_rse_reg() for an explanation on the following instructions */ ubs_end = ia64_rse_skip_regs(bspstore, on_kbs); bsp = ia64_rse_skip_regs(ubs_end, -nlocals); addr = slot = ia64_rse_skip_regs(bsp, r1 - 32); DPRINT(("ubs_end=%p bsp=%p addr=%p slot=0x%lx\n", (void *) ubs_end, (void *) bsp, (void *) addr, ia64_rse_slot_num(addr))); ia64_poke(regs, current, (unsigned long)addr, val); /* * addr will now contain the address of the RNAT for the register */ addr = ia64_rse_rnat_addr(addr); ia64_peek(regs, current, (unsigned long)addr, &rnats); DPRINT(("rnat @%p = 0x%lx nat=%d rnatval=%lx\n", (void *) addr, rnats, nat, rnats &ia64_rse_slot_num(slot))); if (nat) { rnats |= __IA64_UL(1) << ia64_rse_slot_num(slot); } else { rnats &= ~(__IA64_UL(1) << ia64_rse_slot_num(slot)); } ia64_poke(regs, current, (unsigned long)addr, rnats); DPRINT(("rnat changed to @%p = 0x%lx\n", (void *) addr, rnats));}static voidget_rse_reg(struct pt_regs *regs, unsigned long r1, unsigned long *val, int *nat){ struct switch_stack *sw = (struct switch_stack *)regs - 1; unsigned long *kbs = (unsigned long *)current + IA64_RBS_OFFSET/8; unsigned long on_kbs; long nlocals; unsigned long *bsp, *addr, *ubs_end, *slot, *bspstore; unsigned long rnats; /* * cr_ifs=[rv:ifm], ifm=[....:sof(6)] * nlocals=number of local registers in the faulting function */ nlocals = (regs->cr_ifs) & 0x7f; /* * save_switch_stack does a flushrs and saves bspstore. * on_kbs = actual number of registers saved on kernel backing store * (taking into accound potential RNATs) * * Note that this number can be greater than nlocals if the dirty * parititions included more than one stack frame at the time we * switched to KBS */ on_kbs = ia64_rse_num_regs(kbs, (unsigned long *)sw->ar_bspstore); bspstore = (unsigned long *)regs->ar_bspstore; /* * To simplify the logic, we calculate everything as if there was only * one backing store i.e., the user one (UBS). We let it to peek/poke * to figure out whether the register we're looking for really is * on the UBS or on KBS. * * regs->ar_bsptore = address of last register saved on UBS (before switch) * * ubs_end = virtual end of the UBS (if everything had been spilled there) * * We know that ubs_end is the point where the last register on the * stack frame we're interested in as been saved. So we need to walk * our way backward to figure out what the BSP "was" for that frame, * this will give us the location of r32. * * bsp = "virtual UBS" address of r32 for our frame * * Finally, get compute the address of the register we're looking for * using bsp as our base (move up again). * * Please note that in our case, we know that the register is necessarily * on the KBS because we are only interested in the current frame at the moment * we got the exception i.e., bsp is not changed until we switch to KBS. */ ubs_end = ia64_rse_skip_regs(bspstore, on_kbs); bsp = ia64_rse_skip_regs(ubs_end, -nlocals); addr = slot = ia64_rse_skip_regs(bsp, r1 - 32); DPRINT(("ubs_end=%p bsp=%p addr=%p slot=0x%lx\n", (void *) ubs_end, (void *) bsp, (void *) addr, ia64_rse_slot_num(addr))); ia64_peek(regs, current, (unsigned long)addr, val); /* * addr will now contain the address of the RNAT for the register */ addr = ia64_rse_rnat_addr(addr); ia64_peek(regs, current, (unsigned long)addr, &rnats); DPRINT(("rnat @%p = 0x%lx\n", (void *) addr, rnats)); if (nat) *nat = rnats >> ia64_rse_slot_num(slot) & 0x1;}static voidsetreg(unsigned long regnum, unsigned long val, int nat, struct pt_regs *regs){ struct switch_stack *sw = (struct switch_stack *)regs -1; unsigned long addr; unsigned long bitmask; unsigned long *unat; /* * First takes care of stacked registers */ if (regnum >= IA64_FIRST_STACKED_GR) { set_rse_reg(regs, regnum, val, nat); return; } /* * Using r0 as a target raises a General Exception fault which has * higher priority than the Unaligned Reference fault. */ /* * Now look at registers in [0-31] range and init correct UNAT */ if (GR_IN_SW(regnum)) { addr = (unsigned long)sw; unat = &sw->ar_unat; } else { addr = (unsigned long)regs; unat = &sw->caller_unat; } DPRINT(("tmp_base=%lx switch_stack=%s offset=%d\n", addr, unat==&sw->ar_unat ? "yes":"no", GR_OFFS(regnum))); /* * add offset from base of struct * and do it ! */ addr += GR_OFFS(regnum); *(unsigned long *)addr = val; /* * We need to clear the corresponding UNAT bit to fully emulate the load * UNAT bit_pos = GR[r3]{8:3} form EAS-2.4 */ bitmask = __IA64_UL(1) << (addr >> 3 & 0x3f); DPRINT(("*0x%lx=0x%lx NaT=%d prev_unat @%p=%lx\n", addr, val, nat, (void *) unat, *unat)); if (nat) { *unat |= bitmask; } else { *unat &= ~bitmask; } DPRINT(("*0x%lx=0x%lx NaT=%d new unat: %p=%lx\n", addr, val, nat, (void *) unat,*unat));}#define IA64_FPH_OFFS(r) (r - IA64_FIRST_ROTATING_FR)static voidsetfpreg(unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs){ struct switch_stack *sw = (struct switch_stack *)regs - 1; unsigned long addr; /* * From EAS-2.5: FPDisableFault has higher priority than Unaligned * Fault. Thus, when we get here, we know the partition is enabled. * To update f32-f127, there are three choices: * * (1) save f32-f127 to thread.fph and update the values there * (2) use a gigantic switch statement to directly access the registers * (3) generate code on the fly to update the desired register * * For now, we are using approach (1). */ if (regnum >= IA64_FIRST_ROTATING_FR) { ia64_sync_fph(current); current->thread.fph[IA64_FPH_OFFS(regnum)] = *fpval; } else { /* * pt_regs or switch_stack ? */ if (FR_IN_SW(regnum)) { addr = (unsigned long)sw; } else { addr = (unsigned long)regs; } DPRINT(("tmp_base=%lx offset=%d\n", addr, FR_OFFS(regnum))); addr += FR_OFFS(regnum); *(struct ia64_fpreg *)addr = *fpval; /* * mark the low partition as being used now * * It is highly unlikely that this bit is not already set, but * let's do it for safety. */ regs->cr_ipsr |= IA64_PSR_MFL; }}/* * Those 2 inline functions generate the spilled versions of the constant floating point * registers which can be used with stfX */static inline void float_spill_f0(struct ia64_fpreg *final){ __asm__ __volatile__ ("stf.spill [%0]=f0" :: "r"(final) : "memory");}static inline void float_spill_f1(struct ia64_fpreg *final){ __asm__ __volatile__ ("stf.spill [%0]=f1" :: "r"(final) : "memory");}static voidgetfpreg(unsigned long regnum, struct ia64_fpreg *fpval, struct pt_regs *regs){ struct switch_stack *sw = (struct switch_stack *)regs -1; unsigned long addr; /* * From EAS-2.5: FPDisableFault has higher priority than * Unaligned Fault. Thus, when we get here, we know the partition is * enabled. * * When regnum > 31, the register is still live and we need to force a save * to current->thread.fph to get access to it. See discussion in setfpreg() * for reasons and other ways of doing this. */ if (regnum >= IA64_FIRST_ROTATING_FR) { ia64_flush_fph(current); *fpval = current->thread.fph[IA64_FPH_OFFS(regnum)];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -