分类: LINUX
2008-09-18 13:53:45
U-BOOT start_armboot浅析
interrupt_init()函数定义于cpu/arm920t/s3c24x0/interrupts.c第55行:
U-Boot启动分析C语言部分(一)
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
#if 0
unsigned long cpu_clk; /* CPU clock in Hz! */
unsigned long bus_clk;
unsigned long ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table */
} gd_t;
if ((*init_fnc_ptr)() != 0) {
}
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
dram_init, /* configure available RAM banks */
display_dram_config,
#if defined(CONFIG_VCMA9) || defined (CONFIG_CMC_PU2)
checkboard,
#endif
NULL,
};
接上回,我们依次来看看init_sequence[]数组当中的各个元素。
int board_init (void)
{
DECLARE_GLOBAL_DATA_PTR;
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
/* to reduce PLL lock time, adjust the LOCKTIME register */
clk_power->LOCKTIME = 0xFFFFFF;
/* configure MPLL */
clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);
/* some delay between MPLL and UPLL */
delay (4000);
/* configure UPLL */
clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);
/* some delay between MPLL and UPLL */
delay (8000);
/* set up the I/O ports */
gpio->GPACON = 0x007FFFFF;
gpio->GPBCON = 0x00044555;
gpio->GPBUP = 0x000007FF;
gpio->GPCCON = 0xAAAAAAAA;
gpio->GPCUP = 0x0000FFFF;
gpio->GPDCON = 0xAAAAAAAA;
gpio->GPDUP = 0x0000FFFF;
gpio->GPECON = 0xAAAAAAAA;
gpio->GPEUP = 0x0000FFFF;
gpio->GPFCON = 0x000055AA;
gpio->GPFUP = 0x000000FF;
gpio->GPGCON = 0xFF95FFBA;
gpio->GPGUP = 0x0000FFFF;
gpio->GPHCON = 0x002AFAAA;
gpio->GPHUP = 0x000007FF;
/* arch number of SMDK2410-Board */
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100;
icache_enable();
dcache_enable();
return 0;
}int interrupt_init (void)
{
S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();
/* use PWM Timer 4 because it has no output */
/* prescaler for Timer 4 is 16 */
timers->TCFG0 = 0x0f00;
if (timer_load_val == 0)
{
/*
* for 10 ms clock period @ PCLK with 4 bit divider = 1/2
* (default) and prescaler = 16. Should be 10390
* @33.25MHz and 15625 @ 50 MHz
*/
timer_load_val = get_PCLK()/(2 * 16 * 100);
}
/* load value for 10 ms timeout */
lastdec = timers->TCNTB4 = timer_load_val;
/* auto load, manual update of Timer 4 */
timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;
/* auto load, start Timer 4 */
timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
timestamp = 0;
return (0);
}
下内容来自笔者在中国Linux论坛Linux嵌入技术讨论区的张贴:x`"m ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 Uw/%#* --------------------------------------------------------------------------------" aaronwong: u-boot中代码的疑问(_armboot_start与_start)?12Gm ---------------------------=j 我使用的是u-boot-1.3.0-rc2。在cpu/pxa/start.S中,有如下的标号定义: w' _TEXT_BASE: 7B .word TEXT_BASE /*uboot映像在SDRAM中的重定位地址,我设置为0xa170 0000 */ k&BnQf ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 A% .globl _armboot_start /'b&% _armboot_start: 50m B8 .word _start /*_start是程序入口,链接完毕它的值应该是0xa170 0000=TEXT_BASE*/ 2inlX /* 这句话的意思应该是在_armboot_start标号处,保存了_start的值,也就是说,_armboot_start是存放_start的地址,该地址对应的存储单元内容是0xa170 0000*/ ~1 /* ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 [-S( * These are defined in the board-specific linker script. 下面的定义与上面应该是一个意思。 y1sDB */ ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 ?Ud=F} .globl _bss_start W8 _bss_start: 4V1kfj .word __bss_start 5` ====================== XM 按照上面的理解,__bss_start是uboot 的bss段起始地址,那么uboot映像的大小就是__bss_start - _start;在relocate代码段中计算uboot的大小时,也体现了这一点。 fHK'f0 实际上,_armboot_start并没有实际意义,它只是在"ldr r2, _armboot_start"中用來寻址_start的值而已,_bss_start也是一样的道理,真正有意义的应该是_start和 __bss_start本身。 ;{I 但是,令我不解的是,在C入口函数start_armboot()中(对应文件为lib_arm/board.c),有如下代码: =-yz! void start_armboot (void) 6#F[C { ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 dCb ......... *= gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); //第一句话 7 .......... xfw,, monitor_flash_len = _bss_start - _armboot_start; //第二句话 =r1m, ............... =cN^x+ mem_malloc_init (_armboot_start - CFG_MALLOC_LEN); //第三句话 W .......... ? } ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 u ============================================== v#HG/ 按 照上面的理解,_armboot_start与_bss_start都是没有实际意义的,它们只是一个地址,有实际意义的是地址中的内容_start和 __bss_start(虽然也还是地址)。象第一句话,其“意图”很明显,是把gd作为全局数据结构体的指针,并初始化为“SDRAM中的uboot起 始地址(即TEXT_BASE)-CFG_MALLOC_LEN-全局数据结构体大小”。 要 实现这个“意图”,应该是写成:gd = (gd_t*)(_start - CFG_MALLOC_LEN - sizeof(gd_t));或者gd = (gd_t*)(TEXT_BASE- CFG_MALLOC_LEN - sizeof(gd_t));才对阿?用_armboot_start来作运算应该是没有任何意义才对!? #0gYd? 第二句话也是一样的道理,它的意图是要计算u-boot映像的大小,应该写成__bss_start - _start才对阿? @`PVq 我使用readelf工具查看编译所得到的uboot映像文件得到信息如下: NK7,G [aaronwong@localhost build]$ readelf -s u-boot|grep _start G 1018: a1700048 0 NOTYPE GLOBAL DEFAULT 1 _bss_start !Qgo} 1083: a1700044 0 NOTYPE GLOBAL DEFAULT 1 _armboot_start W 1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start b9> 1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start m[ 上面我删除了与该讨论无关的包含“_start""t的标号信息。 &:gP 显 然,我前面的理解应该是正确的(_start=TEXT_BASE=0xa170 0000)。那么u-boot源代码中的monitor_flash_len=_bss_start - _armboot_start=0xa1700048 - 0xa1700044 = 4,有什么意义?? p 迷茫中,期盼大虾指点迷津,谢谢~!!! < ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 M6fJvX --------------------------------------------------------------------------------%:#- eltshan: [Re: aaronwong]9o22#P -----------------Zi 1018: a1700048 0 NOTYPE GLOBAL DEFAULT 1 _bss_start D3dY( 1083: a1700044 0 NOTYPE GLOBAL DEFAULT 1 _armboot_start _mAq> 1142: a1700000 0 NOTYPE GLOBAL DEFAULT 1 _start QNr+Pc 1197: a171b070 0 NOTYPE GLOBAL DEFAULT ABS __bss_start += ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 E-Y> 我想: FCNh{M _start所在的地址是a1700000, eHEsMt _armboot_start 所在的地址是a1700044, ? 那么 根据这句: 7.Iy _armboot_start: .word _start } 所以_armboot_start的值应该是a1700000 w34ok: ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 gh\ 所以 ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 ka monitor_flash_len = _bss_start - _armboot_start = a171b070 - a1700000 = 1b070 ~=w 而不是你说的 = 4 FYxAA@ ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 *E*4z 以上个人意见.Q~St ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 ^p`Sc --------------------------------------------------------------------------------Nkh aaronwong: [Re: eltshan];DQlk5 -------------------p4 谢 谢,eltshan!你的理解是正确的,不过我看了之后还是没能想得很明白,因为我在想,按你所说,那么_start的值应该是多少呢?难道是“b reset”这条指令的机器码?所以我对ELF格式的u-boot映像文件作了反汇编,分析之后终于找到了症结所在。以下是部分分析过程,首先是反汇编: 24 arm-iwmmxt-linux-gnueabi-objectdump -D u-boot > u-boot.s *{|(q# 并提取了monitor_flash_len = _bss_start - _armboot_start;这条语句相关的反汇编代码如下: \ ============================== ^o#c7 a1700044 <_armboot_start>: b? a1700044: a1700000 .word 0xa1700000 e{Zn ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 > a1700048 <_bss_start>: l#"{w a1700048: a171b070 .word 0xa171b070 U3sK ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 !. a171b070 a171b070: 00000000 .word 0x00000000 Q^$ ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 MeE9 ..... m4 a1700f40: e59f41d0 ldr r4, [pc, #464] ; a1701118 //r4=[a1701118]=a1700044 2/NL_; ..... EW0Th a1700f7c: e59f3198 ldr r3, [pc, #408] ; a170111c //r3=[a1700044]=a1700048 D a1700f80: e5942000 ldr r2, [r4] 2/0N0 //r2=[a1700044]=a1700000 mV a1700f84: e59f4194 ldr r4, [pc, #404] ; a1701120 //r4=[a1701120]=a1719d24 #Bnq a1700f88: e5933000 ldr r3, [r3] * //r3=[a1700048]=a171b070 a1700f8c: e0623003 rsb r3, r2, r3 $e8I: //r3= r3-r2 = a171b070-a1700000 = 1b070; q| a1700f90: e59f218c ldr r2, [pc, #396] ; a1701124 //r2=[a1701124]=a171b070 } a1700f94: e5823000 str r3, [r2] h`lC] //monitor_flash_len=[r2]=r3=1b070 mJT:HJ ...... =op4 ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 9 a1701118: a1700044 .word 0xa1700044 Z0 a170111c: a1700048 .word 0xa1700048 fr3g( a1701120: a1719d24 .word 0xa1719d24 EpcDe a1701124: a171b070 .word 0xa171b070 XT& ======================================== : 上面//是我自己的注释。这表明,你的理解的确是正确的。 :}6 经过这个过程之后,我终于认识到自己的误解在哪里了。原来,我是把"汇编语言中LDR伪指令对符号的引用"与"C语言中对汇编程序中符号/常量/变量的引用"搞混淆了。我想说明以下几点:`[I ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 WY (1) readelf以及u-boot.map和System.map所给出的符号表中符号的值,实际上是表示符号所在的地址,而不是指符号本身的值。 E?F'R ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 u (2) 汇编语言中没有指针的概念,因此对符号的引用是"赤裸裸"的。例如: M" ========== wM .globl _armboot_start J% _armboot_start: .word _start d_ ldr r2, _armboot_start Kf ========== , 实际上反汇编以后是: 466 ============ ;/g-oE a1700044 <_armboot_start>: }b a1700044: a1700000 .word 0xa1700000 R a1700074: e51f2038 ldr r2, [pc, #-56] ; a1700044 <_armboot_start> b b}/4 ============ [7A 也就是说,_armboot_start是一个地址0xa1700044,其中的内容是0xa1700000,上面对_armboot_start的引用是直接将其替换为其表示的地址0xa1700044,而非其中的内容0xa1700000。这就是"赤裸裸"的引用。 m ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 )bR; (3) C语言则不同,对变量/符号/常量的引用必须要通过地址来寻址,不管是全局变量还是局部变量,不同的是局部变量在生命期结束后,所占的地址空间会被释放而 已。即使是函数调用时的参数传递,虽然是将实参的值"拷贝"给形参,但"拷贝"的过程也是通过实参和形参的地址来对两者进行访问的。 p 所 以,在C语言中的 "monitor_flash_len = _bss_start - _armboot_start" 这句话中对_armboot_star的引用,实际上是把它用作了指针,把它作为访问对象的地址来使用,通过这个地址即a1700044 来访问对应存储空间所存放的内容亦即0xa1700000,_bss_start也是同样的道理。所以这句话实际上是monitor_flash_len =[a1700048]-[0xa1700044]=a171b070-a1700000 = 1b070,这样就得到了正确的结果。 eNe#ij ©南开大学嵌入式系统与信息安全实验室学术论坛 -- 我的论坛,我的天地 MnK-47 现 在,我们再回答最前面的问题:_start的值是什么?_start表示地址0xa1700000 ,在汇编语言中,对_start的"绝对引用"(这里是与用相对寻址进行跳转进行区别)就是将其替换为0xa1700000,但其中存放的内容的的确确就 是"b reset"这条指令的机器码,所以如果在C语言中引用_start,得到的结果反而就是这个指令的机器码了。其实这个问题很简单,只是和C语言的引用搅 在一起,一些概念被偷换了而已。 * |
由汇编部分转入C语言后第一个执行这个文件中的start_armboot ()函数。
部分代码分析如下:
typedef int (init_fnc_t) (void); // 定义函数类型
// 下面为初始化函数定义
init_fnc_t *init_sequence[] = {
cpu_init, // cp/pxa/cpu.c文件, 执行CPU相关的初始化.
board_init, // board/psbec270/board.c文件, 执行board相关的初始化.
interrupt_init, // cp/pxa/interrupt.c文件, 中断初始化, 一般不需要使用中断.
env_init, // 环境变量初始化
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
dram_init, /* configure available RAM banks */
display_dram_config,
#if defined(CONFIG_VCMA9)
checkboard,
#endif
NULL,
};
// 在start_armboot()函数中实现如下代码, 用于执行上面定义的初始函数.
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
接下来就是一些系统中用到的环境,变量等初始化,最后进入主循环, 下面是具体分析:
typedef int (init_fnc_t) (void);
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
dram_init, /* configure available RAM banks */
display_dram_config,
NULL,
};
// 全局数据结构信息
typedef struct global_data {
bd_t *bd; // 开发板相关参数
unsigned long flags;
unsigned long baudrate; // 串行口通讯速率
unsigned long have_console; // console_init_f()中使用控制台
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
void **jt; // jumptable_init()初始化
} gd_t;
// 开发板相关的信息
typedef struct bd_info {
int bi_baudrate; // serial通讯接口的速率
unsigned long bi_ip_addr; // 本机IP地址
unsigned char bi_enetaddr[6]; // MAC地址
struct environment_s *bi_env; // 环境变量
ulong bi_arch_number; // 开发板ID
/*
该变量标识每一种开发板相关的ID号, 对于本系统来说:
gd->bd->bi_arch_number = MACH_TYPE_MAINSTONE;
该值将传递给内核, 如果这个参数与内核配置的不相同, 那么内核启动解压缩完成后将出现”Error: a”错误, 提示用户这个是体系结构参数传递的不正确. 由于本开发板内核是从Intel的mainstone开发板内核修改而来, 内部的配置都是使用的MAINSTONE开发板的参数, 故这里将ARCH设置为MACH_TYPE_MAINSTONE.
*/
ulong bi_boot_params; // Uboot传递给linux内核的参数保存地址
/*
该变量在board/psbec270/psbec270.c中的int board_init(void)中赋值, 这个值的定义是:
gd->bd->bi_boot_params = 0xA0000100;
该变量保存了Uboot传递给linux的参数的地址, 在linux的引导过程中,
head.s文件中没有对传递进来的参数进行处理, 在init/main.c文件中的
start_kernel函数中, 进行解析.
*/
struct // RAM configuration
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
/*
SDram设置, 可以存在多个bank, 由宏定义
CONFIG_NR_DRAM_BANKS决定.
board/psbec270/psbec270.c文件中dram_init()执行RAM初始化
int dram_init(void)
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
*/
} bd_t;
void start_armboot (void)
{
DECLARE_GLOBAL_DATA_PTR;
// #define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
ulong size;
init_fnc_t **init_fnc_ptr;
char *s;
// gd分配内存在空闲区(直接地址引用)
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__("": : :"memory");
// 给gd分配内存, 这里是在uboot使用的前面, 属于空闲内存.
memset ((void*)gd, 0, sizeof (gd_t));
// bd分配内存在空闲区, gd的低端(直接地址引用)
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start - _armboot_start;// 受监控flash空间大小
// 初始化函数, 见上面定义的函数数组, 其中有几个是开发板相关的.
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/* configure available FLASH banks */
size = flash_init (); // flash初始化
display_flash_config (size);
/* armboot_start is defined in the board-specific linker script */
// malloc使用的内存空间
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
// 如果有nandflash的话就在下面的代码中进行初始化.
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
puts ("NAND:");
nand_init(); /* go init the NAND */
#endif
env_relocate (); // 执行环境初始化
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"); // 环境变量中获得本机IP
// 获得Mac地址
{
int i;
ulong reg;
char *s, *e;
uchar tmp[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;
}
}
devices_init (); // 设备初始化
jumptable_init (); // 给gd->jt分配内存, 然后加入相关的执行函数
console_init_r (); // fully init console as a device
enable_interrupts ();
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
if ((s = getenv ("bootfile")) != NULL) {
copy_filename (BootFile, s, sizeof (BootFile));
}
board_late_init ();
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) { // 进入主循环后等待命令
main_loop ();
}
/* NOTREACHED - no way out of command loop except booting */
}