关键词:bootrom、spl、uboot、linux、mksheader、sb_header、mkimage、image_header_t等等。

首先看一个典型的bootrom->spl->uboot->linux流程log,主要分为4个部分,中间有3个衔接点。

  1. # Hello DeepEye
  2. -- Boot from SD card --
  3. sdio initialize done.
  4. sd card read done.
  5. --------------------------------------------------------------------------->bootrom-spl分割线,以上是bootrom输出内容,从存储介质中读取spl到片内RAM中,并判断magic number
  6. U-Boot SPL 2016.07--g6c3df97-dirty (Aug - ::)
  7. Boot reason: >(Normal).
  8. ddr4 4GB init...
  9. ...
  10. Welcome to SPL!Load U-Boot from SD ...
  11. ---------------------------------------------------------------------------->spl-uboot分割线,以上是spl运行阶段,主要进行pll、时钟、串口等初始化;最主要的是初始化了DDR以及从存储介质中将uboot加载到DDR中。
  12. U-Boot 2016.07 (Jul - :: +)DeepEye1000
  13.  
  14. DRAM: 3.9 GiB
  15. MMC: deepeye_sdhci: , deepeye_sdhci:
  16. Using default environment
  17. ...
  18. ## Booting kernel from Legacy Image at ...
  19. Image Name: Linux-4.9.
  20. Image Type: Sandbox Linux Kernel Image (gzip compressed)
  21. Data Size: Bytes = 4.7 MiB
  22. Load Address:
  23. Entry Point:
  24. Verifying Checksum ... OK
  25. Uncompressing Kernel Image ... OK
  26. dtb_load_addr: 0x8F000000
  27.  
  28. Starting kernel ...
  29. ---------------------------------------------------------------------------->uboot-linux分割线,uboot相较于spl功能更加丰富。提供了丰富的命令,可以操作文件系统、脚本、加载不同操作系统等等。
  30. [ 0.000000] Linux version 4.9. (al@al-B250-HD3) (gcc version 6.3. (C-SKY Tools V3.8.10-kstq-nd-r2 Glibc-2.9
  31. [ 0.000000] C-SKY: https://github.com/c-sky/csky-linux
  32. [ 0.000000] Phys. mem: 4032MB
  33. ...
  34. [ 4.382887] Freeing unused kernel memory: 276k freed
  35. [ 4.387879] This architecture does not have kernel memory protection.
  36. [ 4.702756] EXT4-fs (mmcblk1p2): re-mounted. Opts: nodelalloc,data=journal
  37. Starting mdev...
  38. Starting network: OK
  39. ...
    ----------------------------------------------------------------------------->linux包括kernelrootfs,内核中初始化了外设、操作系统组件、挂载了文件系统,并调用init初始化用户空间环境。

下面主要分析不同阶段之间如何衔接。

1. 各阶段主要作用

bootrom是固化在芯片内部的一块rom,初始化各种接口,并从中读取内容加载到片内SRAM中。因为存储设备接口相对简单,大部分不需要适配即可存取。但是DDR等需要修改代码进行适配。

所以就需要spl,spl被加载到片内SRAM中,片内SRAM不需要初始化即可运行,但是容量有限。spl运行起来后进行必要的初始化后,初始化DDR,并将uboot从存储设备中读到DDR中。

uboot运行在DDR中,则不受空间大小限制,可以进行复杂的操作。支持包括不同文件系统、脚本执行、多种操作系统加载等等操作。其中主要的工作是从存储设备中读取kernel,解析后跳转到kernel执行。

linux这里包括kernel和rootfs两部分,kernel进行系统组件初始化、设备初始化,在一切准备就绪后,调用第一个用户空间程序init进行用户空间初始化。

2. bootrom加载spl分析

bootrom从不同存储介质中读取spl,这些存储介质可能是SD、eMMC或者USB接口、串口等等。

从读取内容中解析spl的sb_header,验证magic_num、确定spl大小、加载到load_addr、进行crc校验结果crc16对比。最后跳转到entry_point进行spl运行。

下面了解一下sb_header结构体?以及sb_header是如何生成的?最后bootrom中是通过如何处理sb_header加载spl的?

2.1 sb_header数据结构

sb_header是bootrom和spl协调一致的数据结构,spl在头部包含此部分数据,bootrom在运行的时候解析它。

  1. #define MAGIC_NUM 0x44454550
  2.  
  3. typedef struct second_boot_header{
  4. unsigned int magic_num;----------------------------两者约定的魔数0x44454550
  5. unsigned int data_size;----------------------------去掉头部的spl大小。
  6. unsigned int load_addr;----------------------------从头部开始的地址。
  7. unsigned int entry_point;--------------------------去掉头部开始的地址。
  8. unsigned int crc16;--------------------------------不包括sb_headercrc校验结果。
  9. }sb_header;

2.2 从u-boot-spl->u-boot-spl.bin->u-boot-spl-bh.bin流程

生成u-boot-spl以及u-boot-spl.map文件:

  1. cmd_spl/u-boot-spl := (cd spl && csky-abiv2-linux-ld.bfd -EL -T u-boot-spl.lds --gc-sections -Bstatic --gc-sections -Ttext 0xfc000000 arch/csky/cpu/ck807_810/start.o --start-group arch/csky/cpu/built-in.o arch/csky/cpu/ck807_810/built-in.o arch/csky/lib/built-in.o board/csky/deepeye1000/built-in.o board/csky/common/built-in.o common/spl/built-in.o common/init/built-in.o common/built-in.o cmd/built-in.o drivers/built-in.o dts/built-in.o fs/built-in.o lib/built-in.o --end-group -L /home/al/csky_toolchain/gcc_v3.8.10-kstq-nd-r2/opt/ext-toolchain/bin/../lib/gcc/csky-linux-gnuabiv2/6.3./hard-fp -lgcc -Map u-boot-spl.map -o u-boot-spl)

其中-T u-boot-spl.lds表示从u-boot-spl.lds中读取链接脚本;-Ttext 0xfc000000表示从.text段起始地址为0xfc000000,并且start.o是spl的起始;--start-group和--end-group表示一个group的起始和结束标志,中间是group的内容;-Map u-boot-spl.map表示输出map文件到u-boot-spl.map;-o u-boot-spl表示输出可执行文件到u-boot-spl。

通过u-boot-spl生成u-boot-spl-nodtb.bin文件:

  1. cmd_spl/u-boot-spl-nodtb.bin := csky-abiv2-linux-objcopy -j .text -j .rodata -j .data -j .u_boot_list -j .dtb.init.rodata -O binary spl/u-boot-spl spl/u-boot-spl-nodtb.bin

-j表示将要copy的section名称,-O表示输出文件格式,这个命令将u-boot-spl中特定section以binary格式输出到u-boot-spl-nodtb.bin中。

u-boot-spl.bin和u-boot-spl.nodtb.bin是同样文件:

  1. cmd_spl/u-boot-spl.bin := cp spl/u-boot-spl-nodtb.bin spl/u-boot-spl.bin

从u-boot-spl.bin到u-boot-spl-bh.bin主要是mksheader给u-boot-spl.bin加了个sb_header头。

  1. spl/u-boot-spl.bin: spl/u-boot-spl
  2. @:
  3. tools/mksheader 0xfc000000 0xfc000180 spl/u-boot-spl.bin spl/u-boot-spl-bh.bin
  4. chmod +x spl/u-boot-spl-bh.bin

2.2.1 mksheader给spl加sb_header头

给spl加sb_header头这个工作是由mksheader来做的,决定了load_addr和entry_point,然后根据结果填充了data_size和crc16。

mksheader读取u-boot-spl.bin文件,加上sb_header头之后,生成新的u-boot-spl-bh.bin文件。下面简单看看mksheader这个工具是如何给spl添加sb_header头。

  1. int main(int argc, char *argv[])
  2. {
  3. unsigned short crc_data;
  4. FILE *file_in = NULL;
  5. FILE *file_out = NULL;
  6. int len;
  7. unsigned char *file_buff = NULL;
  8. sb_header header_data;
  9. unsigned int entry_point;
  10. unsigned int load_addr;
  11.  
  12. if (argc < ) {
  13. printf("Please input like this: %s load_addr entry_addr file_input file_output\n", argv[]);
  14. exit();
  15. }
  16. load_addr = strtoul(argv[], , );
  17. entry_point = strtoul(argv[], , );-------------------分别指定load_addrentry_point
  18.  
  19. len = get_file_size(argv[]);---------------------------获取输入文件的大小,指为data_size
  20. file_buff = malloc(len);
  21. if (!file_buff) {
  22. perror("open file failed:\n");
  23. exit();
  24. }
  25.  
  26. file_in = fopen(argv[], "rb+");
  27. if(!file_in) {
  28. perror("open file_in failed:\n");
  29. exit();
  30. }
  31.  
  32. file_out = fopen(argv[], "wb+");
  33. if(!file_out) {
  34. perror("open file_out failed:\n");
  35. exit();
  36. }
  37.  
  38. if (fread(file_buff, , len, file_in) != ) {-----------将u-boot-spl.bin文件读到file_buff中。
  39. ;
  40. // perror("read input file failed.\n");
  41. // exit(1);
  42. }
  43.  
  44. crc_data = check_sum(file_buff, len);-------------------对u-boot-spl.bin文件进行crc校验,结果写到crc16中。
  45. // printf("crc_data=0x%x, len=0x%x load_addr=0x%x entry_point=0x%x\n",
  46. // crc_data, len, load_addr ,entry_point);
  47.  
  48. header_data.magic_num = 0x44454550;---------------------写入magic_num
  49. header_data.data_size = len;
  50. header_data.entry_point = entry_point;
  51. header_data.load_addr = load_addr;
  52. header_data.crc16 = crc_data;
  53.  
  54. fwrite(&header_data, , sizeof(header_data), file_out);
  55. fwrite(file_buff, , len, file_out);--------------------分别将sb_headeru-boot-spl.bin写入到u-boot-spl-bh.bin中。
  56.  
  57. fclose(file_in);
  58. fclose(file_out);
  59. return ;
  60. }

下面通过BeyondCompare对比u-boot-spl.bin和u-boot-spl-bh.bin的差异:

两者相差只有u-boot-spl-bh.bin多了20字节的头,分别是magic_num(0x44454550)、data_size(0x0000a924)、load_addr(0xfc000000)、entry_point(0xfc000180)、crc16(0x00001e48)。

2.3 bootrom解析sb_header头

下面以sd为例介绍bootrom是如何通过解析sb_header来加载spl的。

  1. int sd_card_boot(void)
  2. {
  3. int i;
  4. sb_header header;
  5. u8* p_header = &header;
  6. u32 buffer_addr = ,block_cnt=,col=;
  7. u16 crc16 = ;
  8. LOAD_ENTRY enter_jump_func;
  9.  
  10. char buffer_r[MMC_MAX_BLOCK_LEN];
  11. memset(buffer_r,0x00,MMC_MAX_BLOCK_LEN);-------------------------------------分配一个block大小buffer_r,即512字节。
  12.  
  13. if (mmc_bread(&sd_card_mmc,SD_CARD_BOOT_ADDR, , (u32)buffer_r) != )---------在SD的第66sector,即33KB处读取一个block
  14. {
  15. debug("mmc_bread failed.\n");
  16. return -BOOT_FAILED;
  17. }
  18. memcpy(p_header,buffer_r,sizeof(header));-------------------------------------取buffer_rsb_header大小内容到p_header中。
  19. sdio_debug("magic=0x%x size=0x%x load_addr=0x%x entry_addr=0x%x crc16=0x%x \n",\
  20. header.magic_num,header.data_size,header.load_addr,header.entry_point,header.crc16);
  21.  
  22. if(header.magic_num != MAGIC_NUM)----------------------------------------------检查magic_num是否正确。
  23. {
  24. debug("magic_num error.\n");
  25. return -BOOT_FAILED;//boot failed
  26. }
  27. //get data len, may be need check len (max) error return -1
  28. if(header.data_size > SPL_MAX_LEN)
  29. {
  30. debug("data_size error.\n");
  31. return -BOOT_FAILED;//boot failed
  32. }
  33. //check load address
  34. if(header.load_addr < SRAM_START_ADDRESS)
  35. {
  36. debug("load_addr error.\n");
  37. return -BOOT_FAILED;//boot failed
  38. }
  39. if((header.load_addr+header.data_size)>SPL_MAX_ADDRESS)
  40. {
  41. debug("the data is out of bounds.\n");
  42. return -BOOT_FAILED;//boot failed
  43. }
  44.  
  45. buffer_addr = header.load_addr;--------------------------------------------将整个u-boot-spl.bin加载到的地址。
  46. block_cnt = (header.data_size+sizeof(header)) / sd_card_mmc.read_bl_len;---需要读取总block数目。
  47. col = (header.data_size+sizeof(header))%sd_card_mmc.read_bl_len;
  48.  
  49. for(i=;i<block_cnt;i++)
  50. {
  51. if (mmc_bread(&sd_card_mmc,SD_CARD_BOOT_ADDR+i, , (u32)buffer_addr) != )
  52. {
  53. debug("mmc_bread failed.\n");
  54. return -BOOT_FAILED;
  55. }
  56. if(i == )
  57. {
  58. memcpy((void*)buffer_addr,(void*)(buffer_r+sizeof(header)),(sd_card_mmc.read_bl_len-sizeof(header)));---这里注意在加载的时候已经将sb_header内容剔除,所以SRAMload_addr地址不包含sb_header
  59. buffer_addr += (sd_card_mmc.read_bl_len-sizeof(header));
  60. }
  61. else
  62. {
  63. buffer_addr += sd_card_mmc.read_bl_len;
  64. }
  65. }
  66. if(col)
  67. {
  68. if (mmc_bread(&sd_card_mmc,SD_CARD_BOOT_ADDR+block_cnt, , (u32)buffer_r) != )
  69. {
  70. debug("mmc_bread failed.\n");
  71. return -BOOT_FAILED;
  72. }
  73. memcpy((void*)buffer_addr,(void*)buffer_r,col);
  74.  
  75. }
  76. info_debug("sd card read done.\n");
  77.  
  78. crc16 = check_sum((u8*)header.load_addr, header.data_size);
  79. if(crc16 != (u16)header.crc16)----------------------------------------------进行crc校验并比较,校验的内容是u-boot-spl.bin而不是u-boot-spl-bh.bin
  80. {
  81. debug("checksum error.\n");
  82. return -BOOT_FAILED;//boot failed
  83. }
  84.  
  85. enter_jump_func = (LOAD_ENTRY)header.entry_point;---------------------------spl的执行地址。
  86. enter_jump_func();----------------------------------------------------------将控制权交给spl
  87. return ;
  88. }

bootrom中首先读取spl的sb_header,进行magic_num检查,以及一些地址范围检查;然后根据load_addr和data_size将u-boot-spl.bin加载到SRAM中;在对u-boot-spl.bin进行crc校验后,跳转到entry_point进行执行。

遗留问题:为什么entry_point和load_addr相差0x00000180。

经查跟start.S汇编中的_start入口函数的偏移有关,在_start()之前有0x180字节的异常handler。

这是跟平台相关的,比如很多平台load_addr和entry_point就是相等的。

3. spl加载uboot分析

3.1 重要数据结构

struct spl_image_info是spl加载uboot.bin所需要的信息,其全局变量为spl_image。

  1. struct spl_image_info {
  2. const char *name;
  3. u8 os;------------------表示类型,为uboot
  4. u32 load_addr;----------uboot.bin加载到DDR中的地址。
  5. u32 entry_point;--------从spl跳转到uboot的入口地址。
  6. u32 size;---------------uboot大小。
  7. u32 flags;
  8. };

3.2 mkimage给uboot加image_header_t头

mkimage给u-boot.bin加image_header_t后变成u-boot.img。

  1. MKIMAGEFLAGS_u-boot.img = -A $(ARCH) -T firmware -C none -O u-boot \
  2. -a $(CONFIG_SYS_TEXT_BASE) -e $(CONFIG_SYS_UBOOT_START) \
  3. -n "U-Boot $(UBOOTRELEASE) for $(BOARD) board"
  4.  
  5. quiet_cmd_mkimage = MKIMAGE $@
  6. cmd_mkimage = $(objtree)/tools/mkimage $(MKIMAGEFLAGS_$(@F)) -d $< $@ \
  7. $(if $(KBUILD_VERBOSE:=), >/dev/null)

uboot的编译从u-boot->u-boot-nodtb.bin->u-boot.bin->u-boot.img,经历的过程如下:

-Ttext 0x17a00000表示.text段其实地址为0x17a00000;-o u-boot表示可执行输出文件为u-boot;-T u-boot.lds表示从u-boot.lds中读取链接脚本;程序从start.o中起始;map文件输出到u-boot.map中。

  1. cmd_u-boot := csky-abiv2-linux-ld.bfd -EL --gc-sections -Bstatic -Ttext 0x17a00000 -o u-boot -T u-boot.lds arch/csky/cpu/ck807_810/start.o --start-group arch/csky/cpu/built-in.o arch/csky/cpu/ck807_810/built-in.o arch/csky/lib/built-in.o board/csky/common/built-in.o board/csky/deepeye1000/built-in.o cmd/built-in.o common/built-in.o disk/built-in.o drivers/built-in.o drivers/dma/built-in.o drivers/gpio/built-in.o drivers/i2c/built-in.o drivers/mmc/built-in.o drivers/mtd/built-in.o drivers/mtd/onenand/built-in.o drivers/mtd/spi/built-in.o drivers/net/built-in.o drivers/net/phy/built-in.o drivers/pci/built-in.o drivers/power/built-in.o drivers/power/battery/built-in.o drivers/power/fuel_gauge/built-in.o drivers/power/mfd/built-in.o drivers/power/pmic/built-in.o drivers/power/regulator/built-in.o drivers/serial/built-in.o drivers/spi/built-in.o drivers/usb/common/built-in.o drivers/usb/dwc3/built-in.o drivers/usb/emul/built-in.o drivers/usb/eth/built-in.o drivers/usb/gadget/built-in.o drivers/usb/gadget/udc/built-in.o drivers/usb/host/built-in.o drivers/usb/musb-new/built-in.o drivers/usb/musb/built-in.o drivers/usb/phy/built-in.o drivers/usb/ulpi/built-in.o fs/built-in.o lib/built-in.o net/built-in.o test/built-in.o test/dm/built-in.o --end-group -L /home/al/csky_toolchain/gcc_v3.8.10-kstq-nd-r2/opt/ext-toolchain/bin/../lib/gcc/csky-linux-gnuabiv2/6.3./hard-fp -lgcc -Map u-boot.map

然后将u-boot中特殊section以binary格式拷贝到u-boot-nodtb.bin中:

  1. cmd_u-boot-nodtb.bin := csky-abiv2-linux-objcopy --gap-fill=0xff -j .text -j .rodata -j .data -j .u_boot_list -j .dtb.init.rodata -O binary u-boot u-boot-nodtb.bin

u-boot.bin和u-boot-nodtb.bin是一样的:

  1. cmd_u-boot.bin := cp u-boot-nodtb.bin u-boot.bin

u-boot.img是在u-boot.bin中加了

  1. cmd_u-boot.img := ./tools/mkimage -A csky -T firmware -C none -O u-boot -a 0x17a00000 -e 0x17a00180 -n "U-Boot 2016.07-00058-g6c3df97-dirty for deepeye1000 board" -d u-boot.bin u-boot.img >/dev/null

u-boot.img相较于u-boot.bin多了个image_header/image_header_t,共64字节大小。

ih_magic(0x27051956)、ih_hcrc(0x40700f2b)、ih_time(0x5da3e81e)、ih_size(0x00028510,即去掉header之后的data大小)、ih_load(0x0a000000,uboot加载地址)、id_ep(0x0a000180,uboot入口执行地址)、ih_dcrc(0x6f71d5c9)、ih_os(0x11,IH_OS_U_BOOT)、ih_arch(0x13,IH_ARCH_SANDBOX)、ih_type(0x05,IH_TYPE_FIRMWARE)、ih_comp(0x00,IH_COMP_NONE),后面的就是ih_name字符串。

3.3 加载uboot流程

boot_init_r()中根据启动模式的不同,从存储介质指定地址中读取内容。然后从中解析image_header_t,如果不存在则通过uboot指定。但最终都是赋给spl_image。

  1. void board_init_r(gd_t *dummy1, ulong dummy2)
  2. {
  3. u8 boot_mode;
  4.  
  5. printf("Welcome to SPL!\n");
  6. ...
  7. boot_mode = get_bootmode() & 0x07;
  8. debug("%s:%d, boot mode %d\n", __FUNCTION__, __LINE__, boot_mode);
  9. switch(boot_mode) {
  10. ...
  11. #ifdef CONFIG_SPL_SD_SUPPORT
  12. case BOOT_TYPE_SDCARD:
  13. printf("Load U-Boot from SD ...\n");
  14. spl_mmc_load_image(BOOT_DEVICE_MMC2);----------------从sd中读取uboot,并解析头到spl_iamge中,将uboot加载到DDR中。
  15. break;
  16. #endif
  17. ...
  18. default:
  19. printf("Invalid boot mode 0x%x ...\n", boot_mode);
  20. while ();
  21. }
  22.  
  23. switch (spl_image.os) {
  24. case IH_OS_U_BOOT:
  25. debug("Jumping to U-Boot\n");
  26. break;
  27. default:
  28. debug("Unsupported OS image.. Jumping nevertheless..\n");
  29. }
  30. ...
  31. debug("loaded - jumping to U-Boot...");
  32. jump_to_image_no_args(&spl_image);----------------------从spl_image.entry_point指定的地址开始执行uboot,这个程序没有返回值。
  33.  
  34. while ();
  35. }
  36.  
  37. int spl_mmc_load_image(u32 boot_device)
  38. {
  39. struct mmc *mmc = NULL;
  40. u32 boot_mode;
  41. int err = ;
  42. __maybe_unused int part;
  43.  
  44. err = spl_mmc_find_device(&mmc, boot_device);
  45. if (err)
  46. return err;
  47.  
  48. err = mmc_init(mmc);
  49. ...
  50. boot_mode = spl_boot_mode(boot_device);
  51. err = -EINVAL;
  52. switch (boot_mode) {
  53. ...
  54. case MMCSD_MODE_RAW:
  55. debug("spl: mmc boot mode: raw\n");
  56. ...
  57. #if defined(CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR)
  58. err =mmc_load_image_raw_sector(mmc,
  59. CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR);--------------CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTORubootsd中的起始sector
  60. if (!err)
  61. return err;
  62. #endif
  63. ...
  64. }
  65. return err;
  66. }
  67.  
  68. static int mmc_load_image_raw_sector(struct mmc *mmc, unsigned long sector)
  69. {
  70. unsigned long count;
  71. struct image_header *header;
  72. int ret = ;
  73.  
  74. header = (struct image_header *)(CONFIG_SYS_TEXT_BASE -
  75. sizeof(struct image_header));---------------------CONFIG_SYS_TEXT_BASEuboot.bin加载到DDR中的地址,所以headeruboot.bin往前移struct image_header一段地址。
  76.  
  77. /* read image header to find the image size & load address */
  78. count = blk_dread(mmc_get_blk_desc(mmc), sector, , header);-------将uboot.bin第一个sector写入到header地址。
  79. debug("hdr read sector %lx, count=%lu\n", sector, count);
  80. ...
  81. if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&...
  82. } else {
  83. ret =mmc_load_legacy(mmc, sector, header);
  84. }
  85.  
  86. end:
  87. if (ret) {
  88. #ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
  89. puts("mmc_load_image_raw_sector: mmc block read error\n");
  90. #endif
  91. return -;
  92. }
  93.  
  94. return ;
  95. }
  96.  
  97. DECLARE_GLOBAL_DATA_PTR;
  98.  
  99. static int mmc_load_legacy(struct mmc *mmc, ulong sector,
  100. struct image_header *header)
  101. {
  102. u32 image_size_sectors;
  103. unsigned long count;
  104. int ret;
  105.  
  106. ret =spl_parse_image_header(header);-----------------解析header数据到spl_image中。
  107. if (ret)
  108. return ret;
  109.  
  110. /* convert size to sectors - round up */
  111. image_size_sectors = (spl_image.size + mmc->read_bl_len - ) /
  112. mmc->read_bl_len;------------------------将需要读取的uboot大小转换成sector数目。
  113.  
  114. /* Read the header too to avoid extra memcpy */
  115. count = blk_dread(mmc_get_blk_desc(mmc), sector, image_size_sectors,
  116. (void *)(ulong)spl_image.load_addr);--------读取指定数目secotorspl_image.load_addr中。
  117. debug("read %x sectors to %x\n", image_size_sectors,
  118. spl_image.load_addr);
  119. if (count != image_size_sectors)
  120. return -EIO;
  121.  
  122. return ;
  123. }
  124.  
  125. int spl_parse_image_header(const struct image_header *header)
  126. {
  127. u32 header_size = sizeof(struct image_header);
  128.  
  129. if (image_get_magic(header) == IH_MAGIC) {-----------------定义struct image_header的情况,从header中获取信息填充spl_header
  130. ...
  131. } else {
  132. #ifdef CONFIG_SPL_PANIC_ON_RAW_IMAGE
  133. ...
  134. #else
  135. /* Signature not found - assume u-boot.bin */
  136. debug("mkimage signature not found - ih_magic = %x\n",
  137. header->ih_magic);
  138. spl_set_header_raw_uboot();---------------------------没有通过mkimage生成uboot.bin文件情况,spl_image内容不从struct image_header中获取。
  139. #endif
  140. }
  141. return ;
  142. }
  143.  
  144. void spl_set_header_raw_uboot(void)
  145. {
  146. spl_image.size = CONFIG_SYS_MONITOR_LEN;-----------------表示uboot最大地址。
  147. spl_image.entry_point = CONFIG_SYS_UBOOT_START;----------ubootDDR中起始运行地址。
  148. spl_image.load_addr = CONFIG_SYS_TEXT_BASE;--------------ubootDDR中加载开始地址。
  149. spl_image.os = IH_OS_U_BOOT;-----------------------------表示下一阶段文件类型是uboot
  150. spl_image.name = "U-Boot";
  151. }
    __weak void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)

{
      typedef void __noreturn (*image_entry_noargs_t)(void);

image_entry_noargs_t image_entry =
      (image_entry_noargs_t)(unsigned long)spl_image->entry_point;---跳转到entry_point地址开始执行uboot。

debug("image entry point: 0x%X\n", spl_image->entry_point);
      image_entry();
  }

同样遗留问题:为什么entry_point相对load_addr往后偏移0x00000180。

跟spl同样原因,因为异常处理函数地址占用了0x180字节。所以_start()函数从0x180开始。

4. uboot加载linux分析

首先分析几个重要数据结构image_header_t、bootm_headers_t,然后分析mkimage是如何给zImage加image_header_t后变成uImage的,最后是uboot命令bootm是如何解析image_header_t并加载linux的。

4.1 重要数据结构

image_header_t是legacy镜像文件的头,bootm_headers_t是bootm命令所使用的参数,这些参数主要从image_header_t中获取。

image_header_t是静态的,bootm_headers_t是动态的,还包括其他一些执行bootm执行时所需要的参数。

struct lmb是linux内存范围以及reserved区域。

  1. typedef struct bootm_headers {
  2. /*
  3. * Legacy os image header, if it is a multi component image
  4. * then boot_get_ramdisk() and get_fdt() will attempt to get
  5. * data from second and third component accordingly.
  6. */
  7. image_header_t *legacy_hdr_os; /* image header pointer */-------原始的image_header_t数据。
  8. image_header_t legacy_hdr_os_copy; /* header copy */----------------拷贝后的image_header_t,后续修改使用。
  9. ulong legacy_hdr_valid;--------------------------------------------对image_header_t的检验是否通过,1表示通过。
  10. ...

#ifndef 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

  1. int verify; /* getenv("verify")[0] != 'n' */----------------1表示需要对data进行crc校验。
  2.  
  3. #define BOOTM_STATE_START (0x00000001)
  4. #define BOOTM_STATE_FINDOS (0x00000002)
  5. #define BOOTM_STATE_FINDOTHER (0x00000004)
  6. #define BOOTM_STATE_LOADOS (0x00000008)
  7. #define BOOTM_STATE_RAMDISK (0x00000010)
  8. #define BOOTM_STATE_FDT (0x00000020)
  9. #define BOOTM_STATE_OS_CMDLINE (0x00000040)
  10. #define BOOTM_STATE_OS_BD_T (0x00000080)
  11. #define BOOTM_STATE_OS_PREP (0x00000100)
  12. #define BOOTM_STATE_OS_FAKE_GO (0x00000200) /* 'Almost' run the OS */
  13. #define BOOTM_STATE_OS_GO (0x00000400)
  14. int state;
  15.  
  16. #ifdef CONFIG_LMB
  17. struct lmb lmb; /* for memory mgmt */
  18. #endif
  19. } bootm_headers_t;

