编写IPL程序

作者: Loyen | 来源:发表于2019-02-03 10:37 被阅读0次

    QNX相关历史文章:

    这篇文章主要描述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处理时可以分为三步:

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

    1.2 linearly mapped

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


    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程序,可用的函数接口如下:


    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程序

        本文链接:https://www.haomeiwen.com/subject/lgrysqtx.html