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

■ はじめに
===========

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

```c
	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


```c: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"の構文は？
--------------------

```c: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

```c: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

```c: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

```c: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)
```

以上です