尊重原创,转载请注明出处;本⽂纯技术交流不涉及版权问题,如有问题联系斧正。
⽬录
1、背景
嵌⼊式设备升级过程中由于外部因素(如断电、强⾏中断等)导致新固件firmware未完整写⼊flash从⽽导致系统不能正常启动,有没有⼀种恢复⽅法来避免此种情况的发⽣?-双镜像机制.双镜像机制可以检测主/备镜象的好坏从⽽recovery坏的镜像,使系统能正常启动。 2、原理
双镜像机制是⽤来维护镜像/固件的备份和恢复。此功能需要两个MTD分区:主固件分区存储固件⽤于启动;备镜像分区存储备份镜像。当U-boot尝试启动镜像时会先检测两个分区镜像的完整性。 如果两个分区的镜像都是正确的,U-boot会启动主镜像
如果两个镜像中⼀个损坏,U-boot会⽤好的分区镜像恢复坏的分区镜像
如果两个分区镜像都顺坏,U-boot则不会启动,系统启动失败
所以保证系统能正常启动⾄少要保证⼀个分区镜像的完整性。
⽬前完整性检测只有kernel内核部分能够进⾏完整性检查。Rootfs根⽂件系统部分只有⼀个简单的数据校验。
3、MTD分区信息
4、启动流程图
5、U-boot配置⽅法
1. cd source_code_dir
2. make xxx_defconfig
3. make menuconfig
5. Modify partition information
To make dual image usable, mtdparts should be at least like this:
mtdparts=ranand:512k(u-boot),512k(u-boot-env),512k(factory),32768k(firmware),32768k(firmware_backup) l Restore kernel only
Select this only if the rootfs is pad to NAND erase boundary.
6、代码实现
关键代码:
int dual_image_check(void)
{
uint64_t image1_off, image2_off, image1_partsize, image2_partsize;
size_t image1_size, image2_size, rootfs1_size = 0, rootfs2_size = 0;
size_t image1_padding_bytes = 0, image2_padding_bytes = 0;
bool image1_ok, image2_ok;
uint64_t image_total_size;
bool deadc0de = false;
void *flash;
int ret;
printf("\nStarting dual image checking ...\n");
flash = board_get_flash_dev();//获取flash设备
if (!flash) {
printf("Fatal: failed to get flash device\n");
printf("Dual image checking is bypassed\n");
return -1;
}
ret = get_mtd_part_info(CONFIG_DUAL_IMAGE_PARTNAME_MAIN,
&image1_off, &image1_partsize);//获取主分区偏移和⼤⼩
if (ret) {
printf("Fatal: failed to get main image partition\n");
printf("Dual image checking is bypassed\n");
return -1;
}
ret = get_mtd_part_info(CONFIG_DUAL_IMAGE_PARTNAME_BACKUP,
&image2_off, &image2_partsize);//获取备份分区偏移和⼤⼩
if (ret) {
printf("Fatal: failed to get backup image partition\n");
printf("Dual image checking is bypassed\n");
return -1;
}
printf("Verifying main image at \n", image1_off);
ret = verify_image(flash, image1_off, image1_partsize, &image1_size);//验证主分区kernel
if (ret < 0) {
printf("Dual image checking is bypassed\n");
return 0;
}
if (ret == 0) {
ret = verify_rootfs(flash, image1_off + image1_size,
image1_partsize - image1_size,
&image1_padding_bytes, &rootfs1_size);//验证主分区rootfs
}
image1_ok = ret == 0;
printf("Verifying backup image at \n", image2_off);
ret = verify_image(flash, image2_off, image2_partsize, &image2_size);//验证备份分区kernel if (ret < 0) {
printf("Dual image checking is bypassed\n");
return 0;
}
if (ret == 0) {
ret = verify_rootfs(flash, image2_off + image2_size,
image2_partsize - image2_size,
&image2_padding_bytes, &rootfs2_size);//验证备份分区rootfs
}
image2_ok = ret == 0;
if (!image1_ok && !image2_ok) {
printf("Fatal: both images are broken.\n");
return 3;
}
if (image1_ok && image2_ok) {
printf("Passed\n");
return 0;
}
if (!image2_ok) {
image_total_size = image1_size;
#ifndef CONFIG_DUAL_IMAGE_RESTORE_KERNEL_ONLY
image_total_size += image1_padding_bytes + rootfs1_size;
deadc0de = true;
#endif
if (image_total_size > image2_partsize) {
printf("Fatal: backup image partition can't hold main image\n");
return 4;
}
printf("Restoring backup image ...\n");
ret = copy_firmware(flash, image1_off, image2_off,
image_total_size, deadc0de);//恢复主分区镜像
} else {
image_total_size = image2_size;
#ifndef CONFIG_DUAL_IMAGE_RESTORE_KERNEL_ONLY
image_total_size += image2_padding_bytes + rootfs2_size;
deadc0de = true;
#endif
if (image_total_size > image1_partsize) {
printf("Fatal: main image partition can't hold backup image\n");
return 4;
}
printf("Restoring main image ...\n");
ret = copy_firmware(flash, image2_off, image1_off,
image_total_size, deadc0de);//恢复备份分区镜像