I am attempting to get initramfs working on an embedded linux device using buildroot and uboot. I have been following the guides have have initramfs set up in the buildroot and kernel config. However as far as I can tell it is not being built into the kernel, although a CPIO or the root FS is being included in the output.
My questions is twofold. First, from a high level standpoint, is my understanding of the boot process correct?
Stage 1 bootloader (Atmel/ARM specific) starts and hands off
Stage 2 bootloader (uboot) initializes a few things, loads the kernel into memory and runs it
The kernel places the CPIO archive into memory, makes that root and runs init in that enviornment
This means the image I flash onto the chip is an IMG with a boot and system partition, the system partition is ext4 and contains the CPIO archive within. I am not sure how to make item 3 actually happen from here.
Second, in terms of actual configurations, the Buildroot menuconfig has a number of options for filesystems
ext2/3/4 root filesystem (on by default)
cpio the root filesystem (for use as an initial RAM filesystem)
initial RAM filesystem linked into linux kernel
Then in the kernel config
General -> Initial RAM filesystem and RAM disk (initramfs/initrd) support
General -> Initramfs source files (set to the generated cpio file)
What is buildroot generating here? From my end it looks like I am getting duplicate images (CPIO and ext4) and the CPIO is being ignored.
For people who may have the same question, the best way forward is to become more familiar with uboot and it's options. In particular the role uboot-env.txt plays in the process.
My understanding of buildroot was wrong. The process is
Building kernel
Creating all root filesystems (ext and CPIO)
Rebuilding the kernel with the CPIO inside
Packaging the ext file as dictated by my genimage.cfg file
This may be of interest if the process is confusing to you.
Here is what the system was doing
Stage 1 bootloader (Atmel/ARM specific) starts and hands off
Stage 2 bootloader (uboot) initializes a few things, then behaves as dictated by as config, by writing the kernel to memory from the ext4 partition and running it
This kernel did not have the cpio so it did not run.
Better understanding the process and components I was able to restructure my img file and use the bootloader to load the kernel that was built with the cpio.
Related
I've successfully used buildroot (v. 2019.05) to built u-boot and Kernel and was able to boot it together.
The problem is that, even though I selected Kernel compression mode to gzip, all I get is the uncompressed Image file.
In the output directory (and Linux as well) there is only Image file, while there should be Image.gz.
How to generate Image.gz from / instead of Image?
On arm64, Linux does not support self-extracting compression. It relies on the boot loader to do that.
The Linux build system does have an Image.gz (and Image.bz2 etc.) target, but it does nothing more than calling gzip on Image (compare this with zImage, which adds a self-extractor).
Since it is easy to do the compression outside of the kernel build system, and since there are so many different compressors possible, Buildroot doesn't provide options for them. However, it is possible to select a custom image name (BR2_LINUX_KERNEL_IMAGE_TARGET_CUSTOM) and then set BR2_LINUX_KERNEL_IMAGE_TARGET_NAME to Image.gz. Alternatively, you can do the compression in a post-build script.
Remember to make sure that the bootloader is able to decompress with that algorithm.
According to the Linux package configuration tool:
This selection will just ensure that the correct host tools are built.
The actual compression for the kernel should be selected in the kernel
configuration menu.
Make sure you select the compression option using make linux-menuconfig too.
In buildroot, besides selecting the compression mechanism you can also select the output format for the kernel image (uImage, zImage, vmlinux ...).
You should find on of those in your output/images/ or in the build directory of your kernel.
When using U-boot you probably want to use the uImage or the zImage. See this question. Both of them will be compressed if selected in the kernel configuration (CONFIG_KERNEL_GZIP).
During boot the uncompressed size of the kernel is logged in the beginning. You can compare it to the size on your filesystem.
## Booting kernel from Legacy Image at 10000000 ...
Image Name: Linux-4.14.73-ltsi
Created: 2019-05-14 11:55:16 UTC
Image Type: ARM Linux Kernel Image (uncompressed)
Data Size: 4684016 Bytes = 4.5 MiB
Load Address: 00008000
Entry Point: 00008000
...
I'm working on an embedded system where the rootfs is constructed in a tmpfs partition by the init process. After the rootfs is complete, it will do a pivot-root and start spawning processes located in the rootfs.
But it seems like XIP is not working for our tmpfs, and all the applications is therefore loaded into ram twice (in the tmpfs and again into ram when loaded).
Can this really be true?
I found an old discussion thread at https://ez.analog.com/thread/45262 which describe the same issue as I'm seeing.
How can I achieve XIP for a file-system located in memory?
What you are attempting to do should be indeed possible (though I haven't tried it myself). The problem is simply you are not going about it the correct way. If you use the block RAM device ("brd") you can create a block device that is actually RAM presented as a block device. To enable this on your kernel (sorry you do not say which kernel you have so I will just go with the kernel 4.14), you need to enable CONFIG_BLK_DEV_RAM as well as CONFIG_BLK_DEV_RAM_DAX in your kernel configuration. They are both under "Device Drivers" -> "Block Devices". Then you create such a RAM backed block device and then create for example an ext2 or ext4 or XFS file system on it and then prepare your rootfs into that file system and then pivot-root into it. Now you are executing in a RAM backed file system which has XIP (now replaced by DAX) functionality thus executing applications should now at least in theory work correctly without creating a copy of the data and simply running it out of the RAM pages of the block RAM device.
Please do beware that such approach has limitation such as for example that kernel modules themselves will still be copied into RAM, get_user_pages() may not work, O_DIRECT may not work, and neither might RDMA, sendfile() and splice().
Some relevant things to look at include:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/block/Kconfig?h=v3.19#n359
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/block/Kconfig?h=v3.19#n396
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/blockdev/ramdisk.txt?h=v3.19
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/filesystems/xip.txt?h=v3.19
Note XIP was replaced by DAX since 4.0 kernel so there see:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/filesystems/dax.txt?h=4.14
Also note that support for DAX was removed from block RAM driver with kernel 4.15 so you will no longer be able to do this once you move to kernel 4.15 and later... See commit 7a862fbbdec665190c5ef298c0c6ec9f3915cf45 for the reasoning behind removing the functionality.
I hope this is enough to set you on the right track and sorry about the bad news that the functionality has been removed since 4.15 kernel...
Does Linux need a writeable file system to function correctly? I'm just running a very simple init programme. Presently I'm not mounting any partitions. The Kernel has mounted the root partition as read-only. Is Linux designed to be able run with just a read-only file system as long as I stick to mallocs, readlines and text to standard out (puts), or does Linux require a writeable file system in-order even to perform standard text input and output?
I ask because I seem to be getting kernel panics and complaints about the stack. I'm not trying to run a useful system at the moment. I already have a useful system on another partition. I'm trying to keep it as simple as possible so as I can fully understand things before adding in an extra layer of complexity.
I'm running a fairly standard x86-64 desktop.
No, writable file system is not required. It is theoretically possible to run GNU/Linux with the only read-only file system.
In practice you probably want to mount /proc, /sys, /dev, possibly /dev/pts to everything work properly. Note that even some bash commands requires writable /tmp. Some other programs - writable /var.
You always can mount /tmp and /var as ramdisk.
Yes and No. No it doesn't need to be writeable if it did almost nothing useful.
Yes, you're running a desktop so it's needed to be writeable.
Many processes actually need a writeable filesystem as many system calls can create files. e.g. Unix Domain Sockets can create files.
Also many applications write into /var, and /tmp
The way to get around this is to mount the filesystem read/only and use a filesystem overlay to overlay an in memory filesystem. That way, the path will be writable but they go to ram and any changes are thrown away on reboot.
See: overlayroot
No it's not required. For example as most distributions have a live version of Linux for booting up for a cd or usb disk with actually using and back end hdd.
Also on normal installations, the root partitions are changed to read-only when there are corruptions on the disk. This way the system still comes up as read-only partition.
You need to capture the vmcore and the stack trace of the panic form the dmesg output to analyse further.
According to this picture from The Kernel Boot Process:
The bulk of the kernel image is only ~1.5MB. I thought the kernel image should be a fairy large binary image file in order to support the hardware (or is this kernel module?) and various kernel functions, since the source is pretty large. Also, where's the initrd?
First the kernel is compressed. Then in most desktop environment, the kernel is built with a minimal set of statically linked modules. The initrd image is loaded from the hard drive, from the /boot folder (see your grub configuration) and is used to detect your desktop hardware to load the appropriate modules, to be able to mount a root file system. The initrd image contains this sets of modules. Imagine a RAID, LVM or an exotic file system that cannot be recognized by the bootloader. This is why the boot partition is most of time in ext2/3/(4?). Then when the right modules are loaded, the root file system is swapped from initrd image with the one on the hard drive (pivot_root). Then the rest of the hardware modules are loaded from the "none initrd" filesystem, mostly with the help of udev.
Hope this helps!
P.S. Correct me if I'm wrong!
I'm developing a linux-based appliance using an alix 2d13.
I've developed a script that takes care of creating an image file, creating the partitions, installing the boot loader (syslinux), the kernel and the initrd and, that takes care to put root filesystem files into the right partition.
Configuration files are on tmpfs filesystem and gets created on system startup by a software that reads an XML file that resides on an own partition.
I'm looking a way to update the filesystem and i've considered two solutions:
the firmware update is a compressed file that could contain kernel, initrd and/or the rootfs partition, in this way, on reboot, initrd will takes care to dd the rootfs image to the right partition;
the firmware update is a compressed file that could contain two tar archives, one for the boot and one for the root filesystem.
Every solution has its own advantages:
- a filesystem image will let me to delete any unused files but needs a lot of time and it will kill the compact flash memory fastly;
- an archive is smaller, needs less time for update, but i'll have the caos on the root filesystem in short time.
An alternative solution could be to put a file list and to put a pre/post update script into the tar archive, so any file that doesn't reside into the file list will be deleted.
What do you think?
I used the following approach. It was somewhat based on the paper "Building Murphy-compatible embedded Linux systems," available here. I used the versions.conf stuff described in that paper, not the cfgsh stuff.
Use a boot kernel whose job is to loop-back mount the "main" root file system. If you need a newer kernel, then kexec into that newer kernel right after you loop-back mount it. I chose to put the boot kernel's complete init in initramfs, along with busybox and kexec (both statically linked), and my init was a simple shell script that I wrote.
One or more "main OS" root file systems exist on an "OS image" file system as disk image files. The boot kernel chooses one of these based on a versions.conf file. I only maintain two main OS image files, the current and fall-back file. If the current one fails (more on failure detection later), then the boot kernel boots the fall-back. If both fail or there is no fall-back, the boot kernel provides a shell.
System config is on a separate partition. This normally isn't upgraded, but there's no reason it couldn't be.
There are four total partitions: boot, OS image, config, and data. The data partition is for user application stuff that is intended for frequent writing. boot is never mounted read/write. OS image is only (re-)mounted read/write during upgrades. config is only mounted read/write when config stuff needs to change (hopefully never). data is always mounted read/write.
The disk image files each contain a full Linux system, including a kernel, init scripts, user programs (e.g. busybox, product applications), and a default config that is copied to the config partition on the first boot. The files are whatever size is necessary to fit everything in them. As long I allowed enough room for growth so that the OS image partition is always big enough to fit three main OS image files (during an upgrade, I don't delete the old fall-back until the new one is extracted), I can allow for the main OS image to grow as needed. These image files are always (loop-back) mounted read-only. Using these files also takes out the pain of dealing with failures of upgrading individual files within a rootfs.
Upgrades are done by transferring a self-extracting tarball to a tmpfs. The beginning of this script remounts the OS image read/write, then extracts the new main OS image to the OS image file system, and then updates the versions.conf file (using the rename method described in the "murphy" paper). After this is done, I touch a stamp file indicating an upgrade has happened, then reboot.
The boot kernel looks for this stamp file. If it finds it, it moves it to another stamp file, then boots the new main OS image file. The main OS image file is expected to remove the stamp file when it starts successfully. If it doesn't, the watchdog will trigger a reboot, and then the boot kernel will see this and detect a failure.
You will note there are a few possible points of failure during an upgrade: syncing the versions.conf during the upgrade, and touching/removing the stamp files (three instances). I couldn't find a way to reduce these further and achieve everything I wanted. If anyone has a better suggestion, I'd love to hear it. File system errors or power failures while writing the OS image could also occur, but I'm hoping the ext3 file system will provide some chance of surviving in that case.
You can have a seperate partition for update(Say Side1/Side2).
The existing kernel,rootfs is in Side1, then put the update in Side2 and switch.
By this you can reduce wear leveling and increase the life but the device gets costlier.
You can quick format the partitions before extracting the tar files. Or go with the image solution but use the smallest possible image and after dd do a filesystem resize (although that is not necessary for readonly storage)