解析設備樹的流程


一、匯編相關部分的代碼流程分析

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 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM