先来引用一下这篇介绍“ARM Linux内核启动要求”的文章ARM Linux Kernel Boot Requirements,是ARM Linux内核的维护者Russell King写的。
引用:
    * CPU register settings
          o r0 = 0.
          o r1 = machine type number.
          o r2 = physical address of tagged list in system RAM. 
    * CPU mode
          o All forms of interrupts must be disabled (IRQs and FIQs.)
          o The CPU must be in SVC mode. (A special exception exists for Angel.) 
    * Caches, MMUs
          o The MMU must be off.
          o Instruction cache may be on or off.
          o Data cache must be off and must not contain any stale data. 
    * Devices
          o DMA to/from devices should be quiesced. 
    * The boot loader is expected to call the kernel image by jumping directly to the first instruction of the kernel image.

大致就是以上条件了,请特别关注一下第一条,这个基本上就是U-Boot的go命令和bootm命令之间的本质区别所在了。先来看看bootm命令的实现,在common/cmd_bootm.c的第119行开始有:

#ifdef CONFIG_PPCstatic boot_os_Fcn do_bootm_linux;#elseextern boot_os_Fcn do_bootm_linux;#endif

这里的预编译宏说明了,非 PPC体系结构的CPU的do_bootm_linux()函数都不是在这个文件内实现的(extern)。可想而知,这个函数的实现应该是和体系结构相关的,具体到arm体系结构的实现就是在lib_arm/armlinux.c这个文件当中。可以看到从lib_arm/armlinux.c中的第77 行开始就是do_bootm_linux()函数的实现。
 
其中第85行声明了这样一个函数指针theKernel:

void (*theKernel)(int zero, int arch, uint params);

看看它的名字和参数的命名我们也可以猜到这个其实就是内核的入口函数的指针了。几个参数的命名也说明了上文提到的ARM Linux内核启动要求的第一条,因为根据ACPS(ARM/Thumb Procedure Call Standard)的规定,这三个参数就是依次使用r0,r1和r2来传递的。
 
接下来第93行就是给这个函数指针赋值:

theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);

可以看到theKernel被赋值为hdr->ih_ep,这个hdr是指使用tools/mkimage工具程序制作uImage时加在linux.bin.gz前面的一个头部,而ih_ep结构体成员保存的就是使用mkimage时指定的-e参数的值,即内核的入口点(Entry Point)。知道了hdr->ih_ep的意义之后,给theKernel赋这个值也就是理所当然的了。
 
最后是对内核入口函数的调用,发生在第270行:

theKernel (0, bd->bi_arch_number, bd->bi_boot_params);

调用的时候对参数进行赋值,r0=0,r1=bd->bi_arch_number,r2=bd->bi_boot_params,一个都不少。至此U-Boot的使命完成,开始进入ARM Linux的美丽新世界。
 
====================================================================
 
要知道哪个地址是启动内核,哪个地址启动文件系统,要分析common/cmd_bootm.c中的函数do_bootm,因为引导kernel就是bootm这条命令的工作,do_bootm是命令bootm的执行函数。

现在我们来分析一下common/cmd_bootm.c中的函数do_bootm,这是bootm命令的处理函数。

