/* * 32-bit misaligned trap handler (c) 1998 San Mehat (CCC) -July 1998 * /proc/sys/debug/alignment, modified and integrated into * Linux 2.1 by Russell King * * Speed optimisations and better fault handling by Russell King. * * *** NOTE *** * This code is not portable to processors with late data abort handling.
*/ #define CODING_BITS(i) (i & 0x0e000000) #define COND_BITS(i) (i & 0xf0000000)
/* Return true if and only if the ARMv6 unaligned access model is in use. */ staticbool cpu_is_v6_unaligned(void)
{ return cpu_architecture() >= CPU_ARCH_ARMv6 && get_cr() & CR_U;
}
staticint safe_usermode(int new_usermode, bool warn)
{ /* * ARMv6 and later CPUs can perform unaligned accesses for * most single load and store instructions up to word size. * LDM, STM, LDRD and STRD still need to be handled. * * Ignoring the alignment fault is not an option on these * CPUs since we spin re-faulting the instruction without * making any progress.
*/ if (cpu_is_v6_unaligned() && !(new_usermode & (UM_FIXUP | UM_SIGNAL))) {
new_usermode |= UM_FIXUP;
if (warn)
pr_warn("alignment: ignoring faults is unsafe on this CPU. Defaulting to fixup mode.\n");
}
/* * LDM/STM alignment handler. * * There are 4 variants of this instruction: * * B = rn pointer before instruction, A = rn pointer after instruction * ------ increasing address -----> * | | r0 | r1 | ... | rx | | * PU = 01 B A * PU = 11 B A * PU = 00 A B * PU = 10 A B
*/ staticint
do_alignment_ldmstm(unsignedlong addr, u32 instr, struct pt_regs *regs)
{ unsignedint rd, rn, correction, nr_regs, regbits; unsignedlong eaddr, newaddr;
if (!LDST_U_BIT(instr))
nr_regs = -nr_regs;
newaddr += nr_regs; if (!LDST_U_BIT(instr))
eaddr = newaddr;
if (LDST_P_EQ_U(instr)) /* U = P */
eaddr += 4;
/* * For alignment faults on the ARM922T/ARM920T the MMU makes * the FSR (and hence addr) equal to the updated base address * of the multiple access rather than the restored value. * Switch this message off if we've got a ARM92[02], otherwise * [ls]dm alignment faults are noisy!
*/ #if !(defined CONFIG_CPU_ARM922T) && !(defined CONFIG_CPU_ARM920T) /* * This is a "hint" - we already have eaddr worked out by the * processor for us.
*/ if (addr != eaddr) {
pr_err("LDMSTM: PC = %08lx, instr = %08x, " "addr = %08lx, eaddr = %08lx\n",
instruction_pointer(regs), instr, addr, eaddr);
show_regs(regs);
} #endif
bad:
pr_err("Alignment trap: not handling ldm with s-bit set\n"); return TYPE_ERROR;
}
/* * Convert Thumb ld/st instruction forms to equivalent ARM instructions so * we can reuse ARM userland alignment fault fixups for Thumb. * * This implementation was initially based on the algorithm found in * gdb/sim/arm/thumbemu.c. It is basically just a code reduction of same * to convert only Thumb ld/st instruction forms to equivalent ARM forms. * * NOTES: * 1. Comments below refer to ARM ARM DDI0100E Thumb Instruction sections. * 2. If for some reason we're passed an non-ld/st Thumb instruction to * decode, we return 0xdeadc0de. This should never happen under normal * circumstances but if it does, we've got other problems to deal with * elsewhere and we obviously can't fix those problems here.
*/
/* 6.5.1 Format 3: */ case 0x4800 >> 11: /* 7.1.28 LDR(3) */ /* NOTE: This case is not technically possible. We're * loading 32-bit memory data via PC relative * addressing mode. So we can and should eliminate * this case. But I'll leave it here for now.
*/ return 0xe59f0000 |
((tinstr & (7<<8)) << (12-8)) | /* Rd */
((tinstr & 255) << (2-0)); /* immed_8 */
/* * Convert Thumb-2 32 bit LDM, STM, LDRD, STRD to equivalent instruction * handlable by ARM alignment handler, also find the corresponding handler, * so that we can reuse ARM userland alignment fault fixups for Thumb. * * @pinstr: original Thumb-2 instruction; returns new handlable instruction * @regs: register context. * @poffset: return offset from faulted addr for later writeback * * NOTES: * 1. Comments below refer to ARMv7 DDI0406A Thumb Instruction sections. * 2. Register name Rt from ARMv7 is same as Rd from ARMv6 (Rd is Rt)
*/ staticvoid *
do_alignment_t32_to_handler(u32 *pinstr, struct pt_regs *regs, union offset_union *poffset)
{
u32 instr = *pinstr;
u16 tinst1 = (instr >> 16) & 0xffff;
u16 tinst2 = instr & 0xffff;
switch (tinst1 & 0xffe0) { /* A6.3.5 Load/Store multiple */ case 0xe880: /* STM/STMIA/STMEA,LDM/LDMIA, PUSH/POP T2 */ case 0xe8a0: /* ...above writeback version */ case 0xe900: /* STMDB/STMFD, LDMDB/LDMEA */ case 0xe920: /* ...above writeback version */ /* no need offset decision since handler calculates it */ return do_alignment_ldmstm;
case 0xf840: /* POP/PUSH T3 (single register) */ if (RN_BITS(instr) == 13 && (tinst2 & 0x09ff) == 0x0904) {
u32 L = !!(LDST_L_BIT(instr)); const u32 subset[2] = {
0xe92d0000, /* STMDB sp!,{registers} */
0xe8bd0000, /* LDMIA sp!,{registers} */
};
*pinstr = subset[L] | (1<<RD_BITS(instr)); return do_alignment_ldmstm;
} /* Else fall through for illegal instruction case */ break;
/* A6.3.6 Load/store double, STRD/LDRD(immed, lit, reg) */ case 0xe860: case 0xe960: case 0xe8e0: case 0xe9e0:
poffset->un = (tinst2 & 0xff) << 2;
fallthrough;
case 0xe940: case 0xe9c0: return do_alignment_ldrdstrd;
/* * No need to handle load/store instructions up to word size * since ARMv6 and later CPUs can perform unaligned accesses.
*/ default: break;
} return NULL;
}
if (ai_usermode & UM_SIGNAL) {
force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr);
} else { /* * We're about to disable the alignment trap and return to * user space. But if an interrupt occurs before actually * reaching user space, then the IRQ vector entry code will * notice that we were still in kernel space and therefore * the alignment trap won't be re-enabled in that case as it * is presumed to be always on from kernel space. * Let's prevent that race by disabling interrupts here (they * are disabled on the way back to user space anyway in * entry-common.S) and disable the alignment trap only if * there is no work pending for this thread.
*/
raw_local_irq_disable(); if (!(read_thread_flags() & _TIF_WORK_MASK))
set_cr(cr_no_alignment);
}
/* * This needs to be done after sysctl_init_bases(), otherwise sys/ will be * overwritten. Actually, this shouldn't be in sys/ at all since * it isn't a sysctl, and it doesn't contain sysctl information. * We now locate it in /proc/cpu/alignment instead.
*/ staticint __init alignment_init(void)
{ #ifdef CONFIG_PROC_FS struct proc_dir_entry *res;
res = proc_create("cpu/alignment", S_IWUSR | S_IRUGO, NULL,
&alignment_proc_ops); if (!res) return -ENOMEM; #endif
if (cpu_is_v6_unaligned()) {
set_cr(__clear_cr(CR_A));
ai_usermode = safe_usermode(ai_usermode, false);
}
/* * ARMv6K and ARMv7 use fault status 3 (0b00011) as Access Flag section * fault, not as alignment error. * * TODO: handle ARMv6K properly. Runtime check for 'K' extension is * needed.
*/ if (cpu_architecture() <= CPU_ARCH_ARMv6) {
hook_fault_code(3, do_alignment, SIGBUS, BUS_ADRALN, "alignment exception");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.