Uncompressing Linux……. done, booting the kernel

Bootloader to user process:

At very high level:


Bootloader :
For ARM architecture its well documented check it for more information .

In my previous tutorial i have mentioned in the command line arguments  -dtb ./arch/arm/boot/dts/vexpress-v2p-ca9.dtb in qemu , So From document i will try to elaborate this a little bit.

4. Setup the kernel tagged list.
4b. Setup the device tree:

Before the introducing of Device Tree in mainline Kernel:
-> The previous versions of kernel contains the entire description of the hardware.
->The bootloader loads a single binary, the kernel image, and executes it.:->uImage or         zImage
->The bootloader prepares some additional information, called ATAGS, which address is passed to the kernel through register r2 Contains information such as memory size and location, kernel command line, etc.
->The bootloader tells the kernel on which board it is being booted through a machine type integer, passed in register r1.

r1:- machine type
r2:- pointers to ATAGS

Modification after the introducing of Device Tree in mainline kernel:
->The kernel is no longer contains the description of the hardware,it is located in a separate binary: the device tree blob
->The bootloader loads two binaries: the kernel image and the DTB
->Kernel image remains uImage or zImage DTB located in arch/arm/boot/dts, one per board
->The bootloader passes the DTB address through r2. It is supposed to adjust the DTB with memory information, kernel command line, and potentially other info.
->No more machine type
r1:- Don’t care
r2:- pointers to DTB

For more information about Devices tree .

The above concept will be well understand with booting kernel with bootloader(UBoot), In Qemu i am just figuring out how to boot kernel along with Uboot, may be in future ill write on that. So now will go through next portion once kernel start booting Linux display console messages as it initializes the various kernel subsystems. Significant portions of the output are common across separate architectures and machines. Two of the more interesting early boot messages are the kernel version string and the kernel command line and so on .

The very 1st line you encounter in display is


Uncompressing Linux……. done, booting the kernel .

Linux prints this message even before zImage is uncompressed itself in ARM Versatile Board. Lets try to understand how and from which file it does.

Make sure in menuconfig below list is enabled to get above banner when kernel Boots up.

make ARCH=arm  menuconfig

For Versatile PB:-



For Vexpress:-





With above these configurations will take effect the following macros.

Say Y here to include definitions of printascii, printch, printhex  in the kernel. This is helpful if you are debugging code that  executes before the console is initialized. 
Note that selecting this option will limit the kernel to a single  UART definition, as specified below. Attempting to boot the kernel  image on a different platform *will not work*, so this option should  not be enabled for kernels that are intended to be portable.


Say Y here if you want to have an early console using the  kernel low-level debugging functions. Add earlyprintk to your kernel parameters to enable this console.

The  head.S file calls the function decompress_kernel which is  defined in misc.c ,this file is the first place to look in. The function decompress_kernel() first checks the stack protection  __stack_chk_guard_setup(); and then architecture related decompression setup arch_decomp_setup().Here the  function decompress_kernel() is writing the message through putstr() function which is defined in same file,

static void putstr(const char *ptr)
char c;

while ((c = *ptr++) != '\0') {
if (c == '\n')


Here  putc () function is implemented as architecture and platform specific

#define AMBA_UART_DR (*(volatile unsigned char *)0x101F1000)
#define AMBA_UART_LCRH (*(volatile unsigned char *)0x101F102C)
#define AMBA_UART_CR (*(volatile unsigned char *)0x101F1030)
#define AMBA_UART_FR (*(volatile unsigned char *)0x101F1018)

* This does not append a newline

static inline void putc(int c)
      while (AMBA_UART_FR & (1 << 5))

From the manual you can find more about below details
1)AMBA_UART_DR:- This is Data register, UARTDR
It is used for transmitting (when writing in the register) and receiving (when reading) bytes; this register is placed at offset 0x0.
2)AMBA_UART_LCRH:-The UARTLCR_H register is the line control register
3)AMBA_UART_CR:-The UARTCR register is the control register.
4)AMBA_UART_FR:-The UARTFR register is the flag register. After reset TXFF, RXFF, and BUSY are 0,and TXFE and RXFE are 1.

“Transmit FIFO Full” flag must be checked in the UARTFR register before writing on the UARTDR register

while (AMBA_UART_FR & (1 << 5))

So from the code its clear that the fifth bit i.e TXFF is checked  i.e TXFF:- “Transmit FIFO Full”
The meaning of this bit depends on the state of the FEN bit in the UARTLCR_H (AMBA_UART_LCRH)register.
If the FIFO is disabled, this bit is set when the transmit holding register is full.
If the FIFO is enabled, the TXFF bit is set when the transmit FIFO is full

So the flag must be checked in the UARTFR register before writing on the UARTDR register

while (AMBA_UART_FR & (1 << 3))
Busy Bit is checked
BUSY:- UART busy. If this bit is set to 1, the UART is busy transmitting data. This bit remains set until the complete byte, including all the stop bits, has been sent from the shift register.This bit is set as soon as the transmit FIFO becomes non-empty (regardless of whether the UART is enabled or not).

This is how Linux-4.4 Kernel was Implemented for versatile board , on further next versions of kernel this implementation has been deprecated and made it in generic way.

Previously kernels needed to be built for each ARM platform, but on going forward development ARM linux will moving towards a “multiplatform” design where all the various ARM System on a Chip (SoC) processors are supported by a single kernel.
Linux-4.5 versatile PB board supports ARCH_MULTIPLATFORM and vexpress also supports it.

In order to support ARCH_MULTIPLATFORM the actual implementation of putc() is a generic assembly function coded into arch/arm/boot/compressed/debug.S.

        addruart r1, r2, r3
        waituart r3, r1
        senduart r0, r1
        busyuart r3, r1
        mov pc, lr

Being generic, debug.S makes reference to few macros like addruart, waituart, senduart, busyuart to get information about the actual UART hardware.

These macros are defined in an include file selected by CONFIG_DEBUG_LL_INCLUDE ( arch/arm/Kconfig.debug for it).

                 bool "Kernel low-level debugging messages via ARM Versatile UART"
                 depends on ARCH_VERSATILE
                 select DEBUG_UART_PL01X
                 Say Y here if you want kernel low-level debugging support
                 on ARM Versatile platforms.
default 0x101f1000 if DEBUG_VERSATILE

Wherein addruart implementation vary depending soc,In case of the Versatile board CONFIG_DEBUG_LL_INCLUDE is defined as arch/arm/include/debug/pl01x.S, where in fact you find those macros.

These registers are defined in serial.h

Similar way for VEXPRESS .

Hope above explanation makes clear how message is printed even before zImage is uncompressed and serial console enabled.





About VinayMahadev

I am passionate about Embedded Linux systems . I believe in "If you want to learn something, read about it. If you want to understand something, write about it. If you want to master something, teach it". Here I am just trying to connect the Dots.
Gallery | This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s