• am335x uboot启动流程分析


    基本指令含义

    .globl _start

    .globl指示告诉汇编器,_start这个符号要被链接器用到,所以要在目标文件的符号表中标记它是一个全局符号

    b,bl

    b是不带返回的跳转  bl带返回的跳转

    .word

    插入一个32-bit的数据队列。(与armasm中的DCD功能相同)

    芯片到uboot启动流程 :ROM → MLO(SPL)→ uboot.img

    启动脚本:/u-boot2011.09/arch/arm/cpu/armv7/omap-common/u-boot_spl.lds

    MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,

    LENGTH = CONFIG_SPL_MAX_SIZE }

    MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR,

    LENGTH = CONFIG_SPL_BSS_MAX_SIZE }

    OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

    OUTPUT_ARCH(arm)

    ENTRY(_start)

    SECTIONS

    {

    .text      :

    {

    __start = .;

      arch/arm/cpu/armv7/start.o(.text)    //入口函数

      *(.text*)

    } >.sram

    . = ALIGN(4);

    .rodata : { *(SORT_BY_ALIGNMENT(.rodata*)) } >.sram

    . = ALIGN(4);

    .data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram

    . = ALIGN(4);

    __image_copy_end = .;

    _end = .;

    .bss :

    {

    . = ALIGN(4);

    __bss_start = .;

    *(.bss*)

    . = ALIGN(4);

    __bss_end__ = .;

    } >.sdram

    }

    分析文件arch/arm/cpu/armv7/start.S

    1. 保存启动参数  arch/arm/cpu/armv7/ti81xx/lowlevel_init.S

    bl  save_boot_params

    简化代码:

    save_boot_params:

    #ifdef CONFIG_SPL_BUILD

    ldrr4, =ti81xx_boot_device

    ldrr5, [r0, #BOOT_DEVICE_OFFSET]

    andr5, r5, #BOOT_DEVICE_MASK

    strr5, [r4]

    #endif

    bxlr

    2. cpu初始化   cpu_init_crit  

    首先设置cpu工作模,把cpu的状态类型为SVC,然后执行cpu_init_crit关闭mmu缓存,接着跳转到arch/arm/cpu/armv7/ti81xx/lowlevel_init.S  lowlevel_init中,

     lowlevel_init 主要调用 boardaplexecm_5206evm.c  s_init 函数

    void s_init(void)

    {

    l2_cache_enable();       //二级缓存

    __raw_writel(0xAAAA, WDT_WSPR);

    while(__raw_readl(WDT_WWPS) != 0x0);

    __raw_writel(0x5555, WDT_WSPR);

    while(__raw_readl(WDT_WWPS) != 0x0);

    pll_init();             //设置时钟

    u32 regVal;

    u32 uart_base = DEFAULT_UART_BASE;

    enable_uart0_pin_mux();   //使能串口0

    if (board_id == IA_BOARD) {

    uart_base = UART3_BASE;

    }

    regVal = __raw_readl(uart_base + UART_SYSCFG_OFFSET);

    regVal |= UART_RESET;    //直接操作寄存器

    __raw_writel(regVal, (uart_base + UART_SYSCFG_OFFSET) );

    while ((__raw_readl(uart_base + UART_SYSSTS_OFFSET) &

    UART_CLK_RUNNING_MASK) != UART_CLK_RUNNING_MASK);

    /* Disable smart idle */

    regVal = __raw_readl((uart_base + UART_SYSCFG_OFFSET));

    regVal |= UART_SMART_IDLE_EN;

    __raw_writel(regVal, (uart_base + UART_SYSCFG_OFFSET));

    init_timer();                //实际上是timer2

    preloader_console_init();   //控制台初始化

    config_am335x_ddr();        //配置DDR

    }

    即:

    1)使能二级缓存  l2_cache_enable();

    2)关闭看门狗    __raw_writel(0x5555, WDT_WSPR);  

    3)设置外设时钟  pll_init()

    PATH: boardaplexecm_5206中

        mpu_pll_config(MPUPLL_M_720);

        core_pll_config();

        per_pll_config();

        ddr_pll_config();

    interface_clocks_enable();    

    power_domain_transition_enable();   

        per_clocks_enable();       

    使能RTC rtc32k_enable()  /board/ti/am335x/evm.c

    使能32K实时时钟时钟,准确是应该是32.768K

    static void rtc32k_enable(void)

    {

        __raw_writel(0x83e70b13, (AM335X_RTC_BASE + RTC_KICK0_REG));

        __raw_writel(0x95a4f1e0, (AM335X_RTC_BASE + RTC_KICK1_REG));

        __raw_writel(0x48, (AM335X_RTC_BASE + RTC_OSC_REG));

    }

    4)使能串口 UART0

    配置串口,主要用于打印中断,也可以配置UART3

    if (board_id == IA_BOARD) {

    uart_base = UART3_BASE;

    }

    6)初始化定时器Timer2 init_timer();

    7)初始化控制台 preloader_console_init()

    简化代码为: 实际上是串口配置  修改这里 可以修改串口控制台应该

    /* This requires UART clocks to be enabled */

    void preloader_console_init(void)  

    {

    const char *u_boot_rev = U_BOOT_VERSION;

    char rev_string_buffer[50];

    gd = &gdata;

    gd->bd = &bdata;

    gd->flags |= GD_FLG_RELOC;

    gd->baudrate = CONFIG_BAUDRATE;        //串口波特率设置

    serial_init();  //串口初始化

    u_boot_rev = &u_boot_rev[7];

    omap_rev_string(rev_string_buffer);

    }

    8)配置DDR  config_am335x_ddr(void)

    简化代码:

    static void config_am335x_ddr(void)  //ddr2初始化

    {

        int data_macro_0 = 0;

        int data_macro_1 = 1;

        enable_ddr_clocks();

        config_vtp();

        Cmd_Macro_Config();

        Data_Macro_Config(data_macro_0);

        Data_Macro_Config(data_macro_1);

        __raw_writel(PHY_RANK0_DELAY, DATA0_RANK0_DELAYS_0);

        __raw_writel(PHY_RANK0_DELAY, DATA1_RANK0_DELAYS_0);

        __raw_writel(DDR_IOCTRL_VALUE, DDR_CMD0_IOCTRL);

        __raw_writel(DDR_IOCTRL_VALUE, DDR_CMD1_IOCTRL);

        __raw_writel(DDR_IOCTRL_VALUE, DDR_CMD2_IOCTRL);

        __raw_writel(DDR_IOCTRL_VALUE, DDR_DATA0_IOCTRL);

        __raw_writel(DDR_IOCTRL_VALUE, DDR_DATA1_IOCTRL);

        __raw_writel(__raw_readl(DDR_IO_CTRL) & 0xefffffff, DDR_IO_CTRL);

        __raw_writel(__raw_readl(DDR_CKE_CTRL) | 0x00000001, DDR_CKE_CTRL);

        config_emif_ddr2();

    }

    到这里,整个cpu已经初始化完成,执行完成s_init之后返回到strt.S中,可以进行一些启动操作  比如配置状态灯等

    3.板级操作初始化  include/configs/ecm_5206.h

    返回start.S,执行

    call_board_init_f

     ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)

        bic sp, sp, #7 /* 8-byte alignment for ABI compliance */

    ldr r0,=0x00000000

    bl  board_init_f    //有返回的跳转到  board_init_f

    1) 设置 internal RAM 内存空间的栈指针

    2)接着跳转到/arch/arm/cpu/armv7/omap-common/spl.c

    执行 C代码  board_init_f(ulong dummy),跳转到spl第二阶段

    void board_init_f(ulong dummy)

    {

        debug(">>board_init_f() ");

    //重新指定

        relocate_code(CONFIG_SPL_STACK, &gdata, CONFIG_SPL_TEXT_BASE);

    4.代码重定位

    1)stack_setup

    2)copy_loop   

    3)clear_bss

    5.返回转到spl第二阶段 ,调用函数 board_init_r   

     路径: /arch/arm/cpu/armv7/omap-common/spl.

    简化代码为:

    void board_init_r(gd_t *id, ulong dummy)

    {

    u32 boot_device;

    timer_init();

    i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);

    spl_board_init();

    boot_device = omap_boot_device();  获取启动选择

    switch (boot_device) {

    case BOOT_DEVICE_MMC1:

    case BOOT_DEVICE_MMC2:

    spl_mmc_load_image();   //EMMC启动

    break;

    case BOOT_DEVICE_NAND:

    spl_nand_load_image();  //SD卡

    break;

    case BOOT_DEVICE_UART:

    spl_ymodem_load_image(); //串口

    break;

    default: hang();

    break;

    }

    jump_to_image_no_args();

    }

    流程概括为:

    1)初始化定时器  timer_init();

    2)i2c配置 i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);

    3)spl板级初始化 spl_board_init();

    4)按照启动方式 加载 image jump_....

    4)加载并跳转到img  代码也在 /arch/arm/cpu/armv7/omap-common/spl.c  

    简化代码为:

    static void jump_to_image_no_args(void)

    {

    typedef void (*image_entry_noargs_t)(void)__attribute__ ((noreturn));

    image_entry_noargs_t image_entry =

    (image_entry_noargs_t) spl_image.entry_point;

    int *p = 0x80000000;

    *p = omap_boot_device();

    image_entry();        //跳转到image中

    }

     board_init_r 中完成 MLO(SPI)阶段的所有初始化,并跳转到 uboot.img 阶段

    ----------------------------uboot.img--------------------------------

    6.uboot.img   arch/arm/lib/board.c

    spl执行第一和第二阶段结束后,已经初始化运行平台,并根据boot_device选择方式加载了image,

    image主要功能,加载kernel

    接下来就是程序重新返回运行start.o,跳转回到board_init_f()

    简化代码为:

    void board_init_f(ulong bootflag)

    {

    bd_t *bd;

    init_fnc_t **init_fnc_ptr;

    gd_t *id;

    ulong addr, addr_sp;

    gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);

    __asm__ __volatile__("": : :"memory");

    memset((void *)gd, 0, sizeof(gd_t));

    gd->mon_len = _bss_end_ofs;

    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

    if ((*init_fnc_ptr)() != 0) {

    hang ();

    }

    }

    gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;

    addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;

    addr -= (LOGBUFF_RESERVE);

    i = getenv_r("pram", (char *)tmp, sizeof(tmp));

    reg = (i > 0) ? simple_strtoul((const char *)tmp, NULL, 10) :

    CONFIG_PRAM;

    addr -= (reg << 10); /* size is in kB */

    addr -= (4096 * 4);

    addr &= ~(0x10000 - 1);

    gd->tlb_addr = addr;

    /* round down to next 4 kB limit */

    addr &= ~(4096 - 1);

    gd->fb_base = CONFIG_FB_ADDR;

    #else

    addr = lcd_setmem(addr);

    gd->fb_base = addr;

    addr -= gd->mon_len;

    addr &= ~(4096 - 1);

    addr_sp = addr - TOTAL_MALLOC_LEN;

    addr_sp -= sizeof (bd_t);

    bd = (bd_t *) addr_sp;

    gd->bd = bd;

    gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux

    addr_sp -= sizeof (gd_t);

    id = (gd_t *) addr_sp;

    gd->irq_sp = addr_sp;

    addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);

    addr_sp -= 12;

    addr_sp &= ~0x07;

    addr_sp += 128; /* leave 32 words for abort-stack   */

    gd->irq_sp = addr_sp;

    post_bootmode_init();

    post_run(NULL, POST_ROM | post_bootmode_get(0));

    gd->bd->bi_baudrate = gd->baudrate;

    dram_init_banksize();

    display_dram_config();

    gd->relocaddr = addr;

    gd->start_addr_sp = addr_sp;

    gd->reloc_off = addr - _TEXT_BASE;

    memcpy(id, (void *)gd, sizeof(gd_t));

    relocate_code(addr_sp, id, addr);

    }

    uboot.img 运行过程中,有两个非常重要的结构体:gd_t 和 bd_t 。

    其中 gd_t :

    global_data 数据结构的定义

    位于:/arch/arm/include/asm/global_data.h 中。

    其成员主要是一些全局的系统初始化参数。

    其中 bd_t :

    bd_info 数据结构的定义

    位于:/arch/arm/include/asm/u-boot.h 中。

     其成员是开发板的相关参数。

    Image重要结构体头文件        include/image.h

    typedef struct bootm_headers {  //boot头结构

    image_header_t *legacy_hdr_os; /* image header pointer */ 

    image_header_t legacy_hdr_os_copy; /* header copy */ ulong legacy_hdr_valid;

    ……

    }

    typedef struct image_header {  //image 头结构体

    ……

    __be32 ih_size; /* Image Data Size */

     __be32 ih_load; /* Data Load Address

    ……

    }

    1. 启动脚本开始分析image  u-boot2011.09/include/configs/ am335x_evm.h.20160426

    #define CONFIG_BOOTCOMMAND

    "if mmc rescan; then "                           #如果是SD/MMC加载

    "echo SD/MMC found on device ${mmc_dev};"    # - - - @0 

    "if run loadbootenv; then "                   #加载loadbootenv - - - @1

    "echo Loaded environment from ${bootenv};"  #bootenv加载- - - @2

    "run importbootenv;"   #运行  importbootenv

    "fi;"

    "if test -n ${uenvcmd}; then "

    "echo Running uenvcmd ...;"

    "run uenvcmd;"         #运行  uenvcmd

    "fi;"

    "if run mmc_load_image; then "

    "run mmc_args;"       #运行  mmc_args

    "bootm ${kloadaddr};"   #加载kloadaddr - - - - - @3

    "fi;"

    "fi;"

    "run nand_boot;"

    @0

    l "mmc_dev=0"      #给mmc_dev赋值

    "mmc_root=/dev/ram rw "

    "nand_root=ubi0:rootfs rw ubi.mtd=7,2048"

    "spi_root=/dev/mtdblock4 rw"

    @1 @2

    l "bootenv=uEnv.txt"

    "loadbootenv=fatload mmc ${mmc_dev} ${loadaddr} ${bootenv}"

    "importbootenv=echo Importing environment from mmc ...; "

    "env import -t ${loadaddr} ${filesize}"

    "mmc_load_image=fatload mmc ${mmc_dev} ${kloadaddr} ${bootfile};"

          "fatload mmc ${mmc_dev} ${rdloadaddr} ${ramdisk}"

    @3

    #define CONFIG_EXTRA_ENV_SETTINGS

    "bootfile=uImage"

    "ramdisk=ramdisk.gz"

    "loadaddr=0x82000000"

    "kloadaddr=0x80007fc0"

    u-boot,imagekernel 的加载函数

    入口

    1. common/cmd_botm,c

    static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])

    {

    ……

    bootm_start_lmb();

    //获取内核

    os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,&images, &images.os.image_start, &images.os.image_len);

    ……

    if (((images.os.type == IH_TYPE_KERNEL) ||

         (images.os.type == IH_TYPE_MULTI)) &&

        (images.os.os == IH_OS_LINUX)) {            //使用IH_OS_LINUX 

    ret = boot_get_ramdisk (argc, argv, &images, IH_INITRD_ARCH,

    &images.rd_start, &images.rd_end);

    ……

    }

    }

    common/Cmd_bootms中,定义了IH_OS_LINUX ,并赋值为do_bootm_linux  这是最后跳转进入kernel的接口

    static boot_os_fn *boot_os[] = {

    #ifdef CONFIG_BOOTM_LINUX

    [IH_OS_LINUX] = do_bootm_linux,

    ……

    };

    即:从这里跳转进入do_bootm_linux

    2. arch/arm/lib/bootm.c

    代码如下:arch/arm/lib/bootm.c

    int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)

    {

    bd_t *bd = gd->bd;

    char *s;

    int machid = bd->bi_arch_number;

    void (*kernel_entry)(int zero, int arch, uint params);

    #ifdef CONFIG_CMDLINE_TAG

    char *commandline = getenv ("bootargs");

    #endif

    show_boot_progress (15);

    kernel_entry = (void (*)(int, int, uint))images->ep;

    setup_start_tag (bd);

    setup_serial_tag (¶ms);

    announce_and_cleanup();

    kernel_entry(0, machid, bd->bi_boot_params);   //进入内核  不返回

    }

    参考文献:http://blog.chinaunix.net/uid-28458801-id-3486399.html

  • 相关阅读:
    week02 线性表
    week01绪论
    第一周作业
    C语言第二次实验作业
    C语言实验报告
    博客作业06--图
    博客作业05--查找
    博客作业04--树
    博客作业03--栈和队列
    博客作业2---线性表
  • 原文地址:https://www.cnblogs.com/ChenChangXiong/p/10779685.html
Copyright © 2020-2023  润新知