19-Openwrt雙固件升級

在上一章節《Openwrt sysupgrade系統升級》中,我們描述了sysupgrade升級系統的過程,這種升級過程會直接firmware分區進行寫入,無法保證系統的安全性,只要在寫入過程突然斷電就會出現系統寫入失敗,升級失敗無法啓動系統的問題。

爲了解決該問題一般會使用雙固件升級的方式,有一個主分區firmware和一個備份分區firmware_backup,常見的有雙固件升級方式有很多種,這邊只介紹一種通用方式

1.升級流程

  • 1.根據sysupgrade的過程,將固件進行校驗寫入,不過寫入的時候將升級文件寫入到備份分區firmware_backup,不直接寫主分區firmware。
  • 2.寫完備份分區後,設置備份分區寫入完成標誌位(一般會開闢一塊很小的分區用來寫標誌位),然後重啓系統
  • 3.uboot啓動的時候,檢測到備份分區標誌位被置位,則讀取備份分區firmware_backup的固件內容。
  • 4.對firmware_backup的內容進行校驗,校驗一切正常後,將firmware_backup的內容寫入到firmware分區。
  • 5.firmware寫入完成後,啓動系統,系統啓動完成後將備份分區標誌位清零,這樣下次啓動後就不會再次升級系統。

通過雙固件的方式就不會出現升級失敗系統啓動不了的問題。

  • 如果步驟1寫入過程被斷電也不會出問題,因爲主分區正常啓動,只是沒有升級到最新的分區而已。
  • 如果是步驟4升級過程被斷電也不會出問題,再次啓動的時候會檢測到主分區有問題會被再次寫入。

2.修改內容

2.1 sysupgrade寫入備份分區

PART_NAME修改位備份分區

PART_NAME=firmware_backup

寫入固件完成重啓前,設置備份分區標誌位

//set backup_flg=1

v "Upgrade completed"
                              
[ -n "$DELAY" ] && sleep "$DELAY" 
v "Rebooting system..."          
upgrade_log_end                  
reboot -f                        
sleep 2                          
force_reboot   

2.2 uboot對雙系統的支持

雙固件最主要的工作都在uboot下面進行,一些mtk提供的新版本uboot一般會有支持部分雙固件功能,位於dual_image.c文件中。

uboot添加雙固件支持的配置


CONFIG_MTK_DUAL_IMAGE_SUPPORT=y
CONFIG_MTK_DUAL_IMAGE_PARTNAME_MAIN="firmware"
CONFIG_MTK_DUAL_IMAGE_PARTNAME_BACKUP="firmware_backup"
CONFIG_MTK_DUAL_IMAGE_SQUASHFS_DATA_CHECK=y
# CONFIG_MTK_DUAL_IMAGE_RESTORE_KERNEL_ONLY is not set

CONFIG_MTDPARTS_DEFAULT="mtdparts=raspi:576k(u-boot),7360k(firmware),7360k(firmware_backup)"

dual_image_check()函數裏面就是校驗firmware合法性的內容。

firmware爲kernel+rootfs,所以校驗的時候兩塊都會進行驗證

printf("Verifying main image at 0x%llx...\n", image1_off);
ret = verify_image(flash, image1_off, image1_partsize, &image1_size);
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);
}

image1_ok = ret == 0;
2.2.1 kernel校驗

kernel的校驗有兩種格式

  • 一種是老版本的kernel,我們會把它稱作LEGACY
  • 另一種是支持最新設備樹的Flattened uImage Tree,會把它稱作FIT

校驗函數位於verify_image

    case IMAGE_FORMAT_LEGACY:
        return verify_legacy_image(flash, offset, maxsize, load_addr,
                       image_size);
#if defined(CONFIG_FIT)
    case IMAGE_FORMAT_FIT:
        return verify_fit_image(flash, offset, maxsize, load_addr,
                    image_size);
#endif
    default:
        printf("Invalid image format\n");
        return 1;
    }

裏面的具體校驗內容,查看代碼細究,裏面對於幾種類型的image都有對於的校驗函數。

 image-fdt.c
 image-fit.c
 image-sig.c
 image.c
2.2.2 rootfs校驗

現在一般使用的都是squashfs文件系統,校驗函數爲verify_squashfs

static int verify_rootfs(void *flash, uint64_t offset, uint64_t maxsize,
             size_t *header_prefix_bytes, size_t *rootfs_size)
{
    uint64_t end = offset + maxsize, leading, extra_bytes, tmp;
    int ret;

    ret = verify_squashfs(flash, offset, end, rootfs_size);
    if (!ret) {
        if (header_prefix_bytes)
            *header_prefix_bytes = 0;
        return 0;
    }

    tmp = offset;
    leading = do_div(tmp, mtk_board_get_flash_erase_size(flash));

    if (!leading)
        return 1;

    extra_bytes = mtk_board_get_flash_erase_size(flash) - leading;
    offset += extra_bytes;

    ret = verify_squashfs(flash, offset, end, rootfs_size);
    if (!ret) {
        if (header_prefix_bytes)
            *header_prefix_bytes = extra_bytes;
        return 0;
    }

    printf("No SquashFS found\n");
    return 1;
}

squashfs文件系統頭部有專門的結構信息,如下

#define SQUASHFS_MAGIC      0x73717368

struct squashfs_super_block {
    __le32 s_magic;
    __le32 pad0[9];
    __le64 bytes_used;
};

校驗內容

if (le32_to_cpu(sb.s_magic) != SQUASHFS_MAGIC)
    return 1;

size = le64_to_cpu(sb.bytes_used);
if (offset + size >= end) {
    printf("RootFS is truncated\n");
    return 1;
}
2.2.3 根據校驗結果進行操作
  • 如果兩個分區的固件都有問題,則報錯
  • 如果主分區和備份分區都沒有問題,判斷備份分區是否有置位,置位則將備份分區拷貝到主分區;
  • 如果主分區正常,備份分區異常,則將主分區拷貝到備份分區
  • 如果主分區異常,備份分區正常,則將備份分區拷貝到主分區
2.2.4 注意事項

spi flash的塊大小爲64K,有時候爲了節省空間把設置標誌位的分區設置成4k,它不是64k的倍數,這樣mtd 讀寫的時候也會報錯

Detected w25q128bv with page size 256 Bytes, erase size 64 KiB, total 16 MiB

另外mount的時候,一般要大於64k的5倍,不然也會出現一直掛載不上

2.3 系統標誌位清零

系統啓動成功後,在init之後的某個腳本處,將標誌位清零即可。後面再次啓動的時候就不會重複燒錄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章