Chinaunix首页 | 论坛 | 博客
  • 博客访问: 169000
  • 博文数量: 60
  • 博客积分: 15
  • 博客等级: 民兵
  • 技术积分: 638
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-26 10:59
个人简介

喜欢coding,因为那是一件伟大的事情,是将无生命的IC赋予灵魂的过程,让我拥有了和上帝一样的成就感。(w1c2g3@163.com)

文章分类

全部博文(60)

文章存档

2017年(7)

2016年(41)

2015年(1)

2014年(4)

2013年(7)

我的朋友

分类: 嵌入式

2017-01-30 22:54:48

Data abort:


()
  1. ENTRY(vectors)
  2.     ventry    el1_sync_invalid        // Synchronous EL1t
  3.     ventry    el1_irq_invalid            // IRQ EL1t
  4.     ventry    el1_fiq_invalid            // FIQ EL1t
  5.     ventry    el1_error_invalid        // Error EL1t

  6.     ventry    el1_sync            // Synchronous EL1h
  7.     ventry    el1_irq                // IRQ EL1h
  8.     ventry    el1_fiq_invalid            // FIQ EL1h
  9.     ventry    el1_error_invalid        // Error EL1h

  10.     ventry    el0_sync            // Synchronous 64-bit EL0
  11.     ventry    el0_irq                // IRQ 64-bit EL0
  12.     ventry    el0_fiq_invalid            // FIQ 64-bit EL0
  13.     ventry    el0_error_invalid        // Error 64-bit EL0

  14. #ifdef CONFIG_COMPAT
  15.     ventry    el0_sync_compat            // Synchronous 32-bit EL0
  16.     ventry    el0_irq_compat            // IRQ 32-bit EL0
  17.     ventry    el0_fiq_invalid_compat        // FIQ 32-bit EL0
  18.     ventry    el0_error_invalid_compat    // Error 32-bit EL0
  19. #else
  20.     ventry    el0_sync_invalid        // Synchronous 32-bit EL0
  21.     ventry    el0_irq_invalid            // IRQ 32-bit EL0
  22.     ventry    el0_fiq_invalid            // FIQ 32-bit EL0
  23.     ventry    el0_error_invalid        // Error 32-bit EL0
  24. #endif
  25. END(vectors)
()
  1. el1_sync:
  2.     kernel_entry 1
  3.     mrs    x1, esr_el1            // read the syndrome register
  4.     lsr    x24, x1, #ESR_ELx_EC_SHIFT    // exception class
  5.     cmp    x24, #ESR_ELx_EC_DABT_CUR    // data abort in EL1
  6.     b.eq    el1_da
  7.     cmp    x24, #ESR_ELx_EC_IABT_CUR    // instruction abort in EL1
  8.     b.eq    el1_ia
  9.     cmp    x24, #ESR_ELx_EC_SYS64        // configurable trap
  10.     b.eq    el1_undef
  11.     cmp    x24, #ESR_ELx_EC_SP_ALIGN    // stack alignment exception
  12.     b.eq    el1_sp_pc
  13.     cmp    x24, #ESR_ELx_EC_PC_ALIGN    // pc alignment exception
  14.     b.eq    el1_sp_pc
  15.     cmp    x24, #ESR_ELx_EC_UNKNOWN    // unknown exception in EL1
  16.     b.eq    el1_undef
  17.     cmp    x24, #ESR_ELx_EC_BREAKPT_CUR    // debug exception in EL1
  18.     b.ge    el1_dbg
  19.     b    el1_inv
