编写IPL程序

QNX相关历史文章:

  • QNX简介
  • QNX Neutrino微内核
  • QNX IPC机制
  • QNX进程管理器
  • QNX资源管理器
  • QNX字符I/O
  • QNX之编写资源管理器(一)
  • QNX之编写资源管理器(二)
  • QNX之编写资源管理器(三)
  • QNX之编写资源管理器(四)
  • QNX之编写资源管理器(五)
  • QNX之编写资源管理器(六)
  • QNX之编写资源管理器(七)
  • QNX之编写资源管理器(八)
  • QNX之编写资源管理器(九)
  • QNX之编写资源管理器(十)
  • QNX BSP分析
  • QNX OS镜像

这篇文章主要描述Initial program loader的相关内容,并以Freescale IXM6处理器为例讲解

1. Initial program loader

IPL的功能可以类比Uboot,IPL程序的任务是对硬件进行最低限度的配置,以创建一个startup程序运行的环境,至少包括以下内容:

  • 从Reset异常向量开始执行;
  • 配置内存控制器;
  • 配置时钟;
  • 设置堆栈,以便允许IPL库执行OS的验证和设置(下载、扫描、设置、跳转到OS镜像)

IPL的代码可能很简单,也可能非常复杂,这分别对应到Warm-start和Cold-start。在Warm-start中,已经有BIOS或者ROM monitor了,IPL需要做的工作就会少很多,而在Cold-start中,没有BIOS或者ROM monitor,因此需要实现全部的功能。

IPL的初始化部分是用汇编实现的(因为它从ROM执行,没有内存控制器),初始化硬件之后,IPL调用main()函数来初始化C语言环境。

设置好C语言环境后,IPL可以执行不同的任务,这个具体取决于操作系统是从linearly mapped设备启动,还是从bank-switched设备启动:

  • linearly mapped,整个镜像在处理器的线性地址空间中,比如ROM;
  • bank-switched,镜像不能完全由处理器寻址,比如Disk device、Network设备、串口或并口、以及bank-switched的ROM或RAM;

1.1 bank-switched

从bank-switched设备中启动时,需要以下几步:

  • IPL必须调用函数与相关设备通信,比如串口下载时,IPL使用image_download_8250()函数,该函数用于配置和控制8250类串口控制器,完成设置后,该函数会将image拷贝到RAM中;
  • IPL调用image_sacn()来扫描整个image,完成一些校验工作;
  • IPL调用image_setup()来完成一些设置工作;
  • IPL调用image_start(),跳转到startup的起始地址,将控制权交给startup;
    镜像加载时,由于是bank-switched,所以需要将整个镜像都拷贝到RAM中,如下图所示:


    编写IPL程序_第1张图片

从图中可以看出来,IPL处理时可以分为三步:

  • IPL接收控制;
  • IPL将image加载到RAM中;
  • IPL将控制权交给加载的image;

1.2 linearly mapped

linearly mapped设备的启动方式与bank-switched设备是一致的,不同点在于,不需要将整个image都拷贝到RAM中,如下图所示:


编写IPL程序_第2张图片

2. 自定义IPL程序

编写IPL程序,需要以下几个步骤:

  • 初始化硬件,包括对系统RAM的访问。注意,只需要初始化必须的硬件(比如时钟等),外围硬件不需要初始化;(汇编实现)
  • 将image镜像(使用mkifs生成)加载到RAM中,加载程序使用header信息来拷贝header和startup到RAM中,如果不是在linearly mapped的设备中,则需要将整个镜像拷贝到RAM中;(比如image_download_8250())
  • 定位OS镜像,并做一些校验工作;(调用image_sacn())
  • 拷贝startup程序;(调用image_setup())
  • 跳转到加载镜像起始位置执行;(调用image_start())

3. IPL库

IPL库包含了一系列的接口,用于实现自定义的IPL程序,可用的函数接口如下:


编写IPL程序_第3张图片

4. Freescale IMX6 IPL

