本文分析基於linux2.4.19 source,pxa 270 cpu.
ARM linux核心啟動時,通過start_kernel()->trap_init()的調用關係,初始化核心的中斷異常向量表.
/* arch/arm/kernel/traps.c */
void __init trap_init(void)
{
extern void __trap_init(unsigned long);
unsigned long base = vectors_base();
__trap_init(base);
if (base != 0)
oopsprintk(KERN_DEBUG "Relocating machine vectors to 0x%08lx/n", base);
#ifdef CONFIG_CPU_32
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
#endif
}
vectors_base是一個宏,它的作用是擷取ARM異常向量的地址,該宏在include/arch/asm-arm/proc-armv/system.h中定義:
extern unsigned long cr_no_alignment; /* defined in entry-armv.S */
extern unsigned long cr_alignment; /* defined in entry-armv.S */
#if __LINUX_ARM_ARCH__ >= 4
#define vectors_base() ((cr_alignment & CR_V) ? 0xffff0000 : 0)
#else
#define vectors_base() (0)
#endif
對於ARMv4以下的版本,這個地址固定為0;ARMv4及其以上的版本,ARM異常向量表的地址受副處理器CP15的c1寄存器
(control
register)中V位(bit[13])的控制,如果V=1,則異常向量表的地址為0x00000000~0x0000001C;如果V=0,則
為:0xffff0000~0xffff001C。(詳情請參考ARM Architecture Reference Manual)
下面分析一下cr_alginment的值是在哪確定的,我們在arch/arm/kernel/entry-armv.S找到cr_alignment的定義:
.globl SYMBOL_NAME(cr_alignment)
.globl SYMBOL_NAME(cr_no_alignment)
SYMBOL_NAME(cr_alignment):
.space 4
SYMBOL_NAME(cr_no_alignment):
.space 4
分析過head-armv.S檔案的朋友都會知道,head-armv.S是非壓縮核心的入口:
1 .section ".text.init",#alloc,#execinstr
2 .type stext, #function
3ENTRY(stext)
4 mov r12, r0
5
6 mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode
7 msr cpsr_c, r0 @ and all irqs disabled
8 bl __lookup_processor_type
9 teq r10, #0 @ invalid processor?
10 moveq r0, #'p' @ yes, error 'p'
11 beq __error
12 bl __lookup_architecture_type
13 teq r7, #0 @ invalid architecture?
14 moveq r0, #'a' @ yes, error 'a'
15 beq __error
16 bl __create_page_tables
17 adr lr, __ret @ return address
18 add pc, r10, #12 @ initialise processor
19 @ (return control reg)
20
21 .type __switch_data, %object
22__switch_data: .long __mmap_switched
23 .long SYMBOL_NAME(__bss_start)
24 .long SYMBOL_NAME(_end)
25 .long SYMBOL_NAME(processor_id)
26 .long SYMBOL_NAME(__machine_arch_type)
27 .long SYMBOL_NAME(cr_alignment)
28 .long SYMBOL_NAME(init_task_union)+8192
29
30 .type __ret, %function
31__ret: ldr lr, __switch_data
32 mcr p15, 0, r0, c1, c0
33 mrc p15, 0, r0, c1, c0, 0 @ read it back.
34 mov r0, r0
35 mov r0, r0
36 mov pc, lr
這裡我們關心的是從17行開始,17行code處將lr放置為__ret標號處的相對位址,以便將來某處返回時跳轉到31行繼續運行;
18行,對於我所分析的pxa270平台,它將是跳轉到arch/arm/mm/proc-xscale.S中執行__xscale_setup函數,
在__xscale_setup中會讀取CP15的control
register(c1)的值到r1寄存器,並在r1寄存器中設定相應的標誌位(其中包括設定V位=1),但在__xscale_setup中,r1寄存
器並不立即寫回到Cp15的control
register中,而是在返回後的某個地方,接下來會慢慢分析到。__xscale_setup調用move pc, lr指令返回跳轉到31行。
31行,在lr寄存器中放置__switch_data中的資料__mmap_switched,在36行程式會跳轉到__mmap_switched處。
32,33行,把r0寄存器中的值寫回到cp15的control register(c1)中,再讀出來放在r0中。
接下來再來看一下跳轉到__mmap_switched處的代碼:
40 _mmap_switched:
41 adr r3, __switch_data + 4
42 ldmia r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat
43 @ sp = stack pointer
44
45 mov fp, #0 @ Clear BSS (and zero fp)
46 1: cmp r4, r5
47 strcc fp, [r4],#4
48 bcc 1b
49
50 str r9, [r6] @ Save processor ID
51 str r1, [r7] @ Save machine type
52 bic r2, r0, #2 @ Clear 'A' bit
53 stmia r8, {r0, r2} @ Save control register values
54 b SYMBOL_NAME(start_kernel)
41~42行的結果是:r4=__bss_start,r5=__end,...,r8=cr_alignment,..,這裡r8儲存的是cr_alignment變數的地址.
到了53行,由於之前r0儲存的是cp15的control register(c1)的值,這裡把r0的值寫入r8指向的地址,即cr_alignment=r0.到此為止,我們就看清楚了cr_alignment的賦值過程。
讓我們回到trap_init()函數,經過上面的分析,我們知道vectors_base返回0xffff0000。函數__trap_init由彙編代碼編寫,在arch/arm/kernel/entry-arm.S:
.align 5
__stubs_start:
vector_IRQ:
...
vector_data:
....
vector_prefetch:
...
vector_undefinstr:
...
vector_FIQ: disable_fiq
subs pc, lr, #4
vector_addrexcptn:
b vector_addrexcptn
...
__stubs_end:
.equ __real_stubs_start, .LCvectors + 0x200
.LCvectors: swi SYS_ERROR0
b __real_stubs_start + (vector_undefinstr - __stubs_start)
ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
b __real_stubs_start + (vector_prefetch - __stubs_start)
b __real_stubs_start + (vector_data - __stubs_start)
b __real_stubs_start + (vector_addrexcptn - __stubs_start)
b __real_stubs_start + (vector_IRQ - __stubs_start)
b __real_stubs_start + (vector_FIQ - __stubs_start)
ENTRY(__trap_init)
stmfd sp!, {r4 - r6, lr} /* 壓棧,儲存資料*/
/* 複製異常向量表(.LCvectors起始的8個地址)到r0指向的地址(異常向量地址),r0就是__trap_init(base)函數調用時傳遞的參數,不明白的請參考ATPCS*/
adr r1, .LCvectors @ set up the vectors
ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr}
stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr}
/* 在異常向量地址後的0x200位移處,放置散轉代碼,即__stubs_start~__stubs_end之間的各個異常處理代碼*/
add r2, r0, #0x200
adr r0, __stubs_start @ copy stubs to 0x200
adr r1, __stubs_end
1: ldr r3, [r0], #4
str r3, [r2], #4
cmp r0, r1
blt 1b
LOADREGS(fd, sp!, {r4 - r6, pc}) /*出棧,恢複資料,函數__trap_init返回*/
__trap_init函數填充後的向量表如下:
虛擬位址 異常 處理代碼
0xffff0000 reset swi SYS_ERROR0
0xffff0004 undefined b __real_stubs_start + (vector_undefinstr - __stubs_start)
0xffff0008 軟體中斷 ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
0xffff000c 取指令異常 b __real_stubs_start + (vector_prefetch - __stubs_start)
0xffff0010 資料異常 b __real_stubs_start + (vector_data - __stubs_start)
0xffff0014 reserved b __real_stubs_start + (vector_addrexcptn - __stubs_start)
0xffff0018 irq b __real_stubs_start + (vector_IRQ - __stubs_start)
0xffff001c fiq b __real_stubs_start + (vector_FIQ - __stubs_start)
當有異常發生時,處理器會跳轉到對應的0xffff0000起始的向量處取指令,然後,通過b指令散轉到異常處理代碼.因為ARM中b指令是相對跳轉,而
且只有+/-32MB的定址範圍,所以把__stubs_start~__stubs_end之間的異常處理代碼複製到了0xffff0200起始處.這
裡可直接用b指令跳轉過去,這樣比使用絕對跳轉(ldr)效率高。
-------------------------參考資料--------------------
1, 劉淼,嵌入式系統介面設計與Linux驅動程式開發,北京航天航空大學出版社,2006.
2, ARM Architecture Reference Manual, ARM limited,2000.
原文地址
http://hi.baidu.com/_%C5%CE%C8%FD%C4%EA_/blog/item/cceac934d9dd95b1d1a2d370.html