进入el1_sync后,将esr_el1读取到X1,然后取esr_el1的EC部分,判断异常类型:
()

  1. #define ESR_ELx_EC_UNKNOWN    (0x00)
  2. #define ESR_ELx_EC_WFx        (0x01)
  3. /* Unallocated EC: 0x02 */
  4. #define ESR_ELx_EC_CP15_32    (0x03)
  5. #define ESR_ELx_EC_CP15_64    (0x04)
  6. #define ESR_ELx_EC_CP14_MR    (0x05)
  7. #define ESR_ELx_EC_CP14_LS    (0x06)
  8. #define ESR_ELx_EC_FP_ASIMD    (0x07)
  9. #define ESR_ELx_EC_CP10_ID    (0x08)
  10. /* Unallocated EC: 0x09 - 0x0B */
  11. #define ESR_ELx_EC_CP14_64    (0x0C)
  12. /* Unallocated EC: 0x0d */
  13. #define ESR_ELx_EC_ILL        (0x0E)
  14. /* Unallocated EC: 0x0F - 0x10 */
  15. #define ESR_ELx_EC_SVC32    (0x11)
  16. #define ESR_ELx_EC_HVC32    (0x12)
  17. #define ESR_ELx_EC_SMC32    (0x13)
  18. /* Unallocated EC: 0x14 */
  19. #define ESR_ELx_EC_SVC64    (0x15)
  20. #define ESR_ELx_EC_HVC64    (0x16)
  21. #define ESR_ELx_EC_SMC64    (0x17)
  22. #define ESR_ELx_EC_SYS64    (0x18)
  23. /* Unallocated EC: 0x19 - 0x1E */
  24. #define ESR_ELx_EC_IMP_DEF    (0x1f)
  25. #define ESR_ELx_EC_IABT_LOW    (0x20)
  26. #define ESR_ELx_EC_IABT_CUR    (0x21)
  27. #define ESR_ELx_EC_PC_ALIGN    (0x22)
  28. /* Unallocated EC: 0x23 */
  29. #define ESR_ELx_EC_DABT_LOW    (0x24)
  30. #define ESR_ELx_EC_DABT_CUR    (0x25)
  31. #define ESR_ELx_EC_SP_ALIGN    (0x26)
  32. /* Unallocated EC: 0x27 */
  33. #define ESR_ELx_EC_FP_EXC32    (0x28)
  34. /* Unallocated EC: 0x29 - 0x2B */
  35. #define ESR_ELx_EC_FP_EXC64    (0x2C)
  36. /* Unallocated EC: 0x2D - 0x2E */
  37. #define ESR_ELx_EC_SERROR    (0x2F)
  38. #define ESR_ELx_EC_BREAKPT_LOW    (0x30)
  39. #define ESR_ELx_EC_BREAKPT_CUR    (0x31)
  40. #define ESR_ELx_EC_SOFTSTP_LOW    (0x32)
  41. #define ESR_ELx_EC_SOFTSTP_CUR    (0x33)
  42. #define ESR_ELx_EC_WATCHPT_LOW    (0x34)
  43. #define ESR_ELx_EC_WATCHPT_CUR    (0x35)
  44. /* Unallocated EC: 0x36 - 0x37 */
  45. #define ESR_ELx_EC_BKPT32    (0x38)
  46. /* Unallocated EC: 0x39 */
  47. #define ESR_ELx_EC_VECTOR32    (0x3A)
  48. /* Unallocted EC: 0x3B */
  49. #define ESR_ELx_EC_BRK64    (0x3C)
  50. /* Unallocated EC: 0x3D - 0x3F */
  51. #define ESR_ELx_EC_MAX        (0x3F)
()
  1. el1_da:
  2.     /*
  3.      * Data abort handling
  4.      */
  5.     mrs    x0, far_el1
  6.     enable_dbg
  7.     // re-enable interrupts if they were enabled in the aborted context
  8.     tbnz    x23, #7, 1f            // PSR_I_BIT
  9.     enable_irq
  10. 1:
  11.     mov    x2, sp                // struct pt_regs
  12.     bl    do_mem_abort

  13.     // disable interrupts before pulling preserved data off the stack
  14.     disable_irq
  15.     kernel_exit 1
