xref: /rk3399_rockchip-uboot/fs/ext4/ext4_sparse.c (revision 2433663c664fe89961507844c5545fc1fcd9307a)
1*2433663cSJoseph Chen /*
2*2433663cSJoseph Chen  * (C) Copyright 2022 Rockchip Electronics Co., Ltd
3*2433663cSJoseph Chen  *
4*2433663cSJoseph Chen  * SPDX-License-Identifier:     GPL-2.0+
5*2433663cSJoseph Chen  */
6*2433663cSJoseph Chen 
7*2433663cSJoseph Chen #include <common.h>
8*2433663cSJoseph Chen #include <blk.h>
9*2433663cSJoseph Chen #include <memalign.h>
10*2433663cSJoseph Chen #include <image-sparse.h>
11*2433663cSJoseph Chen #include <u-boot/sha256.h>
12*2433663cSJoseph Chen 
13*2433663cSJoseph Chen /******************************************************************************/
14*2433663cSJoseph Chen #define PER_BLK_WRITE_SIZE	SZ_8M	  /* Avoid -ENOMEM, eg: bounce buffer */
15*2433663cSJoseph Chen #define DEBUG_SPARSE
16*2433663cSJoseph Chen 
17*2433663cSJoseph Chen /******************************************************************************/
print_header_info(sparse_header_t * header)18*2433663cSJoseph Chen static void print_header_info(sparse_header_t *header)
19*2433663cSJoseph Chen {
20*2433663cSJoseph Chen #ifdef DEBUG_SPARSE
21*2433663cSJoseph Chen 	printf("  ==== sparse header ===\n");
22*2433663cSJoseph Chen 	printf("  magic:          0x%x\n", header->magic);
23*2433663cSJoseph Chen 	printf("  major_version:  0x%x\n", header->major_version);
24*2433663cSJoseph Chen 	printf("  minor_version:  0x%x\n", header->minor_version);
25*2433663cSJoseph Chen 	printf("  file_hdr_sz:    %d\n", header->file_hdr_sz);
26*2433663cSJoseph Chen 	printf("  chunk_hdr_sz:   %d\n", header->chunk_hdr_sz);
27*2433663cSJoseph Chen 	printf("  blk_sz:         %d\n", header->blk_sz);
28*2433663cSJoseph Chen 	printf("  total_blks:     %d\n", header->total_blks);
29*2433663cSJoseph Chen 	printf("  total_chunks:   %d\n", header->total_chunks);
30*2433663cSJoseph Chen 	printf("  image_checksum: %d\n\n", header->image_checksum);
31*2433663cSJoseph Chen #endif
32*2433663cSJoseph Chen }
33*2433663cSJoseph Chen 
print_chunk_info(chunk_header_t * chunk,u32 id,const u8 * ptr,sparse_header_t * header)34*2433663cSJoseph Chen static void print_chunk_info(chunk_header_t *chunk, u32 id,
35*2433663cSJoseph Chen 			     const u8 *ptr, sparse_header_t *header)
36*2433663cSJoseph Chen {
37*2433663cSJoseph Chen #ifdef DEBUG_SPARSE
38*2433663cSJoseph Chen 	const char *type;
39*2433663cSJoseph Chen 
40*2433663cSJoseph Chen 	if (chunk->chunk_type == CHUNK_TYPE_RAW)
41*2433663cSJoseph Chen 		type = "RAW";
42*2433663cSJoseph Chen 	else if (chunk->chunk_type == CHUNK_TYPE_DONT_CARE)
43*2433663cSJoseph Chen 		type = "DONT CARE";
44*2433663cSJoseph Chen 	else if (chunk->chunk_type == CHUNK_TYPE_FILL)
45*2433663cSJoseph Chen 		type = "FILL";
46*2433663cSJoseph Chen 	else if (chunk->chunk_type == CHUNK_TYPE_CRC32)
47*2433663cSJoseph Chen 		type = "CRC32";
48*2433663cSJoseph Chen 	else
49*2433663cSJoseph Chen 		type = "UNK";
50*2433663cSJoseph Chen 
51*2433663cSJoseph Chen 	printf("  === [chunk.%d]\n", id + 1);
52*2433663cSJoseph Chen 	printf("  chunk_type: %s\n", type);
53*2433663cSJoseph Chen 	printf("  chunk_sz:   %d\n", chunk->chunk_sz);
54*2433663cSJoseph Chen 	printf("  total_sz:   %d\n", chunk->total_sz);
55*2433663cSJoseph Chen 	printf("  offset:     %ld\n", (ulong)ptr - (ulong)header);
56*2433663cSJoseph Chen 	printf("  buf:        0x%08lx\n", (ulong)ptr);
57*2433663cSJoseph Chen #endif
58*2433663cSJoseph Chen }
59*2433663cSJoseph Chen 
flash_write_data(struct blk_desc * desc,const u8 * data,ulong offset,ulong blocks)60*2433663cSJoseph Chen static int flash_write_data(struct blk_desc *desc, const u8 *data,
61*2433663cSJoseph Chen 			    ulong offset, ulong blocks)
62*2433663cSJoseph Chen {	const u8 *buf = data;
63*2433663cSJoseph Chen 	u32 step = BLOCK_CNT(PER_BLK_WRITE_SIZE, desc);
64*2433663cSJoseph Chen 	long left = blocks;	/* signed long ! */
65*2433663cSJoseph Chen 	ulong lba = offset;
66*2433663cSJoseph Chen 	ulong blks;
67*2433663cSJoseph Chen 
68*2433663cSJoseph Chen #ifdef DEBUG_SPARSE
69*2433663cSJoseph Chen 	printf("  lba:        0x%08lx - 0x%08lx\n", lba, lba + blocks);
70*2433663cSJoseph Chen #ifdef CONFIG_SHA256
71*2433663cSJoseph Chen 	u8 hash[32];
72*2433663cSJoseph Chen 	int i;
73*2433663cSJoseph Chen 
74*2433663cSJoseph Chen 	sha256_csum(data, blocks * desc->blksz, hash);
75*2433663cSJoseph Chen 	printf("  sha256sum:  ");
76*2433663cSJoseph Chen 	for (i = 0; i < ARRAY_SIZE(hash); i++)
77*2433663cSJoseph Chen 		printf("%02x", hash[i]);
78*2433663cSJoseph Chen 	printf("\n");
79*2433663cSJoseph Chen #endif
80*2433663cSJoseph Chen #endif
81*2433663cSJoseph Chen 	while (left > 0) {
82*2433663cSJoseph Chen 		if (left < step)
83*2433663cSJoseph Chen 			blks = left;
84*2433663cSJoseph Chen 		else
85*2433663cSJoseph Chen 			blks = step;
86*2433663cSJoseph Chen 
87*2433663cSJoseph Chen 		if (blks != blk_dwrite(desc, lba, blks, buf)) {
88*2433663cSJoseph Chen 			printf("Raw data: LBA 0x%lx written error.\n", lba);
89*2433663cSJoseph Chen 			return -EIO;
90*2433663cSJoseph Chen 		}
91*2433663cSJoseph Chen 		buf += blks * desc->blksz;
92*2433663cSJoseph Chen 		lba += blks;
93*2433663cSJoseph Chen 		left -= step;
94*2433663cSJoseph Chen 	}
95*2433663cSJoseph Chen 
96*2433663cSJoseph Chen 	return 0;
97*2433663cSJoseph Chen }
98*2433663cSJoseph Chen 
flash_fill_data(struct blk_desc * desc,ulong offset,ulong blocks,u32 fill_val)99*2433663cSJoseph Chen static int flash_fill_data(struct blk_desc *desc, ulong offset, ulong blocks,
100*2433663cSJoseph Chen 			   u32 fill_val)
101*2433663cSJoseph Chen {
102*2433663cSJoseph Chen 	u32 step = BLOCK_CNT(PER_BLK_WRITE_SIZE, desc);
103*2433663cSJoseph Chen 	long left = blocks;	/* signed long ! */
104*2433663cSJoseph Chen 	ulong lba = offset;
105*2433663cSJoseph Chen 	ulong blks;
106*2433663cSJoseph Chen 	char *buf;
107*2433663cSJoseph Chen 
108*2433663cSJoseph Chen 	buf = malloc(PER_BLK_WRITE_SIZE);
109*2433663cSJoseph Chen 	if (!buf) {
110*2433663cSJoseph Chen 		printf("No memory\n");
111*2433663cSJoseph Chen 		return -ENOMEM;
112*2433663cSJoseph Chen 	}
113*2433663cSJoseph Chen #ifdef DEBUG_SPARSE
114*2433663cSJoseph Chen 	printf("  lba:        0x%08lx - 0x%08lx\n", lba, lba + blocks);
115*2433663cSJoseph Chen 	printf("  fill:       0x%08x\n", fill_val);
116*2433663cSJoseph Chen #endif
117*2433663cSJoseph Chen 	memset((char *)buf, fill_val, PER_BLK_WRITE_SIZE);
118*2433663cSJoseph Chen 	while (left > 0) {
119*2433663cSJoseph Chen 		if (left < step)
120*2433663cSJoseph Chen 			blks = left;
121*2433663cSJoseph Chen 		else
122*2433663cSJoseph Chen 			blks = step;
123*2433663cSJoseph Chen 
124*2433663cSJoseph Chen 		if (blks != blk_dwrite(desc, lba, blks, buf)) {
125*2433663cSJoseph Chen 			printf("Fill data: LBA 0x%lx write error.\n", lba);
126*2433663cSJoseph Chen 			free(buf);
127*2433663cSJoseph Chen 			return -EIO;
128*2433663cSJoseph Chen 		}
129*2433663cSJoseph Chen 		lba += blks;
130*2433663cSJoseph Chen 		left -= step;
131*2433663cSJoseph Chen 	}
132*2433663cSJoseph Chen 	free(buf);
133*2433663cSJoseph Chen 
134*2433663cSJoseph Chen 	return 0;
135*2433663cSJoseph Chen }
136*2433663cSJoseph Chen 
137*2433663cSJoseph Chen /******************************************************************************/
ext4_unsparse(struct blk_desc * desc,const u8 * buf,ulong start)138*2433663cSJoseph Chen int ext4_unsparse(struct blk_desc *desc, const u8 *buf, ulong start)
139*2433663cSJoseph Chen {
140*2433663cSJoseph Chen 	sparse_header_t *header = (sparse_header_t *)buf;
141*2433663cSJoseph Chen 	chunk_header_t *chunk = NULL;
142*2433663cSJoseph Chen 	ulong blk = start;
143*2433663cSJoseph Chen 	ulong chunk_len;
144*2433663cSJoseph Chen 	u64 img_size;
145*2433663cSJoseph Chen 	u32 i, fill;
146*2433663cSJoseph Chen 
147*2433663cSJoseph Chen 	putc('\n');
148*2433663cSJoseph Chen 
149*2433663cSJoseph Chen 	if (!is_sparse_image(header)) {
150*2433663cSJoseph Chen 		printf("Invalid sparse format.\n");
151*2433663cSJoseph Chen 		return -EINVAL;
152*2433663cSJoseph Chen 	}
153*2433663cSJoseph Chen 
154*2433663cSJoseph Chen 	print_header_info(header);
155*2433663cSJoseph Chen 
156*2433663cSJoseph Chen 	/* check fs img's real size is larger than partition size */
157*2433663cSJoseph Chen 	img_size = (u64)(header->total_blks * header->blk_sz);
158*2433663cSJoseph Chen 
159*2433663cSJoseph Chen 	/* erase area: ensure DONT-CARE is 0 and FILL(0x0) is 0 */
160*2433663cSJoseph Chen 	if (blk_derase(desc, start, BLOCK_CNT(img_size, desc)) !=
161*2433663cSJoseph Chen 			BLOCK_CNT(img_size, desc))
162*2433663cSJoseph Chen 		return -EIO;
163*2433663cSJoseph Chen 
164*2433663cSJoseph Chen 	printf("Erase 0x%08lx - 0x%08lx blocks OK.\n\n",
165*2433663cSJoseph Chen 	       start, start + (ulong)BLOCK_CNT(img_size, desc));
166*2433663cSJoseph Chen 
167*2433663cSJoseph Chen 	/* skip the sparse header,to visit first chunk */
168*2433663cSJoseph Chen 	buf += header->file_hdr_sz;
169*2433663cSJoseph Chen 
170*2433663cSJoseph Chen 	/* to visit each chunk */
171*2433663cSJoseph Chen 	for (i = 0; i < header->total_chunks; i++) {
172*2433663cSJoseph Chen 		/* here the chunk_header */
173*2433663cSJoseph Chen 		chunk = (chunk_header_t *)buf;
174*2433663cSJoseph Chen 
175*2433663cSJoseph Chen 		/* go to next chunk's data */
176*2433663cSJoseph Chen 		buf += header->chunk_hdr_sz;
177*2433663cSJoseph Chen 
178*2433663cSJoseph Chen 		switch (chunk->chunk_type) {
179*2433663cSJoseph Chen 		case CHUNK_TYPE_RAW:
180*2433663cSJoseph Chen 			print_chunk_info(chunk, i, buf, header);
181*2433663cSJoseph Chen 
182*2433663cSJoseph Chen 			/* to calculate the length of each chunk */
183*2433663cSJoseph Chen 			chunk_len = chunk->chunk_sz * header->blk_sz;
184*2433663cSJoseph Chen 
185*2433663cSJoseph Chen 			/* verify every chunk to asure it is valid */
186*2433663cSJoseph Chen 			if (chunk->total_sz
187*2433663cSJoseph Chen 			    != (chunk_len + header->chunk_hdr_sz)) {
188*2433663cSJoseph Chen 				printf("No.%d chunk size error.\n", i + 1);
189*2433663cSJoseph Chen 				return -EINVAL;
190*2433663cSJoseph Chen 			}
191*2433663cSJoseph Chen 
192*2433663cSJoseph Chen 			if (flash_write_data(desc, buf,
193*2433663cSJoseph Chen 					     blk, BLOCK_CNT(chunk_len, desc)))
194*2433663cSJoseph Chen 				return -EIO;
195*2433663cSJoseph Chen 
196*2433663cSJoseph Chen 			buf += chunk_len;
197*2433663cSJoseph Chen 			blk += BLOCK_CNT(chunk_len, desc);
198*2433663cSJoseph Chen 			break;
199*2433663cSJoseph Chen 		case CHUNK_TYPE_DONT_CARE:
200*2433663cSJoseph Chen 			print_chunk_info(chunk, i, buf, header);
201*2433663cSJoseph Chen 
202*2433663cSJoseph Chen 			if (chunk->total_sz != header->chunk_hdr_sz) {
203*2433663cSJoseph Chen 				printf("No.%d chunk size error.\n", i + 1);
204*2433663cSJoseph Chen 				return -EINVAL;
205*2433663cSJoseph Chen 			}
206*2433663cSJoseph Chen 
207*2433663cSJoseph Chen 			chunk_len = chunk->chunk_sz * header->blk_sz;
208*2433663cSJoseph Chen 			blk += BLOCK_CNT(chunk_len, desc);
209*2433663cSJoseph Chen 			break;
210*2433663cSJoseph Chen 		case CHUNK_TYPE_FILL:
211*2433663cSJoseph Chen 			print_chunk_info(chunk, i, buf, header);
212*2433663cSJoseph Chen 
213*2433663cSJoseph Chen 			/* verify every chunk to asure it is valid */
214*2433663cSJoseph Chen 			if (chunk->total_sz - header->chunk_hdr_sz != 4) {
215*2433663cSJoseph Chen 				printf("No.%d chunk size error.\n", i);
216*2433663cSJoseph Chen 				return -EINVAL;
217*2433663cSJoseph Chen 			}
218*2433663cSJoseph Chen 
219*2433663cSJoseph Chen 			/* to calculate the length of each chunk */
220*2433663cSJoseph Chen 			chunk_len = chunk->chunk_sz * header->blk_sz;
221*2433663cSJoseph Chen 
222*2433663cSJoseph Chen 			/* ignore fill value "0", as we have erased yet */
223*2433663cSJoseph Chen 			fill = *(u32 *)buf;
224*2433663cSJoseph Chen 			if (fill != 0) {
225*2433663cSJoseph Chen 				if (flash_fill_data(desc, blk,
226*2433663cSJoseph Chen 					BLOCK_CNT(chunk_len, desc), fill))
227*2433663cSJoseph Chen 					return -EIO;
228*2433663cSJoseph Chen 			}
229*2433663cSJoseph Chen 			buf += 4;
230*2433663cSJoseph Chen 			blk += BLOCK_CNT(chunk_len, desc);
231*2433663cSJoseph Chen 			break;
232*2433663cSJoseph Chen 		case CHUNK_TYPE_CRC32:
233*2433663cSJoseph Chen 			print_chunk_info(chunk, i, buf, header);
234*2433663cSJoseph Chen 			printf("No.%d chunk type CRC32, Cannot handle!\n", i + 1);
235*2433663cSJoseph Chen 			return -ENOTSUPP;
236*2433663cSJoseph Chen 		default:
237*2433663cSJoseph Chen 			printf("sparse: unknown chunk type %04x.\n", chunk->chunk_type);
238*2433663cSJoseph Chen 			return -ENOTSUPP;
239*2433663cSJoseph Chen 		}
240*2433663cSJoseph Chen 	}
241*2433663cSJoseph Chen 
242*2433663cSJoseph Chen 	printf("\nUnsparsed is %lld MiB and 0x%08lx - 0x%08lx blocks written OK.\n",
243*2433663cSJoseph Chen 	       img_size >> 20, start, blk);
244*2433663cSJoseph Chen 
245*2433663cSJoseph Chen 	return 0;
246*2433663cSJoseph Chen }
247*2433663cSJoseph Chen 
248