How does the linux kernel know about the initrd when booting with a device tree? - linux-kernel

I am writing a bootloader for my Arm board (32-bit i.MX6) and want to boot the Linux kernel using a device tree and an initrd file located at a static location in memory.
I looked to U-Boot as a reference and I see I can use the bootm command to provide a kernel, device tree and ramdisk:
Boot application image from memory:
bootm [addr [arg ...]] - boot application image stored in memory passing arguments 'arg
...'; when booting a Linux kernel,‘arg' can be the address of an initrd image
I know there are two ways to pass information from a bootloader to the kernel, the legacy ATAGS method and the modern device tree method.
With ATAGS this can be achieved with the ATAG_INITRD2 tag which describes the address that a ramdisk is stored in memory. However, with a flattened device tree no ATAGS are passed to the kernel (which is shown in the boot log with the "No ATAGs?" message). I do not see any method to specify a ramdisk when using a device tree.
If I look at the documentation for booting the Linux kernel on Arm I see the following interface is specified:
- CPU register settings
r0 = 0,
r1 = machine type number discovered in (3) above.
r2 = physical address of tagged list in system RAM, or
physical address of device tree block (dtb) in system RAM
In fact, the same document states that an initramfs must be configured prior to booting, yet includes no details on how to do so when booting with a device tree.
Is there some alternative way to achieve this? Is the required information appended to the device tree automatically by U-Boot, or is there an alternative way to notify the kernel of the ramdisk location?

