Analysis of uboot migration process of Mini440

stay Forward analysis of make configuration compilation of uboot source code of embedded Linux In, we have introduced how to open the u-boot source code through Source Insight and the compilation process of uboot. In this section, we will analyze the source code of u-boot and how to transplant uboot to the Mini2440 development board.

Before migration, we need to clarify the hardware resources of the Mini2440 development board:

  • CPU: S3C2440;
  • NAND: K9F2G08U0C;
  • Network card: DM9000A;
  • SDRAM: HY57V561620FTP;

Download the u-boot source code package from the website, for example: u-boot-2016.05.tar.bz2   (the latest u-boot version does not support s3c2440).

1, uboot startup process analysis

1.1 uboot compilation

In the previous article, we introduced the compilation process of smdk2410. Here, the compilation of uboot is divided into two steps: configuration and compilation:

(1) Configure and select the board to use. I use S3C2440 for debugging, but there is no smdk2440 in the configs directory_ The defconfig file has only smdk2410_defconfig, so execute the following command to generate a. config file:

make smdk2410_defconfig

(2) Compile and execute the make command to generate u-boot:

 make ARCH=arm CROSS_COMPILE=arm-linux-

Cross-copy is a variable defined in the Makefile file, which is used to specify the cross tool chain, and ARCH is used to specify the processor architecture. In addition, we can define in the top-level Makefile of u-boot:


This saves the trouble of inputting on the console every time you compile.

After successful compilation, a u-boot.bin will be generated, which can be burned to the development board, but it is generally useless and needs further modification.


To analyze the startup process, the first step is to analyze the link file and find out where the program entry is. In the analysis of Makefile, you can see that the link script file is in the dependency of u-boot. This file is generated through smdk2410. After successful compilation, you can see in the top-level directory. Open is generated from the file under arch/arm/cpu/

