本章(第二章)内容是设备树讲解中最重要的一章。
设备树文件里面描述的信息可以分为以下三部分:
实际上,内核对于设备树的处理也会分为三部分:
bootloader在启动内核时,会设置r0,r1,r2三个寄存器,通过这三个寄存器将一些参数传给内核。其中:
问:machine id的作用是什么呢?
答:一个内核镜相uImage通常可以支持多种单板,如smdk2410,smdk2440,jz2440,这三种单板之间都有一些微小的差别,也就是说它们分别需要不同的初始化函数。
在内核代码中,它们都有一个用于描述自己的machine_desc结构体,里面有对应的初始化函数,还有一个nr(number)。
那么,当内核启动时,内核怎么知道它在哪个板子上运行,需要调用哪个初始化参数呢?
这就需要用uboot了,uboot需要把machine id传给内核,内核再根据uboot传入的machine id来匹配machine_desc结构体中的nr,如果两者相等就是匹配成功,就会调用对应machine_desc结构体中的初始化函数。
上面是没有使用设备树时的情况,在使用设备树时,r1可以不设置了,也就是不传入machine id,此时,r1可以随便设置。
r2是设备树或ATAGS的起始地址,ATAGS就是之前说的uboot向内核传参数的tag的起始地址。这里需要注意的就是,r2传入的可能是设备树的起始地址,也可能是tag的起始地址。
tag就是uboot对内核传入的启动参数, 对于tag,可以从下列课程处了解更多的相关信息。
下面大致看一下head.S。
首先会获取processor id,然后跳到__lookup_processor_type,看看这款内核能不能支持这款CPU。
如果内核能够支持这款CPU的话,就会有相应的结构体被调用,在之后调用相应的初始化函数。
在代码里面,有很多.S文件,这些文件里面包含proc_info_list结构体(里面含有这类CPU的初始化函数、信息)。
然后往下执行,有一个__vet_atags函数,这个函数就会判断r2传入的是tag的首地址还是dtb文件的首地址。
然后是__create_page_tables,这个主要是创建页表,也就是创建虚拟地址与物理地址的映射关系,这个先不深入。
继续向下,会使能MMU,使能MMU之后就要使用虚拟地址了。
进入__mmap_switched。
进入 __mmap_switched_data,可以看到里面都是一些变量和段的地址(C变量在汇编文件中出现时,变量名表示的该变量的地址)。
继续往下,会从__mmap_switched_data中读值,经过第4步后会将C变量__atags_pointer的地址读到r2。
最后,再将之前保存到r8的tag或dtb首地址赋给地址为r2的内存空间,也就是__atags_pointer变量。
综上所述,在__mmap_switched中,经过一系列的汇编操作,最终会将:
整个head.S的大致流程如下:
总的来说,head.S中所做的操作就是,将r2中保存的tag或dtb的首地址赋给了C变量__atags_pointer。