U-Boot啓動內核流程

先來引用一下這篇介紹“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行開始有:
  1. #ifdef CONFIG_PPC
  2. static boot_os_Fcn do_bootm_linux;
  3. #else
  4. extern boot_os_Fcn do_bootm_linux;
  5. #endif
複製代碼

這裏的預編譯宏說明了,非 PPC體系結構的CPU的do_bootm_linux()函數都不是在這個文件內實現的(extern)。可想而知,這個函數的實現應該是和體系結構相關的,具體到arm體系結構的實現就是在lib_arm/armlinux.c這個文件當中。可以看到從lib_arm/armlinux.c中的第77 行開始就是do_bootm_linux()函數的實現。

其中第85行聲明瞭這樣一個函數指針theKernel:

  1. void (*theKernel)(int zero, int arch, uint params);
複製代碼

看看它的名字和參數的命名我們也可以猜到這個其實就是內核的入口函數的指針了。幾個參數的命名也說明了上文提到的ARM Linux內核啓動要求的第一條,因爲根據ACPS(ARM/Thumb Procedure Call Standard)的規定,這三個參數就是依次使用r0,r1和r2來傳遞的。

接下來第93行就是給這個函數指針賦值:

  1. 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行:

  1. 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命令的處理函數。
  1. ……

  2. image_header_t header;

  3. ulong load_addr = CFG_LOAD_ADDR; /* Default Load Address */

  4. int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
  5. {
  6. ulong iflag;
  7. ulong addr;
  8. ulong data, len, checksum;
  9. ulong *len_ptr;
  10. uint unc_len = 0x400000;
  11. int i, verify;
  12. char *name, *s;
  13. int (*appl)(int, char *[]);
  14. image_header_t *hdr = &header;
複製代碼


讀取uboot的環境變量verify,如果環境變量verify等於’n’,則局部變量verify賦值成爲0;如果環境變量verify爲空(即沒有定義環境變量verify)或者環境變量verify不等於’n’,則局部變量verify賦值成爲1。
  1. s = getenv ("verify");
  2. verify = (s && (*s == 'n')) ? 0 : 1;
複製代碼

如果參數個數小於2(即只是輸入了bootm),使用缺省加載地址CFG_LOAD_ADDR;否則使用第二個參數作爲加載地址。
  1. if (argc < 2) {
  2. addr = load_addr;
  3. } else {
  4. addr = simple_strtoul(argv[1], NULL, 16);
  5. }

  6. SHOW_BOOT_PROGRESS (1);
  7. 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。
  1. #ifdef CONFIG_HAS_DATAFLASH
  2. if (addr_dataflash(addr)){
  3. read_dataflash(addr, sizeof(image_header_t), (char *)&header);
  4. } else
  5. #endif
  6. memmove (&header, (char *)addr, sizeof(image_header_t));
複製代碼


判斷image header的magic是否匹配,如果不匹配,說明下載過程中發生了錯誤。
  1. if (ntohl(hdr->ih_magic) != IH_MAGIC) {
  2. #ifdef __I386__ /* correct image format not implemented yet - fake it */
  3. if (fake_header(hdr, (void*)addr, -1) != NULL) {
  4. /* to compensate for the addition below */
  5. addr -= sizeof(image_header_t);
  6. /* turnof verify,
  7. * fake_header() does not fake the data crc
  8. */
  9. verify = 0;
  10. } else
  11. #endif /* __I386__ */
  12. {
  13. puts ("Bad Magic Number\n");
  14. SHOW_BOOT_PROGRESS (-1);
  15. return 1;
  16. }
  17. }
  18. SHOW_BOOT_PROGRESS (2);
複製代碼


校驗image header的CRC以及image data的CRC,如果校驗不匹配,說明下載過程中發生了錯誤。
  1. data = (ulong)&header;
  2. len = sizeof(image_header_t);

  3. checksum = ntohl(hdr->ih_hcrc);
  4. hdr->ih_hcrc = 0;

  5. if (crc32 (0, (char *)data, len) != checksum) {
  6. puts ("Bad Header Checksum\n");
  7. SHOW_BOOT_PROGRESS (-2);
  8. return 1;
  9. }
  10. SHOW_BOOT_PROGRESS (3);

  11. /* for multi-file images we need the data part, too */
  12. print_image_hdr ((image_header_t *)addr);

  13. data = addr + sizeof(image_header_t);
  14. len = ntohl(hdr->ih_size);

  15. #ifdef CONFIG_HAS_DATAFLASH
  16. if (addr_dataflash(addr)){
  17. read_dataflash(data, len, (char *)CFG_LOAD_ADDR);
  18. data = CFG_LOAD_ADDR;
  19. }
  20. #endif

  21. if (verify) {
  22. puts (" Verifying Checksum ... ");
  23. if (crc32 (0, (char *)data, len) != ntohl(hdr->ih_dcrc)) {
  24. printf ("Bad Data CRC\n");
  25. SHOW_BOOT_PROGRESS (-3);
  26. return 1;
  27. }
  28. puts ("OK\n");
  29. }
  30. SHOW_BOOT_PROGRESS (4);
複製代碼


判斷體系結構。
  1. len_ptr = (ulong *)data;

  2. #if defined(__PPC__)
  3. if (hdr->ih_arch != IH_CPU_PPC)
  4. #elif defined(__ARM__)
  5. if (hdr->ih_arch != IH_CPU_ARM)
  6. #elif defined(__I386__)
  7. if (hdr->ih_arch != IH_CPU_I386)
  8. #elif defined(__mips__)
  9. if (hdr->ih_arch != IH_CPU_MIPS)
  10. #elif defined(__nios__)
  11. if (hdr->ih_arch != IH_CPU_NIOS)
  12. #elif defined(__M68K__)
  13. if (hdr->ih_arch != IH_CPU_M68K)
  14. #elif defined(__microblaze__)
  15. if (hdr->ih_arch != IH_CPU_MICROBLAZE)
  16. #else
  17. # error Unknown CPU type
  18. #endif
  19. {
  20. printf ("Unsupported Architecture 0x%x\n", hdr->ih_arch);
  21. SHOW_BOOT_PROGRESS (-4);
  22. return 1;
  23. }
  24. SHOW_BOOT_PROGRESS (5);
複製代碼


判斷image類型。
  1. switch (hdr->ih_type) {
  2. case IH_TYPE_STANDALONE:
  3. name = "Standalone Application";
  4. /* A second argument overwrites the load address */
  5. if (argc > 2) {
  6. hdr->ih_load = simple_strtoul(argv[2], NULL, 16);
  7. }
  8. break;
  9. case IH_TYPE_KERNEL:
  10. name = "Kernel Image";
  11. break;
  12. case IH_TYPE_MULTI:
  13. name = "Multi-File Image";
  14. len = ntohl(len_ptr[0]);
  15. /* OS kernel is always the first image */
  16. data += 8; /* kernel_len + terminator */
  17. for (i=1; len_ptr[i]; ++i)
  18. data += 4;
  19. break;
  20. default: printf ("Wrong Image Type for %s command\n", cmdtp->name);
  21. SHOW_BOOT_PROGRESS (-5);
  22. return 1;
  23. }
  24. SHOW_BOOT_PROGRESS (6);

  25. /*
  26. * We have reached the point of no return: we are going to
  27. * overwrite all exception vector code, so we cannot easily
  28. * recover from any failures any more...
  29. */

  30. iflag = disable_interrupts();

  31. #ifdef CONFIG_AMIGAONEG3SE
  32. /*
  33. * We've possible left the caches enabled during
  34. * bios emulation, so turn them off again
  35. */
  36. icache_disable();
  37. invalidate_l1_instruction_cache();
  38. flush_data_cache();
  39. dcache_disable();
  40. #endif
複製代碼


判斷image壓縮類型
  1. switch (hdr->ih_comp) {
  2. case IH_COMP_NONE: 沒有壓縮
  3. if(ntohl(hdr->ih_load) == addr) { 如果image header中指示的加載地址和bootm命令中參數2指定的地址相同,則表示不需要copy,可以就地執行。
  4. printf (" XIP %s ... ", name);
  5. } else {
  6. #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
  7. size_t l = len;
  8. void *to = (void *)ntohl(hdr->ih_load);
  9. void *from = (void *)data;

  10. printf (" Loading %s ... ", name);

  11. while (l > 0) {
  12. size_t tail = (l > CHUNKSZ) ? CHUNKSZ : l;
  13. WATCHDOG_RESET();
  14. memmove (to, from, tail);
  15. to += tail;
  16. from += tail;
  17. l -= tail;
  18. }
  19. #else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG) */
複製代碼

如果image header中指示的加載地址和bootm命令中參數2指定的地址不相同,則表示要從image header中指示的加載地址處把image data copy到bootm命令中參數2指定的地址處,然後再執行。
  1. memmove ((void *) ntohl(hdr->ih_load), (uchar *)data, len);
  2. #endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */
  3. }
  4. break;
  5. case IH_COMP_GZIP:
  6. printf (" Uncompressing %s ... ", name);
  7. if (gunzip ((void *)ntohl(hdr->ih_load), unc_len,
  8. (uchar *)data, (int *)&len) != 0) {
  9. puts ("GUNZIP ERROR - must RESET board to recover\n");
  10. SHOW_BOOT_PROGRESS (-6);
  11. do_reset (cmdtp, flag, argc, argv);
  12. }
  13. break;
  14. #ifdef CONFIG_BZIP2
  15. case IH_COMP_BZIP2:
  16. printf (" Uncompressing %s ... ", name);
  17. /*
  18. * If we've got less than 4 MB of malloc() space,
  19. * use slower decompression algorithm which requires
  20. * at most 2300 KB of memory.
  21. */
  22. i = BZ2_bzBuffToBuffDecompress ((char*)ntohl(hdr->ih_load),
  23. &unc_len, (char *)data, len,
  24. CFG_MALLOC_LEN < (4096 * 1024), 0);
  25. if (i != BZ_OK) {
  26. printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i);
  27. SHOW_BOOT_PROGRESS (-6);
  28. udelay(100000);
  29. do_reset (cmdtp, flag, argc, argv);
  30. }
  31. break;
  32. #endif /* CONFIG_BZIP2 */
  33. default:
  34. if (iflag)
  35. enable_interrupts();
  36. printf ("Unimplemented compression type %d\n", hdr->ih_comp);
  37. SHOW_BOOT_PROGRESS (-7);
  38. return 1;
  39. }
  40. puts ("OK\n");
  41. SHOW_BOOT_PROGRESS (7);
