六 memory&chosen节点

根节点那一节我们说过,最简单的设备树也必须包含cpus节点和memory节点。memory节点用来描述硬件内存布局的。如果有多块内存,既可以通过多个memory节点表示,也可以通过一个memory节点的reg属性的多个元素支持。举一个例子,假如某个64位的系统有两块内存,分别是

• RAM: 起始地址 0x0, 长度 0x80000000 (2GB)
• RAM: 起始地址 0x100000000, 长度 0x100000000 (4GB)

对于64位的系统,根节点的#address-cells属性和#size-cells属性都设置成2。一个memory节点的形式如下(还记得前几节说过节点地址必须和reg属性第一个地址相同的事情吧):
    memory@0 {
        device_type = "memory";
        reg = <0x000000000 0x00000000 0x00000000 0x80000000
               0x000000001 0x00000000 0x00000001 0x00000000>;
    };

两个memory节点的形式如下:
    memory@0 {
        device_type = "memory";
        reg = <0x000000000 0x00000000 0x00000000 0x80000000>;
    };
    memory@100000000 {
        device_type = "memory";
        reg = <0x000000001 0x00000000 0x00000001 0x00000000>;
    };

chosen节点也位于根节点下,该节点用来给内核传递参数(不代表实际硬件)。对于Linux内核,该节点下最有用的属性是bootargs,该属性的类型是字符串,用来向Linux内核传递cmdline。规范中还定义了stdout-path和stdin-path两个可选的、字符串类型的属性,这两个属性的目的是用来指定标准输入输出设备的,在linux中,这两个属性基本不用。

memory和chosen节点在内核初始化的代码都位于start_kernel()->setup_arch()->setup_machine_fdt()->early_init_dt_scan_nodes()函数中(位于drivers/of/fdt.c),复制代码如下(本节所有代码都来自官方内核4.4-rc7版本):

1078 void __init early_init_dt_scan_nodes(void)
1079 {      
1080     /* Retrieve various information from the /chosen node */
1081     of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
1082 
1083     /* Initialize {size,address}-cells info */
1084     of_scan_flat_dt(early_init_dt_scan_root, NULL);
1085 
1086     /* Setup memory, calling early_init_dt_add_memory_arch */
1087     of_scan_flat_dt(early_init_dt_scan_memory, NULL);
1088 }

of_scan_flat_dt函数扫描整个设备树,实际的动作是在回调函数中完成的。第1081行是对chosen节点操作,该行代码的作用是将节点下的bootargs属性的字符串拷贝到boot_command_line指向的内存中。boot_command_line是内核的一个全局变量,在内核的多处都会用到。第1084行是根据根节点的#address-cells属性和#size-cells属性初始化全局变量dt_root_size_cells和dt_root_addr_cells,还记得前边说过如果没有设置属性的话就用默认值,这些都在early_init_dt_scan_root函数中实现。第1087行是对内存进行初始化,复制early_init_dt_scan_memory部分代码如下:

 893 /**
 894  * early_init_dt_scan_memory - Look for an parse memory nodes
 895  */
 896 int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
 897                      int depth, void *data)
 898 {
 899     const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
 900     const __be32 *reg, *endp;
 901     int l;
 902 
 903     /* We are scanning "memory" nodes only */
 904     if (type == NULL) {
 905         /*
 906          * The longtrail doesn't have a device_type on the
 907          * /memory node, so look for the node called /memory@0.
 908          */
 909         if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0)
 910             return 0;
 911     } else if (strcmp(type, "memory") != 0)
 912         return 0;
 913 
 914     reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
 915     if (reg == NULL)
 916         reg = of_get_flat_dt_prop(node, "reg", &l);
 917     if (reg == NULL)
 918         return 0;
 919 
 920     endp = reg + (l / sizeof(__be32));
 921 
 922     pr_debug("memory scan node %s, reg size %d,\n", uname, l);
 923 
 924     while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
 925         u64 base, size;
 926 
 927         base = dt_mem_next_cell(dt_root_addr_cells, &reg);
 928         size = dt_mem_next_cell(dt_root_size_cells, &reg);
 929
 930         if (size == 0)
 931             continue;
 932         pr_debug(" - %llx ,  %llx\n", (unsigned long long)base,
 933             (unsigned long long)size);
 934 
 935         early_init_dt_add_memory_arch(base, size);
 936     }
 937 
 938     return 0;
 939 }

第914行可以看出linux内核不仅支持reg属性,也支持linux,usable-memory属性。对于dt_root_addr_cells和dt_root_size_cells的使用也能看出根节点的#address-cells属性和#size-cells属性都是用来描述内存地址和大小的。得到每块内存的起始地址和大小后,在第935行调用early_init_dt_add_memory_arch函数,复制代码如下:
 
 983 void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
 984 {
 985     const u64 phys_offset = __pa(PAGE_OFFSET);
 986 
 987     if (!PAGE_ALIGNED(base)) {
 988         if (size < PAGE_SIZE - (base & ~PAGE_MASK)) {
 989             pr_warn("Ignoring memory block 0x%llx - 0x%llx\n",
 990                 base, base + size);
 991             return;
 992         }
 993         size -= PAGE_SIZE - (base & ~PAGE_MASK);
 994         base = PAGE_ALIGN(base);
 995     }
 996     size &= PAGE_MASK;
 997 
 998     if (base > MAX_MEMBLOCK_ADDR) {
 999         pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
1000                 base, base + size);
1001         return;
1002     }
1003 
1004     if (base + size - 1 > MAX_MEMBLOCK_ADDR) {
1005         pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",
1006                 ((u64)MAX_MEMBLOCK_ADDR) + 1, base + size);
1007         size = MAX_MEMBLOCK_ADDR - base + 1;
1008     }
1009 
1010     if (base + size < phys_offset) {
1011         pr_warning("Ignoring memory block 0x%llx - 0x%llx\n",
1012                base, base + size);
1013         return;
1014     }
1015     if (base < phys_offset) {
1016         pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",
1015     if (base < phys_offset) {
1016         pr_warning("Ignoring memory range 0x%llx - 0x%llx\n",
1017                base, phys_offset);
1018         size -= phys_offset - base;
1019         base = phys_offset;
1020     }
1021     memblock_add(base, size);
1022 }

