LoginSignup
3
1

More than 1 year has passed since last update.

device treeで"memory"を書いてから、memblockに繋がるまでのフロー整理

Posted at

■ はじめに

devicetreeの記述でよくこういうのを見る。

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x20000000>;
    };

「この memoryの定義の構文を確認しよう」と思って探したけど、現状のkernelにはドキュメント存在していない。

そこでソースコードで確認していく。

■ "memory"をつかさどるコードはどこか?

(1)arch/arm/boot/compressed/fdt_check_mem_start.cと(2) drivers/of/fdt.cでございます。

(1)は起動開始時に、linuxのプログラムコード開始位置の決定で使っているっぽい。 ここの説明は端折ってもいいかな...

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/boot/compressed/fdt_check_mem_start.c?h=v5.14.5#n62

■ early_init_dt_scan_memory()

(2)が実際にlinux動作中に使うmemblockの判定用、、、かな? reserved-memoryなどもこのファイルの中に記載されている。

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/of/fdt.c?h=v5.14.5#n998

fdt.c#n998
/*
 * early_init_dt_scan_memory - Look for and parse memory nodes
 */
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
                     int depth, void *data)
{
    const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
    const __be32 *reg, *endp;
    int l;
    bool hotpluggable;

    /* We are scanning "memory" nodes only */
    if (type == NULL || strcmp(type, "memory") != 0)
        return 0;

    reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
    if (reg == NULL)
        reg = of_get_flat_dt_prop(node, "reg", &l);
    if (reg == NULL)
        return 0;

    endp = reg + (l / sizeof(__be32));
    hotpluggable = of_get_flat_dt_prop(node, "hotpluggable", NULL);

    pr_debug("memory scan node %s, reg size %d,\n", uname, l);

    while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
        u64 base, size;

        base = dt_mem_next_cell(dt_root_addr_cells, &reg);
        size = dt_mem_next_cell(dt_root_size_cells, &reg);

        if (size == 0)
            continue;
        pr_debug(" - %llx, %llx\n", base, size);

        early_init_dt_add_memory_arch(base, size);

        if (!hotpluggable)
            continue;

        if (early_init_dt_mark_hotplug_memory_arch(base, size))
            pr_warn("failed to mark hotplug range 0x%llx - 0x%llx\n",
                base, base + size);
    }

    return 0;
}

◯ ”memory"の構文は?

ANY.dtsi
    memory {
        reg = <adr1,size1[,adr2,size2]*>;
        [linux,usable-memory = <adr1,size1,[,adr2,size2]*;]
        [hotpluggable];
    };

◯hotpluggable

メモリを動作中に抜き差しできるかどうかのパラメータ。普通のlinux systemであれば特に気にしなくてよい。

◯reg

アドレスとサイズのペアを記述する。複数個を記述することもできる。

◯linux-usable-memory

regよりも優先的に使われるパラメータ。複数個を記述することもできる。なお、regとの個数を一致させる必要はない。

■early_init_dt_add_memory_arch()

さて、memoy node 内で reg あるいは linux-usable-memory で指定された領域は、early_init_dt_add_memory_arch() に引き渡される。そのあとのことをも確認しておこう。

説明を端折ると、base,sizeに対する微調整が行われたのちに、memblock_add(base,size) が呼ばれる。

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/drivers/of/fdt.c?h=v5.14.5#n1100

fdt.c#n1100
void __init __weak early_init_dt_add_memory_arch(u64 base, u64 size)
{
    const u64 phys_offset = MIN_MEMBLOCK_ADDR;

    /* sizeとoffsetを、PAGE_OFFSETに会うように微修正 */

    memblock_add(base, size);
}

■memblock_add()

memblock_add() は、memblock_add_range()に、RAM領域のためのmemblockの追加を申請する(第一引数のmemblock.memory)がポイント。

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/mm/memblock.c?h=v5.14.5#n671

memblock.c#n671
/**
 * memblock_add - add new memblock region
 * @base: base address of the new region
 * @size: size of the new region
 *
 * Add new memblock region [@base, @base + @size) to the "memory"
 * type. See memblock_add_range() description for mode details
 *
 * Return:
 * 0 on success, -errno on failure.
 */
int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size)
{
    phys_addr_t end = base + size - 1;

    memblock_dbg("%s: [%pa-%pa] %pS\n", __func__,
             &base, &end, (void *)_RET_IP_);

    return memblock_add_range(&memblock.memory, base, size, MAX_NUMNODES, 0);
}

■memblock_add_range()

で、ここから先がmemblockの追加の話だけど、今回の主題とはずれてきてしまうので、説明はここまで。

これによって、無事にmemblock.memoryに、device treeのmemory nodeに記載されたアドレス・サイズ指定が取り込まれることとなりました。

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/mm/memblock.c?h=v5.14.5#n551

memblock.c#n551
/**
 * memblock_add_range - add new memblock region
 * @type: memblock type to add new region into
 * @base: base address of the new region
 * @size: size of the new region
 * @nid: nid of the new region
 * @flags: flags of the new region
 *
 * Add new memblock region [@base, @base + @size) into @type.  The new region
 * is allowed to overlap with existing ones - overlaps don't affect already
 * existing regions.  @type is guaranteed to be minimal (all neighbouring
 * compatible regions are merged) after the addition.
 *
 * Return:
 * 0 on success, -errno on failure.
 */
static int __init_memblock memblock_add_range(struct memblock_type *type,
                phys_addr_t base, phys_addr_t size,
                int nid, enum memblock_flags flags)

以上です

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1