制作U-Boot烧写镜像到SD卡过程
csdn 2023-12-08

在嵌入式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

具体过程为:

  1. S5PV210上电复位后,将从iROM处执行已固化的启动代码——BL0;
  2. 在BL0里初始化过程中对启动设备进行判断,并从启动设备拷贝BL1(16KB)到iRAM处,其中这16KB的内容需要包括16字节的HeaderInfo(包含CheckSum),BL1检查HeaderInfo后,继续运行,并拷贝 BL2到iRAM 中并对其校验,通过后转入BL2;
  3. 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 //运行文件,三个参数(第一个参数必须是本文件) 

通过这段程序的运行,就可以将u-boot.bin生成u-boot.16k,该文件仅有16k的大小,并且包含HeaderInfo。

hexdump查看二进制文件

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

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

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

hexdump -C u-boot.16k | less //-C 输出规范的十六进制和ASCII码 

运行结果如图所示:

查看uboot.bin:

arm-linux-objdump -S u-boot | less

运行结果如图所示:

注意到,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 

前两个选项iflag和oflag表示采取异步的方式,if表示输入文件,of表示输出设备,seek表示从第几个扇区开始烧写。

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

检测结果

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

声明: 本文转载自其它媒体或授权刊载,目的在于信息传递,并不代表本站赞同其观点和对其真实性负责,如有新闻稿件和图片作品的内容、版权以及其它问题的,请联系我们及时删除。(联系我们,邮箱:evan.li@aspencore.com )
0
评论
  • 相关技术文库
  • 单片机
  • 嵌入式
  • MCU
  • STM
下载排行榜
更多
评测报告
更多
广告