android13/u-boot/fs/ext4/ext4_sparse.c

248 lines
6.3 KiB
C

/*
* (C) Copyright 2022 Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <blk.h>
#include <memalign.h>
#include <image-sparse.h>
#include <u-boot/sha256.h>
/******************************************************************************/
#define PER_BLK_WRITE_SIZE SZ_8M /* Avoid -ENOMEM, eg: bounce buffer */
#define DEBUG_SPARSE
/******************************************************************************/
static void print_header_info(sparse_header_t *header)
{
#ifdef DEBUG_SPARSE
printf(" ==== sparse header ===\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\n", header->image_checksum);
#endif
}
static void print_chunk_info(chunk_header_t *chunk, u32 id,
const u8 *ptr, sparse_header_t *header)
{
#ifdef DEBUG_SPARSE
const char *type;
if (chunk->chunk_type == CHUNK_TYPE_RAW)
type = "RAW";
else if (chunk->chunk_type == CHUNK_TYPE_DONT_CARE)
type = "DONT CARE";
else if (chunk->chunk_type == CHUNK_TYPE_FILL)
type = "FILL";
else if (chunk->chunk_type == CHUNK_TYPE_CRC32)
type = "CRC32";
else
type = "UNK";
printf(" === [chunk.%d]\n", id + 1);
printf(" chunk_type: %s\n", type);
printf(" chunk_sz: %d\n", chunk->chunk_sz);
printf(" total_sz: %d\n", chunk->total_sz);
printf(" offset: %ld\n", (ulong)ptr - (ulong)header);
printf(" buf: 0x%08lx\n", (ulong)ptr);
#endif
}
static int flash_write_data(struct blk_desc *desc, const u8 *data,
ulong offset, ulong blocks)
{ const u8 *buf = data;
u32 step = BLOCK_CNT(PER_BLK_WRITE_SIZE, desc);
long left = blocks; /* signed long ! */
ulong lba = offset;
ulong blks;
#ifdef DEBUG_SPARSE
printf(" lba: 0x%08lx - 0x%08lx\n", lba, lba + blocks);
#ifdef CONFIG_SHA256
u8 hash[32];
int i;
sha256_csum(data, blocks * desc->blksz, hash);
printf(" sha256sum: ");
for (i = 0; i < ARRAY_SIZE(hash); i++)
printf("%02x", hash[i]);
printf("\n");
#endif
#endif
while (left > 0) {
if (left < step)
blks = left;
else
blks = step;
if (blks != blk_dwrite(desc, lba, blks, buf)) {
printf("Raw data: LBA 0x%lx written error.\n", lba);
return -EIO;
}
buf += blks * desc->blksz;
lba += blks;
left -= step;
}
return 0;
}
static int flash_fill_data(struct blk_desc *desc, ulong offset, ulong blocks,
u32 fill_val)
{
u32 step = BLOCK_CNT(PER_BLK_WRITE_SIZE, desc);
long left = blocks; /* signed long ! */
ulong lba = offset;
ulong blks;
char *buf;
buf = malloc(PER_BLK_WRITE_SIZE);
if (!buf) {
printf("No memory\n");
return -ENOMEM;
}
#ifdef DEBUG_SPARSE
printf(" lba: 0x%08lx - 0x%08lx\n", lba, lba + blocks);
printf(" fill: 0x%08x\n", fill_val);
#endif
memset((char *)buf, fill_val, PER_BLK_WRITE_SIZE);
while (left > 0) {
if (left < step)
blks = left;
else
blks = step;
if (blks != blk_dwrite(desc, lba, blks, buf)) {
printf("Fill data: LBA 0x%lx write error.\n", lba);
free(buf);
return -EIO;
}
lba += blks;
left -= step;
}
free(buf);
return 0;
}
/******************************************************************************/
int ext4_unsparse(struct blk_desc *desc, const u8 *buf, ulong start)
{
sparse_header_t *header = (sparse_header_t *)buf;
chunk_header_t *chunk = NULL;
ulong blk = start;
ulong chunk_len;
u64 img_size;
u32 i, fill;
putc('\n');
if (!is_sparse_image(header)) {
printf("Invalid sparse format.\n");
return -EINVAL;
}
print_header_info(header);
/* check fs img's real size is larger than partition size */
img_size = (u64)(header->total_blks * header->blk_sz);
/* erase area: ensure DONT-CARE is 0 and FILL(0x0) is 0 */
if (blk_derase(desc, start, BLOCK_CNT(img_size, desc)) !=
BLOCK_CNT(img_size, desc))
return -EIO;
printf("Erase 0x%08lx - 0x%08lx blocks OK.\n\n",
start, start + (ulong)BLOCK_CNT(img_size, desc));
/* skip the sparse header,to visit first chunk */
buf += header->file_hdr_sz;
/* to visit each chunk */
for (i = 0; i < header->total_chunks; i++) {
/* here the chunk_header */
chunk = (chunk_header_t *)buf;
/* go to next chunk's data */
buf += header->chunk_hdr_sz;
switch (chunk->chunk_type) {
case CHUNK_TYPE_RAW:
print_chunk_info(chunk, i, buf, header);
/* 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 + 1);
return -EINVAL;
}
if (flash_write_data(desc, buf,
blk, BLOCK_CNT(chunk_len, desc)))
return -EIO;
buf += chunk_len;
blk += BLOCK_CNT(chunk_len, desc);
break;
case CHUNK_TYPE_DONT_CARE:
print_chunk_info(chunk, i, buf, header);
if (chunk->total_sz != header->chunk_hdr_sz) {
printf("No.%d chunk size error.\n", i + 1);
return -EINVAL;
}
chunk_len = chunk->chunk_sz * header->blk_sz;
blk += BLOCK_CNT(chunk_len, desc);
break;
case CHUNK_TYPE_FILL:
print_chunk_info(chunk, i, buf, header);
/* verify every chunk to asure it is valid */
if (chunk->total_sz - header->chunk_hdr_sz != 4) {
printf("No.%d chunk size error.\n", i);
return -EINVAL;
}
/* to calculate the length of each chunk */
chunk_len = chunk->chunk_sz * header->blk_sz;
/* ignore fill value "0", as we have erased yet */
fill = *(u32 *)buf;
if (fill != 0) {
if (flash_fill_data(desc, blk,
BLOCK_CNT(chunk_len, desc), fill))
return -EIO;
}
buf += 4;
blk += BLOCK_CNT(chunk_len, desc);
break;
case CHUNK_TYPE_CRC32:
print_chunk_info(chunk, i, buf, header);
printf("No.%d chunk type CRC32, Cannot handle!\n", i + 1);
return -ENOTSUPP;
default:
printf("sparse: unknown chunk type %04x.\n", chunk->chunk_type);
return -ENOTSUPP;
}
}
printf("\nUnsparsed is %lld MiB and 0x%08lx - 0x%08lx blocks written OK.\n",
img_size >> 20, start, blk);
return 0;
}