进入C code,其中入参X0=far_el1, X1=esr_el1, X2=sp (---> pt_regs)
()
  1. asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
  2.                      struct pt_regs *regs)
  3. {
  4.     const struct fault_info *inf = fault_info + (esr & 63);
  5.     struct siginfo info;

  6.     if (!inf->fn(addr, esr, regs))
  7.         return;

  8.     pr_alert("Unhandled fault: %s (0x%08x) at 0x%016lx\n",
  9.          inf->name, esr, addr);

  10.     info.si_signo = inf->sig;
  11.     info.si_errno = 0;
  12.     info.si_code = inf->code;
  13.     info.si_addr = (void __user *)addr;
  14.     arm64_notify_die("", regs, &info, esr);
  15. }
通过esr_el1的低6bits和fault_info数组,确定具体处理函数:
()
  1. static const struct fault_info {
  2.     int    (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs);
  3.     int    sig;
  4.     int    code;
  5.     const char *name;
  6. } fault_info[] = {
  7.     { do_bad,        SIGBUS, 0,        "ttbr address size fault"    },
  8.     { do_bad,        SIGBUS, 0,        "level 1 address size fault"    },
  9.     { do_bad,        SIGBUS, 0,        "level 2 address size fault"    },
  10.     { do_bad,        SIGBUS, 0,        "level 3 address size fault"    },
  11.     { do_translation_fault,    SIGSEGV, SEGV_MAPERR,    "level 0 translation fault"    },
  12.     { do_translation_fault,    SIGSEGV, SEGV_MAPERR,    "level 1 translation fault"    },
  13.     { do_translation_fault,    SIGSEGV, SEGV_MAPERR,    "level 2 translation fault"    },
  14.     { do_page_fault,    SIGSEGV, SEGV_MAPERR,    "level 3 translation fault"    },
  15.     { do_bad,        SIGBUS, 0,        "unknown 8"            },
  16.     { do_page_fault,    SIGSEGV, SEGV_ACCERR,    "level 1 access flag fault"    },
  17.     { do_page_fault,    SIGSEGV, SEGV_ACCERR,    "level 2 access flag fault"    },
  18.     { do_page_fault,    SIGSEGV, SEGV_ACCERR,    "level 3 access flag fault"    },
  19.     { do_bad,        SIGBUS, 0,        "unknown 12"            },
  20.     { do_page_fault,    SIGSEGV, SEGV_ACCERR,    "level 1 permission fault"    },
  21.     { do_page_fault,    SIGSEGV, SEGV_ACCERR,    "level 2 permission fault"    },
  22.     { do_page_fault,    SIGSEGV, SEGV_ACCERR,    "level 3 permission fault"    },
  23.     { do_bad,        SIGBUS, 0,        "synchronous external abort"    },
  24.     { do_bad,        SIGBUS, 0,        "unknown 17"            },
  25.     { do_bad,        SIGBUS, 0,        "unknown 18"            },
  26.     { do_bad,        SIGBUS, 0,        "unknown 19"            },
  27.     { do_bad,        SIGBUS, 0,        "synchronous abort (translation table walk)" },
  28.     { do_bad,        SIGBUS, 0,        "synchronous abort (translation table walk)" },
  29.     { do_bad,        SIGBUS, 0,        "synchronous abort (translation table walk)" },
  30.     { do_bad,        SIGBUS, 0,        "synchronous abort (translation table walk)" },
  31.     { do_bad,        SIGBUS, 0,        "synchronous parity error"    },
  32.     { do_bad,        SIGBUS, 0,        "unknown 25"            },
  33.     { do_bad,        SIGBUS, 0,        "unknown 26"            },
  34.     { do_bad,        SIGBUS, 0,        "unknown 27"            },
  35.     { do_bad,        SIGBUS, 0,        "synchronous parity error (translation table walk)" },
  36.     { do_bad,        SIGBUS, 0,        "synchronous parity error (translation table walk)" },
  37.     { do_bad,        SIGBUS, 0,        "synchronous parity error (translation table walk)" },
  38.     { do_bad,        SIGBUS, 0,        "synchronous parity error (translation table walk)" },
  39.     { do_bad,        SIGBUS, 0,        "unknown 32"            },
  40.     { do_alignment_fault,    SIGBUS, BUS_ADRALN,    "alignment fault"        },
  41.     { do_bad,        SIGBUS, 0,        "unknown 34"            },
  42.     { do_bad,        SIGBUS, 0,        "unknown 35"            },
  43.     { do_bad,        SIGBUS, 0,        "unknown 36"            },
  44.     { do_bad,        SIGBUS, 0,        "unknown 37"            },
  45.     { do_bad,        SIGBUS, 0,        "unknown 38"            },
  46.     { do_bad,        SIGBUS, 0,        "unknown 39"            },
  47.     { do_bad,        SIGBUS, 0,        "unknown 40"            },
  48.     { do_bad,        SIGBUS, 0,        "unknown 41"            },
  49.     { do_bad,        SIGBUS, 0,        "unknown 42"            },
  50.     { do_bad,        SIGBUS, 0,        "unknown 43"            },
  51.     { do_bad,        SIGBUS, 0,        "unknown 44"            },
  52.     { do_bad,        SIGBUS, 0,        "unknown 45"            },
  53.     { do_bad,        SIGBUS, 0,        "unknown 46"            },
  54.     { do_bad,        SIGBUS, 0,        "unknown 47"            },
  55.     { do_bad,        SIGBUS, 0,        "TLB conflict abort"        },
  56.     { do_bad,        SIGBUS, 0,        "unknown 49"            },
  57.     { do_bad,        SIGBUS, 0,        "unknown 50"            },
  58.     { do_bad,        SIGBUS, 0,        "unknown 51"            },
  59.     { do_bad,        SIGBUS, 0,        "implementation fault (lockdown abort)" },
  60.     { do_bad,        SIGBUS, 0,        "implementation fault (unsupported exclusive)" },
  61.     { do_bad,        SIGBUS, 0,        "unknown 54"            },
  62.     { do_bad,        SIGBUS, 0,        "unknown 55"            },
  63.     { do_bad,        SIGBUS, 0,        "unknown 56"            },
  64.     { do_bad,        SIGBUS, 0,        "unknown 57"            },
  65.     { do_bad,        SIGBUS, 0,        "unknown 58"             },
  66.     { do_bad,        SIGBUS, 0,        "unknown 59"            },
  67.     { do_bad,        SIGBUS, 0,        "unknown 60"            },
  68.     { do_bad,        SIGBUS, 0,        "section domain fault"        },
  69.     { do_bad,        SIGBUS, 0,        "page domain fault"        },
  70.     { do_bad,        SIGBUS, 0,        "unknown 63"            },
  71. };
