Android培训班(102)内核入口汇编2

从前面的入口函数可知,内核进入时并没有知道自己到底运行在什么样的CPU里,因此就没有办法知道自己到底调用那个函数来初始化,或者用什么来做正确的工作。为了解决这个问题,就会调用__lookup_processor_type函数来查找CPU的类型。在这个函数里主要是通过预先填写CPUID的信息,跟目前获取到CPUID进行比较,如果能从预先定义的数组里找到一样的CPUID,说明就找到合适的初始化函数了。由于不同处理器的代码是分别写到不同的文件里,如何才能把这些信息组织到一起呢?其实是通过连接脚本文件来组织的,如下:

__proc_info_begin = .;

*(.proc.info.init)

__proc_info_end = .;

在这个脚本声明里,就可以把段名标记为.proc.info.init的段代码全部编译到一起,就连接成一个很大的数组。__proc_info_begin指向数据开始位置,__proc_info_end指向数组的结束位置。因而在代码里只需遍历整个数组,就可找到代码支持的CPU类型了。不过,还需要知道每个数组元素是长得什么样呢,并且有多大,这就需要了解proc_info_list结构,它的C定义如下:

structproc_info_list {

unsignedint cpu_val;

unsignedint cpu_mask;

unsignedlong __cpu_mm_mmu_flags; /* used by head.S */

unsignedlong __cpu_io_mmu_flags; /* used by head.S */

unsignedlong __cpu_flush; /* used by head.S */

constchar *arch_name;

constchar *elf_name;

unsignedint elf_hwcap;

constchar *cpu_name;

structprocessor *proc;

structcpu_tlb_fns *tlb;

structcpu_user_fns *user;

structcpu_cache_fns *cache;

};


通过这个结构,可以看到cpu_val是保存CPUID的值,cpu_mask是指明那几位数据起作用,__cpu_mm_mmu_flags是保存MMU的内存相关标志,__cpu_io_mmu_flags是保存MMUIO相关的标志,__cpu_flush是保存架构相关的初始化代码入口,其它就是CPU相关的信息和函数,后面再继续了解,在入口函数只需要了解这几个成员就行了。有了结构的定义,就可以知道每个结构占用内存的大小,通过如下的方式获得:

DEFINE(PROC_INFO_SZ, sizeof(structproc_info_list));

这样在PROC_INFO_SZ就保存了每个CPU信息结构的大小。有了这些基本知识之后,就可以来理解__lookup_processor_type函数的具体实现过程了,它的代码如下:

__lookup_processor_type:

adr r3, 3f

计算3f标号地址。


ldmda r3, {r5 - r7}

加载处理器信息地址到r5,r6,r7


sub r3, r3, r7 @get offset between virt&phys

add r5, r5, r3 @convert virt addresses to

add r6, r6, r3 @physical address space

把虚拟地址转换为物理地址。


1: ldmia r5, {r3,r4} @ value, mask

and r4, r4, r9 @mask wanted bits

teq r3, r4

beq 2f

比较CPUID,如果一致跳到标号2返回。


add r5, r5,#PROC_INFO_SZ @ sizeof(proc_info_list)

cmp r5, r6

增加处理器信息数组地址计数,是否到结束,如果到了就返回0.


blo 1b

mov r5, #0 @unknown processor

2: mov pc, lr

ENDPROC(__lookup_processor_type)


/*

*Look in <asm/procinfo.h> and arch/arm/kernel/arch.[ch] for

*more information about the __proc_info and __arch_info structures.

*/

.long __proc_info_begin

.long __proc_info_end

3: .long .

.long __arch_info_begin

.long __arch_info_end

首先通过3f的标号获取到处理器信息列表的数组地址,也就是获取__proc_info_begin__proc_info_end的值,因此通过ldmda r3,{r5 -r7}语句的执行,就把3f标号的虚拟地址保存到r7里,__proc_info_end的值保存到r6__proc_info_begin的值保存到r5。通过编译时生产3f标号的虚拟地址与当前3f标号运行地址进行计算,就可以算出当前虚拟地址与物理地址之间的偏移,然后就可以把__proc_info_begin__proc_info_end的值计算成虚拟地址。然后通过加载proc_info_list里的CPUID来与传送入的r9寄存器的CPUID进行比较,如果一致就说明已经找到相应的CPU信息了,接着放到r5寄存器返回找到的CPU信息结构的地址。

你可能感兴趣的:(Android培训班(102)内核入口汇编2)