Uboot源码.docx
《Uboot源码.docx》由会员分享,可在线阅读,更多相关《Uboot源码.docx(38页珍藏版)》请在冰豆网上搜索。
Uboot源码
uboot源码阅读
这篇文章属于转载
cpu/arm920t/start.S
在开始处首先定义了一个全局的标签,
.globl_start
这个是整个uboot程序的入口,可在链接脚本board/s3c2410/u-boot.lds中找到。
这个标签所指的地址处就是一跳转指令
_start:
breset
开始复位。
reset:
/*
*setthecputoSVC32mode
*/
mrsr0,cpsr
bicr0,r0,#0x1f
/*disableIRQandFIQ,ARMinstructset,supervisormode.addedbyBoySKung*/
orrr0,r0,#0xd3
msrcpsr,r0
首先将cpu设置为supervisor模式。
通过设置cpsr的低5位为10011实现,并禁止IRQ、FIQ。
即cpsr的第七第六位设置为11.
接着关闭看门狗定时器
ldrr0,=pWTCON
movr1,#0x0
strr1,[r0]
将看门狗定时器控制寄存器置0。
ldrr0,=CLKDIVN
movr1,#3
strr1,[r0]
设置FCLK:
HCLK:
PCLK为1:
2:
4,通过设置CLKDIVN控制寄存器的HDIVN、PDIVN值实现不同的
比例
00(1:
1:
1)
01(1:
1:
2)
10(1:
2:
2)
11(1:
2:
4)
这些然后开初始化cpu
cpu_init_crit:
/*
*flushv4I/Dcaches
*/
movr0,#0
mcrp15,0,r0,c7,c7,0/*flushv3/v4cache*/
mcrp15,0,r0,c8,c7,0/*flushv4TLB*/
首先关闭ICache和DCache.
/*
*disableMMUstuffandcaches
*/
mrcp15,0,r0,c1,c0,0
bicr0,r0,#0x00002300@clearbits13,9:
8(--V---RS)
bicr0,r0,#0x00000087@clearbits7,2:
0(B----CAM)
orrr0,r0,#0x00000002@setbit2(A)Align
orrr0,r0,#0x00001000@setbit12(I)I-Cache
mcrp15,0,r0,c1,c0,0
关闭MMU,数据存储格式为小端。
开启数据地址对齐错误检测,使能ICACHE。
/*
*beforerelocating,wehavetosetupRAMtiming
*becausememorytimingisboard-dependend,youwill
*findamemsetup.Sinyourboarddirectory.
*/
movip,lr
blmemsetup
movlr,ip
movpc,lr
还要对内存时序进行设置,因为内存时序是依赖于开发板的。
Cpu初始化结束后,开始初始化串口uart
@InitializeUART
@
@r0=numberofUARTport
InitUART:
ldrr1,=0x
movr2,#0x0
strr2,[r1,#0x8]
strr2,[r1,#0xc]
movr2,#0x3
strr2,[r1,#0x0]
ldrr2,=0x245
strr2,[r1,#0x4]
/*=PCLK=202.8/4=50.7whenFCLKat202.8*/
#defineUART_BRD((/(115200*16))-1)
movr2,#UART_BRD
strr2,[r1,#0x28]
这里主要对uart的一些控制寄存器进行了设置,并设置了串口的波特率
movr3,#100
movr2,#0x0
1:
subr3,r3,#0x1
tstr2,r3
bne1b
movpc,lr
这里是一段延时,具体起什么作用还不清楚。
这些初始化都结束后开始代码重定位
relocate:
/*relocateU-BoottoRAM*/
adrr0,_start/*r0<-currentpositionofcode*/
ldrr1,_TEXT_BASE/*testifwerunfromflashorRAM*/
cmpr0,r1/*don'trelocduringdebug*/
beqstack_setup
ldrr2,_armboot_start
ldrr3,_bss_start
subr2,r3,r2/*r2<-sizeofarmboot*/
addr2,r0,r2/*r2<-sourceendaddress*/
copy_loop:
ldmiar0!
{r3-r10}/*copyfromsourceaddress[r0]*/
stmiar1!
{r3-r10}/*copytotargetaddress[r1]*/
cmpr0,r2/*untilsourceendaddreee[r2]*/
blecopy_loop
重定位时,首先检查当前是不是在ram中运行,若是则不需重定位
紧接着设置堆栈
stack_setup:
@ldrr1,=0x
@ldrr0,[r1]
@blPrintHexWord
/*enteransynchronous,whencpuat202.8MHZ,itmustintoasynchronousmode*/
mrcp15,0,r1,c1,c0,0@readctrlregister
orrr1,r1,#0xc0000000@Asynchronous
mcrp15,0,r1,c1,c0,0@writectrlregister
ldrr0,_TEXT_BASE/*upper128KiB:
relocateduboot*/
subr0,r0,#CFG_MALLOC_LEN/*mallocarea*/
subr0,r0,#CFG_GBL_DATA_SIZE/*bdinfo*/
#ifdefCONFIG_USE_IRQ
subr0,r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
subsp,r0,#12/*leave3wordsforabort-stack*/
清除bss段
clear_bss:
ldrr0,_bss_start/*findstartofbsssegment*/
addr0,r0,#4/*startatfirstbyteofbss*/
ldrr1,_bss_end/*stophere*/
movr2,#0x00000000/*clear*/
clbss_l:
strr2,[r0]/*clearloop...*/
addr0,r0,#4
cmpr0,r1
bneclbss_l
就是将bss段全部置0
最后
ldrpc,_start_armboot
开始进入c的世界,uboot启动的stage2
Lib_arm/board.c
当第一阶段的汇编部分执行完,跳到stage2时,开始执行c函数start_armboot
开头首先声明一个全局指针变量DECLARE_GLOBAL_DATA_PTR;这个宏定义在头文件
include/asm-arm/global_data.h中
#defineDECLARE_GLOBAL_DATA_PTRregistervolatilegd_t*gdasm("r8")
typedefstructglobal_data{
bd_t*bd;
unsignedlongflags;
unsignedlongbaudrate;
unsignedlonghave_console;/*serial_init()wascalled*/
unsignedlongreloc_off;/*RelocationOffset*/
unsignedlongenv_addr;/*AddressofEnvironmentstruct*/
unsignedlongenv_valid;/*ChecksumofEnvironmentvalid?
*/
unsignedlongfb_base;/*baseaddressofframebuffer*/
#ifdefCONFIG_VFD
unsignedcharvfd_type;/*displaytype*/
#endif
#if0
unsignedlongcpu_clk;/*CPUclockinHz!
*/
unsignedlongbus_clk;
unsignedlongram_size;/*RAMsize*/
unsignedlongreset_status;/*resetstatusregisteratboot*/
#endif
void**jt;/*jumptable*/
}gd_t;
在这个结构中存放一些全局数据。
gd=(gd_t*)(_armboot_start-CFG_MALLOC_LEN-sizeof(gd_t));
这行是对指针的初始化。
接下来通过一个循环,执行初始化序列中的一些函数
for(init_fnc_ptr=init_sequence;*init_fnc_ptr;++init_fnc_ptr){
if((*init_fnc_ptr)()!
=0){
hang();
}
}
初始化序列的定义如下:
init_fnc_t*init_sequence[]={
cpu_init,//初始化cpu,主要是设置FIQ和IRQ的堆栈起始地址
board_init,//开发板初始化,设置电源、时钟、I/O端口及全能I/DCache
interrupt_init,//中断初始化,设置PWM时钟
env_init,//环境变量初始化,检测环境变量是否有效,并初始化相全局变
量
init_baudrate,//初始化波特率,设置板子通信时的波特率
serial_init,//初始化串口,设置串口和通信时数据结构,包括起始/停止位等
console_init_f,//控制台初始化,将控制台设置为silent模式
display_banner,//打印板子相关信息
dram_init,//内存初始化,设置内存的起始地址和大小
display_dram_config,//显示内存配置信息
#ifdefined(CONFIG_VCMA9)
checkboard,
#endif
NULL,
};
这些初始化都完成后开始初始化flash
unsignedlongflash_init(void)
{
unsignedlongsize_b0;
inti;
/*Init:
noFLASHesknown*/
for(i=0;iflash_info[i].flash_id=FLASH_UNKNOWN;
}
/*StaticFLASHBankconfigurationhere-FIXMEXXX*/
#if1
debug("\n##Getflashbank1size@0x%08x\n",CFG_FLASH_BASE);
#endif
//根据flash的基地址CFG_FLASH_BASE,获得flash的大小,
size_b0=flash_get_size((vu_short*)CFG_FLASH_BASE,&flash_info[0]);
if(flash_info[0].flash_id==FLASH_UNKNOWN){
printf("##UnknownFLASHonBank0:
"
"ID0x%lx,Size=0x%08lx=%ldMB\n",
flash_info[0].flash_id,
size_b0,size_b0<<20);
}
//获得并保存flash各块的起始地址
flash_get_offsets(CFG_FLASH_BASE,&flash_info[0]);
//保存flash的大小
flash_info[0].size=size_b0;
//下面是根据配置,对flash中相应块进行保护,以免数据丢失。
#ifCFG_MONITOR_BASE>=CFG_FLASH_BASE
/*monitorprotectionONbydefault*/
flash_protect(FLAG_PROTECT_SET,
CFG_MONITOR_BASE,
CFG_MONITOR_BASE+monitor_flash_len-1,
&flash_info[0]);
#endif
#ifdefCFG_ENV_IS_IN_FLASH
/*ENVprotectionONbydefault*/
flash_protect(FLAG_PROTECT_SET,
CFG_ENV_ADDR,
CFG_ENV_ADDR+CFG_ENV_SECT_SIZE-1,
&flash_info[0]);
#endif
returnsize_b0;
}
然后是初始内存堆
static
voidmem_malloc_init(ulongdest_addr)
{
mem_malloc_start=dest_addr;
mem_malloc_end=dest_addr+CFG_MALLOC_LEN;
mem_malloc_brk=mem_malloc_start;
memset((void*)mem_malloc_start,0,
mem_malloc_end-mem_malloc_start);
}
之后要进行环境变量重定位,把环境变量放到内存当中
env_relocate();
还要设置网卡的ip地址和mac地址
/*IPAddress*/
gd->bd->bi_ip_addr=getenv_IPaddr("ipaddr");
/*MACAddress*/
{
inti;
ulongreg;
char*s,*e;
uchartmp[64];
i=getenv_r("ethaddr",tmp,sizeof(tmp));
s=(i>0)?
tmp:
NULL;
for(reg=0;reg<6;++reg){
gd->bd->bi_enetaddr[reg]=s?
simple_strtoul(s,&e,16):
0;
if(s)
s=(*e)?
e+1:
e;
}
}
设备初始化
intdevices_init(void)
{
char*s;
#ifndefCONFIG_ARM/*alreadyrelocatedforcurrentARMimplementation*/
DECLARE_GLOBAL_DATA_PTR;
ulongrelocation_offset=gd->reloc_off;
inti;
/*relocatedevicenamepointers*/
for(i=0;i<(sizeof(stdio_names)/sizeof(char*));++i){
stdio_names[i]=(char*)(((ulong)stdio_names[i])+
relocation_offset);
}
#endif
/*Initializethelist*/
devlist=ListCreate(sizeof(device_t));
if(devlist==NULL){
eputs("Cannotinitializethelistofdevices!
\n");
return-1;
}
#ifdefined(CONFIG_HARD_I2C)||defined(CONFIG_SOFT_I2C)
i2c_init(CFG_I2C_SPEED,CFG_I2C_SLAVE);
#endif
#ifdefCONFIG_LCD
drv_lcd_init();
#endif
#ifdefined(CONFIG_VIDEO)||defined(CONFIG_CFB_CONSOLE)
if((s=getenv("sm501mode"))!
=NULL)
{
sm501mode=simple_strtoul(s,NULL,10);
if(!
(sm501mode>=1&&sm501mode<=13))
{
printf("sm501modeerror,setsm501modeto1!
\n");
sm501mode=1;
}
}
if((s=getenv("sm501bpp"))!
=NULL)
{
sm501bpp=simple_strtoul(s,NULL,10);
if(sm501mode!
=16)
{
printf("sm501bpperror,only16bppvalid,setsm501bppto16!
\n");
sm501bpp=16;
}
}
drv_video_init();
#endif
#ifdefCONFIG_KEYBOARD
//drv_keyboard_init();
#endif
#ifdefCONFIG_LOGBUFFER
drv_logbuff_init();
#endif
drv_system_init();
return(0);
}
这里要创建一个设备列表,把一些设备放入这个表中,初始化i2c总线,设置sm501显卡,
初始化视频设备,初始化并注册个别系统设备。
voidjumptable_init(void)
{
DECLARE_GLOBAL_DATA_PTR;
inti;
gd->jt=(void**)malloc(XF_MAX*sizeof(void*));
for(i=0;igd->jt[i]=(void*)dummy;
gd->jt[XF_get_version]=(void*)get_version;
gd->jt[XF_malloc]=(void*)malloc;
gd->jt[XF_free]=(void*)free;
gd->jt[XF_get_timer]=(void*)get_timer;
gd->jt[XF_udelay]=(void*)udelay;
#ifdefined(CONFIG_I386)||defined(CONFIG_PPC)
gd->jt[XF_install_hdlr]=(void*)irq_install_handler;
gd->jt[XF_free_hdlr]=(void*)irq_free_handler;
#endif/*I386||PPC*/
#if(CONFIG_COMMANDS&CFG_CMD_I2C)
gd->jt[XF_i2c_write]=(void*)i2c_write;
gd->jt[XF_i2c_read]=(void*)i2c_read;
#endif/*CFG_CMD_I2C*/
}
初始化跳转表,主要是保存了一些系统函数的指针。
便于以后引用
然后对控制台进行全面的初始化
console_init_r();/*fullyinitconsoleasadevice*/
主要是初始化控制台输入输出设备
接下来开中断
/*enableexceptions*/
enable_interrupts();
初始化网卡设备等
/*Performnetworkcardinitialisationifnecessary*/
#ifdefCONFIG_DRIVER_DM9000
DM9000_get_enetaddr(gd->bd->bi_enetaddr);
#endif
最后进入主循环
/*main_loop()canreturntoretryautoboot,ifsojustrunitagain.*/
for(;;){
main_loop();
}
在这个循环中,将检测是自动启动还是命令行起动。
起动后,uboot的stage2也就结束了
Uboot中的每一个命令都由一个宏进行声明,这宏在文件include/command.h中定义
#defineU_BOOT_CMD(name,maxargs,rep,cmd,usage,help)\
cmd_tbl_t__u_boot_cmd_##nameStruct_Section={#name,maxargs,rep,cmd,usage,help}
如i2c设备操作中的某个命令的声明如下
U_BOOT_CMD(
imd,4,1,do_i2c_md,\
"imd-i2cmemorydisplay\n",\
"chipaddress[.0,.1,.2][#ofobjects]\n-i2cmemorydisplay\n"\
);
这个声明里初始化了cmd_tbl_t结构
structcmd_tbl_s{
char*name;/*CommandName*/
intmaxargs;/*maximumnumber