具体参见《ARM(V8) Architecture Reference Manual》中ESR_ELX的详解。
如果处理函数返回非0,会走到下面的默认处理方法:
  1.     info.si_signo = inf->sig;
  2.     info.si_errno = 0;
  3.     info.si_code = inf->code;
  4.     info.si_addr = (void __user *)addr;
  5.     arm64_notify_die("", regs, &info, esr);
其中触发data abt的地址会被记录在FAR_EL1中:

接下来在arm64_notify_die中,分别处理user和kernel部分:
()
  1. void arm64_notify_die(const char *str, struct pt_regs *regs,
  2.          struct siginfo *info, int err)
  3. {
  4.     if (user_mode(regs)) {
  5.         current->thread.fault_address = 0;
  6.         current->thread.fault_code = err;
  7.         force_sig_info(info->si_signo, info, current); // 发送signal到user space
  8.     } else {
  9.         die(str, regs, err);  // 发生在kernel,直接call die 
  10.     }
  11. }
()
  1. int
  2. force_sig_info(int sig, struct siginfo *info, struct task_struct *t)
  3. {
  4.     unsigned long int flags;
  5.     int ret, blocked, ignored;
  6.     struct k_sigaction *action;

  7.     spin_lock_irqsave(&t->sighand->siglock, flags);
  8.     action = &t->sighand->action[sig-1];
  9.     ignored = action->sa.sa_handler == SIG_IGN;
  10.     blocked = sigismember(&t->blocked, sig);
  11.     if (blocked || ignored) {
  12.         action->sa.sa_handler = SIG_DFL;
  13.         if (blocked) {
  14.             sigdelset(&t->blocked, sig);
  15.             recalc_sigpending_and_wake(t);
  16.         }
  17.     }
  18.     if (action->sa.sa_handler == SIG_DFL)
  19.         t->signal->flags &= ~SIGNAL_UNKILLABLE;
  20.     ret = specific_send_sig_info(sig, info, t);
  21.     spin_unlock_irqrestore(&t->sighand->siglock, flags);

  22.     return ret;
  23. }