Freescale IMX6 BSP包:BSP_freescale-imx6SoloX-sabre-sdb_br-660_be-660_SVN815609_JBN555.zip

下载Freescale IMX6 BSP zip包并解压,IPL代码位于src/hardware/ipl/boards/mx6sx-sabre-sdb中,其中mx6sx-sabre-sdb.lnk为链接文件,指定了程序的入口以及内存的分段及布局等。
程序的入口:ENTRY(_start),在start.S文件中完成了以下工作:

  • 设置CPU为SVC32模式
  • Invalidate L1 I/D and TLBs
  • Disable MMU和Caches
  • 使能ICache
  • 设置堆栈
  • 跳转到main函数
    在该目录中的main.c完成IPL的主要工作:
int main(void)
{
    unsigned int image = QNX_LOAD_ADDR;
 
    init_aips();
 
    init_clocks();
 
    init_pinmux();
 
    init_sermx6(MXC_CONSOLE_BASE, 115200, 80000000, 2);
 
    ser_putstr("\nWelcome to QNX Neutrino Initial Program Loader for:\n");
    ser_putstr("  Freescale i.MX6 SoloX Sabre SDB (ARM Cortex-A9/M4)\n");
 
    while (1) {
    ser_putstr("Command:\n");
    ser_putstr("Press 'D' for UART IFS download, using the 'sendnto' utility.\n");
    ser_putstr("Press 'M' for SDMMC IFS download.\n");
    ser_putstr("Press 'J' for JTAG IFS boot of image loaded to 0x"); ser_puthex(QNX_LOAD_ADDR); ser_putstr(".\n");
    switch (ser_getchar()) {
        case 'D': case 'd':
        ser_putstr("send image now...\n");
        if (image_download_ser(QNX_LOAD_ADDR)) {
            ser_putstr(str_download_failed);
            continue;
        } else {
            ser_putstr(str_download_ok);
        }
        break;
 
        case 'M': case 'm':
        if (sdmmc_load_file(QNX_LOAD_ADDR, QNX_IFS_FILENAME) != 0) {
            ser_putstr(str_download_failed);
            continue;
        }
        ser_putstr(str_download_ok);
        break;
 
        case 'J': case 'j':
        break;
 
        default:
        break;
    }
 
    /* No safe boot media, must be scanned */
    image = image_scan_2(image, image + MAX_SCAN, 1);
    if (image != 0xffffffff) {
        ser_putstr(str_found_image);
        ser_puthex(image);
        ser_putstr("\n");
        image_setup_2(image);
 
        ser_putstr(str_jump_to_image);
        ser_puthex(startup_hdr.startup_vaddr);
        ser_putstr("\n\n");
 
        image_start_2(image);
 
        /* Never reaches here */
        return 0;
    }
    ser_putstr(str_image_scan_fail);
    } /* Forever */
 
    /* Never reaches here */
    return 0;
}

进入main分别完成了以下工作:

  • init_aips(),该函数用于设置AHB到IP Bridge的属性,跟Trust Zone相关;
  • init_clocks(),该函数用于设置系统的时钟;
  • init_pinmux(),该函数用于设置管脚的复用,主要是设置Uart和SD相关,其中Uart用于调试,而SD用于加载image;
  • init_sermx6(),该函数用于初始化串口信息;
  • image_download_ser()/sdmmc_load_file(),这两个函数用于完成Image的加载;
  • image_scan_2(),该函数用于扫描image,对Image进行一些校验检查;
  • image_start_2(),该函数跳转到Image的入口去执行,也就是跳转到startup程序中去运行;
    从以上的流程可以看出IPL整体的功能并不复杂,完成最少硬件(需要用到的,比如时钟、串口、SD)的初始化,然后对Image加载和校验,最终跳转过去执行即可。

当然,我对这个IPL可运行性是持怀疑态度的,因为很重要的DDR Controler的相关初始化并没有看到。

你可能感兴趣的:(编写IPL程序)