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
}
_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语言代码*/
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 {
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())