()
  1. static int
  2. specific_send_sig_info(int sig, struct siginfo *info, struct task_struct *t)
  3. {
  4.     return send_signal(sig, info, t, 0);
  5. }

IRQ:


()
  1. ENTRY(vectors)
  2.     ventry    el1_sync_invalid        // Synchronous EL1t
  3.     ventry    el1_irq_invalid            // IRQ EL1t
  4.     ventry    el1_fiq_invalid            // FIQ EL1t
  5.     ventry    el1_error_invalid        // Error EL1t

  6.     ventry    el1_sync            // Synchronous EL1h
  7.     ventry    el1_irq                // IRQ EL1h
  8.     ventry    el1_fiq_invalid            // FIQ EL1h
  9.     ventry    el1_error_invalid        // Error EL1h

  10.     ventry    el0_sync            // Synchronous 64-bit EL0
  11.     ventry    el0_irq                // IRQ 64-bit EL0
  12.     ventry    el0_fiq_invalid            // FIQ 64-bit EL0
  13.     ventry    el0_error_invalid        // Error 64-bit EL0

  14. #ifdef CONFIG_COMPAT
  15.     ventry    el0_sync_compat            // Synchronous 32-bit EL0
  16.     ventry    el0_irq_compat            // IRQ 32-bit EL0
  17.     ventry    el0_fiq_invalid_compat        // FIQ 32-bit EL0
  18.     ventry    el0_error_invalid_compat    // Error 32-bit EL0
  19. #else
  20.     ventry    el0_sync_invalid        // Synchronous 32-bit EL0
  21.     ventry    el0_irq_invalid            // IRQ 32-bit EL0
  22.     ventry    el0_fiq_invalid            // FIQ 32-bit EL0
  23.     ventry    el0_error_invalid        // Error 32-bit EL0
  24. #endif
  25. END(vectors)
