u-boot启动代码分析
u-boot版本为u-boot-2009.08,平台smdk2410 ? 一、第一阶段启动代码分析 在学习ARM时就知道,ARM在上电复位时将PC指针修改为0,即ARM是从0地址开始读取指令执行的。在cpu/arm920t/目录下有个u-boot.lds链接脚本,首先看代码段定义: . =0x00000000; ? . =ALIGN(4); .text : { ?????? cpu/arm920t/start.o?????? (.text) ?????? *(.text) } 我们看start.o就是放在0地址处的,所以它就是我们要找的启动代码,再看程序的入口点在哪: ENTRY(_start) 程序入口点是由ENTRY伪指令指定的,所以程序的入口点就是_start,最终我们找到了程序的入口点,cpu/arm920t/start.S中的_start。 1.异常向量表定义 .globl_start _start:???? b??????start_code ?????? ldr?? pc,_undefined_instruction ?????? ldr?? pc,_software_interrupt ?????? ldr?? pc,_prefetch_abort ?????? ldr?? pc,_data_abort ?????? ldr?? pc,_not_used ?????? ldr?? pc,_irq ?????? ldr?? pc,_fiq ? _undefined_instruction:? .word undefined_instruction _software_interrupt:????? .word software_interrupt _prefetch_abort:???? .word prefetch_abort _data_abort:?????????? .word data_abort _not_used:???????????? .word not_used _irq:?????????????? .word irq _fiq:?????????????? .word fiq 上电复位后,一条跳转指令跳转到start_code ? 2.设置ARM工作模式 start_code: ?????? /* ?????? ?*set the cpu to SVC32 mode ?????? ?*/ ?????? mrs? r0,cpsr ?????? bic?? r0,r0,#0x1f ?????? orr?? r0,#0xd3 ?????? msr? cpsr,r0 设置ARM工作模式为管理模式,并禁止所有中断。 ? 3.关闭看门狗 ?????? ldr????????? r0,=pWTCON ?????? mov ? ? ??r1,#0x0 ?????? str????????? r1,[r0] ? 4.屏蔽所有中断 ?????? /* ?????? ?*mask all IRQs by setting all bits in the INTMR - default ?????? ?*/ ?????? mov ? ? ? r1,#0xffffffff ?????? ldr????????? r0,=INTMSK ?????? str????????? r1,[r0] # if defined(CONFIG_S3C2410) ?????? ldr?? r1,=0x3ff ?????? ldr?? r0,=INTSUBMSK ?????? str?? r1,[r0] # endif ? 5.设置ARM时钟频率分频比 ?????? /* FCLK:HCLK:PCLK = 1:2:4 */ ?????? /* default FCLK is 120 MHz ! */ ?????? ldr????????? r0,=CLKDIVN ?????? mov ? ? ? r1,#3 ?????? str????????? r1,[r0] ? 6.清除cache和禁止MMU bl???? cpu_init_crit 一条跳转指令跳转到cpu_init_crit cpu_init_crit: ?????? /* ?????? ?*flush v4 I/D caches ?????? ?*/ ?????? mov r0,#0 ?????? mcr p15,c7,0????? /* flush v3/v4 cache*/ ?????? mcr p15,c8,0????? /* flush v4 TLB */ ? ?????? /* ?????? ?*disable MMU stuff and caches ?????? ?*/ ?????? mrc p15,c1,c0,0 ?????? bic?? r0,#0x00002300????? @ clear bits 13,9:8(--V- --RS) ?????? bic?? r0,#0x00000087????? @ clear bits 7,2:0(B--- -CAM) ?????? orr?? r0,#0x00000002????? @ set bit 2 (A) Align ?????? orr?? r0,#0x00001000????? @ set bit 12 (I)I-Cache ?????? mcr p15,0 ? 7.初始化ARM存储控制器 bl???? lowlevel_init 跳转到lowlevel_init,这个符号定义在board/samsung/smdk2410/lowlevel_init.S中 ? 8.拷贝代码到SDRAM中 #ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate:?????????????????????? /* relocate U-Boot to RAM??? ??? */ ?????? adr?? r0,_start??????? /* r0 <- current positionof code?? */ ?????? ldr ? ?r1,_TEXT_BASE???????? /* test if we run fromflash or RAM */ ?????? cmp????r0,r1????????????????? /* don'treloc during debug???????? */ ?????? beq????stack_setup ? ?????? ldr ? ?r2,_armboot_start ?????? ldr ? ?r3,_bss_start ?????? sub? r2,r3,r2??????? /* r2 <- size ofarmboot??????????? */ ?????? add? r2,r2??????? /* r2 <- source endaddress???????? */ ? copy_loop: ?????? ldmia?????? r0!,{r3-r10}???????? /* copy from sourceaddress [r0]??? */ ?????? stmia?????? r1!,{r3-r10}???????? /* copy to?? target address [r1]??? */ ?????? cmp r0,r2??????????????????? /* until source endaddreee [r2]??? */ ?????? ble?? copy_loop #endif???? /* CONFIG_SKIP_RELOCATE_UBOOT */ 先是比较_start和_TEXT_BASE这两个符号的地址是否相等,_TEXT_BASE这个符号取值为TEXT_BASE,它的值为0x33F80000。其实就是判断u-boot是否已经在SDRAM中,如果u-boot已经在SDRAM中,那么也就不必拷贝了,直接跳到堆栈设置。如果u-boot没有在SDRAM中,那么就将u-boot拷贝到SDRAM中,CPU是可以从nor flash中取指执行的,但是在SDRAM中执行速度更快,所以将代码拷贝到SDRAM中。 u-boot代码段起始地址是放在寄存器r0中的,然后计算代码段的结束地址,结束地址放在寄存器r2中,然后使用多寄存器加载指令将代码复制到SDRAM中,代码放在SDRAM中的什么地方的呢,就是放在TEXT_BASE这个地方的,也就是0x33F80000这个地方。 ? 9.设置栈 ?????? /* Set up the stack???????????????????????????????????? ??? */ stack_setup: ?????? ldr?? r0,_TEXT_BASE???????? /* upper 128 KiB:relocated uboot?? */ ?????? sub? r0,#CONFIG_SYS_MALLOC_LEN????? /* mallocarea????????????????????? */ ?????? sub? r0,#CONFIG_SYS_GBL_DATA_SIZE /* bdinfo??????????????????????? */ #ifdef CONFIG_USE_IRQ ?????? sub? r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif ?????? sub? sp,#12???????????? /* leave 3 words forabort-stack??? */ 0x33F80000是代码段的起始地址,将这个地址减去堆空间大小192K,其中堆空间包括64K的环境变量空间。再减去全局数据区的128字节,将栈指针指向这里。其实就是将栈指针指向一段空闲的内存区。 ? 10.bss段清零 clear_bss: ?????? ldr?? r0,_bss_start???????? /* find start of bsssegment??????? */ ?????? ldr?? r1,_bss_end????????? /* stop here??????????????????????? */ ?????? mov r2,#0x00000000?????????? /* clear??????????????????????????? */ ? clbss_l:str?????? r2,[r0]?????????? /*clear loop...??????????????????? */ ?????? add? r0,#4 ?????? cmp r0,r1 ?????? ble?? clbss_l ? 11.跳转到第二阶段执行 ldr?? pc,_start_armboot ? 二、第二阶段启动代码分析 第二阶段启动代码是从lib_arm/board.c的start_armboot函数开始的 ?????? /* Pointer is writable since we allocateda register for it */ ?????? gd = (gd_t*)(_armboot_start -CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)); ?????? /* compiler optimization barrier neededfor GCC >= 3.4 */ ?????? __asm__ __volatile__("": ::"memory"); ? ?????? memset ((void*)gd,sizeof (gd_t)); ?????? gd->bd = (bd_t*)((char*)gd -sizeof(bd_t)); ?????? memset (gd->bd,sizeof (bd_t)); 首先将全局数据指针指向全局数据区,清零这个内存区,全局数据区后为板级数据区,将板级数据指针指向这里。 SDRAM空间分配图如下: ?????? for (init_fnc_ptr = init_sequence;*init_fnc_ptr; ++init_fnc_ptr) { ????????????? if ((*init_fnc_ptr)() != 0) { ???????????????????? hang (); ????????????? } ?????? } 然后一个for循环执行init_sequence函数指针数组中的函数。 1.板级初始化 int board_init (void) { ?????? S3C24X0_CLOCK_POWER * const clk_power =S3C24X0_GetBase_CLOCK_POWER(); ?????? S3C24X0_GPIO * const gpio =S3C24X0_GetBase_GPIO(); ? ?????? /* to reduce PLL lock time,adjust theLOCKTIME register */ ?????? clk_power->LOCKTIME = 0xFFFFFF; ? ?????? /*设置FCLK和UCLK时钟频率*/ ?????? /* 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 = 0x00044555; ?????? 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 = 0xFF95FFBA; ?????? gpio->GPGUP = 0x0000FFFF; ?????? gpio->GPHCON = 0x002AFAAA; ?????? gpio->GPHUP = 0x000007FF; ? ?????? /*设置板子机器码,要和Linux中机器码对应*/ ?????? /* 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; } ? 2.时钟初始化 int timer_init (void) { ?????? S3C24X0_TIMERS * const timers =S3C24X0_GetBase_TIMERS(); ? ?????? /* use PWM Timer 4 because it has nooutput */ ?????? /* prescaler for Timer 4 is 16 */ ?????? timers->TCFG0 = 0x0f00; ?????? if (timer_load_val == 0) ?????? { ????????????? /* ????????????? ?* for 10 ms clock period @ PCLK with 4 bitdivider = 1/2 ????????????? ?* (default) and prescaler = 16. Should be10390 ????????????? ?* @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); } 这个函数做时钟的初始化,在移植到S3C2440的时候要注意,S3C2440和S3C2410的时钟频率设置是不一样的,所以这个地方要针对S3C2440做特别的修改。 ? 3.第三个函数是env_init,环境变量初始化,这要看我们是将环境变量放在什么地方了,如果是放在nor flash中,那么执行的是common/env_flash.c中的函数,如果是放在nand flash中,那么执行的是common/env_nand.c中的函数。我们只看common/env_flash.c中的env_init函数。 int? env_init(void) { ?????? if (crc32(0,env_ptr->data,ENV_SIZE)== env_ptr->crc) { ????????????? gd->env_addr? = (ulong)&(env_ptr->data); ????????????? gd->env_valid = 1; ????????????? return(0); ?????? } ? ?????? gd->env_addr? = (ulong)&default_environment[0]; ?????? gd->env_valid = 0; ?????? return (0); } 首先是判断flash是否存在环境变量,如果flash存在环境变量,那就将全局数据结构中环境变量起始地址设置为include/configs/smdk2410.h由宏CONFIG_ENV_ADDR定义的值。如果flash不存在环境变量,那就将这个地址设置为default_environment数组地址值,表示采用默认环境变量。 ? 4.串口波特率初始化 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); } 首先是从环境变量中去获取,如果环境变量中没有定义,就使用配置文件中include/configs/smdk2410.h中定义的波特率值。 ? 5.串口初始化 /*Initialise the serial port. The settings are always 8 data bits,no parity, ?* 1 stop bit,no start bits. ?*/ static int serial_init_dev(const int dev_index) { ?????? S3C24X0_UART * const uart =S3C24X0_GetBase_UART(dev_index); ? ?????? /* 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; ? #ifdef CONFIG_HWFLOW ?????? uart->UMCON = 0x1; /* RTS up */ #endif ? ?????? /* FIXME: This is sooooooooooooooooooougly */ #if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2) ?????? /* we need auto hw flow control on thegsm and gps port */ ?????? if (dev_index == 0 || dev_index == 1) ????????????? uart->UMCON = 0x10; #endif ?????? _serial_setbrg(dev_index); ? ?????? return (0); } ? #if !defined(CONFIG_SERIAL_MULTI) /*Initialise the serial port. The settings are always 8 data bits,no start bits. ?*/ int serial_init (void) { ?????? return serial_init_dev(UART_NR); } #endif 6.第一阶段控制台初始化 /* Calledbefore relocation - use serial functions */ 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; } ? 7.显示u-boot版本信息 static int display_banner (void) { ?????? printf ("nn%snn",version_string); ?????? debug ("U-Boot code: %08lX ->%08lX? BSS: -> %08lXn", ?????? ??????_armboot_start,_bss_start,_bss_end); #ifdef CONFIG_MODEM_SUPPORT ?????? debug ("Modem Supportenabledn"); #endif #ifdef CONFIG_USE_IRQ ?????? debug ("IRQ Stack: %08lxn",IRQ_STACK_START); ?????? debug ("FIQ Stack: %08lxn",FIQ_STACK_START); #endif ? ?????? return (0); } 在u-boot启动起来后,显示的第一条信息 ? 8.内存初始化 int dram_init (void) { ?????? /*SDRAM起始地址和SDRAM大小*/ ?????? gd->bd->bi_dram[0].start =PHYS_SDRAM_1; ?????? gd->bd->bi_dram[0].size =PHYS_SDRAM_1_SIZE; ? ?????? return 0; } ? 9.显示SDRAM相关信息 static int display_dram_config (void) { ?????? int i; ? #ifdef DEBUG ?????? puts ("RAM Configuration:n"); ? ?????? for(i=0; i<CONFIG_NR_DRAM_BANKS; i++){ ????????????? printf ("Bank #%d: %08lx",i,gd->bd->bi_dram[i].start); ????????????? print_size(gd->bd->bi_dram[i].size,"n"); ?????? } #else ?????? ulong size = 0; ? ?????? for (i=0; i<CONFIG_NR_DRAM_BANKS; i++){ ????????????? size +=gd->bd->bi_dram[i].size; ?????? } ?????? puts("DRAM:? "); ?????? print_size(size,"n"); #endif ? ?????? return (0); } ? init_sequence数组中的函数也就执行完了。 ?????? /* armboot_start is defined in theboard-specific linker script */ ?????? mem_malloc_init (_armboot_start -CONFIG_SYS_MALLOC_LEN); 接下来是将堆的内存空间清零。 ? 10.显示flash相关信息 static void display_flash_config (ulong size) { ?????? puts ("Flash: "); ?????? print_size (size,"n"); } ? 11.nand flash初始化 void nand_init(void) { ?????? int i; ?????? unsigned int size = 0; ?????? for (i = 0; i <CONFIG_SYS_MAX_NAND_DEVICE; i++) { ????????????? nand_init_chip(&nand_info[i],&nand_chip[i],base_address[i]); ????????????? size += nand_info[i].size / 1024; ????????????? if (nand_curr_device == -1) ???????????????????? nand_curr_device = i; ?????? } ?????? printf("%u MiBn",size /1024); ? #ifdef CONFIG_SYS_NAND_SELECT_DEVICE ?????? /* ?????? ?*Select the chip in the board/cpu specific driver ?????? ?*/ ?????? board_nand_select_device(nand_info[nand_curr_device].priv,nand_curr_device); #endif } ? 12.加载环境变量 void env_relocate (void) { ?????? DEBUGF ("%s[%d] offset = 0x%lxn",__FUNCTION__,__LINE__, ????????????? gd->reloc_off); ? #ifdef CONFIG_AMIGAONEG3SE ?????? enable_nvram(); #endif ? #ifdef ENV_IS_EMBEDDED ?????? /* ?????? ?*The environment buffer is embedded with the text segment, ?????? ?*just relocate the environment pointer ?????? ?*/ ?????? env_ptr = (env_t *)((ulong)env_ptr +gd->reloc_off); ?????? DEBUGF ("%s[%d] embedded ENV at%pn",env_ptr); #else ?????? /*在堆上为环境变量申请内存空间*/ ?????? /* ?????? ?*We must allocate a buffer for the environment ?????? ?*/ ?????? env_ptr = (env_t *)malloc(CONFIG_ENV_SIZE); ?????? DEBUGF ("%s[%d] malloced ENV at%pn",env_ptr); #endif ? ?????? /*gd->env_valid== 0表示flash没有环境变量,从而采用默认的环境变量*/ ?????? if (gd->env_valid == 0) { #if defined(CONFIG_GTH)?? || defined(CONFIG_ENV_IS_NOWHERE)?? /*Environment not changable */ ????????????? puts ("Using defaultenvironmentnn"); #else ????????????? puts ("*** Warning - bad CRC,using default environmentnn"); ????????????? show_boot_progress (-60); #endif ????????????? /*拷贝默认的环境变量到相关内存中*/ ????????????? set_default_env(); ?????? } ?????? else { ????????????? /*将环境变量拷贝到内存中*/ ????????????? env_relocate_spec (); ?????? } ?????? /*将全局数据结构中环境变量指针指向环境变量内存空间处*/ ?????? gd->env_addr =(ulong)&(env_ptr->data); ? #ifdef CONFIG_AMIGAONEG3SE ?????? disable_nvram(); #endif } 如果flash中有环境变量,则将flash中环境变量拷贝到SDRAM中环境变量空间中。但是如果flash没有环境变量,就采用默认的环境变量,也要将默认的环境变量拷贝到环境变量空间中,所以说在新烧写的u-boot启动的时候,会显示一条警告信息,表示采用默认的环境变量。 ? 13.以太网的初始化 ?????? eth_initialize(gd->bd); ? 14.最后死循环,处理用户命令 ?????? /* main_loop() can return to retryautoboot,if so just run it again. */ ?????? for (;;) { ????????????? main_loop (); ?????? } 第二阶段启动代码主要是外围设备的初始化,比如nor flash、nand flash、串口和以太网等等。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |