解析设备树的流程
一、汇编相关部分的代码流程分析
ENTRY(stext) {//head.S (kernel-3.10\arch\arm\kernel) bl __lookup_processor_type bl __vet_atags bl __fixup_smp bl __create_page_tables ldr r13, =__mmap_switched //__mmap_switched是一个函数,具体在head-common.S (kernel-3.10\arch\arm\kernel) b __enable_mmu { b __turn_mmu_on { mov r3, r13 mov pc, r3 { //调到r13中存放的地址去执行,即执行__mmap_switched函数。 b start_kernel //跳转到start_kernel函数去执行,该函数具体在kernel-3.10\init\Main.c } } } }
二、C相关的函数流程
start_kernel(void) { // kernel-3.10\init\Main.c setup_arch(&command_line); { // kernel-3.10\arch\arm\kernel\Setup.c struct machine_desc *mdesc; mdesc = setup_machine_fdt(__atags_pointer); { //setup_machine_fdt(unsigned int dt_phys) devtree = phys_to_virt(dt_phys); initial_boot_params = devtree; // initial_boot_params 变量为全局变量,在后面的解析设备树会用到 } mdesc = setup_machine_tags(__atags_pointer, __machine_arch_type); { //if (!mdesc) for_each_machine_desc(p){ //#define for_each_machine_desc(p) for (p = __arch_info_begin; p < __arch_info_end; p++) if (machine_nr == p->nr){ mdesc = p; break; } } } machine_desc = mdesc; //注意全局变量 machine_desc unflatten_device_tree(); { __unflatten_device_tree(initial_boot_params, &of_allnodes, early_init_dt_alloc_memory_arch); { //注意全局参数 of_allnodes create tree of device_nodes from flat blob struct device_node **allnextp = &of_allnodes; start = ((unsigned long)blob) + be32_to_cpu(blob->off_dt_struct); //获取fdt开始的地址 size = unflatten_dt_node(blob, 0, &start, NULL, NULL, 0); //先获取到需要的内存大小 mem = (unsigned long)dt_alloc(size + 4, __alignof__(struct device_node)); //分配内存 unflatten_dt_node(blob, mem, &start, NULL, &allnextp, 0); //解析所有的fdt_node Alloc and populate a device_node from the flat tree } of_alias_scan(early_init_dt_alloc_memory_arch); { //把设备树中的/chosen 和 /aliases 节点解析出来,单独放到一个变量中 //Get pointer to "/chosen" and "/aliasas" nodes for use everywhere of_chosen = of_find_node_by_path("/chosen"); //注意变量 of_chosen, 在Base.c (kernel-3.10\drivers\of) 文件中共享 of_aliases = of_find_node_by_path("/aliases"); //注意变量 of_aliases, 在Base.c (kernel-3.10\drivers\of) 文件中共享 for_each_property_of_node(of_aliases, pp) { of_alias_add(ap, np, id, start, len); list_add_tail(&ap->link, &aliases_lookup); //注意变量 aliases_lookup,把 /aliases中的元素全部解析到该链表 } } } } rest_init();{ kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); { //启动一个内核线程,运行kernel_init函数 ... ... //经过一系列的初始化后,运行kernel_init函数 kernel_init(void *unused){ kernel_init_freeable();{ do_basic_setup();{ do_initcalls();{ for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++){ do_initcall_level(level);{ for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++){ do_one_initcall(*fn);{ fn(); } } } } } } } } } } }
三、友情提供相关信息
{ #define DT_MACHINE_START(_name, _namestr) \ static const struct machine_desc __mach_desc_##_name \ __used \ __attribute__((__section__(".arch.info.init"))) = { \ .nr = ~0, \ .name = _namestr, } Core.c (kernel-3.10\drivers\misc\mediatek\mach\mt6735) 2632 2016/9/14 DT_MACHINE_START(MT6735_DT, "MT6735") .map_io = mt_map_io, .smp = smp_ops(mt_smp_ops), /*.init_irq = mt_dt_init_irq,*/ /*.init_time = mt_timer_init,*/ .init_machine = mt_init, .fixup = mt_dt_fixup, /* FIXME: need to implement the restart function */ .restart = arm_machine_restart, .reserve = mt_reserve, .dt_compat = mt_dt_match, MACHINE_END .init.arch.info : { __arch_info_begin = .; *(.arch.info.init) __arch_info_end = .; } }
四、接下来执行那些initcall函数:
arch_initcall(customize_machine); { //Setup.c (kernel-3.10\arch\arm\kernel) // 解析设备树中的所有顶层的节点和符合match节点的子节点,添加到platform BUS的设备链表中 customize_machine(void) { machine_desc->init_machine(); //此处就是我们友情提供的信息中的相关函数 mt_init(void) { mt_dt_init();{ of_platform_populate(NULL, dt_bus_match, NULL, NULL);{ root = of_find_node_by_path("/");{ 根据of_allnodes中的所有节点,找到根节点 } for_each_child_of_node(root, child){ of_platform_bus_create(child, matches, lookup, parent, true);{ dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); { //创建一个platform设备的结构体,并加到链表中 dev = of_device_alloc(np, bus_id, parent); //为platform设备结构体分配内存 dev->dev.bus = &platform_bus_type; //指定设备bus为platform of_device_add(dev); { //添加设备结构体到链表中 device_add(&ofdev->dev); { bus_add_device(dev);{ struct bus_type *bus = bus_get(dev->bus); klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); } dpm_sysfs_add(dev); device_pm_add(dev);{ list_add_tail(&dev->power.entry, &dpm_list); } bus_probe_device(dev);{ //把添加的设备与该总线下的驱动进行一一适配 } } } } if (!dev || !of_match_node(matches, bus)) return 0; //如果该节点没有子节点或者分配失败,则返回。 for_each_child_of_node(bus, child){ //递归调用,将所有子节点均添加到platform bus下面。 of_platform_bus_create(child, matches, lookup, &dev->dev, strict); } } } } } } } } module_init(mt_i2c_init); { //I2c.c (kernel-3.10\drivers\misc\mediatek\i2c\mt6735) //注册I2C平台驱动,把符合I2C match的节点解析出来,添加到I2C BUS的设备链表中 platform_driver_register(&mt_i2c_driver); ... ... mt_i2c_probe(struct platform_device *pdev) { mt_i2c *i2c = kzalloc(sizeof(mt_i2c), GFP_KERNEL); i2c->adap.dev.of_node = pdev->dev.of_node; i2c_add_numbered_adapter(&i2c->adap); { //注册i2c的adapter __i2c_add_numbered_adapter(adap);{ i2c_register_adapter(adap);{ adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; device_register(&adap->dev); } } } of_i2c_register_devices(&i2c->adap); { //注册I2C adapter节点下面的全部I2C设备 for_each_available_child_of_node(adap->dev.of_node, node) { i2c_new_device(adap, &info); { struct i2c_client *client = kzalloc(sizeof *client, GFP_KERNEL); client->dev.bus = &i2c_bus_type; client->dev.type = &i2c_client_type; device_register(&client->dev);{ device_add(dev);{ bus_add_device(dev);{ klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); } device_pm_add(dev);{ list_add_tail(&dev->power.entry, &dpm_list); } } } } } } } } module_init ( mt_spi_init );{ //Spi.c (kernel-3.10\drivers\misc\mediatek\spi\mt6735) //注册I2C平台驱动,把符合spi match的节点解析出来,添加到SPI BUS的设备链表中 platform_driver_register ( &mt_spi_driver ); ... ... mt_spi_probe(struct platform_device *pdev){ struct spi_master *master = spi_alloc_master(&pdev->dev, sizeof(struct mt_spi_t)); ms = spi_master_get_devdata(master); ms->pdev = pdev; spi_register_master ( master );{ of_spi_register_master(master);{ //这里面没有继续向后分析 } device_add(&master->dev);{ //老生常谈的步骤,就不再继续分析。 } list_add_tail(&master->list, &spi_master_list); /* Register devices from the device tree and ACPI */ of_register_spi_devices(master);{ for_each_available_child_of_node(master->dev.of_node, nc){ spi = spi_alloc_device(master);{ spi = kzalloc(sizeof *spi, GFP_KERNEL); spi->master = master; spi->dev.bus = &spi_bus_type; } spi_add_device(spi);{ device_add(&spi->dev);{ //老生常谈的步骤,不再分析。 } } } } acpi_register_spi_devices(master);{ //没有具体分析,不清楚什么功能 } } } }
五、initcall的执行顺序:
#define pure_initcall(fn) __define_initcall(fn, 0) #define core_initcall(fn) __define_initcall(fn, 1) #define core_initcall_sync(fn) __define_initcall(fn, 1s) #define postcore_initcall(fn) __define_initcall(fn, 2) #define postcore_initcall_sync(fn) __define_initcall(fn, 2s) #define arch_initcall(fn) __define_initcall(fn, 3) #define arch_initcall_sync(fn) __define_initcall(fn, 3s) #define subsys_initcall(fn) __define_initcall(fn, 4) #define subsys_initcall_sync(fn) __define_initcall(fn, 4s) #define fs_initcall(fn) __define_initcall(fn, 5) #define fs_initcall_sync(fn) __define_initcall(fn, 5s) #define rootfs_initcall(fn) __define_initcall(fn, rootfs) #define device_initcall(fn) __define_initcall(fn, 6) #define device_initcall_sync(fn) __define_initcall(fn, 6s) #define late_initcall(fn) __define_initcall(fn, 7) #define late_initcall_sync(fn) __define_initcall(fn, 7s) 若等级相同,则根据编译进目标代码的顺序执行。
注意Init.h (kernel-3.10\include\linux) 中的宏的定义:
在编译进内核时,MODULE 是没有定义的,所以 #ifndef MODULE 是真的;
当编译成.ko模块时,MODULE 是已经定义的, #ifndef MODULE 为假;
也就是根据编译到的目的地方不同,所定义的宏也不一致:
例如:
#ifndef MODULE
#define arch_initcall(fn) __define_initcall(fn, 3)
#else
#define arch_initcall(fn) module_init(fn)
#endif
在MODULE被定义的情况下(大部分可动态加载的driver模块都属于此, obj-m),module_init定义如下:
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
这段宏定义关键点是后面一句,通过alias将initfn变名为init_module。
前面那个__inittest的定义其实是种技巧,用来对initfn进行某种静态的类型检查,
如果阁下将模块初始化函数定义成,比如,void gpio_init(void)或者是int gpio_init(int),
那么在编译时都会有类似下面的warning:
GPIO/fsl-gpio.c: In function '__inittest':
GPIO/fsl-gpio.c:46: warning: return from incompatible pointer type
通过module_init将模块初始化函数统一别名为init_module,这样以后insmod时候,
在系统内部会调用sys_init_module()去找到init_module函数的入口地址。
如果objdump -t gpio.ko,就会发现init_module和gpio_init位于相同的地址偏移处。
简言之,这种情况下模块的初始化函数在insmod时候被调用
六、编译和反编译设备树
单独编译设备树:
cd linux-x.xx & make dtbs
生成的dtb在目录linux-x.xx/arch/xxx/boot/dts下;
Android系统对应的目录为:out\target\product\rq6735_35gt_b_l1\obj\KERNEL_OBJ\arch\arm\boot\dts下;
生成的目标文件的后缀为.dtb。
利用dtc工具,反编译dtb,生成dts:
Linux源码生成的工具路径:linux-x.xx/scripts/dtc/dtc
Android源码生成的工具路径:out\target\product\rq6735_35gt_b_l1\obj\KERNEL_OBJ\scripts\dtc\dtc
./dtc -I dtb -O dts xxxx.dtb -o xxxx.dts