rk3568上,實現ota,計算hash,驗證簽名,判斷激活分區,并通過dd命令,寫入對應AB分區

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述在這里插入圖片描述
在這里插入圖片描述

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

在這里插入圖片描述

通過自定義升級程序,更直觀的理解ota升級原理。
一、模擬計算hash,驗證簽名,判斷激活分區,并通過dd命令,寫入對應分區

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <openssl/sha.h>
#include <json-c/json.h>// 1. 檢測當前激活分區(A或B)
char get_active_slot() {FILE *fp = fopen("/proc/mounts", "r");if (!fp) return 'A'; // 默認A分區char line[256];while (fgets(line, sizeof(line), fp)) {if (strstr(line, "system_a")) {fclose(fp);return 'A';}if (strstr(line, "system_b")) {fclose(fp);return 'B';}}fclose(fp);return 'A';
}// 2. 計算文件SHA256哈希
void calculate_sha256(const char *file_path, char *sha_str) {unsigned char sha_hash[SHA256_DIGEST_LENGTH];SHA256_CTX sha_ctx;SHA256_Init(&sha_ctx);FILE *fp = fopen(file_path, "rb");if (!fp) {strcpy(sha_str, "");return;}unsigned char buffer[4096];size_t bytes_read;while ((bytes_read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {SHA256_Update(&sha_ctx, buffer, bytes_read);}fclose(fp);SHA256_Final(sha_hash, &sha_ctx);// 轉換為字符串for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {sprintf(sha_str + (i * 2), "%02x", sha_hash[i]);}sha_str[SHA256_DIGEST_LENGTH * 2] = '\0';
}// 3. 驗證升級包簽名(簡化示例)
int verify_signature(const char *manifest_path) {// 實際應使用OpenSSL驗證簽名,此處簡化為返回成功printf("驗證簽名: 成功\n");return 0;
}// 4. 寫入分區
int write_partition(const char *img_path, const char *part_name) {char cmd[256];snprintf(cmd, sizeof(cmd), "dd if=%s of=/dev/disk/by-name/%s bs=4M status=progress", img_path, part_name);printf("執行命令: %s\n", cmd);return system(cmd);
}// 5. 設置misc分區標志
int set_misc_flag(const char *flag) {int fd = open("/dev/disk/by-name/misc", O_WRONLY);if (fd < 0) {perror("打開misc分區失敗");return -1;}ssize_t bytes_written = write(fd, flag, strlen(flag));close(fd);if (bytes_written != strlen(flag)) {perror("寫入misc標志失敗");return -1;}printf("寫入misc標志: %s\n", flag);return 0;
}// 主函數:OTA升級流程
int main(int argc, char *argv[]) {if (argc != 2) {fprintf(stderr, "用法: %s <ota_package_url>\n", argv[0]);return 1;}const char *ota_url = argv[1];// 步驟1:檢測當前激活分區char current_slot = get_active_slot();char target_slot = (current_slot == 'A') ? 'B' : 'A';printf("當前分區: %c, 目標分區: %c\n", current_slot, target_slot);// 步驟2:下載OTA包(簡化為本地文件)printf("下載升級包: %s\n", ota_url);// 實際應使用libcurl等工具下載,此處假設已下載到本地const char *ota_package = "ota_package.zip";system("unzip -q ota_package.zip -d ota_temp"); // 解壓到臨時目錄// 步驟3:解析manifest并驗證struct json_object *manifest = json_object_from_file("ota_temp/manifest.json");if (!manifest) {fprintf(stderr, "解析manifest失敗\n");return 1;}// 驗證簽名if (verify_signature("ota_temp/manifest.json") != 0) {fprintf(stderr, "簽名驗證失敗\n");return 1;}// 步驟4:寫入目標分區struct json_object *partitions;json_object_object_get_ex(manifest, "partitions", &partitions);int n = json_object_array_length(partitions);for (int i = 0; i < n; i++) {struct json_object *part = json_object_array_get_idx(partitions, i);const char *name = json_object_get_string(json_object_object_get(part, "name"));const char *img = json_object_get_string(json_object_object_get(part, "image"));const char *expected_sha = json_object_get_string(json_object_object_get(part, "sha256"));// 拼接目標分區名(如boot -> boot_b)char target_part[32];snprintf(target_part, sizeof(target_part), "%s_%c", name, target_slot);// 校驗鏡像哈希char actual_sha[65];char img_path[64];snprintf(img_path, sizeof(img_path), "ota_temp/%s", img);calculate_sha256(img_path, actual_sha);if (strcmp(actual_sha, expected_sha) != 0) {fprintf(stderr, "%s 哈希校驗失敗\n", img);return 1;}// 寫入分區if (write_partition(img_path, target_part) != 0) {fprintf(stderr, "寫入%s失敗\n", target_part);return 1;}}// 步驟5:設置啟動標志,重啟char misc_flag[128];snprintf(misc_flag, sizeof(misc_flag), "target_slot=%c\nboot_retry_count=3\n", target_slot);if (set_misc_flag(misc_flag) != 0) {fprintf(stderr, "設置misc標志失敗\n");return 1;}printf("升級準備完成,重啟中...\n");system("reboot");return 0;
}

二、rk3568上開啟ota,ab分區

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
將每個分區的hash寫入json,打包進入下載包。

寫一個ota下載,驗證進程。
在這里插入圖片描述
通過fuse里或某個只讀分區,讀出公鑰的哈希,計算升級包里的公鑰哈希并與前面的哈希對比
驗證公鑰的安全,然后對鏡像包的哈希進行解密,獲得哈希,并計算升級包的鏡像分區哈希,進行對比
通過則升級。

在這里插入圖片描述
在這里插入圖片描述
寫入完后,還需要讀寫出來,進行比較,失敗則重新刷寫3次為止。

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述
這里還需要做一個超時判斷,若超時則失敗,回滾到a分區,

在這里插入圖片描述
斷電保護:寫入分區時建議分塊寫入并記錄日志,斷電電后可從斷點續傳;

四、參考demo
build_ota_package.sh,打包升級包的腳本,打包為zip

#!/bin/bash
# 生成OTA升級包(包含鏡像、manifest.json和簽名)# 配置
OTA_DIR="ota_temp"
OTA_PACKAGE="ota_v1.1.zip"
TARGET_SLOT="B"  # 目標分區(實際應根據當前版本動態設置)# 創建臨時目錄
rm -rf $OTA_DIR
mkdir -p $OTA_DIR# 復制鏡像文件(假設已編譯好)
cp ../build/boot.img $OTA_DIR/
cp ../build/system.img $OTA_DIR/
cp ../build/vendor.img $OTA_DIR/# 生成manifest.json
cat > $OTA_DIR/manifest.json << EOF
{"version": "v1.1","target_slot": "$TARGET_SLOT","partitions": [{"name": "boot","image": "boot.img","sha256": "$(sha256sum $OTA_DIR/boot.img | awk '{print $1}')"},{"name": "system","image": "system.img","sha256": "$(sha256sum $OTA_DIR/system.img | awk '{print $1}')"},{"name": "vendor","image": "vendor.img","sha256": "$(sha256sum $OTA_DIR/vendor.img | awk '{print $1}')"}],"signature": "dummy_signature"  # 實際應替換為RSA簽名
}
EOF# 生成簽名(示例:使用openssl生成RSA簽名)
# openssl dgst -sha256 -sign private.key -out $OTA_DIR/manifest.sig $OTA_DIR/manifest.json# 打包為OTA包
cd $OTA_DIR && zip -r ../$OTA_PACKAGE ./* && cd ..echo "OTA包生成完成: $OTA_PACKAGE"
rm -rf $OTA_DIR

ota_client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <openssl/sha.h>
#include <json-c/json.h>// 1. 檢測當前激活分區(A或B)
char get_active_slot() {FILE *fp = fopen("/proc/mounts", "r");if (!fp) return 'A'; // 默認A分區char line[256];while (fgets(line, sizeof(line), fp)) {if (strstr(line, "system_a")) {fclose(fp);return 'A';}if (strstr(line, "system_b")) {fclose(fp);return 'B';}}fclose(fp);return 'A';
}// 2. 計算文件SHA256哈希
void calculate_sha256(const char *file_path, char *sha_str) {unsigned char sha_hash[SHA256_DIGEST_LENGTH];SHA256_CTX sha_ctx;SHA256_Init(&sha_ctx);FILE *fp = fopen(file_path, "rb");if (!fp) {strcpy(sha_str, "");return;}unsigned char buffer[4096];size_t bytes_read;while ((bytes_read = fread(buffer, 1, sizeof(buffer), fp)) > 0) {SHA256_Update(&sha_ctx, buffer, bytes_read);}fclose(fp);SHA256_Final(sha_hash, &sha_ctx);// 轉換為字符串for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {sprintf(sha_str + (i * 2), "%02x", sha_hash[i]);}sha_str[SHA256_DIGEST_LENGTH * 2] = '\0';
}// 3. 驗證升級包簽名(簡化示例)
int verify_signature(const char *manifest_path) {// 實際應使用OpenSSL驗證簽名,此處簡化為返回成功printf("驗證簽名: 成功\n");return 0;
}// 4. 寫入分區
int write_partition(const char *img_path, const char *part_name) {char cmd[256];snprintf(cmd, sizeof(cmd), "dd if=%s of=/dev/disk/by-name/%s bs=4M status=progress", img_path, part_name);printf("執行命令: %s\n", cmd);return system(cmd);
}// 5. 設置misc分區標志
int set_misc_flag(const char *flag) {int fd = open("/dev/disk/by-name/misc", O_WRONLY);if (fd < 0) {perror("打開misc分區失敗");return -1;}ssize_t bytes_written = write(fd, flag, strlen(flag));close(fd);if (bytes_written != strlen(flag)) {perror("寫入misc標志失敗");return -1;}printf("寫入misc標志: %s\n", flag);return 0;
}// 主函數:OTA升級流程
int main(int argc, char *argv[]) {if (argc != 2) {fprintf(stderr, "用法: %s <ota_package_url>\n", argv[0]);return 1;}const char *ota_url = argv[1];// 步驟1:檢測當前激活分區char current_slot = get_active_slot();char target_slot = (current_slot == 'A') ? 'B' : 'A';printf("當前分區: %c, 目標分區: %c\n", current_slot, target_slot);// 步驟2:下載OTA包(簡化為本地文件)printf("下載升級包: %s\n", ota_url);// 實際應使用libcurl等工具下載,此處假設已下載到本地const char *ota_package = "ota_package.zip";system("unzip -q ota_package.zip -d ota_temp"); // 解壓到臨時目錄// 步驟3:解析manifest并驗證struct json_object *manifest = json_object_from_file("ota_temp/manifest.json");if (!manifest) {fprintf(stderr, "解析manifest失敗\n");return 1;}// 驗證簽名if (verify_signature("ota_temp/manifest.json") != 0) {fprintf(stderr, "簽名驗證失敗\n");return 1;}// 步驟4:寫入目標分區struct json_object *partitions;json_object_object_get_ex(manifest, "partitions", &partitions);int n = json_object_array_length(partitions);for (int i = 0; i < n; i++) {struct json_object *part = json_object_array_get_idx(partitions, i);const char *name = json_object_get_string(json_object_object_get(part, "name"));const char *img = json_object_get_string(json_object_object_get(part, "image"));const char *expected_sha = json_object_get_string(json_object_object_get(part, "sha256"));// 拼接目標分區名(如boot -> boot_b)char target_part[32];snprintf(target_part, sizeof(target_part), "%s_%c", name, target_slot);// 校驗鏡像哈希char actual_sha[65];char img_path[64];snprintf(img_path, sizeof(img_path), "ota_temp/%s", img);calculate_sha256(img_path, actual_sha);if (strcmp(actual_sha, expected_sha) != 0) {fprintf(stderr, "%s 哈希校驗失敗\n", img);return 1;}// 寫入分區if (write_partition(img_path, target_part) != 0) {fprintf(stderr, "寫入%s失敗\n", target_part);return 1;}}// 步驟5:設置啟動標志,重啟char misc_flag[128];snprintf(misc_flag, sizeof(misc_flag), "target_slot=%c\nboot_retry_count=3\n", target_slot);if (set_misc_flag(misc_flag) != 0) {fprintf(stderr, "設置misc標志失敗\n");return 1;}printf("升級準備完成,重啟中...\n");system("reboot");return 0;
}

verify_new_system.c,重啟后進行校驗是否能正常啟動。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>// 檢查關鍵服務是否啟動
int check_critical_services() {// 檢查sshd服務if (system("pgrep sshd > /dev/null 2>&1") != 0) {printf("sshd服務未啟動\n");return -1;}// 檢查應用程序if (system("pgrep my_app > /dev/null 2>&1") != 0) {printf("應用程序my_app未啟動\n");return -1;}return 0;
}// 設置misc分區標志
int set_misc_flag(const char *flag) {int fd = open("/dev/disk/by-name/misc", O_WRONLY);if (fd < 0) {perror("打開misc分區失敗");return -1;}ssize_t bytes_written = write(fd, flag, strlen(flag));close(fd);return (bytes_written == strlen(flag)) ? 0 : -1;
}// 檢測當前啟動的分區
char get_current_slot() {FILE *fp = fopen("/proc/mounts", "r");if (!fp) return 'A';char line[256];while (fgets(line, sizeof(line), fp)) {if (strstr(line, "system_a")) {fclose(fp);return 'A';}if (strstr(line, "system_b")) {fclose(fp);return 'B';}}fclose(fp);return 'A';
}int main() {char current_slot = get_current_slot();printf("當前啟動分區: %c,開始驗證...\n", current_slot);// 驗證系統狀態if (check_critical_services() != 0) {printf("系統驗證失敗,準備回滾\n");char flag[64];snprintf(flag, sizeof(flag), "boot_failed=%c\n", current_slot);set_misc_flag(flag);system("reboot"); // 重啟后U-Boot會回滾return 1;}// 驗證成功,標記分區為穩定printf("系統驗證成功,更新分區狀態\n");char flag[128];snprintf(flag, sizeof(flag), "slot_successful=%c\ntarget_slot=%c\n", current_slot, current_slot);set_misc_flag(flag);return 0;
}

有了ab分區,在uboot啟動時,運行一段時間后,如出現崩潰,可以在uboot失敗多次,嘗試回滾到a區

總結,rk和nvidia的方案,是有些區別的,nvidia是可以更新uboot,kernel,system,因為nvidia把uboot分出去了
到cboot,cboot代替uboot。rk的芯片是沒有cboot的,所以uboot是不能更新的。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917430.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917430.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917430.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據分析—numpy庫

numpy庫NumPy 庫全面指南NumPy (Numerical Python) 是 Python 科學計算的基礎庫&#xff0c;提供了高性能的多維數組對象和工具。以下是 NumPy 的核心功能和使用方法。一、安裝與基礎1. 安裝 NumPypip install numpy2. 導入 NumPyimport numpy as np # 標準導入方式二、數組創建…

Vue3 setup、ref和reactive函數

一、setup函數1.理解&#xff1a;Vue3.0中一個新的配置項&#xff0c;值為一個函數。2.setup是所有Composition API(組合API)的“表演舞臺”。3.組件中用到的&#xff1a;數據、方法等等&#xff0c;均要配置在setup中。4.setup函數的兩種返回值&#xff1a;(1).若返回一個對象…

python中appium 的NoSuchElementException錯誤 原因以及解決辦法

錯誤收集D:\Program\Util\python.exe "D:/Program/myUtil/PyCharm 2024.3.5/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --target demo.py::TestAppium Testing started at 15:57 ... Launching pytest with arguments demo.py::TestAppium --no-hea…

mybatis-plus從入門到入土(四):持久層接口之BaseMapper和選裝件

大家好&#xff0c;今天繼續更新MybatisPlus從入門到入土系列&#xff0c;上一次的持久層接口還沒講完&#xff0c;只講了IService接口&#xff0c;今天我們繼續來講一下。 BaseMapper BaseMapper中的方法也比較簡單&#xff0c;都是增刪改查的基礎API&#xff0c;不知道大家還…

數組和指針的關系

在 C 語言中&#xff0c;?指針和數組有著非常緊密的聯系&#xff0c;但它們本質上是 ?不同的概念。理解它們的關系是掌握 C 語言內存操作的關鍵。下面我會從多個角度幫你梳理 ?指針和數組的直接聯系&#xff0c;并解釋它們的異同點。1. 數組和指針的本質區別?概念本質存儲方…

AI大模型探索之路-實戰篇:智能化IT領域搜索引擎之github網站在線搜索

系列篇章?? No. 文章 1 AI大模型探索之路-實戰篇:智能化IT領域搜索引擎的構建與初步實踐 2 AI大模型探索之路-實戰篇:智能化IT領域搜索引擎之GLM-4大模型技術的實踐探索 3 AI大模型探索之路-實戰篇:智能化IT領域搜索引擎之知乎網站數據獲取(初步實踐) 4 AI大模型探索之路…

從0到1學PHP(十二):PHP 框架入門與項目實戰

目錄一、主流 PHP 框架介紹1.1 Laravel1.2 ThinkPHP1.3 Yii1.4 框架的優勢二、框架基本使用&#xff08;以 Laravel 為例&#xff09;2.1 框架的安裝與配置2.2 路由定義、控制器創建、視圖渲染2.3 數據庫操作&#xff08;ORM 的使用&#xff09;三、小型項目實戰3.1 項目需求分…

MPLS LSP

一、概述上一章我們已經介紹過,LSP是MPLS報文在MPLS網絡中轉發時經過的路徑,可以看作是由報文傳輸方向節點為對應FEC分配的MPLS入標簽組成的,因為每臺設備上為每個FEC分配的入標簽是唯一 的&#xff0c;并與由下游節點為本地節點上該FEC分配的出標簽建立映射關系&#xff0c; 所…

圖像、視頻、音頻多模態大模型中長上下文token壓縮方法綜述

多模態大模型MLLMs 能夠處理高分辨率圖像、長視頻序列和冗長音頻輸入等復雜上下文&#xff0c;但自注意力機制的二次復雜度使得大量輸入 token 帶來了巨大的計算和內存需求。 如下圖&#xff0c;上&#xff1a;圖像、視頻和音頻數據類型可以在其表示維度上進行擴展&#xff0c;…

Spring MVC 九大組件源碼深度剖析(一):MultipartResolver - 文件上傳的幕后指揮官

文章目錄一、為什么從 MultipartResolver 開始&#xff1f;二、核心接口&#xff1a;定義文件上傳的契約三、實現解析&#xff1a;兩種策略的源碼較量1. StandardServletMultipartResolver&#xff08;Servlet 3.0 首選&#xff09;2. CommonsMultipartResolver&#xff08;兼容…

stm32是如何實現電源控制的?

STM32的電源控制主要通過內置的電源管理模塊&#xff08;PWR&#xff09;實現&#xff0c;涵蓋電壓調節、功耗模式切換和電源監控等功能。以下是其核心機制及實現方式&#xff1a;??1. 電源架構與供電區域??STM32的電源系統分為多個供電區域&#xff0c;各司其職&#xff1…

《R for Data Science (2e)》免費中文翻譯 (第3章) --- Data transformation(1)

寫在前面 本系列推文為《R for Data Science (2)》的中文翻譯版本。所有內容都通過開源免費的方式上傳至Github&#xff0c;歡迎大家參與貢獻&#xff0c;詳細信息見&#xff1a; Books-zh-cn 項目介紹&#xff1a; Books-zh-cn&#xff1a;開源免費的中文書籍社區 r4ds-zh-cn …

rclone、rsync、scp使用總結

數據同步工具使用總結【rclone、rsync、scp】一、數據處理背景二、數據處理方法對比1、數據關系梳理2、不同工具處理方法3、經驗總結三、工具擴展知識1、rclone工具介紹&#xff08;1&#xff09;、rclone概述&#xff08;2&#xff09;、安裝工具及配置本地文件遷移到云上服務…

用latex+vscode+ctex寫畢業論文

文章目錄前言一、安裝latex二、安裝ctex包三、更新ctex包四、使用ctex文檔類前言 用latexvscodectex寫畢業論文。&#xff08;英文論文不用安裝ctex&#xff09; CTEX 宏集是面向中文排版的通用 LATEX 排版框架&#xff0c;為中文 LATEX 文檔提供了漢字輸出支持、標點壓縮、字…

深度學習·mmsegmentation基礎教程

mmsegmentation的使用教程 mmsegmentation微調方法總結 自定義自己的數據集&#xff1a;mmsegmentation\configs\_base_\datasets\ZihaoDataset_pipeline.py注冊&#xff1a;mmsegmentation\configs\_base_\datasets\__init__.py定義訓練和測試的pipeline&#xff1a;mmsegme…

InfluxDB 與 Node.js 框架:Express 集成方案(二)

四、優化與注意事項 &#xff08;一&#xff09;性能優化技巧 連接池管理&#xff1a;使用連接池可以有效減少創建和銷毀數據庫連接的開銷。在 Node.js 中&#xff0c;可以借助influx模塊結合第三方連接池庫&#xff0c;如generic-pool來實現連接池的管理 。通過設置連接池的…

單位長度上的RC參數

1inch1000mil25.4mm2.54cm 使用SI9000計算導線上電容電感參數并使用Q2D進行仿真驗證。使用SI9000建立一個阻抗為50歐的微帶線模型&#xff0c;后對該模型進行1GHz頻域計算 通過計算得到結果&#xff0c;可知1GHz頻率下單位傳輸線上的RLGC參數使用SI9000計算好單位長度上的RLGC參…

基于Dockerfile 部署一個 Flask 應用

Docker 與 Python&#xff1a;容器化部署應用&#xff0c;實現快速發布與彈性伸縮 以下是一個簡單的 Flask 應用 # app.py - 一個簡單的Flask應用 from flask import Flask import osapp Flask(__name__)app.route("/") def hello():env os.environ.get(FLASK_ENV,…

DFT設計中的不同階段介紹

在DFT&#xff08;Design for Test&#xff0c;可測試性設計&#xff09;軟件開發中&#xff0c;針對設計檢測的完整流程通常包含Setup&#xff08;設置&#xff09;、Analysis&#xff08;分析&#xff09;、Insertion&#xff08;插入&#xff09;和Verification&#xff08;…

自動化測試準備工作:概念篇

自動化 什么是自動化? 超市的自動閘門&#xff0c;不需要手動的開門關門生活中的自動動化案例有效的減少了人力的消耗&#xff0c;同時也提高了生活的質量。 軟件自動化測試同理&#xff0c;通過編寫自動化測試程序&#xff08;減少人力和時間的消耗&#xff0c;提高軟件的…