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 主要调用 board\aplex\ecm_5206\evm.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: board\aplex\ecm_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()\n");

//重新指定

    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\0" \      #给mmc_dev赋值

"mmc_root=/dev/ram rw \0" \

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

"spi_root=/dev/mtdblock4 rw\0" \

@1 @2

 

l "bootenv=uEnv.txt\0" \

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

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

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

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

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

@3

#define CONFIG_EXTRA_ENV_SETTINGS \

"bootfile=uImage\0" \

"ramdisk=ramdisk.gz\0" \

"loadaddr=0x82000000\0" \

"kloadaddr=0x80007fc0\0" \

 

 

 

 

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

上一篇:Flutter for Android——app如何调用第三方应用打开文件


下一篇:SpringCloud微服务实战——搭建企业级开发框架(二十八):扩展MybatisPlus插件DataPermissionInterceptor实现数据权限控制