typedef struct image_info {
      ulong start, end; /* start/end of blob */----------------------------------------整个镜像包括image_header_t和image data的起始地址。
      ulong image_start, image_len; /* start of image within blob, len of image */-----对应image减去image_header_t大小的地址,image_len对应ih_size。
      ulong load; /* load addr for the image */----------------------------------------对应ih_load。
      uint8_t comp, type, os; /* compression, type of image, os type */----------------对应ih_comp、ih_type、ih_os。
      uint8_t arch; /* CPU architecture */---------------------------------------------对应ih_arch。
  } image_info_t;

  1. typedef struct image_header {
  2. __be32 ih_magic; /* Image Header Magic Number */---------识别镜像的magic numver:#define IH_MAGIC 0x27051956
  3. __be32 ih_hcrc; /* Image Header CRC Checksum */----------指的是image_header_t这部分的crc校验值,在比较之前首先将ih_crc清空,然后对image_header_tcrc校验结果和ih_hcrc进行比较。
  4. __be32 ih_time; /* Image Creation Timestamp */-----------镜像创建时间。
  5. __be32 ih_size; /* Image Data Size */----------------除去image_header_t后的image大小。
  6. __be32 ih_load; /* Data Load Address */---------镜像被加载到的地址。
  7. __be32 ih_ep; /* Entry Point Address */----------linux从此处开始执行。
  8. __be32 ih_dcrc; /* Image Data CRC Checksum */------------镜像除去image_header_t部分的crc校验值。
  9. uint8_t ih_os; /* Operating System */------------镜像的OS类型,比如IH_OS_LINUXIH_OS_LINUX等等。
  10. uint8_t ih_arch; /* CPU architecture */--------------CPU架构类型,比如IH_ARCH_ARMIH_ARCH_SANDBOX等等。
  11. uint8_t ih_type; /* Image Type */----------------镜像类型,比如IH_TYPE_KERNELIH_TYPE_RAMDISK等等。
  12. uint8_t ih_comp; /* Compression Type */--------------镜像压缩类型。
  13. uint8_t ih_name[IH_NMLEN]; /* Image Name */----------镜像名称。
  14. } image_header_t;
  15.  
  16. struct lmb_property {
  17. phys_addr_t base;
  18. phys_size_t size;
  19. };
  20.  
  21. struct lmb_region {
  22. unsigned long cnt;
  23. phys_size_t size;
  24. struct lmb_property region[MAX_LMB_REGIONS+];
  25. };
  26.  
  27. struct lmb {
  28. struct lmb_region memory;
  29. struct lmb_region reserved;
  30. };

