中断为啥跳到startup_stm32f40xx.s里面的而不是stm32f4xx_it.c的大神们帮帮忙
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了中断为啥跳到startup_stm32f40xx.s里面的而不是stm32f4xx_it.c的大神们帮帮忙相关的知识,希望对你有一定的参考价值。
用IAR开发,中断为什么跳到startup_stm32f40xx.s里面定义的中断函数,,而不是stm32f4xx_it.c里面定义的中断函数?根据IAR编译器文档,startup_stm32f40xx.s里面定义的中断函数都是PUBWEAK,,意思是如果另外定义了同名函数,则PUBWEAK属性的函数失效。但是st例程里面的是正常的,如果在stm32f4xx_it.c里面定义了中断函数,则不会跳到startup_stm32f40xx.s里面定义的中断函数。具体要设置哪里呢?以前使用MDK也有这样的问题。我的解决方法是,将startup_stm32f40xx.s里面除reset_Handler之外的所有中断函数删除,,全部在stm32f4xx_it.c里面定义。。。。但心里总有疑问,不爽啊,,找了网上别人遇到的,都没有解决这个问题。。
反编译下你的二进制文件,看看链接进去的是哪个还有就是看看代码,看看你写的中断向量地址那里给的是哪个文件里的中断处理函数 参考技术A 反编译下你的
二进制文件
,看看链接进去的是哪个还有就是看看代码,看看你写的中断向量地址那里给的是哪个文件里的中断处理函数
查看原帖>>
中断处理流程梳理
在之前的ARMv8-A的异常文章中提到,ARMv8-A将中断也当做一种异常,中断分为IRQ和FIQ
假设当前在EL0运行一个64位的应用程序,触发了一个EL0的IRQ中断,则处理器会做如下的操作
- 将CPU的状态PSTATE保存到SPSR_EL1寄存器中,PC保存到ELR_EL1寄存器中
- 跳到异常的处理函数处
则就会跳到ARM64对应的异常向量表
/*
* Exception vectors.
*/
.pushsection ".entry.text", "ax"
.align 11
ENTRY(vectors)
kernel_ventry 1, sync_invalid // Synchronous EL1t
kernel_ventry 1, irq_invalid // IRQ EL1t
kernel_ventry 1, fiq_invalid // FIQ EL1t
kernel_ventry 1, error_invalid // Error EL1t
kernel_ventry 1, sync // Synchronous EL1h
kernel_ventry 1, irq // IRQ EL1h
kernel_ventry 1, fiq_invalid // FIQ EL1h
kernel_ventry 1, error // Error EL1h
kernel_ventry 0, sync // Synchronous 64-bit EL0
kernel_ventry 0, irq // IRQ 64-bit EL0
kernel_ventry 0, fiq_invalid // FIQ 64-bit EL0
kernel_ventry 0, error // Error 64-bit EL0
#ifdef CONFIG_COMPAT
kernel_ventry 0, sync_compat, 32 // Synchronous 32-bit EL0
kernel_ventry 0, irq_compat, 32 // IRQ 32-bit EL0
kernel_ventry 0, fiq_invalid_compat, 32 // FIQ 32-bit EL0
kernel_ventry 0, error_compat, 32 // Error 32-bit EL0
#else
kernel_ventry 0, sync_invalid, 32 // Synchronous 32-bit EL0
kernel_ventry 0, irq_invalid, 32 // IRQ 32-bit EL0
kernel_ventry 0, fiq_invalid, 32 // FIQ 32-bit EL0
kernel_ventry 0, error_invalid, 32 // Error 32-bit EL0
#endif
END(vectors)
因为是EL0触发的异常,而且异常状态是IRQ,则会根据异常向量表的基址跳大EL0_irq处, kernel_ventry 0, irq // IRQ 64-bit EL0
el0_irq:
kernel_entry 0
el0_irq_naked:
enable_da_f
irq_handler
b ret_to_user
ENDPROC(el0_irq)
kernel_entry 0,其中kernel_entry是一个宏,此宏会保存当前的线程,也就是进程的寄存器
stp x0, x1, [sp, #16 * 0]
stp x2, x3, [sp, #16 * 1]
stp x4, x5, [sp, #16 * 2]
stp x6, x7, [sp, #16 * 3]
stp x8, x9, [sp, #16 * 4]
stp x10, x11, [sp, #16 * 5]
stp x12, x13, [sp, #16 * 6]
stp x14, x15, [sp, #16 * 7]
stp x16, x17, [sp, #16 * 8]
stp x18, x19, [sp, #16 * 9]
stp x20, x21, [sp, #16 * 10]
stp x22, x23, [sp, #16 * 11]
stp x24, x25, [sp, #16 * 12]
stp x26, x27, [sp, #16 * 13]
stp x28, x29, [sp, #16 * 14]
enable_da_f 这个在ARM处理器状态由描述,DAIF。这里是关闭IRQ中断
/* IRQ is the lowest priority flag, unconditionally unmask the rest. */
.macro enable_da_f
msr daifclr, #(8 | 4 | 1)
.endm
- 跳转到irq_handler去处理中断
- 当中断处理完毕后,就会通过ret_to_user返回到用户空间
/*
* Interrupt handling.
*/
.macro irq_handler
ldr_l x1, handle_arch_irq
mov x0, sp
irq_stack_entry
blr x1
irq_stack_exit
.endm
.text
- 将handle_arch_irq设置到x1, 而handle_arch_irq就是上一篇在gic驱动中设置的。set_handle_irq(gic_handle_irq);
- 从进程的内核栈切换到中断栈
.macro irq_stack_entry
mov x19, sp // preserve the original sp
/*
* Compare sp with the base of the task stack.
* If the top ~(THREAD_SIZE - 1) bits match, we are on a task stack,
* and should switch to the irq stack.
*/
ldr x25, [tsk, TSK_STACK]
eor x25, x25, x19
and x25, x25, #~(THREAD_SIZE - 1)
cbnz x25, 9998f
ldr_this_cpu x25, irq_stack_ptr, x26
mov x26, #IRQ_STACK_SIZE
add x26, x25, x26
/* switch to the irq stack */
mov sp, x26
9998:
.endm
其中irq_stack_ptr就是中断栈,每一个cpu都会存在一个中断栈的。
DEFINE_PER_CPU_ALIGNED(unsigned long [IRQ_STACK_SIZE/sizeof(long)], irq_stack);
static void init_irq_stacks(void)
int cpu;
for_each_possible_cpu(cpu)
per_cpu(irq_stack_ptr, cpu) = per_cpu(irq_stack, cpu);
当切换到中断栈之后,则会跳到handle_arch_irq,则就会跳到之前设置的gic_handle_irq中。
static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
u32 irqnr;
do
irqnr = gic_read_iar();
if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192)
int err;
uncached_logk(LOGK_IRQ, (void *)(uintptr_t)irqnr);
if (static_branch_likely(&supports_deactivate_key))
gic_write_eoir(irqnr);
else
isb();
err = handle_domain_irq(gic_data.domain, irqnr, regs); //处理中断
if (err)
WARN_ONCE(true, "Unexpected interrupt received!\\n");
if (static_branch_likely(&supports_deactivate_key))
if (irqnr < 8192)
gic_write_dir(irqnr);
else
gic_write_eoir(irqnr);
continue;
假设当前的中断类型是SPI,则会进入到hanle_domian_irq中去
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;
#endif
- irq_enter则是代表进入中断上下文
- 然后根据hwirq和domain,其中此domain是root domain,则寻找到hwirq对应的softirq
- 如果此softirq 属于非法irq,则发回bad ack
- 否则调用generic_handle_irq函数
- 当处理完毕后,会退出中断上下文
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;
根据softirq 获取此中断的中断描述符,调用generic_handle_irq_desc去处理中断,后面会涉及到irq domain的知识了。后面详细说
以上是关于中断为啥跳到startup_stm32f40xx.s里面的而不是stm32f4xx_it.c的大神们帮帮忙的主要内容,如果未能解决你的问题,请参考以下文章
stm32启动文件ld md hd cl vl xl分析及选择