ARM Linux启动代码分析
单说说上一个“年代”的bootloader是怎么引导、启动内核的,主要的流程如下:
(1)上电
(2)必要的设置
(3)关看门狗
(4)初始化SDRAM、初始化Nand Flash
(5)把bootloader拷贝到SDRAM的高处
(6)清BSS段
(7)跳到SDRAM继续执行
(8)把Nand Flash中的内核Image拷贝到SDRAM(0x58)
(9)设置启动参数,r0、r1等寄存器,关闭MMU、cache等
(10)跳到内核Image的起始处(0x58)执行,此后,bootloader时代一去不复返,进入Linux新时代。
现在应该知道执行到161行时,PC的值就为0x50~0x58之间的某一个值(假定内存为128MB,s3c6410物理内存的起始地址为0x50),即一物理地址,因此r3的值就为194行的标号3处的物理地址。
162行,分别将r3、r3+4、r3+8地址上的内容存放到r5、r6、r7寄存器中,即r5存放的是__proc_info_begin的值(是一个链接地址,或者说虚拟地址),r6存放的是__proc_info_end的值(是一个链接地址,或者说虚拟地址),因为 . 表示的是当前的链接地址,所以r7存放的是标号4的链接地址,这跟LD链接脚本里的 . 表示的意思是一样的。
163行,将r3的值加8,即现在r3的值为196行的标号4的物理地址。
164行,r3 = r3 – r7,即r3 = 标号4的物理地址 - 标号4的虚拟地址,这样就可以计算出物理地址和虚拟地址的偏移量,显然r3的值为一负数。
165行,结果为r5 = __proc_info_begin的物理地址。
166行,结果为r6 = __proc_info_end的物理地址。
167行,取出struct proc_info_list结构体的前两个成员的值分别放到r3、r4。struct proc_info_list结构体的定义如下:
struct proc_info_list {unsigned int cpu_val;unsigned int cpu_mask;unsigned long __cpu_mm_mmu_flags; /* used by head.S */unsigned long __cpu_io_mmu_flags; /* used by head.S */unsigned long __cpu_flush; /* used by head.S */const char *arch_name;const char *elf_name;unsigned int elf_hwcap;const char *cpu_name;struct processor *proc;struct cpu_tlb_fns *tlb;struct cpu_user_fns *user;struct cpu_cache_fns *cache;};
每一种体系结构都有一个这样的结构体变量,对于s3c6410,来说,它属于ARMv6体系结构,它的struct proc_info_list变量在arch/arm/mm/proc-v6.S中定义,在链接的时候所有这些变量都被放在__proc_info_begin和__proc_info_end之间。因此,167行执行后,r3 = cpu_val,r4 = cpu_mask。
168行,将r4的值与r9的值相与,得到的CPU ID存在r4中。
169行,比较r4与r3的值。
170行,如果r4=r3,那么跳到175行处执行,即子程序返回。如果r4不等于r3,那么执行171行,将r5的值加上sizeof(struct proc_info_list),即指向下一个struct proc_info_list变量。
172行,比较r5和r6。
173行,如果r5小于r6,则跳转到167行,重复上面的过程。如果所有struct proc_info_list变量都比较后都没有找到对应的CPU ID,那么执行174行,r5 = 0,然后返回。
至此,__lookup_processor_type分析完毕,回到head.S的83行,把r5的值赋给r10,并影响标志位。
84行,如果r5=0,那么跳转到__error_p标号。这里假设内核是支持当前CPU的,即r5不为0,因此不分析__error_p的内容。
85行,跳到__lookup_machine_type标号处,同样是在arch/arm/kernel/head-common.S中定义:
00196 4: .long .00197 .long __arch_info_begin00198 .long __arch_info_end00211 __lookup_machine_type:00212 adr r3, 4b00213 ldmia r3, {r4, r5, r6}00214 sub r3, r3, r4 @ get offset between virt&phys00215 add r5, r5, r3 @ convert virt addresses to00216 add r6, r6, r3 @ physical address space00217 1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type00218 teq r3, r1 @ matches loader number?00219 beq 2f @ found00220 add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc00221 cmp r5, r600 blo 1b00223 mov r5, #0 @ unknown machine00224 2: mov pc, lr00225 ENDPROC(__lookup_machine_type)
和前面的__lookup_processor_type非常类似,只不过这里查找的是struct machine_desc结构体变量,
ARMLinux启动代 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)