メモリの行方(kernel/initrd/fdtはどうなるの？）

小ネタですが、起動時に物理メモリ上においたkernel/initrd/fdtがどうなるのかを簡単にまとめておく。

まず、見るべきはこちらのコード

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/arm/mm/init.c

起動したらどんどんreserveを予約している。

- Kernel ： reservedのまま
- initrd : 解釈終わったら破棄
- fdt : （多分）reserveのまま

って見えますね……


```c:init.c

void __init arm_memblock_init(const struct machine_desc *mdesc)
{
	/* Register the kernel text, kernel data and initrd with memblock. */
	memblock_reserve(__pa(KERNEL_START), KERNEL_END - KERNEL_START);　★Kernelをreserve

	arm_initrd_init(); ★initrdをreserve

	arm_mm_memblock_reserve();★pagetableをreserve

	/* reserve any platform specific memblock areas */
	if (mdesc->reserve)
		mdesc->reserve();

	early_init_fdt_reserve_self(); ★fdt領域をreserve
	early_init_fdt_scan_reserved_mem(); ★fdt内のreserved memoryをreserve

	/* reserve memory for DMA contiguous allocations */
	dma_contiguous_reserve(arm_dma_limit); ★dma reserve

	arm_memblock_steal_permitted = false;
	memblock_dump_all();
}

```


```c:init.c
/*
 * Reserve the special regions of memory
 */
void __init arm_mm_memblock_reserve(void)
{
    /*
     * Reserve the page tables.  These are already in use,
     * and can only be in node 0.
     */
    memblock_reserve(__pa(swapper_pg_dir), SWAPPER_PG_DIR_SIZE);

#ifdef CONFIG_SA1111
    /*
     * Because of the SA1111 DMA bug, we want to preserve our
     * precious DMA-able memory...
     */
    memblock_reserve(PHYS_OFFSET, __pa(swapper_pg_dir) - PHYS_OFFSET);
#endif
}



static void __init arm_initrd_init(void)
{
#ifdef CONFIG_BLK_DEV_INITRD
	phys_addr_t start;
	unsigned long size;

	initrd_start = initrd_end = 0;

	if (!phys_initrd_size)
		return;

	/*
	 * Round the memory region to page boundaries as per free_initrd_mem()
	 * This allows us to detect whether the pages overlapping the initrd
	 * are in use, but more importantly, reserves the entire set of pages
	 * as we don't want these pages allocated for other purposes.
	 */
	start = round_down(phys_initrd_start, PAGE_SIZE);
	size = phys_initrd_size + (phys_initrd_start - start);
	size = round_up(size, PAGE_SIZE);

	if (!memblock_is_region_memory(start, size)) {
		pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region - disabling initrd\n",
		       (u64)start, size);
		return;
	}

	if (memblock_is_region_reserved(start, size)) {
		pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region - disabling initrd\n",
		       (u64)start, size);
		return;
	}

	memblock_reserve(start, size);

	/* Now convert initrd to virtual addresses */
	initrd_start = __phys_to_virt(phys_initrd_start);
	initrd_end = initrd_start + phys_initrd_size;
#endif
}
```

うん、だいぶわかってきた気がする。

fdt関係は、driver/of 以下に実装がある。reserveはしているみたい。

```c:drivers/of/fdt.c
/**
 * early_init_fdt_reserve_self() - reserve the memory used by the FDT blob
 */
void __init early_init_fdt_reserve_self(void)
{
    if (!initial_boot_params)
        return;

    /* Reserve the dtb region */
    early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
                      fdt_totalsize(initial_boot_params),
                      false);
}

/**
 * early_init_fdt_scan_reserved_mem() - create reserved memory regions
 *
 * This function grabs memory from early allocator for device exclusive use
 * defined in device tree structures. It should be called by arch specific code
 * once the early allocator (i.e. memblock) has been fully activated.
 */
void __init early_init_fdt_scan_reserved_mem(void)
{
    int n;
    u64 base, size;

    if (!initial_boot_params)
        return;

    /* Process header /memreserve/ fields */
    for (n = 0; ; n++) {
        fdt_get_mem_rsv(initial_boot_params, n, &base, &size);
        if (!size)
            break;
        early_init_dt_reserve_memory_arch(base, size, false);
    }

    of_scan_flat_dt(__fdt_scan_reserved_mem, NULL);
    fdt_init_reserved_mem();
}

```

ということで、init/initramfs.c から呼ばれるわけですが、rootfs展開ができたら、initrdは捨てちゃうっぽい。

```c:init/initramfs.c
static int __init populate_rootfs(void)
{
    /* Load the built in initramfs */
    char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size);
    if (err)
        panic("%s", err); /* Failed to decompress INTERNAL initramfs */

    if (!initrd_start || IS_ENABLED(CONFIG_INITRAMFS_FORCE))
        goto done;

    if (IS_ENABLED(CONFIG_BLK_DEV_RAM))
        printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n");
    else
        printk(KERN_INFO "Unpacking initramfs...\n");

    err = unpack_to_rootfs((char *)initrd_start, initrd_end - initrd_start);
    if (err) {
        clean_rootfs();
        populate_initrd_image(err);
    }

done:
    /*
     * If the initrd region is overlapped with crashkernel reserved region,
     * free only memory that is not part of crashkernel region.
     */
    if (!do_retain_initrd && initrd_start && !kexec_free_initrd())
        free_initrd_mem(initrd_start, initrd_end);★ココ！！！！！！！！！！！
    initrd_start = 0;
    initrd_end = 0;

    flush_delayed_fput();
    return 0;
}
rootfs_initcall(populate_rootfs);
```

以上です。