u-boot啓動第二階段的梳理

u-boot啓動第二階流程梳理:

本 階 段 主 要 有 兩 個 核 心 函 數 , 分 別 是 board_init_f( ) 和 board_init_r() , 這 兩 個 函 數 都 是 在arch\arm\Lib\Board.c 中。
1.board_init_f()

1) gd_t 數據結構空間分配
2) 回調一組初始化函數
3) 對 gd_t 數據結構進行初始化
 

Global_data.h arch\arm\include\Asm
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type */
#endif
#ifdef CONFIG_FSL_ESDHC
unsigned long sdhc_clk;
#endif
#ifdef CONFIG_AT91FAMILY
/* "static data" needed by at91's clock.c */
unsigned long cpu_clk_rate_hz;
unsigned long main_clk_rate_hz;
unsigned long mck_rate_hz;
unsigned long plla_rate_hz;
unsigned long pllb_rate_hz;
unsigned long at91_pllb_usb_init;
#endif
#ifdef CONFIG_ARM
/* "static data" needed by most of timer.c on ARM platforms */
unsigned long timer_rate_hz;
unsigned long tbl;
unsigned long tbu;
unsigned long long timer_reset_value;
unsigned long lastinc;
#endif
unsigned long relocaddr; /* Start address of U-Boot in RAM */
phys_size_t ram_size; /* RAM size */
unsigned long mon_len; /* monitor len */
unsigned long irq_sp; /* irq stack pointer */
unsigned long start_addr_sp;/* start_addr_stackpointer */
unsigned long reloc_off;
#if !(defined(CONFIG_SYS_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE))
unsigned long tlb_addr;
#endif
void **jt; /* jump table */
char env_buf[32]; /* buffer for getenv() before reloc. */
} gd_t;

以下定義一個 gd 指針,並且把這指針存放在 r8 寄存器中。
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
說明: 通過 register 表示寄存器變量, asm ("r8")代表把變量存放在 r8 寄存器中。
4) relocate_code(U-boot 重定義代碼,即自搬移)

以下是函數board_init_f的源碼分析:

這個函數初始化了 u-boot 全局的指針 gd, 最後調用 relocate_code 函數對代碼進行重新定位,把u-boot 搬運到內存的高端地址, 清 BSS 段代碼, 跳轉到重新定位後的 u-boot 代碼中的 board_init_r()函數開始執行。
 

