Chinaunix首页 | 论坛 | 博客
  • 博客访问: 134819
  • 博文数量: 17
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 40
  • 用 户 组: 普通用户
  • 注册时间: 2018-08-08 00:05
文章分类
文章存档

2020年(4)

2019年(10)

2018年(3)

我的朋友

分类: LINUX

2019-10-23 16:06:48

 entry-armv.s中
当ARM处理器发生异常(中断是一种异常)时,会跳转到异常向量表(起始地址为0xFFFF_0000或 0x0000_0000)。 如3.2节中所述,在中断机制的初始化过程中,把在arch/arm/kernel/entry-armv.S中的异常向量表及其处理程序的stub重定 位到了0xFFFF_0000处,这样才使得ARM处理器能够正常处理异常,而且事实上执行的就是entry-armv.S中的异常处理程序

.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
异常向量表:
@下面这些才是最初的入口点,__vector_start和__vector_end之间的代码会被移动到CONFIG_VECTORS_BASE开始的地方,例如0xc000000
 .globl __vectors_start
__vectors_start:
 swi SYS_ERROR0                                      @Reset 
 b vector_und + stubs_offset
 ldr pc, .LCvswi + stubs_offset                      @swi instructionSWI指令用于产生软件中断,从而实现从用户模式到管理模式的变换
 b vector_pabt + stubs_offset
 b vector_dabt + stubs_offset
 b vector_addrexcptn + stubs_offset
 b vector_irq + stubs_offset              @irq中断入口
 b vector_fiq + stubs_offset
 .globl __vectors_end
__vectors_end:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
__stubs_start__stubs_end之间,实际上就是异常处理程序的入口
 .globl __stubs_start
__stubs_start:
/*
 * Interrupt dispatcher
 */
 vector_stub irq, IRQ_MODE, 4                       @此处便是vector_irq的声明调用
 .long __irq_usr   @  0  (USR_26 / USR_32)    用户模式
 .long __irq_invalid   @  1  (FIQ_26 / FIQ_32)
 .long __irq_invalid   @  2  (IRQ_26 / IRQ_32)
 .long __irq_svc   @  3  (SVC_26 / SVC_32)  系统模式
 .long __irq_invalid   @  4
 .long __irq_invalid   @  5
 .long __irq_invalid   @  6
 .long __irq_invalid   @  7
 .long __irq_invalid   @  8
 .long __irq_invalid   @  9
 .long __irq_invalid   @  a
 .long __irq_invalid   @  b
 .long __irq_invalid   @  c
 .long __irq_invalid   @  d
 .long __irq_invalid   @  e
 .long __irq_invalid   @  f
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
 * Vector stubs.
 *
 * This code is copied to 0xffff0200 so we can use branches in the
 * vectors, rather than ldr's.  Note that this code must not
 * exceed 0x300 bytes.
 *
 * Common stub entry macro:
 *   Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 *
 * SP points to a minimal amount of processor-private memory, the address
 * of which is copied into r0 for the mode specific abort handler.
 */
 .macro vector_stub, name, mode, correction=0        @看看 vector_stub irq, IRQ_MODE, 4 是如何变成vector_irq的。
 .align 5