複製代碼


根據image 執行type來決定如何引導。

  1. switch (hdr->ih_type) {
  2. case IH_TYPE_STANDALONE:
  3. if (iflag)
  4. enable_interrupts();

  5. /* load (and uncompress), but don't start if "autostart"
  6. * is set to "no"
  7. */
  8. if (((s = getenv("autostart")) != NULL) && (strcmp(s,"no") == 0)) {
  9. char buf[32];
  10. sprintf(buf, "%lX", len);
  11. setenv("filesize", buf);
  12. return 0;
  13. }
  14. appl = (int (*)(int, char *[]))ntohl(hdr->ih_ep);
  15. (*appl)(argc-1, &argv[1]);
  16. return 0;
  17. case IH_TYPE_KERNEL:
  18. case IH_TYPE_MULTI:
  19. /* handled below */
  20. break; 下面將有代碼專門處理這兩種image類型
  21. default:
  22. if (iflag)
  23. enable_interrupts();
  24. printf ("Can't boot image type %d\n", hdr->ih_type);
  25. SHOW_BOOT_PROGRESS (-8);
  26. return 1;
  27. }
  28. SHOW_BOOT_PROGRESS (8);
複製代碼


根據image 的OS type來決定如何引導
  1. switch (hdr->ih_os) {
  2. default: /* handled by (original) Linux case */
  3. case IH_OS_LINUX:
  4. #ifdef CONFIG_SILENT_CONSOLE
  5. fixup_silent_linux();
  6. #endif
  7. do_bootm_linux (cmdtp, flag, argc, argv,
  8. addr, len_ptr, verify);
  9. break;
  10. case IH_OS_NETBSD:
  11. do_bootm_netbsd (cmdtp, flag, argc, argv,
  12. addr, len_ptr, verify);
  13. break;

  14. #ifdef CONFIG_LYNXKDI
  15. case IH_OS_LYNXOS:
  16. do_bootm_lynxkdi (cmdtp, flag, argc, argv,
  17. addr, len_ptr, verify);
  18. break;
  19. #endif

  20. case IH_OS_RTEMS:
  21. do_bootm_rtems (cmdtp, flag, argc, argv,
  22. addr, len_ptr, verify);
  23. break;

  24. #if (CONFIG_COMMANDS & CFG_CMD_ELF)
  25. case IH_OS_VXWORKS:
  26. do_bootm_vxworks (cmdtp, flag, argc, argv,
  27. addr, len_ptr, verify);
  28. break;
  29. case IH_OS_QNX:
  30. do_bootm_qnxelf (cmdtp, flag, argc, argv,
  31. addr, len_ptr, verify);
  32. break;
  33. #endif /* CFG_CMD_ELF */
  34. #ifdef CONFIG_ARTOS
  35. case IH_OS_ARTOS:
  36. do_bootm_artos (cmdtp, flag, argc, argv,
  37. addr, len_ptr, verify);
  38. break;
  39. #endif
  40. }

  41. SHOW_BOOT_PROGRESS (-9);
  42. #ifdef DEBUG
  43. puts ("\n## Control returned to monitor - resetting...\n");
  44. do_reset (cmdtp, flag, argc, argv);
  45. #endif
  46. return 1;
  47. }
複製代碼

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 指定製作映象的源文件

本文轉自:http://blog.chinaunix.net/u/17660/showart_279896.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章