zoukankan      html  css  js  c++  java
  • 解析设备树的流程

    一、汇编相关部分的代码流程分析

    ENTRY(stext)	{//head.S (kernel-3.10archarmkernel)	
      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.10archarmkernel)
      b	__enable_mmu	{
        b	__turn_mmu_on {
          mov	r3, r13
          mov	pc, r3	{	     //调到r13中存放的地址去执行,即执行__mmap_switched函数。
            b	start_kernel		//跳转到start_kernel函数去执行,该函数具体在kernel-3.10initMain.c
          }
        }
      }
    }
    

    二、C相关的函数流程

    start_kernel(void)	{	// kernel-3.10initMain.c
      setup_arch(&command_line);	{	// kernel-3.10archarmkernelSetup.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.10driversof)	文件中共享
            of_aliases = of_find_node_by_path("/aliases");	//注意变量  of_aliases, 在Base.c (kernel-3.10driversof)	文件中共享
            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.10driversmiscmediatekmachmt6735)	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.10archarmkernel)			// 解析设备树中的所有顶层的节点和符合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.10driversmiscmediateki2cmt6735)	//注册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.10driversmiscmediatekspimt6735)	//注册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.10includelinux) 中的宏的定义:
    在编译进内核时,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 argetproduct q6735_35gt_b_l1objKERNEL_OBJarcharmootdts下;

    生成的目标文件的后缀为.dtb。

    利用dtc工具,反编译dtb,生成dts:

      Linux源码生成的工具路径:linux-x.xx/scripts/dtc/dtc

      Android源码生成的工具路径:out argetproduct q6735_35gt_b_l1objKERNEL_OBJscriptsdtcdtc

      ./dtc -I dtb -O dts xxxx.dtb -o xxxx.dts 

  • 相关阅读:
    php连接mysql数据库
    关于chrome控制台警告:Synchronous XMLHttpRequest on the main thread
    mac“打不开身份不明的开发者”
    微信小程序开发warning: Now you can provide attr "wx:key" for a "wx:for" to improve performance
    将任何GitHub内的代码转为外部CDN网址
    把自己的代码发布到npm(npm publish)
    wordpress写文章添加gif图片变成静态图片的解决办法
    canvas实现刮刮乐
    api文档管理系统合集
    niubi-job:一个分布式的任务调度框架设计原理以及实现
  • 原文地址:https://www.cnblogs.com/hei-da-mi/p/6066493.html
Copyright © 2011-2022 走看看