vector_\name:
 .if \correction
 sub lr, lr, #\correction   @修正返回地址,如果必要的话
 .endif
 @
 @ Save r0, lr_ (parent PC) and spsr_
 @ (parent CPSR)
 @
 stmia sp, {r0, lr}  @ save r0, lr
 mrs lr, spsr
 str lr, [sp, #8]  @ save spsr
 @
 @ Prepare for SVC32 mode.  IRQs remain disabled.
 @
 mrs r0, cpsr
 eor r0, r0, #(\mode ^ SVC_MODE)
 msr spsr_cxsf, r0
 @
 @ the branch table must immediately follow this code
 @
 and lr, lr, #0x0f
 mov r0, sp
 ldr lr, [pc, lr, lsl #2]
 movs pc, lr   @ branch to handler in SVC mode

ENDPROC(vector_\name)
 .endm
//如果中断以前是svc模式,就会跳到__irq_svc 
http://blog.chinaunix.net/uid-12567959-id-160972.html 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 .align 5
__irq_svc:
 svc_entry
#ifdef CONFIG_TRACE_IRQFLAGS
 bl trace_hardirqs_off
#endif
#ifdef CONFIG_PREEMPT
 get_thread_info tsk
 ldr r8, [tsk, #TI_PREEMPT]  @ get preempt count
 add r7, r8, #1   @ increment it
 str r7, [tsk, #TI_PREEMPT]
#endif
 irq_handler        @调用中断处理程序
#ifdef CONFIG_PREEMPT
 str r8, [tsk, #TI_PREEMPT]  @ restore preempt count
 ldr r0, [tsk, #TI_FLAGS]  @ get flags
 teq r8, #0    @ if preempt count != 0
 movne r0, #0    @ force flags to 0
 tst r0, #_TIF_NEED_RESCHED
 blne svc_preempt
#endif
 ldr r0, [sp, #S_PSR]  @ irqs are already disabled
 msr spsr_cxsf, r0
#ifdef CONFIG_TRACE_IRQFLAGS
 tst r0, #PSR_I_BIT
 bleq trace_hardirqs_on
#endif
 ldmia sp, {r0 - pc}^   @ load r0 - pc, cpsr
ENDPROC(__irq_svc)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@usr模式中断入口
 .align 5
__irq_usr:
 usr_entry
 kuser_cmpxchg_check
#ifdef CONFIG_TRACE_IRQFLAGS
 bl trace_hardirqs_off
#endif
 get_thread_info tsk
#ifdef CONFIG_PREEMPT
 ldr r8, [tsk, #TI_PREEMPT]  @ get preempt count
 add r7, r8, #1   @ increment it
 str r7, [tsk, #TI_PREEMPT]
#endif
 irq_handler                      @调用中断处理程序
#ifdef CONFIG_PREEMPT
 ldr r0, [tsk, #TI_PREEMPT]
 str r8, [tsk, #TI_PREEMPT]
 teq r0, r7
 strne r0, [r0, -r0]
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
 bl trace_hardirqs_on
#endif
 mov why, #0
 b ret_to_user
ENDPROC(__irq_usr)
回看__irq_usr和__irq_svc标号处的代码,在完成了irq_handler中断处理函数后,要完成从中断异常处理程序返回到中断点的工作。如果中断产生于用户空间,则调用ret_to_user来恢复中断现场并返回用户空间继续运行:


 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
entry_common.s中

ENTRY(ret_to_user)
ret_slow_syscall:
 disable_irq    @ disable interrupts
 ldr r1, [tsk, #TI_FLAGS]
 tst r1, #_TIF_WORK_MASK
 bne work_pending
no_work_pending:
 /* perform architecture specific actions before user return */
 arch_ret_to_user r1, lr
 @ slow_restore_user_regs
 ldr r1, [sp, #S_PSR]  @ get calling cpsr
 ldr lr, [sp, #S_PC]!  @ get pc
 msr spsr_cxsf, r1   @ save in spsr_svc
 ldmdb sp, {r0 - lr}^   @ get calling r0 - lr
 mov r0, r0
 add sp, sp, #S_FRAME_SIZE - S_PC
 movs pc, lr    @ return & move spsr_svc into cpsr
ENDPROC(ret_to_user)
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 .macro irq_handler
 get_irqnr_preamble r5, lr
1: get_irqnr_and_base r0, r6, r5, lr   
                                                   宏查询ISPR(IRQ待定中断服务寄存器,当有需要处理的中断时,
                                                                    这个寄存器的相应位会置位,任意时刻,最多一个位会置位)计算出的中断号放在irqnr指定的寄存器中

 movne r1, sp                             @如果中断号不等于0,将r1=sp,即pt_regs结构体首地址
 @
 @ routine called with r0 = irq number, r1 = struct pt_regs *
 @
 adrne lr, 1b      @返回到1处,asm_do_IRQ返回后将再次查询发生的中断????
                                  @如果r0(中断号)不等于0, lr(返回地址)等于标号1处,即get_irqnr_and_base r0, r6, r5, lr的那行,即循环处理所有的中断 

 
 bne asm_do_IRQ        @kernel的中断处理函数
//中断底层汇编可知asm_do_IRQ函数式中断的C语言总入口函数
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
traps.c中的early_trap_init()函数中
unsigned long vectors = CONFIG_VECTORS_BASE;
/*
  * Copy the vectors, stubs and kuser helpers (in entry-armv.S)
  * into the vector page, mapped at 0xffff0000, and ensure these
  * are visible to the instruction stream.
  */
 memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
 memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start
//entry_armv.s中有.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
 memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

实际copy动作一目了然,就是两个memcpy(第三个实际上是拷贝一些别的东西,原理是一样的,这里不提了). copy的目的地是vectors,这个值是CONFIG_VECTORS_BASE,在2.6.32.7内核中CONFIG_VECTORS_BASE是在各个平台的配置文件中设定的,如: arch/arm/configs/S3C2410_defconfig
 
CONFIG_VECTORS_BASE=0xffff0000






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