1 /* $OpenBSD: gpt.c,v 1.94 2024/05/21 05:00:47 jsg Exp $ */
2 /*
3 * Copyright (c) 2015 Markus Muller <mmu@grummel.net>
4 * Copyright (c) 2015 Kenneth R Westerback <krw@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h> /* DEV_BSIZE */
20 #include <sys/disklabel.h>
21 #include <sys/dkio.h>
22 #include <sys/ioctl.h>
23
24 #include <ctype.h>
25 #include <err.h>
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <uuid.h>
31
32 #include "part.h"
33 #include "disk.h"
34 #include "mbr.h"
35 #include "misc.h"
36 #include "gpt.h"
37
38 #ifdef DEBUG
39 #define DPRINTF(x...) printf(x)
40 #else
41 #define DPRINTF(x...)
42 #endif
43
44 struct mbr gmbr;
45 struct gpt_header gh;
46 struct gpt_partition gp[NGPTPARTITIONS];
47
48 const struct gpt_partition * const *sort_gpt(void);
49 int lba_free(uint64_t *, uint64_t *);
50 int add_partition(const uint8_t *, const char *, uint64_t);
51 int find_partition(const uint8_t *);
52 int get_header(const uint64_t);
53 int get_partition_table(void);
54 int init_gh(void);
55 int init_gp(const int);
56 uint32_t crc32(const u_char *, const uint32_t);
57 int protective_mbr(const struct mbr *);
58 int gpt_chk_mbr(struct dos_partition *, uint64_t);
59 void string_to_name(const unsigned int, const char *);
60 const char *name_to_string(const unsigned int);
61
62 void
string_to_name(const unsigned int pn,const char * ch)63 string_to_name(const unsigned int pn, const char *ch)
64 {
65 unsigned int i;
66
67 memset(gp[pn].gp_name, 0, sizeof(gp[pn].gp_name));
68
69 for (i = 0; i < sizeof(gp[pn].gp_name) && ch[i] != '\0'; i++)
70 gp[pn].gp_name[i] = htole16((unsigned int)ch[i]);
71 }
72
73 const char *
name_to_string(const unsigned int pn)74 name_to_string(const unsigned int pn)
75 {
76 static char name[GPTPARTNAMESIZE + 1];
77 unsigned int i;
78
79 for (i = 0; i < GPTPARTNAMESIZE && gp[pn].gp_name[i] != 0; i++)
80 name[i] = letoh16(gp[pn].gp_name[i]) & 0x7F;
81 name[i] = '\0';
82
83 return name;
84 }
85
86 /*
87 * Return the index into dp[] of the EFI GPT (0xEE) partition, or -1 if no such
88 * partition exists.
89 *
90 * Taken from kern/subr_disk.c.
91 *
92 */
93 int
gpt_chk_mbr(struct dos_partition * dp,u_int64_t dsize)94 gpt_chk_mbr(struct dos_partition *dp, u_int64_t dsize)
95 {
96 struct dos_partition *dp2;
97 int efi, eficnt, found, i;
98 uint32_t psize;
99
100 found = efi = eficnt = 0;
101 for (dp2 = dp, i = 0; i < NDOSPART; i++, dp2++) {
102 if (dp2->dp_typ == DOSPTYP_UNUSED)
103 continue;
104 found++;
105 if (dp2->dp_typ != DOSPTYP_EFI)
106 continue;
107 if (letoh32(dp2->dp_start) != GPTSECTOR)
108 continue;
109 psize = letoh32(dp2->dp_size);
110 if (psize <= (dsize - GPTSECTOR) || psize == UINT32_MAX) {
111 efi = i;
112 eficnt++;
113 }
114 }
115 if (found == 1 && eficnt == 1)
116 return efi;
117
118 return -1;
119 }
120
121 int
protective_mbr(const struct mbr * mbr)122 protective_mbr(const struct mbr *mbr)
123 {
124 struct dos_partition dp[NDOSPART], dos_partition;
125 unsigned int i;
126
127 if (mbr->mbr_lba_self != 0)
128 return -1;
129
130 for (i = 0; i < nitems(dp); i++) {
131 memset(&dos_partition, 0, sizeof(dos_partition));
132 if (i < nitems(mbr->mbr_prt))
133 PRT_prt_to_dp(&mbr->mbr_prt[i], mbr->mbr_lba_self,
134 mbr->mbr_lba_firstembr, &dos_partition);
135 memcpy(&dp[i], &dos_partition, sizeof(dp[i]));
136 }
137
138 return gpt_chk_mbr(dp, DL_GETDSIZE(&dl));
139 }
140
141 int
get_header(const uint64_t sector)142 get_header(const uint64_t sector)
143 {
144 struct gpt_header legh;
145 uint64_t gpbytes, gpsectors, lba_end;
146
147 if (DISK_readbytes(&legh, sector, sizeof(legh)))
148 return -1;
149
150 gh.gh_sig = letoh64(legh.gh_sig);
151 if (gh.gh_sig != GPTSIGNATURE) {
152 DPRINTF("gpt signature: expected 0x%llx, got 0x%llx\n",
153 GPTSIGNATURE, gh.gh_sig);
154 return -1;
155 }
156
157 gh.gh_rev = letoh32(legh.gh_rev);
158 if (gh.gh_rev != GPTREVISION) {
159 DPRINTF("gpt revision: expected 0x%x, got 0x%x\n",
160 GPTREVISION, gh.gh_rev);
161 return -1;
162 }
163
164 gh.gh_lba_self = letoh64(legh.gh_lba_self);
165 if (gh.gh_lba_self != sector) {
166 DPRINTF("gpt self lba: expected %llu, got %llu\n",
167 sector, gh.gh_lba_self);
168 return -1;
169 }
170
171 gh.gh_size = letoh32(legh.gh_size);
172 if (gh.gh_size != GPTMINHDRSIZE) {
173 DPRINTF("gpt header size: expected %u, got %u\n",
174 GPTMINHDRSIZE, gh.gh_size);
175 return -1;
176 }
177
178 gh.gh_part_size = letoh32(legh.gh_part_size);
179 if (gh.gh_part_size != GPTMINPARTSIZE) {
180 DPRINTF("gpt partition size: expected %u, got %u\n",
181 GPTMINPARTSIZE, gh.gh_part_size);
182 return -1;
183 }
184
185 if ((dl.d_secsize % gh.gh_part_size) != 0) {
186 DPRINTF("gpt sector size %% partition size (%u %% %u) != 0\n",
187 dl.d_secsize, gh.gh_part_size);
188 return -1;
189 }
190
191 gh.gh_part_num = letoh32(legh.gh_part_num);
192 if (gh.gh_part_num > NGPTPARTITIONS) {
193 DPRINTF("gpt partition count: expected <= %u, got %u\n",
194 NGPTPARTITIONS, gh.gh_part_num);
195 return -1;
196 }
197
198 gh.gh_csum = letoh32(legh.gh_csum);
199 legh.gh_csum = 0;
200 legh.gh_csum = crc32((unsigned char *)&legh, gh.gh_size);
201 if (legh.gh_csum != gh.gh_csum) {
202 DPRINTF("gpt header checksum: expected 0x%x, got 0x%x\n",
203 legh.gh_csum, gh.gh_csum);
204 /* Accept wrong-endian checksum. */
205 if (swap32(legh.gh_csum) != gh.gh_csum)
206 return -1;
207 }
208
209 gpbytes = gh.gh_part_num * gh.gh_part_size;
210 gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize;
211 lba_end = DL_GETDSIZE(&dl) - gpsectors - 2;
212
213 gh.gh_lba_end = letoh64(legh.gh_lba_end);
214 if (gh.gh_lba_end > lba_end) {
215 DPRINTF("gpt last usable LBA: reduced from %llu to %llu\n",
216 gh.gh_lba_end, lba_end);
217 gh.gh_lba_end = lba_end;
218 }
219
220 gh.gh_lba_start = letoh64(legh.gh_lba_start);
221 if (gh.gh_lba_start >= gh.gh_lba_end) {
222 DPRINTF("gpt first usable LBA: expected < %llu, got %llu\n",
223 gh.gh_lba_end, gh.gh_lba_start);
224 return -1;
225 }
226
227 gh.gh_part_lba = letoh64(legh.gh_part_lba);
228 if (gh.gh_lba_self == GPTSECTOR) {
229 if (gh.gh_part_lba <= GPTSECTOR) {
230 DPRINTF("gpt partition entries start: expected > %u, "
231 "got %llu\n", GPTSECTOR, gh.gh_part_lba);
232 return -1;
233 }
234 if (gh.gh_part_lba + gpsectors > gh.gh_lba_start) {
235 DPRINTF("gpt partition entries end: expected < %llu, "
236 "got %llu\n", gh.gh_lba_start,
237 gh.gh_part_lba + gpsectors);
238 return -1;
239 }
240 } else {
241 if (gh.gh_part_lba <= gh.gh_lba_end) {
242 DPRINTF("gpt partition entries start: expected > %llu, "
243 "got %llu\n", gh.gh_lba_end, gh.gh_part_lba);
244 return -1;
245 }
246 if (gh.gh_part_lba + gpsectors > gh.gh_lba_self) {
247 DPRINTF("gpt partition entries end: expected < %llu, "
248 "got %llu\n", gh.gh_lba_self,
249 gh.gh_part_lba + gpsectors);
250 return -1;
251 }
252 }
253
254 gh.gh_lba_alt = letoh32(legh.gh_lba_alt);
255 gh.gh_part_csum = letoh32(legh.gh_part_csum);
256 gh.gh_rsvd = letoh32(legh.gh_rsvd); /* Should always be 0. */
257 uuid_dec_le(&legh.gh_guid, &gh.gh_guid);
258
259 return 0;
260 }
261
262 int
get_partition_table(void)263 get_partition_table(void)
264 {
265 struct gpt_partition *legp;
266 uint64_t gpbytes;
267 unsigned int pn;
268 int rslt = -1;
269 uint32_t gh_part_csum;
270
271 DPRINTF("gpt partition table being read from LBA %llu\n",
272 gh.gh_part_lba);
273
274 gpbytes = gh.gh_part_num * gh.gh_part_size;
275
276 legp = calloc(1, gpbytes);
277 if (legp == NULL)
278 err(1, "legp");
279
280 if (DISK_readbytes(legp, gh.gh_part_lba, gpbytes))
281 goto done;
282 gh_part_csum = crc32((unsigned char *)legp, gpbytes);
283
284 if (gh_part_csum != gh.gh_part_csum) {
285 DPRINTF("gpt partition table checksum: expected 0x%x, "
286 "got 0x%x\n", gh.gh_part_csum, gh_part_csum);
287 /* Accept wrong-endian checksum. */
288 if (swap32(gh_part_csum) != gh.gh_part_csum)
289 goto done;
290 }
291
292 memset(&gp, 0, sizeof(gp));
293 for (pn = 0; pn < gh.gh_part_num; pn++) {
294 uuid_dec_le(&legp[pn].gp_type, &gp[pn].gp_type);
295 uuid_dec_le(&legp[pn].gp_guid, &gp[pn].gp_guid);
296 gp[pn].gp_lba_start = letoh64(legp[pn].gp_lba_start);
297 gp[pn].gp_lba_end = letoh64(legp[pn].gp_lba_end);
298 gp[pn].gp_attrs = letoh64(legp[pn].gp_attrs);
299 memcpy(gp[pn].gp_name, legp[pn].gp_name,
300 sizeof(gp[pn].gp_name));
301 }
302 rslt = 0;
303
304 done:
305 free(legp);
306 return rslt;
307 }
308
309 int
GPT_read(const int which)310 GPT_read(const int which)
311 {
312 int error;
313
314 error = MBR_read(0, 0, &gmbr);
315 if (error)
316 goto done;
317 error = protective_mbr(&gmbr);
318 if (error == -1)
319 goto done;
320
321 switch (which) {
322 case PRIMARYGPT:
323 error = get_header(GPTSECTOR);
324 break;
325 case SECONDARYGPT:
326 error = get_header(DL_GETDSIZE(&dl) - 1);
327 break;
328 case ANYGPT:
329 error = get_header(GPTSECTOR);
330 if (error != 0 || get_partition_table() != 0)
331 error = get_header(DL_GETDSIZE(&dl) - 1);
332 break;
333 default:
334 return -1;
335 }
336
337 if (error == 0)
338 error = get_partition_table();
339
340 done:
341 if (error != 0) {
342 /* No valid GPT found. Zap any artifacts. */
343 memset(&gmbr, 0, sizeof(gmbr));
344 memset(&gh, 0, sizeof(gh));
345 memset(&gp, 0, sizeof(gp));
346 }
347
348 return error;
349 }
350
351 void
GPT_print(const char * units,const int verbosity)352 GPT_print(const char *units, const int verbosity)
353 {
354 const struct unit_type *ut;
355 const int secsize = dl.d_secsize;
356 char *guidstr = NULL;
357 double size;
358 unsigned int pn;
359 uint32_t status;
360
361 #ifdef DEBUG
362 char *p;
363 uint64_t sig;
364 unsigned int i;
365
366 sig = htole64(gh.gh_sig);
367 p = (char *)&sig;
368
369 printf("gh_sig : ");
370 for (i = 0; i < sizeof(sig); i++)
371 printf("%c", isprint((unsigned char)p[i]) ? p[i] : '?');
372 printf(" (");
373 for (i = 0; i < sizeof(sig); i++) {
374 printf("%02x", p[i]);
375 if ((i + 1) < sizeof(sig))
376 printf(":");
377 }
378 printf(")\n");
379 printf("gh_rev : %u\n", gh.gh_rev);
380 printf("gh_size : %u (%zd)\n", gh.gh_size, sizeof(gh));
381 printf("gh_csum : 0x%x\n", gh.gh_csum);
382 printf("gh_rsvd : %u\n", gh.gh_rsvd);
383 printf("gh_lba_self : %llu\n", gh.gh_lba_self);
384 printf("gh_lba_alt : %llu\n", gh.gh_lba_alt);
385 printf("gh_lba_start : %llu\n", gh.gh_lba_start);
386 printf("gh_lba_end : %llu\n", gh.gh_lba_end);
387 p = NULL;
388 uuid_to_string(&gh.gh_guid, &p, &status);
389 printf("gh_gh_guid : %s\n", (status == uuid_s_ok) ? p : "<invalid>");
390 free(p);
391 printf("gh_gh_part_lba : %llu\n", gh.gh_part_lba);
392 printf("gh_gh_part_num : %u (%zu)\n", gh.gh_part_num, nitems(gp));
393 printf("gh_gh_part_size: %u (%zu)\n", gh.gh_part_size, sizeof(gp[0]));
394 printf("gh_gh_part_csum: 0x%x\n", gh.gh_part_csum);
395 printf("\n");
396 #endif /* DEBUG */
397
398 size = units_size(units, DL_GETDSIZE(&dl), &ut);
399 printf("Disk: %s Usable LBA: %llu to %llu [%.0f ",
400 disk.dk_name, gh.gh_lba_start, gh.gh_lba_end, size);
401 if (ut->ut_conversion == 0 && secsize != DEV_BSIZE)
402 printf("%d-byte ", secsize);
403 printf("%s]\n", ut->ut_lname);
404
405 if (verbosity == VERBOSE) {
406 printf("GUID: ");
407 uuid_to_string(&gh.gh_guid, &guidstr, &status);
408 if (status == uuid_s_ok)
409 printf("%s\n", guidstr);
410 else
411 printf("<invalid header GUID>\n");
412 free(guidstr);
413 }
414
415 GPT_print_parthdr(verbosity);
416 for (pn = 0; pn < gh.gh_part_num; pn++) {
417 if (uuid_is_nil(&gp[pn].gp_type, NULL))
418 continue;
419 GPT_print_part(pn, units, verbosity);
420 }
421 }
422
423 void
GPT_print_parthdr(const int verbosity)424 GPT_print_parthdr(const int verbosity)
425 {
426 printf(" #: type "
427 " [ start: size ]\n");
428 if (verbosity == VERBOSE)
429 printf(" guid name\n");
430 printf("--------------------------------------------------------"
431 "----------------\n");
432 }
433
434 void
GPT_print_part(const unsigned int pn,const char * units,const int verbosity)435 GPT_print_part(const unsigned int pn, const char *units, const int verbosity)
436 {
437 const struct unit_type *ut;
438 char *guidstr = NULL;
439 double size;
440 uint64_t attrs, end, start;
441 uint32_t status;
442
443 start = gp[pn].gp_lba_start;
444 end = gp[pn].gp_lba_end;
445 size = units_size(units, (start > end) ? 0 : end - start + 1, &ut);
446
447 printf(" %3u: %-36s [%12lld: %12.0f%s]\n", pn,
448 PRT_uuid_to_desc(&gp[pn].gp_type), start, size, ut->ut_abbr);
449
450 if (verbosity == VERBOSE) {
451 uuid_to_string(&gp[pn].gp_guid, &guidstr, &status);
452 if (status != uuid_s_ok)
453 printf(" <invalid partition guid> ");
454 else
455 printf(" %-36s ", guidstr);
456 printf("%s\n", name_to_string(pn));
457 free(guidstr);
458 attrs = gp[pn].gp_attrs;
459 if (attrs) {
460 printf(" Attributes: (0x%016llx) ", attrs);
461 if (attrs & GPTPARTATTR_REQUIRED)
462 printf("Required " );
463 if (attrs & GPTPARTATTR_IGNORE)
464 printf("Ignore ");
465 if (attrs & GPTPARTATTR_BOOTABLE)
466 printf("Bootable ");
467 if (attrs & GPTPARTATTR_MS_READONLY)
468 printf("MSReadOnly " );
469 if (attrs & GPTPARTATTR_MS_SHADOW)
470 printf("MSShadow ");
471 if (attrs & GPTPARTATTR_MS_HIDDEN)
472 printf("MSHidden ");
473 if (attrs & GPTPARTATTR_MS_NOAUTOMOUNT)
474 printf("MSNoAutoMount ");
475 printf("\n");
476 }
477 }
478
479 if (uuid_is_nil(&gp[pn].gp_type, NULL) == 0) {
480 if (start > end)
481 printf("partition %u first LBA is > last LBA\n", pn);
482 if (start < gh.gh_lba_start || end > gh.gh_lba_end)
483 printf("partition %u extends beyond usable LBA range "
484 "of %s\n", pn, disk.dk_name);
485 }
486 }
487
488 int
find_partition(const uint8_t * beuuid)489 find_partition(const uint8_t *beuuid)
490 {
491 struct uuid uuid;
492 unsigned int pn;
493
494 uuid_dec_be(beuuid, &uuid);
495
496 for (pn = 0; pn < gh.gh_part_num; pn++) {
497 if (uuid_compare(&gp[pn].gp_type, &uuid, NULL) == 0)
498 return pn;
499 }
500 return -1;
501 }
502
503 int
add_partition(const uint8_t * beuuid,const char * name,uint64_t sectors)504 add_partition(const uint8_t *beuuid, const char *name, uint64_t sectors)
505 {
506 struct uuid uuid;
507 int rslt;
508 uint64_t end, freesectors, start;
509 uint32_t status, pn;
510
511 uuid_dec_be(beuuid, &uuid);
512
513 for (pn = 0; pn < gh.gh_part_num; pn++) {
514 if (uuid_is_nil(&gp[pn].gp_type, NULL))
515 break;
516 }
517 if (pn == gh.gh_part_num)
518 goto done;
519
520 rslt = lba_free(&start, &end);
521 if (rslt == -1)
522 goto done;
523
524 if (start % BLOCKALIGNMENT)
525 start += (BLOCKALIGNMENT - start % BLOCKALIGNMENT);
526 if (start >= end)
527 goto done;
528
529 freesectors = end - start + 1;
530
531 if (sectors == 0)
532 sectors = freesectors;
533
534 if (freesectors < sectors)
535 goto done;
536 else if (freesectors > sectors)
537 end = start + sectors - 1;
538
539 gp[pn].gp_type = uuid;
540 gp[pn].gp_lba_start = start;
541 gp[pn].gp_lba_end = end;
542 string_to_name(pn, name);
543
544 uuid_create(&gp[pn].gp_guid, &status);
545 if (status == uuid_s_ok)
546 return 0;
547
548 done:
549 if (pn != gh.gh_part_num)
550 memset(&gp[pn], 0, sizeof(gp[pn]));
551 printf("unable to add %s\n", name);
552 return -1;
553 }
554
555 int
init_gh(void)556 init_gh(void)
557 {
558 struct gpt_header oldgh;
559 const int secsize = dl.d_secsize;
560 int needed;
561 uint32_t status;
562
563 memcpy(&oldgh, &gh, sizeof(oldgh));
564 memset(&gh, 0, sizeof(gh));
565 memset(&gmbr, 0, sizeof(gmbr));
566
567 /* XXX Do we need the boot code? UEFI spec & Apple says no. */
568 memcpy(gmbr.mbr_code, default_dmbr.dmbr_boot, sizeof(gmbr.mbr_code));
569 gmbr.mbr_prt[0].prt_id = DOSPTYP_EFI;
570 gmbr.mbr_prt[0].prt_bs = 1;
571 gmbr.mbr_prt[0].prt_ns = UINT32_MAX;
572 gmbr.mbr_signature = DOSMBR_SIGNATURE;
573
574 needed = sizeof(gp) / secsize + 2;
575
576 if (needed % BLOCKALIGNMENT)
577 needed += (needed - (needed % BLOCKALIGNMENT));
578
579 gh.gh_sig = GPTSIGNATURE;
580 gh.gh_rev = GPTREVISION;
581 gh.gh_size = GPTMINHDRSIZE;
582 gh.gh_csum = 0;
583 gh.gh_rsvd = 0;
584 gh.gh_lba_self = 1;
585 gh.gh_lba_alt = DL_GETDSIZE(&dl) - 1;
586 gh.gh_lba_start = needed;
587 gh.gh_lba_end = DL_GETDSIZE(&dl) - needed;
588 uuid_create(&gh.gh_guid, &status);
589 if (status != uuid_s_ok) {
590 memcpy(&gh, &oldgh, sizeof(gh));
591 return -1;
592 }
593 gh.gh_part_lba = 2;
594 gh.gh_part_num = NGPTPARTITIONS;
595 gh.gh_part_size = GPTMINPARTSIZE;
596 gh.gh_part_csum = 0;
597
598 return 0;
599 }
600
601 int
init_gp(const int how)602 init_gp(const int how)
603 {
604 struct gpt_partition oldgp[NGPTPARTITIONS];
605 const uint8_t gpt_uuid_efi_system[] = GPT_UUID_EFI_SYSTEM;
606 const uint8_t gpt_uuid_openbsd[] = GPT_UUID_OPENBSD;
607 uint64_t prt_ns;
608 int pn, rslt;
609
610 memcpy(&oldgp, &gp, sizeof(oldgp));
611 if (how == GHANDGP)
612 memset(&gp, 0, sizeof(gp));
613 else {
614 for (pn = 0; pn < gh.gh_part_num; pn++) {
615 if (PRT_protected_uuid(&gp[pn].gp_type) ||
616 (gp[pn].gp_attrs & GPTPARTATTR_REQUIRED))
617 continue;
618 memset(&gp[pn], 0, sizeof(gp[pn]));
619 }
620 }
621
622 rslt = 0;
623 if (disk.dk_bootprt.prt_ns > 0) {
624 pn = find_partition(gpt_uuid_efi_system);
625 if (pn == -1) {
626 rslt = add_partition(gpt_uuid_efi_system,
627 "EFI System Area", disk.dk_bootprt.prt_ns);
628 } else {
629 prt_ns = gp[pn].gp_lba_end - gp[pn].gp_lba_start + 1;
630 if (prt_ns < disk.dk_bootprt.prt_ns) {
631 printf("EFI System Area < %llu sectors\n",
632 disk.dk_bootprt.prt_ns);
633 rslt = -1;
634 }
635 }
636 }
637 if (rslt == 0)
638 rslt = add_partition(gpt_uuid_openbsd, "OpenBSD Area", 0);
639
640 if (rslt != 0)
641 memcpy(&gp, &oldgp, sizeof(gp));
642
643 return rslt;
644 }
645
646 int
GPT_init(const int how)647 GPT_init(const int how)
648 {
649 int rslt = 0;
650
651 if (how == GHANDGP)
652 rslt = init_gh();
653 if (rslt == 0)
654 rslt = init_gp(how);
655
656 return rslt;
657 }
658
659 void
GPT_zap_headers(void)660 GPT_zap_headers(void)
661 {
662 struct gpt_header legh;
663
664 if (DISK_readbytes(&legh, GPTSECTOR, sizeof(legh)))
665 return;
666
667 if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
668 memset(&legh, 0, sizeof(legh));
669 if (DISK_writebytes(&legh, GPTSECTOR, sizeof(legh)))
670 DPRINTF("Unable to zap GPT header @ sector %d",
671 GPTSECTOR);
672 }
673
674 if (DISK_readbytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
675 return;
676
677 if (letoh64(legh.gh_sig) == GPTSIGNATURE) {
678 memset(&legh, 0, GPTMINHDRSIZE);
679 if (DISK_writebytes(&legh, DL_GETDSIZE(&dl) - 1, sizeof(legh)))
680 DPRINTF("Unable to zap GPT header @ sector %llu",
681 DL_GETDSIZE(&dl) - 1);
682 }
683 }
684
685 int
GPT_write(void)686 GPT_write(void)
687 {
688 struct gpt_header legh;
689 struct gpt_partition *legp;
690 uint64_t altgh, altgp;
691 uint64_t gpbytes, gpsectors;
692 unsigned int pn;
693 int rslt = -1;
694
695 if (MBR_write(&gmbr))
696 return -1;
697
698 gpbytes = gh.gh_part_num * gh.gh_part_size;
699 gpsectors = (gpbytes + dl.d_secsize - 1) / dl.d_secsize;
700
701 altgh = DL_GETDSIZE(&dl) - 1;
702 altgp = altgh - gpsectors;
703
704 legh.gh_sig = htole64(GPTSIGNATURE);
705 legh.gh_rev = htole32(GPTREVISION);
706 legh.gh_size = htole32(GPTMINHDRSIZE);
707 legh.gh_rsvd = 0;
708 legh.gh_lba_self = htole64(GPTSECTOR);
709 legh.gh_lba_alt = htole64(altgh);
710 legh.gh_lba_start = htole64(gh.gh_lba_start);
711 legh.gh_lba_end = htole64(gh.gh_lba_end);
712 uuid_enc_le(&legh.gh_guid, &gh.gh_guid);
713 legh.gh_part_lba = htole64(GPTSECTOR + 1);
714 legh.gh_part_num = htole32(gh.gh_part_num);
715 legh.gh_part_size = htole32(GPTMINPARTSIZE);
716
717 legp = calloc(1, gpbytes);
718 if (legp == NULL)
719 err(1, "legp");
720
721 for (pn = 0; pn < gh.gh_part_num; pn++) {
722 uuid_enc_le(&legp[pn].gp_type, &gp[pn].gp_type);
723 uuid_enc_le(&legp[pn].gp_guid, &gp[pn].gp_guid);
724 legp[pn].gp_lba_start = htole64(gp[pn].gp_lba_start);
725 legp[pn].gp_lba_end = htole64(gp[pn].gp_lba_end);
726 legp[pn].gp_attrs = htole64(gp[pn].gp_attrs);
727 memcpy(legp[pn].gp_name, gp[pn].gp_name,
728 sizeof(legp[pn].gp_name));
729 }
730 legh.gh_part_csum = htole32(crc32((unsigned char *)legp, gpbytes));
731 legh.gh_csum = 0;
732 legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
733
734 if (DISK_writebytes(&legh, GPTSECTOR, gh.gh_size) ||
735 DISK_writebytes(legp, GPTSECTOR + 1, gpbytes))
736 goto done;
737
738 legh.gh_lba_self = htole64(altgh);
739 legh.gh_lba_alt = htole64(GPTSECTOR);
740 legh.gh_part_lba = htole64(altgp);
741 legh.gh_csum = 0;
742 legh.gh_csum = htole32(crc32((unsigned char *)&legh, gh.gh_size));
743
744 if (DISK_writebytes(&legh, altgh, gh.gh_size) ||
745 DISK_writebytes(&gp, altgp, gpbytes))
746 goto done;
747
748 /* Refresh in-kernel disklabel from the updated disk information. */
749 if (ioctl(disk.dk_fd, DIOCRLDINFO, 0) == -1)
750 warn("DIOCRLDINFO");
751 rslt = 0;
752
753 done:
754 free(legp);
755 return rslt;
756 }
757
758 int
gp_lba_start_cmp(const void * e1,const void * e2)759 gp_lba_start_cmp(const void *e1, const void *e2)
760 {
761 struct gpt_partition *p1 = *(struct gpt_partition **)e1;
762 struct gpt_partition *p2 = *(struct gpt_partition **)e2;
763 uint64_t o1;
764 uint64_t o2;
765
766 o1 = p1->gp_lba_start;
767 o2 = p2->gp_lba_start;
768
769 if (o1 < o2)
770 return -1;
771 else if (o1 > o2)
772 return 1;
773 else
774 return 0;
775 }
776
777 const struct gpt_partition * const *
sort_gpt(void)778 sort_gpt(void)
779 {
780 static const struct gpt_partition *sgp[NGPTPARTITIONS+2];
781 unsigned int i, pn;
782
783 memset(sgp, 0, sizeof(sgp));
784
785 i = 0;
786 for (pn = 0; pn < gh.gh_part_num; pn++) {
787 if (gp[pn].gp_lba_start >= gh.gh_lba_start)
788 sgp[i++] = &gp[pn];
789 }
790
791 if (i > 1) {
792 if (mergesort(sgp, i, sizeof(sgp[0]), gp_lba_start_cmp) == -1) {
793 printf("unable to sort gpt by lba start\n");
794 return NULL;
795 }
796 }
797
798 return sgp;
799 }
800
801 int
lba_free(uint64_t * start,uint64_t * end)802 lba_free(uint64_t *start, uint64_t *end)
803 {
804 const struct gpt_partition * const *sgp;
805 uint64_t bs, bigbs, nextbs, ns;
806 unsigned int i;
807
808 sgp = sort_gpt();
809 if (sgp == NULL)
810 return -1;
811
812 bs = gh.gh_lba_start;
813 ns = gh.gh_lba_end - bs + 1;
814
815 if (sgp[0] != NULL) {
816 bigbs = bs;
817 ns = 0;
818 for (i = 0; sgp[i] != NULL; i++) {
819 nextbs = sgp[i]->gp_lba_start;
820 if (bs < nextbs && ns < nextbs - bs) {
821 ns = nextbs - bs;
822 bigbs = bs;
823 }
824 bs = sgp[i]->gp_lba_end + 1;
825 }
826 nextbs = gh.gh_lba_end + 1;
827 if (bs < nextbs && ns < nextbs - bs) {
828 ns = nextbs - bs;
829 bigbs = bs;
830 }
831 bs = bigbs;
832 }
833
834 if (ns == 0)
835 return -1;
836
837 if (start != NULL)
838 *start = bs;
839 if (end != NULL)
840 *end = bs + ns - 1;
841
842 return 0;
843 }
844
845 int
GPT_get_lba_start(const unsigned int pn)846 GPT_get_lba_start(const unsigned int pn)
847 {
848 uint64_t bs;
849 unsigned int i;
850 int rslt;
851
852 bs = gh.gh_lba_start;
853
854 if (gp[pn].gp_lba_start >= bs) {
855 bs = gp[pn].gp_lba_start;
856 } else {
857 rslt = lba_free(&bs, NULL);
858 if (rslt == -1) {
859 printf("no space for partition %u\n", pn);
860 return -1;
861 }
862 }
863
864 bs = getuint64("Partition offset", bs, gh.gh_lba_start, gh.gh_lba_end);
865 for (i = 0; i < gh.gh_part_num; i++) {
866 if (i == pn)
867 continue;
868 if (bs >= gp[i].gp_lba_start && bs <= gp[i].gp_lba_end) {
869 printf("partition %u can't start inside partition %u\n",
870 pn, i);
871 return -1;
872 }
873 }
874
875 gp[pn].gp_lba_start = bs;
876
877 return 0;
878 }
879
880 int
GPT_get_lba_end(const unsigned int pn)881 GPT_get_lba_end(const unsigned int pn)
882 {
883 const struct gpt_partition * const *sgp;
884 uint64_t bs, nextbs, ns;
885 unsigned int i;
886
887 sgp = sort_gpt();
888 if (sgp == NULL)
889 return -1;
890
891 bs = gp[pn].gp_lba_start;
892 ns = gh.gh_lba_end - bs + 1;
893 for (i = 0; sgp[i] != NULL; i++) {
894 nextbs = sgp[i]->gp_lba_start;
895 if (nextbs > bs) {
896 ns = nextbs - bs;
897 break;
898 }
899 }
900 ns = getuint64("Partition size", ns, 1, ns);
901
902 gp[pn].gp_lba_end = bs + ns - 1;
903
904 return 0;
905 }
906
907 int
GPT_get_name(const unsigned int pn)908 GPT_get_name(const unsigned int pn)
909 {
910 char name[GPTPARTNAMESIZE + 1];
911
912 printf("Partition name: [%s] ", name_to_string(pn));
913 string_from_line(name, sizeof(name), UNTRIMMED);
914
915 switch (strlen(name)) {
916 case 0:
917 break;
918 case GPTPARTNAMESIZE:
919 printf("partition name must be < %d characters\n",
920 GPTPARTNAMESIZE);
921 return -1;
922 default:
923 string_to_name(pn, name);
924 break;
925 }
926
927 return 0;
928 }
929
930 /*
931 * Adapted from Hacker's Delight crc32b().
932 *
933 * To quote http://www.hackersdelight.org/permissions.htm :
934 *
935 * "You are free to use, copy, and distribute any of the code on
936 * this web site, whether modified by you or not. You need not give
937 * attribution. This includes the algorithms (some of which appear
938 * in Hacker's Delight), the Hacker's Assistant, and any code submitted
939 * by readers. Submitters implicitly agree to this."
940 */
941 uint32_t
crc32(const u_char * buf,const uint32_t size)942 crc32(const u_char *buf, const uint32_t size)
943 {
944 int j;
945 uint32_t i, byte, crc, mask;
946
947 crc = 0xFFFFFFFF;
948
949 for (i = 0; i < size; i++) {
950 byte = buf[i]; /* Get next byte. */
951 crc = crc ^ byte;
952 for (j = 7; j >= 0; j--) { /* Do eight times. */
953 mask = -(crc & 1);
954 crc = (crc >> 1) ^ (0xEDB88320 & mask);
955 }
956 }
957
958 return ~crc;
959 }
960