0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

稀疏镜像在 OpenHarmony 上的探索

电子发烧友开源社区 来源:未知 2023-01-06 08:30 次阅读
一、稀疏镜像升级背景
常用系统镜像格式为原始镜像,即RAW格式。镜像体积比较大,在烧录固件或者升级固件时比较耗时,而且在移动设备升级过程时比较耗费流量。为此,将原始镜像用稀疏描述,可以大大的缩减镜像体积,省时省流量。

二、稀疏镜像原理
1. 稀疏镜像概念
  • 原始镜像:即raw image,完整的ext4分区镜像,包含很多全零的无效填充区
  • 稀疏镜像:即sparse image,将raw ext4进行稀疏描述,因此尺寸比较小,制作目录有多少文件就计算多少,没有全零填充

2. 稀疏镜像格式

稀疏镜像数据格式:首先是sparse_header占用28byte,然后是12byte的chunk_header,同样这chunk_header的类型决定了后面跟着的数据,如果读到数据是0xCAC1意味着后面是本身的raw_data,如果是0xCAC3,则后面num为0,接着再0xCAC2意味着后面填充4byte的内容。

三、实现稀疏镜像升级方案
1. 稀疏镜像烧录
生成稀疏格式镜像,有2种方法可以生成稀疏镜像:
修改文件build/ohos_var.gni中,sparse_image=true

编译命令增加--sparse-image字段,
如./build.sh --product-name=xxx --sparse-image

增加稀疏格式转换工具
在目录build/ohos/images/mkimage中增加文件img2simg,该工具用于编译完成后将raw镜像转换为sparse格式,并设置权限为777。

编译后的镜像对比

编译出的镜像格式为sparse格式,镜像大小相比raw格式明显变小。烧录稀疏镜像方法和烧录原始镜像方法一致。稀疏镜像本身是不能直接挂载的,在烧录过程中通过uboot将稀疏格式镜,还原为原始镜像,然后写到磁盘中,系统启动后可挂载对应的镜像。

2. 稀疏镜像OTA升级
OTA升级的升级包采用稀疏镜像制作。

(1)修改升级包制作工具
官方升级包工具不支持生成稀疏镜像的升级包,修改升级包工具,生成稀疏格式的升级包。
.aseupdatepackaging_toolsimage_class.py
按照上图所示注释代码

(2)生成稀疏镜像升级包
和全量镜像升级包制作方法一致。

(3)适配updater组件中稀疏镜像功能
增加写稀疏镜像分支
.aseupdateupdaterservicesapplypatchdata_writer.cpp
写数据函数CreateDataWriter增加写稀疏镜像分支
case WRITE_SPARSE:{std::make_unique(partitionName));return std::move(writer);}
增加稀疏镜像类声明
.aseupdateupdaterservicesapplypatch
aw_writer.h
增加稀疏镜像类声明及相关变量定义
typedef struct sparse_header {  uint32_t  magic;      /* 0xed26ff3a */  uint16_t  major_version;  /* (0x1) - reject images with higher major versions */  uint16_t  minor_version;  /* (0x0) - allow images with higer minor versions */  uint16_t  file_hdr_sz;    /* 28 bytes for first revision of the file format */  uint16_t  chunk_hdr_sz;   /* 12 bytes for first revision of the file format */  uint32_t  blk_sz;     /* block size in bytes, must be a multiple of 4 (4096) */  uint32_t  total_blks; /* total blocks in the non-sparse output image */  uint32_t  total_chunks;   /* total chunks in the sparse input image */  uint32_t  image_checksum; /* CRC32 checksum of the original data, counting "don't care" *//* as 0. Standard 802.3 polynomial, use a Public Domain *//* table implementation */} sparse_header_t;#define SPARSE_HEADER_MAGIC 0xed26ff3a#define CHUNK_TYPE_RAW      0xCAC1#define CHUNK_TYPE_FILL     0xCAC2#define CHUNK_TYPE_DONT_CARE    0xCAC3#define CHUNK_TYPE_CRC32    0xCAC4typedef struct chunk_header {  uint16_t  chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */  uint16_t  reserved1;  uint32_t  chunk_sz;   /* in blocks in output image */  uint32_t  total_sz;   /* in bytes of chunk input file including chunk header and data */} chunk_header_t;class SparseWriter : public DataWriter {public:    virtual bool Write(const uint8_t *addr, size_t len, WriteMode mode, const std::string &partitionName);    explicit SparseWriter(const std::string partitionName) : offset_(0), fd_(-1), partitionName_(partitionName) {}    virtual ~SparseWriter()    {        offset_ = 0;if (fd_ > 0) {            fsync(fd_);close(fd_);        }        fd_ = -1;    }private:int WriteInternal(int fd, const uint8_t *data, size_t len, const std::string &partitionName);    SparseWriter(const SparseWriter&) = delete;const SparseWriter& operator=(const SparseWriter&) = delete;    off64_t offset_;int fd_;    std::string partitionName_;};
增加稀疏镜像类实现
.aseupdateupdaterservicesapplypatch
aw_writer.cpp
增加稀疏镜像类实现及相关变量定义,原有代码不变
bool SparseWriter::Write(const uint8_t *addr, size_t len, WriteMode mode, const std::string &partitionName)
{
if (addr == nullptr) {
LOG(ERROR) << "SparseWriter: invalid address.";
return false;
}
if (len == 0) {
LOG(INFO) << "SparseWriter: write length is 0, skip.";
return false;
}
if (fd_ < 0) {
fd_ = OpenPartition(partitionName_);
if (fd_ < 0) {
return false;
}
}




UPDATER_CHECK_ONLY_RETURN(WriteInternal(fd_, addr, len, partitionName_) >= 0, return false);
return true;
}








int SparseWriter::WriteInternal(int fd, const uint8_t *data, size_t len, const std::string &partitionName)
{
uint32_t written = 0;
sparse_header_t *sparse_header;
chunk_header_t *chunk_header;
unsigned int chunk;
void *membuf = NULL;
uint32_t *fill_buf = NULL;
uint32_t fill_val;
uint32_t bytes_written = 0;
uint32_t total_bytes = 0;
uint32_t blk = 0;
uint32_t chunk_data_sz = 0;
uint32_t blkcnt = 0;
uint32_t blks = 0;
uint32_t total_blocks = 0;
uint32_t addr_offset = 0;
uint32_t fill_buf_num_blks = 0;








uint32_t block_size = 4096;
uint32_t block_count = 524288;
uint32_t i;
uint32_t j;
int ret = lseek64(fd, offset_, SEEK_SET);
UPDATER_FILE_CHECK(ret != -1, "RawWriter: failed to seek file to " << offset_, return -1);
fill_buf_num_blks = CONFIG_FASTBOOT_FLASH_FILLBUF_SIZE / block_size;
LOG(INFO) << "WriteInternal offset_ " << offset_;
/* Read and skip over sparse image header */
sparse_header = (sparse_header_t *)data;
data += sparse_header->file_hdr_sz;
if (sparse_header->file_hdr_sz > sizeof(sparse_header_t)) {
/*
* Skip the remaining bytes in a header that is longer than
* we expected.
*/
data += (sparse_header->file_hdr_sz - sizeof(sparse_header_t));
}
LOG(INFO) << "=== Sparse Image Header ===";
LOG(INFO) << "magic: " << sparse_header->magic;
LOG(INFO) << "major_version: " << sparse_header->major_version;
LOG(INFO) << "minor_version: " << sparse_header->minor_version;
LOG(INFO) << "file_hdr_sz: " << sparse_header->file_hdr_sz;
LOG(INFO) << "chunk_hdr_sz: " << sparse_header->chunk_hdr_sz;
LOG(INFO) << "blk_sz: " << sparse_header->blk_sz;
LOG(INFO) << "total_blks: " << sparse_header->total_blks;
LOG(INFO) << "total_chunks: " << sparse_header->total_chunks;








LOG(INFO) << "Flashing Sparse Image";
blk = 0;
for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) {
/* Read and skip over chunk header */
chunk_header = (chunk_header_t *)data;
data += sizeof(chunk_header_t);
if (chunk_header->chunk_type != CHUNK_TYPE_RAW)
{
LOG(INFO) << "=== Chunk Header ===";
LOG(INFO) << "chunk_type: " << chunk_header->chunk_type;
LOG(INFO) << "chunk_sz: " << chunk_header->chunk_sz;
LOG(INFO) << "total_sz: " << chunk_header->total_sz;
}
if (sparse_header->chunk_hdr_sz > sizeof(chunk_header_t)) {
/*
* Skip the remaining bytes in a header that is longer
* than we expected.
*/
data += (sparse_header->chunk_hdr_sz -
sizeof(chunk_header_t));
}
chunk_data_sz = sparse_header->blk_sz * chunk_header->chunk_sz;
blkcnt = chunk_data_sz / block_size;
switch (chunk_header->chunk_type) {
case CHUNK_TYPE_RAW:
if (chunk_header->total_sz !=
(sparse_header->chunk_hdr_sz + chunk_data_sz)) {
LOG(ERROR) << "Bogus chunk size for chunk type Raw";
return -1;
}
if (blk + blkcnt > 0 + block_count) {
LOG(ERROR) << "Request would exceed partition size!";
return -1;
}
addr_offset = blk * block_size;
ret = lseek64(fd, offset_ + addr_offset, SEEK_SET);
if (ret < 0) {
LOG(ERROR) << "failed to seek file to " << addr_offset << " error=" << strerror(errno);
return -1;
}
written = write(fd, data, blkcnt * block_size);
if (written < 0) {
LOG(ERROR) << "SparseWriter: failed to write data of len ";
return -1;
}
total_bytes = total_bytes + blkcnt * block_size;
blks = written / block_size;
blk += blks;
bytes_written += blkcnt * block_size;
total_blocks += chunk_header->chunk_sz;
data += chunk_data_sz;
break;
case CHUNK_TYPE_FILL:
if (chunk_header->total_sz !=
(sparse_header->chunk_hdr_sz + sizeof(uint32_t))) {
LOG(ERROR) << "Bogus chunk size for chunk type FILL total_sz err " << chunk_header->total_sz << " ";
return -1;
}
ret = posix_memalign (&membuf, 64,
ROUNDUP(
block_size * fill_buf_num_blks,
64));
if (ret) {
LOG(ERROR) << "posix_memalign:" << strerror (errno);
return -1;
}
fill_buf = (uint32_t *)membuf;
if (!fill_buf) {
LOG(ERROR) << "Malloc failed for: CHUNK_TYPE_FILL";
return -1;
}
fill_val = *(uint32_t *)data;
data = data + sizeof(uint32_t);
for (i = 0;
i < (block_size * fill_buf_num_blks /
sizeof(fill_val));
i++)
fill_buf[i] = fill_val;
if (blk + blkcnt > 0 + block_count) {
LOG(ERROR) << "Request would exceed partition size!";
return -1;
}
for (i = 0; i < blkcnt;) {
j = blkcnt - i;
if (j > fill_buf_num_blks)
j = fill_buf_num_blks;
addr_offset = blk * block_size;
ret = lseek64(fd, offset_ + addr_offset, SEEK_SET);
if (ret < 0) {
LOG(ERROR) << "failed to lseek file to " << addr_offset << " error=" << strerror(errno);
return -1;
}
written = write(fd, fill_buf, j * block_size);
if (written < 0) {
LOG(ERROR) << "SparseWriter: failed to write data of len ";
return -1;
}
total_bytes = total_bytes + j * block_size;
blks = written / block_size;
if (blks < j) {
LOG(ERROR) << "Write failed, block";
free(fill_buf);
return -1;
}
blk += blks;
i += j;
}
bytes_written += blkcnt * block_size;
total_blocks += chunk_data_sz / sparse_header->blk_sz;
free(fill_buf);
break;
case CHUNK_TYPE_DONT_CARE:
blk += blkcnt;
total_blocks += chunk_header->chunk_sz;
break;
case CHUNK_TYPE_CRC32:
if (chunk_header->total_sz !=
sparse_header->chunk_hdr_sz) {
LOG(ERROR) << "Bogus chunk size for chunk type CRC32 total_sz err " << chunk_header->total_sz;
return -1;
}
total_blocks += chunk_header->chunk_sz;
data += chunk_data_sz;
break;
default:
LOG(INFO) << __func__ << ": Unknown chunk type: " << chunk_header->chunk_type;
return -1;
}
}
LOG(INFO) << "Wrote "<< chunk <<"blocks, expected to write " << sparse_header->total_blks << "blocks ";
LOG(INFO) << "........ wrote "<< bytes_written <<"bytes to " << partitionName << " ";
LOG(INFO) << "total_bytes=" << total_bytes;
return 0;
}

