⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 unaligned.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * 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 + -