[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的链接来看

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 ,定义在
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*/
};

[i.MX6Q][QNX Neutrino 6.6.0]调试笔记------IPL源码分析_第1张图片

从图上可以很清楚看到各参数的作用,总体来说就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())






你可能感兴趣的:(QNX,Neutrino,6.6.0)