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大小数据给写到第一个字中,只是我没有发现。
测试:有两种方法:一是再看看代码是不是真的有写;二是直接使用自己写入一个正确数据和一个错误数据,在第一个字节处,
通过测试看看是否有什么不对劲的地方,以此得出结论。
解决:我还没有去测试,有些忙,等测试处结果了,就把结果和日期加在这里。
【2019年08月25日早0:20分】测试完成,先写下结论,真的是没有关系的,不管在头数据的第一个字中写上什么数据,都可
以的,并且发现其实里面有一个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呢?要说是一位那还好,可明明就是一个字节!
猜测:这东西不好猜测,按照老师讲的,应该就是0或1,但我还是想试试。
测试:直接打印出来就好了。
解决:有时间测试好补充,欢迎朋友和我分享。
【2019年08月25日早9:00】测试完成,因为打印格式和对字节、字还没有更加深入的了解,调试有些阻碍,不过大致完成。
结果确实和我想的一样,里面并不是存放0或1的,可能老师也是这么讲的,但是我没有注意到。不过我测试到check其实真不
是0或1的叠加,我这里调试出来,缓冲区中放的可以是随机一些列在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;
}
--------------------------------------------------------------------------------------------------------------------------/