i.MX6Q的qnx调试笔记-qnx的IPL源码分析代码流程分析
2017-10-21 21:19
821 查看
锋影
e-mail 174176320@qq.com
QNX源码下三大主分支,也是组成QNX系统的三大模块:Initial
program loader(IPL)、Startup、Flash
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的链接来看
IPL第一个执行的部分就是bsp_working_dir/src/hardware/ipl/boards/mx6q_sabresmart/_start.S,这是一段汇编代码
bsp_working_dir/src/hardware/ipl/boards/mx6q_sabresmart/main.c
在分析三个关键函数之前,先了解下一个重要的结构体struct startup_header ,定义在<sys/startup.h>
可以很清楚看到各参数的作用,总体来说就IPL就需要这几个步骤
上面三个关键函数,就是完成这几个步骤的:
关键函数①:
关键函数②
关键函数③
总结:
IPL这段精简的代码主要流程如下
①初始化硬件 (汇编代码_start.S)
②将image下载到RAM (sdmmc_load_file())
③定位 OS image (image_scan_2())
④拷贝startup程序 (image_setup())
⑤跳转到加载好的image执行 (image_start())
e-mail 174176320@qq.com
QNX源码下三大主分支,也是组成QNX系统的三大模块:Initial
program loader(IPL)、Startup、Flash
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的链接来看
TARGET(elf32-littlearm) OUTPUT_FORMAT(elf32-littlearm) ENTRY(_start) MEMORY { stack : ORIGIN = 0x90C000, LENGTH = 0x1000 rom : ORIGIN = 0x907000, LENGTH = 0x6000 } SECTIONS { .text : { image_header.o (.text.imageheader) *(.text) *(.note.gnu.build-id) *(.rodata*) } > rom . = ALIGN(4); _etext = .; .data : { *(.data) *(.sdata) } > rom . = ALIGN(4); _ecopy = .; .bss : { *(.bss) *(.sbss) } > rom }
IPL第一个执行的部分就是bsp_working_dir/src/hardware/ipl/boards/mx6q_sabresmart/_start.S,这是一段汇编代码
_start: /* * 将CPU设置为SVC32管理模式 */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr,r0 /* * 禁止 L1 I/D and TLBs */ mov r0, #0 @ set up for MCR mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs mcr p15, 0, r0, c7, c5, 0 @ invalidate icache /* * 禁止 MMU and Caches */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002000 @ clear bits 13 (--V-) bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM) orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB mcr p15, 0, r0, c1, c0, 0 mrc p15, 0, ip, c1, c0, 0 orr ip, ip, #(1 << 12) // enable I Cache mcr p15, 0, ip, c1, c0, 0 dsb isb mov r0, r0 mov r0, r0 mov r0, r0 mov r0, r0 inv_dcache /*禁止D-Cache*/ init_l2cc /*禁止I-Cache*/ /* Setup the Stack */ ldr sp, =0x90C000 bl main /*跳入C语言代码*/
bsp_working_dir/src/hardware/ipl/boards/mx6q_sabresmart/main.c
int main() { unsigned image = QNX_LOAD_ADDR; //0x18000000 char c = 'M'; /* default: load ifs from SD card */ /* Allow access to the AIPS registers */ init_aips(); /* Initialise the system clocks */ init_clocks(); init_pinmux(); //引脚配置,这里包括串口、装置image的SD /* Init serial interface */ /* 115200bps, 80MHz clk, divisor (RFDIV 1-7) 2 */ init_sermx6(MX6X_UART1_BASE, 115200, 80000000, 2); //初始化串口 ser_putstr("\nWelcome to QNX Neutrino Initial Program Loader for Freescale i.MX6Q Sabre-Smart (ARM Cortex-A9 MPCore)\n"); while (1) { if (!c) { ser_putstr("Command:\n"); ser_putstr("Press 'D' for serial download, using the 'sendnto' utility\n"); ser_putstr("Press 'M' for SDMMC download, IFS filename MUST be 'QNX-IFS'.\n"); c = ser_getchar(); } switch (c) { case 'D': case 'd': ser_putstr("send image now...\n"); if (image_download_ser(image)) { ser_putstr("download failed...\n"); c = 0; } else ser_putstr("download OK...\n"); break; case 'M': case 'm': ser_putstr("SDMMC download...\n"); if (sdmmc_load_file(image, "QNX-IFS") == 0) { //加载image(QNX-IFS),所以需要把编译的好的image 改名为QNX-IFS ser_putstr("load image done.\n"); /* Proceed to image scan */ } else { ser_putstr("Load image failed.\n"); c = 0; } break; default: ser_putstr("Unknown command.\n"); c = 0; } if (!c) continue; image = image_scan_2(image, image + 0x200,1); //关键函数1 if (image != 0xffffffff) { ser_putstr("Found image @ 0x"); ser_puthex(image); ser_putstr("\n"); image_setup(image); //关键函数2 ser_putstr("Jumping to startup @ 0x"); ser_puthex(startup_hdr.startup_vaddr); ser_putstr("\n\n"); image_start(image); //关键函数3 /* Never reach here */ return 0; } ser_putstr("Image_scan failed...\n"); } return 0; }
在分析三个关键函数之前,先了解下一个重要的结构体struct startup_header ,定义在<sys/startup.h>
struct startup_header { unsigned long signature; /* 头标志0x00FF7EEB */ unsigned short version; /* mkifs版本号 */ unsigned char flags1; /*IS Misc flags, see below*/ unsigned char flags2; /* No flags defined yet*/ unsigned short header_size; /* sizeof(struct startup_header) */ unsigned short machine; /* 机器型号 sys/elf.h*/ unsigned long startup_vaddr; /* 在IPL执行完后的跳转地址,也就是startup开始执行的地址*/ unsigned long paddr_bias; /*S Value to add to physical address*/ /* to get a value to put into a*/ /* pointer and indirected through*/ unsigned long image_paddr; /* image的物理地址*/ unsigned long ram_paddr; /* 将image复制到RAM中的物理地址*/ unsigned long ram_size; /* image占用RAM空间大小*/ unsigned long startup_size; /* startup大小*/ unsigned long stored_size; /* 整个QNF-IFS大小,包括iamge 和headr*/ unsigned long imagefs_paddr; /* 将IPL设置为映像文件系统的物理地址*/ unsigned long imagefs_size; /* 未压缩映像文件系统的大小*/ unsigned short preboot_size; /*I Size of loaded before header*/ unsigned short zero0; /* Zeros */ unsigned long zero[3]; /* Zeros */ unsigned long info[48]; /*IS Array of startup_info* structures*/ };
可以很清楚看到各参数的作用,总体来说就IPL就需要这几个步骤
checksum (image_paddr, startup_size) checksum (image_paddr + startup_size, stored_size - startup_size) copy (image_paddr, ram_paddr, startup_size) jump (startup_vaddr)
上面三个关键函数,就是完成这几个步骤的:
关键函数①:
unsigned long image_scan_2 (unsigned long start, unsigned long end, int docksum) { struct startup_header *hdr; /* * image starts on word boundary * We need this scan because it could have 8 raw bytes in front of imagefs * depending on how the IFS is programmed */ for (; start < end; start += 4) { hdr = (struct startup_header *)(start); /* No endian issues here since stored "naturally" */ if (hdr->signature == STARTUP_HDR_SIGNATURE) //搜寻头标志位,这个一个startup_header开始的标志位 break; } if (start >= end) return (-1L); copy ((unsigned long)(&startup_hdr), start, sizeof(startup_hdr)); // startup_hdr是一个由startup_header定义的全局变量 /* now we got the image signature */ if (docksum) { #ifdef __ARM__ //检测 startup if (checksum_2(start, startup_hdr.startup_size) != 0) { #else if (checksum(start, startup_hdr.startup_size) != 0) { #endif ser_putstr("startup checksum error\n"); return (-1L); } #ifdef __ARM__ //检测 image if (checksum_2(start + startup_hdr.startup_size, startup_hdr.stored_size - startup_hdr.startup_size) != 0) { #else if (checksum(start + startup_hdr.startup_size, startup_hdr.stored_size - startup_hdr.startup_size) != 0) { #endif ser_putstr("imagefs checksum error\n"); return (-1L); } } return (start); }
关键函数②
int image_setup (unsigned long addr) { unsigned long ram_addr; // // Copy the data from the address into our structure in memory // copy ((unsigned long)(&startup_hdr), addr, sizeof(startup_hdr)); // // get ram_addr and patch startup with the images physical // location. Startup will handle the rest ... // ram_addr = startup_hdr.ram_paddr + startup_hdr.paddr_bias; startup_hdr.imagefs_paddr = addr + startup_hdr.startup_size - startup_hdr.paddr_bias; // // Copy startup to ram_addr. // copy(ram_addr,(unsigned long)(&startup_hdr),sizeof(startup_hdr)); //拷贝startup_header结构体信息 copy ((ram_addr+sizeof(startup_hdr)),(addr+sizeof(startup_hdr)), (startup_hdr.startup_size - sizeof(startup_hdr)));//拷贝startup本身 // // All set now for image_start // return(0); }
关键函数③
int image_start (unsigned long addr) { copy ((unsigned long)(&startup_hdr), addr, sizeof(startup_hdr)); // // Options here include custom jump functions, // cast as a function call? use the longjmp call // jump (startup_hdr.startup_vaddr); //跳转到startup开始执行 return(-1); }
总结:
IPL这段精简的代码主要流程如下
①初始化硬件 (汇编代码_start.S)
②将image下载到RAM (sdmmc_load_file())
③定位 OS image (image_scan_2())
④拷贝startup程序 (image_setup())
⑤跳转到加载好的image执行 (image_start())
相关文章推荐
- [i.MX6Q][QNX Neutrino 6.6.0]调试笔记------IPL源码分析
- i.MX6qSabreLite内核源码阅读笔记-----board-mx6q_sabrelite.c 分析----2
- i.MX6qSabreLite内核源码阅读笔记-----mx6q_sabrelite_init_uart 分析
- [i.MX6Q][QNX Neutrino 6.6.0]调试笔记------初识QNX_Neutrino
- [i.MX6Q][QNX Neutrino 6.6.0]调试笔记------Buildfile
- Android笔记--View绘制流程源码分析(二)
- Android源码学习笔记1-短信发送流程分析
- MT8735 andorid7.0 充电调试总结(2)——电池驱动流程关键代码分析
- i.MX6qSabreLite内核源码阅读笔记-----board-mx6q_sabrelite.c 分析----1
- [i.MX6Q][QNX Neutrino 6.6.0]调试笔记------Buildfile
- QNX学习笔记-Neutrino-QNX-boot启动流程分析
- [原创]java WEB学习笔记70:Struts2 学习之路-- struts2拦截器源码分析,运行流程
- MySQL源码分析——代码结构与基本流程
- CSipSimple源码分析(一)之代码流程和用户注册流程
- 利用xdebug和netbeans调试thinkphp源码及流程分析
- Android笔记--View绘制流程源码分析(一)
- etcd启动流程源码分析笔记(-)
- MyBatis源码学习笔记(十)SQL执行流程分析
- [i.MX6Q][QNX Neutrino 6.6.0]调试笔记------LVDS显示配置
- i.MX6Q的qnx调试笔记------调试imx6的LVDS显示配置-imx6的LVDS显示屏调试