zoukankan      html  css  js  c++  java
  • Linux设备树(六 memory&chosen节点)

    六 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, ", 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将内存块加入内核。

  • 相关阅读:
    Azure PowerShell (7) 使用CSV文件批量设置Virtual Machine Endpoint
    Windows Azure Cloud Service (39) 如何将现有Web应用迁移到Azure PaaS平台
    Azure China (7) 使用WebMetrix将Web Site发布至Azure China
    Microsoft Azure News(4) Azure新D系列虚拟机上线
    Windows Azure Cloud Service (38) 微软IaaS与PaaS比较
    Windows Azure Cloud Service (37) 浅谈Cloud Service
    Azure PowerShell (6) 设置单个Virtual Machine Endpoint
    Azure PowerShell (5) 使用Azure PowerShell创建简单的Azure虚拟机和Linux虚拟机
    功能代码(1)---通过Jquery来处理复选框
    案例1.用Ajax实现用户名的校验
  • 原文地址:https://www.cnblogs.com/god-of-death/p/10271002.html
Copyright © 2011-2022 走看看