This article is a record of Teacher Peng’s first live broadcast on Bilibili, where he took notes while streaming. The video has been uploaded to Bilibili.
Now it has been organized into this article. If you want to learn about the detailed process of U-Boot startup code, you can enter my Bilibili space to learn together with the video.
Bilibili Username: YikouLinux
For ARM series articles, please click the following summary link:
“Learn ARM from Scratch Collection“
Introduction
In the previous ARM series courses, we have explained the ARM architecture, assembly instructions, exceptions, and the drivers for commonly used peripherals. Thus, we have acquired the basic skills for developing ARM series products.
This article will introduce a commonly used bootloader: U-Boot. Through the introduction of U-Boot and detailed analysis of its source code, we aim to integrate all previous ARM-related knowledge points.
1. U-Boot
1. Concept
U-Boot is a bootloader primarily used for embedded systems, supporting various computer architectures including PPC, ARM, AVR32, MIPS, x86, 68k, Nios, and MicroBlaze. It is also a free software released under the GNU General Public License.
U-Boot not only supports the booting of embedded Linux systems but also supports NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, and Android embedded operating systems. Currently, it aims to support target operating systems such as OpenBSD, NetBSD, FreeBSD, 4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS, and Android.
2. Basic Functions of U-Boot
The main function list supported by U-Boot includes:
-
System boot supports NFS mount and RAMDISK (compressed or uncompressed) root filesystems; supports NFS mount and booting the kernel from FLASH (compressed or uncompressed); -
Basic auxiliary function with powerful operating system interface capabilities; can flexibly set and pass multiple key parameters to the operating system, suitable for debugging requirements at different stages of system development and product release, with the strongest support for Linux; supports multiple storage methods for target board environment parameters, such as FLASH, NVRAM, EEPROM; -
CRC32 check can verify whether the kernel and RAMDISK image files in FLASH are intact; -
Device drivers for serial port, SDRAM, FLASH, Ethernet, LCD, NVRAM, EEPROM, keyboard, USB, PCMCIA, PCI, RTC, etc.; -
Power-on self-test function for automatic detection of SDRAM and FLASH size; SDRAM fault detection; CPU model.
3. Common Commands
There are many U-Boot commands; below are only the commands needed for network boot:
Command | Meaning |
---|---|
printenv | Print U-Boot environment variables |
setenv | Set U-Boot environment variables |
ipaddr | Local IP address |
serverip | TFTP server’s IP address |
ethaddr | Ethernet MAC address |
netmask | Ethernet network mask |
gateway | Ethernet gateway |
bootcmd | Command to execute on automatic startup |
bootargs | Boot parameters passed to the Linux kernel |
bootm | Boots the program image stored in memory. This memory includes RAM and Flash that can be permanently saved. |
bootdelay | Wait seconds for executing automatic startup (bootcmd commands) |
baudrate | Baud rate of serial console |
4. Example Configuration Parameters
Below is an example of downloading the kernel over the network and mounting NFS.
1) Ubuntu Environment
Ubuntu IP: 192.168.6.186
NFS Configuration:
Configuration file as follows:
/etc/exports
Configuration information as follows:
2) Development Board Settings
Development board IP: 192.168.6.187
Configuration commands:
setenv ipaddr 192.168.6.187 ; Board's IP
setenv serverip 192.168.6.186 ; Virtual machine's IP
setenv gatewayip 192.168.1.1 ; Gateway
saveenv ; Save configuration
-
Load kernel and device tree
setenv bootcmd tftp 41000000 uImage
tftp 42000000 exynos4412-fs4412.dtbootm 41000000 - 42000000
bootcmd: After U-Boot starts, it first executes this parameter to perform the subsequent commands.
Download the kernel image uImage from the TFTP server to address 41000000, and the device tree file exynos4412-fs4412.dtb to 42000000, and use the command bootm to load and start the kernel.
-
Mount NFS
setenv bootargs root=/dev/nfs nfsroot=192.168.6.186:/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.6.187
Mount NFS filesystem
-
root=/dev/nfs -
nfsroot=192.168.6.186:/rootfs NFS server address 192.168.6.186, directory is /rootfs, -
rw File system operation permission is writable -
console=ttySAC2,115200 Serial port name and baud rate -
init=/linuxrc The process to run after the kernel starts is linuxrc -
ip=192.168.6.187 Development board address
2. Exynos-4412 SoC Boot Sequence
To understand the boot sequence of Exynos-4412, we first need to understand the memory layout of the SoC.
1. Exynos-4412 Memory Layout
Typically, the memory of a SoC is predetermined by the manufacturer during design, and users cannot change it.
We only care about one address related to startup,
-
iROM Inside the SoC, the manufacturer has solidified specific programs at factory time, and the program in iROM is unchangeable for users.
-
iRAM Inside the SoC, faster speed but smaller space.
-
DMC RAM controller, located inside the SoC, used to drive RAM. Large capacity RAM needs to be connected to this controller.
2. Booting Sequence
The booting sequence varies among different manufacturers. This article mainly uses Samsung’s Exynos-4412 SoC as a basis to explain the U-Boot boot sequence based on this board.
According to the above figure, the approximate order of system startup is as follows:
-
iROM is inside the SoC, a 64KB ROM that pools some necessary functions for system startup, such as: clock, stack.
-
iROM is responsible for loading the BL1 image from special startup peripherals into the 256KB SRAM inside the SoC. The startup peripherals are determined by the operation buttons. Depending on the value of different keys, iROM will perform different checks on the BL1 image.
-
BL1 initializes the system clock and DRAM controller, and then loads the OS image into DRAM from the startup peripherals. Depending on the value of the startup buttons, BL1 will perform different checks on the OS.
-
After startup is complete, BL1 jumps to the operating system (kernel).
iROM will select different startup devices based on the different OM pins, and the corresponding OM registers need to provide the corresponding startup information.
3. Overview of Kernel Startup Process
1. Overview of Kernel Startup Process
As shown in the figure above:
-
After powering on the device, it first executes the factory code in iROM, performing necessary hardware initialization and then executing U-Boot,
-
Usually, the kernel and device tree files are placed in FLASH.
-
After the program starts, it often first boots from FLASH, running U-Boot.
-
Step 1: Perform hardware initialization (svc mode stack, clock, memory, serial port) Step 2: Self-relocation: Copy U-Boot from FLASH to RAM, jump to RAM to execute the remaining U-Boot code. Step 3: Copy the kernel to RAM, execute the kernel, and hand over control to the kernel.
2. Detailed Kernel Startup Process
4. Detailed Analysis of U-Boot Startup Process Code
1. lds File
To understand the entire code flow of the U-Boot project, one must first understand the link script [Link script reference “7. Learn ARM-GNU pseudo instructions from scratch, using lds”].
This file determines the final generated image file of U-Boot and the layout of each segment.
The U-Boot link script is as follows:
u-boot-2013.01/arch/arm/cpu/u-boot.lds
File content:
26 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
27 OUTPUT_ARCH(arm)
28 ENTRY(_start)
29 SECTIONS
30 {
31 . = 0x00000000;
32
33 . = ALIGN(4);
34 .text :
35 {
36 __image_copy_start = .;
37 CPUDIR/start.o (.text*)
38 *(.text*)
39 }
40
41 . = ALIGN(4);
42 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
43
44 . = ALIGN(4);
45 .data : {
46 *(.data*)
47 }
48
49 . = ALIGN(4);
50
51 . = .;
52
53 . = ALIGN(4);
54 .u_boot_list : {
55 #include <u-boot.lst>
56 }
57
58 . = ALIGN(4);
59
60 __image_copy_end = .;
61
62 .rel.dyn : {
63 __rel_dyn_start = .;
64 *(.rel*)
65 __rel_dyn_end = .;
66 }
67
68 .dynsym : {
69 __dynsym_start = .;
70 *(.dynsym)
71 }
72
73 _end = .;
74
75 /*
76 * Deprecated: this MMU section is used by pxa at present but
77 * should not be used by new boards/CPUs.
78 */
79 . = ALIGN(4096);
80 .mmutable : {
81 *(.mmutable)
82 }
83
84 .bss __rel_dyn_start (OVERLAY) : {
85 __bss_start = .;
86 *(.bss*)
87 . = ALIGN(4);
88 __bss_end__ = .;
89 }
90
91 /DISCARD/ : { *(.dynstr*) }
92 /DISCARD/ : { *(.dynamic*) }
93 /DISCARD/ : { *(.plt*) }
94 /DISCARD/ : { *(.interp*) }
95 /DISCARD/ : { *(.gnu*) }
96 }
97
Core content explanation:
27 OUTPUT_ARCH(arm) : This image runs on ARM architecture hardware
28 ENTRY(_start) : The entry point of the program is _start
29 SECTIONS
30 {
31 . = 0x00000000; : The linking address of the program, not the running address [U-Boot must be position-independent code]
34 .text :
35 {
36 __image_copy_start = .; : Macro corresponding to the address of the entire program after compilation, initial position of self-relocation code
37 CPUDIR/start.o (.text*) : The code segment in the first target file CPUDIR/start.o
38 *(.text*) : The code segments of the remaining target files
39 }
60 __image_copy_end = .; : End of self-relocation code
BSS global uninitialized variables and global variables initialized to 0 are located in the segment:
84 .bss __rel_dyn_start (OVERLAY) : {
85 __bss_start = .;
86 *(.bss*)
87 . = ALIGN(4);
88 __bss_end__ = .;
89 }
2. Overview of U-Boot Startup Code Flow
The code is analyzed only up to the U-Boot command line, at the position of function main_loop().
3. Detailed Analysis of Startup Code
The _start entry is located in the following file:
u-boot-2013.01/arch/arm/cpu/armv7/start.S
Phase One:
Phase Two
The second phase code starts from _main:
For detailed explanations of the above code, please learn synchronously with the Bilibili video.
5. Several Key Knowledge Points of U-Boot Startup
-
How to determine the position of the first machine instruction?
The link script determines the memory layout.
The U-Boot link script is as follows:
u-boot-2013.01/arch/arm/cpu/u-boot.lds
File content:
28 ENTRY(_start)
29 SECTIONS
30 {
31 . = 0x00000000;
32
The entry point of U-Boot is _start
The linking address is 0x00000000
-
How does U-Boot relocate code?
The code is located at:
u-boot-2013.01/arch/arm/cpu/armv7/start.S
The relocation code is as follows:
ENTRY(relocate_code)
mov r4, r0 /* save addr_sp */
mov r5, r1 /* save addr of gd */
mov r6, r2 /* save addr of destination */
adr r0, _start
cmp r0, r6
moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */
beq relocate_done /* skip relocation */
mov r1, r6 /* r1 <- scratch for copy_loop */
ldr r3, _image_copy_end_ofs
add r2, r0, r3 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop
For details, refer to Chapter 4, Section 3.
-
How does U-Boot determine whether the boot is from a power-off state or from a sleep state?
board/samsung/fs4412/lowlevel_init.S
The code is as follows:
41 lowlevel_init:
54 /* AFTR wakeup reset */
55 ldr r2, =S5P_CHECK_DIDLE
56 cmp r1, r2
57 beq exit_wakeup
58
59 /* LPA wakeup reset */
60 ldr r2, =S5P_CHECK_LPA
61 cmp r1, r2
62 beq exit_wakeup
63
64 /* Sleep wakeup reset */
65 ldr r2, =S5P_CHECK_SLEEP
66 cmp r1, r2
67 beq wakeup_reset
112 wakeup_reset:
113 bl system_clock_init
114 bl mem_ctrl_asm_init
115 bl tzpc_init
116
117 exit_wakeup:
118 /* Load return address and jump to kernel */
119 ldr r0, =(EXYNOS4_POWER_BASE + INFORM0_OFFSET)
120
121 /* r1 = physical address of exynos4210_cpu_resume function */
122 ldr r1, [r0]
123
124 /* Jump to kernel*/
125 mov pc, r1
From the above, we can see that when a mobile phone enters sleep for various reasons, it protects the context of the currently executing program and writes specific data to some PMIC registers to indicate why it entered sleep.
However, the phone is not completely powered off but is in a low-power mode, so the RAM still has data. Therefore, after this startup, it only needs to read the corresponding values from special registers to know the reason for the previous sleep and restore the context before sleep.
-
After U-Boot code is moved to RAM, the running address of the code changes. How to ensure that the program jump does not go wrong?
In addition to ensuring that U-Boot code is position-independent, .rel.dyn helps us solve this, mainly because the compiler does a lot of work for us.
For position-independent code, refer to “15. Learn ARM from Scratch – What is Position-Independent Code?”
-
When the device starts, it may boot directly from RAM. How to know whether it started from FLASH or RAM?
File:
board/samsung/fs4412/lowlevel_init.S
Code:
lowlevel_init:
85 /*
86 * If U-Boot is already running in RAM, no need to relocate U-Boot.
87 * Memory controller must be configured before relocating U-Boot
88 * in RAM.
89 */
90 ldr r0, =0x0ffffff /* r0 <- Mask Bits*/
91 bic r1, pc, r0 /* pc <- current addr of code */
92 /* r1 <- unmasked bits of pc */
93 ldr r2, _TEXT_BASE /* r2 <- original base addr in RAM */
94 bic r2, r2, r0 /* r2 <- unmasked bits of r2*/
95 cmp r1, r2 /* compare r1, r2 */
96 beq 1f /* r0 == r1 then skip SDRAM init */
The principle is: The RAM address space is: 0x40000000-0xA0000000 0xA0000000-0x00000000 and the iROM/iRAM address’s bit:28-31 are all 0, so we only need to read the value of PC when executing lowlevel_init to determine whether the bit:28-31 is 0 to know if the code is running in RAM.
The source code, datasheet, and cross-compilation tools used in this article can be obtained by replying “uboot2013” in the WeChat public account.
Recommended Reading
To join the group, please add Yikoujun’s personal WeChat to guide you in embedded learning.
Reply “1024” in the public account to get free learning materials, looking forward to your attention~