在google上输入arm linux,找到全是如何配制,就没有人解释一下吗?靠,我觉得不仅要知其然,还要知其所以然。不知道放哪里好。我喜欢这里,所以来这里投,试试 还有,我的机还在用PII,老是出问题,前不久,分区就没了一会,心痛啊!!!!!!!!!!!好多好东东都没了。顺便问一下大家谁有碰到过没有光驱接上,硬盘就不能启动的问题啊。这个问题可把我这个自称高手的人给难坏了 2004.5.23
今天开始,我要好好看看arm linux的代码了,好久没有这样的豪言壮语了
拿哪个开刀呢。翻翻以前的工作,加上正好有人问,所以决定先搞定entry-armv.S文件。
看我一刀
文件一开始是一大堆宏定义,用于指明是用的何种芯片。定义了三个针对不同芯片的宏,disable_fiq、get_irqnr_and_base和irq_prio_table。
跳过这些宏,来到:
.section “.text.init”, #alloc, #execinstr 这里说明了下面的段是初始化段,有关初始化段的内容参考书籍。
1 vector_IRQ:
2 ldr r13, .LCsirq
3 sub lr, lr, #4
4 str lr, [r13]
5 mrs lr, spsr
6 str lr, [r13, #4]
7 mrs r13, spsr
8 bic r13, r13, #MODE_MASK
9 orr r13, r13, #MODE_SVC|I_BIT
10 msr spsr_c, r13
11 and lr, lr, #15
12 ldr lr, [pc, lr, lsl #2]
13 movs pc, lr
14 .LCtab_irq: .word __irq_usr
15 .word __irq_invalid
16 .word __irq_invalid
17 .word __irq_svc
18 .word __irq_invalid
…
解释:
1.IRQ中断向量的入口
2.读取.LCsirq地址的内容到r13寄存器中。.LCsirq定义如下:
.LCsirq: .word __temp_irq
即.LCsirq中保存了__temp_irq的地址,而__temp_irq定义如下:
__temp_irq: .word 0 @saved lr_irq
.word 0 @saved spsr_irq
.word -1 @old_r0
从这里可以得出r13保存了__temp_irq的地址,而这个地址用来保存了lr_irq、spsr_irq、old_r0的值。具体情况参见下面的解释。
3.恢复发生中断时的地址(该地址即是中断返回地址),参考ARM IRQ异常的书籍。
4.保存返回地址到r13,前面看到了r13的赋值情况,所以地址是被保存在__temp_irq的第一个word中。
5.读取中断发生时的CPSR,有关情况参考ARM IRQ异常的书籍
6.保存CPSR到__temp_irq的第二个word中。
7.将SPSR保存到r13中。注:这里不使用lr,而要另赋值到r13中,是因为要改变SPSR的值,而原来的值也有用。所以改变用r13,原来的值用lr。
8.清除模式位。MODE_MASK的定义参见\linux-2.4.26\include\asm-arm\proc-armv\ ptrace.h文件:
#define MODE_MASK 0x1f
9.在\linux-2.4.26\include\asm-arm\proc-armv\ ptrace.h下还定义了:
#define SVC_MODE 0x13
#define I_BIT 0x80
这时r13被修改为禁止irq中断的svc模式。
10.将修改好的模式放入spsr中。
11.lr保留了原来的cpsr,做与操作后,只保留其模式位。即说明该中断发生自何种模式下。
12.读取.LCtab_irq中的内容,放置于lr寄存器中。而.LCtab_irq中保存了中断发生自各种不同模式下的处理函数。其中使用了pc寄存器,详情参考ARM书籍。
13.切换模式并跳转到相应处理函数。
0 __irq_usr: sub sp, sp, #S_FRAME_SIZE
1 stmia sp, {r0 - r12}
2 ldr r4, .LCirq
3 add r8, sp, #S_PC
4 ldmia r4, {r5 - r7}
5 stmia r8, {r5 - r7}
6 stmdb r8, {sp, lr}^
7 alignment_trap r4, r7, __temp_irq
8 zero_fp
9 get_irqnr_and_base r0, r6, r5, lr
10 movne r1, sp
11 adrsvc ne, lr, 1b
12 bne do_IRQ
13 mov why, #0
14 get_current_task tsk
15 b ret_to_usr
解释:
0.此函数是当IRQ中断发生在usr模式时,调用的。它首先从堆栈中保留出存放寄存器的空间,用于保存现场。在entry-header.s中定义了如下宏:
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#else
#define S_FRAME_SIZE 68
#define S_OLD_R0 64
#define S_PSR 60
#endif
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define S_OFF 8
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
从这些宏中,我们可以得到arm linux使用的栈结构如下图:
0 |-------------|
| S_R0 |
4 |-------------|
| S_R1 |
8 |-------------|
| S_R2 |
12 |-------------|
| S_R3 |
16 |-------------|
| S_R4 |
20 |-------------|
| S_R5 |
24 |-------------|
| S_R6 |
28 |-------------|
| S_R7 |
32 |-------------|
| S_R8 |
36 |-------------|
| S_R9 |
40 |-------------|
| S_R10 |
44 |-------------|
| S_FP |
48 |-------------|
| S_IP |
52 |-------------|
| S_SP |
56 |-------------|
| S_LR |
60 |-------------|
| S_PC |
64 |-------------|
| S_PSR |
68 |-------------|
| S_OLD_R0 |
72 |-------------|
注:这个图在arm linux中很重要!
1.保存如上图所示的r0到r12寄存器。因为这些寄存器得以保存。我们现在可以大胆的使用这些寄存器了
2.读取.LCirq的内容到r4寄存器中。.LCirq的定义如下:
.LCirq .word __temp_irq
哈哈,在vector_IRQ中提到的__temp_irq在此显身。我们知道__temp_irq中保存了些什么。我想各位应该猜到了下面的代码是什么了吧
在这里将.LCirq的地址放到r4寄存器中。
3.将r8指到前面所说的保存S_PC的地方。前面保存了r0-r12,还有S_SP、S_LR、S_PC、S_PSR、S_OLD_R0没有保存。
4.读取__temp_irq中的内容
5.保存S_PC、S_PSR、S_OLD_R0。
6.保存S_SP、S_LR,要注意保存的是用户模式的sp和lr。
7.在entry-header.S中,定义了alignment_trap宏:
.macro alignment_trap, rbase, rtemp, sym
#ifdef CONFIG_ALIGNMENT_TRAP
#define OFF_CR_ALIGNMENT(x) cr_alignment - x
ldr \rtemp, [\rbase, #OFF_CR_ALIGNMENT(\sym)]
mcr p15, 0, \rtemp, c1, c0
#endif
.endm