uboot 分析之 启动流程
生活随笔
收集整理的這篇文章主要介紹了
uboot 分析之 启动流程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
uboot的啟動流程:
看一幅圖:
1.第一階段:start.s的內容:
#include <config.h> @該文件是第二步中mkconfig文件執行時創建的。include/config.h #include <version.h> #include <status_led.h> @在include目錄下。 /* ************************************************************************* * * Jump vector table as in table 3.1 in [1] * ************************************************************************* */ @這是一個異常跳轉表, .globl _start _start:? b start_code? @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 @在標號_undefined_instruction處存儲了一個變量,該變量也是一個標號地址 _software_interrupt: .word software_interrupt @執行上面的ldr pc, _undefined_instruction;指令會將變量undefined_instruction的值裝入PC _prefetch_abort: .word prefetch_abort @pc指向一個地址,CPU從該地址中取指執行 _data_abort: .word data_abort _not_used: .word not_used _irq: .word irq _fiq: .word fiq .balignl 16,0xdeadbeef /* ************************************************************************* * * 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 * ************************************************************************* */ _TEXT_BASE: .word?TEXT_BASE @變量TEXT_BASE由連接腳本指定,
.globl _armboot_start
_armboot_start: .word?_start @_start是在起始代碼處定義的標號,當跳到_armboot_start后還是會跳轉到_start /* * These are defined in the board-specific linker script. */ .globl _bss_start
_bss_start: .word?__bss_start? @這是board/mini2440/u-boot.lds腳本中的變量,
.globl _bss_end _bss_end: .word?_end? @這也是board/mini2440/u-boot.lds腳本中的變量,
#ifdef CONFIG_USE_IRQ? @這個宏在include/configs/$(board_name).h中定義。可以取消 /* IRQ stack memory (calculated at run-time) */ .globl IRQ_STACK_START IRQ_STACK_START: .word 0x0badc0de /* IRQ stack memory (calculated at run-time) */ .globl FIQ_STACK_START FIQ_STACK_START: .word 0x0badc0de #endif /* * the actual start code */
start_code:? @這是真正的啟動代碼 /*?set the cpu to SVC32 mode*/ mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0 bl coloured_LED_init @這個函數在status_led.h中被提升了作用域,但是在哪里面實現??status_led.h文件 bl red_LED_on @同上 @下面這些宏開關也是在include/configs/$(board_name).h中定義?? #if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) || defined(CONFIG_AT91RM9200DF)
/* * relocate exception table */ ldr r0, =_start ldr r1, =0x0 mov r2, #16
copyex: subs r2, r2, #1 ldr r3, [r0], #4 str r3, [r1], #4 bne copyex #endif
@下面這些宏開關是在include/configs/$(board_name).h中定義 #if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)||defined(CONFIG_S3C2440) /* turn off the watchdog */ # if defined(CONFIG_S3C2400) # define pWTCON 0x15300000 # define INTMSK 0x14400008 /* Interupt-Controller base addresses */ # define CLKDIVN 0x14800014 /* clock divisor register */ #else # define pWTCON 0x53000000 # define INTMSK 0x4A000008 /* Interupt-Controller base addresses */ # define INTSUBMSK 0x4A00001C # define CLKDIVN 0x4C000014 /* clock divisor register */ # endif #define CLK_CTL_BASE 0x4c000000 #define MDIV_405 0x7f<<12 #define PSDIV_405 0x21 #define UPLL_MDIV_48 0x38<<12 #define UPLL_PSDIV_48 0x22 #define MDIV_200 0xa1<<12 #define PSDIV_200 0x31 ldr r0, =pWTCON mov r1, #0x0 str r1, [r0]
/* * 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
/*add by gray*/ #if defined(CONFIG_S3C2440) ldr r1, =0x7fff ldr r0, =INTSUBMSK str r1, [r0] #endif
/*add by gray,這里是修改后的,其實可以在uboot啟動的第二階段start_armboot()函數里調用board_init()函數時重置CPU鐘 對于S3C2440,MPLL(clk)=(2 * m * Fin) / p * 2^s, m = MDIV + 8, p = PDIV + 2, s =?SDIV. 這里MDIV =? 0x7f, PDIV = 0x2 ,SDIV = 0x1? */ #if defined(CONFIG_S3C2440) /*FCLK:HCLK:PCLK=1:2:4*/ ldr r0,?=CLKDIVN mov r1, #5 str r1,[r0] mrc p15,0,r1,c1,c0,0 /*read ctrl reg*/ orr r1,r1,#0xc0000000 /*ASYN*/ mcr p15,0,r1,c1,c0,0 /*write ctrl reg*/ mov r1,#CLK_CTL_BASE mov r2,#UPLL_MDIV_48 add r2, r2,#UPLL_PSDIV_48 str r2,[r1,#0x08] /*write UPLL first,48MHZ*/ mov r2,#MDIV_405 add r2,r2,#PSDIV_405? /*mpll_405MHZ*/ str r2,[r1,#0x04]? /*MPLLCON*/ #else /*F:H:P=1:2:4*/ ldr r0, =CLKDIVN mov r1, #3 str r1,[r0] mrc p15,0,r1,c1,c0,0? /*read ctrl reg*/ orr r1,r1,#0xc0000000 mcr p15,0,r1,c1,c0,0 mov r1,#CLK_CTL_BASE mov r2,#MDIV_200 add r2,r2,#PSDIV_200? /*mpll_200MHZ*/ str r2,[r1,#0x04]? /*MPLLCON*/ #endif #endif /*CONFIG_S3C2400 || CONFIG_S3C2410 || CONFIG_S3C2440*/
/* * we do sys-critical inits only at reboot, * not when booting from ram! */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit @如果沒有定義CONFIG_SKIP_LOWLEVEL_INIT,就進行嚴格的初始化,調用lowlevel_init函數 #endif #ifndef CONFIG_AT91RM9200 @AT91RM9200評估板,基于ARM920T內核 #ifndef CONFIG_SKIP_RELOCATE_UBOOT
/* relocate U-Boot to RAM */ relocate:? adr r0, _start ? /* r0 <- current position of code */ ldr r1, _TEXT_BASE? /* test if we run from flash or RAM */ cmp r0, r1? /* don't reloc during debug */ beq stack_setup ? /*如果_start與_TEXT_BASE相等,就直接跳去stack_setup進行棧的設置, 否則就要進行代碼的拷貝,把代碼拷貝到內存中*/
ldr r2, _armboot_start ldr r3, _bss_start? @計算代碼段大小=_bss_start-_armboot_start sub r2, r3, r2? /* r2 <- size of armboot */ add r2, r0, r2? /* r2 <- source end address */ @ r2 = _start的地址+代碼段大小 copy_loop: ldmia r0!, {r3-r10}? /* copy from source address [r0] */ stmia r1!, {r3-r10}? /* copy to target address [r1] */ cmp r0, r2? /* until source end addreee [r2] */ ble copy_loop #endif? /* CONFIG_SKIP_RELOCATE_UBOOT */ #endif /* Set up the stack,設置棧 */ stack_setup: ldr r0,?_TEXT_BASE?/*?upper 128 KiB: relocated uboot?*/ sub r0, r0, #CFG_MALLOC_LEN? /* malloc area,這是堆區 */ sub r0, r0, #CFG_GBL_DATA_SIZE? /* bdinfo */ #ifdef CONFIG_USE_IRQ @如果定義了IRQ,就要為IRQ,FIQ分配中斷棧。 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp, r0, #12? /* leave 3 words for abort-stack */ clear_bss:? @清除bss段,_bss_start 和_bss_end就是u-boot.lds腳本傳入的變量 ldr r0,?_bss_start? /* find start of bss segment */ ldr r1,?_bss_end? /* stop here */ mov r2, #0x00000000? /* clear */ clbss_l:str r2, [r0]? /* clear loop... */ add r0, r0, #4 cmp r0, r1 ble clbss_l
ldr pc, _start_armboot _start_armboot: .word?start_armboot?@跳到start_armboot()函數執行,進入uboot的第二階段 /* ************************************************************************* * * CPU_init_critical registers * * setup important registers * setup memory timing * ************************************************************************* */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT cpu_init_crit:? @進行嚴格的初始化,關閉I/D caches,MMU,
/* * flush v4 I/D caches */ 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 */ 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 2 (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 #if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) || defined(CONFIG_AT91RM9200DF) #else
bl lowlevel_init? @這個函數是重點。。在lowlevel_init.S中 #endif
mov lr, ip mov pc, lr #endif? /* CONFIG_SKIP_LOWLEVEL_INIT */ lowlevel_init.S的內容: _TEXT_BASE: .word TEXT_BASE .globl lowlevel_init
lowlevel_init:? @很明顯,是初始化SDRAM /* memory control configuration */ /* make r0 relative the current location so that it */ /* reads SMRDATA out of FLASH rather than memory ! */ ldr r0, =SMRDATA ldr r1, _TEXT_BASE sub r0, r0, r1 ldr r1, =BWSCON? /* Bus Width Status Controller */ add r2, r0, #13*4
0: ldr r3, [r0], #4 str r3, [r1], #4 cmp r2, r0 bne 0b
/* everything is fine now */ mov pc, lr .ltorg
2.進入uboot啟動的第二階段:start_armboot()
start_armboot()在board.c中,處于lib_arm/下,在進入lib_arm目錄下編譯LIBS目標時依賴board.o文件
void start_armboot?(void)
{
?init_fnc_t?**init_fnc_ptr;
?char?*s;
#if?!defined(CFG_NO_FLASH)?||?defined?(CONFIG_VFD)?||?defined(CONFIG_LCD)
?ulong size;
#endif
#if?defined(CONFIG_VFD)?||?defined(CONFIG_LCD)
?unsigned long addr;
#endif
?//gd是個寄存器變量,ARM平臺對應R8寄存器
?/*?Pointer?is?writable since we allocated a register?for?it
??*_armboot_start是代碼的起始地址,鏈接腳本指定為0x33f80000,CFG_MALLOC_LEN=192k,
??*所以gd的起始地址=0x33F50000
??*/
?gd?=?(gd_t*)(_armboot_start?-?CFG_MALLOC_LEN?-?sizeof(gd_t));
?/*?compiler optimization barrier needed?for?GCC?>=?3.4?*/
?__asm__ __volatile__("":?:?:"memory");
?memset?((void*)gd,?0,?sizeof?(gd_t));//清空gd_t結構體
?gd->bd?=?(bd_t*)((char*)gd?-?sizeof(bd_t));//bd_t結構體
?memset?(gd->bd,?0,?sizeof?(bd_t));?
?gd->flags?|=?GD_FLG_RELOC;
?monitor_flash_len?=?_bss_start?-?_armboot_start;//uboot代碼的長度
?//對結構體init_fnc_t?*init_sequence[]數組中的函數指針挨個調用。
?for?(init_fnc_ptr?=?init_sequence;?*init_fnc_ptr;?++init_fnc_ptr)?{
??if?((*init_fnc_ptr)()?!=?0)?{
???hang?();
??}
?}
?/*?初始化flash*/
?size?=?flash_init?();
?。。。。。。
?/*?初始化一個堆空間?*/
?mem_malloc_init?(_armboot_start?-?CFG_MALLOC_LEN);
?nand_init();?/*?go init the NAND?*/
?/*?初始化環境變量,將環境參數讀入內存指定位置?*/
?env_relocate?();
?/*?must?do?this after the framebuffer?is?allocated,串口初始化?*/
?serial_initialize();
?/*?IP Address?*/
?gd->bd->bi_ip_addr?=?getenv_IPaddr?("ipaddr");
?/*?MAC Address,省略?*/
?devices_init?();?/*?get?the devices list going.?*/
?jumptable_init?();?/*?跳轉表初始化?*/
?console_init_r?();?/*?fully init console as a device?*/
?/*?使能異常中斷?*/
?enable_interrupts?();
?/*?Perform network card initialisation?if?necessary?*/
#ifdef CONFIG_DRIVER_CS8900
?cs8900_get_enetaddr?(gd->bd->bi_enetaddr);
#endif
?/*?main_loop()?can return?to?retry autoboot,?if?so just run it again.?*/
?for?(;;)?{
??main_loop?();
?}
?/*?NOTREACHED?-?no way out of command?loop?except booting?*/
}
??init_fnc_t?*init_sequence[]?=?{
?????????cpu_init,?/*?基本的處理器相關配置,初始化IRQ/FIQ模式的棧?--?cpu/arm920t/cpu.c?*/
?????????board_init,?/*?基本的板級相關配置,設置系統時鐘等?--?board/smdk2410/smdk2410.c?*/
?????????interrupt_init,?/*?初始化定時器處理?--?cpu/arm920t/s3c24x0/interrupt.c?*/
?????????env_init,?/*?初始化環境變量,檢查Flash上的環境參數是否有效?--?common/env_flash.c?*/
?????????init_baudrate,?/*?初始化波特率設置?--?lib_arm/board.c?*/
?????????serial_init,?/*?串口通訊設置?--?cpu/arm920t/s3c24x0/serial.c?*/
?????????console_init_f,?/*?控制臺初始化階段1?--?common/console.c?*/
?????????display_banner,?/*?打印u-boot信息?--?lib_arm/board.c?*/
?????????dram_init,?/*?配置可用的RAM,檢測系統內存映射?--?board/smdk2410/smdk2410.c?*/
?????????display_dram_config,?/*?顯示RAM的配置大小?--?lib_arm/board.c?*/
?????????NULL,
??};
??下面逐個分析初始化函數:
1.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;
}
2.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 the LOCKTIME register?*/
?clk_power->LOCKTIME?=?0xFFFFFF;
?/*?configure MPLL?*/
?clk_power->MPLLCON?=?((M_MDIV?<<?12)?+?(M_PDIV?<<?4)?+?M_SDIV);
?......
?/*?configure UPLL?*/
?clk_power->UPLLCON?=?((U_M_MDIV?<<?12)?+?(U_M_PDIV?<<?4)?+?U_M_SDIV);
?......
?/*?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;
?/*?arch number of SMDK2410-Board?*/
?gd->bd->bi_arch_number?=?MACH_TYPE_SMDK2410;
?/*?address of boot parameters?*/
?gd->bd->bi_boot_params?=?0x30000100;//為什么是這個地址
?icache_enable();//下面這兩個函數都是設置協處理器的相關寄存器,使能緩存
?dcache_enable();
}
3.int?interrupt_init?(void) //初始化2410的PWM timer 4,使其能自動裝載計數值,
??? //恒定的產生時間中斷信號,但是中斷被屏蔽了用不上。
{
?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);
}
4.int?env_init(void)
{
?int?crc1_ok?=?0,?crc2_ok?=?0;
?uchar flag1?=?flash_addr->flags;
?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);
?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;
?}
?return?(0);
}
PS:
uboot的重要的數據結構
1)gd 全局數據變量指針,它保存了u-boot運行需要的全局數據,類型定義:
? typedef struct global_data {
?????????? bd_t? *bd;????? //board data pointor板子數據指針
?????????? unsigned long flags; ?//指示標志,如設備已經初始化標志等。
?????????? unsigned long baudrate; //串口波特率
?????????? unsigned long have_console; /* 串口初始化標志*/
?????????? unsigned long reloc_off;?? /* 重定位偏移,就是實際定向的位置與編譯連接時指定的位置之差,一般為0 */
?????????? unsigned long env_addr; /* 環境參數地址*/
?????????? unsigned long env_valid; /* 環境參數CRC檢驗有效標志 */
?????????? unsigned long fb_base; /* base address of frame buffer */
????????? #ifdef CONFIG_VFD
?????????? unsigned char vfd_type; /* display type */
????????? #endif
?????????? void? **jt;? /* 跳轉表,1.1.6中用來函數調用地址登記 */
????????? } gd_t;
2)bd 板子數據指針。板子很多重要的參數。 類型定義如下:????
?? typedef struct bd_info {
???????????? int?? bi_baudrate;???? /* 串口波特率 */
???????????? unsigned long bi_ip_addr;?? /* IP 地址 */
???????????? unsigned char bi_enetaddr[6]; /* MAC地址*/
???????????? struct environment_s??????? *bi_env;
???????????? ulong???????? bi_arch_number; /* unique id for this board */
???????????? ulong???????? bi_boot_params; /* 啟動參數 */
???????????? struct??? ???/* RAM 配置 */
???????????? {
??????????? ulong start;
??????????? ulong size;
???????????? }bi_dram[CONFIG_NR_DRAM_BANKS];
???????? } bd_t;?
3)環境變量指針 env_t *env_ptr = (env_t *)(&environment[0]);(common/env_flash.c)
? env_ptr指向環境參數區,系統啟動時默認的環境參數environment[],定義在common/environment.c中。
? 參數解釋:
??? bootdelay 定義執行自動啟動的等候秒數?
??? baudrate 定義串口控制臺的波特率?
??? netmask 定義以太網接口的掩碼?
??? ethaddr 定義以太網接口的MAC地址?
??? bootfile 定義缺省的下載文件?
??? bootargs 定義傳遞給Linux內核的命令行參數?
??? bootcmd 定義自動啟動時執行的幾條命令?
??? serverip 定義tftp服務器端的IP地址?
??? ipaddr 定義本地的IP地址?
??? stdin 定義標準輸入設備,一般是串口?
??? stdout 定義標準輸出設備,一般是串口?
??? stderr 定義標準出錯信息輸出設備,一般是串口?
? 4)設備相關:
?? 標準IO設備數組?evice_t *stdio_devices[] = { NULL, NULL, NULL };
?? 設備列表 list_t??? devlist = 0;
?? device_t的定義:include\devices.h中:
??? typedef struct {
???? int flags;??? /* Device flags: input/output/system */
???? int ext;????? /* Supported extensions?? */
???? char name[16];?? /* Device name??? */????
??? /* GENERAL functions */????
???? int (*start) (void);? /* To start the device?? */
???? int (*stop) (void);? /* To stop the device?? */????
??? /* 輸出函數 */????
???? void (*putc) (const char c); /* To put a char?? */
???? void (*puts) (const char *s); /* To put a string (accelerator) */???
??? /* 輸入函數 */???
???? int (*tstc) (void);? /* To test if a char is ready... */
???? int (*getc) (void);? /* To get that char?? */???
??? /* Other functions */????
???? void *priv;?? /* Private extensions?? */
??? } device_t;
? u-boot把可以用為控制臺輸入輸出的設備添加到設備列表devlist,并把當前用作標準IO的設備指針加入stdio_devices數組中。
? 在調用標準IO函數如printf()時將調用stdio_devices數組對應設備的IO函數如putc()。
5)命令相關的數據結構,后面介紹。
6)與具體設備有關的數據結構,
???? 如flash_info_t flash_info[CFG_MAX_FLASH_BANKS];記錄nor flash的信息。
???? nand_info_t nand_info[CFG_MAX_NAND_DEVICE]; nand flash塊設備信息
?????
?????
5.static int init_baudrate (void)
{
?char tmp[64];?/* long enough for environment variables */
?int i = getenv_r ("baudrate", tmp, sizeof (tmp));//環境變量存于tmp緩沖中
?gd->bd->bi_baudrate = gd->baudrate = (i > 0)
???? (int) simple_strtoul (tmp, NULL, 10)
???: CONFIG_BAUDRATE; return (0);
}
6.int serial_init()實際調用下面這個函數,在移植時這個函數是第一個要修改的,因為要用到串口輸出信息
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;?//這里使用的是串口0.
?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 sooooooooooooooooooo ugly */
#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)
?/* we need auto hw flow control on the gsm and gps port */
?if (dev_index == 0 || dev_index == 1)
??uart->UMCON = 0x10;
#endif
?_serial_setbrg(dev_index); return (0);
}
7.由于標準設備還沒有初始化(gd->flags & GD_FLG_DEVINIT=0),這時控制臺使用串口作為控制臺
??? 函數只有一句:gd->have_console = 1;
int console_init_f (void)
{
?gd->have_console = 1;?//將標志位置1 #ifdef CONFIG_SILENT_CONSOLE
?if (getenv("silent") != NULL)
??gd->flags |= GD_FLG_SILENT;
#endif return (0);
}
8.int dram_init (void)
{//PHYS_SDRAM_1在整個include/configs.h文件中有很多定義,只在一個地方define為0x30000000
?//PHYS_SDRAM_1_SIZE定義為64MB
?gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
?gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; return 0;
}
9. ulong flash_init (void) 這里smdk2410的默認flash是AMD的,mini2440的板子是SST39F1601,如果NOR Flash符合CFI接口標準,可以使用drivers/cfi_flash.c
里面的接口函數,否則就得重寫。如果使用cfi_flash.c,需要修改include/configs/$(board_name).h,在里面增加:
?#define CFG_FLASH_CFI_DRIVER ?1
在board/$(board_name)/Makefile中去掉flash.o
在《嵌入式linux完全手冊》上P273頁有說明。 首先是有一個變量flash_info_t flash_info[CFG_MAX_FLASH_BANKS]來記錄flash的信息。flash_info_t定義:
?? typedef struct {
??? ulong size;?? /* 總大小BYTE? */
??? ushort sector_count;? /* 總的sector數*/
??? ulong flash_id;? /* combined device & manufacturer code */
??? ulong start[CFG_MAX_FLASH_SECT];?? /* 每個sector的起始物理地址。 */
??? uchar protect[CFG_MAX_FLASH_SECT]; /* 每個sector的保護狀態,如果置1,在執行erase操作的時候將跳過對應sector*/
???? #ifdef CFG_FLASH_CFI //我不管CFI接口。
??? .....
???? #endif
?? } flash_info_t;
??? flash_init()的操作就是讀取ID號,ID號指明了生產商和設備號,根據這些信息設置size,sector_count,flash_id.
??? 以及start[]、protect[]。 10.mem_malloc_init()
11.nand_init()
12.env_relocate()
13.devices_init ();?/* get the devices list going. */定義于common/devices.c
14.jumptable_init ()
15.console_init_r ();?/* fully init console as a device ,前面結構體中的是前期控制臺初始化,這是后期*/
主要過程:查看環境參數stdin,stdout,stderr中對標準IO的指定的設備名稱,再按照環境指定的名稱搜索devlist,將搜到的設備指針賦給標準IO數組stdio_devices[]。置gd->flag標志GD_FLG_DEVINIT。這個標志影響putc,getc函數的實現,未定義此標志時直接由串口serial_getc和serial_putc實現,定義以后通過標準設備數組stdio_devices[]中的putc和getc來實現IO。
?下面是相關代碼:
??? void putc (const char c)
???????? {
???????? #ifdef CONFIG_SILENT_CONSOLE
????????? if (gd->flags & GD_FLG_SILENT)//GD_FLG_SILENT無輸出標志
?????????? return;
???????? #endif
????????? if (gd->flags & GD_FLG_DEVINIT) {//設備list已經初始化
?????????? /* Send to the standard output */
?????????? fputc (stdout, c);
????????? } else {
?????????? /* Send directly to the handler */
?????????? serial_putc (c);//未初始化時直接從串口輸出。
????????? }
???????? }
?????? void fputc (int file, const char c)
??????? {
???????? if (file < MAX_FILES)
????????? stdio_devices[file]->putc (c);
??????? } 為什么要使用devlist,std_device[]? 為了更靈活地實現標準IO重定向,任何可以作為標準IO的設備,如USB鍵盤,LCD屏,串口等都可以對應一個device_t的結構體變量,只需要實現getc和putc等函數,就能加入到devlist列表中去,也就可以被assign為標準IO設備std_device中去。如函數 int console_assign (int file, char *devname); /* Assign the console 重定向標準輸入輸出*/ 這個函數功能就是把名為devname的設備重定向為標準IO文件file(stdin,stdout,stderr)。其執行過程是在devlist中查找devname的設備,返回這個設備的device_t指針,并把指針值賦給std_device[file]。
16.enable_interrupts (); (1)首先,需要設置系統時鐘、初始化串口,只要這兩個設置好,就能從串口看到打印信息。
? board_init函數設置MPLL,改變系統時鐘,這是一個開發板相關的函數。
? serial_init函數用來初始化串口,設置UART控制器,是CPU相關的函數,
? (2)檢測系統內存映射
? 對于特定的開發板,內存分布是比較明確的,dram_init函數指定了目標開發板的內存起始地址為0x30000000,大小為64M
? (3)U-boot命令的格式
? uboot中的每個命令都是通過U_BOOT_CMD宏來定義,格式:U_BOOT_CMD(name,maxargs,repeatable,command,"usage","help")
? 這個宏定義在include/command.h中,
? (4)為內核設置啟動參數
? uboot是通過標記列表向內核傳遞參數,設置內存標記、命令標記的函數setup_memory_tags、setup_commandline_tag,
? 在lib_arm/armlinux.c中定義實現。如果要定義一個命令,需要在include/configs/$(board_name).h中增加一個關于
? 命令的配置項:
? 如: #define CONFIG_CMDLINE_TAG? 1
? 對于arm架構的CPU,都是通過lib_arm/armlinux.c中的do_bootm_linux函數來啟動內核,在這個函數中,設置標記列表,最后通過
? the_kernel(0,bd->bi_arch_number,bd->bi_boot_params)調用內核。 PS: 1.分析過程參照了一位網友的文章,很有參考價值,給我很大幫助。我只是按照配置->編譯->啟動過程 的這么一個認識過程來分析,可能會顯得比較混亂。 ?U-BOOT源碼分析及移植.txt?
??
2.博客鏈接:http://blog.csdn.net/liukun321/article/details/5680504
1.第一階段:start.s的內容:
點擊(此處)折疊或打開
2.進入uboot啟動的第二階段:start_armboot()
點擊(此處)折疊或打開
PS:
uboot的重要的數據結構
1)gd 全局數據變量指針,它保存了u-boot運行需要的全局數據,類型定義:
? typedef struct global_data {
?????????? bd_t? *bd;????? //board data pointor板子數據指針
?????????? unsigned long flags; ?//指示標志,如設備已經初始化標志等。
?????????? unsigned long baudrate; //串口波特率
?????????? unsigned long have_console; /* 串口初始化標志*/
?????????? unsigned long reloc_off;?? /* 重定位偏移,就是實際定向的位置與編譯連接時指定的位置之差,一般為0 */
?????????? unsigned long env_addr; /* 環境參數地址*/
?????????? unsigned long env_valid; /* 環境參數CRC檢驗有效標志 */
?????????? unsigned long fb_base; /* base address of frame buffer */
????????? #ifdef CONFIG_VFD
?????????? unsigned char vfd_type; /* display type */
????????? #endif
?????????? void? **jt;? /* 跳轉表,1.1.6中用來函數調用地址登記 */
????????? } gd_t;
2)bd 板子數據指針。板子很多重要的參數。 類型定義如下:????
?? typedef struct bd_info {
???????????? int?? bi_baudrate;???? /* 串口波特率 */
???????????? unsigned long bi_ip_addr;?? /* IP 地址 */
???????????? unsigned char bi_enetaddr[6]; /* MAC地址*/
???????????? struct environment_s??????? *bi_env;
???????????? ulong???????? bi_arch_number; /* unique id for this board */
???????????? ulong???????? bi_boot_params; /* 啟動參數 */
???????????? struct??? ???/* RAM 配置 */
???????????? {
??????????? ulong start;
??????????? ulong size;
???????????? }bi_dram[CONFIG_NR_DRAM_BANKS];
???????? } bd_t;?
3)環境變量指針 env_t *env_ptr = (env_t *)(&environment[0]);(common/env_flash.c)
? env_ptr指向環境參數區,系統啟動時默認的環境參數environment[],定義在common/environment.c中。
? 參數解釋:
??? bootdelay 定義執行自動啟動的等候秒數?
??? baudrate 定義串口控制臺的波特率?
??? netmask 定義以太網接口的掩碼?
??? ethaddr 定義以太網接口的MAC地址?
??? bootfile 定義缺省的下載文件?
??? bootargs 定義傳遞給Linux內核的命令行參數?
??? bootcmd 定義自動啟動時執行的幾條命令?
??? serverip 定義tftp服務器端的IP地址?
??? ipaddr 定義本地的IP地址?
??? stdin 定義標準輸入設備,一般是串口?
??? stdout 定義標準輸出設備,一般是串口?
??? stderr 定義標準出錯信息輸出設備,一般是串口?
? 4)設備相關:
?? 標準IO設備數組?evice_t *stdio_devices[] = { NULL, NULL, NULL };
?? 設備列表 list_t??? devlist = 0;
?? device_t的定義:include\devices.h中:
??? typedef struct {
???? int flags;??? /* Device flags: input/output/system */
???? int ext;????? /* Supported extensions?? */
???? char name[16];?? /* Device name??? */????
??? /* GENERAL functions */????
???? int (*start) (void);? /* To start the device?? */
???? int (*stop) (void);? /* To stop the device?? */????
??? /* 輸出函數 */????
???? void (*putc) (const char c); /* To put a char?? */
???? void (*puts) (const char *s); /* To put a string (accelerator) */???
??? /* 輸入函數 */???
???? int (*tstc) (void);? /* To test if a char is ready... */
???? int (*getc) (void);? /* To get that char?? */???
??? /* Other functions */????
???? void *priv;?? /* Private extensions?? */
??? } device_t;
? u-boot把可以用為控制臺輸入輸出的設備添加到設備列表devlist,并把當前用作標準IO的設備指針加入stdio_devices數組中。
? 在調用標準IO函數如printf()時將調用stdio_devices數組對應設備的IO函數如putc()。
5)命令相關的數據結構,后面介紹。
6)與具體設備有關的數據結構,
???? 如flash_info_t flash_info[CFG_MAX_FLASH_BANKS];記錄nor flash的信息。
???? nand_info_t nand_info[CFG_MAX_NAND_DEVICE]; nand flash塊設備信息
?????
?????
5.static int init_baudrate (void)
{
?char tmp[64];?/* long enough for environment variables */
?int i = getenv_r ("baudrate", tmp, sizeof (tmp));//環境變量存于tmp緩沖中
?gd->bd->bi_baudrate = gd->baudrate = (i > 0)
???? (int) simple_strtoul (tmp, NULL, 10)
???: CONFIG_BAUDRATE; return (0);
}
6.int serial_init()實際調用下面這個函數,在移植時這個函數是第一個要修改的,因為要用到串口輸出信息
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;?//這里使用的是串口0.
?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 sooooooooooooooooooo ugly */
#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)
?/* we need auto hw flow control on the gsm and gps port */
?if (dev_index == 0 || dev_index == 1)
??uart->UMCON = 0x10;
#endif
?_serial_setbrg(dev_index); return (0);
}
7.由于標準設備還沒有初始化(gd->flags & GD_FLG_DEVINIT=0),這時控制臺使用串口作為控制臺
??? 函數只有一句:gd->have_console = 1;
int console_init_f (void)
{
?gd->have_console = 1;?//將標志位置1 #ifdef CONFIG_SILENT_CONSOLE
?if (getenv("silent") != NULL)
??gd->flags |= GD_FLG_SILENT;
#endif return (0);
}
8.int dram_init (void)
{//PHYS_SDRAM_1在整個include/configs.h文件中有很多定義,只在一個地方define為0x30000000
?//PHYS_SDRAM_1_SIZE定義為64MB
?gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
?gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE; return 0;
}
9. ulong flash_init (void) 這里smdk2410的默認flash是AMD的,mini2440的板子是SST39F1601,如果NOR Flash符合CFI接口標準,可以使用drivers/cfi_flash.c
里面的接口函數,否則就得重寫。如果使用cfi_flash.c,需要修改include/configs/$(board_name).h,在里面增加:
?#define CFG_FLASH_CFI_DRIVER ?1
在board/$(board_name)/Makefile中去掉flash.o
在《嵌入式linux完全手冊》上P273頁有說明。 首先是有一個變量flash_info_t flash_info[CFG_MAX_FLASH_BANKS]來記錄flash的信息。flash_info_t定義:
?? typedef struct {
??? ulong size;?? /* 總大小BYTE? */
??? ushort sector_count;? /* 總的sector數*/
??? ulong flash_id;? /* combined device & manufacturer code */
??? ulong start[CFG_MAX_FLASH_SECT];?? /* 每個sector的起始物理地址。 */
??? uchar protect[CFG_MAX_FLASH_SECT]; /* 每個sector的保護狀態,如果置1,在執行erase操作的時候將跳過對應sector*/
???? #ifdef CFG_FLASH_CFI //我不管CFI接口。
??? .....
???? #endif
?? } flash_info_t;
??? flash_init()的操作就是讀取ID號,ID號指明了生產商和設備號,根據這些信息設置size,sector_count,flash_id.
??? 以及start[]、protect[]。 10.mem_malloc_init()
11.nand_init()
12.env_relocate()
13.devices_init ();?/* get the devices list going. */定義于common/devices.c
14.jumptable_init ()
15.console_init_r ();?/* fully init console as a device ,前面結構體中的是前期控制臺初始化,這是后期*/
主要過程:查看環境參數stdin,stdout,stderr中對標準IO的指定的設備名稱,再按照環境指定的名稱搜索devlist,將搜到的設備指針賦給標準IO數組stdio_devices[]。置gd->flag標志GD_FLG_DEVINIT。這個標志影響putc,getc函數的實現,未定義此標志時直接由串口serial_getc和serial_putc實現,定義以后通過標準設備數組stdio_devices[]中的putc和getc來實現IO。
?下面是相關代碼:
??? void putc (const char c)
???????? {
???????? #ifdef CONFIG_SILENT_CONSOLE
????????? if (gd->flags & GD_FLG_SILENT)//GD_FLG_SILENT無輸出標志
?????????? return;
???????? #endif
????????? if (gd->flags & GD_FLG_DEVINIT) {//設備list已經初始化
?????????? /* Send to the standard output */
?????????? fputc (stdout, c);
????????? } else {
?????????? /* Send directly to the handler */
?????????? serial_putc (c);//未初始化時直接從串口輸出。
????????? }
???????? }
?????? void fputc (int file, const char c)
??????? {
???????? if (file < MAX_FILES)
????????? stdio_devices[file]->putc (c);
??????? } 為什么要使用devlist,std_device[]? 為了更靈活地實現標準IO重定向,任何可以作為標準IO的設備,如USB鍵盤,LCD屏,串口等都可以對應一個device_t的結構體變量,只需要實現getc和putc等函數,就能加入到devlist列表中去,也就可以被assign為標準IO設備std_device中去。如函數 int console_assign (int file, char *devname); /* Assign the console 重定向標準輸入輸出*/ 這個函數功能就是把名為devname的設備重定向為標準IO文件file(stdin,stdout,stderr)。其執行過程是在devlist中查找devname的設備,返回這個設備的device_t指針,并把指針值賦給std_device[file]。
16.enable_interrupts (); (1)首先,需要設置系統時鐘、初始化串口,只要這兩個設置好,就能從串口看到打印信息。
? board_init函數設置MPLL,改變系統時鐘,這是一個開發板相關的函數。
? serial_init函數用來初始化串口,設置UART控制器,是CPU相關的函數,
? (2)檢測系統內存映射
? 對于特定的開發板,內存分布是比較明確的,dram_init函數指定了目標開發板的內存起始地址為0x30000000,大小為64M
? (3)U-boot命令的格式
? uboot中的每個命令都是通過U_BOOT_CMD宏來定義,格式:U_BOOT_CMD(name,maxargs,repeatable,command,"usage","help")
? 這個宏定義在include/command.h中,
? (4)為內核設置啟動參數
? uboot是通過標記列表向內核傳遞參數,設置內存標記、命令標記的函數setup_memory_tags、setup_commandline_tag,
? 在lib_arm/armlinux.c中定義實現。如果要定義一個命令,需要在include/configs/$(board_name).h中增加一個關于
? 命令的配置項:
? 如: #define CONFIG_CMDLINE_TAG? 1
? 對于arm架構的CPU,都是通過lib_arm/armlinux.c中的do_bootm_linux函數來啟動內核,在這個函數中,設置標記列表,最后通過
? the_kernel(0,bd->bi_arch_number,bd->bi_boot_params)調用內核。 PS: 1.分析過程參照了一位網友的文章,很有參考價值,給我很大幫助。我只是按照配置->編譯->啟動過程 的這么一個認識過程來分析,可能會顯得比較混亂。 ?U-BOOT源碼分析及移植.txt?
??
2.博客鏈接:http://blog.csdn.net/liukun321/article/details/5680504
總結
以上是生活随笔為你收集整理的uboot 分析之 启动流程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Bootloader概述
- 下一篇: Uboot启动全过程