……image_header_t header;ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address */int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]){ulong iflag;ulong addr;ulong data, len, checksum;ulong *len_ptr;uint unc_len = 0x400000;int i, verify;char *name, *s;int (*appl)(int, char *[]);image_header_t *hdr = &header;

读取uboot的环境变量verify,如果环境变量verify等于’n’,则局部变量verify赋值成为0;如果环境变量verify为空(即没有定义环境变量verify)或者环境变量verify不等于’n’,则局部变量verify赋值成为1。

s = getenv ("verify");verify = (s && (*s == 'n')) ? 0 : 1;

如果参数个数小于2(即只是输入了bootm),使用缺省加载地址CFG_LOAD_ADDR;否则使用第二个参数作为加载地址。

if (argc < 2) {addr = load_addr;} else {addr = simple_strtoul(argv[1], NULL, 16);}SHOW_BOOT_PROGRESS (1);printf ("## Booting image at %08lx ...\n", addr);

将mkimage添加到映象文件头部的64字节提取到image_header_t 结构变量header中。
/* Copy header so we can blank CRC field for re-calculation */
定义了CONFIG_HAS_DATAFLASH,表示系统中存在ATMEL的数据Flash。

#ifdef CONFIG_HAS_DATAFLASHif (addr_dataflash(addr)){read_dataflash(addr, sizeof(image_header_t), (char *)&header);} else#endifmemmove (&header, (char *)addr, sizeof(image_header_t));

判断image header的magic是否匹配,如果不匹配,说明下载过程中发生了错误。

if (ntohl(hdr->ih_magic) != IH_MAGIC) {#ifdef __I386__ /* correct image format not implemented yet - fake it */if (fake_header(hdr, (void*)addr, -1) != NULL) {/* to compensate for the addition below */addr -= sizeof(image_header_t);/* turnof verify,* fake_header() does not fake the data crc*/verify = 0;} else#endif /* __I386__ */{puts ("Bad Magic Number\n");SHOW_BOOT_PROGRESS (-1);return 1;}}SHOW_BOOT_PROGRESS (2);

校验image header的CRC以及image data的CRC,如果校验不匹配,说明下载过程中发生了错误。

data = (ulong)&header;len = sizeof(image_header_t);checksum = ntohl(hdr->ih_hcrc);hdr->ih_hcrc = 0;if (crc32 (0, (char *)data, len) != checksum) {puts ("Bad Header Checksum\n");SHOW_BOOT_PROGRESS (-2);return 1;}SHOW_BOOT_PROGRESS (3);/* for multi-file images we need the data part, too */print_image_hdr ((image_header_t *)addr);data = addr + sizeof(image_header_t);len = ntohl(hdr->ih_size);#ifdef CONFIG_HAS_DATAFLASHif (addr_dataflash(addr)){read_dataflash(data, len, (char *)CFG_LOAD_ADDR);data = CFG_LOAD_ADDR;}#endifif (verify) {puts (" Verifying Checksum ... ");if (crc32 (0, (char *)data, len) != ntohl(hdr->ih_dcrc)) {printf ("Bad Data CRC\n");SHOW_BOOT_PROGRESS (-3);return 1;}puts ("OK\n");}SHOW_BOOT_PROGRESS (4);

判断体系结构。

len_ptr = (ulong *)data;#if defined(__PPC__)if (hdr->ih_arch != IH_CPU_PPC)#elif defined(__ARM__)if (hdr->ih_arch != IH_CPU_ARM)#elif defined(__I386__)if (hdr->ih_arch != IH_CPU_I386)#elif defined(__mips__)if (hdr->ih_arch != IH_CPU_MIPS)#elif defined(__nios__)if (hdr->ih_arch != IH_CPU_NIOS)#elif defined(__M68K__)if (hdr->ih_arch != IH_CPU_M68K)#elif defined(__microblaze__)if (hdr->ih_arch != IH_CPU_MICROBLAZE)#else# error Unknown CPU type#endif{printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch);SHOW_BOOT_PROGRESS (-4);return 1;}SHOW_BOOT_PROGRESS (5);

判断image类型。

switch (hdr->ih_type) {case IH_TYPE_STANDALONE:name = "Standalone Application";/* A second argument overwrites the load address */if (argc > 2) {hdr->ih_load = simple_strtoul(argv[2], NULL, 16);}break;case IH_TYPE_KERNEL:name = "Kernel Image";break;case IH_TYPE_MULTI:name = "Multi-File Image";len = ntohl(len_ptr[0]);/* OS kernel is always the first image */data += 8; /* kernel_len + terminator */for (i=1; len_ptr; ++i)data += 4;break;default: printf ("Wrong Image Type for %s command\n", cmdtp->name);SHOW_BOOT_PROGRESS (-5);return 1;}SHOW_BOOT_PROGRESS (6);/** We have reached the point of no return: we are going to* overwrite all exception vector code, so we cannot easily* recover from any failures any more...*/iflag = disable_interrupts();#ifdef CONFIG_AMIGAONEG3SE/** We've possible left the caches enabled during* bios emulation, so turn them off again*/icache_disable();invalidate_l1_instruction_cache();flush_data_cache();dcache_disable();#endif

判断image压缩类型

switch (hdr->ih_comp) {case IH_COMP_NONE: 没有压缩if(ntohl(hdr->ih_load) == addr) { 如果image header中指示的加载地址和bootm命令中参数2指定的地址相同,则表示不需要copy,可以就地执行。printf (" XIP %s ... ", name);} else {#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)size_t l = len;void *to = (void *)ntohl(hdr->ih_load);void *from = (void *)data;printf (" Loading %s ... ", name);while (l > 0) {size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;WATCHDOG_RESET();memmove (to, from, tail);to += tail;from += tail;l -= tail;}#else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */

如果image header中指示的加载地址和bootm命令中参数2指定的地址不相同,则表示要从image header中指示的加载地址处把image data copy到bootm命令中参数2指定的地址处,然后再执行。

memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);#endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */}break;case IH_COMP_GZIP:printf (" Uncompressing %s ... ", name);if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,(uchar *)data, (int *)&len) != 0) {puts ("GUNZIP ERROR - must RESET board to recover\n");SHOW_BOOT_PROGRESS (-6);do_reset (cmdtp, flag, argc, argv);}break;#ifdef CONFIG_BZIP2case IH_COMP_BZIP2:printf (" Uncompressing %s ... ", name);/** If we've got less than 4 MB of malloc() space,* use slower decompression algorithm which requires* at most 2300 KB of memory.*/i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),&unc_len, (char *)data, len,CFG_MALLOC_LEN < (4096 * 1024), 0);if (i != BZ_OK) {printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i);SHOW_BOOT_PROGRESS (-6);udelay(100000);do_reset (cmdtp, flag, argc, argv);}break;#endif /* CONFIG_BZIP2 */default:if (iflag)enable_interrupts();printf ("Unimplemented compression type %d\n", hdr->ih_comp);SHOW_BOOT_PROGRESS (-7);return 1;}puts ("OK\n");SHOW_BOOT_PROGRESS (7);

根据image 执行type来决定如何引导。

switch (hdr->ih_type) {case IH_TYPE_STANDALONE:if (iflag)enable_interrupts();/* load (and uncompress), but don't start if "autostart"* is set to "no"*/if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) {char buf[32];sprintf(buf, "%lX", len);setenv("filesize", buf);return 0;}appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep);(*appl)(argc-1, &argv[1]);return 0;case IH_TYPE_KERNEL:case IH_TYPE_MULTI:/* handled below */break; 下面将有代码专门处理这两种image类型default:if (iflag)enable_interrupts();printf ("Can't boot image type %d\n", hdr->ih_type);SHOW_BOOT_PROGRESS (-8);return 1;}SHOW_BOOT_PROGRESS (8);

根据image 的OS type来决定如何引导

switch (hdr->ih_os) {default: /* handled by (original) Linux case */case IH_OS_LINUX:#ifdef CONFIG_SILENT_CONSOLEfixup_silent_linux();#endifdo_bootm_linux (cmdtp, flag, argc, argv,addr, len_ptr, verify);break;case IH_OS_NETBSD:do_bootm_netbsd (cmdtp, flag, argc, argv,addr, len_ptr, verify);break;#ifdef CONFIG_LYNXKDIcase IH_OS_LYNXOS:do_bootm_lynxkdi (cmdtp, flag, argc, argv,addr, len_ptr, verify);break;#endifcase IH_OS_RTEMS:do_bootm_rtems (cmdtp, flag, argc, argv,addr, len_ptr, verify);break;#if (CONFIG_COMMANDS & CFG_CMD_ELF)case IH_OS_VXWORKS:do_bootm_vxworks (cmdtp, flag, argc, argv,addr, len_ptr, verify);break;case IH_OS_QNX:do_bootm_qnxelf (cmdtp, flag, argc, argv,addr, len_ptr, verify);break;#endif /* CFG_CMD_ELF */#ifdef CONFIG_ARTOScase IH_OS_ARTOS:do_bootm_artos (cmdtp, flag, argc, argv,addr, len_ptr, verify);break;#endif}SHOW_BOOT_PROGRESS (-9);#ifdef DEBUGputs ("\n## Control returned to monitor - resetting...\n");do_reset (cmdtp, flag, argc, argv);#endifreturn 1;}

bootm命令是用来引导经过u-boot的工具mkimage打包后的kernel image的,什么叫做经过u-boot的工具mkimage打包后的kernel image,这个就要看mkimage的代码,看看它做了些什么,虽然我很希望大家不要偷懒,认真地去看看,但是我知道还是有很多人懒得去做这件,那么我就j将分析mkimage代码后得到的总结告诉大家,mkimage做了些什么,怎么用这个工具。

mkimage的用法
uboot源代码的tools/目录下有mkimage工具,这个工具可以用来制作不压缩或者压缩的多种可启动映象文件。

mkimage在制作映象文件的时候,是在原来的可执行映象文件的前面加上一个0x40字节的头,记录参数所指定的信息,这样uboot才能识别这个映象是针对哪个CPU体系结构的,哪个OS的,哪种类型,加载内存中的哪个位置, 入口点在内存的那个位置以及映象名是什么
引用:
root@Glym:/tftpboot# ./mkimage
Usage: ./mkimage -l image
-l ==> list image header information
./mkimage -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image
-A ==> set architecture to 'arch'
-O ==> set operating system to 'os'
-T ==> set image type to 'type'
-C ==> set compression type 'comp'
-a ==> set load address to 'addr' (hex)
-e ==> set entry point to 'ep' (hex)
-n ==> set image name to 'name'
-d ==> use image data from 'datafile'
-x ==> set XIP (execute in place)

参数说明:

-A 指定CPU的体系结构:

取值 表示的体系结构
alpha Alpha
arm A RM
x86 Intel x86
ia64 IA64
mips MIPS
mips64 MIPS 64 Bit
ppc PowerPC
s390 IBM S390
sh SuperH
sparc SPARC
sparc64 SPARC 64 Bit
m68k MC68000

-O 指定操作系统类型,可以取以下值:
openbsd、netbsd、freebsd、4_4bsd、linux、svr4、esix、solaris、irix、sco、dell、ncr、lynxos、vxworks、psos、qnx、u-boot、rtems、artos

-T 指定映象类型,可以取以下值:
standalone、kernel、ramdisk、multi、firmware、script、filesystem

-C 指定映象压缩方式,可以取以下值:
none 不压缩
gzip 用gzip的压缩方式
bzip2 用bzip2的压缩方式

-a 指定映象在内存中的加载地址,映象下载到内存中时,要按照用mkimage制作映象时,这个参数所指定的地址值来下载

-e 指定映象运行的入口点地址,这个地址就是-a参数指定的值加上0x40(因为前面有个mkimage添加的0x40个字节的头)

-n 指定映象名

-d 指定制作映象的源文件

uboot do_bootm函数详解相关推荐

  1. 主线剧情03-NXP-i.MX系列的u-boot移植基础详解

    u-boot 移植基础详解 本文系广泛撷取.借鉴和整理(相关的内容在网络上有很多,但很多相互抄,或者是版本太老,或者就是不通用的非常有平台针对性的步骤,碎片化泛滥,甚至就是有待分拣的垃圾厂,当然也有一 ...

  2. U-Boot启动流程详解

    参考:U-Boot顶层目录链接脚本文件(u-boot.lds)介绍 作者:一只青木呀 发布时间: 2020-10-23 13:52:23 网址:https://blog.csdn.net/weixin ...

  3. uboot copy_from_nand代码详解

    uboot copy_from_nand代码详解 copy_from_nand函数简介: 函数将会把nand flash中的uboot二进制程序镜像拷贝到内存中,通常这一步出现问题,uboot是不可能 ...

  4. FatFs文件系统Nandflash驱动函数详解

    FatFs文件系统Nandflash驱动函数详解 转载 2015-08-06 14:43:27 标签:nandflash驱动函数详fatfs文件系统nandfla 本文讲解FatFs文件系统 下需要的 ...

  5. 【正点原子Linux连载】第三十二章 U-Boot启动流程详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  6. 【正点原子Linux连载】第三十一章 U-Boot顶层Makefile详解 -摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  7. U-Boot 顶层 Makefile 详解

    U-Boot 顶层 Makefile 详解 1.U-Boot 工程目录分析 我们在分析 uboot 源码之前一定要 先在 Ubuntu 中编译一下 uboot 源码,因为编译过程会生成一些文件,而生成 ...

  8. 嵌入式linux的u-boot系统启动过程,【站友投递】U-boot启动过程详解

    [站友投递]U-boot启动过程详解 来源:互联网 作者:denny 时间:2009-03-18 Tag:点击: 一.U-BOOT的目录结构 u-boot目录下有18个子目录,分别存放管理不通的源程序 ...

  9. C语言网络编程:accept函数详解

    文章目录 前言 函数描述 代码实例 如何得到客户端的IP 和 端口号 前言 当使用tcp服务器使用socket创建通信文件描述符,bind绑定了文件描述符,服务器ip和端口号,listen将服务器端的 ...

最新文章

  1. Stanford UFLDL教程 数据预处理
  2. ORACLE 普通表转换成分区表(在线重定义)
  3. #define const typedef
  4. python与正则表达式(part5)--re模块使用
  5. number java_java中Number Math 类方法
  6. 北航教授李帅:“VR+医疗”仿真系统及关键技术分享
  7. AOP的XML架构、AOP的@AspectJ
  8. [mark] some free templates sites
  9. 链新:探索NFT中国化路径,与实体经济相结合
  10. 2023最新淘宝天猫商品销量,宝贝详情,店铺列表信息分析
  11. 中文词性标注学习笔记(一)---词性标注概念
  12. 线性回归正则化 regularized linear regression
  13. DataStage(ETL)技术总结 -- 介绍篇
  14. 绿毒 GreenPoisOn RC5 完美越狱 iOS 4.2.1
  15. andorid第三方不错的效果(持续更新)
  16. python中math.ceil是什么意思_带有Python示例的math.ceil()方法
  17. 【MySQL基础】MySQL概述及基本SQL语句
  18. 你什么档次?敢和我用一样的即时通讯平台WorkPlus?
  19. 网页错误404 not found的解决方法
  20. 各个Android 市场的开发者的网址

热门文章

  1. 北鲲云超算平台助力国产芯片设计
  2. 苹果电脑/macbookpro 拔出电源充电器但还是显示充电的解决办法
  3. [生存志] 第79节 国语述诸国
  4. spring的AOP术语
  5. 再来!使用frida框架hook来获取APP的加密算法的参数
  6. mysql outer join报错_千与千寻-MySQL联结join
  7. 凝思系统机器名怎么查看_凝思操作系统Custom Linx安装教程
  8. 哪种锻炼方式最能让程序猿远离亚健康? - 强烈推荐
  9. java8/jdk1.8 官网下载地址
  10. Excel之match index 和vlookup函数 和双条件查找匹配