()
  1. el1_irq:
  2.     kernel_entry 1
  3.     enable_dbg
  4. #ifdef CONFIG_TRACE_IRQFLAGS
  5.     bl    trace_hardirqs_off
  6. #endif

  7.     irq_handler

  8. #ifdef CONFIG_PREEMPT
  9.     ldr    w24, [tsk, #TI_PREEMPT]        // get preempt count
  10.     cbnz    w24, 1f                // preempt count != 0
  11.     ldr    x0, [tsk, #TI_FLAGS]        // get flags
  12.     tbz    x0, #TIF_NEED_RESCHED, 1f    // needs rescheduling?
  13.     bl    el1_preempt
  14. 1:
  15. #endif
  16. #ifdef CONFIG_TRACE_IRQFLAGS
  17.     bl    trace_hardirqs_on
  18. #endif
  19.     kernel_exit 1
  20. ENDPROC(el1_irq)
()
  1. .macro    irq_handler
  2.     ldr_l    x1, handle_arch_irq
  3.     mov    x0, sp
  4.     irq_stack_entry
  5.     blr    x1
  6.     irq_stack_exit
  7.     .endm
()
  1. static int __init __gic_init_bases(struct gic_chip_data *gic,
  2.                  int irq_start,
  3.                  struct fwnode_handle *handle)
  4. {
  5.     char *name;
  6.     int i, ret;

  7.     if (WARN_ON(!gic || gic->domain))
  8.         return -EINVAL;

  9.     if (gic == &gic_data[0]) {
  10.         /*
  11.          * Initialize the CPU interface map to all CPUs.
  12.          * It will be refined as each CPU probes its ID.
  13.          * This is only necessary for the primary GIC.
  14.          */
  15.         for (i = 0; i < NR_GIC_CPU_IF; i++)
  16.             gic_cpu_map[i] = 0xff;
  17. #ifdef CONFIG_SMP
  18.         set_smp_cross_call(gic_raise_softirq);
  19. #endif
  20.         cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
  21.                      "AP_IRQ_GIC_STARTING",
  22.                      gic_starting_cpu, NULL);
  23.         set_handle_irq(gic_handle_irq);
  24.         if (static_key_true(&supports_deactivate))
  25.             pr_info("GIC: Using split EOI/Deactivate mode\n");
  26.     }

  27.     if (static_key_true(&supports_deactivate) && gic == &gic_data[0]) {
  28.         name = kasprintf(GFP_KERNEL, "GICv2");
  29.         gic_init_chip(gic, NULL, name, true);
  30.     } else {
  31.         name = kasprintf(GFP_KERNEL, "GIC-%d", (int)(gic-&gic_data[0]));
  32.         gic_init_chip(gic, NULL, name, false);
  33.     }

  34.     ret = gic_init_bases(gic, irq_start, handle);
  35.     if (ret)
  36.         kfree(name);

  37.     return ret;
  38. }
()
  1. static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
  2. {
  3.     u32 irqstat, irqnr;
  4.     struct gic_chip_data *gic = &gic_data[0];
  5.     void __iomem *cpu_base = gic_data_cpu_base(gic);

  6.     do {
  7.         irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
  8.         irqnr = irqstat & GICC_IAR_INT_ID_MASK;

  9.         if (likely(irqnr > 15 && irqnr < 1020)) {
  10.             if (static_key_true(&supports_deactivate))
  11.                 writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
  12.             handle_domain_irq(gic->domain, irqnr, regs);
  13.             continue;
  14.         }
  15.         if (irqnr < 16) {
  16.             writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
  17.             if (static_key_true(&supports_deactivate))
  18.                 writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
  19. #ifdef CONFIG_SMP
  20.             /*
  21.              * Ensure any shared data written by the CPU sending
  22.              * the IPI is read after we've read the ACK register
  23.              * on the GIC.
  24.              *
  25.              * Pairs with the write barrier in gic_raise_softirq
  26.              */
  27.             smp_rmb();
  28.             handle_IPI(irqnr, regs);
  29. #endif
  30.             continue;
  31.         }
  32.         break;
  33.     } while (1);
  34. }
()
  1. static inline int handle_domain_irq(struct irq_domain *domain,
  2.                  unsigned int hwirq, struct pt_regs *regs)
  3. {
  4.     return __handle_domain_irq(domain, hwirq, true, regs);
  5. }
()
  1. int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
  2.             bool lookup, struct pt_regs *regs)
  3. {
  4.     struct pt_regs *old_regs = set_irq_regs(regs);
  5.     unsigned int irq = hwirq;
  6.     int ret = 0;

  7.     irq_enter();

  8. #ifdef CONFIG_IRQ_DOMAIN
  9.     if (lookup)
  10.         irq = irq_find_mapping(domain, hwirq);
  11. #endif

  12.     /*
  13.      * Some hardware gives randomly wrong interrupts. Rather
  14.      * than crashing, do something sensible.
  15.      */
  16.     if (unlikely(!irq || irq >= nr_irqs)) {
  17.         ack_bad_irq(irq);
  18.         ret = -EINVAL;
  19.     } else {
  20.         generic_handle_irq(irq);
  21.     }

  22.     irq_exit();
  23.     set_irq_regs(old_regs);
  24.     return ret;
  25. }
()
  1. int generic_handle_irq(unsigned int irq)
  2. {
  3.     struct irq_desc *desc = irq_to_desc(irq);

  4.     if (!desc)
  5.         return -EINVAL;
  6.     generic_handle_irq_desc(desc);
  7.     return 0;
  8. }
()
  1. static inline void generic_handle_irq_desc(struct irq_desc *desc)
  2. {
  3.     desc->handle_irq(desc);
  4. }














阅读(2952) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~