|
本帖最后由 FSL_TICS_ZJJ 于 2014-9-11 14:46 编辑
这是我自己经常采用的的一种方法,如觉得有效,欢迎支持,如有任何需要,欢迎站短。
设有两个int32值crc32Value和crc32Value2存放在FLASH存储空间的最末尾
这篇文章面向于以下几个情境:
1. 无bootloader,裸程序运行,程序运行前进行自检
2. 有bootloader,bootloader为预先由开发人员(你)烧写到MCU里,复位后先运行bootloader,bootloader进行应用程序的完整性检查,完整性检查失败后,进行bootloader操作。否则,跳转至应用程序
3. 有bootloader,或者无bootloader,原始程序里面有加密的字节,根据MCU唯一ID计算出来的校验值。为表述方便,设这个校验值为CheckValue,类型为int32。对于这种情况
> 原始程序里有一个setCheckValue()的程序,
> 一个erase_setCheckValue()的程序,
> 和一个setCRC32Value2()的程序。
编译时,CheckValue的初始值为0xFFFFFF。程序第一次运行时,setCheckValue()根据MCU的唯一ID计算出一个值,FLASH在线烧写到CheckValue的地址,并且之后,erase_setCheckValue()将setCheckValue()所在地址的程序擦除,然后调用setCrc32Value2()计算新的CRC32值,FLASH在线烧写到crc32Value2。之后程序进入正常运行阶段,在程序的正常运行阶段,校验CheckValue和MCU唯一ID值的关系来确定程序合法性。
对于第1、2种情况,程序的完整性校验实现起来很简单,因为程序代码在运行时不改变,因而只需要一个crc32Value即可。
对于第3种情况,程序在第一次运行前,需要完整性检查,使用crc32Value即可。程序在第一次运行后,程序本身的代码发生了变化,需要使用另一个值crc32Value2进行检查。
总结第1、2、3种情况,检查过程可以统一为,检查crc32Value的值是否正确,若不正确,检查crc32Value2的值是否正确。若都不正确,则程序错误,进行bootloader错误处理阶段。
使用Keil MDK时,我们为了方便,需要自动生成crc32Value,下面主要说明编译器的设置,单片机代码端的上述逻辑的处理是比较方便的,以后再说
首先,在options里面添加一项
这项指定编译完成后,执行autocalcrc32.bat批处理程序
- "F:\Program Files\Keil\ARM\ARMCC\bin\fromelf.exe" --bin --bincombined --output="E:\Project\Workspace\test\final.bin" "E:\Project\Workspace\test\IO_Toggle\MDK-ARM\IO_Toggle\IO_Toggle.axf"
- "E:\Project\Workspace\test\Libraries\AUTHORIZSTION_LIB\crc32\crc32.exe" "E:\Project\Workspace\test\final.bin" "E:\Project\Workspace\test\Libraries\AUTHORIZSTION_LIB\authorization_auto_generated.c" 393216
复制代码
其中第一条,调用fromelf文件,生成二进制程序镜像.bin
第二条,调用crc32.exe生成文件authorization_auto_generated.c,393216为bin文件的大小。.c文件内容为
变量program_crc32_chk_value_original为编译时的CRC32值
变量program_crc32_chk_value_reserved为crc32Value2作用的值
其中crc32.exe为我自己编译的程序,如下为文件crc32.c:
- /*****************************************************
- ** Name : crc32.c
- ** Author : wsun
- ** Version : 2.1
- ** Date : 2014-8-24
- ** Description : CRC32 Checking For Cortex Embedded
- ******************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- //#include <errno.h>
- //#include <unistd.h>
- //#include <fcntl.h>
- //#include <sys/stat.h>
- /*指定程序的大小*/
- /*实际校验字节的大小为DOCSIZE-8,留有两个int32的空间*/
- /*DOCSIZE-8应该小于MAXBUFSIZE*/
- #define MAXBUFSIZE 1024*1024
- static void usage(void);
- static int calc_img_crc(const char * in_file, unsigned int * img_crc, unsigned int docsize);
- static void usage(void)
- {
- }
- /*我的32位int数据CRC32计算程序,应该与下位机的一致*/
- unsigned int CrcGen_INT32(unsigned int crc,unsigned int data[], unsigned int size)
- {
- unsigned int i;
- for(i=0;i<size;i++){
- unsigned int temp = data[i];
- unsigned int j;
- for(j=0;j<32;j++){
- if( (crc ^ temp) & 0x80000000 ){
- crc = 0x04C11DB7 ^ (crc<<1);
- }else{
- crc <<=1;
- }
- temp<<=1;
- }
- }
- return crc;
- }
- /*
- **计算大文件的CRC校验码:crc32函数,是对一个buffer进行处理,
- **但如果一个文件相对较大,显然不能直接读取到内存当中
- **所以只能将文件分段读取出来进行crc校验,
- **然后循环将上一次的crc校验码再传递给新的buffer校验函数,
- **到最后,生成的crc校验码就是该文件的crc校验码.(经过测试)
- */
- unsigned char buf[MAXBUFSIZE];
- static int calc_img_crc(const char *in_file, unsigned int *img_crc, unsigned int docsize)
- {
- FILE* fd;
- int nread;
- int size;
- int ret;
- /*第一次传入的值需要固定,如果发送端使用该值计算crc校验码,
- **那么接收端也同样需要使用该值进行计算 */
- unsigned int crc = 0x6E59438A; //as "wsun", crc32value is 0x6E59438A;
- fd = fopen(in_file, "rb");
- if (!fd) {
- printf("%d:open %s.\n", __LINE__, strerror(errno));
- return -1;
- }
- size = 0;
- while ((nread = fread(buf, 1, docsize-8, fd)) > 0) {
- crc = CrcGen_INT32(crc, (int*)buf, nread/4);
- size += nread;
- break;
- }
- *img_crc = crc;
- printf("calculate first %d bytes of file: ",size);
- close(fd);
- if (nread < 0) {
- printf("%d:read %s.\n", __LINE__, strerror(errno));
- return -1;
- }
- return 0;
- }
- int main(int argc, char **argv)
- {
- FILE* filsave;
- FILE* filver;
- int status;
- unsigned int ver = 0;
- unsigned int verdate = 0;
- unsigned int img_crc;
- const char *in_file = argv[1];
- const char *out_file = argv[2];
- const unsigned int docsize = atoi(argv[3]);
- if (argc < 3) {
- usage();
- exit(1);
- }
- /*Version 号产生程序,做相应更改以适用你自己的代码*/
- {
- int i;
- for(i=0;__DATE__[i];i++)
- {
- verdate += __DATE__[i];
- verdate = verdate ^ (verdate>>1)^ (verdate>>2)^ (verdate>>3)^ (verdate>>4)^ (verdate>>5);
- }
- }
- /*计算CRC32*/
- status = calc_img_crc(in_file, &img_crc, docsize);
- if (status < 0) {
- exit(1);
- }
- printf("[%s] is:\nCRC32:%08X\n", in_file, img_crc);
- /*编译次数文件,计算当前的编译次数*/
- filver = fopen("ver.txt","r");
- if(!filver)
- {
- filver = fopen("ver.txt","w");
- fprintf(filver,"%d",0);
- fclose(filver);
- filver = fopen("ver.txt","r");
- }
- if(filver)
- {
- fscanf(filver,"%d",&ver);
- fclose(filver);
- ver ++;
- filver = fopen("ver.txt","w");
- if(filver)
- {
- fprintf(filver,"%d",ver);
- fclose(filver);
- }
- else
- {
- printf("%d:open %s.\n", __LINE__, strerror(errno));
- return -1;
- }
- }
- else
- {
- printf("%d:open %s.\n", __LINE__, strerror(errno));
- return -1;
- }
- /*生成.c文件*/
- filsave = fopen(out_file,"w");
- if(filsave)
- {
- /*!三个变量的地址需要根据项目进行更改*/
- fprintf(filsave,"const unsigned int program_crc32_chk_value_original __attribute__((used)) __attribute__((at(0x0806FFF8))) = 0x%08X;\r\n",img_crc);
- fprintf(filsave,"const unsigned int program_crc32_chk_value_reserved __attribute__((used)) __attribute__((at(0x0806FFFC))) = 0xFFFFFFFF;\r\n");
- // fprintf(filsave,"const unsigned int CompileTick __attribute__((used)) __attribute__((at(0x0806FFF4))) = %d;\r\n",verdate);
- fclose(filsave);
- }
- else
- {
- printf("%d:open %s.\n", __LINE__, strerror(errno));
- return -1;
- }
- /*输出编译次数,退出*/
- printf("ver = %d\n",ver);
- return 0;
- }
复制代码
编译工具采用VC,命令行调用compile crc32.c进行编译
其中compile.bat为
- @echo off
- path=.\VCPack\Bin;.\VCPack\Bin;%path%
-
- set lib=.\VCPack\Lib
- set include=.\VCPack\Include
-
- (cl kernel32.lib LIBC.lib OLDNAMES.lib %1
- )&&(
- link kernel32.lib LIBC.lib OLDNAMES.lib %~n1.obj
- )
- pause
复制代码
crc32.c可以根据需要修改。这里计算略去了最后的八个字节,因为是program_crc32_chk_value_original和program_crc32_chk_value_reserved的保留空间。并且文件大小需要整字大小。
Keil编译完后,会调用autocalcrc32.bat,计算出CRC32的值之后,更改文件authorization_auto_generated.c中的program_crc32_chk_value_original
之后需要重新手动编译一遍Keil,使得新的program_crc32_chk_value_original编译到最终的二进制文件里
以上是编译起部分的操作。主要实现的功能是自动生成程序的CRC32校验值,希望对大家有所帮助。
PS:我尝试了许多个CRC32计算的网站,发现应该选择http://www.zorc.breitbandkatze.de/crc.html如下的设定,和我的CRC校验结果一直
测试结果如下,可以发现结果一致:
CRC32选项:
CRC32.exe运行结果
其中text.txt为
不知解释的是否清楚,希望对大家有帮助~,单片机端的实现比较简单,有需要的话,我再另写一篇
整个的文件 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
x
阿莫论坛20周年了!感谢大家的支持与爱护!!
一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。
|