2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

本章(第二章)内容是设备树讲解中最重要的一章。

设备树文件里面描述的信息可以分为以下三部分:

  1. platform identification(平台信息);
  2. runtime configuration(运行时的配置信息);
  3. device population(设备的特性);

实际上,内核对于设备树的处理也会分为三部分:

2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

 bootloader在启动内核时,会设置r0,r1,r2三个寄存器,通过这三个寄存器将一些参数传给内核。其中:

  • r0一般设置为0;
  • r1一般设置为machine id(在使用设备树时该参数没有用到);
  • r2一般设置为ATAGS或DTB的开始地址;

问: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,可以从下列课程处了解更多的相关信息。

2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

下面大致看一下head.S。

 首先会获取processor id,然后跳到__lookup_processor_type,看看这款内核能不能支持这款CPU。

2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

如果内核能够支持这款CPU的话,就会有相应的结构体被调用,在之后调用相应的初始化函数。

在代码里面,有很多.S文件,这些文件里面包含proc_info_list结构体(里面含有这类CPU的初始化函数、信息)。

2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

 然后往下执行,有一个__vet_atags函数,这个函数就会判断r2传入的是tag的首地址还是dtb文件的首地址。

 2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

然后是__create_page_tables,这个主要是创建页表,也就是创建虚拟地址与物理地址的映射关系,这个先不深入。

继续向下,会使能MMU,使能MMU之后就要使用虚拟地址了。

2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

进入__mmap_switched。

2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

进入 __mmap_switched_data,可以看到里面都是一些变量和段的地址(C变量在汇编文件中出现时,变量名表示的该变量的地址)。

2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

 继续往下,会从__mmap_switched_data中读值,经过第4步后会将C变量__atags_pointer的地址读到r2。

2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

最后,再将之前保存到r8的tag或dtb首地址赋给地址为r2的内存空间,也就是__atags_pointer变量。

2.1内核对设备树的处理——从源头分析_内核head.S对dtb的简单处理

综上所述,在__mmap_switched中,经过一系列的汇编操作,最终会将:

  • 把bootloader传来的r1值, 赋给了C变量: __machine_arch_type
  • 把bootloader传来的r2值, 赋给了C变量: __atags_pointer     // dtb首地址

整个head.S的大致流程如下:

  1. __lookup_processor_type : 使用汇编指令读取CPU ID, 根据该ID找到对应的proc_info_list结构体(里面含有这类CPU的初始化函数、信息)
  2. __vet_atags             : 判断是否存在可用的ATAGS或DTB
  3. __create_page_tables    : 创建页表, 即创建虚拟地址和物理地址的映射关系
  4. __enable_mmu            : 使能MMU, 以后就要使用虚拟地址了
  5. __mmap_switched         : 上述函数里将会调用__mmap_switched
  6. 把bootloader传入的r2参数, 保存到变量__atags_pointer中
  7. 调用C函数start_kernel

总的来说,head.S中所做的操作就是,将r2中保存的tag或dtb的首地址赋给了C变量__atags_pointer。

上一篇:machine unlearning 论文阅读笔记


下一篇:重装Oracle数据库,亲测有效!!!