After getting u-boot fully debuggable it is finally time to use such powerful tool and deep dive in U-boot to learn how it is working. Looking ahead here is diagram providing U-boot initialization sequence starting from first step in reset function and finishing with cli_loop checking for input command typed by user through long list of initialization functions called to get peripheral working and ready to start Linux Kernel:
So let’s walk through this graph.
Two stages of U-boot
Generally U-boot has two initialization stages:
- first are steps from 1 to 43 on the graph (violet path)
- second stage are steps from 45 to 80 (green path)
divided among themselves by relocation procedure (step 44, red color).
First stage executes on starting memory area. Than during relocation procedure U-boot copies itself to the end of RAM memory by address calculated on last steps of first stage and jumps to new address after that. Second stage executes on final memory area and results on booting kernel or providing user access console depending whether user typed anything in console or not. Each stage ends by calling same initcall_run_list function but with different arguments. That function (initcall_run_list) accepts array of initialization function pointers to be called in loop. On first stage there is init_sequence_f (common/board_f.c) array of function pointers provided to that function as input argument:
On second stage init_sequnce_r (common/board_r.c) array provided:
U boot execution starts in Assembly written /arch/arm/cpu/<arch>/startup.S file /arch/arm/cpu/armv7/startup.S in our case:
STEPS 1, 2, 3, 4 ON THE GRAPH.
First things it do – calls few assembly/C written functions that disable interrupts, initialize interrupts vector table (SCTLR register), setup CP15 registers (cache, MMU, TLBs disabling), setups a temporary stack, e.g. make very early initialization without setup DRAM, without using global_data, without any console prints.
STEPS 5, 6, 7 ON THE GRAPH.
Next _main function being called to reserve memory area for global_data (gd pointer storing all globally used information, e.g. main system state descriptor) by calling board_init_f_alloc_reserve and board_init_init_reserve functions.
STEP 8, 9 ON THE GRAPH.
And after that _main calls for board_init_f that in turn calls initcall_run_list for the first time.
STEP 10-43 ON THE GRAPH.
Bunch of initialization functions called in loop by initcall_run_list. Here initialization of console (23), environment (21), dts parsing (17), timers (19), DRAM and other things finishing with calculation of relocation address (31-43).
STEP 44-45 ON THE GRAPH.
STEP 48, 49 ON THE GRAPH.
After that reset calls for board_init_r that in turn calls initcall_run_list for the second time.
STEP 50-75 ON THE GRAPH.
Bunch of initialization functions called in loop by initcall_run_list. The last called here function is run_main_loop (75) that calls main_loop (77) function which starts countdown timer before Linux kernel to be loaded if user doesn’t type any symbol in console before it.
That is it. Of course there is just a general description of U-boot sceleton. There are lot of things happens in each initialization function like Device Tree scanning. Set of articles describing this thing as well as Linux Kernel loading, environment variables initialization, input command handling in main loop planned.