1 uboot链接脚本u-boot.lds
参考文档
u-boot.lds在uboot源码根目录下,但是实际上uboot源码根目录原来是没有u-boot.lds的,是uboot Makefile从其他位置拷贝、修改生成的。
uboot Makefile
ifndef LDSCRIPT
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/board/$(BOARDDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/$(CPUDIR)/u-boot.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(srctree)/arch/$(ARCH)/cpu/u-boot.lds //实际路径 arch/arm/cpu/u-boot.lds
endif
endif
uboot根目录下也是实际使用的u-boot.lds
/* 指定可执行文件是32位、elf格式,arm 小端序 */
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
/* 指定arm架构 */
OUTPUT_ARCH(arm)
/* 指定起始代码段为_stext,_stext在./arch/arm/cpu/slsiap/s5p6818/start.S */
ENTRY(_stext)
SECTIONS
{/* 全局入口点,这个位置(0x0)通常是存储uboot flash(常用emmc)的0x0地址 */
. = 0x00000000;
. = ALIGN(4); /* 4字节对齐 */
.text : /* 代码段开始 */
{/* 将uboot拷贝到内存中,先拷贝start.o,然后拷贝其他文件,也就是说在ram中start.o地址最前,也就是说start.o在ram中装载地址、执行地址都0x0 */
*(.__image_copy_start)
/* 在arch/arm/cpu/u-boot.lds中,下面一行是CPUDIR/start.o (.text*),CPUDIR也就是arch/arm/cpu/slsiap/s5p6818/目录 */
arch/arm/cpu/slsiap/s5p6818/start.o (.text*)
*(.text*) /* 除了start.o外其他输出文件的代码段 */
}
/* 代码段,内存可能并不是4字节对齐,这里需要再4字节对齐一次 */
. = ALIGN(4);
/* rodata段开始,保存只读数据,由所有输出文件的rodata段组成 */
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4); /* 同样4字节对齐 */
/* data段开始 */
.data : {
*(.data*)
}
. = ALIGN(4);
. = .;
. = ALIGN(4);
/*存放u-boot自有的一些function,例如u-boot command等*/
.u_boot_list : {
KEEP(*(SORT(.u_boot_list*)));
}
. = ALIGN(4);
.image_copy_end :
{
/* u-boot需要自拷贝的内容结束,总结一下,包括代码段,数据段,以及u_boot_list */
*(.__image_copy_end)
}
.rel_dyn_start :
{
/*在老的uboot中,如果我们想要uboot启动后把自己拷贝到内存中的某个地方,只要把要拷贝的地址写给TEXT_BASE即可,然后boot启动后就会把自己拷贝到TEXT_BASE内的地址处运行,在拷贝之前的代码都是相对的,不能出现绝对的跳转,否则会跑飞。在新版的uboot里(2013.07),TEXT_BASE的含义改变了。它表示用户要把这段代码加载到哪里,通常是通过串口等工具。然后搬移的时候由uboot自己计算一个地址来进行搬移。新版的uboot采用了动态链接技术,在lds文件中有__rel_dyn_start和__rel_dyn_end,这两个符号之间的区域存放着动态链接符号,只要给这里面的符号加上一定的偏移,拷贝到内存中代码的后面相应的位置处,就可以在绝对跳转中找到正确的函数。*/
*(.__rel_dyn_start)
}
.rel.dyn : {
/* 动态链接符存放在的段 */
*(.rel*)
}
.rel_dyn_end :
{
*(.__rel_dyn_end)
}
.end :
{
*(.__end)
}
_image_binary_end = .;
. = ALIGN(4096);
.mmutable : {
*(.mmutable)
}
/* bss段 */
.bss_start (OVERLAY) : {
KEEP(*(.__bss_start));
__bss_base = .; /* 把__bss_base 赋值为当前位置 */
}
.bss __bss_base (OVERLAY) : {
*(.bss*)
. = ALIGN(4);
__bss_limit = .;
}
.bss_end __bss_limit (OVERLAY) : {
KEEP(*(.__bss_end));
}
.dynsym _image_binary_end : { *(.dynsym) }
.dynbss : { *(.dynbss) }
.dynstr : { *(.dynstr*) }
.dynamic : { *(.dynamic*) }
.plt : { *(.plt*) }
.interp : { *(.interp*) }
.gnu.hash : { *(.gnu.hash) }
.gnu : { *(.gnu*) }
.ARM.exidx : { *(.ARM.exidx*) }
.gnu.linkonce.armexidx : { *(.gnu.linkonce.armexidx.*) }
}
start.o中会有一个u-boot自拷贝及重定位过程,start.o执行到最后时,整个u-boot已经被复制到了内存的TEXT_BASE(0x42C00000)位置。TEXT_BASE可以从uboot源码根目录下Makefile中找到,Makefile实际用的是CONFIG_SYS_TEXT_BASE,这个宏定义在include/configs/s5p6818_drone.h中
#define CONFIG_SYS_TEXT_BASE 0x42C00000
#define CONFIG_SYS_INIT_SP_ADDR CONFIG_SYS_TEXT_BASE /* init and run stack pointer */
所以重定位后uboot(应该包含start.o,已执行完)的执行地址变更为0x42c00000
2 start.S
(u-boot\arch\arm\cpu\slsiap\s5p6818)
异常向量表及uboot首地址
.globl _stext /* 整个uboot首地址_stext,接下来就是reset、异常向量表 */
_stext:
b reset
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
重定位是在reset中完成的
#ifdef CONFIG_RELOC_TO_TEXT_BASE
relocate_to_text:
/*
* relocate u-boot code on memory to text base
* for nexell arm core (add by jhkim)
*/
adr r0, _stext /*r0 <- current position of code,uboot首地址,从start.o _stext开始*/
ldr r1, TEXT_BASE /* test if we run from flash or RAM,重定位的地址 */
cmp r0, r1 /* don't reloc during debug,两个地址相同,重定位已经完成,跳转到clear_bss */
beq clear_bss
ldr r2, _bss_start_ofs /* 变量定义在start.S等于__bss_start - _stext,也就是__bss_start相对于_stext的偏移 */
add r2, r0, r2 /* r2 <- source end address 现在r2就是__bss_start的地址 */
copy_loop_text:
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_text /* 循环拷贝,将uboot中__bss_start之前的内容拷贝到TEXT_BASE地址开始的位置 */
ldr r1, TEXT_BASE /* restart at text base */
mov pc, r1 /* 从TEXT_BASE开始执行,start.S会被再执行一次,重定位不再执行,跳转到clear_bss */
clear_bss 完成比较多的工作
clear_bss:
#ifdef CONFIG_MMU_ENABLE
bl mmu_turn_on /* 1 开mmu */
#endif
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
ldr r4, TEXT_BASE /* text addr */
add r0, r0, r4
add r1, r1, r4
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... 2循环清bss */
add r0, r0, #4
cmp r0, r1
bne clbss_l
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
sub sp, #GD_SIZE /* allocate one GD above SP */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
mov r9, sp /* GD is above SP */
mov r0, #0
bl board_init_f /* 3 设置好需要传递的参数后,调用board_init_f(在common/board_f.c中) */
mov sp, r9 /* SP is GD's base address */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
sub sp, #GENERATED_BD_INFO_SIZE /* allocate one BD above SP */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
mov r0, r9 /* gd_t *gd */
ldr r1, TEXT_BASE /* ulong text */
mov r2, sp /* ulong sp */
bl gdt_reset /* 4 调用gdt_reset */
/* call board_init_r(gd_t *id, ulong dest_addr) */
mov r0, r9 /* gd_t */
ldr r1, =(CONFIG_SYS_MALLOC_END) /* dest_addr for malloc heap end */
/* call board_init_r 5调用board_init_r(board_r.c) */
ldr pc, =board_init_r /* this is auto-relocated! */
那么uboot中C代码被调用的三个函数就是board_init_f、gdt_reset、board_init_r最后转入到board_init_r代码,彻底进入C代码,汇编完成。
3 common/board_f.c
board_init_f 函数中最重要的就是init_sequence_f,board_init_f 实际的工作就是调用init_sequence_f数组中的函数。将init_sequence_f中实际未用到的部分去掉
static init_fnc_t init_sequence_f[] = {//重点函数有几个
setup_mon_len,
setup_fdt,
trace_early_init,
arch_cpu_init, /* 1 cpu 初始化 cpu.c kernel\arch\arm\mach-s5p6818 */
mark_bootstage,
#ifdef CONFIG_OF_CONTROL
fdtdec_check_fdt,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f, /* 2 gpio pmu rtc 初始化 board.c kernel\arch\arm\plat-s5p6818\drone */
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_MIPS)
timer_init, /* 3 timer 初始化 */
#endif
env_init, /* 4 环境变量初始化 */
init_baud_rate, /* 5 串口、波特率初始化,串口初始化完成后就可以打印调试信息了 */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
#ifdef CONFIG_OF_CONTROL
fdtdec_prepare_fdt,
#endif
display_options, /* say that we are here */
display_text_info, /* show debugging info if required */
print_cpuinfo, /* 6 打印cpu信息 display cpu info (and speed) */
#if defined(CONFIG_DISPLAY_BOARDINFO) /*未定义*/
checkboard, /* display board info */
#endif
INIT_FUNC_WATCHDOG_INIT
INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
init_func_i2c, /* 7 i2c初始化 */
#endif
#if defined(CONFIG_HARD_SPI) /*未定义*/
init_func_spi,
#endif
announce_dram_init,
/* TODO: unify all these dram functions? */
#ifdef CONFIG_ARM /* 8 内存初始化,这里仅获取内存大小 */
dram_init, /* configure available RAM banks */
#endif
INIT_FUNC_WATCHDOG_RESET
INIT_FUNC_WATCHDOG_RESET
INIT_FUNC_WATCHDOG_RESET
/*
* Now that we have DRAM mapped and working, we can
* relocate the code and continue running from DRAM.
*
* Reserve memory at end of RAM for (top down in that order):
* - area that won't get touched by U-Boot and Linux (optional)
* - kernel log buffer
* - protected RAM
* - LCD framebuffer
* - monitor code
* - board info struct
*/
setup_dest_addr,
reserve_round_4k,
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
defined(CONFIG_ARM)
reserve_mmu,
#endif
#ifdef CONFIG_LCD /*未定义*/
reserve_lcd,
#endif
reserve_trace,
reserve_uboot,
#ifndef CONFIG_SPL_BUILD
reserve_malloc,
reserve_board,
#endif
setup_machine,
reserve_global_data,
reserve_fdt,
reserve_stacks,
setup_dram_config, /* 8 配置、显示内存参数 */
show_dram_config,
display_new_sp,
INIT_FUNC_WATCHDOG_RESET
reloc_fdt,
setup_reloc,
NULL,
};
3.1 env_init
使用的是emmc,所以调用的env_init函数是在env_emmc.c(u-boot/common)
int env_init(void)
{
/* use default */
gd->env_addr = (ulong)&default_environment[0];
gd->env_valid = 1;
return 0;
}
default_environment数组是在include/env_default.h定义的,default_environment数组中保存的是一个个宏定义的环境变量,需要使用的环境变量通常会在板子的头文件中定义,这里的头文件是s5p6818_drone.h u-boot\include\configs。
default_environment数组中用到一个CONFIG_BOOTDELAY宏,在s5p6818_drone.h中定义为
#define CONFIG_BOOTDELAY 0
4 gdt_reset
gdt_reset函数在cpu.c u-boot\arch\arm\cpu\slsiap\s5p6818,主要是对uboot全局变量gd及其成员bd初始化。
void gdt_reset(gd_t *gd, ulong text, ulong sp)
{
ulong text_start, text_end, heap_end;
ulong bd, pc;
/* for smp cores */
global_descriptor = gd; /* 全局变量gd */
/* reconfig stack info */
gd->relocaddr = text;
gd->start_addr_sp = sp;
gd->reloc_off = 0;
/* copy bd info */
bd = (ulong)gd - sizeof(bd_t); /* bd board保存板子的信息 */
memcpy((void *)bd, (void *)gd->bd, sizeof(bd_t));
/* reset gd->bd */
gd->bd = (bd_t *)bd;
/* prevent dataabort, when access enva_addr + data (0x04) */
gd->env_addr = (ulong)default_environment;
/* get cpu info */
text_start = (ulong)(gd->relocaddr);
text_end = (ulong)(gd->relocaddr + _bss_end_ofs);
heap_end = CONFIG_SYS_MALLOC_END;
#if defined(CONFIG_SYS_GENERIC_BOARD)
/* refer initr_malloc (common/board_r.c) */
gd->relocaddr = heap_end;
#endif
flush_dcache_all();
#if defined(CONFIG_DISPLAY_CPUINFO)
asm("mov %0, pc":"=r" (pc));
asm("mov %0, sp":"=r" (sp));
printf("Heap = 0x%08lx~0x%08lx\n", heap_end-TOTAL_MALLOC_LEN, heap_end);
printf("Code = 0x%08lx~0x%08lx\n", text_start, text_end);
printf("GLD = 0x%08lx\n", (ulong)gd);
printf("GLBD = 0x%08lx\n", (ulong)gd->bd);
printf("SP = 0x%08lx,0x%08lx(CURR)\n", gd->start_addr_sp, sp);
printf("TAGS = 0x%08lx \n", gd->bd->bi_boot_params);
#ifdef CONFIG_MMU_ENABLE
ulong page_tlb = (text_end & 0xffff0000) + 0x10000;
printf("PAGE = 0x%08lx~0x%08lx\n", page_tlb, page_tlb + 0xc000 );
#endif
#ifdef CONFIG_SMP
printf("SMPSP= 0x%08x (-0x%08x)\n", CONFIG_SYS_SMP_SP_ADDR, CONFIG_SYS_SMP_SP_SIZE*(NR_CPUS-1));
#endif
printf("MACH = [%ld] \n", gd->bd->bi_arch_number);
printf("VER = %u \n", nxp_cpu_version());
printf("BOARD= [%s] \n", CONFIG_SYS_BOARD);
#endif
}
5 board_init_r
board_init_r在board_r.c u-boot\common中,这个函数的实际完成init_sequence_r数组中函数的调用、执行,数组中有部分重要函数需要特别注意
init_fnc_t init_sequence_r[] = {
initr_trace,
initr_reloc,
/* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARM
initr_caches,
board_init, /* Setup chipselects */
#endif
/*
* TODO: printing of the clock inforamtion of the board is now
* implemented as part of bdinfo command. Currently only support for
* davinci SOC's is added. Remove this check once all the board
* implement this.
*/
#ifdef CONFIG_CLOCKS
set_cpu_clk_info, /* Setup clock information */
#endif
initr_reloc_global_data,
initr_serial,
initr_announce,
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_PPC
INIT_FUNC_WATCHDOG_RESET
INIT_FUNC_WATCHDOG_RESET
initr_barrier,
initr_malloc,
bootstage_relocate,
power_init_board,
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_GENERIC_MMC
initr_mmc, /* 1 mmc初始化 */
#endif
initr_env, /* 2 环境变量初始化 */
INIT_FUNC_WATCHDOG_RESET
initr_secondary_cpu,
INIT_FUNC_WATCHDOG_RESET
stdio_init, /* 3 部分平台uboot显示需要走stdio_init, 但是6818显示不走stdio_init */
initr_jumptable,
console_init_r, /* fully init console as a device */
INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDB
initr_kgdb,
#endif
interrupt_init,
#if defined(CONFIG_ARM) || defined(CONFIG_x86)
initr_enable_interrupts,
#endif
/* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NET
initr_ethaddr,
#endif
#ifdef CONFIG_BOARD_LATE_INIT
board_late_init, /* 4 6818 fastboot升级走board_late_init,并且显示部分从这里开始 */
#endif
#ifdef CONFIG_CMD_NET
INIT_FUNC_WATCHDOG_RESET
initr_net,
#endif
run_main_loop, /* 5 uboot中最重要的函数之一,在这里串口接收到回车进入命令行等待输入命令,如回车,启动kernel */
};
5.1 mmc 初始化
mmc初始化函数init_mmc实际调用mmc_initialize函数来完成初始化
int mmc_initialize(bd_t *bis)
{
INIT_LIST_HEAD (&mmc_devices);
cur_dev_num = 0;
if (board_mmc_init(bis) < 0) //6818 mmc初始化函数
cpu_mmc_init(bis);
#ifndef CONFIG_SPL_BUILD
print_mmc_devices(',');
#endif
do_preinit(); //preinit
return 0;
}
5.2 环境变量初始化
static int initr_env(void)
{
/* initialize environment */
if (should_load_env())
env_relocate(); /* 调用env_relocate,最后还是需要调用set_default_env */
else
set_default_env(NULL);
/* Initialize from environment */
load_addr = getenv_ulong("loadaddr", 16, load_addr); /* 从环境变量获取加载地址 */
}
set_default_env函数按照特别格式解析default_environment,并将解析后的内容保存到hash表中
void set_default_env(const char *s) /* env_common.c u-boot\common * /
{
int flags = 0;
if (sizeof(default_environment) > ENV_SIZE) {
puts("*** Error - default environment is too large\n\n");
return;
}
if (s) {
if (*s == '!') {
printf("*** Warning - %s, "
"using default environment\n\n",
s + 1);
} else {
flags = H_INTERACTIVE;
puts(s);
}
} else {
puts("Using default environment\n\n");
}
if (himport_r(&env_htab, (char *)default_environment,
sizeof(default_environment), '\0', flags,
0, NULL) == 0)
error("Environment import failed: errno = %d\n", errno);
gd->flags |= GD_FLG_ENV_READY;
}
5.3 board_late_init
board_late_init在board.c kernel\arch\arm\plat-s5p6818\drone,主要完成判断是否需要进入fastboot升级状态以及开始uboot logo显示流程,显示部分函数
void bd_display_run(char *cmd, int bl_duty, int bl_on)
{
static int display_init = 0;
if (cmd) {
run_command(cmd, 0); /* 画logo */
lcd_draw_boot_logo(CONFIG_FB_ADDR, CFG_DISP_PRI_RESOL_WIDTH,
CFG_DISP_PRI_RESOL_HEIGHT, CFG_DISP_PRI_SCREEN_PIXEL_BYTE);
}
if (!display_init) { /* 6818显示控制器配置 */
bd_display();
pwm_init(CFG_LCD_PRI_PWM_CH, 0, 0);
display_init = 1;
}
pwm_config(CFG_LCD_PRI_PWM_CH,TO_DUTY_NS(bl_duty, CFG_LCD_PRI_PWM_FREQ),
TO_PERIOD_NS(CFG_LCD_PRI_PWM_FREQ));
if (bl_on)
pwm_enable(CFG_LCD_PRI_PWM_CH); /* 背光亮 */
}
5.4 run_main_loop
run_main_loop函数主要内容就是main_loop函数
void main_loop(void)
{
const char *s;
modem_init();
cli_init();
run_preboot_environment_command();
s = bootdelay_process();
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s); /* bootdelay部分在这里完成 */
cli_loop();
}
跑到run_main_loop函数,uboot已经到了最后了,接下来就是启动kernel或者进入命令行等待命令。
400

被折叠的 条评论
为什么被折叠?



