ARM专题:mkv210_image.c源码的详细分析

1、前言

mkv210_image.c源码是自己在学S5PV210裸机时遇到的,它的作用可以简述为将USB启动的镜像bin文件,转变为SD卡启动的镜像bin文件,整个转化过程其实就是在原有bin基础上,在其前加上16字节的校验头,通过SD卡启动的时候,iROM内固化的代码会读取NAND或者SD卡前16K的内容,通过比对前16字节中的校验和是否正确,正确则会继续,错误则停止

--------------------------------------------------------------------------------------------------------------------------/

2、demo

/* 本文件来自于友善之臂的裸机教程,据友善之臂的文档中讲述,本文件是一个热心网友提供,在此表示感谢。
 * 本质上是C源文件,这源文件其实是在gcc中编译的,不是交叉编译工具栏编译的,和裸机其实关系不大。
 */
#include <stdio.h> 
#include <string.h>
#include <stdlib.h> // 文件包含,执行相关函数需要

#define BUFSIZE                 (16*1024)  // 宏定义缓冲区的大小,16K
#define IMG_SIZE                (16*1024)  // 宏定义镜像的大小,16K
#define SPL_HEADER_SIZE         16         // 宏定义校验和大小,16字节
//#define SPL_HEADER              "S5PC110 HEADER  "
#define SPL_HEADER              "****************"  // 宏定义校验和的内容,什么都是可以的,16字节就行

// 主函数,带参数的标准main函数
// 详细可以看我另外一篇文章:https://blog.csdn.net/mmphhh/article/details/100008045
int main (int argc, char *argv[])
{
	FILE	*fp;        // 定义文件指针,用于文件相关操作
	char	*Buf, *a;   // 定义缓冲区的指针和中间转换地址的指针变量
	int		BufLen;     // 定义缓冲区长度变量
	int		nbytes, fileLen;  // 定义读文件流后返回值判断变量和文件的长度变量
	unsigned int checksum, count;  // 定义校验和变量和长度
	int		i;          // 循环变量,用于for循环
	
	// 01 对main函数传参判断,规定传入参数必须3个
	// 比如执行文件是mkx210,则执行的代码为./mkx210 led.bin 210.bin
	if (argc != 3)
	{
		printf("Usage: %s <source file> <destination file>\n", argv[0]); // 如果不是3报错
		return -1;
	}

	// 02 分配16K大小的缓冲区buffer
	BufLen = BUFSIZE;  // 缓冲区的16K字节长度赋值到缓冲区长度变量
	// 手动进行malloc动态内存分配,并将返回的void *空指针强制转换为char *,之后要记得free释放
	// 强制类型转换后的char *缓冲区指针赋值到缓冲区指针变量中
	Buf = (char *)malloc(BufLen); 
	if (!Buf)  // 每次使用malloc分配完内存都需要进行判断,以验证内存分配是否成功
	{
		printf("Alloc buffer failed!\n");  // 如果是空指针,就打印分配失败
		return -1;
	}
	// memset函数作用是将某一块内存中的内容全部设置为指定的值,常为新申请的内存做初始化工作
	// 在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法
	// 原型定义是void *memset(void *s, int ch, size_t n);这里是往生成的16K空间中初始化为0
	memset(Buf, 0x00, BufLen);

	// 03 读取原先的bin文件到buffer缓冲区中
	// 3.1 打开源bin
	fp = fopen(argv[1], "rb"); // 通过只读二进制文件方式打开传入的参数1,并得到文件指针fp
	if(fp == NULL)  // 判断文件是否打开成功
	{
		printf("source file open error\n");
		free(Buf);
		return -1;
	}
	// 3.2 获取原先bin的长度
	// fseek重定位流(数据流/文件)上的文件内部位置指针
	// 位置指针指向文件内部的字节位置,随着文件的读取会移动
	// 函数原型:int fseek( FILE *stream, long offset, int origin );
	// 第一个参数stream为文件指针;第二个参数offset为偏移量,正数表示正向偏移,负数表示负向偏移;
	// 第三个参数origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
	// SEEK_SET: 文件开头;SEEK_CUR: 当前位置;SEEK_END: 文件结尾;
	// 其中SEEK_SET,SEEK_CUR和SEEK_END依次为0,1和2,也可以直接用数字传递
	// 0L中的L表明这个数是long int类型的,不写的话默认认为是int(可以正数可以负数,负数应该是添-)
	// ftell用于得到文件位置指针当前位置相对于文件首的偏移字节数
	fseek(fp, 0L, SEEK_END);  // 定位到文件尾
	fileLen = ftell(fp);      // 得到文件长度
	fseek(fp, 0L, SEEK_SET);  // 再次定位到文件头
	// 3.3 原先的bin长度不得超过16K-16byte
	// 一个三目运算符,判断文件长度是否小于原先的bin文件长度16K - 16字节的校验头
	// 之所以要判断,是为了确定BL1的大小要不超过16K,如果成立就将文件的长度给赋值给校验和大小的变量
	// 如果不成立的话,就把16K-16byte赋值给count,其实这个是不恰当的,因为不知道是否能支持这样的BL1
	// 但没有关系,之后可以根据具体情况来修改不成立的那种情况
	count = (fileLen < (IMG_SIZE - SPL_HEADER_SIZE)) ? fileLen : (IMG_SIZE - SPL_HEADER_SIZE);
	// 3.4 在缓冲区buffer[0~15]中存放"S5PC110 HEADER  "
	// 函数原型:void *memcpy(void *destin, void *source, unsigned n);
	// 从源source所指内存地址起始位置,开始拷贝n个字节到目标destin所指的内存地址的起始位置中
	// destin--指向用于存储复制内容的目标数组,类型强制转换为 void* 指针
	// source-- 指向要复制的数据源,类型强制转换为 void* 指针,n-- 要被复制的字节数
	memcpy(&Buf[0], SPL_HEADER, SPL_HEADER_SIZE);
	// 3.5 读原先的bin到buffer[16]
	// 函数原型:size_t fread (void *buffer, size_t size, size_t count, FILE *stream) ;
	// 它从文件流中读数据,从给定流stream读取数据,最多读取count个项,每个项size个字节
	// 如果调用成功返回实际读取到的项个数(小于或等于count),如果不成功或读到文件末尾返回0
	// buffer--用于接收数据的内存地址;size--要读的每个数据项的字节数,单位是字节(这里char型);
	// count--要读count个数据项,每个数据项size个字节;stream--输入流
	// 整个过程结束,就将原先的bin文件内容全部读取到了缓冲区的16字节之后(数组下标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);  // 关闭一开始的文件指针,因为缓冲区中已经得到了需要的数据

	// 04 计算校验和
 	// 4.1 从第16byte开始统计buffer(原本bin文件被复制后的内容)中共有几个1(这里没弄懂,是不是真的都是0或1)
	// 从第16byte开始计算,把buffer中所有的字节数据加和起来得到的结果
	a = Buf + SPL_HEADER_SIZE;  // 把复制后的bin文件的起使地址赋值给中间地址转换指针变量a
	// 无论是整个复制后的bin文件的最大空间大小,也就是16K-16byte的大小,处理时按照字节处理
	// checksum += (0x000000FF) & *a++;相当于:checksum += (0x000000FF) & *a; a++;
	// 就是把每个字节(8位)给取出来,是0或1,叠加在checksum上面,随着a++加后继续得到和
	for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++)
		checksum += (0x000000FF) & *a++;
	// 4.2 将校验和保存在buffer[8~15]
	// 这里的中间指针转换变量在之前使用完之后,又可以在这里进行其他使用
	// Buf是210.bin的起始地址,+8表示向后位移2个字,也就是说写入到第3个字
	// 注意一个字的大小一般是寄存器的位数,这里就是32位,因此一个字就是4个字节,内存就像一个个小房子一样
	// 每加1就是加上一个字节,就这样加上4就是一个字,加8就是两个字,所以这里是从第3个字处开始写入校验和
	// 回想之前的头数据,是16个字节的,也就是4个字,因此,这里是从第三个字处开始写入,符合数据手册意思
	a = Buf + 8;   
	*( (unsigned int *)a ) = checksum; // 因为校验和的数据有可能超过256,所以要强制类型转换

	// 05 拷贝buffer中的内容到目的bin
	// 5.1 打开目的bin
	// 通过些二进制的方式打开第二个传入的参数,这个参数是要生成的新bin文件,fp继续存放此时的文件指针
	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;  // 重新将中间地址转换指针指向缓冲区的起始地址
	// 把a指向的缓冲区内的加了头数据的bin文件复制到新bin文件中,长度是16K
	nbytes	= fwrite(a, 1, BufLen, fp);  
	if ( nbytes != BufLen )  // 判断是否读取成功,不成功警告并退出
	{
		printf("destination file write error\n");
		free(Buf);
		fclose(fp);
		return -1;
	}

	free(Buf);   // 释放掉malloc申请的缓冲区
	fclose(fp);  // 关掉打开的新bin文件
	return 0;
}

--------------------------------------------------------------------------------------------------------------------------/

3、一些备注和待测试问题(更新)

01 备注

01 这个代码是和BL1相关的,BL1必须要有头数据,头数据用于被iROM代码复制到内部SRAM。头数据有两个信息。一个是BL1的大小,另一个是BL1的校验和数据
02 加载BL1时,iROM检查头数据中BL1的大小,并将BL1复制到内部SRAM。复制BL1后,iROM复制BL1的sum数据,并与BL1头数据中的checksum数据进行比较。如果成功了,就开始吧。否则iROM将尝试从SDMMC通道2端口进行第二次引导(4位SD/MMC)。
03 16字节(4个字)头数据的内容是:有序
01 BL1大小单位:字节(用户写)(1个字)
02 预留(应为0)(1个字)
03 校验和(用户写)(1个字)
04 预留(应为0)(1个字)

02 待测试问题

01 这个代码里面好像没有在头数据中的第一个字处,写入BL1的大小信息(字节单位),也即头数据貌似只在第三个字处写入了校验和而已,这里如果没写的话,第一个字节处,就全都是0,难道真的是不用写的?

猜测:是不是根据备注的02点,其实iROM中的BL0在检擦头数据中BL1大小的时候,这个过程是无关紧要的,结果不对也没影响
。重点是之后将BL1的头数据复制到SRAM后,复制sum数据和checksum进行比较这一步。这一步如果错了,才会造成无法启动。
当然,还有一种猜测,就是这份代码中有某个步骤有将这个BL大小数据给写到第一个字中,只是我没有发现。

测试:有两种方法:一是再看看代码是不是真的有写;二是直接使用自己写入一个正确数据和一个错误数据,在第一个字节处,
通过测试看看是否有什么不对劲的地方,以此得出结论。

解决:我还没有去测试,有些忙,等测试处结果了,就把结果和日期加在这里。
【20190825日早020分】测试完成,先写下结论,真的是没有关系的,不管在头数据的第一个字中写上什么数据,都可
以的,并且发现其实里面有一个memcpy函数也是可以不需要的,说是给buffer[1]~buffer[15]缓存一些数据,确实没必要,
虽说什么都可以往里面写,这个一开始的乱写还影响我的测试,我就把其删掉了。写入的函数其实也十分简单,如下:
a = Buf + 0;
// *((unsigned int *)a)  = fileLen + SPL_HEADER_SIZE; // 准确的BL1长度的信息
*((unsigned int *)a)  = fileLen + SPL_HEADER_SIZE + 20; // 瞎写的BL1长度的信息
printf("test %d\n", *((unsigned int *)a));  // 把详细的信息给打印出来

02 听老师说checksum是把原先bin文件中的数据复制到缓冲区中的16字节后,每个字节中存放的都是0或1,只需要统计1的个数,就可以得到校验和checksum,我有点好奇,每个字节可以存放的数据最大是255,为什么一定就会是0或者1呢?要说是一位那还好,可明明就是一个字节!

猜测:这东西不好猜测,按照老师讲的,应该就是01,但我还是想试试。

测试:直接打印出来就好了。

解决:有时间测试好补充,欢迎朋友和我分享。
【20190825日早900】测试完成,因为打印格式和对字节、字还没有更加深入的了解,调试有些阻碍,不过大致完成。
结果确实和我想的一样,里面并不是存放01的,可能老师也是这么讲的,但是我没有注意到。不过我测试到check其实真不
是01的叠加,我这里调试出来,缓冲区中放的可以是随机一些列在0~255之间的数,最终校验和也是和checksum相等的。

--------------------------------------------------------------------------------------------------------------------------/

4、少注释代码

/*
 * 本文件来自于友善之臂的裸机教程,据友善之臂的文档中讲述,本文件是一个热心网友提供,在此表示感谢。
 */
#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  "
#define SPL_HEADER              "****************"

int main (int argc, char *argv[])
{
	FILE	*fp;
	char	*Buf, *a;
	int		BufLen;
	int		nbytes, fileLen;
	unsigned int checksum, count;
	int		i;
	
	// 01 3个参数
	if (argc != 3)
	{
		printf("Usage: %s <source file> <destination file>\n", argv[0]);
		return -1;
	}

	// 02 分配16K的buffer
	BufLen = BUFSIZE;
	Buf = (char *)malloc(BufLen);
	if (!Buf)
	{
		printf("Alloc buffer failed!\n");
		return -1;
	}
	memset(Buf, 0x00, BufLen);
	
	// 03 读源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);
	// 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
	// 4.1 从第16byte开始计算,把buffer中所有的字节数据加和起来得到的结果
	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;							// Buf是210.bin的起始地址,+8表示向后位移2个字,也就是说写入到第3个字
	*( (unsigned int *)a ) = checksum;

	// 05 拷贝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;
}

--------------------------------------------------------------------------------------------------------------------------/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学不懂啊阿田

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值