#include <config.h>

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
    /DISCARD/ : { *(.u_boot_list_2_cmd_*) }
     * If CONFIG_ARMV7_SECURE_BASE is true, secure code will not
     * bundle with u-boot, and code offsets are fixed. Secure zone
     * only needs to be copied from the loading address to
     * CONFIG_ARMV7_SECURE_BASE, which is the linking and running
     * address for secure code.
     * If CONFIG_ARMV7_SECURE_BASE is undefined, the secure zone will
     * be included in u-boot address space, and some absolute address
     * were used in secure code. The absolute addresses of the secure
     * code also needs to be relocated along with the accompanying u-boot
     * code.
    /DISCARD/ : { *(.rel._secure*) }
    . = 0x00000000;

    . = ALIGN(4);
    .text :
        CPUDIR/start.o (.text*)



    .__secure_start : {
        . = ALIGN(0x1000);

    .secure_text CONFIG_ARMV7_SECURE_BASE :
        AT(ADDR(.__secure_start) + SIZEOF(.__secure_start))

    . = LOADADDR(.__secure_start) +
        SIZEOF(.__secure_start) +

    __secure_end_lma = .;
    .__secure_end : AT(__secure_end_lma) {
        LONG(0x1d1071c);    /* Must output something to reset LMA */

    . = ALIGN(4);
    .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

    . = ALIGN(4);
    .data : {

    . = ALIGN(4);

    . = .;

    . = ALIGN(4);
    .u_boot_list : {

    . = ALIGN(4);

    .__efi_runtime_start : {

    .efi_runtime : {

    .__efi_runtime_stop : {

    .efi_runtime_rel_start :

    .efi_runtime_rel : {

    .efi_runtime_rel_stop :

    . = ALIGN(4);

    .image_copy_end :

    .rel_dyn_start :

    .rel.dyn : {

    .rel_dyn_end :

    .end :

    _image_binary_end = .;

     * Deprecated: this MMU section is used by pxa at present but
     * should not be used by new boards/CPUs.
    . = ALIGN(4096);
    .mmutable : {

 * Compiler-generated __bss_start and __bss_end, see arch/arm/lib/bss.c
 * __bss_base and __bss_limit are for linker only (overlay ordering)

    .bss_start __rel_dyn_start (OVERLAY) : {
        __bss_base = .;

    .bss __bss_base (OVERLAY) : {
         . = ALIGN(4);
         __bss_limit = .;

    .bss_end __bss_limit (OVERLAY) : {

    .dynsym _image_binary_end : { *(.dynsym) }
    .dynbss : { *(.dynbss) }
    .dynstr : { *(.dynstr*) }
    .dynamic : { *(.dynamic*) }
    .plt : { *(.plt*) }
    .interp : { *(.interp*) }
    .gnu.hash : { *(.gnu.hash) }
    .gnu : { *(.gnu*) }
    .ARM.exidx : { *(.ARM.exidx*) }
    .gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
  • OUTPUT_FORMAT: Specifies that the output executable file is elf format, 32-bit ARM instruction, small end;
  • OUTPUT_ARCH: indicates that the platform for outputting executable files is ARM;
  • ENTRY: Specifies that the starting ENTRY code segment of the output executable file is_ start,_ Start is in arch/arm/cpu/armv7/start.S;
  • 0x00000000 is the link starting point of the program, and the uboot.bin link address finally generated by arm linux LD starts from 0x00000000;
  • . text {}: declare that this segment is a code segment. In this segment, all remaining code segments of section (. _image_copy_start), section(.vectors), section (all code segments (. Text *) in start. O) and section(*(.text *) are stored in turn.
    • *(. _image_copy_start): the interesting one here is section (. _image_copy_start), which is defined in arch/arm/lib/sections.c as follows:
      char __image_copy_start[0] __attribute__((section(".__image_copy_start")));
      This is a 0-length array. The extension of GNU to C actually achieves the purpose that section (. _image_copy_start) does not occupy the link address space when linking, and the starting address of subsequent section(.vectors) is still 0x00000000, and then__ image_ copy_ Start stores an address, that is 0x00000000, which is equivalent to creating a location label; Represents the starting address of the copy when the program is relocated;
    • *(. vectors): literally means vector segment. In arch/arm/lib/vectors.S, the. Vertos segment is defined:. section ".vectors", "ax"; This is a pseudo operation of a user-defined segment label, which means: define a segment label named. Vectors, which is an allowed and executable segment;
    • CPUDIR/start.o (.text *): CPUDIR is defined in the Makefile of the root directory according to the chip; Select different start.S compilations, and the. text segment in start.o compiled will be linked here; Here CPUDIR=arch/arm/cpu/arm920t;
    • *(. text *): all remaining codes are placed in this section;
  • . rodata {}: declare a read-only data segment, called rodata segment for short, to store constants, character constants, const constants, and debug information;
  • . data {}: declare initialized data segment (abbreviated as data segment), which stores the initialized global and initialized static variables in the program;
  • . bss {}: declare uninitialized data segment (bss segment for short), which stores uninitialized global and uninitialized static variables in the program. This area will be cleared by the kernel when the program is loaded;

__ image_copy_start,__ image_copy_end is used for u-boot to move its image to the specified ddr address;

__ rel_dyn_start,__ rel_dyn_end for relocation code;

__ bss_start,__ bss_end is the start and end address of the BSS segment;

These variables are used in arch/arm/lib/sections.c to define some global variables to act on the starting address of each section:

//C In this way, a variable or function is marked to a specified segment in the file.
char __bss_start[0] __attribute__((section(".__bss_start")));
char __bss_end[0] __attribute__((section(".__bss_end")));
char __image_copy_start[0] __attribute__((section(".__image_copy_start")));
char __image_copy_end[0] __attribute__((section(".__image_copy_end")));
char __rel_dyn_start[0] __attribute__((section(".__rel_dyn_start")));
char __rel_dyn_end[0] __attribute__((section(".__rel_dyn_end")));
char __secure_start[0] __attribute__((section(".__secure_start")));
char __secure_end[0] __attribute__((section(".__secure_end")));

Obviously, the first instruction of the program is in arch/arm/cpu/arm920t/start.S.

1.3 analysis start.S

 *  armboot - Startup Code for ARM920 CPU-core
 *  Copyright (c) 2001  Marius Gröger <>
 *  Copyright (c) 2002  Alex Züpke <>
 *  Copyright (c) 2002  Gary Jennejohn <>
 * SPDX-License-Identifier: GPL-2.0+

#include <asm-offsets.h>
#include <common.h>
#include <config.h>

 * Startup Code (called from the ARM reset exception vector)
 * do important init only if we don't start from memory!
 * relocate armboot to ram
 * setup stack
 * jump to second stage

    .globl  reset

     * set the cpu to SVC32 mode    1. Set to SVC mode
    mrs r0, cpsr
    bic r0, r0, #0x1f
    orr r0, r0, #0xd3
    msr cpsr, r0    

#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)
     * relocate exception table
    ldr r0, =_start
    ldr r1, =0x0
    mov r2, #16
    subs    r2, r2, #1
    ldr r3, [r0], #4
    str r3, [r1], #4
    bne copyex

#ifdef CONFIG_S3C24X0
    /* turn off the watchdog */

# if defined(CONFIG_S3C2400)
#  define pWTCON    0x15300000
#  define INTMSK    0x14400008  /* Interrupt-Controller base addresses */
#  define CLKDIVN   0x14800014  /* clock divisor register */
#  define pWTCON    0x53000000  /* Watchdog control register address */
#  define INTMSK    0x4A000008  /* Interrupt-Controller base addresses */
#  define INTSUBMSK 0x4A00001C
#  define CLKDIVN   0x4C000014  /* clock divisor register */
# endif

    ldr r0, =pWTCON        //2. Shut the watchdog
    mov r1, #0x0
    str r1, [r0]

     * mask all IRQs by setting all bits in the INTMR - default
    mov r1, #0xffffffff    //3. Mask interrupt
    ldr r0, =INTMSK
    str r1, [r0]
# if defined(CONFIG_S3C2410)
    ldr r1, =0x3ff
    ldr r0, =INTSUBMSK
    str r1, [r0]
# endif

    /* FCLK:HCLK:PCLK = 1:2:4 */
    /* default FCLK is 120 MHz ! */
    ldr r0, =CLKDIVN      //4. Set the frequency division coefficient, and the clock frequency is not set
    mov r1, #3
    str r1, [r0]
#endif  /* CONFIG_S3C24X0 */

     * we do sys-critical inits only at reboot,
     * not when booting from ram!
    bl  cpu_init_crit    //5. Jump to cpu_init_crit function

    bl  _main           //6. get into arch/arm/lib/crt0.S of_main Function for other initialization


    .globl  c_runtime_cpu_setup

    mov pc, lr

 * CPU_init_critical registers
 * setup important registers
 * setup memory timing

     * flush v4 I/D caches      [1]. Clear cache
    mov r0, #0
    mcr p15, 0, r0, c7, c7, 0   /* flush v3/v4 cache */
    mcr p15, 0, r0, c8, c7, 0   /* flush v4 TLB */

     * disable MMU stuff and caches  [2]. Disable MMU
    mrc p15, 0, r0, c1, c0, 0
    bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
    bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
    orr r0, r0, #0x00000002 @ set bit 1 (A) Align
    orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
    mcr p15, 0, r0, c1, c0, 0

     * before relocating, we have to setup RAM timing
     * because memory timing is board-dependend, you will
     * find a lowlevel_init.S in your board directory.
    mov ip, lr

    bl  lowlevel_init          //[3]. get into board/samsung/smdk2410/lowlevel_init.S Execution,
                               //     Set timing parameters of memory control register
    mov lr, ip
    mov pc, lr

Reference article:

[1]U-BOOT-2016.07 transplantation (Part I) preliminary analysis

[2]Uboot startup -- uboot basic startup process

Posted by Tonsil on Thu, 28 Oct 2021 08:36:28 -0700