u-boot启动分析
1.完成配置任务后,再一次make
$(obj)u-boot: depend \
$(SUBDIR_TOOLS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
//start.o是第一个依赖文件,因此先分析start.s
OBJS = $(CPUDIR)/start.o
//u-boot.lds作为最后一个依赖项,负责链接任务
2.分析/arch/arm/cpu/armv7/start.s
/* Set stackpointer in internal RAM to call board_init_f */
call_board_init_f:
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp,
ldr r0,=0x00000000
bl relocate_data
ldr r0,=0x00000000
bl board_init_f
2.函数board_init_f在/arch/arm/lib/board.c,分析board_init_f //CONFIG_SYS_TEXT_BASE = 0xF0000000 (in board/broadcom/bcm95340x)
board_init_f
bootstage_mark_name //进行标记,若失败,可定位错误
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07) //给gd分配空间
/*
给gd赋值,用于存储各种资源信息,如串口等
*/
/*以下为一系列的初始化函数*/
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
展开可得:
...
serial_init
...
display_banner
debug("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",_TEXT_BASE,_bss_start_ofs + _TEXT_BASE, _bss_end_ofs + _TEXT_BASE); //U-Boot code: F0000000 -> F0066A10 BSS: -> F0073048
...
...
...
dram_init();
ddr_init2();
printf("DEV ID = 0x%x\n", dev_id);
printf("SKU ID = 0x%x\n", sku_id);
printf("DDR type: DDR%d\n", (ddr_type == 1)? 3 : 4);
printf("MEMC 0 DDR speed = %dMHz\n", ddr_clk);
printf("PHY revision version: 0x%08x\n", val); ///* Wait for DDR PHY up */
printf("ddr_init2: Calling soc_and28_shmoo_dram_info_set\n");
soc_and28_shmoo_dram_info_set();
printf("ddr_init2: Calling soc_and28_shmoo_phy_init\n");
soc_and28_shmoo_phy_init();
switch(ddr_clk) {
ddr_init_regs
}
printf("DDR Interface Ready\n");
/*以上为一系列的初始化函数*/
dram_init_banksize(); /* Ram ist board specific, so move it to board code ... */
//in /arch/arm/lib
void dram_init_banksize(void)
__attribute__((weak, alias("__dram_init_banksize")));
void __dram_init_banksize(void)
{
gd->bd->bi_dram[0].start = CONFIG_SYS_SDRAM_BASE;
gd->bd->bi_dram[0].size = gd->ram_size;
}
display_dram_config();
relocate_code();
board_init_r
bootstage_mark_name
board_init //芯片相关初始化
gd->bd->bi_arch_number = CONFIG_MACH_TYPE; /* board id for linux */
gd->bd->bi_boot_params = LINUX_BOOT_PARAM_ADDR; /* adress of boot parameters */ //
serial_initialize
debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr);
puts("Flash: ");
flash_size = flash_init();
puts("PNOR flash is not present - switch decoding bit back for NAND\n");
puts("NAND: ");
nand_init(); /* go init the NAND */
nand_init_chip
board_nand_init
iproc_nand_init
iproc_nand_config
for (i = 0; i < IPROC_NAND_CHIP_LIST_COUNT; i++) { //选择Micron下不同型号的芯片
uint32_t devid;
struct iproc_nand_chips *ch;
ch = &iproc_chip_list[i];
devid = ch->id[0] << 24 | ch->id[1] << 16 | ch->id[2] << 8 | ch->id[3];
if (devid == inand[cs].device_id) {
inand[cs].chip_ptr = &iproc_chip_list[i];
break;
}
}
printf("%lu MiB\n", total_nand_size / 1024);
puts("MMC: ");
mmc_initialize(gd->bd);
env_relocate();
env_relocate_spec
spi_flash_probe
for (i = 0; i < ARRAY_SIZE(flashes); ++i) //选择SPI_FLASH芯片厂商
if (flashes[i].shift == shift && flashes[i].idcode == *idp) {
/* we have a match, call probe */
flash = flashes[i].probe(spi, idp); //将进入对应厂商的xxx_probe函数,里面又将选择不同的芯片型号
if (flash)
break;
}
printf("SF: Detected %s with page size ", flash->name);
stdio_init(); //标准输入输出初始化
console_init_r();
stdio_print_current_devices
puts("In: ");
puts("Out: ");
puts("Err: ");
/* set up exceptions */
interrupt_init();
/* enable exceptions */
enable_interrupts();
board_late_init();
iproc_clk_enum();
printf("arm_clk=%dMHz, axi_clk=%dMHz, apb_clk=%dMHz, arm_periph_clk=%dMHz\n",
save_shmoo_to_flash();
soc_and28_shmoo_ctl
printf("DDR Tuning Complete\n");
puts("Net: ");
eth_initialize(gd->bd);
printf("========== relocate address: 0x%lx, offset 0x%lx ==========\n",id->relocaddr, id->relocaddr - 0x1e000000);
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop();
}
3.分析main_loop
main_loop()
s = getenv ("bootcmd");
if (bootdelay != -1 && s && !abortboot(bootdelay)) {
int prev = disable_ctrlc(1); /* disable Control C checking */
run_command_list(s, -1, 0); //执行bootcmd对应的处理函数
disable_ctrlc(prev); /* restore Control C checking */
}
for (;;) { //等待输入命令,然后执行对应的处理函数
len = readline (CONFIG_SYS_PROMPT);
...
strcpy (lastcommand, console_buffer);
...
rc = run_command(lastcommand, flag);
}
4.分析run_command
run_command
builtin_run_command
cmd_process
5.分析cmd_process()是如何根据输入的命令找到对应的处理函数的,下面以ping命令为例
cmd_process
find_cmd
int len = &__u_boot_cmd_end - &__u_boot_cmd_start;
return find_cmd_tbl(cmd, &__u_boot_cmd_start, len);
cmd_call
int do_ping (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
....
}
U_BOOT_CMD( //将U_BOOT_CMD展开可知,其属性为.u-boot.cmd,在u-boot.lds时,把他们放在从__u_boot_cmd_start到__u_boot_cmd_end的存储空间
ping, 2, 1, do_ping,
"send ICMP ECHO_REQUEST to network host",
"pingAddress"
);
6.分析 bootm
do_bootm
bootm_start
boot_get_kernel
image_get_kernel
image_print_contents
printf("%sImage Name: %.*s\n"
printf("%sData Size:
...
puts(" Verifying Checksum ... "); puts("OK\n");
boot_get_ramdisk //虚拟硬盘
bootm_load_os
printf(" XIP %s ... ", type_name); puts("OK\n");
boot_fn = boot_os[images.os.os]; //根据os匹配得 boot_fn = do_bootm_linux
boot_fn(0, argc, argv, &images); //即do_bootm_linux(0, argc, argv, &images);
boot_prep_linux(images);
#ifdef CONFIG_CMDLINE_TAG
char *commandline = getenv("bootargs");
printf("%s commandline: %s\n", __FUNCTION__, commandline);
#endif
debug("using: ATAGS\n");
setup_start_tag(gd->bd);
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag(gd->bd, commandline);
#endif
setup_end_tag(gd->bd);
boot_jump_linux(images);
kernel_entry = (void (*)(int, int, uint))images->ep; //Kernel入口地址
s = getenv("machid"); //机器码
announce_and_cleanup();
printf("\nStarting kernel ...\n\n");
cleanup_before_linux();
r2 = gd->bd->bi_boot_params;
kernel_entry(0, machid, r2); //转向Kernel执行,在arm架构中,函数传递参数的用寄存器r1、r2、r3保存
7.以下开始分析Kernel启动过程,同样的,首先分析 kernel/Makefile,观察先后顺序,发现$(head-y)在最前面,通过grep,找到arch/arm/kernel/head.s ,开始分析head.s
head.s //分析寄存器r1、r2、r3中保存的函数参数
start_kernel
8.分析 start_kernel (in linux/init/main.c)
start_kernel
printk(KERN_NOTICE "%s", linux_banner);
setup_arch(&command_line);
setup_processor();
lookup_processor_type //得到cpu相关信息
machine_desc = setup_machine_tags(machine_arch_type);
/*通过轮询由MACHINE_START定义的内容,得到对应的板级信息*/
for (p = __arch_info_begin; p < __arch_info_end; p++) /*由MACHINE_START定义 __arch_info_begin */
...
strlcpy(boot_command_line, from, COMMAND_LINE_SIZE);
strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = cmd_line; //此时command_line被赋值为了uboot传过来的Bootargs
parse_early_param();
parse_early_options
do_early_param
for (p = __setup_start; p < __setup_end; p++) { //执行__setup属性的函数
root_dev_setup、mtdpart_setup等
parse_early_param(); //若以上已经完成,则跳过
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
-1, -1, &unknown_bootoption); //对unknown_bootoption进行处理,怎么处理还未细看
...
console_init();
...
rest_init();
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
...
cpu_idle(); //把cpu让给kernel_init
9.分析 kernel_init
kernel_init
do_basic_setup();
...
driver_init();
...
do_initcalls(); //根据优先级
for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
do_initcall_level(level); //
包括:late_initcall(northstar_mtd_setup); arch_initcall(customize_machine)等
northstar_mtd_setup
brcm_setup_spi_master
brcm_setup_spi_flash
master = spi_busnum_to_master(bus_num); //bus_num为1,所有maser返回的是spi1
spi_new_device
spi_add_device
dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),spi->chip_select); //该处组合成spi1.0
if (!ramdisk_execute_command)
ramdisk_execute_command = "/init";
if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
ramdisk_execute_command = NULL;
prepare_namespace();
if (saved_root_name[0]) {
if (!strncmp(root_device_name, "mtd", 3) ||
!strncmp(root_device_name, "ubi", 3)) {
mount_block_root(root_device_name, root_mountflags);
get_fs_names(fs_names);
get_filesystem_list //从已经注册的filesytems中选择对用的
do_mount_root
sys_mount(name, "/root", fs, flags, data);
printk(KERN_INFO"VFS: Mounted root (%s filesystem)%s on device %u:%u.\n",
goto out;
...
mount_root();
#ifdef CONFIG_ROOT_NFS
if (ROOT_DEV == Root_NFS) {
if (mount_nfs_root())
return;
printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n");
ROOT_DEV = Root_FD0;
}
}
#endif
...
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0);
(void) sys_dup(0);
...
init_post();
if (execute_command) { //execute_command在init_setup()中赋值 __setup("init=", init_setup);
run_init_process(execute_command);
//如果没有指定Init=,则运行默认脚本
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel. "
"See Linux Documentation/init.txt for guidance.");
m25p_probe //此处为何时调用尚不可知
mtd_device_parse_register
parse_mtd_partitions
parse_cmdline_partitions //分析bootargs中“mtdparts=”内容,构建传入的flash分区表
if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id))) //与dev_set_name组合成的spi1.0进行匹配
//iproc MACH_IPROC IPROC 4735(0x127f)
MACHINE_START(IPROC, "Broadcom iProc")