原文:http://blog.chinaunix.net/uid-20522771-id-3785808.html
本文代码均来自标准 linux kernel 3.10,可以到这里下载 https://www.kernel.org/
以 arch/arm/mach-msm/board-dt-8960.c 为例,在该文件中的msm_dt_init
函数的作用就是利用 dt(device tree)结构初始化 platform device。
of_platform_populate
实现在 drivers/of/platform.c,是 OF 的标准函数。
其中of_platform_populate
函数的注释写得很明白:“Populate platform_devices from device tree data”。但是这个“device tree data”又是从那里来的呢?
在of_platform_populate
中如果 root 为 NULL,则将 root 赋值为根节点,这个根节点是用of_find_node_by_path
取到的。
在这个函数中有一个很关键的全局变量:of_allnodes
,它的定义是在 drivers/of/base.c 里面。
这应该所就是那个所谓的“device tree data”了。它应该指向了 device tree 的根节点。问题又来了,这个of_allnodes
又是咋来的呢?我们知道 device tree 是由 DTC(Device Tree Compiler)编译成二进制文件 DTB(Ddevice Tree Blob)的,然后在系统上电之后由 bootloader 加载到内存中去,这个时候还没有 device tree,而在内存中只有一个所谓的 DTB,这只是一个以某个内存地址开始的一堆原始的 dt 数据,没有树结构。kernel 的任务需要把这些数据转换成一个树结构然后再把这棵树的根节点的地址赋值给 of_allnodes 就行了。这个过程一定是非常重要,因为没有这个 device tree 那所有的设备就没办法初始化,所以这个 dt 树的形成一定在 kernel 刚刚启动的时候就完成了。
既然如此,我们来看看 kernel 初始化的代码(init/main.c),大部分代码与本主题无关用 … 代替。
这个setup_arch
就是各个架构自己的设置函数(arch 是 architecture 的缩写),哪个参与了编译就调用哪个,在本文中应当是 arch/arm/kernel/setup.c 中的 setup_arch。
同样,无关的代码以 … 代替。
看到了吧,setup_machine_fdt
,其中 fdt 的 f 就是扁平(flat)的意思。这个时候 DTB 只是加载到内存中的 .dtb 文件而已,这个文件中不仅包含数据结构,还包含了一些文件头等信息,kernel 需要从这些信息中获取到数据结构相关的信息,然后再生成设备树。这个函数的调用还有个参数 __atags_pointer
,看名字似乎这是一个指针,干嘛的呢?以后再说,先进入函数看看。
现在有点意思了,phys_to_virt
字面上的意思是物理地址转换成虚拟地址,那就是说__atags_pointer
是一个物理地址,这就印证了我们的猜测,__atags_pointer
的确是一个指针,再看变量 devtree 它指向了一个struct boot_param_header
结构体。随后 kernel 把这个指针赋给了全局变量initial_boot_params
。也就是说以后 kernel 会是用这个指针指向的数据去初始化 device tree。
看这个结构体,很像之前所说的文件头,有魔数、大小、数据结构偏移量、版本等等,kernel 就应该通过这个结构获取数据,并最终生成设备树。现在回到 `setup_arch`,果然在随后的代码中有这么一个函数。
|
|
看见了吧,`of_allnodes`就是在这里赋值的,device tree 也是在这里建立完成的。`__unflatten_device_tree` 函数我们就不去深究了,推测其功能应该就是解析数据、申请内存、填充结构等等。
到此为止,device tree 的初始化就算完成了,在以后的启动过程中,kernel 就会依据这个 dt 来初始化各个设备。但是还有一个小问题,那就是在 `setup_arch` 函数中 `__atags_pointer` 从何而来。全局搜索这个变量,结构在 arch/arm/kernel/head-common.S 中发现了它的踪迹。
|
|
arm 汇编不懂,大概能看出来给 __atags_pointer
赋值的过程(‘@’之后是注释)。