Data abort:
()
-
ENTRY(vectors)
-
ventry el1_sync_invalid // Synchronous EL1t
-
ventry el1_irq_invalid // IRQ EL1t
-
ventry el1_fiq_invalid // FIQ EL1t
-
ventry el1_error_invalid // Error EL1t
-
-
ventry el1_sync // Synchronous EL1h
-
ventry el1_irq // IRQ EL1h
-
ventry el1_fiq_invalid // FIQ EL1h
-
ventry el1_error_invalid // Error EL1h
-
-
ventry el0_sync // Synchronous 64-bit EL0
-
ventry el0_irq // IRQ 64-bit EL0
-
ventry el0_fiq_invalid // FIQ 64-bit EL0
-
ventry el0_error_invalid // Error 64-bit EL0
-
-
#ifdef CONFIG_COMPAT
-
ventry el0_sync_compat // Synchronous 32-bit EL0
-
ventry el0_irq_compat // IRQ 32-bit EL0
-
ventry el0_fiq_invalid_compat // FIQ 32-bit EL0
-
ventry el0_error_invalid_compat // Error 32-bit EL0
-
#else
-
ventry el0_sync_invalid // Synchronous 32-bit EL0
-
ventry el0_irq_invalid // IRQ 32-bit EL0
-
ventry el0_fiq_invalid // FIQ 32-bit EL0
-
ventry el0_error_invalid // Error 32-bit EL0
-
#endif
-
END(vectors)
()
-
el1_sync:
-
kernel_entry 1
-
mrs x1, esr_el1 // read the syndrome register
-
lsr x24, x1, #ESR_ELx_EC_SHIFT // exception class
-
cmp x24, #ESR_ELx_EC_DABT_CUR // data abort in EL1
-
b.eq el1_da
-
cmp x24, #ESR_ELx_EC_IABT_CUR // instruction abort in EL1
-
b.eq el1_ia
-
cmp x24, #ESR_ELx_EC_SYS64 // configurable trap
-
b.eq el1_undef
-
cmp x24, #ESR_ELx_EC_SP_ALIGN // stack alignment exception
-
b.eq el1_sp_pc
-
cmp x24, #ESR_ELx_EC_PC_ALIGN // pc alignment exception
-
b.eq el1_sp_pc
-
cmp x24, #ESR_ELx_EC_UNKNOWN // unknown exception in EL1
-
b.eq el1_undef
-
cmp x24, #ESR_ELx_EC_BREAKPT_CUR // debug exception in EL1
-
b.ge el1_dbg
-
b el1_inv
进入el1_sync后,将esr_el1读取到X1,然后取
esr_el1的EC部分,判断异常类型:
()
-
#define ESR_ELx_EC_UNKNOWN (0x00)
-
#define ESR_ELx_EC_WFx (0x01)
-
/* Unallocated EC: 0x02 */
-
#define ESR_ELx_EC_CP15_32 (0x03)
-
#define ESR_ELx_EC_CP15_64 (0x04)
-
#define ESR_ELx_EC_CP14_MR (0x05)
-
#define ESR_ELx_EC_CP14_LS (0x06)
-
#define ESR_ELx_EC_FP_ASIMD (0x07)
-
#define ESR_ELx_EC_CP10_ID (0x08)
-
/* Unallocated EC: 0x09 - 0x0B */
-
#define ESR_ELx_EC_CP14_64 (0x0C)
-
/* Unallocated EC: 0x0d */
-
#define ESR_ELx_EC_ILL (0x0E)
-
/* Unallocated EC: 0x0F - 0x10 */
-
#define ESR_ELx_EC_SVC32 (0x11)
-
#define ESR_ELx_EC_HVC32 (0x12)
-
#define ESR_ELx_EC_SMC32 (0x13)
-
/* Unallocated EC: 0x14 */
-
#define ESR_ELx_EC_SVC64 (0x15)
-
#define ESR_ELx_EC_HVC64 (0x16)
-
#define ESR_ELx_EC_SMC64 (0x17)
-
#define ESR_ELx_EC_SYS64 (0x18)
-
/* Unallocated EC: 0x19 - 0x1E */
-
#define ESR_ELx_EC_IMP_DEF (0x1f)
-
#define ESR_ELx_EC_IABT_LOW (0x20)
-
#define ESR_ELx_EC_IABT_CUR (0x21)
-
#define ESR_ELx_EC_PC_ALIGN (0x22)
-
/* Unallocated EC: 0x23 */
-
#define ESR_ELx_EC_DABT_LOW (0x24)
-
#define ESR_ELx_EC_DABT_CUR (0x25)
-
#define ESR_ELx_EC_SP_ALIGN (0x26)
-
/* Unallocated EC: 0x27 */
-
#define ESR_ELx_EC_FP_EXC32 (0x28)
-
/* Unallocated EC: 0x29 - 0x2B */
-
#define ESR_ELx_EC_FP_EXC64 (0x2C)
-
/* Unallocated EC: 0x2D - 0x2E */
-
#define ESR_ELx_EC_SERROR (0x2F)
-
#define ESR_ELx_EC_BREAKPT_LOW (0x30)
-
#define ESR_ELx_EC_BREAKPT_CUR (0x31)
-
#define ESR_ELx_EC_SOFTSTP_LOW (0x32)
-
#define ESR_ELx_EC_SOFTSTP_CUR (0x33)
-
#define ESR_ELx_EC_WATCHPT_LOW (0x34)
-
#define ESR_ELx_EC_WATCHPT_CUR (0x35)
-
/* Unallocated EC: 0x36 - 0x37 */
-
#define ESR_ELx_EC_BKPT32 (0x38)
-
/* Unallocated EC: 0x39 */
-
#define ESR_ELx_EC_VECTOR32 (0x3A)
-
/* Unallocted EC: 0x3B */
-
#define ESR_ELx_EC_BRK64 (0x3C)
-
/* Unallocated EC: 0x3D - 0x3F */
-
#define ESR_ELx_EC_MAX (0x3F)
()
-
el1_da:
-
/*
-
* Data abort handling
-
*/
-
mrs x0, far_el1
-
enable_dbg
-
// re-enable interrupts if they were enabled in the aborted context
-
tbnz x23, #7, 1f // PSR_I_BIT
-
enable_irq
-
1:
-
mov x2, sp // struct pt_regs
-
bl do_mem_abort
-
-
// disable interrupts before pulling preserved data off the stack
-
disable_irq
-
kernel_exit 1
进入C code,其中入参X0=far_el1, X1=esr_el1, X2=sp (---> pt_regs)
()
-
asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
-
struct pt_regs *regs)
-
{
-
const struct fault_info *inf = fault_info + (esr & 63);
-
struct siginfo info;
-
-
if (!inf->fn(addr, esr, regs))
-
return;
-
-
pr_alert("Unhandled fault: %s (0x%08x) at 0x%016lx\n",
-
inf->name, esr, addr);
-
-
info.si_signo = inf->sig;
-
info.si_errno = 0;
-
info.si_code = inf->code;
-
info.si_addr = (void __user *)addr;
-
arm64_notify_die("", regs, &info, esr);
-
}
通过
esr_el1的低6bits和fault_info数组,确定具体处理函数:
()
-
static const struct fault_info {
-
int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs);
-
int sig;
-
int code;
-
const char *name;
-
} fault_info[] = {
-
{ do_bad, SIGBUS, 0, "ttbr address size fault" },
-
{ do_bad, SIGBUS, 0, "level 1 address size fault" },
-
{ do_bad, SIGBUS, 0, "level 2 address size fault" },
-
{ do_bad, SIGBUS, 0, "level 3 address size fault" },
-
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 0 translation fault" },
-
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" },
-
{ do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" },
-
{ do_page_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" },
-
{ do_bad, SIGBUS, 0, "unknown 8" },
-
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" },
-
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" },
-
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" },
-
{ do_bad, SIGBUS, 0, "unknown 12" },
-
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" },
-
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" },
-
{ do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" },
-
{ do_bad, SIGBUS, 0, "synchronous external abort" },
-
{ do_bad, SIGBUS, 0, "unknown 17" },
-
{ do_bad, SIGBUS, 0, "unknown 18" },
-
{ do_bad, SIGBUS, 0, "unknown 19" },
-
{ do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
-
{ do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
-
{ do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
-
{ do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
-
{ do_bad, SIGBUS, 0, "synchronous parity error" },
-
{ do_bad, SIGBUS, 0, "unknown 25" },
-
{ do_bad, SIGBUS, 0, "unknown 26" },
-
{ do_bad, SIGBUS, 0, "unknown 27" },
-
{ do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
-
{ do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
-
{ do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
-
{ do_bad, SIGBUS, 0, "synchronous parity error (translation table walk)" },
-
{ do_bad, SIGBUS, 0, "unknown 32" },
-
{ do_alignment_fault, SIGBUS, BUS_ADRALN, "alignment fault" },
-
{ do_bad, SIGBUS, 0, "unknown 34" },
-
{ do_bad, SIGBUS, 0, "unknown 35" },
-
{ do_bad, SIGBUS, 0, "unknown 36" },
-
{ do_bad, SIGBUS, 0, "unknown 37" },
-
{ do_bad, SIGBUS, 0, "unknown 38" },
-
{ do_bad, SIGBUS, 0, "unknown 39" },
-
{ do_bad, SIGBUS, 0, "unknown 40" },
-
{ do_bad, SIGBUS, 0, "unknown 41" },
-
{ do_bad, SIGBUS, 0, "unknown 42" },
-
{ do_bad, SIGBUS, 0, "unknown 43" },
-
{ do_bad, SIGBUS, 0, "unknown 44" },
-
{ do_bad, SIGBUS, 0, "unknown 45" },
-
{ do_bad, SIGBUS, 0, "unknown 46" },
-
{ do_bad, SIGBUS, 0, "unknown 47" },
-
{ do_bad, SIGBUS, 0, "TLB conflict abort" },
-
{ do_bad, SIGBUS, 0, "unknown 49" },
-
{ do_bad, SIGBUS, 0, "unknown 50" },
-
{ do_bad, SIGBUS, 0, "unknown 51" },
-
{ do_bad, SIGBUS, 0, "implementation fault (lockdown abort)" },
-
{ do_bad, SIGBUS, 0, "implementation fault (unsupported exclusive)" },
-
{ do_bad, SIGBUS, 0, "unknown 54" },
-
{ do_bad, SIGBUS, 0, "unknown 55" },
-
{ do_bad, SIGBUS, 0, "unknown 56" },
-
{ do_bad, SIGBUS, 0, "unknown 57" },
-
{ do_bad, SIGBUS, 0, "unknown 58" },
-
{ do_bad, SIGBUS, 0, "unknown 59" },
-
{ do_bad, SIGBUS, 0, "unknown 60" },
-
{ do_bad, SIGBUS, 0, "section domain fault" },
-
{ do_bad, SIGBUS, 0, "page domain fault" },
-
{ do_bad, SIGBUS, 0, "unknown 63" },
-
};
具体参见《ARM(V8) Architecture Reference Manual》中ESR_ELX的详解。
如果处理函数返回非0,会走到下面的默认处理方法:
-
info.si_signo = inf->sig;
-
info.si_errno = 0;
-
info.si_code = inf->code;
-
info.si_addr = (void __user *)addr;
-
arm64_notify_die("", regs, &info, esr);
其中触发data abt的地址会被记录在FAR_EL1中:
接下来在arm64_notify_die中,分别处理user和kernel部分:
()
-
void arm64_notify_die(const char *str, struct pt_regs *regs,
-
struct siginfo *info, int err)
-
{
-
if (user_mode(regs)) {
-
current->thread.fault_address = 0;
-
current->thread.fault_code = err;
-
force_sig_info(info->si_signo, info, current); // 发送signal到user space
-
} else {
-
die(str, regs, err); // 发生在kernel,直接call die
-
}
-
}
()
-
int
-
force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
-
{
-
unsigned long int flags;
-
int ret, blocked, ignored;
-
struct k_sigaction *action;
-
-
spin_lock_irqsave(&t->sighand->siglock, flags);
-
action = &t->sighand->action[sig-1];
-
ignored = action->sa.sa_handler == SIG_IGN;
-
blocked = sigismember(&t->blocked, sig);
-
if (blocked || ignored) {
-
action->sa.sa_handler = SIG_DFL;
-
if (blocked) {
-
sigdelset(&t->blocked, sig);
-
recalc_sigpending_and_wake(t);
-
}
-
}
-
if (action->sa.sa_handler == SIG_DFL)
-
t->signal->flags &= ~SIGNAL_UNKILLABLE;
-
ret = specific_send_sig_info(sig, info, t);
-
spin_unlock_irqrestore(&t->sighand->siglock, flags);
-
-
return ret;
-
}
()
-
static int
-
specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
-
{
-
return send_signal(sig, info, t, 0);
-
}
IRQ:
()
-
ENTRY(vectors)
-
ventry el1_sync_invalid // Synchronous EL1t
-
ventry el1_irq_invalid // IRQ EL1t
-
ventry el1_fiq_invalid // FIQ EL1t
-
ventry el1_error_invalid // Error EL1t
-
-
ventry el1_sync // Synchronous EL1h
-
ventry el1_irq // IRQ EL1h
-
ventry el1_fiq_invalid // FIQ EL1h
-
ventry el1_error_invalid // Error EL1h
-
-
ventry el0_sync // Synchronous 64-bit EL0
-
ventry el0_irq // IRQ 64-bit EL0
-
ventry el0_fiq_invalid // FIQ 64-bit EL0
-
ventry el0_error_invalid // Error 64-bit EL0
-
-
#ifdef CONFIG_COMPAT
-
ventry el0_sync_compat // Synchronous 32-bit EL0
-
ventry el0_irq_compat // IRQ 32-bit EL0
-
ventry el0_fiq_invalid_compat // FIQ 32-bit EL0
-
ventry el0_error_invalid_compat // Error 32-bit EL0
-
#else
-
ventry el0_sync_invalid // Synchronous 32-bit EL0
-
ventry el0_irq_invalid // IRQ 32-bit EL0
-
ventry el0_fiq_invalid // FIQ 32-bit EL0
-
ventry el0_error_invalid // Error 32-bit EL0
-
#endif
-
END(vectors)
()
-
el1_irq:
-
kernel_entry 1
-
enable_dbg
-
#ifdef CONFIG_TRACE_IRQFLAGS
-
bl trace_hardirqs_off
-
#endif
-
-
irq_handler
-
-
#ifdef CONFIG_PREEMPT
-
ldr w24, [tsk, #TI_PREEMPT] // get preempt count
-
cbnz w24, 1f // preempt count != 0
-
ldr x0, [tsk, #TI_FLAGS] // get flags
-
tbz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling?
-
bl el1_preempt
-
1:
-
#endif
-
#ifdef CONFIG_TRACE_IRQFLAGS
-
bl trace_hardirqs_on
-
#endif
-
kernel_exit 1
-
ENDPROC(el1_irq)
()
-
.macro irq_handler
-
ldr_l x1, handle_arch_irq
-
mov x0, sp
-
irq_stack_entry
-
blr x1
-
irq_stack_exit
-
.endm
()
-
static int __init __gic_init_bases(struct gic_chip_data *gic,
-
int irq_start,
-
struct fwnode_handle *handle)
-
{
-
char *name;
-
int i, ret;
-
-
if (WARN_ON(!gic || gic->domain))
-
return -EINVAL;
-
-
if (gic == &gic_data[0]) {
-
/*
-
* Initialize the CPU interface map to all CPUs.
-
* It will be refined as each CPU probes its ID.
-
* This is only necessary for the primary GIC.
-
*/
-
for (i = 0; i < NR_GIC_CPU_IF; i++)
-
gic_cpu_map[i] = 0xff;
-
#ifdef CONFIG_SMP
-
set_smp_cross_call(gic_raise_softirq);
-
#endif
-
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
-
"AP_IRQ_GIC_STARTING",
-
gic_starting_cpu, NULL);
-
set_handle_irq(gic_handle_irq);
-
if (static_key_true(&supports_deactivate))
-
pr_info("GIC: Using split EOI/Deactivate mode\n");
-
}
-
-
if (static_key_true(&supports_deactivate) && gic == &gic_data[0]) {
-
name = kasprintf(GFP_KERNEL, "GICv2");
-
gic_init_chip(gic, NULL, name, true);
-
} else {
-
name = kasprintf(GFP_KERNEL, "GIC-%d", (int)(gic-&gic_data[0]));
-
gic_init_chip(gic, NULL, name, false);
-
}
-
-
ret = gic_init_bases(gic, irq_start, handle);
-
if (ret)
-
kfree(name);
-
-
return ret;
-
}
()
-
static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
-
{
-
u32 irqstat, irqnr;
-
struct gic_chip_data *gic = &gic_data[0];
-
void __iomem *cpu_base = gic_data_cpu_base(gic);
-
-
do {
-
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
-
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
-
-
if (likely(irqnr > 15 && irqnr < 1020)) {
-
if (static_key_true(&supports_deactivate))
-
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
-
handle_domain_irq(gic->domain, irqnr, regs);
-
continue;
-
}
-
if (irqnr < 16) {
-
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
-
if (static_key_true(&supports_deactivate))
-
writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
-
#ifdef CONFIG_SMP
-
/*
-
* Ensure any shared data written by the CPU sending
-
* the IPI is read after we've read the ACK register
-
* on the GIC.
-
*
-
* Pairs with the write barrier in gic_raise_softirq
-
*/
-
smp_rmb();
-
handle_IPI(irqnr, regs);
-
#endif
-
continue;
-
}
-
break;
-
} while (1);
-
}
()
-
static inline int handle_domain_irq(struct irq_domain *domain,
-
unsigned int hwirq, struct pt_regs *regs)
-
{
-
return __handle_domain_irq(domain, hwirq, true, regs);
-
}
(
)
-
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
-
bool lookup, struct pt_regs *regs)
-
{
-
struct pt_regs *old_regs = set_irq_regs(regs);
-
unsigned int irq = hwirq;
-
int ret = 0;
-
-
irq_enter();
-
-
#ifdef CONFIG_IRQ_DOMAIN
-
if (lookup)
-
irq = irq_find_mapping(domain, hwirq);
-
#endif
-
-
/*
-
* Some hardware gives randomly wrong interrupts. Rather
-
* than crashing, do something sensible.
-
*/
-
if (unlikely(!irq || irq >= nr_irqs)) {
-
ack_bad_irq(irq);
-
ret = -EINVAL;
-
} else {
-
generic_handle_irq(irq);
-
}
-
-
irq_exit();
-
set_irq_regs(old_regs);
-
return ret;
-
}
()
-
int generic_handle_irq(unsigned int irq)
-
{
-
struct irq_desc *desc = irq_to_desc(irq);
-
-
if (!desc)
-
return -EINVAL;
-
generic_handle_irq_desc(desc);
-
return 0;
-
}
()
-
static inline void generic_handle_irq_desc(struct irq_desc *desc)
-
{
-
desc->handle_irq(desc);
-
}
阅读(2983) | 评论(0) | 转发(0) |