mkimage

本文链接:https://blog.csdn.net/qq_38410730/article/details/90373733

在嵌入式Linux操作系统中,需要将三样东西(BootLoader、内核kernel、根文件系统)传输到目标板中。一般而言,U-Boot烧写到SD卡中,而内核、根文件系统都采用TFTP的方式传输到目标板,然后通过U-Boot的命令进行启动。

那么U-Boot是如何烧写到SD卡中的呢?

为了检测U-Boot是否真正少烧写到了SD卡中,本文通过修改U-Boot下的U-boot/arch/arm/cpu/armv7/start.S文件,在该文件中增加对GPIO的操作,来对LED进行点亮操作,这样如果U-Boot顺利烧写到SD卡中,开机运行后就会点亮LED。


S5PV210启动机制
S5PV210的启动机制如下所示:

S5PV210的启动过程有三个步骤组成,其中,iROM是平台独立的,存储在片内内存中,即芯片应该固化好的;First boot,也是平台独立的,但是它存储在外部内存中(如nandflash\sd卡等),也就是说,这部分代码由用户去实现;second boot,是平台相关的,存储在外部内存,是真正的boot loader代码。一般称iROM过程为BL0,称First Boot Loader为BL1,称second boot loader为BL2。

具体过程为:

S5PV210上电复位后,将从iROM处执行已固化的启动代码——BL0;
在BL0里初始化过程中对启动设备进行判断,并从启动设备拷贝BL1(16KB)到iRAM处,其中这16KB的内容需要包括16字节的HeaderInfo(包含CheckSum),BL1检查HeaderInfo后,继续运行,并拷贝 BL2到iRAM 中并对其校验,通过后转入BL2;
BL2完成一些比较复杂的初始化,包括DARAM的初始化,完成后将OS代码拷贝DARAM 中,并跳到OS中执行并完成启动引导。

S5PV210的烧写过程
本文策略
本文通过修改start.S文件,在start.S文件中增加点亮LED的内容,然后对start.S进行编译,编译完成后,制作成16kB大小的内容,烧写到SD卡中。

在start.S文件末尾增加GPIO的初始化部分和点亮的部分:

gpio_out:
    ldr r11, =0xE0200280            //获得寄存器地址
    ldr r12, =0x00001111            //配置成输出状态
    str r12, [r11]                    //将r12寄存器的值放回r11

    ldr r11, =0xE0200284
    ldr r12, =0xF
    str r12, [r11]
    mov pc, lr

.globl led1_on
led1_on:
    ldr r11, =0xE0200284
    ldr r12, [r11]
    bic r12, r12, #1            //将r12的第一位清零,回写到r12
    str r12, [r11]
    mov pc, lr

然后在文件reset过程中调用这两个过程即可:

reset:
    bl    save_boot_params
    /*
     * set the cpu to SVC32 mode
     */
    mrs    r0, cpsr
    bic    r0, r0, #0x1f
    orr    r0, r0, #0xd3
    msr    cpsr,r0

/*
 * Setup vector:
 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
 * Continue to use ROM code vector only in OMAP4 spl)
 */
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
    /* Set V=0 in CP15 SCTRL register - for VBAR to point to vector */
    mrc    p15, 0, r0, c1, c0, 0    @ Read CP15 SCTRL Register
    bic    r0, #CR_V        @ V = 0
    mcr    p15, 0, r0, c1, c0, 0    @ Write CP15 SCTRL Register

    /* Set vector address in CP15 VBAR register */
    ldr    r0, =_start
    mcr    p15, 0, r0, c12, c0, 0    @Set VBAR
