首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

powerpc linux下dts解析过程详解

powerpc linux下dts解析过程详解

一. 在linux中,对dtb文件解析的整个过程序如下:1)首先将从u-boot 传递过来的映像基地址和dtb 文件映像基地址保存通用寄存器r30,r31;
2)通过调用machine_init()、early_init_devtree()函数来获取内核前期初始化所需的bootargs,cmd_line等系统引导参数;
3)调用start_kernel()、setup_arch()、unflatten_device_tree()函数来解析dtb 文件,构建一个由device_node 结构连接而成的单项链表,并使用全局变量allnodes 指针来保存这个链表的头指针;如下在此函数执行过后,在内存中会存在一个如下的链表,后面所有的函数,如果需要从of tree结构上读取设备资料的,都将从这个链表中遍历并读取。


4)内核调用OF 提供的API 函数获取allnodes链表信息来初始化内核其他子系统、设备等。(of_flatform_device)

二、dts文件首地址解析在进行DTS文件解析之前先要从bootm启动命令中获取dtb文件所在的地址。而这一步的开始是从arch/powerpc/kernel/head_32.s文件开始中的。
1、arch/powerpc/kernel/head_32.s
* This is where the main kernel code starts.
*/
start_here:

/* ptr to current */

lis
r2,init_task@h

ori
r2,r2,init_task@l

/* Set up for using our exception vectors */

/* ptr to phys current thread */

tophys(r4,r2)

addi
r4,r4,THREAD
/* init task's THREAD */

CLR_TOP32(r4)

mtspr
SPRN_SPRG_THREAD,r4

li
r3,0

mtspr
SPRN_SPRG_RTAS,r3
/* 0 => not in RTAS */

/* stack */

lis
r1,init_thread_union@ha

addi
r1,r1,init_thread_union@l

li
r0,0

stwu
r0,THREAD_SIZE-STACK_FRAME_OVERHEAD(r1)
/*
* Do early platform-specific initialization,
* and set up the MMU.
*/

mr
r3,r31

mr
r4,r30

bl
machine_init  //在这个函数中


bl
__save_cpu_setup

bl
MMU_init



notrace void __init machine_init(unsigned long dt_ptr)
{

lockdep_init();



/* Enable early debugging if any specified (see udbg.h) */

udbg_early_init();



/* Do some early initialization based on the flat device tree */

early_init_devtree(__va(dt_ptr));




probe_machine();



setup_kdump_trampoline();


#ifdef CONFIG_6xx

if (cpu_has_feature(CPU_FTR_CAN_DOZE) ||

   cpu_has_feature(CPU_FTR_CAN_NAP))

ppc_md.power_save = ppc6xx_idle;
#endif


#ifdef CONFIG_E500

if (cpu_has_feature(CPU_FTR_CAN_DOZE) ||

   cpu_has_feature(CPU_FTR_CAN_NAP))

ppc_md.power_save = e500_idle;
#endif

if (ppc_md.progress)

ppc_md.progress("id mach(): done", 0x200);
}


void __init early_init_devtree(void *params)
{

phys_addr_t limit;



DBG(" -> early_init_devtree(%p)\n", params);



/* Setup flat device-tree pointer */

initial_boot_params = params;


#ifdef CONFIG_PPC_RTAS

/* Some machines might need RTAS info for debugging, grab it now. */

of_scan_flat_dt(early_init_dt_scan_rtas, NULL);
#endif


#ifdef CONFIG_PHYP_DUMP

/* scan tree to see if dump occured during last boot */

of_scan_flat_dt(early_init_dt_scan_phyp_dump, NULL);
#endif



/* Retrieve various informations from the /chosen node of the

* device-tree, including the platform type, initrd location and

* size, TCE reserve, and more ...

*/

of_scan_flat_dt(early_init_dt_scan_chosen, NULL);



/* Scan memory nodes and rebuild LMBs */

lmb_init();

of_scan_flat_dt(early_init_dt_scan_root, NULL);

of_scan_flat_dt(early_init_dt_scan_memory, NULL);



/* Save command line for /proc/cmdline and then parse parameters */

strlcpy(boot_command_line, cmd_line, COMMAND_LINE_SIZE);

parse_early_param();



/* Reserve LMB regions used by kernel, initrd, dt, etc... */

lmb_reserve(PHYSICAL_START, __pa(klimit) - PHYSICAL_START);

/* If relocatable, reserve first 32k for interrupt vectors etc. */

if (PHYSICAL_START > MEMORY_START)

lmb_reserve(MEMORY_START, 0x8000);

reserve_kdump_trampoline();

reserve_crashkernel();

early_reserve_mem();

phyp_dump_reserve_mem();



limit = memory_limit;

if (! limit) {

phys_addr_t memsize;



/* Ensure that total memory size is page-aligned, because

* otherwise mark_bootmem() gets upset. */

lmb_analyze();

memsize = lmb_phys_mem_size();

if ((memsize & PAGE_MASK) != memsize)

limit = memsize & PAGE_MASK;

}

lmb_enforce_memory_limit(limit);



lmb_analyze();

lmb_dump_all();



DBG("Phys. mem: %llx\n", lmb_phys_mem_size());



/* We may need to relocate the flat tree, do it now.

* FIXME .. and the initrd too? */

move_device_tree();



DBG("Scanning CPUs ...\n");



/* Retreive CPU related informations from the flat tree

* (altivec support, boot CPU ID, ...)

*/

of_scan_flat_dt(early_init_dt_scan_cpus, NULL);



DBG(" <- early_init_devtree()\n");
}
三、解析main.c-------------->arch/pwerpc/kernel/setup_32.c-------------->arch/powerpc/kernel/prom.c
start_kernel()------>setup_arch()---------->unflatten_device_tree()

/**
* unflattens the device-tree passed by the firmware, creating the
* tree of struct device_node. It also fills the "name" and "type"
* pointers of the nodes so the normal device-tree walking functions
* can be used (this used to be done by finish_device_tree)
*/
void __init unflatten_device_tree(void)
{

unsigned long start, mem, size;

struct device_node **allnextp = &allnodes;



DBG(" -> unflatten_device_tree()\n");



/* First pass, scan for size */

start = ((unsigned long)initial_boot_params) +

initial_boot_params->off_dt_struct;

size = unflatten_dt_node(0, &start, NULL, NULL, 0);

size = (size | 3) + 1;



DBG("  size is %lx, allocating...\n", size);



/* Allocate memory for the expanded device tree */

mem = lmb_alloc(size + 4, __alignof__(struct device_node));

mem = (unsigned long) __va(mem);



((u32 *)mem)[size / 4] = 0xdeadbeef;



DBG("  unflattening %lx...\n", mem);



/* Second pass, do actual unflattening */

start = ((unsigned long)initial_boot_params) +

initial_boot_params->off_dt_struct;

unflatten_dt_node(mem, &start, NULL, &allnextp, 0);

if (*((u32 *)start) != OF_DT_END)

printk(KERN_WARNING "Weird tag at end of tree: %08x\n", *((u32 *)start));

if (((u32 *)mem)[size / 4] != 0xdeadbeef)

printk(KERN_WARNING "End of tree marker overwritten: %08x\n",

      ((u32 *)mem)[size / 4] );

*allnextp = NULL;



/* Get pointer to OF "/chosen" node for use everywhere */

of_chosen = of_find_node_by_path("/chosen");

if (of_chosen == NULL)

of_chosen = of_find_node_by_path("/chosen@0");



DBG(" <- unflatten_device_tree()\n");
}
返回列表