从以上代码可以看出内核对地址和大小做了一系列判断后,最后调用memblock_add将内存块加入内核。

我眼中的Linux设备树(六 memory&chosen节点)的更多相关文章

  1. Linux设备树(六 memory&chosen节点)

    六 memory&chosen节点 根节点那一节我们说过,最简单的设备树也必须包含cpus节点和memory节点.memory节点用来描述硬件内存布局的.如果有多块内存,既可以通过多个memo ...

  2. 我眼中的Linux设备树(五 根节点)

    五 根节点一个最简单的设备树必须包含根节点,cpus节点,memory节点.根节点的名字及全路径都是"/",至少需要包含model和compatible两个属性.model属性我们 ...

  3. 我眼中的Linux设备树(一 概述)

    一 概述设备树(Device tree)是一套用来描述硬件属相的规则.ARM Linux采用设备树机制源于2011年3月份Linux创始人Linus Torvalds发的一封邮件,在这封邮件中他提倡A ...

  4. 我眼中的Linux设备树(二 节点)

    二 节点(node)的表示首先说节点的表示方法,除了根节点只用一个斜杠"/"表示外,其他节点的表示形式如"node-name@unit-address".@前边 ...

  5. 我眼中的Linux设备树(四 中断)

    四 中断中断一般包括中断产生设备和中断处理设备.中断控制器负责处理中断,每一个中断都有对应的中断号及触发条件.中断产生设备可能有多个中断源,有时多个中断源对应中断控制器中的一个中断,这种情况中断产生设 ...

  6. 我眼中的Linux设备树(三 属性)

    三 属性(property)device_type = "memory"就是一个属性,等号前边是属性,后边是值.节点是一个逻辑上相对独立的实体,属性是用来描述节点特性的,根据需要一 ...

  7. linux设备树语法

    设备树语法及绑定 概述 Device Tree是一种用来描述硬件的数据结构,类似板级描述语言,起源于OpenFirmware(OF). 就ARM平台来说,设备树文件存放在arch/arm/boot/d ...

  8. Linux设备树(五 根节点)

    五 根节点 一个最简单的设备树必须包含根节点,cpus节点,memory节点.根节点的名字及全路径都是“/”,至少需要包含model和compatible两个属性.model属性我们在属性那节已经说过 ...

  9. linux 设备树【转】

    转自:http://blog.csdn.net/chenqianleo/article/details/77779439 [-] linux 设备树 为什么要使用设备树Device Tree 设备树的 ...

随机推荐

  1. 64. Minimum Path Sum(中等, 又做出一个DP题, 你们非问我开不开心,当然开心喽!^^)

    Given an m x n grid filled with nonnegative numbers, find a path from top left to bottom right which ...

  2. vue+node.js+webpack开发微信公众号功能填坑——v -for循环

    页面整体框架实现,实现小功能,循环出数据,整体代码是上一篇 vue+node.js+webpack开发微信公众号功能填坑--组件按需引入 修改部门代码 app.vue <yd-flexbox&g ...

  3. /bin/bash 常用命令

    ls -a 查看一切文件 ls -l 查看目录文件信息 clear 清屏(信息保留屏幕) reset 清屏(所有信息) ls *.txt 查看所有txt文件 wc 统计文件 -l 行数: - c 统计 ...

  4. Lua语言模型 与 Redis应用

    Lua语言模型 与 Redis应用 标签: Java与NoSQL 从 2.6版本 起, Redis 开始支持 Lua 脚本 让开发者自己扩展 Redis. 本篇博客主要介绍了 Lua 语言不一样的设计 ...

  5. 动手试试Android Studio插件开发

    由于业务关系,经常需要写一些表单页面,基本也就是简单的增删改查然后上传,做过几个页面之后就有点想偷懒了,这么低水平重复性的体力劳动,能不能用什么办法自动生成呢,查阅相关资料,发现android stu ...

  6. springMVC源码分析--@ModelAttribute使用及运行原理

    这一篇博客我们简单的介绍一下ModelAttribute的使用和运行原理. 1.首先@ModelAttribute是使用在方法或者上的,当使用在方法上时其作用于本身所在的Controller,在访问C ...

  7. Apache shiro集群实现 (八) web集群时session同步的3种方法

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  8. Ribbon WorkBench 当ValueRule的值为空时的设置

    在定制Ribbon按钮的规则的时候,有时需要根据某个字段值是否为空不设定Ribbon按钮的Display rules或Enable Rules,根据Crm的版本的不同,设置有所差别: 对于Dynami ...

  9. bmp格式图像的读写函数(对一个开源代码的封装)

    在网上看到一段读写bmp格式图像的代码,本文对这段代码分成两个函数封装起来方便使用,一个函数是读取bmp格式的图像,一个是向指定文件写入bmp格式的图像. 前提 我们不需要知道这段代码是如何读取bmp ...

  10. [Vim]vim学习笔记--多个文件打开,切换,关闭

    一种情况是在shell中用vim打开多个文件,另一种是在vim编辑器中打开多个文件 同时打开多个文件 vim file1 file2  打开文件并水平窗口显示 vim -o file1 file2 打 ...