4.2 uImage头生成image_header_t流程

内核从vmlinux到生成uImage,经历过Image和zImage。

从vmlinux->Image->zImage->uImage,需要经历如下基本编译命令。

  1. cmd_arch/csky/boot/Image := csky-abiv2-linux-objcopy -O binary vmlinux arch/csky/boot/Image
  2. cmd_arch/csky/boot/zImage := (cat arch/csky/boot/Image | gzip -n -f - > arch/csky/boot/zImage) || (rm -f arch/csky/boot/zImage ; false)
  3. cmd_arch/csky/boot/uImage := /bin/bash ./scripts/mkuboot.sh -A sandbox -O linux -C gzip -T kernel -a -e -n 'Linux-4.9.56' -d arch/csky/boot/zImage arch/csky/boot/uImage

从vmlinux到Image,objcopy仅拷贝vmlinux的binary部分到Image;从Image到zImage,使用gzip进行压缩;从zImage到uImage,经过mkuboot.sh调用mkimage命令添加image_header_t头。

所以后面对uImage的处理是一个反向的过程:需要解析头,然后进行gunzip处理,才会得到和Image同样内容。

mkimage添加image_header_t的过程参考mkimage.c和default_image.c(apt-get source u-boot-tools获取相关源码)。

  1. static void image_set_header(void *ptr, struct stat *sbuf, int ifd,
  2. struct image_tool_params *params)
  3. {
  4. uint32_t checksum;
  5. char *source_date_epoch;
  6. time_t time;
  7.  
  8. image_header_t * hdr = (image_header_t *)ptr;
  9.  
  10. checksum = crc32(,
  11. (const unsigned char *)(ptr +
  12. sizeof(image_header_t)),
  13. sbuf->st_size - sizeof(image_header_t));
  14.  
  15. source_date_epoch = getenv("SOURCE_DATE_EPOCH");
  16. if (source_date_epoch != NULL) {
  17. time = (time_t) strtol(source_date_epoch, NULL, );
  18.  
  19. if (gmtime(&time) == NULL) {
  20. fprintf(stderr, "%s: SOURCE_DATE_EPOCH is not valid\n",
  21. __func__);
  22. time = ;
  23. }
  24. } else {
  25. time = sbuf->st_mtime;
  26. }
  27.  
  28. /* Build new header */
  29. image_set_magic(hdr, IH_MAGIC);
  30. image_set_time(hdr, time);
  31. image_set_size(hdr, sbuf->st_size - sizeof(image_header_t));
  32. image_set_load(hdr, params->addr);
  33. image_set_ep(hdr, params->ep);
  34. image_set_dcrc(hdr, checksum);
  35. image_set_os(hdr, params->os);
  36. image_set_arch(hdr, params->arch);
  37. image_set_type(hdr, params->type);
  38. image_set_comp(hdr, params->comp);
  39.  
  40. image_set_name(hdr, params->imagename);
  41.  
  42. checksum = crc32(, (const unsigned char *)hdr,
  43. sizeof(image_header_t));
  44.  
  45. image_set_hcrc(hdr, checksum);
  46. }

