Friday, October 25, 2013

Building Linux kernel image for ARM with Eclipse [Part 4]

F. Board configuration

1.     Bootstrap configuration.
1.1  Makefile
The first thing we need take a look should be the makefile. It is on top directory of project. The makefile defines how we compile the project. There are some worthy of notes in this makefile.
- It included the file .config which stores list of all compilation options of project.

ifeq ($(filter $(noconfig_targets),$(MAKECMDGOALS)),)
-include .config

- Some of compilation options in the makefile were used in C code as macros indirectly, for instance the macro JUMP_ADDR in the file main.c. JUMP_ADDR in C code is a variable JUMP_ADDR defined in makefile and this variable took its value from CONFIG_JUMP_ADDR in the file .config. Finally, JUMP_ADDR in makefile is passed to the compiler via CPPFLAGS variable in an another makefile, that is Main purpose of this design is to reduce code size of the program.

            -DIMG_ADDRESS=$(IMG_ADDRESS)   \
            -DIMG_SIZE=$(IMG_SIZE)         \
            -DJUMP_ADDR=$(JUMP_ADDR)       \

There are three important compilation options in makefile need to take a look that are
CONFIG_IMG_ADDRESS: holding address of u-boot image in flash (NAND flash, Data flash, SD card).
CONFIG_IMG_SIZE: holding size of memory area which stores u-boot image. This memory area might be bigger than the real size of u-boot image.
CONFIG_JUMP_ADDR: holding address in RAM that program will jump to after complete loading uboot image. It’s also address in RAM that u-boot image will be copied to. Default value of above variables is defined an automatic generated file \config\at91bootstrap-config\autoconf.h.
Bootstrap will be loaded by the ROM boot program which was build-in in the SoC. ROM boot program always loads bootstrap image from address 0x00000000 of flash (Data flash or NAND flash) to address 0x00000000 in RAM and loaded code size is value of _edata variable declared at 6th exception vector in start-up code (crt0_gnu.S) but must be inferior 4Kb. We will examine this variable in section Linker Description Script later. Olimex board has 1 MB Data flash, 64 MB RAM and 512 MB NAND flash.
After completing loading bootstrap to RAM, the ROM boot program will reset entire system to reset status, perform remap memory and set the PC register to value 0x00000000. The address 0x00000000 now is RAM, not ROM, so bootstrap code will be executed. In its turn, bootstrap will load bootloader from flash to RAM and jump to bootloader to continue booting progressing.
In order to jump to bootloader, the main function (in file main.c) of bootstrap will return JUMP_ADDR. By convention, this returned value will be stored in register R0. The instruction ”bx r0” in bootstrap start-up code as below (in file crt0_gnu.S) will help the program jumping from bootstrap to the address JUMP_ADDR which is entry of u-boot bootloader.
In our project, CONFIG_JUMP_ADDR = 0x21F00000. CONFIG_IMG_SIZE = 0x80000.

/* Branch to the application at the end of the bootstrap init */
            ldr      r1, =BACKUP_REGISTER_BOOT_MODE_R4
            ldr      r4, [r1]
            ldr      r1, =MACH_TYPE
            mov     lr, pc
            bx      r0

1.2  Linker Description Script

Linker description script is a file to descript how the compiling generated sections are laid out in memory.
As previous part, we mentioned about _edata variable in makefile. In liker file elf32-littlearm.lds_edata is put at end of the section .data. So, its value is equal with size of section .text plus size of section .data. This value is entire size of the generated executable file stored in flash memory.

Note: to modify configuration in Bootstrap, try to avoid modify directly in source code but try to modify in the menu configuration by running below command in terminal: make menuconfig.

2.     U-boot configuration.

2.1 System configuration.
There are some important files relative to system configuration that we should take a look.
-         /u-boot-at91sam9260/arch/arm/cpu/arm926ejs/start.S
This file is start-up code of program. The first instruction of u-boot will start at label _start. This address store exception vector table. First exception vector is reset vector which stores the instruction “b     reset” to jump to the reset handler.
In reset handler, firstly the program switches to SVC mode which allows to access all resources of CPU.
Next, it performs low level initialization (setting up the CPU clock, PLL, SDRAM, disable MMU, flush D cache, I cache, TLB…) by executing the instruction “bl cpu_init_crit” to jump to the handler cpu_init_crit.
Next, it configures stack memory by setting SP register at call_board_init_f.
Next, it executes “bl board_init_f” to jump to C function board_init_f.
The function board_init_f (in file /u-boot-at91sam9260/arch/arm/lib/board.c) is first C function running in u-boot.
Notice that there are two functions board_init_f. The one defined in board.c as above, another one is used for SPL framework, defined in file /u-boot-at91sam9260/arch/arm/cpu/arm926ejs/davinci/spl.c. In this section, we are talking about first one in file board.c.
Basically, the function board_init_f does two main tasks that are to initialize hardware and the global variable gd having data type gd_t, this variable basically stores essential information will be passed to u-boot. Some information in gd as stack memory address, load address are used by relocation code which is to load u-boot image to RAM.

