在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

通过自定义升级程序,更直观的理解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;提高软件的…