Search code examples
linux-kernelembedded-linuxu-bootrootfs

Size constraints of initramfs on ARM?


I'm creating a bootable Linux system on a PicoZed board (ARM CortexA9 core), and I've run into a "limitation", which I don't think really is a limitation (I get the feeling it's another problem masquerading as a limitation).

I boot by starting the system in JTAG boot mode; after powering on the board I use the xmd debugger to place u-boot into the system's RAM and then I run it.

Next I place the kernel (uImage), the gzip'd initramfs image and the device tree into memory. Finally I tell u-boot to boot the system using bootm, and using three arguments to point out the memory locations of all the images.

All of this works, and I manage to boot up Linux + userland. However, I need to grow the initramfs, and this is where I run into problems.

The working image is 16MiB exactly. I tried to make it 24MiB (completely regenerated from scratch each time I try to boot), but just after the kernel has loaded and it tries to find init the kernel reports file system faults and fails. There shouldn't be any overlaps, but just in case I tried moving things around a little, but the same problem occurred.

After searching for some tips, I saw someone on a forum say that the image needs to be placed at a 16MiB alignment (which I don't think is true, but I tried it none the less, but it didn't get a working system). Another post claimed that the images must be aligned with their sizes (which I again don't think is true, but I tried that as well, but with no change). Yet another post claimed that this happens if the initramfs image crosses the __init end boundary, and that placing the initramfs image firmly inside will allow the memory to be reclaimed after the image has been uncompressed by the kernel, and placing it beyond the __init section will work but then that memory is forever lost after boot. I know way too little about Linux in order to know if any of this is in any way true/accurate, and I have no idea where "__init"'s - if such a thing exists - end-boundary is, but if the issue is that I was crossing it I tried moving the initramfs image way beyond anywhere were I was previously using it, but that didn't change anything.

I also tried the original location (which works with a 16MiB image) and create a 16MiB + 1K sized image, but this didn't work either. (Checking that it didn't overlay any of the over images, obviously).

This originally led me to think there's a 16MiB initramfs size limit lurking somewhere. But on searching for it, it got me thinking that doesn't make sense -- as far as I can gather the bootm command in u-boot shoud set up the tag list for the system (which includes the location and size of the initramfs), and I haven't come across any note about a 16MiB limit in relation to the tagged lists for the initramfs so far.

I have found a page which claims that the initramfs size is in practical terms limited to roughly half the size of physical ram in the system, and the PicoZed board has 1G RAM, so we're in orders of magnitude away from what "should" be a limitation.

To clarify: The 16MiB image is the size of the raw image; compressed it's just under 6MiB. However, if I populate it so it's not compressed as tightly it doesn't make any difference -- the problem doesn't appear to be related to the size of the compressed image, only the uncompressed image.

Main question:

  • Where is this apparent 16MiB initramfs size limit coming from?

Side-question:

  • Is there such a thing as an "kernel __init section" which is reclaimed by the kernel after it has uncompressed/loaded images? If so, how do I see/configure the location/size of it?

Solution

  • Size constraints of initramfs on ARM?

    I have not encountered a 16 MiB size constraint as you allege.
    At one time I thought there was a size limit too, but that turned out to be a memory footprint issue during boot. Once that was sorted out I've been using large initramfs of 30MiB (e.g. with glibc, gtstreamer and qt5 libraries).

    Where is this apparent 16MiB initramfs size limit coming from?

    There isn't one. A ramfs is only constrained by available RAM.
    There is a definition for the "Default RAM disk size", but this would not affect the size of an initramfs.



    Revision:
    According to this article, the initramfs is initially limited to using 25% of physical memory.



    Your method of booting with the U-Boot bootm command is suspect, i.e. you're passing the memory address of the initramfs as the second argument.
    The U-Boot documentation clearly describes the second argument as "the address of an initrd image" (emphasis added).
    There is no mention of initramfs as an argument.

    Linux kernel documentation states that the initramfs archive can be "linked into the linux kernel image". There's a kernel menuconfig entry for specifying the path to the initramfs cpio file. The make script will append this cpio file to the kernel image so that there is a single image file for booting.

    Or (like an initrd) "a separate file" can be passed to the kernel at boot to populate the initramfs.
    U-Boot passes the location (and length) of this archive either as an ATAG entry or as a reserved memory region in the Device Tree blob.
    The kernel expects either a cpio archive for the initramfs or a tar archive for an initrd.

    You neglect to mention (besides its compression) what kind of archive this "initramfs" or "separate file" actually is .
    So it's not clear if you're booting the kernel with an initrd (tar archive) or an initramfs (cpio archive).

    Your repeated reference to the initramfs as an "image" file rather than a cpio archive suggests that you really are using an initrd.
    An initrd would definitely have a size constraint.


    Is there such a thing as an "kernel __init section" which is reclaimed by the kernel after it has uncompressed/loaded images?

    Yes, there is an __init section of memory, which is released after kernel initialization is complete.

    If so, how do I see/configure the location/size of it?

    Usually routines and data that have no use after initialization can be declared with the __init macro. See this.
    The location and size of this memory section would be under the control of the linker script, rather than explicit user control. The kernel System.map file should have the info for review.