gk7205v200-uboot/fs/ext4/unsparse.c

361 lines
9.2 KiB
C
Raw Normal View History

2025-08-07 17:13:54 +08:00
/*
* Copyright (c) Hunan Goke,Chengdu Goke,Shandong Goke. 2021. All rights reserved.
*/
#include <common.h>
#include <command.h>
#include <div64.h>
#include <mmc.h>
#include <image-sparse.h>
/******************************************************************************/
#ifdef CONFIG_CMD_UFS
#include <ufs.h>
#define UFS_BLKSIZE_SHIFT (12)
#endif
#define EMMC_BLKSIZE_SHIFT (9)
#define SZ_1M_SHIFT (20)
/* #define DEBUG_EXT4 */
/******************************************************************************/
void print_header_info(sparse_header_t *header)
{
#ifdef DEBUG_EXT4
printf("sparse header info:\n");
printf("magic: 0x%x\n", header->magic);
printf("major_version: 0x%x\n", header->major_version);
printf("minor_version: 0x%x\n", header->minor_version);
printf("file_hdr_sz: %d\n", header->file_hdr_sz);
printf("chunk_hdr_sz: %d\n", header->chunk_hdr_sz);
printf("blk_sz: %d\n", header->blk_sz);
printf("total_blks: %d\n", header->total_blks);
printf("total_chunks: %d\n", header->total_chunks);
printf("image_checksum: %d\n", header->image_checksum);
#endif
}
/******************************************************************************/
void print_chunk_info(chunk_header_t *chunk)
{
#ifdef DEBUG_EXT4
printf("chunk header info:\n");
printf("chunk_type: 0x%x\n", chunk->chunk_type);
printf("chunk_sz: 0x%d\n", chunk->chunk_sz);
printf("total_sz: 0x%d\n", chunk->total_sz);
#endif
}
int get_unspare_header_info(const u8 *pbuf,sparse_header_t *sparse_header)
{
sparse_header_t *header = NULL;
header = memcpy(sparse_header, pbuf,sizeof(sparse_header_t));
if (!is_sparse_image(sparse_header)) {
printf("Invalid sparse format.\n");
return 1;
}
print_header_info(header);
return 0;
}
/******************************************************************************/
int ext4_unsparse(struct mmc *mmc, u32 dev, u8 *pbuf, u32 blk, u32 cnt)
{
u32 i, num;
u64 dense_len = 0;
u64 sparse_len = 0;
u64 img_size;
u32 chunk_len;
s32 percent_complete = -1;
chunk_header_t *chunk = NULL;
sparse_header_t *header = (sparse_header_t *)pbuf;
if (!is_sparse_image(header)) {
printf("Invalid sparse format.\n");
return 1;
}
/* check header->blk_sz align to emmc block size */
if (header->blk_sz & ((1 << EMMC_BLKSIZE_SHIFT) - 1)) {
printf("image blk size %d is not aligned to 512Byte.\n",
header->blk_sz);
return 1;
}
/* check fs img's real size is larger than partition size */
img_size = (u64)(header->total_blks * header->blk_sz);
if ((img_size >> EMMC_BLKSIZE_SHIFT) > (u64)cnt) {
printf("partition size %d MB not enough.\n",
(int)(cnt >> (SZ_1M_SHIFT - EMMC_BLKSIZE_SHIFT)));
print_header_info(header);
return 1;
}
/* skip the sparse header,to visit first chunk */
pbuf += header->file_hdr_sz;
/* to visit each chunk */
for (i = 0; i < header->total_chunks; i++) {
/* here the chunk_header */
chunk = (chunk_header_t *)pbuf;
/* go to next chunk's data */
pbuf += header->chunk_hdr_sz;
switch (chunk->chunk_type) {
case CHUNK_TYPE_RAW:
/* to calculate the length of each chunk */
chunk_len = chunk->chunk_sz * header->blk_sz;
/* verify every chunk to asure it is valid */
if (chunk->total_sz
!= (chunk_len + header->chunk_hdr_sz)) {
printf("No.%d chunk size error.\n", i);
print_chunk_info(chunk);
return 1;
}
dense_len += chunk_len;
sparse_len += chunk_len;
if (sparse_len > ((u64)cnt << EMMC_BLKSIZE_SHIFT)) {
printf("error: sparse size %d MB is "
"larger than partiton size %d MB.\n",
(int)(sparse_len >> SZ_1M_SHIFT),
(int)(cnt >> (SZ_1M_SHIFT
- EMMC_BLKSIZE_SHIFT)));
print_chunk_info(chunk);
return 1;
}
num = blk_dwrite(mmc_get_blk_desc(mmc), blk,
(chunk_len >> EMMC_BLKSIZE_SHIFT), pbuf);
if (num != (chunk_len >> EMMC_BLKSIZE_SHIFT)) {
printf("Raw data: No.%d blocks written: %s.\n",
num, "ERROR");
return 1;
}
pbuf += chunk_len;
blk += (chunk_len >> EMMC_BLKSIZE_SHIFT);
break;
case CHUNK_TYPE_DONT_CARE:
if (chunk->total_sz != header->chunk_hdr_sz) {
printf("No.%d chunk size error.\n", i);
print_chunk_info(chunk);
return 1;
}
chunk_len = chunk->chunk_sz * header->blk_sz;
sparse_len += chunk_len;
if (sparse_len > ((u64)cnt << EMMC_BLKSIZE_SHIFT)) {
printf("error: sparse size %d MB is "
"larger than partiton size %d MB.\n",
(int)(sparse_len >> SZ_1M_SHIFT),
(int)(cnt >> (SZ_1M_SHIFT
- EMMC_BLKSIZE_SHIFT)));
print_chunk_info(chunk);
return 1;
}
blk += (chunk_len >> EMMC_BLKSIZE_SHIFT);
break;
default:
printf("sparse: unknow chunk type %04x.\n",
chunk->chunk_type);
return 1;
}
{
u64 n = (u64)(sparse_len >> EMMC_BLKSIZE_SHIFT) * 100;
int percent;
do_div(n, cnt);
percent = (int)n;
if (percent != percent_complete) {
percent_complete = percent;
printf("\rWriting at %d blk# "
"-- %3d%% complete.",
(int)(blk
+ (sparse_len >> EMMC_BLKSIZE_SHIFT)),
percent);
}
}
}
puts("\n");
if (((u64)cnt << EMMC_BLKSIZE_SHIFT) != sparse_len) {
printf("error: partition size %d MB != ext4 image size %d MB\n",
(int)(cnt >> (SZ_1M_SHIFT - EMMC_BLKSIZE_SHIFT)),
(int)(sparse_len >> SZ_1M_SHIFT));
return 1;
}
printf("sparse: %d MB / %d MB.\n", (int)(dense_len >> SZ_1M_SHIFT),
(int)(sparse_len >> SZ_1M_SHIFT));
return 0;
}
#ifdef CONFIG_CMD_UFS
/******************************************************************************/
int ufs_ext4_unsparse(const u8 *pbuf, u32 blk, u32 cnt)
{
u32 i, num;
u64 dense_len = 0;
u64 sparse_len = 0;
u64 img_size;
u32 chunk_len;
s32 percent_complete = -1;
chunk_header_t *chunk = NULL;
sparse_header_t *header = (sparse_header_t *)pbuf;
if (!is_sparse_image(header)) {
printf("Invalid sparse format.\n");
return 1;
}
/* check header->blk_sz align to emmc block size */
if (header->blk_sz & ((1 << UFS_BLKSIZE_SHIFT) - 1)) {
printf("image blk size %d is not aligned to 512Byte.\n",
header->blk_sz);
return 1;
}
/* check fs img's real size is larger than partition size */
img_size = (u64)(header->total_blks * header->blk_sz);
if ((img_size >> UFS_BLKSIZE_SHIFT) > (u64)cnt) {
printf("partition size %d MB not enough.\n",
(int)(cnt >> (SZ_1M_SHIFT - UFS_BLKSIZE_SHIFT)));
print_header_info(header);
return 1;
}
/* skip the sparse header,to visit first chunk */
pbuf += header->file_hdr_sz;
/* to visit each chunk */
for (i = 0; i < header->total_chunks; i++) {
/* here the chunk_header */
chunk = (chunk_header_t *)pbuf;
/* go to next chunk's data */
pbuf += header->chunk_hdr_sz;
switch (chunk->chunk_type) {
case CHUNK_TYPE_RAW:
/* to calculate the length of each chunk */
chunk_len = chunk->chunk_sz * header->blk_sz;
/* verify every chunk to asure it is valid */
if (chunk->total_sz
!= (chunk_len + header->chunk_hdr_sz)) {
printf("No.%d chunk size error.\n", i);
print_chunk_info(chunk);
return 1;
}
dense_len += chunk_len;
sparse_len += chunk_len;
if (sparse_len > ((u64)cnt << UFS_BLKSIZE_SHIFT)) {
printf("error: sparse size %d MB is "
"larger than partiton size %d MB.\n",
(int)(sparse_len >> SZ_1M_SHIFT),
(int)(cnt >> (SZ_1M_SHIFT
- UFS_BLKSIZE_SHIFT)));
print_chunk_info(chunk);
return 1;
}
if (ufs_write_storage((uint64_t)pbuf,
(blk << UFS_BLKSIZE_SHIFT),
chunk_len) == 0)
num = chunk_len >> UFS_BLKSIZE_SHIFT;
else
num = 0;
if (num != (chunk_len >> UFS_BLKSIZE_SHIFT)) {
printf("Raw data: No.%d blocks written: %s.\n",
num, "ERROR");
return 1;
}
pbuf += chunk_len;
blk += (chunk_len >> UFS_BLKSIZE_SHIFT);
break;
case CHUNK_TYPE_DONT_CARE:
if (chunk->total_sz != header->chunk_hdr_sz) {
printf("No.%d chunk size error.\n", i);
print_chunk_info(chunk);
return 1;
}
chunk_len = chunk->chunk_sz * header->blk_sz;
sparse_len += chunk_len;
if (sparse_len > ((u64)cnt << UFS_BLKSIZE_SHIFT)) {
printf("error: sparse size %d MB is "
"larger than partiton size %d MB.\n",
(int)(sparse_len >> SZ_1M_SHIFT),
(int)(cnt >> (SZ_1M_SHIFT
- UFS_BLKSIZE_SHIFT)));
print_chunk_info(chunk);
return 1;
}
blk += (chunk_len >> UFS_BLKSIZE_SHIFT);
break;
default:
printf("sparse: unknown chunk type %04x.\n",
chunk->chunk_type);
return 1;
}
{
u64 n = (u64)(sparse_len >> UFS_BLKSIZE_SHIFT) * 100;
int percent;
do_div(n, cnt);
percent = (int)n;
if (percent != percent_complete) {
percent_complete = percent;
printf("\rWriting at %d blk# "
"-- %3d%% complete.",
(int)(blk
+ (sparse_len >> UFS_BLKSIZE_SHIFT)),
percent);
}
}
}
puts("\n");
if (((u64)cnt << UFS_BLKSIZE_SHIFT) != sparse_len) {
printf("error: partition size %d MB != ext4 image size %d MB\n",
(int)(cnt >> (SZ_1M_SHIFT - UFS_BLKSIZE_SHIFT)),
(int)(sparse_len >> SZ_1M_SHIFT));
return 1;
}
printf("sparse: %d MB / %d MB.\n", (int)(dense_len >> SZ_1M_SHIFT),
(int)(sparse_len >> SZ_1M_SHIFT));
return 0;
}
#endif