u-boot-2016.5启动流程
来源:互联网 发布:minecraft java版 编辑:程序博客网 时间:2024/05/01 03:05
0、由u-boot-2016.05\arch\arm\cpu\u-boot.lds链接文件中ENTRY(_start)
可知程序的入口在_start,在SourceInsight中查找可发现程序的入口_start在u-boot-2016.05\arch\arm\lib\vectors.S中。
...ENTRY(_start)SECTIONS{ ... . = 0x00000000; . = ALIGN(4); .text : { *(.__image_copy_start) *(.vectors) CPUDIR/start.o (.text*) *(.text*) } ... . = ALIGN(4); .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } . = ALIGN(4); .data : { *(.data*) } . = ALIGN(4); . = .; ... .bss_start __rel_dyn_start (OVERLAY) : { KEEP(*(.__bss_start)); __bss_base = .; } .bss __bss_base (OVERLAY) : { *(.bss*) . = ALIGN(4); __bss_limit = .; } .bss_end __bss_limit (OVERLAY) : { KEEP(*(.__bss_end)); } ...}
进入boot-2016.05\arch\arm\lib\vectors.S中,可以看到从_start开始后就跳转到reset去执行:
....globl _start..._start:#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG .word CONFIG_SYS_DV_NOR_BOOT_CFG#endif b reset ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq ldr pc, _fiq...
1、从u-boot-2016.05\arch\arm\cpu\arm920t\start.S中reset执行
主要执行流程:reset -> cpu_init_crit -> lowlevel_init -> _main
reset:...#ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit#endif bl _main...#ifndef CONFIG_SKIP_LOWLEVEL_INITcpu_init_crit:... bl lowlevel_init...#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
2、由bl _main跳转到u-boot-2016.05\arch\arm\lib\crt0.S中从入口_main开始执行
主要执行流程:board_init_f -> relocate_code -> board_init_r
ENTRY(_main) ... bl board_init_f_alloc_reserve ... bl board_init_f_init_reserve ... bl board_init_f#if ! defined(CONFIG_SPL_BUILD) ... b relocate_code ...#endif#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK) ...#if defined(CONFIG_SYS_THUMB_BUILD) ...#else ldr pc, =board_init_r #endif#endifENDPROC(_main)
这部分有三点说明:
⑴、u-boot-2016.05\common\board_f.c:board_init_f通过initcall_run_list(init_sequence_f)函数执行一系列初始化函数以实现前半部分板级初始化。全局结构体gd在u-boot-2016.05\arch\arm\include\asm\global_data.h中声明:
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r9")
⑵、u-boot-2016.05\arch\arm\lib\relocate.S:relocate_code实现uboot代码的重定位,此部分如果觉得源代码不是简单明了可自己改写。
⑶、去重定位uboot有两种路径:
一种是将gd->flags设为0,在初始化函数序列init_sequence_f中的jump_to_copy函数中去跳转到relocate_code:
static int jump_to_copy(void){ if (gd->flags & GD_FLG_SKIP_RELOC) return 0; ...#if defined(CONFIG_X86) || defined(CONFIG_ARC) ...#else relocate_code(gd->start_addr_sp, gd->new_gd, gd->relocaddr);#endif return 0;}
另一种就是不宏定义CONFIG_SPL_BUILD,然后在u-boot-2016.05\arch\arm\lib\crt0.S中通过
#if ! defined(CONFIG_SPL_BUILD) ... b relocate_code ...#endif
来跳转到relocate_code。以上两种方法选其一,另一种就得去掉。
3、在上一步通过ldr pc, =board_init_r指令进入u-boot-2016.05\common\board_r.c:board_init_r函数,进而调用initcall_run_list(init_sequence_r)函数执行一系列初始化函数以实现后半部分板级初始化,并在initcall_run_list函数里进入run_main_loop不再返回。
void board_init_r(gd_t *new_gd, ulong dest_addr){ ... if (initcall_run_list(init_sequence_r)) hang(); /* NOTREACHED - run_main_loop() does not return */ hang();}
init_sequence_r是一个函数指针数组,里面存放了很多初始化函数指针,里面有两个重要的函数指针initr_announce和run_main_loop:
init_fnc_t init_sequence_r[] = { ... initr_announce, ... run_main_loop,};
initr_announce函数声明从此处开始程序就将跳转到RAM中运行:
static int initr_announce(void){ debug("Now running in RAM - U-Boot at: %08lx\n", gd->relocaddr); return 0;}
最后一项是run_main_loop ,进入run_main_loop 后便不再返回。
4、在run_main_loop 里会进入u-boot-2016.05\common\main.c:main_loop函数
static int run_main_loop(void){ ... for (;;) main_loop(); return 0;}
进入main_loop之前就已经完成初始化,接下来准备去处理命令
/* We come here after U-Boot is initialised and ready to process commands */void main_loop(void){ const char *s; bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); ... /* get environment_variable: s = getenv("bootcmd"); -> bootcmd */ s = bootdelay_process(); ... autoboot_command(s); ...}
main_loop函数里有两个重要的过程:
⑴、首先在bootdelay_process函数里通过s = getenv(“bootcmd”)得到bootcmd参数并返回bootcmd参数,
const char *bootdelay_process(void){ char *s; int bootdelay; ... s = getenv("bootdelay"); ... debug("### main_loop entered: bootdelay=%d\n\n", bootdelay); ... s = getenv("bootcmd"); ... stored_bootdelay = bootdelay; return s;}
其中,bootcmd参数通过以下方式指定:
先在u-boot-2016.05\include\env_default.h中
#ifdef CONFIG_BOOTCOMMAND "bootcmd=" CONFIG_BOOTCOMMAND "\0"#endif
再在u-boot-2016.05\include\configs\smdk2440.h中指定
#define CONFIG_BOOTCOMMAND "nand read 30000000 kernel;bootm 30000000"
⑵、然后进入autoboot_command函数,并将bootcmd参数传入,继而进入run_command_list函数,继续将bootcmd参数传入
void autoboot_command(const char *s){ ... if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) { ... run_command_list(s, -1, 0); ... } ...}
5、从autoboot_command函数进入u-boot-2016.05\common\cli.c:run_command_list函数后,接着会调用board_run_command函数去执行命令
int run_command_list(const char *cmd, int len, int flag){ int need_buff = 1; char *buff = (char *)cmd; /* cast away const */ int rcode = 0; if (len == -1) { len = strlen(cmd);#ifdef CONFIG_SYS_HUSH_PARSER ...#else /* the built-in parser will change our string if it sees \n */ need_buff = strchr(cmd, '\n') != NULL;#endif } if (need_buff) { buff = malloc(len + 1); if (!buff) return 1; memcpy(buff, cmd, len); buff[len] = '\0'; }#ifdef CONFIG_SYS_HUSH_PARSER ...#ifdef CONFIG_CMDLINE ...#else rcode = board_run_command(buff);#endif#endif ...}
那么,board_run_command如何去执行命令?
首先,board_run_command函数通过bootcmd参数中的bootm命令找到u-boot-2016.05\cmd\bootm.c中的
U_BOOT_CMD( bootm, CONFIG_SYS_MAXARGS, 1, do_bootm, "boot application image from memory", bootm_help_text);
然后,根据这个信息找到执行bootm命令的处理函数指针do_bootm,并进入do_bootm函数执行相关代码,而U_BOOT_CMD在u-boot-2016.05\include\command.h中定义:
#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help) \ U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, \ _comp) \ _CMD_REMOVE(sub_ ## _name, _cmd)
#define _CMD_REMOVE(_name, _cmd) \ int __remove_ ## _name(void) \ { \ if (0) \ _cmd(NULL, 0, 0, NULL); \ return 0; \ }
在此,board_run_command函数还会将bootm命令中的参数(内核映像所在地址)30000000赋给bootm_headers_t结构体变量images,则images首地址就是30000000,images在u-boot-2016.05\cmd\bootm.c中定义:
bootm_headers_t images;
6、根据U_BOOT_CMD信息进入u-boot-2016.05\cmd\bootm.c:do_bootm函数
int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){ ... return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER | BOOTM_STATE_LOADOS |#if defined(CONFIG_PPC) || defined(CONFIG_MIPS) BOOTM_STATE_OS_CMDLINE |#endif BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO, &images, 1);}
其中BOOTM_STATE_START 、BOOTM_STATE_FINDOS 、BOOTM_STATE_FINDOTHER 、BOOTM_STATE_LOADOS 、BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 这些在u-boot-2016.05\include\image.h中bootm_headers结构体中指定:
#define BOOTM_STATE_START (0x00000001)#define BOOTM_STATE_FINDOS (0x00000002)#define BOOTM_STATE_FINDOTHER (0x00000004)#define BOOTM_STATE_LOADOS (0x00000008)#define BOOTM_STATE_RAMDISK (0x00000010)#define BOOTM_STATE_FDT (0x00000020)#define BOOTM_STATE_OS_CMDLINE (0x00000040)#define BOOTM_STATE_OS_BD_T (0x00000080)#define BOOTM_STATE_OS_PREP (0x00000100)#define BOOTM_STATE_OS_FAKE_GO (0x00000200) /* 'Almost' run the OS */#define BOOTM_STATE_OS_GO (0x00000400)
7、从do_bootm进入u-boot-2016.05\common\bootm.c:do_bootm_states函数,Now run the OS!
int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[], int states, bootm_headers_t *images, int boot_progress){ boot_os_fn *boot_fn; ulong iflag = 0; int ret = 0, need_boot_fn; images->state |= states; /* * Work through the states and see how far we get. We stop on * any error. */ if (states & BOOTM_STATE_START) ret = bootm_start(cmdtp, flag, argc, argv); if (!ret && (states & BOOTM_STATE_FINDOS)) ret = bootm_find_os(cmdtp, flag, argc, argv); if (!ret && (states & BOOTM_STATE_FINDOTHER)) { ret = bootm_find_other(cmdtp, flag, argc, argv); argc = 0; /* consume the args */ } /* Load the OS */ if (!ret && (states & BOOTM_STATE_LOADOS)) { ulong load_end; iflag = bootm_disable_interrupts(); ret = bootm_load_os(images, &load_end, 0); if (ret == 0) lmb_reserve(&images->lmb, images->os.load, (load_end - images->os.load)); else if (ret && ret != BOOTM_ERR_OVERLAP) goto err; else if (ret == BOOTM_ERR_OVERLAP) ret = 0;#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) if (images->os.os == IH_OS_LINUX) fixup_silent_linux();#endif } /* Relocate the ramdisk */#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH if (!ret && (states & BOOTM_STATE_RAMDISK)) { ulong rd_len = images->rd_end - images->rd_start; ret = boot_ramdisk_high(&images->lmb, images->rd_start, rd_len, &images->initrd_start, &images->initrd_end); if (!ret) { setenv_hex("initrd_start", images->initrd_start); setenv_hex("initrd_end", images->initrd_end); } }#endif#if IMAGE_ENABLE_OF_LIBFDT && defined(CONFIG_LMB) if (!ret && (states & BOOTM_STATE_FDT)) { boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr); ret = boot_relocate_fdt(&images->lmb, &images->ft_addr, &images->ft_len); }#endif /* From now on, we need the OS boot function */ if (ret) return ret; boot_fn = bootm_os_get_boot_func(images->os.os); need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE | BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO); if (boot_fn == NULL && need_boot_fn) { if (iflag) enable_interrupts(); printf("ERROR: booting os '%s' (%d) is not supported\n", genimg_get_os_name(images->os.os), images->os.os); bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); return 1; } /* Call various other states that are not generally used */ if (!ret && (states & BOOTM_STATE_OS_CMDLINE)) ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images); if (!ret && (states & BOOTM_STATE_OS_BD_T)) ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images); if (!ret && (states & BOOTM_STATE_OS_PREP)) ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);#ifdef CONFIG_TRACE /* Pretend to run the OS, then run a user command */ if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) { char *cmd_list = getenv("fakegocmd"); ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO, images, boot_fn); if (!ret && cmd_list) ret = run_command_list(cmd_list, -1, flag); }#endif /* Check for unsupported subcommand. */ if (ret) { puts("subcommand not supported\n"); return ret; } /* Now run the OS! We hope this doesn't return */ if (!ret && (states & BOOTM_STATE_OS_GO)) ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, images, boot_fn); /* Deal with any fallout */err: if (iflag) enable_interrupts(); if (ret == BOOTM_ERR_UNIMPLEMENTED) bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); else if (ret == BOOTM_ERR_RESET) do_reset(cmdtp, flag, argc, argv); return ret;}
do_bootm_states函数总共分8个部分:
⑴、 Work through the states and see how far we get. We stop on any error.
其中主要函数bootm_find_os实现三个功能:get kernel image header, start address and length,get image parameters。大概过程是:bootm_find_os -> boot_get_kernel -> image_get_kernel 。
static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){ const void *os_hdr; bool ep_found = false; int ret; /* get kernel image header, start address and length */ os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, &images, &images.os.image_start, &images.os.image_len); ... /* get image parameters */ switch (genimg_get_format(os_hdr)) {#if defined(CONFIG_IMAGE_FORMAT_LEGACY) case IMAGE_FORMAT_LEGACY: /*旧系统格式的内核映像*/ images.os.type = image_get_type(os_hdr); images.os.comp = image_get_comp(os_hdr); images.os.os = image_get_os(os_hdr); images.os.end = image_get_image_end(os_hdr); images.os.load = image_get_load(os_hdr); images.os.arch = image_get_arch(os_hdr); break;#endif#if IMAGE_ENABLE_FIT case IMAGE_FORMAT_FIT: ...#endif#ifdef CONFIG_ANDROID_BOOT_IMAGE case IMAGE_FORMAT_ANDROID: ...#endif default: puts("ERROR: unknown image format type!\n"); return 1; } ... if (images.os.type == IH_TYPE_KERNEL_NOLOAD) { images.os.load = images.os.image_start; images.ep += images.os.load; } ...
关于boot_get_kernel 、image_get_kernel 的说明:
boot_get_kernel - find kernel image(returns:
pointer to image header if valid image was found, plus kernel start address and length, otherwise NULL)
image_get_kernel - verify legacy format kernel image(returns:
pointer to a legacy image header if valid image was found otherwise return NULL)
⑵、Load the OS
⑶、Relocate the ramdisk
⑷、From now on, we need the OS boot function
由boot_fn = bootm_os_get_boot_func(images->os.os);
得到boot处理函数指针并赋给boot_fn。
①、关于参数images->os.os,可以由下列定义得知它是系统内核的类型,并在(2)中被赋值,若系统类型为linux,则images->os.os=5。
typedef struct bootm_headers { ...#ifndef USE_HOSTCC /*USE_HOSTCC 没有宏定义*/ image_info_t os; /* os image info */ ulong ep; /* entry point of OS */ ulong rd_start, rd_end;/* ramdisk start/end */ char *ft_addr; /* flat dev tree address */ ulong ft_len; /* length of flat device tree */ ulong initrd_start; ulong initrd_end; ulong cmdline_start; ulong cmdline_end; bd_t *kbd;#endif ...} bootm_headers_t;
bootm_headers_t images;
typedef struct image_info { ulong start, end; /* start/end of blob */ ulong image_start, image_len; /* start of image within blob, len of image */ ulong load; /* load addr for the image */ uint8_t comp, type, os; /* compression, type of image, os type */ uint8_t arch; /* CPU architecture */} image_info_t;
得到images.os.os的值:
static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){ const void *os_hdr; ... /* get kernel image header, start address and length */ os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, &images, &images.os.image_start, &images.os.image_len); ... /* get image parameters */ switch (genimg_get_format(os_hdr)) {#if defined(CONFIG_IMAGE_FORMAT_LEGACY) case IMAGE_FORMAT_LEGACY: /*旧系统格式的内核映像*/ ... images.os.os = image_get_os(os_hdr); ... break;#endif
②、bootm_os_get_boot_func中会用到函数指针数组boot_os,该数组利用传入的images.os.os=5的值得到boot处理函数指针do_bootm_linux返回给boot_fn 。
boot_fn = bootm_os_get_boot_func(images->os.os);
boot_os_fn *bootm_os_get_boot_func(int os){ ... return boot_os[os];}
static boot_os_fn *boot_os[] = { [IH_OS_U_BOOT] = do_bootm_standalone,#ifdef CONFIG_BOOTM_LINUX [IH_OS_LINUX] = do_bootm_linux,#endif#ifdef CONFIG_BOOTM_NETBSD [IH_OS_NETBSD] = do_bootm_netbsd,#endif ...};
操作系统代号可在u-boot-2016.05\include\image.h中查看
/* * Operating System Codes */#define IH_OS_INVALID 0 /* Invalid OS */#define IH_OS_OPENBSD 1 /* OpenBSD */#define IH_OS_NETBSD 2 /* NetBSD */#define IH_OS_FREEBSD 3 /* FreeBSD */#define IH_OS_4_4BSD 4 /* 4.4BSD */#define IH_OS_LINUX 5 /* Linux */ ...
⑸、Call various other states that are not generally used
⑹、Check for unsupported subcommand
⑺、Now run the OS! We hope this doesn’t return
if (!ret && (states & BOOTM_STATE_OS_GO)) ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, images, boot_fn);
从do_bootm_states进入u-boot-2016.05\common\bootm_os.c:boot_selected_os函数,执行boot_fn(state, argc, argv, images);
int boot_selected_os(int argc, char * const argv[], int state, bootm_headers_t *images, boot_os_fn *boot_fn){ ... boot_fn(state, argc, argv, images); ...}
⑻、Deal with any fallout
8、执行boot_fn(state, argc, argv, images)
,因为boot_fn=do_bootm_linux,所以相当于执行do_bootm_linux(state, argc, argv, images)
,程序跳到u-boot-2016.05\arch\arm\lib\bootm.c:
/* Main Entry point for arm bootm implementation*/int do_bootm_linux(int flag, int argc, char * const argv[], bootm_headers_t *images){ ... boot_jump_linux(images, flag); ...}
do_bootm_linux -> boot_jump_linux -> kernel_entry(0, machid, r2);
static void boot_jump_linux(bootm_headers_t *images, int flag){ ... unsigned long machid = gd->bd->bi_arch_number; char *s; void (*kernel_entry)(int zero, int arch, uint params); unsigned long r2; int fake = (flag & BOOTM_STATE_OS_FAKE_GO); kernel_entry = (void (*)(int, int, uint))images->ep; /* ep:entry point of OS*/ s = getenv("machid"); if (s) { if (strict_strtoul(s, 16, &machid) < 0) { debug("strict_strtoul failed!\n"); return; } printf("Using machid 0x%lx from environment\n", machid); } ... if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) r2 = (unsigned long)images->ft_addr; else r2 = gd->bd->bi_boot_params; if (!fake) { ... kernel_entry(0, machid, r2); }#endif}
run the OS!
说明:
关于kernel_entry = (void (*)(int, int, uint))images->ep;
中的images->ep在u-boot-2016.05\common\bootm.c:bootm_find_os函数中被赋值。
static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){ ... if (images.os.type == IH_TYPE_KERNEL_NOLOAD) { images.os.load = images.os.image_start; images.ep += images.os.load; } ...}
- u-boot-2016.5启动流程
- u-boot启动流程
- u-boot启动流程
- u-boot启动流程
- u-boot启动流程
- u-boot启动流程
- u-boot总的启动流程
- u-boot启动流程一
- U-BOOT的启动流程
- u-boot启动流程分析
- U-BOOT启动流程之一
- U-Boot启动内核流程
- u-boot启动流程分析
- u-boot-2012.10启动流程
- u-boot启动流程-第一阶段
- u-boot关于启动流程
- u-boot启动流程分析
- U-Boot的启动流程
- 字符串
- Intellj Idea 的默认首页修改
- oracle 9i/10g/11g(11.2.0.3)安装包和PATCH下载地址汇总
- TraceView的使用方法
- 统计分析基础 (一) 数据统计与图表
- u-boot-2016.5启动流程
- AppleScirpt设置打开Terminal快捷键
- C++ 常用泛型算法的使用
- 多图详解Spring框架的设计理念与设计模式
- JMM和synchronize的一些记录:
- JVM内存区域介绍
- 软件测试的一些理解
- Android基础(三)
- Leetcode 234. Palindrome Linked List (Easy) (cpp)