Before jumping to board_init_f, stack memory hasn’t setup yet so the program needs to setup stack memory. However, at this time, external DRAM has not initialized yet so the program cannot use DRAM for stack memory. AT91SAM9260 provides two small internal static RAM banks to help setting up the stack memory. Each SRAM bank is 4KB. SP register will be setup to bank 1 (address 0x00300000) and so stack size is around 4KB.

/* Set stackpointer in internal RAM to call board_init_f */
#ifdef CONFIG_NAND_SPL /* deprecated, use instead CONFIG_SPL_BUILD */
            ldr        sp, =(CONFIG_SYS_INIT_SP_ADDR)
            ldr      sp, =(CONFIG_SPL_STACK)
            ldr      sp, =(CONFIG_SYS_INIT_SP_ADDR)  /* Set up the stack to internal SRAM*/
            bic      sp, sp, #7 /* 8-byte alignment for ABI compliance */
            ldr      r0,=0x00000000
            bl       board_init_f

Later on, after completing execution the function  board_init_f, the external DRAM already was initialized so we will have more memory space for stack and then SP register will be set to this DRAM.
            mov      r4, r0   /* save addr_sp */
            mov      r5, r1   /* save addr of gd */
            mov      r6, r2   /* save addr of destination */

            /* Set up the stack */
            mov      sp, r4 /* Set up again the stack to external DRAM*/

At the end of the function board_init_f, it calls assembly handler relocate_code with three enclosed arguments. By convention, compiler will store these arguments in registers R0, R1, R3 in order. relocate_code handler will use three arguments as its input parameters.
Main task of relocate_code is to copy u-boot image from Flash to RAM then starts executing program from RAM to getting faster execution speed.
Before coping the image to RAM, it needs to check whether the image is already existing in RAM or not by comparing entry point address of image (value of label _start) and load address (get from global variable gd).

            mov     r4, r0   /* save addr_sp */
            mov     r5, r1   /* save addr of gd */
            mov     r6, r2   /* save addr of destination */

            /* Set up the stack  */
            mov     sp, r4

            adr     r0, _start
            sub     r9, r6, r0 /* r9 - relocation offset */
            cmp     r0, r6     /*compare load address (r6) and entry point address (r0)*/
            moveq  r9, #0      /* no relocation. relocation offset(r9) = 0 */
            beq    clear_bss   /* skip relocation if r6 = r0 */

In this project, as mentioned previously, Boostrap already loaded u-boot image to address 0x21F00000, so load address and entry point address is same, hence there is no need to relocation.
Question is how to know value of entry point address ?.
In the file:  /u-boot-at91sam9260/, a linker option is used: LDFLAGS_u-boot += -Ttext $(CONFIG_SYS_TEXT_BASE)

