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