本帖最后由 小恩GG 于 2021-7-19 17:26 编辑
【经验分享】OTAbootloader swap与Rollback功能
一 文档介绍
官方RT1060 SDK 代码提供了ota_bootloader, 可以供客户实现代码的在线升级,RT10xx OTA bootloader主要功能有两个:
1. 在flash里面实现ISP下载代码到外部flash
2. 提供API给用户,实现不同APP的 swap与rollback
之前分享了几篇经验分享,详细讲解了使用ISP下载代码的情况:
本篇文章主要讲解OTA的memory结构,相关常用API,swap/roll back功能,并且下载APP1,APP2到不同的区域,利用uart输入字符去选择具体的swap运行APP2,还是roll back为APP1运行。
二 ota bootloader原理
2.1 OTA boot loader分区情况
图1
上述分区情况是在外部QSPI flash,大小8Mbyte的情况下。
OTA bootloader: RT SDK ota bootloader 代码
Boot meta 0: 包含了三个partition的起始地址,大小等信息, ISP的外设信息.
Boot meta 1: 包含了三个partition的起始地址,大小等信息.,ISP的外设信息.
Swap meta 0: Bootloader根据meta中的数据进行swap操作
Swap meta 1: Bootloader根据meta中的数据进行swap操作
Partition 1: APP1存放位置
Partition 2: APP2存放位置
Scratch part: APP1 backup位置, 0X60441000前面足够放APP1并且是整数倍sector大小的空间位置。
User data: 用户数据
2.2 swap 和rollback原理
图2
Partition1和partition2的APP需要带有满足bootloader的ota_header, bootloader 会对APP进行CRC校验,通过才会boot,否则会进入ISP。
调用API获取partition2的信息,将需要更新的image放到partition2中,这个image必须要具有合法的header,否则不能swap。
Swap先将partition2scratch位置擦除,然后把partition1的代码写到scratch中,擦除partition1位置,并且将partition2代码写到Partition1位置。
Rollback, 重新运行之前的APP1,擦除partition2位置,把partition1代码写回partition2位置, 擦除partition1位置,拷贝parition2 scratch位置的代码写到partition1.
2.3 boot meta 和 swap meta
boot_meta 0adddr: 0x0x6003c000 size: 0x0x0000020c
boot_meta 1adddr: 0x0x6003d000 size: 0x0x0000020c
2.3.1 Boot meta
OTA bootloader可以从两个地址获取boot Meta数据,当两个地址存放的meta都是合法时(tag要为('B', 'L', 'M', 'T') // 0x42, 0x4c,0x4d, 0x54),bootloader选择version大的meta。如果两个地方都没有合法的meta在,bootloader会将default的bootmeta值烧写到第一个地址处。
Bootmeta中包含了三个partition的起始地址,大小等信息。SDK demo可以通过调用Bootloader的API找到partition的信息,从而进行image的烧写。
另外boot meta还包含了ISP的peripheral,启动后跳转到APP的等待时间等信息(默认5s)。具体参见如下结构体:
- //!@brief Partition information table definitions
- typedef struct
- {
- uint32_t start; //!< Start address of the partition
- uint32_t size; //!< Size of the partition
- uint32_t image_state; //!< Active/ReadyForTest/UnderTest
- uint32_t attribute; //!< Partition Attribute - Defined for futher use
- uint32_t reserved[12]; //!< Reserved for future use
- } partition_t;
- //!@brief Bootloader meta data structure
- typedef struct
- {
- struct
- {
- uint32_t wdTimeout;
- uint32_t periphDetectTimeout;
- uint32_t enabledPeripherals;
- uint32_t reserved[12];
- } features;
- partition_t partition[kPartition_Max];
- uint32_t meta_version;
- uint32_t patition_entries;
- uint32_t reserved0;
- uint32_t tag;
- } bootloader_meta_t;
复制代码
2.3.2 swap meta
OTA bootloader 会从两个地方获取swap meta,如果都非法的话,bootloader会设置一个default值用于进入ISP,这个值最终会写到meta 0对应的地址中(0x6003e000)。如果两个都合法,bootloader会选择version大的meta。
Bootloader根据meta中的数据进行swap操作,某些情况下,reset后meta中的数据会动态修改。主要的行为体现在swap_type这个变量中.
Swap_type的说明如下:
修改meta中的swap_type为kSwapType_Test.
再次reset后,由于meta中的swap type为kSwapType_Test.Bootloader根据kSwapType_Test.的操作规则进行一次rollback。
2. kSwapType_Test: reset后进行rollback
这个参数仅供bootloader内部使用,作用就是进行image的rollback
3. kSwapType_Rollback
Bootloader会在meta中写入kSwapType_Test,reset后bootloader按照kSwapType_Test进行操作
4. kSwapType_Permanent
Reset后修改meta为kSwapType_Permanent,永远从partition1 boot
结构体如下:
- //!@brief Swap progress definitions
- typedef struct
- {
- uint32_t swap_offset; //!< Current swap offset
- uint32_t scratch_size; //!< The scratch area size during current swapping
- uint32_t swap_status; // 1 : A -> B scratch, 2 : B -> A
- uint32_t remaining_size; //!< Remaining size to be swapped
- } swap_progress_t;
- typedef struct
- {
- uint32_t size;
- uint32_t active_flag;
- } image_info_t;
- //!@brief Swap meta information, used for the swapping operation
- typedef struct
- {
- image_info_t image_info[2]; //!< Image info table
- #if !defined(BL_FEATURE_HARDWARE_SWAP_SUPPORTED) || (BL_FEATURE_HARDWARE_SWAP_SUPPORTED == 0)
- swap_progress_t swap_progress; //!< Swap progress
- #endif
- uint32_t swap_type; //!< Swap type
- uint32_t copy_status; //!< Copy status
- uint32_t confirm_info; //!< Confirm Information
- uint32_t meta_version; //!< Meta version
- uint32_t reserved[7]; //!< Reserved for future use
- uint32_t tag; //!< Swap meta tag
- } swap_meta_t;//!@brief Swap progress definitions
- typedef struct
- {
- uint32_t swap_offset; //!< Current swap offset
- uint32_t scratch_size; //!< The scratch area size during current swapping
- uint32_t swap_status; // 1 : A -> B scratch, 2 : B -> A
- uint32_t remaining_size; //!< Remaining size to be swapped
- } swap_progress_t;
- typedef struct
- {
- uint32_t size;
- uint32_t active_flag;
- } image_info_t;
- //!@brief Swap meta information, used for the swapping operation
- typedef struct
- {
- image_info_t image_info[2]; //!< Image info table
- #if !defined(BL_FEATURE_HARDWARE_SWAP_SUPPORTED) || (BL_FEATURE_HARDWARE_SWAP_SUPPORTED == 0)
- swap_progress_t swap_progress; //!< Swap progress
- #endif
- uint32_t swap_type; //!< Swap type
- uint32_t copy_status; //!< Copy status
- uint32_t confirm_info; //!< Confirm Information
- uint32_t meta_version; //!< Meta version
- uint32_t reserved[7]; //!< Reserved for future use
- uint32_t tag; //!< Swap meta tag
- } swap_meta_t;
复制代码
2.4 常用API
Bootloader对外提供API进行操作。SDK常用的API如下
2.4.1 update_image_state
更新swap meta中的数据,更新之前会校验partition1中image的合法性,如果没有合法的image,swap meta中的数据不会更新,返回failure
2.4.2 get_update_partition_info
获取partition的信息,从而指定需要更新的image的写入地址
2.4.3 get_image_state
获取当前bootimage的状态
None/permanent – 表示image不会再swap
UnderTest – 表示reset后会进行rollback操作
三 操作步骤与实现
本次操作,准备两个APP:APP1(起始地址0X60040000)和APP2(起始地址0X60240000) bin文件, 分别使用ISP USB HID下载到partition1和partition2, 第一次上电,默认运行APP1,然后通过APP1里面的串口选择实现swap 和rollback功能。
3.1 APP准备
实现代码如下:
- int main(void)
- {
- char ch;
- status_t status;
- /* Board pin init */
- BOARD_InitPins();
- BOARD_InitBootClocks();
- /* Update the core clock */
- SystemCoreClockUpdate();
- BOARD_InitDebugConsole();
- PRINTF("\r\n------------------hello world + led blinky demo 2.------------------\r\n");
- PRINTF("\r\nOTA bootloader test...\r\n"
- "1 - ReadyForTest\r\n"
- "3 - kSwapType_Permanent\r\n"
- "4 - kSwapType_Rollback\r\n"
- "5 - show image state\r\n"
- "6 - led blinking for 5times\r\n"
- "r - NVIC reset\r\n");
- // show swap state in swap meta
- get_image_swap_state();
- /* Set systick reload value to generate 1ms interrupt */
- if (SysTick_Config(SystemCoreClock / 1000U))
- {
- while (1)
- {
- }
- }
- while(1)
- {
- ch = GETCHAR();
- switch(ch)
- {
- case '1':
- status = bl_update_image_state(kSwapType_ReadyForTest);
- PRINTF("update_image_state to kSwapType_ReadyForTest status: %i\n", status);
- if (status != 0)
- PRINTF("update_image_state(kSwapType_ReadyForTest): failed\n");
- else
- NVIC_SystemReset();
- break;
- case '3':
- status = bl_update_image_state(kSwapType_Permanent);
- PRINTF("update_image_state to kSwapType_Permanent status: %i\n", status);
- if (status != 0)
- PRINTF("update_image_state(kSwapType_Permanent): failed\n");
- else
- NVIC_SystemReset();
- break;
- case '4':
- status = bl_update_image_state(4); // current API code in SDK does not include enumerate data for this, use 4 instead
- PRINTF("update_image_state to kSwapType_Rollback status: %i\n", status);
- if (status != 0)
- PRINTF("update_image_state(kSwapType_Rollback): failed\n");
- else
- NVIC_SystemReset();
- break;
- case '5':
- // show swap state in swap meta
- get_image_swap_state();
- break;
- case '6':
- Led_blink10times();
- break;
- case 'r':
- NVIC_SystemReset();
- break;
- }
- }
- }
复制代码
准备的APP需要注意添加正确的ota_header在前0X400位置,否则不能boot或者不能实现swap。
- const boot_image_header_t ota_header = {
- .tag = IMG_HDR_TAG,
- .load_addr = ((uint32_t)&ota_header) + BL_IMG_HEADER_SIZE,
- .image_type = IMG_TYPE_XIP,
- .image_size = 0,
- .algorithm = IMG_CHK_ALG_CRC32,
- .header_size = BL_IMG_HEADER_SIZE,
- .image_version = 0,
- .checksum = {0xFFFFFFFF},
- };
复制代码
给出一个正确的样本:
图3
这里注意的是,之前的经验分享添加的ota_header只能实现APP1的boot,不能实现swap功能,是因为没有添加正确的image size和checksum值。
所以本文给出一个小软件image_header_padding.exe,可以将输入不带ota_header自动添加正确带有image大小和crc值的前0X400头部。
3.2 操作步骤
准备好的不带ota_header的APP1 evkmimxrt1060_APP1_0X60040400.bin, APP2 image evkmimxrt1060_APP2_0X60240400.bin,blhost.exe,image_header_padding.exe放到一个文件夹下面,APP1,APP2几乎一样,只是打印的结果一个是版本1,一个是版本2:
hello world + led blinky demo 1
hello world + led blinky demo 2
运行如下bat的命令:
- image_header_padding.exe evkmimxrt1060_APP1_0X60040400.bin 0x60040400
- sleep 20
- blhost.exe -t 50000 -u 0x15a2,0x0073 -j -- get-property 1 0
- sleep 20
- blhost.exe -u -t 1000000 -- flash-erase-region 0x6003c000 0x4000
- sleep 50
- blhost -u -t 5000 -- flash-erase-region 0x60040000 0x10000
- sleep 50
- blhost -u -t 5000 -- write-memory 0x60040000 boot_img_crc32.bin
- sleep 100
- image_header_padding.exe evkmimxrt1060_APP2_0X60240400.bin 0x60040400
- sleep 20
- blhost -u -t 5000 -- flash-erase-region 0x60240000 0x10000
- sleep 50
- blhost -u -t 5000 -- flash-erase-region 0x6043b000 0x10000
- sleep 50
- blhost -u -t 5000 -- write-memory 0x60240000 boot_img_crc32.bin
- sleep 100
- pause
复制代码
主要功能是生成APP1对应带头的image,擦除partition1位置,写入APP1。 生成APP2对应带头的image,擦除partition2和scratch位置,写入APP2。
运行bat在下载ota_bootloader并且复位5秒内,运行ISP连接:
blhost.exe -t 50000 -u 0x15a2,0x0073 -j --get-property 1 0
ota_bootloader可以直接通过IDE下载到0X60000000开始的bootloader区域。
3.3 测试结果
图4
从测试结果可以看到,第一次boot了APP1,imagestate: none
输入1,进行swap,可以发现APP2运行,image state: underTest
输入3, 选择permenant,复位后可以发现,运行的依旧是APP2, image state: permenant
输入4, 选择rollback,复位后可以看到运行APP1,image states:none
自此,完成了swap以及rollback功能的准确运行。
另外输入6,可以查看带有SDRAM的led blinky功能可以准确工作,再一次验证了之前ota_bootloader添加DCD的运行准确。
测试log
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>image_header_padding.exe evkmimxrt1060_APP1_0X60040400.bin 0x60040400
- loadAddress=60040400
- The new boot image has been generated!
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>sleep 20
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>blhost.exe -t 50000 -u 0x15a2,0x0073 -j -- get-property 1 0
- {
- "command" : "get-property",
- "response" : [ 1258424320 ],
- "status" : {
- "description" : "0 (0x0) Success.",
- "value" : 0
- }
- }
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>sleep 20
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>blhost.exe -u -t 1000000 -- flash-erase-region 0x6003c000 0x4000
- Inject command 'flash-erase-region'
- Successful generic response to command 'flash-erase-region'
- Response status = 0 (0x0) Success.
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>sleep 50
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>blhost -u -t 5000 -- flash-erase-region 0x60040000 0x10000
- Inject command 'flash-erase-region'
- Successful generic response to command 'flash-erase-region'
- Response status = 0 (0x0) Success.
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>sleep 50
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>blhost -u -t 5000 -- write-memory 0x60040000 boot_img_crc32.bin
- Inject command 'write-memory'
- Preparing to send 21516 (0x540c) bytes to the target.
- Successful generic response to command 'write-memory'
- (1/1)100% Completed!
- Successful generic response to command 'write-memory'
- Response status = 0 (0x0) Success.
- Wrote 21516 of 21516 bytes.
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>sleep 100
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>image_header_padding.exe evkmimxrt1060_APP2_0X60240400.bin 0x60040400
- loadAddress=60040400
- The new boot image has been generated!
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>sleep 20
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>blhost -u -t 5000 -- flash-erase-region 0x60240000 0x10000
- Inject command 'flash-erase-region'
- Successful generic response to command 'flash-erase-region'
- Response status = 0 (0x0) Success.
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>sleep 50
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>blhost -u -t 5000 -- flash-erase-region 0x6043b000 0x10000
- Inject command 'flash-erase-region'
- Successful generic response to command 'flash-erase-region'
- Response status = 0 (0x0) Success.
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>sleep 50
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>blhost -u -t 5000 -- write-memory 0x60240000 boot_img_crc32.bin
- Inject command 'write-memory'
- Preparing to send 21516 (0x540c) bytes to the target.
- Successful generic response to command 'write-memory'
- (1/1)100% Completed!
- Successful generic response to command 'write-memory'
- Response status = 0 (0x0) Success.
- Wrote 21516 of 21516 bytes.
- C:\KerryPC\IMXRTCode\Question\RT1060\bootloader\AN12604\kerry\OTAtest>sleep 100
复制代码
|