■ はじめに
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のプログラムコード開始位置の決定で使っているっぽい。 ここの説明は端折ってもいいかな...
■ early_init_dt_scan_memory()
(2)が実際にlinux動作中に使うmemblockの判定用、、、かな? reserved-memoryなどもこのファイルの中に記載されている。
/*
* 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, ®);
size = dt_mem_next_cell(dt_root_size_cells, ®);
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"の構文は?
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)
が呼ばれる。
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_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_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)
以上です