全部博文(153)
分类: LINUX
2010-08-28 16:51:37
其实u-boot还是比较简单的,总结下来就是以下这么多点:
1。在board目录下相应平台下:congfig.mk规定了TEXT_BASE的地址(也就是希望运行的地址,链接地址,真实(上电启动,pc指针指过去)是在.start里执行的),链接脚本u-boot.lds也在这里。
在cpu目录相应平台下:除了主要的start.s等文件,也有一个config.mk文件,不过该文件主要定义了编译选项,比如armv6架构等。
2。 通过链接地址TEXT_BASE和运行地址.start的不同来决定是否要relocate
3。 在启动部分可以看到在uboot中栈的位置是在TEXT_BASE下面(小于TEXT_BASE的地址)的:
ldr r0, _TEXT_BASE
sub r0, r0, #CFG_MALLOC_LEN ;//这些参数是在include/configs/相应平台下设定的,
sub r0, r0, #CFG_GBL_DATA_SIZE
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
因此uboot部分的内存分布应该如下:
Svc stack |
Irq stack |
Fiq stack |
Gbl(放全局变量gd的) |
malloc区 |
TEXT_BASE |
4。系统c部分的代码主要是在lib/lib_arm/board.c中的start_armboot函数中:
在gbl区存放了uboot中唯一一个全局变量(当然因为这个全局变量涵盖的东西够多了,所以只需要这么一个了)gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
看一下这个gd_t的类型:
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
unsigned long reloc_off; /* Relocation Offset */
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
void **jt; /* jump table */
} gd_t;
bd的位置紧挨gd之下:
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
typedef struct bd_info {
unsigned long bi_memstart; /* start of DRAM memory */
unsigned long bi_memsize; /* size of DRAM memory in bytes */
unsigned long bi_flashstart; /* start of FLASH memory */
unsigned long bi_flashsize; /* size of FLASH memory */
unsigned long bi_flashoffset; /* reserved area for startup monitor */
unsigned long bi_sramstart; /* start of SRAM memory */
unsigned long bi_sramsize; /* size of SRAM memory */
unsigned long bi_ip_addr; /* IP Address */
unsigned char bi_enetaddr[6]; /* Ethernet adress */
unsigned long bi_baudrate; /* Console Baudrate */
} bd_t;
5。接下来主要完成这么一个数组的执行,数组中存放一个指针,指针指向的内容是一个函数。
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, //这里会设定传递给os的机器号和参数地址
interrupt_init, /* set up exceptions */
env_init, //根据配置的不同,将环境变量初始化在不同的地方
根据:CFG_ENV_IS_IN_FLASH,CFG_ENV_IS_IN_ONENAND,CFG_ENV_IS_IN_NAND,CFG_ENV_IS_NOWHERE等选项来调用相应的env_init函数。当然这些配置在include/configs/具体平台文件中。比如4020的uboot中是定义的IN_FLASH,因此会执行common/env_flash.c中的函数。
init_baudrate, //这里会从env中获得波特率的值然后赋给gd->bd->bi_baudrate
//have passed here
serial_init, /* serial communications setup */
//console also passed
console_init_f, /* stage 1 init of console */
display_banner, /* 打印点信息 */
dram_init, //其实一般到这里ram早就配置好了,这里也是将ram信息赋给gd->bd->bi*
display_dram_config,
NULL,
};
6。完成了上面那个数组的函数,其实已经完成了基本的配置。接下来的函数就是进一步细化模块的初始化,包括有:(注:一下均为功能性伪码,不一定要相同的函数名)
flash_init ();
Frambuffer_init();
nand_init();
env_relocate ();(虽然上面数组中有env_init的,但是在env是有crc校验的,一般而言刚开始flash中的初始数据肯定是不满足crc的,此时系统会自动做以下操作gd->env_addr =(ulong)&default_environment[0];gd->env_valid = 0;因此其实env并没有完全初始化好,在这里会将defalut的拷贝至flash中(如果定义的话),然后重新将gd->env_addr指向configs中规定的地方)
ethernet_init() ;
最终进入main_loop:main_loop中就是不断的循环等待你输入命令,然后执行命令。
7。 命令的执行:
uboot中所有的命令都通过以下这种方式定义:
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
比如:printenv命令
U_BOOT_CMD(
printenv, CFG_MAXARGS, 1, do_printenv,
"printenv- print environment variables\n",
"\n - print values of all environment variables\n"
"printenv name ...\n"
" - print value of environment variable 'name'\n"
);这个其实就定义了一个cmd_tbl_t类型的结构体,并且由于在编译的时候依旧加了限定符Struct_Section,因此这些命令的空间都在.u_boot_cmd的地方,在lds脚本中也是将它们独立存放的
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
这种方法的好处,就是在用户输入一条命令,系统去查找这条命令是否存在,以及它的处理函数时非常方便,这种做法在linux中解析bootargs等也大量用到了,详见之前linux c语言启动部分——参数分析专题。