• uboot启动流程源码分析(一)


    其实前面我学习韦神的书的时候,学习过uboot,知道分为两个阶段。期间对这个uboot的源码没有了解深入。最近坐AVB校验,需要uboot到kernel的这个过程。这里再学习一下。

    与大多数BootLoader一样,uboot的启动过程分为BL1和BL2两个阶段。

    BL1阶段通常是开发板的配置等设备初始化代码,需要依赖依赖于SoC体系结构,通常用汇编语言来实现;

    BL2阶段主要是对外部设备如网卡、Flash等的初始化以及uboot命令集等的自身实现,通常用C语言来实现。

    (这里不要和ATF的BL1搞混了哈)

    1、BL1阶段

    uboot的BL1阶段代码通常放在start.s文件中,用汇编语言实现,其主要代码功能如下:

    (1) 指定uboot的入口。在链接脚本uboot.lds中指定uboot的入口为start.S中的_start。

    (2)设置异常向量(exception vector)

    (3)关闭IRQ、FIQ,设置SVC模式

    (4)关闭L1 cache、设置L2 cache、关闭MMU

    (5)根据OM引脚确定启动方式

    (6)在SoC内部SRAM中设置栈

    (7)lowlevel_init(主要初始化系统时钟、SDRAM初始化、串口初始化等)

    (8)设置开发板供电锁存

    (9)设置SDRAM中的栈

    (10)将uboot从SD卡拷贝到SDRAM中

    (11)设置并开启MMU

    (12)通过对SDRAM整体使用规划,在SDRAM中合适的地方设置栈

    (13)清除bss段,远跳转到start_armboot执行,BL1阶段执行完

    2、BL2阶段

    start_armboot函数位于lib_arm/board.c中,是C语言开始的函数,也是BL2阶段代码中C语言的 主函数,同时还是整个u-boot(armboot)的主函数,BL2阶段的主要功能如下:

    (1)规划uboot的内存使用

    (2)遍历调用函数指针数组init_sequence中的初始化函数

    (3)初始化uboot的堆管理器mem_malloc_init

    (4)初始化SMDKV210开发板的SD/MMC控制器mmc_initialize

    (5)环境变量重定位env_relocate

    (6)将环境变量中网卡地址赋值给全局变量的开发板变量

    (7)开发板硬件设备的初始化devices_init

    (8)跳转表jumptable_init

    (9)控制台初始化console_init_r

    (10)网卡芯片初始化eth_initialize

    (11)uboot进入主循环main_loop

    这里主要对第二个阶段BL2进行一个分析。

    3、start_armboot函数分析

    start_armboot函数的主要功能如下:

    (1)遍历调用函数指针数组init_sequence中的初始化函数

    依次遍历调用函数指针数组init_sequence中的函数,如果有函数执行出错,则执行hang函数,打印出”### ERROR ### Please RESET the board ###”,进入死循环。

    (2)初始化uboot的堆管理器mem_malloc_init

    (3)初始化SMDKV210的SD/MMC控制器mmc_initialize

    (4)环境变量重定位env_relocate

    (5)将环境变量中网卡地址赋值给全局变量的开发板变量

    (6)开发板硬件设备的初始化devices_init

    (7)跳转表jumptable_init

    (8)控制台初始化console_init_r

    (9)网卡芯片初始化eth_initialize

    (10)uboot进入主循环main_loop

    1、第二阶段的函数入口: start_armboot(void)
    void start_armboot (void)
    {
        init_fnc_t **init_fnc_ptr;
        char *s;
    #ifndef CFG_NO_FLASH
        ulong size;
    #endif
    #if defined(CONFIG_VFD) || defined(CONFIG_LCD)
        unsigned long addr;
    #endif
        /* Pointer is writable since we allocated a register for it */
        gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));  //gd结构体内所有信息,最终会传递给Linux内核//
        /* compiler optimization barrier needed for GCC >= 3.4 */
        __asm__ __volatile__("": : :"memory");
        memset ((void*)gd, 0, sizeof (gd_t));
        gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
        memset (gd->bd, 0, sizeof (bd_t));
        monitor_flash_len = _bss_start - _armboot_start;
        for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {  / /这里for循环的是一个函数接口数组:
            if ((*init_fnc_ptr)() != 0) {
                hang ();
            }
        }
    /*板子初始化函数数组,函数被按照顺序调用*/
    init_fnc_t *init_sequence[] = {
        cpu_init,        /* basic cpu dependent setup */
        board_init,        /* basic board dependent setup */
        interrupt_init,        /* set up exceptions */
        env_init,        /* initialize environment */
        init_baudrate,        /* initialze baudrate settings */
        serial_init,        /* serial communications setup */
        console_init_f,        /* stage 1 init of console */
        display_banner,        /* say that we are here */
    #if defined(CONFIG_DISPLAY_CPUINFO)
        print_cpuinfo,        /* display cpu info (and speed) */
    #endif
    #if defined(CONFIG_DISPLAY_BOARDINFO)
        checkboard,        /* display board info */
    #endif
        dram_init,        /* configure available RAM banks */
        display_dram_config,
        NULL,
    };
    /
    2、cpu_init()对CPU的IRQ和FIQ堆栈初始化
    此函数在./cpu/armxxx/cpu.c里
    int cpu_init (void)
    {
        /*
         * setup up stacks if necessary
         */
    #ifdef CONFIG_USE_IRQ
        IRQ_STACK_START = _armboot_start - CFG_MALLOC_LEN - CFG_GBL_DATA_SIZE - 4;
        FIQ_STACK_START = IRQ_STACK_START - CONFIG_STACKSIZE_IRQ;
    #endif
        return 0;
    }
    //
    3、 board_init()对CPU的系统时钟、GPIO口和串口的初始化
    此函数在./board/xxx/xxx.上
    int board_init (void)
    {
        DECLARE_GLOBAL_DATA_PTR;
        S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
        S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
        /* to reduce PLL lock time, adjust the LOCKTIME register */
        clk_power->LOCKTIME = 0xFFFFFF;
        /* configure MPLL */
        clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);
        /* some delay between MPLL and UPLL */
        delay (4000);
        /* configure UPLL */
        clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);
        /* some delay between MPLL and UPLL */
        delay (8000);
        /* set up the I/O ports */
        gpio->GPACON = 0x007FFFFF;
        gpio->GPBCON = 0x00044556;
        gpio->GPBUP = 0x000007FF;
        gpio->GPCCON = 0xAAAAAAAA;
        gpio->GPCUP = 0x0000FFFF;
        gpio->GPDCON = 0xAAAAAAAA;
        gpio->GPDUP = 0x0000FFFF;
        gpio->GPECON = 0xAAAAAAAA;
        gpio->GPEUP = 0x0000FFFF;
        gpio->GPFCON = 0x000055AA;
        gpio->GPFUP = 0x000000FF;
        gpio->GPGCON = 0xFF95FF3A;
        gpio->GPGUP = 0x0000FFFF;
        gpio->GPHCON = 0x0016FAAA;
        gpio->GPHUP = 0x000007FF;
        gpio->EXTINT0=0x22222222;
        gpio->EXTINT1=0x22222222;
        gpio->EXTINT2=0x22222222;
        /* arch number of SMDK2410-Board */
        gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
        /* adress of boot parameters */
        gd->bd->bi_boot_params = 0x30000100;
        icache_enable();     //地址总线高速缓存区使能//
        dcache_enable();   //数据总线高速缓存区使能//
        return 0;
    }
    串口通信初始化,函数在/cpu/armxxx/xxx/serial.c里
    void serial_setbrg (void)
    {
        S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);
        int i;
        unsigned int reg = 0;
        /* value is calculated so : (int)(PCLK/16./baudrate) -1 */
        reg = get_PCLK() / (16 * gd->baudrate) - 1;
        /* FIFO enable, Tx/Rx FIFO clear */
        uart->UFCON = 0x07;
        uart->UMCON = 0x0;
        /* Normal,No parity,1 stop,8 bit */
        uart->ULCON = 0x3;
        /*
         * tx=level,rx=edge,disable timeout int.,enable rx error int.,
         * normal,interrupt or polling
         */
        uart->UCON = 0x245;
        uart->UBRDIV = reg;
    #ifdef CONFIG_HWFLOW
        uart->UMCON = 0x1; /* RTS up */
    #endif
        for (i = 0; i < 100; i++);
    }
    /*
    * Initialise the serial port with the given baudrate. The settings
    * are always 8 data bits, no parity, 1 stop bit, no start bits.
    *
    */
    int serial_init (void)
    {
        serial_setbrg ();
        return (0);
    }
    //
    4、 interrupt_init()配置启动定时器4中断,10ms一次
    此函数在./cpu/armxxx/xxx/interupts.c上
    int interrupt_init (void)
    {
        S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();
        /* use PWM Timer 4 because it has no output */
        /* prescaler for Timer 4 is 16 */
        timers->TCFG0 = 0x0f00;
        if (timer_load_val == 0)
        {
            /*
             * for 10 ms clock period @ PCLK with 4 bit divider = 1/2
             * (default) and prescaler = 16. Should be 10390
             * @33.25MHz and 15625 @ 50 MHz
             */
            timer_load_val = get_PCLK()/(2 * 16 * 100);
        }
        /* load value for 10 ms timeout */
        lastdec = timers->TCNTB4 = timer_load_val;
        /* auto load, manual update of Timer 4 */
        timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;
        /* auto load, start Timer 4 */
        timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
        timestamp = 0;
        return (0);
    }
    //
    5、 env_init()配置检查可用的FLASH
    此函数在./common/env_flash.c里
    int  env_init(void)
    {
        int crc1_ok = 0, crc2_ok = 0;
        uchar flag1 = flash_addr->flags;          //用来判断FLASH是否是空的
        uchar flag2 = flash_addr_new->flags; .
        ulong addr_default = (ulong)&default_environment[0];
        ulong addr1 = (ulong)&(flash_addr->data);
        ulong addr2 = (ulong)&(flash_addr_new->data);
    #ifdef CONFIG_OMAP2420H4
        int flash_probe(void);
        if(flash_probe() == 0)
            goto bad_flash;
    #endif
        /*对待用的新地址进行CRC校验*/
        crc1_ok = (crc32(0, flash_addr->data, ENV_SIZE) == flash_addr->crc);
        crc2_ok = (crc32(0, flash_addr_new->data, ENV_SIZE) == flash_addr_new->crc);
        if (crc1_ok && ! crc2_ok) {
            gd->env_addr  = addr1;
            gd->env_valid = 1;
        } else if (! crc1_ok && crc2_ok) {
            gd->env_addr  = addr2;
            gd->env_valid = 1;
        } else if (! crc1_ok && ! crc2_ok) {
            gd->env_addr  = addr_default;
            gd->env_valid = 0;
        } else if (flag1 == ACTIVE_FLAG && flag2 == OBSOLETE_FLAG) {
            gd->env_addr  = addr1;
            gd->env_valid = 1;
        } else if (flag1 == OBSOLETE_FLAG && flag2 == ACTIVE_FLAG) {
            gd->env_addr  = addr2;
            gd->env_valid = 1;
        } else if (flag1 == flag2) {
            gd->env_addr  = addr1;
            gd->env_valid = 2;
        } else if (flag1 == 0xFF) {
            gd->env_addr  = addr1;
            gd->env_valid = 2;
        } else if (flag2 == 0xFF) {
            gd->env_addr  = addr2;
            gd->env_valid = 2;
        }
    #ifdef CONFIG_OMAP2420H4
    bad_flash:
    #endif
        return (0);
    }
    //
    6、 init_baudrate()初始化配置串口波特率,递交给内核启动变量
    此函数位置在./lib_xxx/board.c
    static int init_baudrate (void)
    {
        char tmp[64];    /* long enough for environment variables */
        int i = getenv_r ("baudrate", tmp, sizeof (tmp));
        gd->bd->bi_baudrate = gd->baudrate = (i > 0)
                ? (int) simple_strtoul (tmp, NULL, 10)
                : CONFIG_BAUDRATE;
        return (0);
    }
    //
    7、 console_init_f()向Linux内核递交串口控制台信息
    此函数在./common/console.c
    int console_init_f (void)
    {
        gd->have_console = 1;
    #ifdef CONFIG_SILENT_CONSOLE
        if (getenv("silent") != NULL)
            gd->flags |= GD_FLG_SILENT;
    #endif
        return (0);
    }
    ///
    8、 dram_init()函数定义了板子的内存地址与大小等信息,并向内核递交
    此函数在./board/sbc2410x/sbc2410x.c
    int dram_init (void)
    {
        DECLARE_GLOBAL_DATA_PTR;
        gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
        gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
        return 0;
    }
    ///
    9、 main_loop()引导启动Linux内核的真正函数
    此函数在./common/main.c
    这里面其实是启动了U-BOOT的控制台指令集,提供u-boot的各种功能包括引导启动内核
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252

    main_loop()引导启动Linux内核的真正函数,这个main_loop()才是我最关注的函数。

    这一步找到了我想要的关注点,就是main_loop()函数。

    参考链接:
    https://blog.csdn.net/weixin_40639467/article/details/122506413
    https://blog.51cto.com/u_15169172/2710568

  • 相关阅读:
    板块一 Servlet编程:第三节 HttpServletRequest对象全解与请求转发 来自【汤米尼克的JAVAEE全套教程专栏】
    C++面试知识点总结
    VSCODE 使用技巧
    阿里P8晒1月工资条,看完真的狠狠扎心了…
    Linux三剑客
    windows 上的C语言 图形界面设计函数 ( easyx 插件 )
    MySQL——事务隔离级别
    在Windows环境与Linux环境下搭建Zookeeper单机环境与集群环境
    【windows版】TensorRT安装教程
    集成Emscripten+wasm至React项目踩坑记录
  • 原文地址:https://blog.csdn.net/weixin_45264425/article/details/127399246