4.3 bootm命令解析

do_bootm()是bootm命令的函数,调用do_bootm_states()进行不同states的顺序执行。bootm_start()进行lmb准备工作;boot_find_os()检查镜像的头,并填充到bootm_headers_t中;bootm_load_os()将镜像解压到指定地址;bootm_os_get_boot_func()根据os类型,选择合适的boot_fn;之后使用boot_fn进行各种架构相关的加载工作。

  1. int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  2. {
  3. ...
  4. /* determine if we have a sub command */
  5. argc--; argv++;
  6. if (argc > ) {
  7. char *endp;
  8.  
  9. simple_strtoul(argv[], &endp, );
  10.  
  11. if ((*endp != ) && (*endp != ':') && (*endp != '#'))
  12. return do_bootm_subcommand(cmdtp, flag, argc, argv);
  13. }
  14.  
  15. return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |
  16. BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |
  17. BOOTM_STATE_LOADOS |
  18. BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |
  19. BOOTM_STATE_OS_GO, &images, );
  20. }
  21.  
  22. int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
  23. int states, bootm_headers_t *images, int boot_progress)
  24. {
  25. boot_os_fn *boot_fn;
  26. ulong iflag = ;
  27. int ret = , need_boot_fn;
  28.  
  29. images->state |= states;
  30.  
  31. if (states & BOOTM_STATE_START)
  32. ret =bootm_start(cmdtp, flag, argc, argv);
  33.  
  34. if (!ret && (states & BOOTM_STATE_FINDOS))
  35. ret =bootm_find_os(cmdtp, flag, argc, argv);
  36.  
  37. if (!ret && (states & BOOTM_STATE_FINDOTHER)) {
  38. ret = bootm_find_other(cmdtp, flag, argc, argv);
  39. argc = ; /* consume the args */
  40. }
  41.  
  42. /* Load the OS */
  43. if (!ret && (states & BOOTM_STATE_LOADOS)) {
  44. ulong load_end;
  45.  
  46. iflag = bootm_disable_interrupts();
  47. ret = bootm_load_os(images, &load_end, );
  48. if (ret == )
  49. lmb_reserve(&images->lmb, images->os.load,
  50. (load_end - images->os.load));
  51. else if (ret && ret != BOOTM_ERR_OVERLAP)
  52. goto err;
  53. else if (ret == BOOTM_ERR_OVERLAP)
  54. ret = ;
  55. #if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY)
  56. if (images->os.os == IH_OS_LINUX)
  57. fixup_silent_linux();
  58. #endif
  59. }
  60. ...
  61. boot_fn = bootm_os_get_boot_func(images->os.os);----------------------------------根据os类型找到对应的boot_fn,对于linux即是do_bootm_linux()。
  62. need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |
  63. BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |
  64. BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);
  65. ...
  66. /* Call various other states that are not generally used */
  67. if (!ret && (states & BOOTM_STATE_OS_CMDLINE))------------------------------------调用boot_fn()执行不同state的功能。
  68. ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);
  69. if (!ret && (states & BOOTM_STATE_OS_BD_T))
  70. ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);
  71. if (!ret && (states & BOOTM_STATE_OS_PREP))
  72. ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);
  73. ...
  74. if (!ret && (states & BOOTM_STATE_OS_GO))
  75. ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,
  76. images, boot_fn);-----------------------------------------------------最后一步,切换到linux
  77. ...
  78. return ret;
  79. }