#endif

    bl gpio_out                        //调用
    bl led1_on                            //调用
    
    /* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
    bl    cpu_init_cp15
    bl    cpu_init_crit
#endif

修改成功后,make一下,获得u-boot.bin文件。make文件是做什么的呢?是怎么写的呢?可以关注博主下一篇文章。

HeaderInfo
上文提到BL1过程的部分文件大小仅仅为16KB的大小,而u-boot.bin的大小远远超过,而且在16KB的内容中包含一个叫做HeaderInfo的东西,u-boot.bin中显然没有。

本文的内容不涉及BL2的过程,仅仅需要BL1过程即可,即点亮LED等就好了。也就是说,本文只需要制作一个16KB大小的文件,将该文件烧写到SD卡中,到目标板中运行就行了。LED的程序肯定在前16KB的内容中。至于后面的LB2过程,以后的文章中再讲怎么操作。

那么HeaderInfo的东西是怎么生成的呢?在文件mkv210_image.c中详细介绍了整个过程:

/* 在BL0阶段,iRom内固化的代码需要读取nandflash或SD卡前16K的内容,
 * 并比对前16字节中的校验和是否正确,正确则继续,错误则停止。
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
#define BUFSIZE                 (16*1024)
#define IMG_SIZE                (16*1024)
#define SPL_HEADER_SIZE         16
#define SPL_HEADER              "S5PC110 HEADER  "          
 
int main (int argc, char *argv[])
{
    FILE *fp;
    char *Buf, *a;
    int BufLen;
    int nbytes, fileLen;
    unsigned int checksum, count;
    int i;
    
    // 1. 3个参数
    if (argc != 3)                //参数数目不对
    {
        printf("Usage: mkbl1 <source file> <destination file>\n");
        return -1;
    }
 
    // 2. 分配16K的buffer
    BufLen = BUFSIZE;
    Buf = (char *)malloc(BufLen);
    if (!Buf)                    //如果分配空间不够,分配失败
    {
        printf("Alloc buffer failed!\n");
        return -1;
    }
 
    memset(Buf, 0x00, BufLen);                //这段内存区全部清零
 
    // 3. 读源bin到buffer
    // 3.1 打开源bin
    fp = fopen(argv[1], "rb");            //打开文件
    if( fp == NULL)                    //打开失败
    {
        printf("source file open error\n");
        free(Buf);
        return -1;
    }
    // 3.2 获取源bin长度
    fseek(fp, 0L, SEEK_END);        //指针移动到文件末尾
    fileLen = ftell(fp);            //获取文件长度
    fseek(fp, 0L, SEEK_SET);            //指针移动到文件开始
    // 3.3 源bin长度不得超过16K-16byte
    count = (fileLen < (IMG_SIZE - SPL_HEADER_SIZE))
        ? fileLen : (IMG_SIZE - SPL_HEADER_SIZE);            //count等于16kB-16字节
    // 3.4 buffer[0~15]存放"S5PC110 HEADER  "
    memcpy(&Buf[0], SPL_HEADER, SPL_HEADER_SIZE);            //复制移动
    // 3.5 读源bin到buffer[16]
    nbytes = fread(Buf + SPL_HEADER_SIZE, 1, count, fp);    
    if ( nbytes != count )
    {
        printf("source file read error\n");
        free(Buf);
        fclose(fp);
        return -1;
    }
    fclose(fp);
 
    // 4. 计算校验和
     // 4.1 从第16byte开始统计buffer中共有几个1
    a = Buf + SPL_HEADER_SIZE;
    for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++)
        checksum += (0x000000FF) & *a++;
    // 4.2 将校验和保存在buffer[8~15]
    a = Buf + 8;
    *( (unsigned int *)a ) = checksum;
 
    // 5. 拷贝buffer中的内容到目的bin
    // 5.1 打开目的bin
    fp = fopen(argv[2], "wb");
    if (fp == NULL)
    {
        printf("destination file open error\n");
        free(Buf);
        return -1;
    }
    // 5.2 将16k的buffer拷贝到目的bin中
    a = Buf;
    nbytes    = fwrite( a, 1, BufLen, fp);
    if ( nbytes != BufLen )
    {
        printf("destination file write error\n");
        free(Buf);
        fclose(fp);
        return -1;
    }
 
    free(Buf);
    fclose(fp);
 
    return 0;
}

这里介绍一下main()的函数参数:

argc = argument count :表示传入main函数的数组元素个数,为int类型;
argv = argument vector :表示传入main函数的指针数组,为char*[ ]类型。第一个数组元素argv[0]是程序名称,并且包含程序所在的完整路径。argc至少为1,即argv数组至少包含程序名。
也就是说,当编译并运行main()函数的时候:

gcc mkv210_image.c -o mkv210                    //编译文件
./mkv210 u-boot.bin u-boot.16k                //运行文件,三个参数(第一个参数必须是本文件)
1
2
通过这段程序的运行,就可以将u-boot.bin生成u-boot.16k,该文件仅有16k的大小,并且包含HeaderInfo。

hexdump查看二进制文件
当编译文件时,需要将.c文件通过预编译变成.i文件,再通过编译变成.s文件,再通过汇编变成.o文件,最后进行链接变成不带后缀名的文件。

但是不带后缀名的文件内不仅仅是二进制的内容,里面还包括许多的链接内容、注释内容等等,因此文件大小一般比较大,不能直接烧写到目标板中。而.bin后缀的文件删除了这部分的内容,仅仅只有二进制的文件,可以烧写到目标板中。

因此,如果想要分析二进制文件采用不带后缀的文件,想要烧写到目标板中,采用.bin后缀的文件。

hexdump -C u-boot.16k | less        //-C 输出规范的十六进制和ASCII码
1
运行结果如图所示:


查看uboot.bin:

arm-linux-objdump -S u-boot | less
1
运行结果如图所示:


注意到,u-boot里面第一句reset的二进制表达是ea000014,而在u-boot.16k中的表达是140000ea(第二行,去掉前16字节的HeaderInfo)。

这就涉及到大小端的问题了,可以查看链接:大小端问题。

一般而言,处理器以小端模式为主,硬盘等存储设备以大选模式为主,网络通信都是大端模式(先传高位,再传低位)。

烧写到SD卡
烧写到SD卡,一般采用的是dd命令:

sudo dd iflag=dsync oflag=dsync if=u-boot.16k of=/dev/sdb seek=1
1
前两个选项iflag和oflag表示采取异步的方式,if表示输入文件,of表示输出设备,seek表示从第几个扇区开始烧写。

dev是设备(device)的英文缩写。/dev这个目录对所有的用户都十分重要。因为在这个目录中包含了所有Linux系统中使用的外部设备。但是这里并不是放的外部设备的驱动程序,这一点和windows、dos操作系统不一样。它实际上是一个访问这些外部设备的端口。我们可以非常方便地去访问这些外部设备,和访问一个文件、一个目录没有任何区别。

检测结果
插入SD卡到目标板,上电,LED就会按照程序执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值