//下面表中出現的函數都需要實現。
typedef int (init_fnc_t)(void);//定義一個函數類型
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init, /* basic arch cpu dependent setup */
//Sys_info.c arch\arm\cpu\armv7\Exynos
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,
#endif
timer_init, /* initialize timer */
//Timer.c arch\arm\cpu\armv7\S5p-common
#ifdef CONFIG_FSL_ESDHC
get_clocks,
#endif
env_init, /* initialize environment */
#if defined(CONFIG_S5P6450) && !defined(CONFIG_S5P6460_IP_TEST)
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
#endif
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 */
//Tiny4412.c board\samsung\Tiny4412 6617 2015-08-06
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
dram_init, /* configure available RAM banks */
//Tiny4412.c board\samsung\Tiny4412 6617 2015-08-06
#if defined(CONFIG_CMD_PCI) || defined(CONFIG_PCI)
arm_pci_init,
#endif
NULL,
};
//C 語言入口
void board_init_f(ulong bootflag)
{
bd_t *bd;
init_fnc_t **init_fnc_ptr;
gd_t *id;
ulong addr, addr_sp;
/* Pointer is writable since we allocated a register for it */
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);
/* compiler optimization barrier needed for GCC >= 3.4 */
__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();
}
}
debug ("monitor len: %08lX\n", gd->mon_len);
/*
* Ram is setup, size stored in gd !!
*/
debug ("ramsize: %08lX\n", gd->ram_size);
#if defined(CONFIG_SYS_MEM_TOP_HIDE)
/*
* Subtract specified amount of memory to hide so that it won't
* get "touched" at all by U-Boot. By fixing up gd->ram_size
* the Linux kernel should now get passed the now "corrected"
* memory size and won't touch it either. This should work
* for arch/ppc and arch/powerpc. Only Linux board ports in
* arch/powerpc with bootwrapper support, that recalculate the
* memory size from the SDRAM controller setup will have to
* get fixed.
*/
gd->ram_size -= CONFIG_SYS_MEM_TOP_HIDE;
#endif
addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
#ifdef CONFIG_LOGBUFFER
#ifndef CONFIG_ALT_LB_ADDR
/* reserve kernel log buffer */
addr -= (LOGBUFF_RESERVE);
debug ("Reserving %dk for kernel logbuffer at %08lx\n", LOGBUFF_LEN, addr);
#endif
#endif
#ifdef CONFIG_PRAM
/*
* reserve protected RAM
*/
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 */
debug ("Reserving %ldk for protected RAM at %08lx\n", reg, addr);
#endif /* CONFIG_PRAM */
#if !(defined(CONFIG_SYS_NO_ICACHE) && defined(CONFIG_SYS_NO_DCACHE))
/* reserve TLB table */
addr -= (4096 * 4);
/* round down to next 64 kB limit */
addr &= ~(0x10000 - 1);
gd->tlb_addr = addr;
debug ("TLB table at: %08lx\n", addr);
#endif
/* round down to next 4 kB limit */
addr &= ~(4096 - 1);
debug ("Top of RAM usable for U-Boot at: %08lx\n", addr);
#ifdef CONFIG_VFD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for VFD display (always full pages)
*/
addr -= vfd_setmem(addr);
gd->fb_base = addr;
#endif /* CONFIG_VFD */
#ifdef CONFIG_LCD
/* reserve memory for LCD display (always full pages) */
addr = lcd_setmem(addr);
gd->fb_base = addr;
#endif /* CONFIG_LCD */
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len;
addr &= ~(4096 - 1);
#if defined(CONFIG_S5P) || defined(CONFIG_S5P6450)
addr = CONFIG_SYS_LOAD_ADDR;
#endif
debug ("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
#ifndef CONFIG_PRELOADER
/*
* reserve memory for malloc() arena
*/
addr_sp = addr - TOTAL_MALLOC_LEN;
debug ("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN >> 10, addr_sp);
/*
* (permanently) allocate a Board Info struct
* and a permanent copy of the "global" data
*/
addr_sp -= sizeof (bd_t);
bd = (bd_t *) addr_sp;
gd->bd = bd;
debug ("Reserving %zu Bytes for Board Info at: %08lx\n",
sizeof (bd_t), addr_sp);
addr_sp -= sizeof (gd_t);
id = (gd_t *) addr_sp;
debug ("Reserving %zu Bytes for Global Data at: %08lx\n",
sizeof (gd_t), addr_sp);
/* setup stackpointer for exeptions */
gd->irq_sp = addr_sp;
#ifdef CONFIG_USE_IRQ
addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
debug ("Reserving %zu Bytes for IRQ stack at: %08lx\n",
CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp);
#endif
/* leave 3 words for abort-stack */
addr_sp -= 3;
/* 8-byte alignment for ABI compliance */
addr_sp &= ~0x07;
#else
addr_sp += 128; /* leave 32 words for abort-stack */
gd->irq_sp = addr_sp;
#endif
debug ("New Stack Pointer is: %08lx\n", addr_sp);
#ifdef CONFIG_POST
post_bootmode_init();
post_run(NULL, POST_ROM | post_bootmode_get(0));
#endif
gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config(); /* and display it */
gd->relocaddr = addr;
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE;
debug ("relocation Offset is: %08lx\n", gd->reloc_off);
memcpy(id, (void *)gd, sizeof (gd_t));
//調用匯編,對 uboot 進行重新定位,把代碼搬運到它內存的最高端位置的 1M 空間中
//搬運後就跳轉到 DDR 中運行, iRAM 中的內容沒有用
//start.S arch\arm\cpu\armv7
relocate_code(addr_sp, id, addr);
/* NOTREACHED - relocate_code() does not return */
}

 relocate_code 源碼分析

relocate_code 是一個彙編函數,在 arch\arm\cpu\armv7\start.S 文件中實現。 具體工作是先把代碼搬運到 DDR 高端位置, 搬運 data 段, 清 0 BSS 段, 最後跳轉重新定位後 u-boot 代碼中的 board_init_r 函數運行,到些完成整個 u-boot 的啓動過程。
 

/*------------------------------------------------------------------------------*/
/* board.c 中 的 board_init_f 函數最後調用這個函數進行代碼重新定位
* void relocate_code (addr_sp, gd, addr_moni)
*
* This "function" does not return, instead it continues in RAM
* after relocating the monitor code.
*
*/
.globl relocate_code
relocate_code:
mov r4, r0 /* save addr_sp */
mov r5, r1 /* save addr of gd */
mov r6, r2 /* save addr of destination */
/* Set up the stack 設置棧指針 */
stack_setup:
mov sp, r4
adr r0, _start
#if defined(CONFIG_S5PC110) && defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
sub r0, r0, #16
#endif
#ifndef CONFIG_PRELOADER //沒有定義
cmp r0, r6
beq clear_bss /* skip relocation */
#endif
mov r1, r6 /* r1 <- scratch for copy_loop */
ldr r2, _TEXT_BASE
ldr r3, _bss_start_ofs
add r2, r0, r3 /* r2 <- source end address */
//複製代碼開始:
copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop
//以下一大段代碼: 複製完成後根據 rel dyn 段中的信息對代碼進行重新定位。
#ifndef CONFIG_PRELOADER
/*
* fix .rel.dyn relocations
*/
ldr r0, _TEXT_BASE /* r0 <- Text base */
sub r9, r6, r0 /* r9 <- relocation offset */
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */
add r10, r10, r0 /* r10 <- sym table in FLASH */
ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */
add r2, r2, r0 /* r2 <- rel dyn start in FLASH */
ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */
add r3, r3, r0 /* r3 <- rel dyn end in FLASH */
fixloop:
ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */
add r0, r0, r9 /* r0 <- location to fix up in RAM */
ldr r1, [r2, #4]
and r7, r1, #0xff
cmp r7, #23 /* relative fixup? */
beq fixrel
cmp r7, #2 /* absolute fixup? */
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */
add r1, r10, r1 /* r1 <- address of symbol in table */
ldr r1, [r1, #4] /* r1 <- symbol value */
add r1, r1, r9 /* r1 <- relocated sym addr */
b fixnext
fixrel:
/* relative fix: increase location by offset */
ldr r1, [r0]
add r1, r1, r9
fixnext:
str r1, [r0]
add r2, r2, #8 /* each rel.dyn entry is 8 bytes */
cmp r2, r3
blo fixloop
//重新定位後代碼後,開始清除代碼的 BSS 段,到此整個 u-boot 完整了, 可以正常執行代碼了。
clear_bss:
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
ldr r3, _TEXT_BASE /* Text base */
mov r4, r6 /* reloc addr */
add r0, r0, r4
add r1, r1, r4
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
bne clbss_l
#endif /* #ifndef CONFIG_PRELOADER */
//還原 u-boot 代碼映像後, 接下來跳轉到重定位後的 u-boot 代碼執行程序, 此時的 C 語言入口就是
board_init_r() 函數。
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
jump_2_ram:
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
@ add lr, lr, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
mov pc, lr
_board_init_r_ofs: //最後真正的 C 代碼入口
.word board_init_r - _start
_rel_dyn_start_ofs:
.word __rel_dyn_start - _start
_rel_dyn_end_ofs:
.word __rel_dyn_end - _start
_dynsym_start_ofs:
.word __dynsym_start - _start
到些完成了整個 u-boot 的運行流程。

2.board_init_r()

這個函數是由上一步的 relocate_code 後,直接調用,這個函數也是重新定位代碼後執行的第一個C 語言函數。
1) 使能 Cache
2) 板子初始化
3) 串口初始化
4) 外存初始化
5) 環境變量初始化
6) 控制檯初始譁
7) 中斷使能
8) 以太網初始化
9) 進入 main_loop(),等待命令或自動加載內核或自動加載內核。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章