We will look at boot process of linux kernel on dragonboard410c
system-on-chip, built around the ARMv7 ARM Thumb processor. Kwick
byte builds an embedded board called dragonboard410c based on dragonboard410c.
We will take this board as an example and see how Linux boots up on this board.
You also need to read ARM Architecture Reference Manual for
better understanding the boot process.You can download it with the following
link.
http://www.lysator.liu.se/~kjell-e/embedded/ARM-ARM.pdf
U-boot boot Process:
Linux boot sequence involves execution of the following components.
Boot-loader initialization
Kernel initialization
User space initialization
Boot-loader:
A boot-loader is a small program which will load the kernel image into RAM and boots up the kernel image. This is also called bootstrap as it brings(pulls) up system by loading an operating system. Boot-loader starts before any other software starts and initializes the processor and makes CPU ready to execute a program like an operating system. Most processors have a default address from which the first bytes of code are fetched upon power is applied or board is reset. Hardware designers use this information to store the boot-loader code at that address in ROM or flash. Since it should initialize the cpu and should run a program which is located at architecture specific address boot-loaders are highly processor specific and board specific. Every embedded board comes with a bootstrap to download the kernel image or standalone application into the board and start executing the kernel image or application. Boot-loader will be executed when power is applied to a processor board. Basically it will have some minimal features to load the image and boot it up.
It is also possible to control the system using a hardware debug interface such as J TAG. This interface may be used to write the boot loader program into boo-table non-volatile memory (e.g. flash) by instructing the processor core to perform the necessary actions to program non-volatile memory. Generally done for first time to download the basic boot-loader and for some recovery process. J TAG is a standard and popular interface provided by many board vendors. Some micro-controllers provide special hardware interfaces which can’t be used to take arbitrary control of a system or directly run code, but instead they allow the insertion of boot code into boot-able non-volatile memory (like flash memory) via simple protocols. Then at the manufacturing phase, such interfaces are used to inject boot code (and possibly other code) into non-volatile memory. After system reset, the micro-controller begins to execute code programmed into its non-volatile memory, just like usual processors are using ROM’s for booting. In many cases such interfaces are implemented by hardwired logic. In other cases such interfaces could be created by software running in integrated on-chip boot ROM from GPIO pins.
There are some other third party boot-loaders available which provide rich set of features and easy user interface. You can download these third party boot-loaders into board and can make them default boot-loaders for your board. Generally boot-loaders provided by board vendors are replaced with these third party boot-loader. There are a quite few third party boot-loader available and some of them are open source (or free boot-loaders) and some are commercial. Some of them are Das U-Boot, Red boot, GRUB (for desktops), LILO , Loadlin, , bootsect-loader, SYSLINUX, EtherBoot, ELILO.
We will look at U-boot boot-loader . U-boot is the widely used boot-loader in embedded systems. I will explain code from the u-boot-2017.01 source. You can download U-boot from the following site.
http://www.denx.de/wiki/U-Boot
How U-boot is built:
————————-
Based on the configuration of U-boot, all the assembly files (.S) and C files (.c) are compiled using cross compiler which is built for a particular architecture and object files(.o) will be generated. All these object files are linked by linker and an executable file will be created. An object file or executable file is a collection of sections like .text, .data, .bss etc. Object files and executable files have a file format like elf. All the sections of the object files will be arranged in the executable file based on a script called linker script. This script tells where all the sections are to be loaded in the memory when it runs. Understanding this script is very important to know how boot-loader and kernel are composed and how different sections of boot-loader or kernel are loaded in the memory.
Generally, when a program is run (executed) a loader reads executable file and loads different sections of the executable file in the specified memory location and starts executing the start function(entry point) specified in the linker script. But, if you want to run(load) a boot-loader there will not be any loader to load(basically to understand the file format) different sections of executable file into the memory. Then you need to use a tool called objcopy which will take all sections from the executable file and create a binary file which doesn’t have any file format. This binary file can be loaded into the memory and executed or can be written in to the ROM at a particular address (specific to the architecture) which will be executed by CPU when power is applied to the board. You can find good tutorial on linker script in the following location.
file board\qualcomm\dragonboard410c\u-boot.lds
11 OUTPUT_FORMAT("elf64-littleaarch64",
"elf64-littleaarch64", "elf64-littleaarch64")
12
OUTPUT_ARCH(aarch64)
13 ENTRY(_arm64_header)
14 SECTIONS
15 {
16 . = 0x00000000;
17
18 . = ALIGN(8);
19 .text :
20 {
21 *(.__image_copy_start)
22
board/qualcomm/dragonboard410c/head.o (.text*)
23 CPUDIR/start.o (.text*)
24 *(.text*)
25 }
26
27 . = ALIGN(8);
28 .rodata : {
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
29
30 . = ALIGN(8);
31 .data : {
32 *(.data*)
33 }
34
35 . = ALIGN(8);
36
37 . = .;
38
39 . = ALIGN(8);
40 .u_boot_list : {
41 KEEP(*(SORT(.u_boot_list*)));
42 }
43
44 . = ALIGN(8);
45
46 .image_copy_end :
47 {
48 *(.__image_copy_end)
49 }
50
51 . = ALIGN(8);
52
53 .rel_dyn_start :
54 {
55 *(.__rel_dyn_start)
56 }
57
58 .rela.dyn : {
59 *(.rela*)
60 }
61
62 .rel_dyn_end :
63 {
64 *(.__rel_dyn_end)
65 }
66
67 _end = .;
68
69 . = ALIGN(8);
70
71 .bss_start : {
72 KEEP(*(.__bss_start));
73 }
74
75 .bss : {
76 *(.bss*)
77 . = ALIGN(8);
78 }
79
80 .bss_end : {
81 KEEP(*(.__bss_end));
82 }
83
84 /DISCARD/ : { *(.dynsym) }
85 /DISCARD/ : { *(.dynstr*) }
86 /DISCARD/ : { *(.dynamic*) }
87 /DISCARD/ : { *(.plt*) }
88 /DISCARD/ : { *(.interp*) }
89 /DISCARD/ : { *(.gnu*) }
90 }
OUTPUT_FORMAT in line
#11 specify the file format of the executable file. Here the executable file format is elf64 and endianness is
little endian. OUTPUT_ARCH in line # 12
specify the architecture on which this code runs. ENTRY in line #22 specifies the
start function(entry point) of u-boot
program. Here the entry point is _start.
SECTIONS in line #24 defines how different sections are mapped in the
executable file. Loader uses the addresses specified in this section to load
different section of the program into the memory. ‘.’ in the line #16 specifies the start
address where the following sections should be loaded. In this case start
address is 0x00000000. After this in line #18 the memory is aligned by 4 bytes
and the .text section follows in the line #19.
19 .text :
20 {
21 *(.__image_copy_start)
22
board/qualcomm/dragonboard410c/head.o (.text*)
23 CPUDIR/start.o (.text*)
24 *(.text*)
25 }
At the ‘.’ position (0x00000000) the code in board/qualcomm/dragonboard410c/head.o
is mapped and follows the code that is there in .text sections of all other
object (.o) files. /arm/cpu/armv7/ start.o contains the _start() function(in
assembly language) which is entry point of this program.
Now the ‘.’ will be at 0x00000000 + sizeof (.text). Again memory is aligned by 8 bytes and .rodata
section follows in line #27.
27 . =
ALIGN(8);
28 .rodata : {
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
.rodata sections from all objects files are mapped at this
address. Follows the .data and .git
sections.
30 . =
ALIGN(8);
31 .data : {
32 *(.data*)
33 }
34
35 . = ALIGN(8);
Each U-boot command is an object of type ‘cmd_tbl_t’ which
contains command name, help string and
function pointer to be executed when this command is run. All these command
objects are placed in the memory sequentilly. Each of this command object is
built into an U-boot defined section called
.u_boot_cmd in the object file.
These all .u_boot_cmd sections
are placed in the memory after the above sections(.data and .git).
36
37 . = .;
38
39 . = ALIGN(8);
40 .u_boot_list : {
41 KEEP(*(SORT(.u_boot_list*)));
42 }
43
.u_boot_list contains the start of the commands objects and
keep on add to the command objects. And next follows the .bss (uninitialized
global variables) sections.
60 . =
ALIGN(4);
61 __bss_start
= .;
62 .bss
(NOLOAD) : { *(.bss) . = ALIGN(4); }
63 _end = .;
__bss_start points to the .bss start address and _end
contains the end of the all sections.
Using this linker script linker will generate an executable
file called u-boot. Objcopy tool is used to generate a binary file from the
u-boot executable file
u-boot.bin: u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
U-boot binary file will be
copied to the board RAM or written in the flash disk. dragonboard410c
board comes with a boot programmer A
tiny program called qualcomm bootstrap that can be used to download the image
into flash or RAM and start execute or to reset a corrupted board. U-boot will
be copied to flash disk or internal RAM( if it is less size) and will be
downloaded to RAM and will be executed when the board power is applied to the
board. For this board(dragonboard410c) the code is always downloaded from
device address 0x8000_0000 to the address 0x8000_0000 of the SRAM after remap.
That ‘s why we have given the start address of the .text section as 0x80000000. If you want to load the code any where in the
RAM and want to execute U-boot you need to build you code as position
independent code(PIC). Then the
instructions addresses will be offset into to PC(cpu register) value. So the
downloaded code must be position-independent or linked at address 0x8000_0000.
For our explanation purpose assume that U-boot code is linked at 0x80000000 and
the Boot program downloaded U-boot from the data flash(Check dragonboard410c spec
for downloading process)) and call entry
point into the U-boot.
U-boot Execution:
As specified in the linker script U-boot starting function (entry
point) will be _start():\board\qualcomm\dragonboard410c\head.S
File: \board\qualcomm\dragonboard410c\head.S
_start is written in assembly language. First instuction
executed by _start() is a call to start_code: \board\qualcomm\dragonboard410c\head.S.
.globl _start
_start: b start_code
start_code: Will perform the following tasks.
1) Set the cpu in supervisor mode. The current operating
processor status is in the Current Program Status Register (CPSR). The CPSR
holds:
• four ALU flags (Negative, Zero, Carry, and Overflow),
• two interrupt disable bits (one for each type of interrupt
(FIQ and IRQ),
• one bit to indicate ARM or Thumb execution
• five bits to encode the current processor mode
Check ARM processor data sheet for more details on the CPSR
register.
32 .globl reset
33 .globl save_boot_params_ret
34 #ifdef CONFIG_ARMV7_LPAE
35 .global switch_to_hypervisor_ret
36 #endif
37
38 reset:
39 /* Allow the board to save important registers */
40 b save_boot_params
41 save_boot_params_ret:
42 #ifdef CONFIG_ARMV7_LPAE
43 /*
44 * check for Hypervisor support
45 */
46 mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1
47 and r0, r0, #CPUID_ARM_VIRT_MASK @ mask virtualization bits
48 cmp r0, #(1 << CPUID_ARM_VIRT_SHIFT)
49 beq switch_to_hypervisor
50 switch_to_hypervisor_ret:
51 #endif
52 /*
53 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
54 * except if in HYP mode already
55 */
56 mrs r0, cpsr
57 and r1, r0, #0x1f @ mask mode bits
58 teq r1, #0x1a @ test for HYP mode
59 bicne r0, r0, #0x1f @ clear all mode bits
60 orrne r0, r0, #0x13 @ set SVC mode
61 orr r0, r0, #0xc0 @ disable FIQ and IRQ
62 msr cpsr,r0
63
64 /*
65 * Setup vector:
66 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
67 * Continue to use ROM code vector only in OMAP4 spl)
68 */
69 #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
70 /* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
71 mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register
72 bic r0, #CR_V @ V = 0
73 mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register
74
75 /* Set vector address in CP15 VBAR register */
76 ldr r0, =_start
77 mcr p15, 0, r0, c12, c0, 0 @Set VBAR
78 #endif
79
80 /* the mask ROM code should have PLL and others stable */
81 #ifndef CONFIG_SKIP_LOWLEVEL_INIT
82 bl cpu_init_cp15
83 #ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
84 bl cpu_init_crit
85 #endif
86 #endif
87
88 bl _main
1. whenever power on,reset function called from specific location
2. Disable FIQ and IRQ
3. call the cpu_init_cp15
4. call _main
file : \arch\arm\lib\crt0.S
67 ENTRY(_main)
68
69 /*
70 * Set up initial C runtime environment and call board_init_f(0).
71 */
72
73 #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
74 ldr sp, =(CONFIG_SPL_STACK)
75 #else
76 ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
77 #endif
78 #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
79 mov r3, sp
80 bic r3, r3, #7
81 mov sp, r3
82 #else
83 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
84 #endif
85 mov r0, sp
86 bl board_init_f_alloc_reserve
87 mov sp, r0
88 /* set up gd here, outside any C code */
89 mov r9, r0
90 bl board_init_f_init_reserve
91
92 mov r0, #0
93 bl board_init_f
94
95 #if ! defined(CONFIG_SPL_BUILD)
96
97 /*
98 * Set up intermediate environment (new sp and gd) and call
99 * relocate_code(addr_moni). Trick here is that we'll return
100 * 'here' but relocated.
101 */
102
103 ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
104 #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */
105 mov r3, sp
106 bic r3, r3, #7
107 mov sp, r3
108 #else
109 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
110 #endif
111 ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
112 sub r9, r9, #GD_SIZE /* new GD is below bd */
113
114 adr lr, here
115 ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
116 add lr, lr, r0
117 #if defined(CONFIG_CPU_V7M)
118 orr lr, #1 /* As required by Thumb-only */
119 #endif
120 ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
121 b relocate_code
121 b relocate_code
122 here:
123 /*
124 * now relocate vectors
125 */
126
127 bl relocate_vectors
128
129 /* Set up final (full) environment */
130
131 bl c_runtime_cpu_setup /* we still call old routine here */
132 #endif
133 #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
134 # ifdef CONFIG_SPL_BUILD
135 /* Use a DRAM stack for the rest of SPL, if requested */
136 bl spl_relocate_stack_gd
137 cmp r0, #0
138 movne sp, r0
139 movne r9, r0
140 # endif
141 ldr r0, =__bss_start /* this is auto-relocated! */
142
143 #ifdef CONFIG_USE_ARCH_MEMSET
144 ldr r3, =__bss_end /* this is auto-relocated! */
145 mov r1, #0x00000000 /* prepare zero to clear BSS */
146
147 subs r2, r3, r0 /* r2 = memset len */
148 bl memset
149 #else
150 ldr r1, =__bss_end /* this is auto-relocated! */
151 mov r2, #0x00000000 /* prepare zero to clear BSS */
152
153 clbss_l:cmp r0, r1 /* while not at end of BSS */
154 #if defined(CONFIG_CPU_V7M)
155 itt lo
156 #endif
157 strlo r2, [r0] /* clear 32-bit BSS word */
158 addlo r0, r0, #4 /* move to next */
159 blo clbss_l
160 #endif
161
162 #if ! defined(CONFIG_SPL_BUILD)
163 bl coloured_LED_init
164 bl red_led_on
165 #endif
166 /* call board_init_r(gd_t *id, ulong dest_addr) */
167 mov r0, r9 /* gd_t */
168 ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
169 /* call board_init_r */
170 #if defined(CONFIG_SYS_THUMB_BUILD)
171 ldr lr, =board_init_r /* this is auto-relocated! */
172 bx lr
173 #else
174 ldr pc, =board_init_r /* this is auto-relocated! */
175 #endif
176 /* we should not return here. */
177 #endif
178
179 ENDPROC(_main)
This file handles the target-independent stages of the U-Boot
start-up where a C runtime environment is needed. Its entry point
is _main and is branched into from the target's start.S file.
_main execution sequence is:
1. Set up initial environment for calling board_init_f().
This environment only provides a stack and a place to store
the GD ('global data') structure, both located in some readily
available RAM (SRAM, locked cache...). In this context, VARIABLE
global data, initialized or not (BSS), are UNAVAILABLE; only
CONSTANT initialized data are available. GD should be zeroed
before board_init_f() is called.
2. Call board_init_f(). This function prepares the hardware for
execution from system RAM (DRAM, DDR...) As system RAM may not
be available yet, , board_init_f() must use the current GD to
store any data which must be passed on to later stages. These
data include the relocation destination, the future stack, and
the future GD location.
3. Set up intermediate environment where the stack and GD are the
ones allocated by board_init_f() in system RAM, but BSS and
initialized non-const data are still not available.
4a.For U-Boot proper (not SPL), call relocate_code(). This function
relocates U-Boot from its current location into the relocation
destination computed by board_init_f().
4b.For SPL, board_init_f() just returns (to crt0). There is no
code relocation in SPL.
5. Set up final environment for calling board_init_r(). This
environment has BSS (initialized to 0), initialized non-const
data (initialized to their intended value), and stack in system
RAM (for SPL moving the stack and GD into RAM is optional - see
CONFIG_SPL_STACK_R). GD has retained values set by board_init_f().
6. For U-Boot proper (not SPL), some CPUs have some work left to do
at this point regarding memory, so call c_runtime_cpu_setup.
7. Branch to board_init_r().
174 ldr pc, =board_init_r
file : commomn\board_r.c
1. call board_init_r function
void board_init_r(void):
1) Call a set of functions which initialize all subsystems.
if (initcall_run_list(init_sequence_r))
hang();
file lib\initcall.c
2. it call initcall_run_list
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
unsigned long reloc_ofs = 0;
ret = (*init_fnc_ptr)();
}
init_sequence is an array of function pointers defined in commomn\board_r.c. The above loop takes each function pointer and calls it.
The following are some of important functions called.
174 ldr pc, =board_init_r
file : commomn\board_r.c
1. call board_init_r function
void board_init_r(void):
1) Call a set of functions which initialize all subsystems.
if (initcall_run_list(init_sequence_r))
hang();
file lib\initcall.c
2. it call initcall_run_list
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
unsigned long reloc_ofs = 0;
ret = (*init_fnc_ptr)();
}
init_sequence is an array of function pointers defined in commomn\board_r.c. The above loop takes each function pointer and calls it.
The following are some of important functions called.
init_sequence_f_r is the list of init functions which are run when
U-Boot is executing from Flash with a semi-limited 'C' environment.
The following limitations must be considered when implementing an
'_f_r' function:
- 'static' variables are read-only
- Global Data (gd->xxx) is read/write
The '_f_r' sequence must, as a minimum, copy U-Boot to RAM (if
supported). It _should_, if possible, copy global data to RAM and
initialise the CPU caches (to speed up the relocation process)
board_init():
int board_init (void)
{
return 0;
}
This is board specific function and should definitely be defined by each board. This function should some board specific initialization if there are any.
When you are porting u-boot to a new board you must define this function.
This function just sets its board number and tells where the boot params for Linux are stored.
This is a common function called to setup the serial port. This function internally calls cpu or board specific serial_init() function
int serial_init (void)
{
if (!(gd->flags & GD_FLG_RELOC) || !serial_current) {
struct serial_device *dev = default_serial_console ();
return dev->init ();
}
return serial_current->init ();
}
3.timer_init():
This is a cpu specific function and each cpu code must define it. It should basically initilize the timer services in the cpu. timer_init() for AT91RM9200 cpu defined in /arch/arm/cpu/armv7/arch_timer.c.
GD_FLG_RELOC will be set when the u-boot is relocated to RAM. serial_current will point to an object of type struct serial_device of current serial device that we are using. As we have not yet initialized any serial device serial_current will be NULL. default_serial_console() is a function which points to __default_serial_console() defined in common/serial.c.
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
dram_init, /* configure available RAM banks */
Based on configurations the following functions are also called.
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* basic arch cpu dependent setup */
#endif
board_init, /* basic board dependent setup */
#if defined(CONFIG_USE_IRQ)
interrupt_init, /* set up exceptions */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
4) Initialize NAND if configured.
#if defined(CONFIG_CMD_NAND)
puts ("NAND: ");
nand_init(); /* go init the NAND */
#endif
nand_init() function is defined in drivers/mtd/nand/nand.c
5) Initialize if dataflash is configured
#ifdef CONFIG_HAS_DATAFLASH
AT91F_DataflashInit();
dataflash_print_info();
#endif
6) Get all input and output devices list.
stdio_init (); /* get the devices list */
stdio_init() is defined in common/stdio.c.
7) Call console_init_r () to setup the console info like where the input(stdin) should be taken and where the output(stdout) to be returned.
console_init_r (); /* fully init console as a device */
8) Enable the interrupts
enable_interrupts ();
enable_interrupt() for arm boards is defined in lib_arm/interrupts.c. Writing 0x80 into cpsr register will enable the interrupts. enable_interrupts() is the following assembly code.
__asm__ __volatile__("mrs %0, cpsr\n"
"bic %0, %0, #0x80\n"
"msr cpsr_c, %0"
: "=r" (temp)
:
: "memory");
9) Get the ‘loadaddr’ environment variable defined in u-boot through setenv. Kernel image is loaded at the load address.
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
10) All initialization has been done, now enter a main loop where we accept commands from the user and execute them.
/* run_main_loop() can return to retry autoboot, if so just run it again. */
11) the run_main_loop call ,can return to retry autoboot, if so just run it again.
for (;;)
main_loop();
main_loop() function is defined in common/main.c. This function is common for all boards
When the U-boot is booting up it will take a default command that is given in the U-boot enviroment variable bootcmd and executes that command.
If this variable is not defined U-boot will provide U-boot prompt where user can enter the commands.
U-boot can be made to go to its boot prompt even if the bootcmd is defined by giving some bootdelay.
U-boot waits until the bootdelay expires or user presses any key. If user does not press any key U-boot will execute the default command defined in bootcmd else U-boot goes to its prompt.
/* We come here after U-Boot is initialised and ready to process commands */
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
#ifdef CONFIG_VERSION_VARIABLE
setenv("ver", version_string); /* set version variable */
#endif /* CONFIG_VERSION_VARIABLE */
cli_init();
run_preboot_environment_command();
#if defined(CONFIG_UPDATE_TFTP)
update_tftp(0UL, NULL, NULL);
#endif /* CONFIG_UPDATE_TFTP */
s = bootdelay_process();
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s);
cli_loop();
panic("No CLI available");
}
CONFIG_BOOTDELAY is the number of seconds the u-boot should wait for user interrupt before it takes its default action.
As we have explained each U-boot command is an object of type struct cmd_tbl_s. The command name and function to be executed will be stored in this structure. All commands structures are kept in memory at perticular memroy, __u_boot_cmd_start and __u_boot_cmd_end contain start and end address of this memory section called command table.
run_command() function takes the command name and finds the data structure belonging to this command in the command table and calls the corresponding command function. Lets assume that bootcmd is not configured and U-boot provided the prompt to enter commands. And also assume that the linux image is loaded into the ram in a perticular address using tftpboot command or using some ymodem command and bootm command is given at the U-boot prompt.
u-boot# bootm 0x80008000
The function do_bootm() that is responsible for executing bootm command. bootm loads kernel image.
kernel image (zImage) decompression
arch/arm/boot/compressed/head.S: start (108)
First code executed, jumped to by the bootloader, at label "start" (108)
save contents of registers r1 and r2 in r7 and r8 to save off architecture ID and atags pointer passed in by bootloader (118)
execute arch-specific code (inserted at 146)
arch/arm/boot/compressed/head-xscale.S or other arch-specific code file
added to build in arch/arm/boot/compressed/Makefile
linked into head.S by linker section declaration: .section “start”
flush cache, turn off cache and MMU
load registers with stored parameters (152)
sp = stack pointer for decompression code (152)
r4 = zreladdr = kernel entry point physical address
check if running at link address, and fix up global offset table if not (196)
zero decompression bss (205)
call cache_on to turn on cache (218)
defined at arch/arm/boot/compressed/head.S (320)
call call_cache_fn to turn on cache as appropriate for processor variant
defined at arch/arm/boot/compressed/head.S (505)
walk through proc_types list (530) until find corresponding processor
call cache-on function in list item corresponding to processor (511)
for ARMv5tej core, cache_on function is __armv4_mmu_cache_on (417)
call setup_mmu to set up initial page tables since MMU must be on for cache to be on (419)
turn on cache and MMU (426)
check to make sure won't overwrite image during decompression; assume not for this trace (232)
call decompress_kernel to decompress kernel to RAM (277)
branch to call_kernel (278)
call cache_clean_flush to flush cache contents to RAM (484)
call cache_off to turn cache off as expected by kernel initialization routines (485)
jump to start of kernel in RAM (489)
jump to address in r4 = zreladdr from previous load
zreladdr = ZRELADDR = zreladdr-y
zreladdr-y specified in arch/arm/mach-vx115/Makefile.boot
ARM-specific kernel code
arch/arm/kernel/head.S: stext (72)
call __lookup_processor_type (76)
defined in arch/arm/kernel/head-common.S (146)
search list of supported processor types __proc_info_begin (176)
kernel may be built to support more than one processor type
list of proc_info_list structs
defined in arch/arm/mm/proc-arm926.S (467) and other corresponding proc-*.S files
linked into list by section declaration: .section ".proc.info.init"
return pointer to proc_info_list struct corresponding to processor if found, or loop in error if not
call __lookup_machine_type (79)
defined in arch/arm/kernel/head-common.S (194)
search list of supported machines (boards)
kernel may be built to support more than one board
list of machine_desc structs
machine_desc struct for boards defined in board-specific file vx115_vep.c
linked into list by section declaration that's part of MACHINE_DESC macro
return pointer to machine_desc struct corresponding to machine (board)
call __create_page_tables to set up initial MMU tables (82)
set lr to __enable_mmu, r13 to address of __switch_data (91, 93)
lr and r13 used for jumps after the following calls
__switch_data defined in arch/arm/kernel/head-common.S (15)
call the __cpu_flush function pointer in the previously returned proc_info_list struct (94)
offset is #PROCINFO_INITFUNC into struct
this function is __armv7_setup for the ARM 926EJ-S, defined in arch/arm/mm/proc-armv7.S (392)
initialize caches, writebuffer
jump to lr, previously set to address of __enable_mmu
__enable_mmu (147)
set page table pointer (TTB) in MMU hardware so it knows where to start page-table walks (167)
enable MMU so running with virtual addresses (185)
jump to r13, previously set to address of __switch_data, whose first field is address of __mmap_switched
__switch_data defined in arch/arm/kernel/head-common.S (15)
arch/arm/kernel/head-common.S: __mmap_switched (35)
copy data segment to RAM (39)
zero BSS (45)
branch to start_kernel (55)
Processor-independent kernel code
init/main.c: start_kernel (456)
Industrial single board computers are different than your regular home or office desktop computers. They are even quite different from other single board computers embedded in electronic products.
ReplyDeleteembedded project
ReplyDeleteVery enjoyable to visit this blog and find something exciting and amazing.
visit our website
Very Informative.Well Explained!!
ReplyDelete