start_armboot解析
start_armboot函数在lib_arm/board.c文件里,除了函数本身,还有一些用到了的定义和宏。
S5PV210-uboot解析-start.S解析(二)-gd_t和init_sequence函数.note
typedef
int
(init_fnc_t) (
void
);
这里定义的是一个
函数类型
,不是函数指针。
下面是正式代码,我将没有用到的地方都去掉了:
void
start_armboot (
void
)
{
init_fnc_t **init_fnc_ptr;
这里定义的是函数类型的二重指针,也可以看成是函数类型的指针数组。
char
*s;
int
mmc_exist = 0;
/* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
ulong gd_base;
gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE -
sizeof
(gd_t);
gd_base的地址就是 0x33e0_0000 + 2*1024*1024 - (0x4000 + 896*1024) - (512*1024) - (36 + 46 )
就是 基地址 + 2MB -(16KB + 896KB) - (512KB) - (82B)
也就是uboot的基地址+2MB-1424KB,相当于uboot基地址后面623KB多点的位置。
这之间的内存排布就是
gd = (gd_t*)gd_base;
#else
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN -
sizeof
(gd_t));
#endif
/* compiler optimization barrier needed for GCC >= 3.4 */
__asm__ __volatile__(
""
: : :
"memory"
);
这句话是调用汇编的指令,为了防止高版本的GCC的优化造成错误。__asm__ 表示后面的代码为内嵌汇编,__volatile__防止编译器优化代码,括号内的是汇编指令。内嵌汇编语法如下:
__asm__(汇编语句模板: 输出部分: 输入部分: 破坏描述部分)
memset
((
void
*)gd, 0,
sizeof
(gd_t));
gd->bd = (bd_t*)((
char
*)gd -
sizeof
(bd_t));
memset
(gd->bd, 0,
sizeof
(bd_t));
memset初始化指针。
monitor_flash_len = _bss_start - _armboot_start;
for
(init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if
((*init_fnc_ptr)() != 0) {
hang ();
}
}
这个for循环用到了之前定义的函数类型的二重指针(相当于指针数组),遍历init_sequence数组内的元素,即运行每一个函数进行相关的初始化操作,这里面的函数都是初始化相关的。这里操作的时候先赋值init_fnc_ptr = init_sequence 循环的时候操作init_fnc_ptr 而不对init_sequence 进行操作。如果某一个函数初始化失败了,没有返回0,那么就进入hang函数挂起。hang函数内部就是一个死循环。如果uboot进入到hang函数了,只能重启。
#ifndef CFG_NO_FLASH
/* configure available FLASH banks */
size = flash_init ();
display_flash_config (size);
#endif /* CFG_NO_FLASH */
这里是属于代码移植的遗留问题,在210板子中是没有norFlash的,但是这里还是保留了这段代码,而进行实际测试发现如果定义CFG_NO_FLASH的话会产生很多错误,所以没有定义CFG_NO_FLASH。这段代码只是在串口输出了Flash: 8 MB,没有其他的作用。
#ifdef CONFIG_VFD
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for VFD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = vfd_setmem (addr);
gd->fb_base = addr;
#endif /* CONFIG_VFD */
#ifdef CONFIG_LCD
/* board init may have inited fb_base */
if
(!gd->fb_base) {
# ifndef PAGE_SIZE
# define PAGE_SIZE 4096
# endif
/*
* reserve memory for LCD display (always full pages)
*/
/* bss_end is defined in the board-specific linker script */
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
size = lcd_setmem (addr);
gd->fb_base = addr;
}
#endif /* CONFIG_LCD */
CONFIG_VFD和CONFIG_LCD都是和显示相关的,是uboot中自带的LCD显示的软件架构,但这里这两个宏都没有被定义,因为在后面我们使用了自己的LCD显示函数。
/* armboot_start is defined in the board-specific linker script */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
mem_malloc_init (CFG_UBOOT_BASE + CFG_UBOOT_SIZE - CFG_MALLOC_LEN - CFG_STACK_SIZE);
#else
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
#endif
//******************************//
// Board Specific
// #if defined(CONFIG_SMDKXXXX)
//******************************//
接下来的一些代码都是和指定的板子相关的,通过定义宏来选择。
#if defined(
CONFIG_X210
)
#if defined(
CONFIG_GENERIC_MMC
)
puts
(
"SD/MMC: "
);
mmc_exist = mmc_initialize(gd->bd);
if
(mmc_exist != 0)
{
puts
(
"0 MB\n"
);
初始化MMC,其实是初始化SoC内部的MMC控制器。调用
mmc_initialize函数。
__attribute__ 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。
weak 和 alias 分别是两个属性。weak 使得
board_mmc_init 这个符号在目标文件中作为 weak symbol 而不是 global symbol。
而 alias 则使
board_mmc_init 是
__def_mmc_init 的一个别名,
__def_mmc_init 和
board_mmc_init 必须在同一个编译单元中定义,否则会编译出错。
给函数加上weak属性时,即使函数没定义,函数被调用也可以编译成功。
当有两个函数同名时,则使用强符号(也叫全局符号,即没有加weak的函数)来代替弱符号(加weak的函数)。
#ifdef CONFIG_CHECK_X210CV3
check_flash_flag=0;
//check inand error!
#endif
}
#ifdef CONFIG_CHECK_X210CV3
else
{
check_flash_flag=1;
//check inand ok!
}
#endif
#endif
#if defined(CONFIG_MTD_ONENAND)
puts
(
"OneNAND: "
);
onenand_init();
/*setenv("bootcmd", "onenand read c0008000 80000 380000;bootm c0008000");*/
#else
//puts("OneNAND: (FSR layer enabled)\n");
#endif
#if defined(CONFIG_CMD_NAND)
puts
(
"NAND: "
);
nand_init();
#endif
#endif /* CONFIG_X210 */
/* initialize environment */
env_relocate ();
重定位环境变量,因为环境变量最开始都烧录在SD卡中,这里将SD卡中的环境变量读取到DDR中。
/* IP Address */
gd->bd->bi_ip_addr = getenv_IPaddr (
"ipaddr"
);
/* MAC Address */
{
int
i;
ulong reg;
char
*s, *e;
char
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;
}
}
从环境变量中获取IP地址和MAC地址,
getenv_IPaddr 是调用
getenv 函数来获取环境变量中的字符串格式的IP地址,中间还用
string_to_ip 来将字符串格式的IP地址转换成字符串格式的点分十进制格式。
devices_init ();
/* get the devices list going. */
设备初始化函数,放在这里面初始化的设备都是驱动设备,作用就是集中执行各种硬件驱动的初始化函数。
jumptable_init ();
跳转表。应该是要实现函数指针到具体函数的映射,但是查找发现跳转表只是被赋值没有被引用,因此在uboot中没有使用。
#if !defined(CONFIG_SMDK6442)
console_init_r ();
/* fully init console as a device */
#endif
/* enable exceptions */
enable_interrupts ();
中断使能,实际是空函数。没有定义相关的宏,#else分支是空函数。
/* Initialize from environment */
if
((s =
getenv
(
"loadaddr"
)) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
#if defined(CONFIG_CMD_NET)
if
((s =
getenv
(
"bootfile"
)) != NULL) {
copy_filename (BootFile, s,
sizeof
(BootFile));
}
#endif
这两个环境变量是和内核启动有关的,启动linux内核时会参考这两个变量。
#ifdef BOARD_LATE_INIT
board_late_init ();
#endif
开发板晚期初始化的函数,在x210中,这个函数是空的。
****************lxg added**************/
#ifdef CONFIG_MPAD
extern
int
x210_preboot_init(
void
);
x210_preboot_init();
#endif
x210启动起来之前的一些初始化,主要是LCD相关。还有屏幕上的logo显示。
/****************end**********************/
/* check menukey to update from sd */
extern
void
update_all(
void
);
if
(check_menu_update_from_sd()==0)
//update mode
{
puts
(
"[LEFT DOWN] update mode\n"
);
run_command(
"fdisk -c 0"
,0);
update_all();
}
else
puts
(
"[LEFT UP] boot mode\n"
);
这里设置了一个自动更新的功能,开机按下LEFT按键会进入 update mode,之后uboot会从SD卡中读取镜像烧录到iNand中。不按LEFT会进入boot mode,即正常启动。
/* main_loop() can return to retry autoboot, if so just run it again. */
for
(;;) {
main_loop ();
}
uboot启动的最后阶段,main_loop是一个死循环,如果开机倒计时不打断会自动引导内核加载系统,如果打断会进入uboot的命令行。
/* NOTREACHED - no way out of command loop except booting */
}
到这里uboot启动的第二阶段也分析完了。在start_armboot函数中,主要就是对开发板的硬件做了初始化,还有软件的一些数据结构做了初始化。
主要的函数及功能:
init_sequence
cpu_init 空的
board_init 网卡、机器码、内存传参地址
dm9000_pre_init 网卡
gd->bd->bi_arch_number 机器码
gd->bd->bi_boot_params 内存传参地址
interrupt_init 定时器
env_init
init_baudrate gd数据结构中波特率
serial_init 空的
console_init_f 空的
display_banner 打印启动信息
print_cpuinfo 打印CPU时钟设置信息
checkboard 检验开发板名字
dram_init gd数据结构中DDR信息
display_dram_config 打印DDR配置信息表
mem_malloc_init 初始化uboot自己维护的堆管理器的内存
mmc_initialize inand/SD卡的SoC控制器和卡的初始化
env_relocate 环境变量重定位
gd->bd->bi_ip_addr gd数据结构赋值
gd->bd->bi_enetaddr gd数据结构赋值
devices_init 空的
jumptable_init 不用关注的
console_init_r 真正的控制台初始化
enable_interrupts 空的
loadaddr、bootfile 环境变量读出初始化全局变量
board_late_init 空的
eth_initialize 空的
x210_preboot_init LCD初始化和显示logo
check_menu_update_from_sd 检查自动更新
main_loop 主循环