欢迎来到三一办公! | 帮助中心 三一办公31ppt.com(应用文档模板下载平台)
三一办公
全部分类
  • 办公文档>
  • PPT模板>
  • 建筑/施工/环境>
  • 毕业设计>
  • 工程图纸>
  • 教育教学>
  • 素材源码>
  • 生活休闲>
  • 临时分类>
  • ImageVerifierCode 换一换
    首页 三一办公 > 资源分类 > DOC文档下载  

    你了解linux device tree源代码?.doc

    • 资源ID:4927267       资源大小:142.50KB        全文页数:35页
    • 资源格式: DOC        下载积分:10金币
    快捷下载 游客一键下载
    会员登录下载
    三方登录下载: 微信开放平台登录 QQ登录  
    下载资源需要10金币
    邮箱/手机:
    温馨提示:
    用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)
    支付方式: 支付宝    微信支付   
    验证码:   换一换

    加入VIP免费专享
     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    你了解linux device tree源代码?.doc

    你了解linux device tree源代码?/Based on Linux v3.14 source codeLinux设备树机制(Device Tree)一、描述ARM Device Tree起源于OpenFirmware (OF),在过去的Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。为了改变这种局面,Linux社区的大牛们参考了PowerPC等体系架构中使用的Flattened Device Tree(FDT),也采用了Device Tree结构,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。Device Tree是一种描述硬件的数据结构,由一系列被命名的结点(node)和属性(property)组成,而结点本身可包含子结点。所谓属性,其实就是成对出现的name和value。在Device Tree中,可描述的信息包括(原先这些信息大多被hard code到kernel中):CPU的数量和类别,内存基地址和大小,总线和桥,外设连接,中断控制器和中断使用情况,GPIO控制器和GPIO使用情况,Clock控制器和Clock使用情况。通常由.dts文件以文本方式对系统设备树进行描述,经过Device Tree Compiler(dtc)将dts文件转换成二进制文件binary device tree blob(dtb),.dtb文件可由Linux内核解析,有了device tree就可以在不改动Linux内核的情况下,对不同的平台实现无差异的支持,只需更换相应的dts文件,即可满足。二、相关结构体1.U-Boot需要将设备树在内存中的存储地址传给内核。该树主要由三大部分组成:头(Header)、结构块(Structure block)、字符串块(Strings block)。设备树在内存中的存储布局图。-base -> | struct boot_param_header |-| (alignment gap) (*) |-| memory reserve map |-| (alignment gap) |-| | device-tree structure | |-| (alignment gap) |-| | device-tree strings | |-> -|- (base + totalsize)1.1 头(header)头主要描述设备树的一些基本信息,例如设备树大小,结构块偏移地址,字符串块偏移地址等。偏移地址是相对于设备树头的起始地址计算的。struct boot_param_header _be32 magic;                /设备树魔数,固定为0xd00dfeed_be32 totalsize;            /整个设备树的大小_be32 off_dt_struct;        /保存结构块在整个设备树中的偏移_be32 off_dt_strings;        /保存的字符串块在设备树中的偏移_be32 off_mem_rsvmap;        /保留内存区,该区保留了不能被内核动态分配的内存空间_be32 version;            /设备树版本_be32 last_comp_version;    /向下兼容版本号_be32 boot_cpuid_phys;    /为在多核处理器中用于启动的主cpu的物理id_be32 dt_strings_size;    /字符串块大小_be32 dt_struct_size;     /结构块大小;1.2 结构块(struct block)设备树结构块是一个线性化的结构体,是设备树的主体,以节点node的形式保存了目标单板上的设备信息。在结构块中以宏OF_DT_BEGIN_NODE标志一个节点的开始,以宏OF_DT_END_NODE标识一个节点的结束,整个结构块以宏OF_DT_END结束。一个节点主要由以下几部分组成。(1)节点开始标志:一般为OF_DT_BEGIN_NODE。(2)节点路径或者节点的单元名(ersion=0x10以节点单元名表示)(3)填充字段(对齐到四字节)(4)节点属性。每个属性以宏OF_DT_PROP开始,后面依次为属性值的字节长度(4字节)、属性名称在字符串块中的偏移量(4字节)、属性值和填充(对齐到四字节)。(5)如果存在子节点,则定义子节点。(6)节点结束标志OF_DT_END_NODE。1.3 字符串块通过节点的定义知道节点都有若干属性,而不同的节点的属性又有大量相同的属性名称,因此将这些属性名称提取出一张表,当节点需要应用某个属性名称时直接在属性名字段保存该属性名称在字符串块中的偏移量。1.4 设备树源码 DTS 表示设备树源码文件(.dts)以可读可编辑的文本形式描述系统硬件配置设备树,支持 C/C+方式的注释,该结构有一个唯一的根节点“/”,每个节点都有自己的名字并可以包含多个子节点。设备树的数据格式遵循了 Open Firmware IEEE standard 1275。这个设备树中有很多节点,每个节点都指定了节点单元名称。每一个属性后面都给出相应的值。以双引号引出的内容为 ASCII 字符串,以尖括号给出的是 32 位的16进制值。这个树结构是启动 Linux 内核所需节点和属性简化后的集合,包括了根节点的基本模式信息、CPU 和物理内存布局,它还包括通过/chosen 节点传递给内核的命令行参数信息。1.5 machine_desc结构内核提供了一个重要的结构体struct machine_desc ,这个结构体在内核移植中起到相当重要的作用,内核通过machine_desc结构体来控制系统体系架构相关部分的初始化。machine_desc结构体通过MACHINE_START宏来初始化,在代码中, 通过在start_kernel->setup_arch中调用setup_machine_fdt来获取。struct machine_desc unsigned int nr; /* architecture number */const char *name; /* architecture name */unsigned long atag_offset; /* tagged list (relative) */const char *const *dt_compat; /* array of device tree* compaTIble strings */unsigned int nr_irqs; /* number of IRQs */#ifdef CONFIG_ZONE_DMAphys_addr_t dma_zone_size; /* size of DMA-able area */#endifunsigned int video_start; /* start of video RAM */unsigned int video_end; /* end of video RAM */unsigned char reserve_lp0 :1; /* never has lp0 */unsigned char reserve_lp1 :1; /* never has lp1 */unsigned char reserve_lp2 :1; /* never has lp2 */enum reboot_mode reboot_mode; /* default restart mode */struct smp_operaTIons *smp; /* SMP operaTIons */bool (*smp_init)(void);void (*fixup)(struct tag *, char *,struct meminfo *);void (*init_meminfo)(void);void (*reserve)(void);/* reserve mem blocks */void (*map_io)(void);/* IO mapping funcTIon */void (*init_early)(void);void (*init_irq)(void);void (*init_time)(void);void (*init_machine)(void);void (*init_late)(void);#ifdef CONFIG_MULTI_IRQ_HANDLERvoid (*handle_irq)(struct pt_regs *);#endifvoid (*restart)(enum reboot_mode, const char *);1.6 设备节点结构体struct device_node const char *name;    /设备nameconst char *type; /设备类型phandle phandle;const char *full_name; /设备全称,包括父设备名struct property *properties; /设备属性链表struct property *deadprops; /removed propertiesstruct device_node *parent; /指向父节点struct device_node *child; /指向子节点struct device_node *sibling; /指向兄弟节点struct device_node *next; /相同设备类型的下一个节点struct device_node *allnext; /next in list of all nodesstruct proc_dir_entry *pde; /该节点对应的procstruct kref kref;unsigned long _flags;void *data;#if defined(CONFIG_SPARC)const char *path_component_name;unsigned int unique_id;struct of_irq_controller *irq_trans;#endif;1.7 属性结构体struct property char *name;        /属性名int length;        /属性值长度void *value;        /属性值struct property *next; /指向下一个属性unsigned long _flags; /标志unsigned int unique_id;三、设备树初始化及解析分析Linux内核的源码,可以看到其对扁平设备树的解析流程如下:(1)首先在内核入口处将从u-boot传递过来的镜像基地址。(2)通过调用early_init_dt_scan()函数来获取内核前期初始化所需的bootargs,cmd_line等系统引导参数。(3)根据bootargs,cmd_line等系统引导参数进入start_kernel()函数,进行内核的第二阶段初始化。(4)调用unflatten_device_tree()函数来解析dtb文件,构建一个由device_node结构连接而成的单项链表,并使用全局变量of_allnodes指针来保存这个链表的头指针。(5)内核调用OF提供的API函数获取of_allnodes链表信息来初始化内核其他子系统、设备等。/kernel 初始化的代码(init/main.c)asmlinkage void _init start_kernel(void)./这个setup_arch就是各个架构自己的设置函数,哪个参与了编译就调用哪个,arm架构应当是arch/arm/kernel/setup.c中的 setup_arch。setup_arch(.void _init setup_arch(char *cmdline_p)const struct machine_desc *mdesc;setup_processor();/setup_machine_fdt函数获取内核前期初始化所需的bootargs,cmd_line等系统引导参数mdesc = setup_machine_fdt(_atags_pointer);/_atags_pointer是bootloader传递参数的物理地址if (!mdesc)mdesc = setup_machine_tags(_atags_pointer, _machine_arch_type);machine_desc = mdesc;machine_name = mdesc->name;if (mdesc->reboot_mode != REBOOT_HARD)reboot_mode = mdesc->reboot_mode;init_mm.start_code = (unsigned long) _text;init_mm.end_code = (unsigned long) _etext;init_mm.end_data = (unsigned long) _edata;init_mm.brk = (unsigned long) _end;strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);*cmdline_p = cmd_line;parse_early_param();sort(early_paging_init(mdesc, lookup_processor_type(read_cpuid_id();setup_dma_zone(mdesc);sanity_check_meminfo();arm_memblock_init(paging_init(mdesc);request_standard_resources(mdesc);if (mdesc->restart)arm_pm_restart = mdesc->restart;/解析设备树unflatten_device_tree();.(一)函数获取内核前期初始化所需的bootargs,cmd_line等系统引导参数1. setup_machine_fdt()函数获取内核前期初始化所需的bootargs,cmd_line等系统引导参数。const struct machine_desc * _init setup_machine_fdt(unsigned int dt_phys)const struct machine_desc *mdesc, *mdesc_best = NULL;#ifdef CONFIG_ARCH_MULTIPLATFORMDT_MACHINE_START(GENERIC_DT, "Generic DT based system")MACHINE_ENDmdesc_best = #endif/bootloader传递参数的物理地址不为空,并将物理地址转化为虚拟地址,/通过函数early_init_dt_scan从设备树中读出bootargs,cmd_line等系统引导参数。if (!dt_phys | !early_init_dt_scan(phys_to_virt(dt_phys)return NULL;/根据设备树中根节点属性"compatible"的属性描述,找到系统中定义的最匹配的machine_desc结构,该结构控制系统体系架构相关部分的初始化mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);if (!mdesc) const char *prop;long size;unsigned long dt_root;early_print("nError: unrecognized/unsupported ""device tree compatible list:n ");/找到设备树的根节点,dt_root指向根节点的属性地址处dt_root = of_get_flat_dt_root();/读出根节点的"compatible"属性的属性值prop = of_get_flat_dt_prop(dt_root, "compatible", /将根节点的"compatible"属性的属性值打印出来while (size > 0) early_print("%s ", prop);size -= strlen(prop) + 1;prop += strlen(prop) + 1;early_print("nn");dump_machine_table(); /* does not return */Change machine number to match the mdesc were using_machine_arch_type = mdesc->nr;return mdesc;struct boot_param_header *initial_boot_params;bool _init early_init_dt_scan(void *params)if (!params)return false;/参数params是bootloader传递参数的物理地址转化为的虚拟地址,保存设备树起始地址initial_boot_params = params;/验证设备树的magic if (be32_to_cpu(initial_boot_params->magic) != OF_DT_HEADER) initial_boot_params = NULL;return false;/从设备树中读取chosen节点的信息,包括命令行boot_command_line,initrd location及sizeof_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);/得到根节点的size,address-cells信息of_scan_flat_dt(early_init_dt_scan_root, NULL);/读出设备树的系统内存设置of_scan_flat_dt(early_init_dt_scan_memory, NULL);return true;int _init of_scan_flat_dt(int (*it)(unsigned long node,const char *uname, int depth,void *data),void *data)/找到设备树中结构块的地址unsigned long p = (unsigned long)initial_boot_params) + be32_to_cpu(initial_boot_params->off_dt_struct);int rc = 0;int depth = -1;do /获得节点起始标志,即OF_DT_BEGIN_NODE,OF_DT_PROP等u32 tag = be32_to_cpup(_be32 *)p);const char *pathp;p += 4;/跳过节点起始标志/如果是OF_DT_END_NODE标志,表示该节点结束,继续下一结点if (tag = OF_DT_END_NODE) depth-;continue;/OF_DT_NOP标志代表空节点if (tag = OF_DT_NOP)continue;/OF_DT_END标志整个结构块结束if (tag = OF_DT_END)break;/OF_DT_PROP标示属性,Property:属性值的字节长度、属性名称在字符串块中的偏移量、属性值和填充。if (tag = OF_DT_PROP) /属性值的字节大小u32 sz = be32_to_cpup(_be32 *)p);p += 8;/跳过属性值的字节长度、属性名称在字符串块中的偏移量if (be32_to_cpu(initial_boot_params->version) = 8 ? 8 : 4);/跳过该属性值的大小p += sz;/地址对齐p = ALIGN(p, 4);/表示一个属性节点遍历完成,因为这里并不是寻找属性节点,而是找OF_DT_BEGIN_NODE开始的节点continue;/若都不是以上节点类型,也不是节点开始标示(OF_DT_BEGIN_NODE),则出错返回if (tag != OF_DT_BEGIN_NODE) pr_err("Invalid tag %x in flat device tree!n", tag);return -EINVAL;/执行到这里,标示tag=OF_DT_BEGIN_NODE,表示一个节点的开始,探索深度加1depth+;/节点路径或者节点名pathp = (char *)p;/节点地址四字节对齐,然后p指向节点属性地址p = ALIGN(p + strlen(pathp) + 1, 4);/如果是节点路径,则返回路径名的最后一段,假如为/root/my_root,则返回my_root,即获得节点名if (*pathp = /)pathp = kbasename(pathp);/调用相应的节点处理函数,p指向节点属性地址rc = it(p, pathp, depth, data);if (rc != 0)break; while (1);return rc;1.1 chosen节点/chosen 节点并不代表一个真实的设备,只是作为一个为固件和操作系统之间传递数据的地方,比如引导参数。chosen 节点里的数据也不代表硬件。通常,chosen 节点在.dts 源文件中为空,并在启动时填充。在我们的示例系统中,固件可以往 chosen 节点添加以下信息:/chosen /    bootargs = "root=/dev/nfs rw nfsroot=192.168.1.1 console=ttyS0,115200" /节点属性/    linux,initrd-start =  /节点属性/    linux,initrd-end =  /节点属性/;int _init early_init_dt_scan_chosen(unsigned long node, const char *uname,int depth, void *data)unsigned long l;char *p;pr_debug("search "chosen", depth: %d, uname: %sn", depth, uname);/depth深度要为1,表示在根节点下(一般根节点/的depth为0)/data表示系统启动命令行boot_command_line要分配空间/检查节点名是否为chosen节点    if (depth != 1 | !data | (strcmp(uname, "chosen") != 0 ">return 0;/从设备树的chosen节点中读出initrd的起始、结束地址early_init_dt_check_for_initrd(node);/设备树的chosen节点中读取bootargs属性的属性值,并拷贝给boot_command_linep = of_get_flat_dt_prop(node, "bootargs", if (p != NULL ">strlcpy(data, p, min(int)l, COMMAND_LINE_SIZE);pr_debug("Command line is: %sn", (char*)data);return 1;static void _init early_init_dt_check_for_initrd(unsigned long node)u64 start, end;unsigned long len;_be32 *prop;pr_debug("Looking for initrd properties. ");/返回该chosen节点中属性名为"linux,initrd-start"的地址prop = of_get_flat_dt_prop(node, "linux,initrd-start", if (!prop)return;/从该地址读出initrd-start的地址start = of_read_number(prop, len/4);/返回该chosen节点中属性名为"linux,initrd-end"的地址prop = of_get_flat_dt_prop(node, "linux,initrd-end", if (!prop)return;/从该地址读出initrd-end的地址end = of_read_number(prop, len/4);/将读出的地址赋值给全局变量initrd_start和initrd_end,用于跟文件系统的挂载initrd_start = (unsigned long)_va(start);initrd_end = (unsigned long)_va(end);initrd_below_start_ok = 1;pr_debug("initrd_start=0x%llx initrd_end=0x%llxn",(unsigned long long)start, (unsigned long long)end);void *_init of_get_flat_dt_prop(unsigned long node, const char *name,unsigned long *size)return of_fdt_get_property(initial_boot_params, node, name, size);void *of_fdt_get_property(struct boot_param_header *blob,unsigned long node, const char *name,unsigned long *size)/p指向该chosen节点的节点属性地址unsigned long p = node;/因为一个节点中可能包含多个属性,所以这里遍历chosen节点中的所有属性,找到属性名为name的属性do /取得该节点属性的起始标志OF_DT_PROPu32 tag = be32_to_cpup(_be32 *)p);u32 sz, noff;const char *nstr;p += 4;/跳过节点属性的起始标志/空节点则继续if (tag = OF_DT_NOP)continue;/非属性标志则返回NULLif (tag != OF_DT_PROP)return NULL;/运行到这里表示为属性OF_DT_PROP/取得该节点属性的的属性值sizesz = be32_to_cpup(_be32 *)p);/取得该属性名的在字符串块中的偏移值noff = be32_to_cpup(_be32 *)(p + 4);p += 8;/跳过对齐填充字段if (be32_to_cpu(blob->version) = 8 ? 8 : 4);/在字符串块取出该属性的名称nstr = of_fdt_get_string(blob, noff);if (nstr = NULL) pr_warning("Cant find property index name !n");return NULL;/若名称一致,表示找到我们要找的属性if (strcmp(name, nstr) = 0) if (size)*size = sz;/返回该属性的属性值size/返回该属性值所在的地址return (void *)p;/否则继续下一个属性p += sz;p = ALIGN(p, 4); while (1);char *of_fdt_get_string(struct boot_param_header *blob, u32 offset)/从设备树的字符串块的offset处读出namereturn (char *)blob) + be32_to_cpu(blob->off_dt_strings) + offset;static inline u64 of_read_number(const _be32 *cell, int size)u64 r = 0;/读出属性值,属性值大小为sizewhile (size-)r = (r  1表示地址32位,2表示地址64位。#size-cells = 1表示rangs的每部分占一个cell,依此类推 /*/ compatible = "sprd,spx15"#address-cells = #size-cells = interrupt-parent = linux,initrd-end = */所以本函数就是读取根节点的"#address-cells","#size-cells"属性int _init early_init_dt_scan_root(unsigned long node, const char *uname,int depth, void *data)_be32 *prop;/根节点的探索深度depth一定为0,否则不是根节点"/",node为根节点的属性地址    if (depth != 0)return 0;dt_root_size_cells = OF_ROOT_NODE_SIZE_CELLS_DEFAULT;dt_root_addr_cells = OF_ROOT_NODE_ADDR_CELLS_DEFAULT;/返回根节点节点属性名为"#size-cells"的地址prop = of_get_flat_dt_prop(node, "#size-cells", NULL);if (prop)dt_root_size_cells = be32_to_cpup(prop);/从该属性获得root sizepr_debug("dt_root_size_cells = %xn", dt_root_size_cells);/返回根节点节点属性名为"#address-cells"的地址prop = of_get_flat_dt_prop(node, "#address-cells", NULL);if (prop)dt_root_addr_cells = be32_to_cpup(prop);/从该属性获得root addresspr_debug("dt_root_addr_cells = %xn", dt_root_addr_cells);return 1;1.3 memory节点/memory节点用于描述目标板上物理内存范围,一般称作/memory节点,可以有一个或多个。当有多个节点时,需要后跟单元地址予以区分;只有一个单元地址时,可以不写单元地址,默认为0。此节点包含板上物理内存的属性,一般要指定device_type(固定为"memory")和reg属性。其中reg的属性值以的形式给出,如下示例中目标板内存起始地址为0x80000000,大小为0x20000000字节。 /memory /    device_type = "memory"/    reg = /;int _init early_init_dt_scan_memory(unsigned long node, const char *uname,int depth, void *data)/获取该节点中属性"device_type"的属性值char *type = of_get_flat_dt_prop(node, "device_type", NULL);_be32 *reg, *endp;unsigned long l;/检查"device_type"的属性值,确定该节点是否为memory节点if (type = NULL) if (depth != 1 | strcmp(uname, "memory0") != 0)return 0; else if (strcmp

    注意事项

    本文(你了解linux device tree源代码?.doc)为本站会员(sccc)主动上传,三一办公仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知三一办公(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    备案号:宁ICP备20000045号-2

    经营许可证:宁B2-20210002

    宁公网安备 64010402000987号

    三一办公
    收起
    展开