bootm_start()主要更新struct lmb相关的内存数据以及reserved区域。

  1. static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc,
  2. char * const argv[])
  3. {
  4. memset((void *)&images, , sizeof(images));
  5. images.verify = getenv_yesno("verify");-----------------------------是否需要verify进行checksum
  6.  
  7. boot_start_lmb(&images);--------------------------------------------填充images->lmb,包括总内存memory和预留内存reserved
  8.  
  9. bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start");
  10. images.state = BOOTM_STATE_START;
  11.  
  12. return ;
  13. }
  14.  
  15. static void boot_start_lmb(bootm_headers_t *images)
  16. {
  17. ulong mem_start;
  18. phys_size_t mem_size;
  19.  
  20. lmb_init(&images->lmb);
  21.  
  22. mem_start = getenv_bootm_low();
  23. mem_size = getenv_bootm_size();-------------------------------------分别从环境变量中获取bootm_lowbootm_size两个变量。
  24.  
  25. lmb_add(&images->lmb, (phys_addr_t)mem_start, mem_size);------------增加mem region区域到数据结构中。
  26.  
  27. arch_lmb_reserve(&images->lmb);
  28. board_lmb_reserve(&images->lmb);
  29. }

image_get_kernel()对image文件进行验证,并输出相关信息。

  1. Image Name: Linux-4.9.
  2. Image Type: Sandbox Linux Kernel Image (gzip compressed)
  3. Data Size: Bytes = 4.7 MiB
  4. Load Address:
  5. Entry Point:
  6. Verifying Checksum ... OK
  7. Uncompressing Kernel Image ... OK

bootm_find_os()主要对image_header_t进行检查,并进行镜像data的校验,填充bootm命令运行所需要的数据结构bootm_headers_t。

  1. static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,
  2. char * const argv[])
  3. {
  4. const void *os_hdr;
  5. bool ep_found = false;
  6. int ret;
  7.  
  8. /* get kernel image header, start address and length */
  9. os_hdr =boot_get_kernel(cmdtp, flag, argc, argv,
  10. &images, &images.os.image_start, &images.os.image_len);-----返回值指向image_header_t,同时获取了镜像数据开始和大小。
  11. ...
  12. /* get image parameters */
  13. switch (genimg_get_format(os_hdr)) {--------------------------------os_hdr是镜像header开始,分别判断不同格式的magic number,比如image_header_tfdt_headerandr_img_hdr
  14. #if defined(CONFIG_IMAGE_FORMAT_LEGACY)
  15. case IMAGE_FORMAT_LEGACY:
  16. images.os.type = image_get_type(os_hdr);------------------------这些数据参照bootm_headers_timage_header_t两个数据结构。
  17. images.os.comp = image_get_comp(os_hdr);
  18. images.os.os = image_get_os(os_hdr);
  19.  
  20. images.os.end = image_get_image_end(os_hdr);
  21. images.os.load = image_get_load(os_hdr);
  22. images.os.arch = image_get_arch(os_hdr);
  23. break;
  24. #endif
  25. ...
  26. }
  27. ...
  28. if (images.os.type == IH_TYPE_KERNEL_NOLOAD) {
  29. images.os.load = images.os.image_start;
  30. images.ep += images.os.load;
  31. }
  32.  
  33. images.os.start = map_to_sysmem(os_hdr);
  34.  
  35. return ;
  36. }
  37.  
  38. static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc,
  39. char * const argv[], bootm_headers_t *images,
  40. ulong *os_data, ulong *os_len)
  41. {
  42. #if defined(CONFIG_IMAGE_FORMAT_LEGACY)
  43. image_header_t *hdr;
  44. #endif
  45. ulong img_addr;
  46. const void *buf;
  47. const char *fit_uname_config = NULL;
  48. const char *fit_uname_kernel = NULL;
  49. #if IMAGE_ENABLE_FIT
  50. int os_noffset;
  51. #endif
  52.  
  53. img_addr = genimg_get_kernel_addr_fit(argc < ? NULL : argv[],
  54. &fit_uname_config,
  55. &fit_uname_kernel);------------------bootm的入参包括了镜像文件的加载地址。
  56.  
  57. bootstage_mark(BOOTSTAGE_ID_CHECK_MAGIC);
  58.  
  59. /* copy from dataflash if needed */
  60. img_addr = genimg_get_image(img_addr);---------------------如果没有定义CONFIG_HAS_DATAFLASH,返回的是原地址。
  61.  
  62. /* check image type, for FIT images get FIT kernel node */
  63. *os_data = *os_len = ;
  64. buf = map_sysmem(img_addr, );
  65. switch (genimg_get_format(buf)) {
  66. #if defined(CONFIG_IMAGE_FORMAT_LEGACY)
  67. case IMAGE_FORMAT_LEGACY:
  68. printf("## Booting kernel from Legacy Image at %08lx ...\n",
  69. img_addr);
  70. hdr = image_get_kernel(img_addr, images->verify);------主要对镜像的image_header_t进行magic numberheader crcdata crc检查等。
  71. if (!hdr)
  72. return NULL;
  73. bootstage_mark(BOOTSTAGE_ID_CHECK_IMAGETYPE);
  74.  
  75. /* get os_data and os_len */
  76. switch (image_get_type(hdr)) {
  77. case IH_TYPE_KERNEL:
  78. case IH_TYPE_KERNEL_NOLOAD:
  79. *os_data = image_get_data(hdr);
  80. *os_len = image_get_data_size(hdr);
  81. break;
  82. ...
  83. }
  84.  
  85. memmove(&images->legacy_hdr_os_copy, hdr,
  86. sizeof(image_header_t));
  87.  
  88. images->legacy_hdr_os = hdr;
  89.  
  90. images->legacy_hdr_valid = ;---------------------------表明image_header_t检查通过。
  91. bootstage_mark(BOOTSTAGE_ID_DECOMP_IMAGE);
  92. break;
  93. #endif
  94. ...
  95. default:
  96. printf("Wrong Image Format for %s command\n", cmdtp->name);
  97. bootstage_error(BOOTSTAGE_ID_FIT_KERNEL_INFO);
  98. return NULL;
  99. }
  100.  
  101. debug(" kernel data at 0x%08lx, len = 0x%08lx (%ld)\n",
  102. *os_data, *os_len, *os_len);
  103.  
  104. return buf;
  105. }
  106.  
  107. int genimg_get_format(const void *img_addr)
  108. {
  109. #if defined(CONFIG_IMAGE_FORMAT_LEGACY)
  110. const image_header_t *hdr;
  111.  
  112. hdr = (const image_header_t *)img_addr;
  113. if (image_check_magic(hdr))
  114. return IMAGE_FORMAT_LEGACY;
  115. #endif
  116. ...
  117. return IMAGE_FORMAT_INVALID;
  118. }
  119.  
  120. static image_header_t *image_get_kernel(ulong img_addr, int verify)
  121. {
  122. image_header_t *hdr = (image_header_t *)img_addr;
  123.  
  124. if (!image_check_magic(hdr)) {---------------------------------检查ih_magic
  125. puts("Bad Magic Number\n");
  126. bootstage_error(BOOTSTAGE_ID_CHECK_MAGIC);
  127. return NULL;
  128. }
  129. bootstage_mark(BOOTSTAGE_ID_CHECK_HEADER);
  130.  
  131. if (!image_check_hcrc(hdr)) {----------------------------------检查ih_hcrc,检查之前先拷贝一个image_header_t,然后清空ih_hcrc,再进行crc校验对比。
  132. puts("Bad Header Checksum\n");
  133. bootstage_error(BOOTSTAGE_ID_CHECK_HEADER);
  134. return NULL;
  135. }
  136.  
  137. bootstage_mark(BOOTSTAGE_ID_CHECK_CHECKSUM);
  138. image_print_contents(hdr);-------------------------------------打印镜像名称、类型、大小等等信息。
  139.  
  140. if (verify) {--------------------------------------------------进行进行data部分crc校验。
  141. puts(" Verifying Checksum ... ");
  142. if (!image_check_dcrc(hdr)) {
  143. printf("Bad Data CRC\n");
  144. bootstage_error(BOOTSTAGE_ID_CHECK_CHECKSUM);
  145. return NULL;
  146. }
  147. puts("OK\n");
  148. }
  149. bootstage_mark(BOOTSTAGE_ID_CHECK_ARCH);
  150.  
  151. if (!image_check_target_arch(hdr)) {---------------------------ih_arch检查。
  152. printf("Unsupported Architecture 0x%x\n", image_get_arch(hdr));
  153. bootstage_error(BOOTSTAGE_ID_CHECK_ARCH);
  154. return NULL;
  155. }
  156. return hdr;
  157. }
  158.  
  159. void image_print_contents(const void *ptr)
  160. {
  161. const image_header_t *hdr = (const image_header_t *)ptr;
  162. const char __maybe_unused *p;
  163.  
  164. p = IMAGE_INDENT_STRING;
  165. printf("%sImage Name: %.*s\n", p, IH_NMLEN, image_get_name(hdr));
  166. if (IMAGE_ENABLE_TIMESTAMP) {
  167. printf("%sCreated: ", p);
  168. genimg_print_time((time_t)image_get_time(hdr));
  169. }
  170. printf("%sImage Type: ", p);
  171. image_print_type(hdr);
  172. printf("%sData Size: ", p);
  173. genimg_print_size(image_get_data_size(hdr));
  174. printf("%sLoad Address: %08x\n", p, image_get_load(hdr));
  175. printf("%sEntry Point: %08x\n", p, image_get_ep(hdr));
  176.  
  177. if (image_check_type(hdr, IH_TYPE_MULTI) ||
  178. image_check_type(hdr, IH_TYPE_SCRIPT)) {
  179. int i;
  180. ulong data, len;
  181. ulong count = image_multi_count(hdr);
  182.  
  183. printf("%sContents:\n", p);
  184. for (i = ; i < count; i++) {
  185. image_multi_getimg(hdr, i, &data, &len);
  186.  
  187. printf("%s Image %d: ", p, i);
  188. genimg_print_size(len);
  189.  
  190. if (image_check_type(hdr, IH_TYPE_SCRIPT) && i > ) {
  191. printf("%s Offset = 0x%08lx\n", p, data);
  192. }
  193. }
  194. }
  195. }

