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,\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將內存塊加入內核。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM