U-Boot阶段系统全量更新固件包制作杂记

news2024/11/23 3:16:34

        背景:有一个在 U-Boot 阶段做系统全量自更新的需求,要制作系统的全量固件包(U-Boot.img、kerlen.img、rootfs.img)。大体分为三个主要部分:U-Boot-shell 脚本编写、打包各镜像为一个固件包、固件包的加密和解密

一、U-Boot-shell 脚本

1. 一些概念

        既然是在uboot阶段进行全量更新,那么更新的业务逻辑脚本自然要能在uboot下运行,就得编写uboot-shell script(一般我们提起shell脚本就想到是linux下的shell脚本),这里有一些概念需要厘清:shell指命令解释器,而shell有很多种,linux下有自己的shell,最常用的就是我们所熟知的bash,那自然uboot也有自己的命令解释器,一般有两种。

U-Boot 主要使用以下两个 shell 解释器:

1.hush shell

        hush 是U-Boot中默认的命令解释器,他支持基本的脚本功能,如变量、条件语句、循环等。

        hush 提供了比传统的简单命令行解释器更强大的功能,使得编写复杂启动脚本成为可能

        在U-Boot 源码中,hush的实现位于common目录下的cli_hush.c

2.minimal commmand line interpreter(CLI)

        这是一个非常基础的命令解释器,仅提供最基本的命令解释功能,不具有复杂脚本的功能,在uboot 源码中位于common目录下的cli.c文件中

一般 U-Boot 默认使用第二种简单的 shell 解释器

2. 编写一个uboot脚本

        以上概念理解清楚后就可以编写自己的U-Boot-shell 脚本了,比较简单,就像平时在uboot控制台输入uboot命令一样,只不过把这些命令依次写到一个文件中即可,在文件中写入要执行的业务逻辑(核心就是要把固件包中的哪一段写入到flash分区的哪一个分区),这里我们创建一个文件updata.cmd,文件内容如下:

#设置你的分区信息,视自己情况而定

setenv mtdids nand0=nand
setenv mtdparts 'mtdparts=nand:1024k@0(boot0)ro,3072k@1048576(uboot)ro,1024k@4194304(secure_storage)ro,-(sys)'
saveenv

#解密各镜像,如果有加密的话

decode uboot.img

decode kernel.img

decode rootfs.img

#将解密好的各镜像写入到对应分区,写入的方法和地址addr视自己情况而定

flash write addr1 uboot size

flash write addr2 kernel size

flash write addr3 rootfs size

3. 制作可供uboot执行的脚本——scr

        到这里,一个基本的更新逻辑就写完了,那么这个脚本能直接丢到内存中让uboot去运行吗,答案是不可以的,一般我们在linux系统下写的脚本直接通过bash + 文件名就可以执行,但在uboot中,必须要先通过一个工具mkimage,将文件制作为一个scr文件后(其实就是在文件前加了一些头部信息),这个scr文件才能被uboot直接执行。mkimage工具在uboot源码中自带,位于tools目录下。制作scr文件时在linux下输入以下指令即可。

mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n "U-Boot Script" -d script.txt script.scr

-A        指定目标处理器架构类型为 arm。

-O       指定生成的镜像是用于 Linux 系统的

-T        指定镜像类型是script,意为是一个脚本镜像

-a        指定镜像的入口地址,默认为0

-e        指定镜像的实际加载地址,默认为0

-n        指定生成的镜像名称为"U-Boot Script",一个描述性的名称,感觉没什么用

-d        指定源文件

script.txt        原始脚本文件

script.scr       生成的带头部信息的可直接由uboot执行的脚本文件(要的就是这个)

选项的含义可以使用命令 mkimage -h 查看

其实在输入指令时,简单输入以下指令即可,不必指定每一项参数

mkimage -C none -A arm -T script -d updata.cmd updata.scr

        当这个scr文件制作好后,就可以将这个文件下载到内存中,uboot控制台输入source + 内存addr即可以执行这个文件。其中source为uboot专门执行脚本的一个指令

二、打包各镜像为一个固件包

        核心就一条思想:在linux下,利用 cat 指令将更新逻辑脚本和各个镜像拼接起来即可

拼接顺序为:updata.scr + uboot.img + kernel.img + rootfs.img

#!/bin/bash

