• 六 memory&chosen节点【转】


    转自:https://www.cnblogs.com/god-of-death/p/10271002.html

    根节点那一节我们说过,最简单的设备树也必须包含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, ", 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 ", (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 ",
     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 ",
    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 ",
    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 ",
    1012                base, base + size);
    1013         return;
    1014     }
    1015     if (base < phys_offset) {
    1016         pr_warning("Ignoring memory range 0x%llx - 0x%llx ",
    1015     if (base < phys_offset) {
    1016         pr_warning("Ignoring memory range 0x%llx - 0x%llx ",
    1017                base, phys_offset);
    1018         size -= phys_offset - base;
    1019         base = phys_offset;
    1020     }
    1021     memblock_add(base, size);
    1022 }

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

  • 相关阅读:
    我为什么支持从中学课本中撤下鲁迅的文章?
    【老孙随笔】技术不行别人就不服你,怎么办?
    【老孙随笔】求职,不要无的放矢
    读者来信(1)——项目经理,不要迷信制度!
    欣闻鲁迅文章下架
    知道力读书会,欢迎大家参加
    【老孙随笔】是谁杀死了QQ?
    技术,项目经理的命?——项目经理的误区(3)
    可以不封神,但是不能不修炼——亚特兰蒂斯之神特斯拉的启示
    分布式开发2WCF如何正确调用LINQTO SQL
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/12324031.html
Copyright © 2020-2023  润新知