The option –Ttext $(CONFIG_SYS_TEXT_BASE) tells to linker that it needs to link .text sections at address CONFIG_SYS_TEXT_BASE.
In file /u-boot-at91sam9260/include/configs/at91sam9260ek.h, CONFIG_SYS_TEXT_BASE = 0x21F00000.
And we passed this linker option to linker command in the file /u-boot-at91sam9260/makefile. Looking at linker command:

             UNDEF_SYM=`$(OBJDUMP) -x $(LIBBOARD) $(LIBS) | \
             sed  -n -e 's/.*\($(SYM_PREFIX)__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
             cd $(LNDIR) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) $$UNDEF_SYM $(__OBJS) \
             --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
             -Map -o u-boot   

Noted on the line: $(LDFLAGS_$(@F)), here @F is a linker direction and @F=u-boot (please check in linker document for detail). And we can translate $(LDFLAGS_$(@F)) to its full name is LDFLAGS_u-boot. This is a flag defined in file that stores the linker options including the –Ttext option as above. With above linker options, the entry point address was set to 0x21F00000.

Last part of function relocate_code will call the function board_init_r (in file /u-boot-at91sam9260/arch/arm/lib/board.c) which initializes board’s components as NAND flash, Data flash, Ethernet… before entering to function main_loop, the main function in u-boot that interacts with user.

   ldr      r0, _board_init_r_ofs  //address of function board_init_r
   ldr      r1, _TEXT_BASE
   add      lr, r0, r1
   add      lr, lr, r9
   /* setup parameters for board_init_r */
   mov      r0, r5       /* gd_t */
   mov      r1, r6       /* dest_addr */
   /* jump to it ... */
   mov      pc, lr

Note that there does not have main function in u-boot as normal C programs. In u-boot program, the function main_loop will play role as function main.

-         /u-boot-at91sam9260/arch/arm/include/asm/arch-at91/at91sam9260.h
Storing SoC specifications as SoC’s memory map, peripheral mapping.

-         /u-boot-at91sam9260/include/configs/at91sam9260ek.h
Storing board configurations as board’s memory map, Flash memory, CPU clock, SPI, UART, boot parameters…

-         /u-boot-at91sam9260/board/atmel/at91sam9260ek/at91sam9260ek.c
Storing necessary functions for board initialization as memory, Ethernet, serial port initialization.

-         /u-boot-at91sam9260/common/cmd_bootm.c
The functions in this file takes up for loading image to RAM, verifying, decompressing image and copying image header information to global variable image and final is to jump to the kernel code.
The function do_bootm in this file will find the compressed kernel image or ramdisk image in places where these images are stored as flash (NOR or NAND), Data flash or even RAM and then decompresses and copies them to memory (RAM). Finally, it calls the function do_bootm_linux to enter to the kernel entry.
The sequences following will find and get image header information from Data flash:
do_bootm  -> bootm_start -> boot_get_kernel -> genimg_get_image.
The function bootm_load_os will decompress and copy the image to RAM.

Notethe address is passed to these above functions are address of compressed images (uImage) stored in NAND flash, NOR flash, Data Flash or RAM depending on the configuration. It is called load address. Sometime, it also is called booting address depending on certain perspective.

-         /u-boot-at91sam9260/arch/arm/lib/bootm.c
The function do_bootm_linux in this file will be called to setup kernel ATAG list and jumps to kernel entry point kernel_entry. In general, the files bootm.c, cmd_bootm.c, command.c stored functions that is for processing input commands from user or defaulted.

-         /u-boot-at91sam9260/
This is linker description file for the project. It explains how output sections are laid out in memory.
Notice that the entry point address in this .lds file is only an offset (reference) address in virtual memory space. Actually, the .lds file doesn’t tell to the linker about detail information of section’s address. It only provides for the linker the reference addresses which the linker will reference to, in combination with makefile as well to arrange all output sections into a completed elf file. In linker document, these reference address are called location counters.
-Ttext option in makefile will tell to linker to locate the absolute address for given sections in virtual memory space.
Of course, without –Ttext option in makefile or similar options, then the absolute address of sections in .elf file will be laid out based on information of this file. However it’s not current configuration in this project.

-         /u-boot-at91sam9260/Makefile
Makefile of project. Noticed that u-boot doesn’t support menu configuration. All relative configurations will set directly in file at91sam9260ek.h as above.
Because boostrap cannot process elf file, so u-boot.elf file after created will be converted to binary format by using objcopy utility. With binary format (u-boot.bin), all addresses now are physical address and fixed.

-         /u-boot-at91sam9260/
This file is a part of makefile. It stores the linker options.

2.2 ATAGs List
There is able to pass list of parameters to kernel via one of three ways that are ATAGs list, Device Tree Blob or Command line.
ATAG is a simple structure store the essential information need to be passed to kernel as memory map, ramdisk, CommandLine...before kernel starts its life.
File /u-boot-at91sam9260/include/asm/setup.h will define all related information for ATAGs list.
The function do_bootm_linux in file /u-boot-at91sam9260/arch/arm/lib/bootm.c will parse and process the ATAG entries. Finally, the function boot_jump_linux will be called to jump to kernel entry carrying these ATAGs as input parameters for kernel.

2.3 U-Boot Command

U-Boot has a set of built-in commands for booting the system, managing memory, and updating an embedded system’s firmware. User can interact with u-boot via these commands for configuration the system appropriately.
The function main_loop (in file /u-boot-at91sam9260/common/main.c) will process commands from user. Just reminding again that there is no main function in u-boot.
Each command will be associated with a respective function defined in a command table to perform a particular task corresponding with that command.
The order of processing is: main_loop  -> cmd_process -> find_cmd -> cmd_call.
The function cmd_call will call respective function in the function table based on user input data.


3.     Loading Linux kernel image.

In this project, the kernel image (uImage) will be stored in Dataflash at address 0x00040000 (default value is defined in environment variable bootcmd) and to be copied to RAM at address 0x22200000 by u-boot (/u-boot-at91sam9260/include/configs/at91sam9260ek.h). This address is called booting address. Based on uImage header information, u-boot will pass control to “wrapper code” of uImage to un-package/decompress the uImage from the booting address 0x22200000 to the ZRELADDR address 0x20008000 (/arch/arm/mach-at91/Makefile.boot) which is corresponding with virtual address 0xC0008000 (/arch/arm/kernel/
This is address of decompressed kernel image (vmlinux.bin) where Kernel will start its life.

uImage's structure
- There are two kind of “wrapper code”. One is for uImage and other one is for zImage as above figure.
- There are two addresses for Dataflash. The one is logical address which is 0xD0000000 and another one is physical address which is address of SPI module connecting to Dataflash.
- In general, Kernel booting process is variant change depending on the hardware and software design.

4. Loading Root file system image.

Besides loading kernel image, we also need to load root file system image. The root file system image is stored in NAND flash at address 0x00400000
We will reuse the build-in root file system image from Atmel.
For summary, a full picture for kernel’s booting processing is presented as in figure below.


Next: Building Linux kernel image for ARM with Eclipse [Part 5]

No comments: