[i.MX6Q][QNX Neutrino 6.6.0]调试笔记------IPL源码分析

QNX源码下三大主分支,也是组成QNX系统的三大模块:Initial program loader(IPL)StartupFlash Filesystem

1、IPL介绍

   IPL是一段初始化启动程序类似于uboot,在启动QNX时也可以用uboot替代,不过IPL更加简洁,启动时间更快。IPL的主要职责就是进行最小的硬件配置以启动Startup程序,从而启动microkernel,至少包括以下工作:

①从重置向量开始执行

②配置内存控制器

③配置时钟

④设置一个栈,以允许IPL库执行操作系统的验证和设置(download, scan, set up, and jump to the OS image)

根据bsp_working_dir/src/hardware/ipl/boards/mx6q_sabresmart/mx6q_sabresmart.lnk的链接来看


   
   
   
   
  1. TARGET(elf32-littlearm)
  2. OUTPUT_FORMAT(elf32-littlearm)
  3. ENTRY(_start)
  4. MEMORY
  5. {
  6. stack : ORIGIN = 0x90C000, LENGTH = 0x1000
  7. rom : ORIGIN = 0x907000, LENGTH = 0x6000
  8. }
  9. SECTIONS
  10. {
  11. .text : {
  12. image_header.o (.text.imageheader)
  13. *(.text)
  14. *(.note.gnu.build-id)
  15. *(.rodata*)
  16. } > rom
  17. . = ALIGN( 4);
  18. _etext = .;
  19. . data : { *(. data)
  20. *(.sdata)
  21. } > rom
  22. . = ALIGN( 4);
  23. _ecopy = .;
  24. .bss : {
  25. *(.bss)
  26. *(.sbss)
  27. } > rom
  28. }

IPL第一个执行的部分就是bsp_working_dir/src/hardware/ipl/boards/mx6q_sabresmart/_start.S,这是一段汇编代码


   
   
   
   
  1. _start:
  2. /*
  3. * 将CPU设置为SVC32管理模式
  4. */
  5. mrs r0, cpsr
  6. bic r0, r0, #0x1f
  7. orr r0, r0, #0xd3
  8. msr cpsr,r0
  9. /*
  10. * 禁止 L1 I/D and TLBs
  11. */
  12. mov r0, #0 @ set up for MCR
  13. mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
  14. mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
  15. /*
  16. * 禁止 MMU and Caches
  17. */
  18. mrc p15, 0, r0, c1, c0, 0
  19. bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
  20. bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
  21. orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
  22. orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB
  23. mcr p15, 0, r0, c1, c0, 0
  24. mrc p15, 0, ip, c1, c0, 0
  25. orr ip, ip, #(1 << 12) // enable I Cache
  26. mcr p15, 0, ip, c1, c0, 0
  27. dsb
  28. isb
  29. mov r0, r0
  30. mov r0, r0
  31. mov r0, r0
  32. mov r0, r0
  33. inv_dcache /*禁止D-Cache*/
  34. init_l2cc /*禁止I-Cache*/
  35. /* Setup the Stack */
  36. ldr sp, = 0x90C000
  37. bl main /*跳入C语言代码*/

bsp_working_dir/src/hardware/ipl/boards/mx6q_sabresmart/main.c


   
   
   
   
  1. int main()
  2. {
  3. unsigned image = QNX_LOAD_ADDR; //0x18000000
  4. char c = 'M'; /* default: load ifs from SD card */
  5. /* Allow access to the AIPS registers */
  6. init_aips();
  7. /* Initialise the system clocks */
  8. init_clocks();
  9. init_pinmux(); //引脚配置,这里包括串口、装置image的SD
  10. /* Init serial interface */
  11. /* 115200bps, 80MHz clk, divisor (RFDIV 1-7) 2 */
  12. init_sermx6(MX6X_UART1_BASE, 115200, 80000000, 2); //初始化串口
  13. ser_putstr( "\nWelcome to QNX Neutrino Initial Program Loader for Freescale i.MX6Q Sabre-Smart (ARM Cortex-A9 MPCore)\n");
  14. while ( 1) {
  15. if (!c) {
  16. ser_putstr( "Command:\n");
  17. ser_putstr( "Press 'D' for serial download, using the 'sendnto' utility\n");
  18. ser_putstr( "Press 'M' for SDMMC download, IFS filename MUST be 'QNX-IFS'.\n");
  19. c = ser_getchar();
  20. }
  21. switch (c) {
  22. case 'D':
  23. case 'd':
  24. ser_putstr( "send image now...\n");
  25. if (image_download_ser(image)) {
  26. ser_putstr( "download failed...\n");
  27. c = 0;
  28. }
  29. else
  30. ser_putstr( "download OK...\n");
  31. break;
  32. case 'M':
  33. case 'm':
  34. ser_putstr( "SDMMC download...\n");
  35. if (sdmmc_load_file(image, "QNX-IFS") == 0) { //加载image(QNX-IFS),所以需要把编译的好的image 改名为QNX-IFS
  36. ser_putstr( "load image done.\n");
  37. /* Proceed to image scan */
  38. }
  39. else {
  40. ser_putstr( "Load image failed.\n");
  41. c = 0;
  42. }
  43. break;
  44. default:
  45. ser_putstr( "Unknown command.\n");
  46. c = 0;
  47. }
  48. if (!c) continue;
  49. image = image_scan_2(image, image + 0x200, 1); //关键函数1
  50. if (image != 0xffffffff) {
  51. ser_putstr( "Found image @ 0x");
  52. ser_puthex(image);
  53. ser_putstr( "\n");
  54. image_setup(image); //关键函数2
  55. ser_putstr( "Jumping to startup @ 0x");
  56. ser_puthex(startup_hdr.startup_vaddr);
  57. ser_putstr( "\n\n");
  58. image_start(image); //关键函数3
  59. /* Never reach here */
  60. return 0;
  61. }
  62. ser_putstr( "Image_scan failed...\n");
  63. }
  64. return 0;
  65. }

在分析三个关键函数之前,先了解下一个重要的结构体struct startup_header ,定义在

   
   
   
   
  1. struct startup_header {
  2. unsigned long signature; /* 头标志 0x00FF7EEB */
  3. unsigned short version; /* mkifs版本号 */
  4. unsigned char flags1; /*IS Misc flags, see below*/
  5. unsigned char flags2; /* No flags defined yet*/
  6. unsigned short header_size; /* sizeof(struct startup_header) */
  7. unsigned short machine; /* 机器型号 sys/elf.h*/
  8. unsigned long startup_vaddr; /* 在IPL执行完后的跳转地址,也就是startup开始执行的地址*/
  9. unsigned long paddr_bias; /*S Value to add to physical address*/
  10. /* to get a value to put into a*/
  11. /* pointer and indirected through*/
  12. unsigned long image_paddr; /* image的物理地址*/
  13. unsigned long ram_paddr; /* 将image复制到RAM中的物理地址*/
  14. unsigned long ram_size; /* image占用RAM空间大小*/
  15. unsigned long startup_size; /* startup大小*/
  16. unsigned long stored_size; /* 整个QNF-IFS大小,包括iamge 和headr*/
  17. unsigned long imagefs_paddr; /* 将IPL设置为映像文件系统的物理地址*/
  18. unsigned long imagefs_size; /* 未压缩映像文件系统的大小*/
  19. unsigned short preboot_size; /*I Size of loaded before header*/
  20. unsigned short zero0; /* Zeros */
  21. unsigned long zero[ 3]; /* Zeros */
  22. unsigned long info[ 48]; /*IS Array of startup_info* structures*/
  23. };


从图上可以很清楚看到各参数的作用,总体来说就IPL就需要这几个步骤


   
   
   
   
  1. checksum (image_paddr, startup_size)
  2. checksum (image_paddr + startup_size, stored_size - startup_size)
  3. copy (image_paddr, ram_paddr, startup_size)
  4. jump (startup_vaddr)

上面三个关键函数,就是完成这几个步骤的:

关键函数①:


   
   
   
   
  1. unsigned long image_scan_2 (unsigned long start, unsigned long end, int docksum)
  2. {
  3. struct startup_header *hdr;
  4. /*
  5. * image starts on word boundary
  6. * We need this scan because it could have 8 raw bytes in front of imagefs
  7. * depending on how the IFS is programmed
  8. */
  9. for (; start < end; start += 4) {
  10. hdr = (struct startup_header *)(start);
  11. /* No endian issues here since stored "naturally" */
  12. if (hdr->signature == STARTUP_HDR_SIGNATURE) //搜寻头标志位,这个一个startup_header开始的标志位
  13. break;
  14. }
  15. if (start >= end)
  16. return ( -1L);
  17. copy (( unsigned long)(&startup_hdr), start, sizeof(startup_hdr)); // startup_hdr是一个由startup_header定义的全局变量
  18. /* now we got the image signature */
  19. if (docksum) {
  20. #ifdef __ARM__ //检测 startup
  21. if (checksum_2(start, startup_hdr.startup_size) != 0) {
  22. #else
  23. if (checksum(start, startup_hdr.startup_size) != 0) {
  24. #endif
  25. ser_putstr( "startup checksum error\n");
  26. return ( -1L);
  27. }
  28. #ifdef __ARM__ //检测 image
  29. if (checksum_2(start + startup_hdr.startup_size, startup_hdr.stored_size - startup_hdr.startup_size) != 0) {
  30. #else
  31. if (checksum(start + startup_hdr.startup_size, startup_hdr.stored_size - startup_hdr.startup_size) != 0) {
  32. #endif
  33. ser_putstr( "imagefs checksum error\n");
  34. return ( -1L);
  35. }
  36. }
  37. return (start);
  38. }

关键函数②


   
   
   
   
  1. int image_setup ( unsigned long addr) {
  2. unsigned long ram_addr;
  3. //
  4. // Copy the data from the address into our structure in memory
  5. //
  6. copy (( unsigned long)(&startup_hdr), addr, sizeof(startup_hdr));
  7. //
  8. // get ram_addr and patch startup with the images physical
  9. // location. Startup will handle the rest ...
  10. //
  11. ram_addr = startup_hdr.ram_paddr + startup_hdr.paddr_bias;
  12. startup_hdr.imagefs_paddr = addr + startup_hdr.startup_size - startup_hdr.paddr_bias;
  13. //
  14. // Copy startup to ram_addr.
  15. //
  16. copy(ram_addr,( unsigned long)(&startup_hdr), sizeof(startup_hdr)); //拷贝startup_header结构体信息
  17. copy ((ram_addr+ sizeof(startup_hdr)),(addr+ sizeof(startup_hdr)), (startup_hdr.startup_size - sizeof(startup_hdr))); //拷贝startup本身
  18. //
  19. // All set now for image_start
  20. //
  21. return( 0);
  22. }

关键函数③


   
   
   
   
  1. int image_start ( unsigned long addr) {
  2. copy (( unsigned long)(&startup_hdr), addr, sizeof(startup_hdr));
  3. //
  4. // Options here include custom jump functions,
  5. // cast as a function call? use the longjmp call
  6. //
  7. jump (startup_hdr.startup_vaddr); //跳转到startup开始执行
  8. return( -1);
  9. }

总结:

    IPL这段精简的代码主要流程如下

①初始化硬件 (汇编代码_start.S)

②将image下载到RAM (sdmmc_load_file())

③定位 OS image (image_scan_2())

④拷贝startup程序 (image_setup())

⑤跳转到加载好的image执行 (image_start())






你可能感兴趣的:(QNX调试笔记)