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

0b1331709591d260c1c78e86d0c51c18.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值