It will be patched in the dtree by the bootloader; e.g.
/ {
chosen {
linux,initrd-start = <0x....>;
linux,initrd-end = <0x....>;
};

Related

why is load address of kernel, ramdisk important in booting?

I am dealing with android boot.img which is a combination of compressed kernel, ramdisk, and dtb. I see from the serial console log from uboot about the booting process and here's the part that triggers my curiosity
CPU: Freescale i.MX6Q rev1.2 at 792 MHz
CPU: Temperature 27 C
Reset cause: POR
Board: MX6-SabreSD
I2C: ready
DRAM: 1 GiB
PMIC: PFUZE100 ID=0x10
MMC: FSL_SDHC: 0, FSL_SDHC: 1, FSL_SDHC: 2
No panel detected: default to Hannstar-XGA
Display: Hannstar-XGA (1024x768)
In: serial
Out: serial
Err: serial
check_and_clean: reg 0, flag_set 0
Fastboot: Normal
flash target is MMC:1
Net: FEC [PRIME]
Normal Boot
Hit any key to stop autoboot: 3 2 1 0
boota mmc1
kernel # 14008000 (7272352)
ramdisk # 15000000 (869937)
fdt # 14f00000 (44072)
## Booting Android Image at 0x12000000 ...
Kernel load addr 0x14008000 size 7102 KiB
Kernel command line: console=ttymxc0,115200 init=/init video=mxcfb0:dev=hdmi,1920x1080M#60,bpp=32 video=mxcfb1:off video=mxcfb:off video=mxcfb3:off vmalloc=256M androidboot.console=ttymxc0 consolebalank=0 androidboot.hardware=freescale cma=384M
## Flattened Device Tree blob at 14f00000
Booting using the fdt blob at 0x14f00000
Loading Kernel Image ... OK
Using Device Tree in place at 14f00000, end 14f0dc27
switch to ldo_bypass mode!
Starting kernel ...
The kernel's address is 14008000, ramdisk 15000000, fdt 14f00000.
I have found out that these values are saved in the boot.img header and when I manually mess with this values, the image will not boot even though the actual contents are not modified.
Why is this address so critical? Why does it have to be these values? why not other values?
One of my own guess is:
these load address values are hardcoded inside the kernel so if I change it in the boot.img header it will cause side-effects. To elaborate my own theory, loading the kernel to the 'modified' load addr won't be a problem. But when actually executing the lines, it will cause severe problems because these codes are fixed to work with the proper 'load addr'.
Is my theory wrong? If so, I'd be grateful if you could correct me.
After the U-boot finished, it needs to handover the further execution to kernel so that kernel takes care for all further processes. Plus Kernel have some set of routines which are essential to initialize the board.
These set of routines have a entry point start_kernel(). Before this some other routines are also called to execute start_kernel. That can be identified by this linker script and this init.S.
To maintain execution in this procedure kernel need to be started from a particular address, moreover its data segments and other memories are also attached with that so to load and run all these in a proper manner, exact memory location need to be provided.
With this kernel needs the device tree blob for probing the devices attached to it and the rootfs to bring up the user space, here to load the device tree and rootfs exact offsets are needed so that any data should not be missed or corrupted. Every single byte is important for all these execution.
So to safely bootup all these values are being stored in bootloader's configuration.

Building kernel uImage using LOADADDR

While building the kernel I am giving LOADADDR as "0x80008000":
make uImage LOADADDR=0x80008000
Can you please help to understand what is the use of this? Can I change the LOADADDR, is there any restriction on the length of the LOADADDR?
(I'm assuming that you're using ARM based on the mention of U-Boot and the value of LOADADDR.)
Can you please help to understand what is the use of this?
LOADADDR specifies the address where the kernel image will be located by the linker. (This is true for a few architectures (e.g. Blackfin), but not for ARM.
LOADADDR specifies the address where the kernel image will be located by U-Boot and is stored in the U-Boot header by the mkimage utility. Typically the load address (for placement in memory) is also the start address (for execution). Note that the uImage file is typically just the (self-extracting, compressed) zImage file with the U-Boot wrapper.
Can I change the LOADADDR,
Yes, but according to (Vincent Sanders') Booting ARM Linux that would be contrary to ARM convention:
Despite the ability to place zImage anywhere within memory,
convention has it that it is loaded at the base of physical RAM plus
an offset of 0x8000 (32K). This leaves space for the parameter block
usually placed at offset 0x100, zero page exception vectors and page
tables. This convention is very common.
(The uImage mentioned in your question is probably just a zImage with the U-Boot wrapper, so the quotation does apply.)
is there any restriction on the length of the LOADADDR?
The "length"? If you're using a 32-bit processor, then the length of this address would be 32 bits.
ADDENDUM
arch/arm/boot/Makefile only uses LOADADDR for building the uImage from the zImage.
From (Russel King's) Booting ARM Linux the constraints on this LOADADDR are:
The
kernel should be placed in the first 128MiB of RAM. It is recommended
that it is loaded above 32MiB in order to avoid the need to relocate
prior to decompression, which will make the boot process slightly
faster.
When booting a raw (non-zImage) kernel the constraints are tighter.
In this case the kernel must be loaded at an offset into system equal
to TEXT_OFFSET - PAGE_OFFSET.
The expected locations for the Device Tree or ATAGs or an initramfs can add more constraints on this LOADADDR.
Putting this in other terms, kernel compilation can generate different images, compressed or not (i.e. needed for XIP).
LOADADDR is the entry_point of the kernel, must be correct (match with where u-boot loads it in DDR) for certain architectures that compiles with absolute long jumps, and not important at all for ARM that can execute relative jumps.

Linux Kernel Booting Approach Pre built rootfs

I am learning the linux kernel booting process and trying to install linux on my beagleboard xM. I came across two approach both using the SD card.
1. Have the MLO, initrd, uboot.bin and uImage in one partition.
2. Have the MLO, uboot.bin and uImage in one partition and a pre-built rootfs (Angstrom) in 2nd partition.
In the first approach how is the initrd transformed into a complete file system on the 2nd partition. And what happens internally when uboot extracts the kernel from uImage and paste it on the 2nd partition. which directories would get modified of the init rootfs. How the init process of the kernel is called.
In general, the booting sequence in the Beagleboard-XM is something like this :
Firmware -> MLO -> uboot.bin -> uImage -> initrd(optional) -> rootfs
The initrd itself is a simple RAM based file system. It is generally used to mount the actual file system. Some of the modules which are required to mount the rootfs are bundled up into the initrd. Once the rootfs is mounted, the initramfs is cleaned up. If the features that are required to mount the rootfs is built into the kernel (uImage) itself, then there is no need of initrd. As I said earlier, initrd is a simple RAM based file system. That means if you use this as your filesystem, anything you write onto it will not be preserved. A few embedded applications actually use this as their rootfs.
Now answering to your question,
How is the initrd transformed into a complete file system on the 2nd partition?
Initially u-boot loads the kernel(uImage), initrd onto RAM and pass the appropriate the command line arguments to the kernel. Once the u-boot transfers the control over to the kernel, it first initializes the drivers and other modules and finally looks for initrd. The initrd is generally put as a cpio image. The kernel then uncompresses it and looks for 'init'. The init processes few things and then mounts the rootfs. There is no such thing as 2nd partition when it comes to initrd. It resides on the RAM.
What happens internally when uboot extracts the kernel from uImage and paste it on the 2nd partition?
Again, there is no such things as 2nd partition when it comes to uImage. uImage is a format which is understood by u-boot. The u-boot first loads the uImage onto the RAM and before actually passing the control over to the kernel, it extracts the contents from uImage, moves the binary to a predefined location in the RAM and then passes the control over to the kernel. Similar to initrd, the kernel also resides on the RAM itself.
Which directories would get modified of the init rootfs?
No directories gets modifies. But yes, a few of them are mounted, like procfs, sysfs, etc.
How the init process of the kernel is called?
Please refer to the kernel_init() function under init/main.c to see how the kernel executes init (refer http://lxr.free-electrons.com/source/init/main.c#L871).
I am attaching a simple code snippet from it :
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
panic("No working init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");
Therefore, the kernel searches in these default paths for the init binary. If it is not found, the kernel panics!
Hope this helps.

Device tree driven kernel for raspberry pi

I'd like to boot the raspberry pi with a device-tree-driven linux kernel, is there anything special to do to do that?
Can anyone point what are required to set up a device-tree-based kernel boot up for the raspberry pi.
I may need to have raspberry pi kernel source where drivers for devices should be compatible with device tree. If so, where can I find such kernel sources for Raspberry Pi?
Device-Tree support on Raspberry Pi
Raspberry Pi embeds an ARM11 SoC: Broadcom BCM2835. Device Tree (DT) support for ARM is fairly new, but it seems that it has made its way to the Raspberry Pi CPU. You can find a DT for the Raspberry Pi in arch/arm/boot/dts/bcm2835.dts*.
However the default config file bcm2835_defconfig does not enable device tree:
$ grep DT arch/arm/configs/bcm2835_defconfig
<nothing interesting>
I expected something like CONFIG_OF*=y or CONFIG_USE_OF=y. Bad news: that is going to be tough and long (3 noob.month ?).
is there anything special to do to do that?
It depends on your current linux kernel version. Chances are that your current linux already uses the device-tree (linux-3.7 or later ?).
If not, there are wide changes that you need to study:
Device-Tree impact on a system
Device Tree completely changes the way Linux kernel boots. It impacts:
your bootloader (e.g. u-boot.bin). U-Boot must know how to handle device-tree. Old U-Boots do not know what a device-tree is...
the Linux kernel image (e.g. zImage). Linux must know that it has to fetch its hardware description in the device-tree.
the flash/SD card partition layout. You need to make room for the device tree file itself.
Impact means: you need a way to compile/program these images: full-source, build environment, UART access, potentially JTAG hardware. Changing the bootloader without JTAG is usually suicide, except (today) you can reprogram your SD card off the board safely.
You might find references to OpenFirmware (OF) when talking about device tree. OpenFirmware was the original specification on IBM PowerPC before the Device Tree convention was chosen. Code related to device-tree is prefixed with of_ in linux. Not intuitive, I know...
Please read:
http://devicetree.org/Main_Page
Documentation/devicetree
How to generate the Device-Tree Binary (.dtb)?
Example on a PowerPC board using buildroot:
/usr/bin/make -j5 HOSTCC="/usr/bin/gcc" HOSTCFLAGS="" ARCH=powerpc INSTALL_MOD_PATH=/home/evigier/buildroot/output/target CROSS_COMPILE=" /home/evigier/buildroot/output/host/usr/bin/powerpc-buildroot-linux-gnu-" DEPMOD=/home/evigier/buildroot/output/host/sbin/depmod -C /home/evigier/buildroot/output/build/linux-master mpc8347.dtb
Example U-Boot console output on a PowerPC board:
Uncompressing Kernel Image ... OK
kernel loaded at 0x00000000, end = 0x006f8780
## cmdline at 0x0ff1b900 ... 0x0ff1b925
## initrd_high = 0xffffffff, copy_to_ram = 1
ramdisk load start = 0x00000000, ramdisk load end = 0x00000000
## device tree at 00780000 ... 00781f57 (len=20312 [0x4F58])
Loading Device Tree to 0ff16000, end 0ff1af57 ... OK
## Transferring control to Linux (at address 00000000) ...
Booting using OF flat tree..
Happy hacking :-)

The using of address ZTEXTADDR in Linux booting for ARM

What is the role of ZTEXTADDR in Linux kernel ?
From lxr.linux.no, it's an address in RAM that holds address of zImage as sequence below?
A.
uImage (DataFlash/NAND) ---load_to_RAM--->
uImage (# boot_addr) ---decompress_uImage-->
zImage (# ZTEXTADDR) --- decompress_zImage--->
uncompressed image (# ZRELADDR).
or just:
B.
uImage (DataFlash/NAND) ---load_to_RAM--->
uImage (# boot_addr) ---decompress_uImage-->
uncompressed image (# ZRELADDR)
no using ZTEXTADDR in new kernel version for booting process ?
The Linux ARM decompression boot loader is capable of relocating itself when running from RAM. The relocation portion is PC-relative and so it can be loaded at any address. However, if your main image starts from FLASH/ROM, the code can not be relocated; while moving an image in RAM is a simple memmove(), it is much more involved for NOR flash and can be impossible for ROM.
In this case, a compressed boot linker script is used with the ZTEXTADDR as the location of the decompression code. In your diagram, you have a u-boot, which will load the uImage. There is no reason to execute this directly from Flash/ROM. u-boot can copy the image to RAM and there is no need for the ZTEXTADDR value and it should be left as zero.
If your image boots directly from Flash/ROM, without a boot loader then ZTEXTADDR is useful,
zImage (in flash) --> decompress vmlinux.bin to RAM --> run kernel
The zImage may need to be annotated with some chip setup for this to work and would need ATAGS or device trees linked. For this reason, there are many machine variants in boot/compressed; This is not maintainable and these type of files are discouraged. Typically another bootloader loads the image to RAM and the zImage can move itself to whatever destination it needs; I think that is your situation and you should set ZTEXTADDR to zero and forget about it.

Resources