linux内核堆栈设置过程

来源:互联网 发布:地球仪js 编辑:程序博客网 时间:2024/06/07 19:06

内核在没有开启MMU之前,内核堆栈的设置在arch/arm/boot/compressed/head.S中。

代码片段:

restart: adr r0, LC0                    /* 获取标号LC0的地址 */
  ldmia r0, {r1, r2, r3, r6, r10, r11, r12} /*  读取LC0地址处的内容到寄存器列表中的寄存器中 */
  ldr sp, [r0, #28]             /* 偏移28即跳过上面7个32位的偏移,把.L_user_stack_end标号地址加载到sp中,即设定堆栈, */

 

  .align 2
  .type LC0, #object
LC0:  .word LC0   @ r1
  .word __bss_start  @ r2
  .word _end   @ r3
  .word _edata   @ r6
  .word input_data_end - 4 @ r10 (inflated size location)
  .word _got_start  @ r11
  .word _got_end  @ ip
  .word .L_user_stack_end @ sp          /* 会 */
  .size LC0, . - LC0

.......

  .align
  .section ".stack", "aw", %nobits
.L_user_stack: .space 4096
.L_user_stack_end:
从这里看到启动的时候使用的堆栈大小为4KB,起始地址在.L_user_stack_end开始。

---------------------------------------------------------------------------------------------------------------------

开启MMU和cache之后:参考arch/arm/kernel/head-common.S

__mmap_switched:
 adr r3, __mmap_switched_data

 ldmia r3!, {r4, r5, r6, r7}             /* 读取__mmap_switched_data标号处的前4个32位数据,同时更新r3寄存器里的地址 */
 cmp r4, r5    @ Copy data segment if needed
1: cmpne r5, r6
 ldrne fp, [r4], #4
 strne fp, [r5], #4
 bne 1b

 mov fp, #0    @ Clear BSS (and zero fp)
1: cmp r6, r7
 strcc fp, [r6],#4
 bcc 1b

 ARM( ldmia r3, {r4, r5, r6, r7, sp})          /* 这里sp堆栈设定为 init_thread_union + THREAD_START_SP这个值 */
 THUMB( ldmia r3, {r4, r5, r6, r7} )   
 THUMB( ldr sp, [r3, #16]  )
 str r9, [r4]   @ Save processor ID
 str r1, [r5]   @ Save machine type
 str r2, [r6]   @ Save atags pointer
 bic r4, r0, #CR_A   @ Clear 'A' bit
 stmia r7, {r0, r4}   @ Save control register values
 b start_kernel
ENDPROC(__mmap_switched)

 .align 2
 .type __mmap_switched_data, %object
__mmap_switched_data:
 .long __data_loc   @ r4
 .long _sdata    @ r5
 .long __bss_start   @ r6
 .long _end    @ r7
 .long processor_id   @ r4
 .long __machine_arch_type  @ r5
 .long __atags_pointer   @ r6
 .long cr_alignment   @ r7
 .long init_thread_union + THREAD_START_SP @ sp
 .size __mmap_switched_data, . - __mmap_switched_data

而init_thread_union定义在init_task.c中,是0号进程的,也是swapper进程,然后进入start_kernel执行。

进入start_kernel后,setup_arch-->setup_processor-->cpu_init这个函数里会设定irq,和abort,以及undefined异常的堆栈。

 /*
  * setup stacks for re-entrant exception handlers
  */
 __asm__ (
 "msr cpsr_c, %1\n\t"
 "add r14, %0, %2\n\t"
 "mov sp, r14\n\t"
 "msr cpsr_c, %3\n\t"
 "add r14, %0, %4\n\t"
 "mov sp, r14\n\t"
 "msr cpsr_c, %5\n\t"
 "add r14, %0, %6\n\t"
 "mov sp, r14\n\t"
 "msr cpsr_c, %7"
     :
     : "r" (stk),
       PLC (PSR_F_BIT | PSR_I_BIT | IRQ_MODE),
       "I" (offsetof(struct stack, irq[0])),                               /* 偏移0x0 */
       PLC (PSR_F_BIT | PSR_I_BIT | ABT_MODE),
       "I" (offsetof(struct stack, abt[0])),                               /* 偏移0xc */
       PLC (PSR_F_BIT | PSR_I_BIT | UND_MODE),
       "I" (offsetof(struct stack, und[0])),                             /* 偏移0x18 */
       PLC (PSR_F_BIT | PSR_I_BIT | SVC_MODE) 
     : "r14");

从这里看出,每个异常的堆栈只有12个字节大小,为什么呢?

我们接着看一下异常处理中对堆栈的使用情况,我们以irq为例分析一下。

异常处理代码在arch/arm/kernel/entry-armv.S中:

/*
 * Interrupt dispatcher
 */
 vector_stub irq, IRQ_MODE, 4

 .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

这里中断向量表的入口。

 .macro vector_stub, name, mode, correction=0
 .align 5

vector_\name:
 .if \correction
 sub lr, lr, #\correction
 .endif

 @
 @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
 @ (parent CPSR)
 @
 stmia sp, {r0, lr}  @ save r0, lr                /* 这里sp是sp_irq,这里使用了8个字节*/
 mrs lr, spsr
 str lr, [sp, #8]  @ save spsr                    /* 这里sp是sp_irq,这里使用了4个字节 */

 @
 @ Prepare for SVC32 mode.  IRQs remain disabled.
 @
 mrs r0, cpsr
 eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE) 
 msr spsr_cxsf, r0                /* 这里会重新进入内核的SVC_MODE模式 r0 ^(IRQ_MODE ^SVC_MODE)  会清除IRQ模式,并设置SVC模式 */

 @
 @ the branch table must immediately follow this code
 @
 and lr, lr, #0x0f
 THUMB( adr r0, 1f   )
 THUMB( ldr lr, [r0, lr, lsl #2] )
 mov r0, sp                           /* 这里的sp是sp_svc */
 ARM( ldr lr, [pc, lr, lsl #2] )
 movs pc, lr   @ branch to handler in SVC mode
ENDPROC(vector_\name)

 .align 2
 @ handler addresses follow this label
1:
 .endm

从上面的代码看出的确irq异常只需要使用12个字节的堆栈就可以了。

--------------------------------------------------------------------------------------------------

待续。。。

 

0 0
原创粉丝点击