linux内核堆栈设置过程
内核在没有开启MMU之前,内核堆栈的设置在arch/arm/boot/
内核在没有开启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)