linux内核栈和用户栈(二)
一.中断向量
ARM执行的时候,有时会产生中断,根据中断的来源不同,执行不同的中断向量:
中断向量地址
异常中断类型
异常中断模式
优先级(6最低)
0x0
复位
SVC
1
0x4
未定义的指令
Undef
6
0x8
软件中断(SWI)
SVC
6
0x0c
指令预取中止
中止模式
5
0x10
数据访问中止
中止模式
2
0x14
保留
未使用
未使用
0x18
IRQ
IRQ
4
0x1c
FIQ
FIQ
3
Linux由于具有内核态和用户态,与ARM的不同运行模式对应,内核态运行于SVC模式,用户态运行于USR模式,当linux运行时由于产生中断,或者软中断时,会根据上表的中断向量,进入不同的运行流程。
__vectors_start:
W(b)vector_rst
W(b)vector_und
W(ldr)pc, __vectors_start + 0x1000
W(b)vector_pabt
W(b)vector_dabt
W(b)vector_addrexcptn
W(b)vector_irq
W(b)vector_fiq
二.外部中断的产生(IRQ)
2.1外部中断产生的预处理
.macrovector_stub, name, mode, correction=0
.align5
vector_\name:
.if \correction
sublr, lr, #\correction
.endif
@
@ Save r0, lr_ (parent PC) and spsr_
@ (parent CPSR)
@
stmiasp, {r0, lr}@ save r0, lr
mrslr, spsr
strlr, [sp, #8]@ save spsr
@
@ Prepare for SVC32 mode. IRQs remain disabled.
@
mrsr0, cpsr
eorr0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
msrspsr_cxsf, r0
@
@ the branch table must immediately follow this code
@
andlr, lr, #0x0f
THUMB(adrr0, 1f)
THUMB(ldrlr, [r0, lr, lsl #2])
movr0, sp
ARM(ldrlr, [pc, lr, lsl #2])
movspc, lr@ branch to handler in SVC mode
ENDPROC(vector_\name)
当产生IRQ中断后,系统即进入向量0x18的IRQ模式,在该模式中,保存r0, lr, spsr到栈中,然后再更改CPSR切换ARM模式为SVC模式。在IRQ中断产生之前,系统可以运行在USR模式,即用户态,在这种情况下,ARM运行模式从USR模式,切换到IRQ模式,又在IRQ中断处理中,将系统切换到SVC状态,即内核态,用户态的寄存器被保存到SVC的堆栈中了。同样,在IRQ中断产生之前,系统运行在SVC模式下,即内核态,在这种情况下,ARM运行模式切到IRQ模式后,又被切换回SVC模式了,溜了一圈。对于IRQ具体的中断函数处理时都在SVC模式下,这样处理也同样有利于中断嵌套运行。
2.2外部中断的处理
/*
* Interrupt dispatcher
*/
vector_stubirq, 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
Linux系统运行在用户态发生中断时,执行__irq_usr分支;当运行在内核态发生中断时,执行__irq_svc分支,
这两个流程的处理都已经在SVC模式了。
2.3 __irq_usr处理流程
.align5
__irq_usr:
usr_entry
kuser_cmpxchg_check
irq_handler
get_thread_info tsk
movwhy, #0
bret_to_user_from_irq
UNWIND(.fnend)
ENDPROC(__irq_usr)
.macrousr_entry, trace=1
UNWIND(.fnstart)
UNWIND(.cantunwind)@ don't unwind the user space
subsp, sp, #S_FRAME_SIZE
ARM(stmibsp, {r1 - r12})
THUMB(stmiasp, {r0 - r12})
ATRAP(mrcp15, 0, r7, c1, c0, 0)
ATRAP(ldrr8, .LCcralign)
ldmiar0, {r3 - r5}
addr0, sp, #S_PC@ here for interlock avoidance
movr6, #-1@ "" "" "" ""
strr3, [sp]@ save the "real" r0 copied
@ from the exception stack
ATRAP(ldrr8, [r8, #0])
@
@ We are now ready to fill in the remaining blanks on the stack:
@
@ r4 - lr_, already fixed up for correct return/restart
@ r5 - spsr_
@ r6 - orig_r0 (see pt_regs definition in ptrace.h)
@
@ Also, separately save sp_usr and lr_usr
@
stmiar0, {r4 - r6}
ARM(stmdbr0, {sp, lr}^)
THUMB(store_user_sp_lr r0, r1, S_SP - S_PC)
@ Enable the alignment trap while in kernel mode
ATRAP(teqr8, r7)
ATRAP( mcrnep15, 0, r8, c1, c0, 0)
@
@ Clear FP to mark the first stack frame
@
zero_fp
.if\trace
#ifdef CONFIG_IRQSOFF_TRACER
bltrace_hardirqs_off
#endif
ct_user_exit save = 0
.endif
.endm
ENTRY(ret_to_user_from_irq)
ldrr1, [tsk, #TI_FLAGS]
tstr1, #_TIF_WORK_MASK
bnework_pending
no_work_pending:
asm_trace_hardirqs_on
/* perform architecture specific actions before user return */
arch_ret_to_user r1, lr
ct_user_enter save = 0
restore_user_regs fast = 0, offset = 0
ENDPROC(ret_to_user_from_irq)
.macrorestore_user_regs, fast = 0, offset = 0
movr2, sp
ldrr1, [r2, #\offset + S_PSR]@ get calling cpsr
ldrlr, [r2, #\offset + S_PC]!@ get pc
msrspsr_cxsf, r1@ save in spsr_svc
#if defined(CONFIG_CPU_V6) || defined(CONFIG_CPU_32v6K)
@ We must avoid clrex due to Cortex-A15 erratum #830321
strexr1, r2, [r2]@ clear the exclusive monitor
#endif
.if\fast
ldmdbr2, {r1 - lr}^@ get calling r1 - lr
.else
ldmdbr2, {r0 - lr}^@ get calling r0 - lr,
.endif
movr0, r0@ ARMv5T and earlier require a nop
@ after ldm {}^
addsp, sp, #\offset + S_FRAME_SIZE
movspc, lr@ return & move spsr_svc into cpsr
.endm
2.4__irq_svc处理流程
.align5
__irq_svc:
svc_entry
irq_handler
#ifdef CONFIG_PREEMPT
get_thread_info tsk
ldrr8, [tsk, #TI_PREEMPT]@ get preempt count
ldrr0, [tsk, #TI_FLAGS]@ get flags
teqr8, #0@ if preempt count != 0
movner0, #0@ force flags to 0
tstr0, #_TIF_NEED_RESCHED
blnesvc_preempt
#endif
svc_exit r5, irq = 1@ return from exception
UNWIND(.fnend)
ENDPROC(__irq_svc)
三.内核栈的取得
.macroget_thread_info, rd
ARM(mov\rd, sp, lsr #THREAD_SIZE_ORDER + PAGE_SHIFT)
THUMB(mov\rd, sp)
THUMB(lsr\rd, \rd, #THREAD_SIZE_ORDER + PAGE_SHIFT)
mov\rd, \rd, lsl #THREAD_SIZE_ORDER + PAGE_SHIFT
.endm
#define THREAD_SIZE_ORDER 1
#define PAGE_SHIFT 12
struct thread_info {……},该结构大小是8K, 即低13位对齐,当前的SP右移13位,再左移13位即得到当前进程的thread_info, 从这也可以推出task_struct{}