blob: 4830ae745c1d0ea081ff2e045e77951a2864572e [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001// SPDX-License-Identifier: MIT
2/*
3 * Copyright (c) 2009 Travis Geiselbrecht
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files
7 * (the "Software"), to deal in the Software without restriction,
8 * including without limitation the rights to use, copy, modify, merge,
9 * publish, distribute, sublicense, and/or sell copies of the Software,
10 * and to permit persons to whom the Software is furnished to do so,
11 * subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24#include <arch.h>
25#include <assert.h>
26#include <compiler.h>
27#include <debug.h>
28#include <err.h>
29#include <lib/bio.h>
30#include <lib/cksum.h>
31#include <lib/partition.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include "gpt.h"
36#if WITH_LIB_NFTL
37#include <lib/nftl.h>
38#endif
39
40struct chs {
41 uint8_t c;
42 uint8_t h;
43 uint8_t s;
44} __PACKED;
45
46struct mbr_part {
47 uint8_t status;
48 struct chs start;
49 uint8_t type;
50 struct chs end;
51 uint32_t lba_start;
52 uint32_t lba_length;
53} __PACKED;
54
55struct gpt_header {
56 uint64_t first_usable_lba;
57 uint64_t backup_header_lba;
58 uint32_t partition_entry_size;
59 uint32_t header_size;
60 uint32_t max_partition_count;
61};
62
63static status_t validate_mbr_partition(bdev_t *dev, const struct mbr_part *part)
64{
65 /* check for invalid types */
66 if (part->type == 0)
67 return -1;
68 /* check for invalid status */
69 if (part->status != 0x80 && part->status != 0x00)
70 return -1;
71
72 /* make sure the range fits within the device */
73 if (part->lba_start >= dev->block_count)
74 return -1;
75 if ((part->lba_start + part->lba_length) > dev->block_count)
76 return -1;
77
78 /* that's about all we can do, MBR has no other good way to see if it's valid */
79
80 return 0;
81}
82
83/*
84 * Parse the gpt header and get the required header fields
85 * Return 0 on valid signature
86 */
87static unsigned int
88partition_parse_gpt_header(unsigned char *buffer, struct gpt_header* header)
89{
90 /* Check GPT Signature */
91 if (((uint32_t *) buffer)[0] != GPT_SIGNATURE_2 ||
92 ((uint32_t *) buffer)[1] != GPT_SIGNATURE_1)
93 return 1;
94
95 header->header_size = GET_LWORD_FROM_BYTE(&buffer[HEADER_SIZE_OFFSET]);
96 header->backup_header_lba =
97 GET_LLWORD_FROM_BYTE(&buffer[BACKUP_HEADER_OFFSET]);
98 header->first_usable_lba =
99 GET_LLWORD_FROM_BYTE(&buffer[FIRST_USABLE_LBA_OFFSET]);
100 header->max_partition_count =
101 GET_LWORD_FROM_BYTE(&buffer[PARTITION_COUNT_OFFSET]);
102 header->partition_entry_size =
103 GET_LWORD_FROM_BYTE(&buffer[PENTRY_SIZE_OFFSET]);
104
105 return 0;
106}
107
108const char hex_asc[] = "0123456789abcdef";
109#define hex_asc_lo(x) hex_asc[((x)&0x0f)];
110#define hex_asc_hi(x) hex_asc[((x)&0xf0)>>4];
111static inline char *hex_byte_pack(char *buf, u8 byte)
112{
113 *buf++ = hex_asc_hi(byte);
114 *buf++ = hex_asc_lo(byte);
115 return buf;
116}
117
118static char *string(char *buf, char *end, const char *s)
119{
120 int len, i;
121 len = strnlen(s, 37);
122 for (i = 0; i < len; ++i) {
123 if (buf < end)
124 *buf = *s;
125 ++buf;
126 ++s;
127 }
128 return buf;
129}
130
131static char *uuid_string(char *buf, char *args)
132{
133 char uuid[sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")];
134 char *p = uuid;
135 int i;
136 static const u8 be[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
137 static const u8 le[16] = {3,2,1,0,5,4,7,6,8,9,10,11,12,13,14,15};
138 const u8 *index = be;
139 char *end;
140
141 index = le;
142 end = buf + 37;
143 for (i = 0; i < 16; i++) {
144 p = hex_byte_pack(p, args[index[i]]);
145 switch(i) {
146 case 3:
147 case 5:
148 case 7:
149 case 9:
150 *p++ = '-';
151 break;
152 default:
153 break;
154 }
155 }
156
157 *p = 0;
158 return string(buf, end, uuid);
159}
160
161
162int partition_publish(const char *device, off_t offset)
163{
164 int err = 0;
165 int count = 0;
166
167 // clear any partitions that may have already existed
168 partition_unpublish(device);
169
170 bdev_t *dev = bio_open(device);
171 if (!dev) {
172 printf("partition_publish: unable to open device\n");
173 return -1;
174 }
175
176 // get a dma aligned and padded block to read info
177 uint8_t *buf = memalign(CACHE_LINE, dev->block_size);
178 if (buf == NULL)
179 return ERR_NO_MEMORY;
180
181 /* sniff for MBR partition types */
182 do {
183 unsigned int i, j, n;
184 int gpt_partitions_exist = 0;
185
186 err = bio_read(dev, buf, offset, 512);
187 if (err < 0)
188 goto err;
189
190#ifdef NAND_PAGE_ADDR_OF_PMBR
191 /* sniff for DEV header */
192 if (strncmp("BOOTLOADER!", (char *)buf, 11) == 0) {
193 /* skip NAND_PAGE_ADDR_OF_PMBR pages to find MBR & GPT */
194 offset += NAND_PAGE_ADDR_OF_PMBR * dev->block_size;
195
196 err = bio_read(dev, buf, offset, 512);
197 if (err < 0)
198 goto err;
199 }
200#endif
201
202 /* look for the aa55 tag */
203 if (buf[510] != 0x55 || buf[511] != 0xaa)
204 break;
205
206 /* see if a partition table makes sense here */
207 struct mbr_part part[4];
208 memcpy(part, buf + 446, sizeof(part));
209
210#if LK_DEBUGLEVEL >= INFO
211 dprintf(INFO, "mbr partition table dump:\n");
212 for (i=0; i < 4; i++) {
213 dprintf(INFO, "\t%i: status 0x%hhx, type 0x%hhx, start 0x%x, len 0x%x\n", i, part[i].status, part[i].type, part[i].lba_start, part[i].lba_length);
214 }
215#endif
216
217 /* validate each of the partition entries */
218 for (i=0; i < 4; i++) {
219 if (validate_mbr_partition(dev, &part[i]) >= 0) {
220 // publish it
221 char subdevice[128];
222
223 /* Type 0xEE indicates end of MBR and GPT partitions exist */
224 if(part[i].type==0xee) {
225 gpt_partitions_exist = 1;
226 break;
227 }
228
229 sprintf(subdevice, "%sp%d", device, i);
230
231 err = bio_publish_subdevice(device, subdevice, part[i].lba_start, part[i].lba_length);
232 if (err < 0) {
233 dprintf(INFO, "error publishing subdevice '%s'\n", subdevice);
234 continue;
235 }
236 count++;
237 }
238 }
239
240 if(!gpt_partitions_exist) break;
241 dprintf(INFO, "found GPT\n");
242
243 err = bio_read(dev, buf, offset + dev->block_size, dev->block_size);
244 if (err < 0)
245 goto err;
246
247 struct gpt_header gpthdr;
248 err = partition_parse_gpt_header(buf, &gpthdr);
249 if (err) {
250 /* Check the backup gpt */
251
252 uint64_t backup_header_lba = dev->block_count - 1;
253 err = bio_read(dev, buf, (backup_header_lba * dev->block_size), dev->block_size);
254 if (err < 0) {
255 dprintf(CRITICAL, "GPT: Could not read backup gpt from mmc\n");
256 break;
257 }
258
259 err = partition_parse_gpt_header(buf, &gpthdr);
260 if (err) {
261 dprintf(CRITICAL, "GPT: Primary and backup signatures invalid\n");
262 break;
263 }
264 }
265
266 uint32_t part_entry_cnt = dev->block_size / ENTRY_SIZE;
267 uint64_t partition_0 = GET_LLWORD_FROM_BYTE(&buf[PARTITION_ENTRIES_OFFSET]);
268 /* Read GPT Entries */
269 for (i = 0; i < (ROUNDUP(gpthdr.max_partition_count, part_entry_cnt)) / part_entry_cnt; i++) {
270 err = bio_read(dev, buf, offset + (partition_0 * dev->block_size) + (i * dev->block_size),
271 dev->block_size);
272
273 if (err < 0) {
274 dprintf(CRITICAL,
275 "GPT: mmc read card failed reading partition entries.\n");
276 break;
277 }
278
279 for (j = 0; j < part_entry_cnt; j++) {
280 unsigned char type_guid[PARTITION_TYPE_GUID_SIZE];
281 unsigned char name[MAX_GPT_NAME_SIZE];
282 unsigned char UTF16_name[MAX_GPT_NAME_SIZE];
283
284 unsigned char unique_uuid[UNIQUE_PARTITION_GUID_SIZE]={0};
285 unsigned char unique_uuid_str[64]={0};
286 uint64_t first_lba, last_lba, size;
287
288 // guid
289 memcpy(&type_guid,
290 &buf[(j * gpthdr.partition_entry_size)],
291 PARTITION_TYPE_GUID_SIZE);
292 if (type_guid[0]==0 && type_guid[1]==0) {
293 i = ROUNDUP(gpthdr.max_partition_count, part_entry_cnt);
294 break;
295 }
296
297 // size
298 first_lba = GET_LLWORD_FROM_BYTE(&buf[(j * gpthdr.partition_entry_size) + FIRST_LBA_OFFSET]);
299 last_lba = GET_LLWORD_FROM_BYTE(&buf[(j * gpthdr.partition_entry_size) + LAST_LBA_OFFSET]);
300 size = last_lba - first_lba + 1;
301
302 // name
303 memset(&UTF16_name, 0x00, MAX_GPT_NAME_SIZE);
304 memcpy(UTF16_name, &buf[(j * gpthdr.partition_entry_size) +
305 PARTITION_NAME_OFFSET], MAX_GPT_NAME_SIZE);
306
307 /*
308 * Currently partition names in *.xml are UTF-8 and lowercase
309 * Only supporting english for now so removing 2nd byte of UTF-16
310 */
311 for (n = 0; n < MAX_GPT_NAME_SIZE / 2; n++) {
312 name[n] = UTF16_name[n * 2];
313 }
314
315 memcpy((char *)unique_uuid,&buf[(j * gpthdr.partition_entry_size)]+UNIQUE_GUID_OFFSET,UNIQUE_PARTITION_GUID_SIZE);
316 uuid_string((char*)unique_uuid_str,(char*)unique_uuid);
317 dprintf(INFO, "name:%s unique_uuid_str is %s\n",name,unique_uuid_str);
318
319 //dprintf(CRITICAL, "got part '%s' size=%llu!\n", name, size);
320 char subdevice[128];
321 sprintf(subdevice, "%sp%d", device, count+1);
322
323 err = bio_publish_subdevice(device, subdevice, first_lba, size);
324 if (err < 0) {
325 dprintf(INFO, "error publishing subdevice '%s'\n", name);
326 continue;
327 }
328
329 bdev_t *partdev = bio_open(subdevice);
330 partdev->unique_uuid = strdup((char*)unique_uuid_str);
331 partdev->label = strdup((char*)name);
332 partdev->is_gpt = true;
333 bio_close(partdev);
334
335#if WITH_LIB_NFTL
336 nftl_add_part(device, subdevice, partdev->label, (u64)first_lba * dev->block_size, (u64)size * dev->block_size);
337#endif
338 count++;
339 }
340 }
341 } while (0);
342
343 bio_close(dev);
344
345err:
346 free(buf);
347 return (err < 0) ? err : count;
348}
349
350int partition_unpublish(const char *device)
351{
352 int i;
353 int count;
354 bdev_t *dev;
355 char devname[512];
356
357 count = 0;
358 for (i=0; i < NUM_PARTITIONS; i++) {
359 sprintf(devname, "%sp%d", device, i);
360
361 dev = bio_open(devname);
362 if (!dev)
363 continue;
364
365 bio_unregister_device(dev);
366#if WITH_LIB_NFTL
367 nftl_delete_part(dev->name);
368#endif
369 bio_close(dev);
370 count++;
371 }
372
373 return count;
374}
375
376static void
377patch_gpt(bdev_t *dev, uint8_t *gptImage, uint32_t array_size,
378 uint32_t max_part_count, uint32_t part_entry_size)
379{
380 uint8_t *partition_entry_array_start;
381 unsigned char *primary_gpt_header;
382 unsigned char *secondary_gpt_header;
383 unsigned long long card_size_sec = (unsigned long long)dev->block_count;
384 uint32_t block_size = dev->block_size;
385 int total_part = 0, phy_last_part = 0;
386 unsigned long last_part_offset;
387 unsigned int crc_value;
388 unsigned long long last_part_first_lba, last_part_last_lba;
389 unsigned long long sgpt_first_lba;
390 unsigned int partition_align_lba;
391
392 /* Generate second gpt header */
393 memcpy(gptImage + (block_size * 2) + array_size,
394 gptImage + block_size,
395 block_size);
396
397 sgpt_first_lba = (long long)(card_size_sec - MIN_PARTITION_ARRAY_SIZE / block_size - 1);
398 /* Patching primary header */
399 primary_gpt_header = (gptImage + block_size);
400 PUT_LONG_LONG(primary_gpt_header + BACKUP_HEADER_OFFSET,
401 ((long long)(card_size_sec - 1)));
402 PUT_LONG_LONG(primary_gpt_header + LAST_USABLE_LBA_OFFSET,
403 (sgpt_first_lba - 1));
404
405 /* Patching backup GPT */
406 secondary_gpt_header = primary_gpt_header + block_size + array_size;
407 PUT_LONG_LONG(secondary_gpt_header + PRIMARY_HEADER_OFFSET,
408 ((long long)(card_size_sec - 1)));
409 PUT_LONG_LONG(secondary_gpt_header + LAST_USABLE_LBA_OFFSET,
410 (sgpt_first_lba - 1));
411 PUT_LONG_LONG(secondary_gpt_header + PARTITION_ENTRIES_OFFSET,
412 sgpt_first_lba);
413 PUT_LONG_LONG(secondary_gpt_header + BACKUP_HEADER_OFFSET,
414 ((long long)(1)));
415
416 /* Find last partition */
417 while (*(primary_gpt_header + block_size + total_part * ENTRY_SIZE) != 0) {
418 if (GET_LLWORD_FROM_BYTE(primary_gpt_header + block_size + total_part * ENTRY_SIZE + FIRST_LBA_OFFSET) >=
419 GET_LLWORD_FROM_BYTE(primary_gpt_header + block_size + phy_last_part * ENTRY_SIZE + FIRST_LBA_OFFSET)) {
420 phy_last_part = total_part;
421 }
422 total_part++;
423 }
424
425 /* Patching last partition */
426 last_part_offset = (unsigned long)(primary_gpt_header + block_size + phy_last_part * ENTRY_SIZE);
427 last_part_first_lba = GET_LLWORD_FROM_BYTE(last_part_offset + PARTITION_ENTRY_FIRST_LBA);
428
429 /*
430 * For EMMC and NOR case, last partition size should align 64KB;
431 * For NAND, last partition size should align NAND erase size.
432 */
433 if (block_size == 512) {
434 partition_align_lba = 128;
435 } else {
436 /* It's NAND case */
437 partition_align_lba = dev->geometry->erase_size / block_size;
438 }
439 last_part_last_lba = (card_size_sec - 34) - (((card_size_sec - 34) - last_part_first_lba + 1) % partition_align_lba);
440 PUT_LONG_LONG(last_part_offset + PARTITION_ENTRY_LAST_LBA, (long long)last_part_last_lba);
441
442 /* Updating CRC of the Partition entry array in both headers */
443 partition_entry_array_start = primary_gpt_header + block_size;
444 crc_value = (unsigned int)crc32(0x0, partition_entry_array_start,
445 max_part_count * part_entry_size);
446 PUT_LONG(primary_gpt_header + PARTITION_CRC_OFFSET, crc_value);
447 PUT_LONG(secondary_gpt_header + PARTITION_CRC_OFFSET, crc_value);
448
449 /* Clearing CRC fields to calculate */
450 PUT_LONG(primary_gpt_header + HEADER_CRC_OFFSET, 0);
451 crc_value = (unsigned int)crc32(0x0, primary_gpt_header, 92);
452 PUT_LONG(primary_gpt_header + HEADER_CRC_OFFSET, crc_value);
453
454 PUT_LONG(secondary_gpt_header + HEADER_CRC_OFFSET, 0);
455 crc_value = (unsigned int)crc32(0x0, secondary_gpt_header, 92);
456 PUT_LONG(secondary_gpt_header + HEADER_CRC_OFFSET, crc_value);
457}
458
459static int write_gpt(const char *device, uint32_t size, uint8_t *gptImage, uint32_t block_size)
460{
461 int ret;
462 uint64_t device_density;
463
464 bdev_t *dev = bio_open(device);
465 if (!dev) {
466 dprintf(INFO, "write_gpt: unable to open device\n");
467 return -1;
468 }
469
470 /* check size */
471 if (size < (MIN_PARTITION_ARRAY_SIZE + (block_size * 3))) {
472 dprintf(INFO,
473 "write_gpt check size fail:size(%d) < MIN_PARTITION_ARRAY_SIZE(%d) + block_size(%d) * 3\n",
474 size, MIN_PARTITION_ARRAY_SIZE, block_size);
475 ret = -1;
476 goto end;
477 }
478
479 /* Get the density of the storage device */
480 device_density = (uint64_t)dev->block_count * dev->block_size;
481
482 /* Patching the primary and the backup header of the GPT table */
483 patch_gpt(dev, gptImage, MIN_PARTITION_ARRAY_SIZE, 128, 128);
484
485 /* write primary */
486 ret = bio_write(dev, (unsigned int *)gptImage, 0, ((block_size*2) + MIN_PARTITION_ARRAY_SIZE));
487
488 if (ret < 0) {
489 dprintf(INFO, "Failed to write primary\n");
490 goto end;
491 }
492
493 /* write secondary */
494 ret = bio_write(dev, (unsigned int *)(gptImage + (block_size*2)),
495 (device_density - (MIN_PARTITION_ARRAY_SIZE + block_size)),
496 (MIN_PARTITION_ARRAY_SIZE + block_size));
497 if (ret < 0) {
498 dprintf(INFO, "Failed to write secondary\n");
499 goto end;
500 }
501 end:
502 bio_close(dev);
503 return ret;
504}
505
506int partition_update(const char *device, off_t offset, const char *data, size_t sz)
507{
508 uint8_t *buffer;
509 int err = 0;
510 size_t rsize;
511 unsigned int gpt_size;
512
513 bdev_t *dev = bio_open(device);
514 if (!dev) {
515 dprintf(INFO, "partition_update: unable to open device\n");
516 return -1;
517 }
518 gpt_size = MIN_PARTITION_ARRAY_SIZE + dev->block_size * 2;
519
520 // get a dma aligned and padded block to read info
521 uint8_t *buf = memalign(CACHE_LINE, dev->block_size);
522 if (buf == NULL)
523 return ERR_NO_MEMORY;
524
525 /* sniff for MBR partition types */
526 unsigned int i;
527 int gpt_partitions_exist = 0;
528
529 memcpy(buf, data + offset, dev->block_size);
530
531 /* look for the aa55 tag */
532 if (buf[510] != 0x55 || buf[511] != 0xaa) {
533 err = -1;
534 dprintf(INFO, "no 0xaa55 tag, not MBR\n");
535 goto err;
536 }
537
538 /* see if a partition table makes sense here */
539 struct mbr_part part[4];
540 memcpy(part, buf + 446, sizeof(part));
541
542 /* check each entry to find GPT exist or not */
543 for (i=0; i < 4; i++) {
544 if (validate_mbr_partition(dev, &part[i]) >= 0) {
545 /* Type 0xEE indicates end of MBR and GPT partitions exist */
546 if(part[i].type==0xee) {
547 gpt_partitions_exist = 1;
548 break;
549 }
550 }
551 }
552
553 if (!gpt_partitions_exist) {
554 err = -1;
555 dprintf(INFO, "gpt partition is not exist\n");
556 goto err;
557 }
558 dprintf(INFO, "found GPT\n");
559
560 memcpy(buf, data + offset + dev->block_size, dev->block_size);
561
562 struct gpt_header gpthdr;
563 err = partition_parse_gpt_header(buf, &gpthdr);
564 if (err) {
565 err = -1;
566 dprintf(INFO, "GPT: Primary signatures invalid\n");
567 goto err;
568 }
569
570 /* check whether to resize userdata partition */
571 if (gpthdr.backup_header_lba == (dev->block_count - 1))
572 {
573 err = -1;
574 dprintf(INFO, "GPT: Already up to date\n");
575 goto err;
576 }
577 buffer = (uint8_t *)malloc(gpt_size + dev->block_size);
578 if (!buffer) {
579 err = -1;
580 dprintf(CRITICAL, "Failed to Allocate memory to read partition table\n");
581 goto err;
582 }
583
584 memcpy(buffer, data + offset, gpt_size);
585 err = write_gpt(dev->name, gpt_size + dev->block_size, buffer, dev->block_size);
586 if (err < 0) {
587 dprintf(INFO, "Failed to write partition table\n");
588 }
589
590free_buf:
591 free(buffer);
592err:
593 free(buf);
594
595 if (err < 0) {
596 rsize = (size_t)bio_write(dev, data, 0, sz);
597 if (rsize != sz) {
598 err = -1;
599 dprintf(INFO, "bio_write size is not match!\n");
600 } else
601 err = 0;
602 } else {
603 rsize = (size_t)bio_write(dev, data + gpt_size, gpt_size, sz-gpt_size);
604 if (rsize != sz-gpt_size) {
605 err = -1;
606 dprintf(INFO, "gpt update finish, bio_write size is not match!\n");
607 }
608 }
609
610 bio_close(dev);
611
612 return err;
613}
614