首先是对uboot源码的大致架构进行分析,在建立工程时加入有效的目录文件
board目录
单板相关,由于不支持2440,所以只能使用 samsung/smdk2410 文件夹,加入smdk2410整个目录下所有的源码
Arch目录
平台相关,arm的cpu类型存在于arch/arm/cpu下,2440属于arm920t, 将arch/arm/cpu/arm920t下的所有源码加入,然后将arch/arm/cpu/arm920t/s3c24x0目录下的所有源码加入
头文件的加入, arch/arm/include/asm下就是对应各种不同类型cpu的头文件,找到arch\arm\include\asm\arch-s3c24x0目录,将所有源码加入
顶层的arch/arm/include/asm目录下也存在很多通用的头文件,全部加入
arch\arm\include\asm\proc-armv目录可能也是通用的目录,将所有头文件加入
库文件的加入 arch\arm\lib目录下所有的源码加入
Include目录
头文件和开发板的配置文件, 顶层目录 include/下的所有文件加入
include目录下除了configs 目录下其他目录下的所有的文件都加入
include/configs 是单板的配置文件,我们只需要加入smdk2410.h
其他目录下所有的源码都加入其中
uboot的配置编译
首先安装交叉编译工具链 arm-linux-gcc-4.3.2.tar.bz2 解压命令 sudo tar xjf arm-linux-gcc-4.3.2.tar.bz2 -C / 可以直接解压到/usr目录下,并且自动建立软链接arm-linux-gcc,
紧接着配置环境变量
配置: make smdk2410_config
编译: make
uboot源码分析
首先了解生成的uboot是有哪些文件组成,将已经编译完成的uboot删除,重新执行make命令,这样以前的编译,汇编等命令不会再执行,主要会执行链接等命令,就可以看到 链接的文件情况
arm-linux-ld -pie -T u-boot.lds -Bstatic -Ttext 0x0 $UNDEF_SYM arch/arm/cpu/arm920t/start.o --start-group api/libapi.o arch/arm/cpu/arm920t/libarm920t.o arch/arm/cpu/arm920t/s3c24x0/libs3c24x0.o arch/arm/lib/libarm.o common/libcommon.o disk/libdisk.o drivers/bios_emulator/libatibiosemu.o drivers/block/libblock.o drivers/dma/libdma.o drivers/fpga/libfpga.o drivers/gpio/libgpio.o drivers/hwmon/libhwmon.o drivers/i2c/libi2c.o drivers/input/libinput.o drivers/misc/libmisc.o drivers/mmc/libmmc.o drivers/mtd/libmtd.o drivers/mtd/nand/libnand.o drivers/mtd/onenand/libonenand.o drivers/mtd/spi/libspi_flash.o drivers/mtd/ubi/libubi.o drivers/net/libnet.o drivers/net/phy/libphy.o drivers/pci/libpci.o drivers/pcmcia/libpcmcia.o drivers/power/libpower.o drivers/rtc/librtc.o drivers/serial/libserial.o drivers/spi/libspi.o drivers/twserial/libtws.o drivers/usb/eth/libusb_eth.o drivers/usb/gadget/libusb_gadget.o drivers/usb/host/libusb_host.o drivers/usb/musb/libusb_musb.o drivers/usb/phy/libusb_phy.o drivers/usb/ulpi/libusb_ulpi.o drivers/video/libvideo.o drivers/watchdog/libwatchdog.o fs/cramfs/libcramfs.o fs/ext2/libext2fs.o fs/fat/libfat.o fs/fdos/libfdos.o fs/jffs2/libjffs2.o fs/reiserfs/libreiserfs.o fs/ubifs/libubifs.o fs/yaffs2/libyaffs2.o lib/libfdt/libfdt.o lib/libgeneric.o lib/lzma/liblzma.o lib/lzo/liblzo.o lib/zlib/libz.o net/libnet.o post/libpost.o board/samsung/smdk2410/libsmdk2410.o --end-group /home/book/Par_Uboot/mk_uboot/u-boot-2012.04.01/arch/arm/lib/eabi_compat.o -L /usr/local/arm/4.3.2/bin/../lib/gcc/arm-none-linux-gnueabi/4.3.2/armv4t -lgcc -Map u-boot.map -o u-boot
首先从start.s入手
里面的流程大致就是 设置管理员模式 关看门狗 设置时钟(这里只设置了分频系数,没有初始化时钟) 关mmu 初始化sdram
在设置栈指针时,这里是个宏定义,里面的值很难推得到,可以采用直接反汇编的方法查看设置的栈大小
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
对源程序进行反汇编 arm-linux-objdump -D u-boot > uboot.dis 最后可以看到sp =0x30000f80
紧接着对C函数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;
#ifdef CONFIG_PRAM
ulong reg;
#endif
bootstage_mark_name(BOOTSTAGE_ID_START_UBOOT_F, "board_init_f");
/* Pointer is writable since we allocated a register for it */
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); //gd是指向r8寄存器的 DECLARE_GLOBAL_DATA_PTR宏定义指定的
/* compiler optimization barrier needed for GCC >= 3.4 */ //这里gd指向栈指针处 0x30000f80
__asm__ __volatile__("": : :"memory");
memset((void *)gd, 0, sizeof(gd_t));
gd->mon_len = _bss_end_ofs; //所有的代码的大小 从text到bss
#ifdef CONFIG_OF_EMBED
/* Get a pointer to the FDT */
gd->fdt_blob = _binary_dt_dtb_start;
#elif defined CONFIG_OF_SEPARATE
/* FDT is at end of image */
gd->fdt_blob = (void *)(_end_ofs + _TEXT_BASE);
#endif
/* Allow the early environment to override the fdt address */
gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
(uintptr_t)gd->fdt_blob);
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { //初始化各种模块 IO,定时器,时钟等
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
#ifdef CONFIG_OF_CONTROL
/* For now, put this check after the console is ready */
if (fdtdec_prepare_fdt()) {
panic("** CONFIG_OF_CONTROL defined but no FDT - please see "
"doc/README.fdt-control");
}
#endif
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; //0x30000000 + 64M -> 0x34000000
#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
*/
reg = getenv_ulong("pram", 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_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
/* reserve TLB table 预留16k给TLB */
addr -= (4096 * 4);
/* round down to next 64 kB limit 保持64kb对齐*/
addr &= ~(0x10000 - 1);
gd->tlb_addr = addr; //保留16kb给tlb后向下64kb对齐,也就是消除0xffff,0x34000000-0x4000=0x33ffc0000
debug("TLB table at: %08lx\n", addr); //对齐后就是 0x33ff0000
#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_LCD
#ifdef CONFIG_FB_ADDR
gd->fb_base = CONFIG_FB_ADDR;
#else
/* reserve memory for LCD display (always full pages) */
addr = lcd_setmem(addr);
gd->fb_base = addr;
#endif /* CONFIG_FB_ADDR */
#endif /* CONFIG_LCD */
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len; //减去所有段的大小 从text到bss
addr &= ~(4096 - 1); //4k对齐,addr指针确定
debug("Reserving %ldk for U-Boot at: %08lx\n", gd->mon_len >> 10, addr);
#ifndef CONFIG_SPL_BUILD
/*
* 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_t结构体
bd = (bd_t *) addr_sp;
gd->bd = bd;
debug("Reserving %zu Bytes for Board Info at: %08lx\n",
sizeof (bd_t), addr_sp);
#ifdef CONFIG_MACH_TYPE
gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for Linux */
#endif
addr_sp -= sizeof (gd_t); //减去gd结构体
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 -= 12;
/* 8-byte alignment for ABI compliance */
addr_sp &= ~0x07; //8字节对齐后,sp指针确定
#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));
relocate_code(addr_sp, id, addr); //重定位代码,又进入start.s 传入的是栈指针,gd结构体,代码段的链接地址
/* NOTREACHED - relocate_code() does not return */
}
上面就是一系列的内存分布的设置,主要是确定addr指针和addr_sp的指针,整个内存分布大致如下所示:
讲完内存分布就可以分析代码重定位的代码
relocate_code:
mov r4, r0 /* save addr_sp 栈的地址*/
mov r5, r1 /* save addr of gd gd结构体的地址*/
mov r6, r2 /* save addr of destination 代码要链接的地址*/
/* Set up the stack */
stack_setup:
mov sp, r4 //重新修改栈
adr r0, _start
cmp r0, r6
beq clear_bss /* skip relocation 判断当前代码的起始地址和要链接的代码段地址是否相等,相等就直接清bss否则拷贝代码*/
mov r1, r6 /* r1 <- scratch for copy_loop */
ldr r3, _bss_start_ofs
add r2, r0, r3 /* r2 <- source end address 拷贝的代码长度 */
copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] 从start读出代码,读完r0++ */
stmia r1!, {r9-r10} /* copy to target address [r1] 把读出的代码存到sdram上,拷贝完r1++ */
cmp r0, r2 /* until source end address [r2] 直到全部拷贝完成 */
blo copy_loop
/*当前的拷贝方式只适合nor*/
代码重定位完后还存在一个问题,因为程序的链接地址是0,访问全局变量、静态变量、调用函数时是使"基于0地址编译得到的地址" ,现在把程序复制到了SDRAM需要修改代码,需要把"基于0地址编译得到的地址"改为新地址
/*
* fix .rel.dyn relocations
*/
ldr r0, _TEXT_BASE /* r0 <- Text base r0=0*/
sub r9, r6, r0 /* r9 <- relocation offset 重定位的偏移地址(r9 = relocate_addr - 0 =0x33f41000)*/
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs dynsym段的偏移地址 r10 = 00073608*/
add r10, r10, r0 /* r10 <- sym table in FLASH r10 = r10 + 0*/
ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs rel段的偏移地址 r2 = 0006b568*/
add r2, r2, r0 /* r2 <- rel dyn start in FLASH r2 = r2 + 0*/
ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs rel段结束的偏移地址 r3 = 00073608*/
add r3, r3, r0 /* r3 <- rel dyn end in FLASH r3 = r3+0*/
fixloop:
ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! r0 = [0006b568] = 0x00000020*/
add r0, r0, r9 /* r0 <- location to fix up in RAM r0 = r0 + r9(链接地址) =0x00000020+0x33f41000= 0x33f41020 */
ldr r1, [r2, #4] // r1 = [0006b568 + 4 ] = 17
and r7, r1, #0xff // r7 = 17
cmp r7, #23 /* relative fixup? r7 = #23*/
beq fixrel //
cmp r7, #2 /* absolute fixup? */
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs: //有些函数是运行时才确定地址的,所以刚开始给0地址, 等到要运行之前给个新地址,再去运行,下面这一段就是实现这个功能的
/* 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] // r1 = [0x00000020] = 000001e0 从rel段开始读取
add r1, r1, r9 // r1=r1+r9=000001e0 + 0x33f41000 = 33F411E0
fixnext:
str r1, [r0] // [0x33f41020] = 33F411E0 在链接地址处修改对应的地址 例如在flash中0x00000020处的存的是000001e0到sdram中对应就是0x33f41020处变成了33F411E0
add r2, r2, #8 /* each rel.dyn entry is 8 bytes */
cmp r2, r3 //rel段是否全部拷贝完成
blo fixloop
#endif
实现完变量等的地址拷贝之后然后清除bss段
clear_bss:
#ifndef CONFIG_SPL_BUILD
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
mov r4, r6 /* reloc addr */
add r0, r0, r4
add r1, r1, r4
mov r2, #0x00000000 /* clear */
调用c函数,跳转到第二阶段(读取内核,启动内核)
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
add lr, lr, r9 //找到sdram上board_init_r函数对应的地址
/* setup parameters for board_init_r 设置函数形参 */
mov r0, r5 /* gd_t gd_t 结构体的地址*/
mov r1, r6 /* dest_addr 重定位代码的地址*/
/* jump to it ... */
mov pc, lr //调用c函数 board_init_r 跳到第二阶段的程序
_board_init_r_ofs:
.word board_init_r - _start