bootm_find_other()尝试从boot文件中解析出ramdisk等部分。

  1. static int bootm_find_other(cmd_tbl_t *cmdtp, int flag, int argc,
  2. char * const argv[])
  3. {
  4. if (((images.os.type == IH_TYPE_KERNEL) ||
  5. (images.os.type == IH_TYPE_KERNEL_NOLOAD) ||
  6. (images.os.type == IH_TYPE_MULTI)) &&
  7. (images.os.os == IH_OS_LINUX ||
  8. images.os.os == IH_OS_VXWORKS))
  9. returnbootm_find_images(flag, argc, argv);
  10.  
  11. return ;
  12. }
  13.  
  14. int bootm_find_images(int flag, int argc, char * const argv[])
  15. {
  16. int ret;
  17.  
  18. /* find ramdisk */
  19. ret = boot_get_ramdisk(argc, argv, &images, IH_INITRD_ARCH,
  20. &images.rd_start, &images.rd_end);
  21. if (ret) {
  22. puts("Ramdisk image is corrupt or invalid\n");
  23. return ;
  24. }
  25. ...
  26. return ;
  27. }
  28.  
  29. int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images,
  30. uint8_t arch, ulong *rd_start, ulong *rd_end)
  31. {
  32. ulong rd_addr, rd_load;
  33. ulong rd_data, rd_len;
  34. #if defined(CONFIG_IMAGE_FORMAT_LEGACY)
  35. const image_header_t *rd_hdr;
  36. #endif
  37. void *buf;
  38. #ifdef CONFIG_SUPPORT_RAW_INITRD
  39. char *end;
  40. #endif
  41.  
  42. const char *select = NULL;
  43.  
  44. *rd_start = ;
  45. *rd_end = ;
  46.  
  47. if (argc >= )
  48. select = argv[];
  49.  
  50. /*
  51. * Look for a '-' which indicates to ignore the
  52. * ramdisk argument
  53. */
  54. if (select && strcmp(select, "-") == ) {
  55. debug("## Skipping init Ramdisk\n");
  56. rd_len = rd_data = ;
  57. } else if (select || genimg_has_config(images)) {
  58. ...if (!rd_data) {
  59. debug("## No init Ramdisk\n");
  60. } else {
  61. *rd_start = rd_data;
  62. *rd_end = rd_data + rd_len;
  63. }
  64. debug(" ramdisk start = 0x%08lx, ramdisk end = 0x%08lx\n",
  65. *rd_start, *rd_end);
  66.  
  67. return ;
  68. }

bootm_load_os()主要是将镜像的data部分调用os.comp解压算法从images.os.image_start解压到images.os.load。

  1. static int bootm_load_os(bootm_headers_t *images, unsigned long *load_end,
  2. int boot_progress)
  3. {
  4. image_info_t os = images->os;
  5. ulong load = os.load;
  6. ulong blob_start = os.start;
  7. ulong blob_end = os.end;
  8. ulong image_start = os.image_start;
  9. ulong image_len = os.image_len;
  10. bool no_overlap;
  11. void *load_buf, *image_buf;
  12. int err;
  13.  
  14. load_buf = map_sysmem(load, );------------------------------os.image_start是解压前镜像存放地址,os.load是解压后镜像存放地址。
  15. image_buf = map_sysmem(os.image_start, image_len);-----------镜像的存放地址为0x86000000,image_header_t的大小为64字节,所以os.image_start地址为0x86000040
  16.  
  17. err =bootm_decomp_image(os.comp, load, os.image_start, os.type,
  18. load_buf, image_buf, image_len,
  19. CONFIG_SYS_BOOTM_LEN, load_end);---------------image_buf是解压前数据存放处,load_buf是解压后数据存放处;load是解压数据起始地址,load_end是解压后数据末地址。
  20. if (err) {
  21. bootstage_error(BOOTSTAGE_ID_DECOMP_IMAGE);
  22. return err;
  23. }
  24. flush_cache(load, ALIGN(*load_end - load, ARCH_DMA_MINALIGN));
  25.  
  26. debug(" kernel loaded at 0x%08lx, end = 0x%08lx\n", load, *load_end);
  27. bootstage_mark(BOOTSTAGE_ID_KERNEL_LOADED);------------------数据已经被解压加载到指定地址,可以执行。
  28.  
  29. no_overlap = (os.comp == IH_COMP_NONE && load == image_start);
  30. ...
  31. return ;
  32. }
  33.  
  34. int bootm_decomp_image(int comp, ulong load, ulong image_start, int type,
  35. void *load_buf, void *image_buf, ulong image_len,
  36. uint unc_len, ulong *load_end)
  37. {
  38. ...
  39. switch (comp) {
  40. ...
  41. #ifdef CONFIG_GZIP
  42. case IH_COMP_GZIP: {
  43. ret = gunzip(load_buf, unc_len, image_buf, &image_len);--调用具体解压算法进行解压缩。
  44. break;
  45. }
  46. #endif /* CONFIG_GZIP */...
  47. }
  48. ...
  49. *load_end = load + image_len;
  50.  
  51. puts("OK\n");
  52.  
  53. return ;
  54. }

bootm_os_get_boot_func()根据os的类型,执行kernel的entry point。

对于Linux来说就是do_bootm_linux,根据架构进行准备必要的准备,然后跳转到entry point,将CPU执行权交给Linux。

  1. boot_os_fn *bootm_os_get_boot_func(int os)
  2. {
  3. returnboot_os[os];
  4. }
  5.  
  6. static boot_os_fn *boot_os[] = {
  7. [IH_OS_U_BOOT] = do_bootm_standalone,
  8. #ifdef CONFIG_BOOTM_LINUX
  9. [IH_OS_LINUX] =do_bootm_linux,
  10. #endif...
  11. };
  12.  
  13. int do_bootm_linux(int flag, int argc, char * const argv[],
  14. bootm_headers_t *images)
  15. {
  16. void (*theKernel)(int magic, void * params);
  17. char *tmp;
  18. unsigned int dtb_load_addr;
  19.  
  20. theKernel = (void (*)(int, void *))images->ep;----------------images->epuboot跳转到linux的入口点。
  21. printf("\nStarting kernel ... \n\n");
  22.  
  23. disable_interrupts();
  24. flush_cache(,);
  25. theKernel (0x20150401, (void *)dtb_load_addr);----------------跳转到linux,两个入参。
  26. return ;
  27. }

5. 小结

从以上分析可知,每一个阶段启动下一阶段都是通过识别头开始的。

mksheader给spl加sb_header头,bootrom进行解析;mkimage给uboot加image_header_t头,spl进行解析;mkimage给kernel加image_header_t头,uboot进行解析。

都是通过工具在程序代码之前加上一个头,然后上一级工具进行解析加载。

bootrom/spl/uboot/linux逐级加载是如何实现的?的更多相关文章

  1. QEMU 运行uboot,动态加载内核与文件系统

    背景 上一讲我们完成了 编译 QEMU 以及简单地做了仿真.这一讲在 启动uboot 的基础上进行,以加强对于 运行地址,加载地址等理解. 有关资料: uboot 与 代码重定位 有这样的约定,ubo ...

  2. 驱动开发学习笔记. 0.07 Uboot链接地址 加载地址 和 链接脚本地址

    驱动开发学习笔记. 0.07 Uboot链接地址 加载地址 和 链接脚本地址 最近重新看了乾龙_Heron的<ARM 上电启动及 Uboot 代码分析>(下简称<代码分析>) ...

  3. 【easyui】treegrid逐级加载源码

    当初看这源码的目的是: 1.treegrid是怎么实现逐级加载树结构的. 解: 见demo,主要就是点击节点的时候会请求后台. 2.treegrid加载后,第二次展开节点会不会再次请求后台. 解:第二 ...

  4. Linux内核加载全流程

    无论是Linux还是Windows,在加电后的第一步都是先运行BIOS(Basic Input/Output System)程序——不知道是不是所以的电脑系统都是如此.BIOS保存在主板上的一个non ...

  5. linux配置加载顺序

    linux加载配置项时通过下面方式 首先 加载/etc/profile配置 然后 加载/ect/profile.d/下面的所有脚本 然后 加载当前用户 .bash_profile 然后 加载.bash ...

  6. Linux firmware 加载【转】

    转自:http://blog.chinaunix.net/uid-22028680-id-3157922.html 1.request_firmware在内核使用,需要文件系统支持,就是说,启动的时候 ...

  7. [ARM-Linux开发]Linux下加载.ko驱动模块的两种方法:insmod与modprobe

    假设要加载的驱动程序模块名为SHT21.ko 加载驱动模块 方法一:  进入SHT21.ko驱动模块文件所在的目录,然后直接  insmod SHT21.ko  即可 方法二:  将SHT21.ko文 ...

  8. linux C 加载so文件 指定路径

    在Linux C中动态加载.so文件用dlopen("libdemo.so", RTLD_NOW); 但是默认的so搜索目录不包括当前程序目录,所以必须复制到系统的so目录 才能运 ...

  9. Linux开机加载过程

    2015-01-06 10:29:13   目录 1 开机加载简介 2 常规加载流程 2.1 加载BIOS 2.2 读取MBR 2.3 boot loader 2.4 加载内核 2.5 init依据i ...

随机推荐

  1. webpack 配置babel-loader babel7

    babel 7版本配置 在webpack中 默认只能处理部分 ES6的新语法,一些更高级的ES6或ES7的语法,webpack是处理不了的这个时候就需要借助第三方的loader 来帮助webpack ...

  2. socket互传对象以及IO流的顺序问题

    UserInfo.java package com.company.s6; import java.io.Serializable; public class UserInfo implements ...

  3. MS16-072域内中间人攻击

    0x01 漏洞利用 在目标主机域用户口令已知的条件下,目标主机在进行策略更新时,对域服务器的认证存在漏洞,攻击者劫持认证服务器的过程,引导至伪造的域服务器,并在域服务器中制定用户的计划任务策略,可以获 ...

  4. navicat连接不上Linux服务器上的mysql的解决办法

    一开始,心情是沉痛的,截图如下: 转载请注明出处:https://www.cnblogs.com/NaughtyCat/p/how-to-connect-to-mysql-on-linux-by-na ...

  5. qcom 8953平台 LCD亮灭屏流程及LCD知识点总结【转】

    一.LK中亮屏流程 gcdb_display_init(),进行display初始化的起始地方: oem_panel_select(),在这里去选择哪一款屏,也可以在这里添加新一款屏: dsi_pan ...

  6. [Go]TCP服务中增加消息队列与工作池

    之前的处理中每一个连接都会创建一个主groutine , 每个连接中的主groutine中创建出读groutine 和写groutine 每个连接处理业务再单独开出一个groutine ,这样如果有1 ...

  7. 选择IT公司的雇主提问

    做为IT从业人员,我们去一家公司时,判断一家公司的专业性时,可以通过以下提问获得反馈: 技术问题 1.这个项目使用了哪些技术(语言,框架,库)?2.应用程序是一体化架构还是微服务架构?3.采用了哪些设 ...

  8. 五、如何通过CT三维图像得到DRR图像

    一.介绍 获取DRR图像是医疗图像配准里面的一个重要的前置步骤:它的主要目的是,通过CT三维图像,获取模拟X射线影像,这个过程也被称为数字影响重建. 在2D/3D的配准流程里面,需要首先通过CT三维图 ...

  9. 关于String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";

    关于 String path = request.getContextPath(); String basePath = request.getScheme()+"://"+req ...

  10. JS获取url请求参数

    JS获取url请求参数,代码如下: // 获取url请求参数 function getQueryParams() { var query = location.search.substring(1) ...