先总述:第一阶段cpu/arm920t/start.S和board/smdk2410/lowlevel_init.S进行初始化,再跳到第二阶段的入口点lib_arm/board.c中的start_armboot函数。
第二阶段start_armboot函数需要用到include/asm-arm/global_data.h中结构体gd_t; include/asm-arm.u/u-boot.h中结构体bd_t。gd_t,bd_t结构体中的信息来设置标记列表,U-Boot启动内核时给内核传递参数。逐个调用init_sequence数组中的初始化函数。最后执行 common/main.c中的 main_loop函数
U-Boot启动第二阶段代码分析

重要数据结构
(1)gd_t结构体
U-Boot使用了一个结构体gd_t来存储全局数据区的数据,这个结构体在include/asm-arm/global_data.h中定义如下:

typedef  struct     global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
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; /* jump table */
} gd_t;
   U-Boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址:

define DECLARE_GLOBAL_DATA_PTR (register volatile gd_t *)gd asm (“r8”)
DECLARE_GLOBAL_DATA_PTR定义一个gd_t全局数据结构的指针,这个指针存放在指定的寄存器r8中。这个声明也避免编译器把r8分配给其它的变量。任何想要访问全局数据区的代码,只要代码开头加入“DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。
根据U-Boot内存使用图中可以计算gd的值:
gd = TEXT_BASE -CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)

(2)bd_t结构体
bd_t在include/asm-arm.u/u-boot.h中定义如下:

typedef struct bd_info {
int bi_baudrate; /* 串口通讯波特率 */
unsigned long bi_ip_addr; /* IP 地址*/
struct environment_s *bi_env; /* 环境变量开始地址 */
ulong bi_arch_number; /* 开发板的机器码 */
ulong bi_boot_params; /* 内核参数的开始地址 */
struct /* RAM配置信息 */
{
ulong start;
ulong size;
}bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;

U-Boot启动内核时要给内核传递参数,这时就要使用gd_t,bd_t结构体中的信息来设置标记列表。第一阶段调用start_armboot指向C语言执行代码区,首先它要从内存上的重定位数据获得不完全配置的全局数据表格和板级信息表格,即获得gd_t和bd_t,这两个类型变量记录了刚启动时的信息,并将要记录作为引导内核和文件系统的参数,如bootargs等等,并且将来还会在启动内核时,由uboot交由kernel时会有所用。

(3)init_sequence数组
U-Boot使用一个数组init_sequence来存储对于大多数开发板都要执行的初始化函数的函数指针。init_sequence数组中有较多的编译选项,去掉编译选项后init_sequence数组如下所示:

init_fnc_t *init_sequence[] = {
board_init, /*开发板相关的配置--board/samsung/mini2440/mini2440.c */
timer_init, /* 时钟初始化-- cpu/arm920t/s3c24x0/timer.c */
env_init, /*初始化环境变量--common/env_flash.c 或common/env_nand.c*/
init_baudrate, /*初始化波特率-- lib_arm/board.c */
serial_init, /* 串口初始化-- drivers/serial/serial_s3c24x0.c */
console_init_f, /* 控制通讯台初始化阶段1-- common/console.c */
display_banner, /*打印U-Boot版本、编译的时间-- gedit lib_arm/board.c */
dram_init, /*配置可用的RAM-- board/samsung/mini2440/mini2440.c */
display_dram_config, /* 显示RAM大小-- lib_arm/board.c */
NULL,
};

其中的board_init函数在board/smdk2410/smdk2410.c中定义,该函数设置了MPLLCOM,UPLLCON,以及一些GPIO寄存器的值,还设置了U-Boot机器码和内核启动参数地址 :

/* smdk2410开发板的机器码 */

gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;

/* 内核启动参数地址 */

gd->bd->bi_boot_params = 0x30000100;

dram_init函数在board/smdk2410/smdk2410.c中定义int
检测系统内存映射,特定的开发板其内存映射是明确的,这些参数在后面传递给内核。

dram_init (void)

{
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
return 0;

}

分析完上述结构,下析start_armboot函数:

void start_armboot (void)
{
/* 计算全局数据结构的地址gd */
gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
… …
memset ((void*)gd, 0, sizeof (gd_t));
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
gd->flags |= GD_FLG_RELOC;
monitor_flash_len = _bss_start - _armboot_start;
/* 逐个调用init_sequence数组中的初始化函数 */
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/* armboot_start 在cpu/arm920t/start.S 中被初始化为u-boot.lds连接脚本中的_start */
mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,
CONFIG_SYS_MALLOC_LEN);
/* NOR Flash初始化 */
#ifndef CONFIG_SYS_NO_FLASH
/* configure available FLASH banks */
display_flash_config (flash_init ());
#endif /* CONFIG_SYS_NO_FLASH */

… …
/* NAND Flash 初始化*/
#if defined(CONFIG_CMD_NAND)
puts ("NAND: ");
nand_init(); /* go init the NAND */
#endif
… …
/*配置环境变量,重新定位 */
env_relocate ();
… …
/* 从环境变量中获取IP地址 */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
stdio_init (); /* get the devices list going. */
jumptable_init ();
… …
console_init_r (); /* fully init console as a device */
… …
/* enable exceptions */
enable_interrupts ();

#ifdef CONFIG_USB_DEVICE
usb_init_slave();
#endif

/* 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
… …
/* 网卡初始化 */
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
puts ("Net: ");
#endif
eth_initialize(gd->bd);
… …
#endif

/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();//main_loop!!
}
/* NOTREACHED - no way out of command loop except booting */
}

main_loop函数在common/main.c中定义。

更多相关文章

  1. 字符串处理函数strcat和strtok
  2. Linux的时间函数(转载)
  3. 【linux】下的mkfifo 命令 和【C语言】中的mkfifo函数
  4. linux c (4) 进程终止-exit和_exit函数
  5. 看谁能找出bug★☆open函数总是返回-1
  6. [置顶] Linux C编程--string.h函数解析
  7. linux 下的时间获取函数
  8. linux c 网络编程, 常用网络函数,范例
  9. brk()和sbrk()函数的使用

随机推荐

  1. 真正解决方案:Cannot resolve symbol 'xxx
  2. Android 简介:Android SDK 和开发框架简介
  3. android 屏幕方向切换 锁定方向
  4. Caused by: java.lang.IllegalStateExcep
  5. Android 基础 源码 工具
  6. Android 呼吸灯流程分析(一)
  7. android中校验email是否合法
  8. Android中设置控件透明度的方法
  9. 封装的一个android底部操作弹出窗
  10. Android文档阅读03—开发工具