本文介绍了OpenHarmony系统中实现稀疏镜像升级的方法,理解稀疏镜像原理及稀疏镜像还原方法可以快速在自己的系统中应用稀疏镜像升级,提高系统升级速度。


更多热点文章阅读
  • DevEco Studio新特性分享-跨语言调试,让调试更便捷高效
  • 基于 OpenHarmony 的智联北斗海防系统
  • 玩转OpenHarmony智能家居:如何实现树莓派“碰一碰”设备控制
  • 玩转OpenHarmony社交场景:即时通讯平台
  • HarmonyOS多媒体框架介绍


提示:本文由电子发烧友社区发布,转载请注明以上来源。如需社区合作及入群交流,请添加微信EEFans0806,或者发邮箱liuyong@huaqiu.com。


原文标题:稀疏镜像在 OpenHarmony 上的探索

文章出处:【微信公众号:电子发烧友开源社区】欢迎添加关注!文章转载请注明出处。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 电子发烧友
    +关注

    关注

    33

    文章

    552

    浏览量

    33011
  • 开源社区
    +关注

    关注

    0

    文章

    94

    浏览量

    455

原文标题:稀疏镜像在 OpenHarmony 上的探索

文章出处:【微信号:HarmonyOS_Community,微信公众号:电子发烧友开源社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    OpenHarmony通过挂载镜像来修改镜像内容,RK3566鸿蒙开发板演示

    OpenHarmony通过挂载镜像来修改镜像内容的教程,提高修改镜像内容效率!
    的头像 发表于 01-03 14:21 103次阅读
    <b class='flag-5'>OpenHarmony</b>通过挂载<b class='flag-5'>镜像</b>来修改<b class='flag-5'>镜像</b>内容,RK3566鸿蒙开发板演示

    OpenHarmony源码编译后烧录镜像教程,RK3566鸿蒙开发板演示

    本文介绍瑞芯微主板/开发板编译OpenHarmony源码后烧录镜像的教程,触觉智能Purple Pi OH鸿蒙开发板演示。搭载了瑞芯微RK3566四核处理器,树莓派卡片电脑设计,支持开源鸿蒙OpenHarmony3.2-5.0系
    的头像 发表于 12-30 10:08 91次阅读
    <b class='flag-5'>OpenHarmony</b>源码编译后烧录<b class='flag-5'>镜像</b>教程,RK3566鸿蒙开发板演示

    dayu200 rk3568 openharmony5.0 sim卡 通话服务

    想请问一下有人在dayu200 rk3568插过sim卡并且成功完成过通话服务吗? 我现在在dayu200烧录了openHarmony release 5.0.0版本的镜像,在上面
    发表于 12-26 16:52

    嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-mfgtools烧录流程介绍之烧写所需镜像

    USB OTG烧写所需镜像在:ELF 1开发板资料包\\06-常用工具\\06-4 烧写工具\\OTG烧写\\mfgtools\\Profiles\\Linux\\OS Firmware
    发表于 12-21 09:25

    飞凌嵌入式ElfBoard ELF 1板卡-mfgtools烧录流程介绍之烧写所需镜像

    USB OTG烧写所需镜像在:ELF 1开发板资料包\\06-常用工具\\06-4 烧写工具\\OTG烧写\\mfgtools\\Profiles\\Linux\\OS Firmware
    发表于 12-20 09:05

    星光璀璨,聆听 OpenHarmony 贡献者之声

    在第三届开放原子开源基金会OpenHarmony威廉希尔官方网站 大会上, 我们满怀敬意地对取得优秀成果的星光OpenHarmony威廉希尔官方网站 俱乐部、星光导师、星光贡献者、星光活动进行了致谢。 他们不仅是威廉希尔官方网站 的探索
    发表于 10-28 17:09

    [2K300适配OpenharmonyV4.1]根文件系统制作请教

    官方你好,我这边需要适配OpenharmonyV4.1,但是目前网上找不到2K300的开源资料,我尝试使用2K500开源资料搭建开发环境,目前已成功编译出对应镜像,但是我想将OpenHarmony
    发表于 09-11 11:18

    Openharmony软件评估指南-米尔瑞芯微RK3568开发板

    Openharmony软件评估指南用于介绍在米尔的开发板运行Openharmony系统下的核心资源与外设资源的测试步骤与评估方法。本文可作为前期评估指南使用,也可以作为通用系统开发的测试指导书
    发表于 09-06 20:06

    河南大学OpenHarmony威廉希尔官方网站 俱乐部正式揭牌成立

    8月30日,由OpenAtom OpenHarmony(以下简称“OpenHarmony”)项目群威廉希尔官方网站 指导委员会与河南大学共同举办的“河南大学OpenHarmony威廉希尔官方网站 俱乐部成立大会”在郑州校区友兰
    的头像 发表于 09-03 16:12 427次阅读
    河南大学<b class='flag-5'>OpenHarmony</b>威廉希尔官方网站
俱乐部正式揭牌成立

    第二届大会回顾第25期 | OpenHarmony的Python设备应用开发

    Python以其简单、易学和功能强大而闻名,有着广泛的用户群体。采用Python开发有助于降低OpenHarmony的学习门槛。如何在OpenHarmony用Python开发设备应用,有哪些关键威廉希尔官方网站 ?电
    的头像 发表于 08-27 11:53 724次阅读
    第二届大会回顾第25期 | <b class='flag-5'>OpenHarmony</b><b class='flag-5'>上</b>的Python设备应用开发

    鸿蒙OpenHarmony南向/北向快速开发教程-迅为RK3568开发板

    4.1学习之旅了吗?快来加入我们,一起探索鸿蒙4.1系统的无限魅力吧! 【北京迅为】OpenHarmony学习开发系列教程(第1期 北向基础篇一) P0_先导课 P1_OpenHarmony系统概述
    发表于 07-23 10:44

    【飞凌嵌入式OK527N-C开发板体验】- 3. 打包镜像以及烧录

    只想更新boot或者设备树等则勾选单或多分区下载,并勾选需要下载的部分。 内核镜像在BOOT分区,设备树和uboot在BOOT-RESOURCE分区 查看烧录的是不是自己编译的版本,进入系统后输入如下
    发表于 07-11 21:27

    【飞凌嵌入式OK527N-C开发板体验】-打包镜像以及烧录

    只想更新boot或者设备树等则勾选单或多分区下载,并勾选需要下载的部分。 内核镜像在BOOT分区,设备树和uboot在BOOT-RESOURCE分区 查看烧录的是不是自己编译的版本,进入系统后输入如下
    发表于 07-05 23:15

    【五】Purple Pi OH开发板带你7天入门OpenHarmony

    在完成了PurplePiOH大部分的接口测试之后,紧接着就是一个充满挑战的任务——利用SDK来编译生成我们自己的镜像文件。通过这一过程,不仅能够让你获得一个可在真实硬件运行的系统镜像,更重要的是,它让你对
    的头像 发表于 03-07 08:31 485次阅读
    【五】Purple Pi OH开发板带你7天入门<b class='flag-5'>OpenHarmony</b>

    请问如何将镜像烧写到emmc

    如何将镜像烧写到emmc
    发表于 01-10 07:47