Linux内核启动流程中设备树相关内容.docx
- 文档编号:7136505
- 上传时间:2023-01-21
- 格式:DOCX
- 页数:14
- 大小:22.17KB
Linux内核启动流程中设备树相关内容.docx
《Linux内核启动流程中设备树相关内容.docx》由会员分享,可在线阅读,更多相关《Linux内核启动流程中设备树相关内容.docx(14页珍藏版)》请在冰豆网上搜索。
Linux内核启动流程中设备树相关内容
Linux内核启动流程
----设备树的识别曹忠明
ARMLinux内核在Linux-3.x内核有了很大的变化,对一些新的平台的支持取消了传统的设备文件而用设备树取代,这里以FS4412设备树识别为例说明Linux是如何识别设备树的。
1、设备树文件
FS4412设备树文件(exynos4412-fs4412.dts)部分内容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
* Insignal's Exynos4412 based Origen board device tree source
*
* Copyright (c) 2012-2013 Samsung Electronics Co., Ltd.
*
*
* Device tree source file for Insignal's Origen board which is based on
* Samsung's Exynos4412 SoC.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/ dts - v1 /;
#include "exynos4412.dtsi"
/
{
model = "Insignal Origen evaluation board based on Exynos4412";
compatible = "insignal,origen4412", "samsung,exynos4412";
memory {
reg = <0x40000000 0x40000000>;
};
chosen {
bootargs = "root=/dev/nfs nfsroot=192.168.9.120:
/source/rootfs init=/linuxrc console=ttySAC2,115200 ip=192.168.9.233";
};
firmware@0203F000 {
compatible = "samsung,secure-firmware";
reg = <0x0203F000 0x1000>;
};
srom - cs1@5000000 {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x5000000 0x1000000>;
ranges;
ethernet@5000000 {
compatible = "davicom,dm9000";
reg = <0x5000000 0x2 0x5000004 0x2>;
interrupt - parent = <&gpx0>;
interrupts = <6 4>;
davicom, no - eeprom;
mac - address = [00 0a 2d a6 55 a2];
};
};
regulators {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
mmc_reg:
regulator@0 {
compatible = "regulator-fixed";
reg = <0>;
regulator - name = "VMEM_VDD_2.8V";
regulator - min - microvolt = <2800000>;
regulator - max - microvolt = <2800000>;
gpio = <&gpx1 1 0>;
enable - active - high;
};
};
g2d@10800000 {
status = "okay";
};
sdhci@12530000 {
bus - width = <4>;
pinctrl - 0 = <&sd2_clk &sd2_cmd &sd2_bus4 &sd2_cd>;
pinctrl - names = "default";
vmmc - supply = <&mmc_reg>;
status = "okay";
};
......
};
编译设备树文件,内核顶层目录下执行如下命令可以编译设备树文件:
$makedtbs
编译后生成文件为exynos4412-fs4412.dtb,dtb文件是使用大端字节序存储,显示其内容如下:
$hexdumpexynos4412-fs4412.dtb
内容如下:
00000000dd0edfe0000a5880000380000000c82
000001000002800000011000000100000000000
0000020000099060000d4810000000000000000
000003000000000000000000000010000000000
000004000000300000004000000000000000100
0000050000003000000040000000f0000000100
0000060000003000000040000001b0000000100
0000070000003000000270000002c006e696973
00000806e676c616f2c69726567346e31340032
00000906173736d6e752c6778656e79736f3434
.....
0008880006e627663612d6b6f70637200686676
00088906f72746e702d726f686376007973636e
00088a06c2d6e650000
00088a5
在uboot引导内核之前,会将设备树文件加载到内存中,以备Linux内核使用,这里就不详细说明了
2、Linux内核启动
Linux内核启动分几个阶段:
1)Linux内核自解压
2)Linux内核初始化----汇编
3)Linux内核初始化----C
这里从第三阶段开始说明,分析这个阶段,主要是查看函数start_kernel,在start_kernel中有几个函数这里重点分析:
2.1setup_arch
setup_arch(&command_line);
void__initsetup_arch(char**cmdline_p)
{
conststructmachine_desc*mdesc;
…
mdesc=setup_machine_fdt(__atags_pointer);
if(!
mdesc)
mdesc=setup_machine_tags(__atags__pointer,__machine_arch_type);
…
}
setup_machine_fdt:
structmachine_desc*setup_machine_fdt(unsignedintdt_phys)函数是用来识别设备树,Linux内核中是这样描述这个函数的:
/*
*Machinesetupwhenandtbwaspassedtothekernel,dt_phys
*@dt_phys:
physicaladdressofdtblob
*
*Ifadtbwaspassedtothekernelinr2,thenuseittochoosethecorrectmachine_desc
*andtosetupthesystem
*/
也就是说bootloader如果将一个设备树文件加载到内存中,其通过r2寄存器将设备树的物理地址传递到Linux内核中,Linux内核来选择正确的机器且对其进行设置
setup_machine_tags:
这函数是在Linux内核不使用设备树的情况是使用,这里就不分析了。
setup_machine_fdt:
cosntstructmachine_desc*__initsetup_machine_fdt(unsignedintdt_phys)
{
……
mdesc=of_flat_dt_match_machine(mdesc_best,arch_get_next_mach);
if(!
mdesc){
dt_root=of_get_flat_dt_root();
prop=of_get_flat_dt_prop(dt_root,“compatible”,&size);
}
……
returnmdesc;
}
函数中需要重点分析的函数有:
of_flat_match_machine和of_get_flat_dt_root
of_flat_match_machine:
constvoid*__initof_flat_dt_match_machine(constvoid*default_match
constvoid*(*get_next_compat)(constchar*const**))
{
……
/*
*找到设备树中的根节点(开始结点)
*/
dt_root=of_get_flat_dt_root();
/*
*get_next_compat=arch_get_next_mach
*内核中可以同时支持多个平台,这个函数用来获得下一个平台的
*dt_compat属性,结合上边函数克制data内容
*/
while((data=get_next_compat(&compat))){
/*
*of_flat_dt_match用来匹配设备树中内容和内核所支持平台是否匹配
*/
score=of_flat_dt_match(dt_root,compat);
if(score>0&&score best_data=data; best_score=score; } } if(! best_data){ constchar*prop; longsize; /* *设备树中内容和内核所支持平台没有匹配则打印出错提示 */ pr_err("\nunrecognizeddevicetreelist: \n"); prop=of_get_flat_dt_prop(dt_root,"compatible",&size); if(prop){ while(size>0){ printk("'%s'",prop); size-=strlen(prop)+1; prop+=strlen(prop)+1; } printk("]\n\n"); returnNULL; } } } 函数中会调用函数of_get_flat_dt_root和get_next_compat,of_get_flat_dt_root用来从设备树文件中找到根节点,get_next_compat按照上下文这个函数等于arch_get_next_mach,下边会重点分析这两个函数。 of_get_flat_dt_root: 补充: 设备树相关宏和结构体: 结点相关宏: OF_DT_HEADER0xd00dfeed标记 OF_DT_BEGIN_NODE0x1开始结点 OF_DT_END_NODE0x2结束结点 OF_DT_PROP0x3Property: nameoff,size,*content 资源 OF_DT_NOP0x4NOP OF_DT_END0x9结束 OF_DT_VERSION0x10版本 相关结构体: structboot_param_header{ __be32magic;//OF_DT_HEADER幻数 __be32totalsize;//设备树总体大小 __be32off_dt_struct;//structure偏移 __be32off_dt_strings;//strings偏移 __be32off_mem_rsvmap;//内存预留映射表偏移 __be32version;//格式版本 __be32last_comp_version;//最后兼容版本 __be32boot_cpuid_phys;//我们要启动的CPU的ID __be32dt_strings_size;//设备树strings块大小 __be32dt_struct_size;//设备树structure块大小 }; 设备树前40个字节就是boot_param_header,设备树采用大端存储,所以显示内容如下,使用时ARM多设置为小端存储,所以需要使用be32_to_cpu将大端字节序转换为本地字节序,显示如下: 00000000dd0edfe0000a5880000380000000c82 000001000002800000011000000100000000000 0000020000099060000d4810000000000000000 structboot_param_header{ __be32magic;//0x0dd00xedfe==>le0xd00d0xfeed __be32totalsize;//0x00000xa588==>le0x00000x88a5 __be32off_dt_struct;//0x00000x3800==>le0x00000x0038 __be32off_dt_strings;//0x00000x0c82==>le0x00000x820c __be32off_mem_rsvmap;//0x00000x2800==>le0x00000x0028 __be32version;//0x00000x1100==>le0x00000x0011 __be32last_comp_version;//0x00000x1000==>le0x00000x0010 __be32boot_cpuid_phys;//0x00000x0000==>le0x00000x0000 __be32dt_strings_size;//0x00000x9906==>le0x00000x6099 __be32dt_struct_size;//0x00000xd481==>le0x00000x81d4 }; unsignedlong__intiof_get_flat_dt_root(void) { unsignedlongp=((unsignedlong)initial_boot_params)+ be32_to_cpu(initial_boot_params->off_dt_struct); /* *结合上述内容p=设备树起始地址+0x0038 *设备树文件按16进制显示: *000003000000000000000000000010000000000 *0x38=0x00000001 */ /* *跳过所有无效数据 */ while(be32_to_cpup((__be32*)p)==OF_DT_NOP) p+=4; /* *p=OF_DT_BEGIN_NODE表示找到开始节点,也就是设备树有效数据的开始 */ BUG_ON(be32_to_cpup((__be32*)p)! =OF_DT_BEGIN_NODE); p+=4; /* *数据对齐 */ returnALIGN(p+strlen(char*p)+1,4); } arch_get_next_mach: staticconstvoid*__initarch_get_next_mach(constchar*const**match) { staticconststructmachine_desc*mdesc=__arch_info_begin; if(m>__arch_info_end) returnNULL; mdesc++; *match=m->dt_compat; returnm; } 内核中又一个段叫.arch.info.init,内核在编译的时候会将所有支持的平台的信息链接到这个段中即machine_desc,其中__arch_info_begin表示这个段的开始__arch_info_end表示这个段的结束。 连接脚本参考arch/arm/kernel/vmlinux.lds中有如下内容: .init.arch.info: { __arch_info_begin=.; *(.arch.info.init) __arch_info_end=.; } machine_desc注册参考arch/arm/mach-exynos/mach-exynos4-dt.c中 staticvoid__initexynos4_dt_machine_init(void){ exynos_cpuidle_init(); exynos_cpufreq_init(); of_platform_populate(NULL,of_default_bus_match_table,NULL,NULL); } staticcharconst*exynos4_dt_compat[]__initdata={ "samsung,exynos4210", "samsung,exynos4212", "samsung,exynos4410", NULL }; DT_MACHINE_START(EXYNOS4410_DT,"SamsungExynos4(FlattenedDeviceTree)") .dt_compat=exynos4_dt_compat, .init_machine=exynos4_dt_machine_init, .... MACHINE_END DT_MACHINE_START的含义就是定义结构体machine_desc并初始化且标识链接到.arch.info.init段中,定义如下: #defineDT_MACHINE_START(_name,_namestr)\ staticconststructmachine_desc__mach_desc_##_name\ __used\ __attribute__((__section__(“.arch.info.init”)))={}\ .nr=~0,\ .name=_namestr, 上述内容主要是通过读取设备树信息,检测当前使用内核是否支持本平台,如果支持则做相应的初始化,这里主要分析平台识别过程。 2.2rest_init reset_init函数中创建了几个内核线程,其中kernel_init使我们要重点分析的。 其中kernel_init---->kernel_init_freeable----->do_basic_setup---->do_initcall staticvoid__initdo_initcalls(void) { intlevel; for(level=0;leve
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Linux 内核 启动 流程 设备 相关内容