#make uboot script
mkimage -C none -A arm -T script -d ./updata_ubi.cmd ./updata.scr

#cp file to current path
cp ~/a8replace/out/a40i_c/p3-spinand-ubi/pack_out/boot_package.fex ./uboot.img
cp ~/a8replace/out/a40i_c/p3-spinand-ubi/buildroot/boot.img ./kernel.img
cp ~/a8replace/out/a40i_c/p3-spinand-ubi/buildroot/rootfs.ubifs ./rootfs.ubifs

#encode image
此处省略

#bytes alignment
script_size=$(stat --format=%s updata.scr)
remainder=$((script_size % 1024))
if [ $remainder -ne 0 ]; then
    padding=$((1024 - remainder))
    dd if=/dev/zero bs=1 count=$padding >> updata.scr
fi

#make firmware
cat updata.scr uboot_encode.img kernel_encode.img rootfs_encode.ubifs > allwinner_updata_packet.bin

注意:这里有一个坑,在进行拼接时,要考虑到字节对齐问题,如若不然,内存中镜像写入flash时可能导致uboot异常或镜像写入后无法启动。因为ARM架构下默认是打开字节对齐优化的,因为这可以提高内存的访问效率。在 U-Boot 的 start.s 启动文件中我们可以看到:

这里的        orr     r0, r0, #0x00000002     @ set bit 1 (--A-) Align        就是打开字节对齐的开关,可以看到默认是打开的。

如果要关闭的话更改此行为     bic     r0, r0, #0x00000002     @ 清除 bit 1 (--A-) 关闭字节对齐优化

        那么我们是关闭字节优化吗,当然不是,这虽然解决了内存中镜像写入flash时可能导致uboot异常或镜像写入后无法启动的问题,但这是以损失系统运行效率为代价的,绝不推荐这样做,那怎么避免这个问题呢,答案是我们在制作固件包时就对齐好地址。一般来说更新逻辑脚本(updata.scr)后紧接的是第一个镜像(uboot.img),字节对齐就是要求uboot.img的起始地址为整数地址,如42000800等类似的地址,我这里是按照1024字节对齐的,所以我在脚本中把updata.scr后补0,使其为1024字节的整数倍。之后再紧跟uboot.img。

三、固件包的加密和解密

1. 是否要加密

        固件包是否要加密,这取决于你的安全需求,我这里的固件包是要加密的。既然要加密,首先就要考虑用哪种方式去做加密,是对称加密还是非对称加密、是整个固件加密还是只加密某一段。这取决于你的具体需求。同时还应考虑到加密算法的选取,如果加密强度太大,则下位机解密花费的代价太大。总之一句话,要综合自己的需求选择合适的加密算法和加密方式。

2.TEA加密

        我这里采用的是对称加密算法中的TEA加密算法,简单、高效,适用于嵌入式类的资源受限场景。TEA算法全称是Tiny Encryption Algorithm由剑桥大学计算机实验室的David Wheeler和Roger Needham于1994年发明。它是对称加密中的一种分组(分块)密码算法,其明文密文块为64比特(8字节),密钥长度为128比特(16字节)。该算法使用了一个神秘常数δ来参与加密,它来源于黄金比率,以保证每一轮加密都不相同。但δ的精确值似乎并不重要,这里取其近似值即可 TEA 把它定义为 δ=「(√5 - 1)/231」(也就是程序中的 0×9E3779B9)。TEA算法利用不断增加的Delta(黄金分割率)近似值作为变化,使得每轮的加密是不同,该加密算法的迭代次数可以改变,建议的迭代次数为32轮

        TEA加密的原理:是把输入分成两组,分别是v[0],v[1],绿色方格为做加法,红色圆圈为做异或,可以看出,用密钥k[0],k[1]加密后,把两个数做一次置换,再加密一次,这样经过多轮加密以后就可以通过简单的算法把两个数变得很复杂,满足加密算法混乱和扩散的特性。

以下是采用TEA加密,对一个文件进行加、解密的简单demo。

上位机加密

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

// TEA加密模块
void tea_encrypt(uint32_t *v, uint32_t *k)
{
        uint32_t v0 = v[0], v1 = v[1];
        uint32_t sum = 0;
        uint32_t delta = 0x9e3779b9; // A constant

        for (int i = 0; i < 32; i++)
        {
                sum += delta;
                v0 += ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
                v1 += ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
        }
        v[0] = v0;
        v[1] = v1;
}

int main(int argc, char *argv[])
{
        if (argc != 4)
        {
                fprintf(stderr, "Usage: %s <input file> <output file> <key>\n", argv[0]);
                return 1;
        }

        const char *input_file = argv[1];
        const char *output_file = argv[2];
        const char *key_str = argv[3];

        // 读取输入文件
        FILE *in = fopen(input_file, "rb");
        if (!in)
        {
                perror("Failed to open input file");
                return 1;
        }

        // 创建输出文件
        FILE *out = fopen(output_file, "wb");
        if (!out)
        {
                perror("Failed to open output file");
                fclose(in);
                return 1;
        }

        // 将字符串密钥转换为4个32位整数
        uint32_t key[4];
        sscanf(key_str, "%08X%08X%08X%08X", &key[0], &key[1], &key[2], &key[3]);

        // 缓冲区用于存储8字节的数据块
        uint32_t data[2];
        size_t bytes_read;

        while ((bytes_read = fread(data, sizeof(uint32_t), 2, in)) > 0)
        {
                if (bytes_read == 1)
                {
                        // 如果只读到了4字节,用0填充剩余部分
                        data[1] = 0;
                }

                // 加密数据
                tea_encrypt(data, key);

                // 写入加密后的数据
                fwrite(data, sizeof(uint32_t), 2, out);
        }

        // 关闭文件
        fclose(in);
        fclose(out);

        printf("Encryption completed.\n");
        return 0;
}

                                          

上位机解密

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

// TEA解密
void tea_decrypt(uint32_t *v, uint32_t *k) {
        uint32_t v0 = v[0], v1 = v[1];
        uint32_t sum = 0xC6EF3720; // delta * 32
        uint32_t delta = 0x9e3779b9;

        for (int i = 0; i < 32; i++)
        {
                v1 -= ((v0 << 4) + k[2]) ^ (v0 + sum) ^ ((v0 >> 5) + k[3]);
                v0 -= ((v1 << 4) + k[0]) ^ (v1 + sum) ^ ((v1 >> 5) + k[1]);
                sum -= delta;
        }
        v[0] = v0;
        v[1] = v1;
}

int main(int argc, char *argv[])
{
        if (argc != 4)
        {
                fprintf(stderr, "Usage: %s <encrypted file> <decrypted file> <key>\n", argv[0]);
                return 1;
        }

        const char *encrypted_file = argv[1];
        const char *decrypted_file = argv[2];
        const char *key_str = argv[3];

        // 读取加密文件
        FILE *in = fopen(encrypted_file, "rb");
        if (!in)
        {
                perror("Failed to open encrypted file");
                return 1;
        }

        // 创建解密文件
        FILE *out = fopen(decrypted_file, "wb");
        if (!out)
        {
                perror("Failed to open decrypted file");
                fclose(in);
                return 1;
        }

        // 将字符串密钥转换为4个32位整数
        uint32_t key[4];
        sscanf(key_str, "%08X%08X%08X%08X", &key[0], &key[1], &key[2], &key[3]);

        // 缓冲区用于存储8字节的数据块
        uint32_t data[2];
        size_t bytes_read;

        while ((bytes_read = fread(data, sizeof(uint32_t), 2, in)) > 0)
        {
                if (bytes_read == 1)
                {
                        // 如果只读到了4字节,用0填充剩余部分
                        data[1] = 0;
                }

                // 解密数据
                tea_decrypt(data, key);

                // 写入解密后的数据
                fwrite(data, sizeof(uint32_t), 2, out);
        }

        // 关闭文件
        fclose(in);
        fclose(out);

        printf("Decryption completed.\n");
        return 0;
}

通过diff指令(比较两个文件内容是否相同)可以看出加密解密是否成功

注意:TEA加密时,待加密数据一定要8是字节的整数倍,否则就要做补零处理,这样在解密时也需要将补的0重新去掉,会带来很多麻烦。推荐只对镜像的一部分做加密,这样不论镜像大小再怎么变,我们加密段的数据大小始终为8字节的整数倍。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2212884.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电采暖集控系统陕西高陵体育馆应用项目案例

电采暖集控系统是一种集监测、控制和管理于一体的智能管理系统&#xff0c;旨在提高采暖效率、降低能耗和运营成本&#xff0c;同时提升用户的舒适度。该系统利用先进的计算机控制技术和系统集成技术&#xff0c;实现对电热采暖设备的集中管理和远程操控。 陕西高陵体育馆 是…

四川方维嘉术科技有限公司简介

四川方维嘉术科技有限公司 公司简介 四川方维嘉术科技有限公司成立于2023年&#xff0c;注册资本100万元整&#xff0c;位于中国西南地区的中心位置&#xff0c;是一家专注于供应医疗设备、高值耗材并提供医疗方面解决方案的企业。 【主要代理产品】 湖南瑞康通 &#xff1…

Alberta Wells数据集:首个包含超过213,000个油气井的大规模高质量基准数据集,它们是温室气体和其他污染物的重要来源,助力环境监测与气候变化。

2024-10-11&#xff0c;由Mila – Quebec AI Institute和McGill University等机构创建了首个大规模油井检测数据集&#xff0c;这个数据集的意义在于提供了一个工具&#xff0c;能够通过卫星图像识别和定位全球数以百万计的废弃油气井&#xff0c;这对于减少温室气体排放和保护…

数据结构与算法:堆与优先队列的深入剖析

数据结构与算法&#xff1a;堆与优先队列的深入剖析 堆是一种特殊的树形数据结构&#xff0c;广泛应用于优先队列的实现以及各种高效的算法中&#xff0c;如排序和图算法。通过深入了解堆的结构、不同堆的实现方式&#xff0c;以及堆在实际系统中的应用&#xff0c;我们可以掌…

使用js和canvas实现简单的网页打砖块小游戏

玩法介绍 点击开始游戏后&#xff0c;使用键盘上的←→控制移动&#xff0c;小球会不停移动&#xff0c;板子触碰小球时会反弹&#xff0c;碰撞到砖块时会摧毁砖块&#xff0c;如果没有用板子接住小球就游戏失败 代码实现 代码比较简单&#xff0c;直接阅读注释即可&#x…

工作日志:elementplus上传图片问题

问题&#xff1a;打开弹窗&#xff0c;上传一张照片后&#xff0c;关闭再打开&#xff0c;之前上传的图片仍在列表里展示。 然后添加了几行代码&#xff0c;报错。 <el-upload list-type"picture-card":limit"1":on-success"handleAvatarSuccess&…

Spring Boot 之三大配置文件.properties、.yml、.yaml 及其优先级解析

Spring Boot 的强大之处在于其高度可配置性&#xff0c;允许开发者根据不同环境和需求定制应用程序的行为。而这一切的核心便是配置文件。Spring Boot 支持多种配置文件格式&#xff0c;其中最常用的三种是 .properties、.yml 和 .yaml。 1. .properties&#xff1a;传统方式 …

基于NXP LS1023+FPGA的嵌入式解决方案

基于 NXP公司的LS1043A高性能64位ARM四核处理器。 LS1043A处理器是NXP公司面向嵌入式网络推出的一款四核64位ARM处理器&#xff0c; 支持无风扇设计的灵活I/O封装&#xff0c; 提供超过10 Gbps的性能&#xff0c;是专为小规格网络和工业应用而设计的解决方案。全新23x23封装方式…

jquery实现点击菜单实现高德地图定位点与数据展示联动效果

&#x1f34a;jquery实现点击菜单实现高德地图定位点与数据展示联动效果 版本介绍&#xff1a; jQuery v3.7.1高德地图JS API 2.0 代码仓库 ⭐ Gitee&#xff1a;实现点击菜单实现高德地图定位点与数据展示联动效果 1.启动说明 &#x1f4d4; 推荐VS Code编辑器插件Live Ser…

java项目之信息化在线教学平台的设计与实现(源码+文档)

项目简介 信息化在线教学平台的设计与实现实现了以下功能&#xff1a; 信息化在线教学平台的设计与实现的主要使用者管理员功能有个人中心&#xff0c;学生信息管理&#xff0c;教师信息管理&#xff0c;教学信息管理&#xff0c;学生成绩管理&#xff0c;留言板管理&#xf…

29.数据结构与算法-查找-查找的基本概念

查找的基本概念 查找表 主关键字与次关键字 查找是否成功 查找的目的&#xff08;查询&#xff0c;检索&#xff0c;插入&#xff0c;删除&#xff09; 查找表的分类&#xff08;静态查找表&#xff0c;动态查找表&#xff09; 如何评价查找算法&#xff08;平均查找长度ASL&a…

前端编程艺术(5)---Vue3(从零基础到项目开发)

目录 1.Vue.js 2.快速上手 2.数据响应式 1.reactive函数 2.ref函数 3.Vue工程化 1.安装node.js 2.脚手架创建项目 3.项目setup 4.vue指令 1.内容渲染指令 2.属性绑定指令 3.事件绑定指令 4.条件渲染指令 5.列表渲染指令 6.双向绑定指令 7.指令修饰符 8.样式绑…

[JAVAEE] 创建线程的方法 + Thread类中的常用方法 + 线程状态

目录 一. 创建线程的方法 1.1 继承 Thread 类. 1.2 实现 Runnable 接口. 1.3 lambda表达式创建线程 二. Thread类中的常用方法 2.1 start方法 2.2 run方法 2.3 sleep静态方法 2.4 isDaemoon() and setDaemon() 2.5 isAlive() 2.6 Thread.currentThread() 2.7 inter…

anzocapital:交易量对止损和止盈策略的影响

在金融市场中&#xff0c;交易量的波动对交易策略有着深远的影响。anzocapital作为一家专业的金融服务提供商&#xff0c;深知交易量对止损和止盈策略的重要性。大额交易订单往往优先执行&#xff0c;这可能导致EA设置的小止损和小止盈在实际操作中出现偏差。 以市场开盘为例&…

什么是大模型?(超详细)大模型从入门到精通,看这一篇就够了!

大模型的定义 大模型是指具有数千万甚至数亿参数的深度学习模型。近年来&#xff0c;随着计算机技术和大数据的快速发展&#xff0c;深度学习在各个领域取得了显著的成果&#xff0c;如自然语言处理&#xff0c;图片生成&#xff0c;工业数字化等。为了提高模型的性能&#xf…

【Kubernetes① 基础】一、容器基础

目录 一、进程二、隔离与限制三、容器镜像 一、进程 容器技术的兴起源于PaaS技术(平台即服务)的普及&#xff1b;Docker公司发布的Docker项目具有里程碑式的意义&#xff1b;Docker项目通过“容器镜像”解决了应用打包这个根本性难题(CloudFoundry)。 容器本身的价值非常有限&a…

大模型还能让我们望梅止渴多久?

大模型梦碎的时间点似乎越来越近。过去一周&#xff0c;有关人工智能的消息糟糕多于积极。 周初&#xff0c;诺贝尔物理学奖和化学奖接连砸向时下正热的人工智能领域。这些奖项出人意料且鼓舞人心&#xff0c;意味着人工智能的确已经根本性地改变了我们生活和科学体系的方方面…

个人用数据挖掘笔记(待补充)

文章目录 零、复习事前准备一、绪论期末主观题 二、数据仓库&OLAP理论数据仓库数据仓库多维建模概念分层&#xff08;把底层概念映射到更高层、更一般的概念&#xff09;维度分层数值分层 建模方式数据立方体组成星形模型&#xff08;Star schema&#xff09;雪花模型&…

2024 kali系统2024版本,可视化界面汉化教程(需要命令行更改),英文版切换为中文版,基于Debian创建的kali虚拟机

我的界面如下所示 1. 安装 locales sudo apt install locales 2. 生成中文语言环境 sudo locale-gen zh_CN.UTF-8 如果你希望安装繁体中文&#xff0c;可以加入&#xff1a; sudo locale-gen zh_TW.UTF-8 3. 修改 /etc/default/locale 文件 确保有以下内容 LANGzh_CN.UT…

关于学生宿舍一进五出智能模块电表的功能介绍

一进五出学生宿舍智能模块电表石家庄光大远通电气有限公司精心打造的五进一出宿舍智能模块电表&#xff0c;正以其卓越之姿&#xff0c;引领着校园生活的智能化变革。这不仅仅是一块电表&#xff0c;它是智慧校园的守护者&#xff0c;专为新建或焕新的学生公寓量身定制&#xf…