1 背景 通常情况下,桌面机和服务器可以兼容大部分软件。最好的结果就是当添加新硬件时,无需重新编译Linux内核。标准的固件接口可以保证bootloader将正确的参数传递给内核。PC可以采用bios,PowerPC and Sparc采用Open-Firmware接口。但是对于嵌入式系统,软件差别太大,内核本身也是定制的,bootloader只需要传递很少的参数,因为大部分信息都是硬编码在系统的配置中的。这样同一个内核映像很难同时使用在多个不同的平台上。
2 设备树的描述方式 简单的说设备书是一种描述硬件配置信息的数据结构,包括CPU,内存,总线及相关外设。内核启动时可以解析这些信息,以此决定如何配置内核及加载那些驱动。该数据结构有一个单一的根节点“/”。每个节点有个名字并可以包含多个子节点。数据的格式遵循IEEE standard 1275。
Device tree source (.dts)采用一种易编辑的文本方式来表达设备树,device tree compiler tool (dtc)将.dts转换成binary device tree blob(.dtb)。设备树并不是控制系统设备的唯一方法,比如内核对USB和PCI已经有非常方便的检测机制。
#if defined(CONFIG_OF_LIBFDT) if (of_flat_tree) { /* device tree; boot new style */ /* * Linux Kernel Parameters (passing device tree): * r3: pointer to the fdt, followed by the board info data * r4: physical pointer to the kernel itself * r5: NULL * r6: NULL * r7: NULL */ debug (" Booting using OF flat tree...\n"); (*kernel) ((bd_t *)of_flat_tree, (ulong)kernel, 0, 0, 0); /* does not return */ } else #endif { /* * Linux Kernel Parameters (passing board info data): * r3: ptr to board info data * r4: initrd_start or 0 if no initrd * r5: initrd_end - unused if r4 is 0 * r6: Start of command line string * r7: End of command line string */ debug (" Booting using board info...\n"); (*kernel) (kbd, initrd_start, initrd_end, cmd_start, cmd_end); /* does not return */ }
Head_32.S /* * This is where the main kernel code starts. */ start_here: 。。。。 /* * Do early platform-specific initialization, * and set up the MMU. */ mr r3,r31 mr r4,r30 bl machine_init
/* * Find out what kind of machine we're on and save any data we need * from the early boot process (devtree is copied on pmac by prom_init()). * This is called very early on the boot process, after a minimal * MMU environment has been set up but before MMU_init is called. */ void __init machine_init(unsigned long dt_ptr, unsigned long phys) { /* If btext is enabled, we might have a BAT setup for early display, * thus we do enable some very basic udbg output */ #ifdef CONFIG_BOOTX_TEXT udbg_putc = btext_drawchar; #endif
/* Do some early initialization based on the flat device tree */ early_init_devtree(__va(dt_ptr)); }
/* Warning, IO base is not yet inited */ void __init setup_arch(char **cmdline_p) { *cmdline_p = cmd_line;
/* so udelay does something sensible, assume <= 1000 bogomips */ loops_per_jiffy = 500000000 / HZ;
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;
/* 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("");