1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (c) 2019, Joyent, Inc.
14 * Copyright 2024 MNX Cloud, Inc.
15 */
16
17 /*
18 * The on-disk elements here are all little-endian, and this code doesn't make
19 * any attempt to adjust for running on a big-endian system.
20 */
21
22 #include <sys/types.h>
23 #include <sys/crc32.h>
24 #include <sys/debug.h>
25 #include <sys/sysmacros.h>
26 #include <sys/dktp/fdisk.h>
27 #include <sys/efi_partition.h>
28 #include <sys/fs/pc_fs.h>
29 #include <sys/vtoc.h>
30
31 #include <assert.h>
32 #include <ctype.h>
33 #include <uuid/uuid.h>
34 #include <stdbool.h>
35
36 #include <mdb/mdb_modapi.h>
37 #include <mdb/mdb_debug.h>
38
39 #include "installboot.h"
40
41 #ifdef _BIG_ENDIAN
42 #error needs porting for big-endian system
43 #endif
44
45 /* See usr/src/grub/grub-0.97/stage1/stage1.h */
46 #define GRUB_VERSION_OFF (0x3e)
47 #define GRUB_COMPAT_VERSION_MAJOR 3
48 #define GRUB_COMPAT_VERSION_MINOR 2
49 #define GRUB_VERSION (2 << 8 | 3) /* 3.2 */
50
51 #define LOADER_VERSION (1)
52 #define LOADER_JOYENT_VERSION (2)
53
54 typedef enum {
55 MBR_TYPE_UNKNOWN,
56 MBR_TYPE_GRUB1,
57 MBR_TYPE_LOADER,
58 MBR_TYPE_LOADER_JOYENT,
59 } mbr_type_t;
60
61 typedef struct stringval {
62 const char *sv_text;
63 int sv_value;
64 } stringval_t;
65
66 stringval_t ptag_array[] = {
67 { "unassigned", V_UNASSIGNED },
68 { "boot", V_BOOT },
69 { "root", V_ROOT },
70 { "swap", V_SWAP },
71 { "usr", V_USR },
72 { "backup", V_BACKUP },
73 { "stand", V_STAND },
74 { "var", V_VAR },
75 { "home", V_HOME },
76 { "alternates", V_ALTSCTR },
77 { "reserved", V_RESERVED },
78 { "system", V_SYSTEM },
79 { "BIOS_boot", V_BIOS_BOOT },
80 { "FreeBSD boot", V_FREEBSD_BOOT },
81 { "FreeBSD swap", V_FREEBSD_SWAP },
82 { "FreeBSD UFS", V_FREEBSD_UFS },
83 { "FreeBSD ZFS", V_FREEBSD_ZFS },
84 { "FreeBSD NANDFS", V_FREEBSD_NANDFS },
85
86 { NULL }
87 };
88
89 stringval_t pflag_array[] = {
90 { "wm", 0 },
91 { "wu", V_UNMNT },
92 { "rm", V_RONLY },
93 { "ru", V_RONLY | V_UNMNT },
94 { NULL }
95 };
96
97 size_t sector_size = SECTOR_SIZE;
98
99 static const char *
array_find_string(stringval_t * array,int match_value)100 array_find_string(stringval_t *array, int match_value)
101 {
102 for (; array->sv_text != NULL; array++) {
103 if (array->sv_value == match_value) {
104 return (array->sv_text);
105 }
106 }
107
108 return (NULL);
109 }
110
111 static int
array_widest_str(stringval_t * array)112 array_widest_str(stringval_t *array)
113 {
114 int i;
115 int width;
116
117 width = 0;
118 for (; array->sv_text != NULL; array++) {
119 if ((i = strlen(array->sv_text)) > width)
120 width = i;
121 }
122
123 return (width);
124 }
125
126 static void
print_fdisk_part(struct ipart * ip,size_t nr)127 print_fdisk_part(struct ipart *ip, size_t nr)
128 {
129 char typestr[128];
130 char begchs[128];
131 char endchs[128];
132 char *c = NULL;
133
134 if (ip->systid == UNUSED) {
135 mdb_printf("%-4llu %s:%#lx\n", nr, "UNUSED", ip->systid);
136 return;
137 }
138
139 switch (ip->systid) {
140 case DOSOS12: c = "DOSOS12"; break;
141 case PCIXOS: c = "PCIXOS"; break;
142 case DOSOS16: c = "DOSOS16"; break;
143 case EXTDOS: c = "EXTDOS"; break;
144 case DOSHUGE: c = "DOSHUGE"; break;
145 case FDISK_IFS: c = "FDISK_IFS"; break;
146 case FDISK_AIXBOOT: c = "FDISK_AIXBOOT"; break;
147 case FDISK_AIXDATA: c = "FDISK_AIXDATA"; break;
148 case FDISK_OS2BOOT: c = "FDISK_OS2BOOT"; break;
149 case FDISK_WINDOWS: c = "FDISK_WINDOWS"; break;
150 case FDISK_EXT_WIN: c = "FDISK_EXT_WIN"; break;
151 case FDISK_FAT95: c = "FDISK_FAT95"; break;
152 case FDISK_EXTLBA: c = "FDISK_EXTLBA"; break;
153 case DIAGPART: c = "DIAGPART"; break;
154 case FDISK_LINUX: c = "FDISK_LINUX"; break;
155 case FDISK_LINUXDSWAP: c = "FDISK_LINUXDSWAP"; break;
156 case FDISK_LINUXDNAT: c = "FDISK_LINUXDNAT"; break;
157 case FDISK_CPM: c = "FDISK_CPM"; break;
158 case DOSDATA: c = "DOSDATA"; break;
159 case OTHEROS: c = "OTHEROS"; break;
160 case UNIXOS: c = "UNIXOS"; break;
161 case FDISK_NOVELL2: c = "FDISK_NOVELL2"; break;
162 case FDISK_NOVELL3: c = "FDISK_NOVELL3"; break;
163 case FDISK_QNX4: c = "FDISK_QNX4"; break;
164 case FDISK_QNX42: c = "FDISK_QNX42"; break;
165 case FDISK_QNX43: c = "FDISK_QNX43"; break;
166 case SUNIXOS: c = "SUNIXOS"; break;
167 case FDISK_LINUXNAT: c = "FDISK_LINUXNAT"; break;
168 case FDISK_NTFSVOL1: c = "FDISK_NTFSVOL1"; break;
169 case FDISK_NTFSVOL2: c = "FDISK_NTFSVOL2"; break;
170 case FDISK_BSD: c = "FDISK_BSD"; break;
171 case FDISK_NEXTSTEP: c = "FDISK_NEXTSTEP"; break;
172 case FDISK_BSDIFS: c = "FDISK_BSDIFS"; break;
173 case FDISK_BSDISWAP: c = "FDISK_BSDISWAP"; break;
174 case X86BOOT: c = "X86BOOT"; break;
175 case SUNIXOS2: c = "SUNIXOS2"; break;
176 case EFI_PMBR: c = "EFI_PMBR"; break;
177 case EFI_FS: c = "EFI_FS"; break;
178 default: c = NULL; break;
179 }
180
181 if (c != NULL) {
182 mdb_snprintf(typestr, sizeof (typestr), "%s:%#lx",
183 c, ip->systid);
184 } else {
185 mdb_snprintf(typestr, sizeof (typestr), "%#lx", ip->systid);
186 }
187
188 mdb_snprintf(begchs, sizeof (begchs), "%hu/%hu/%hu",
189 (uint16_t)ip->begcyl | (uint16_t)(ip->begsect & ~0x3f) << 2,
190 (uint16_t)ip->beghead, (uint16_t)ip->begsect & 0x3f);
191 mdb_snprintf(endchs, sizeof (endchs), "%hu/%hu/%hu",
192 (uint16_t)ip->endcyl | (uint16_t)(ip->endsect & ~0x3f) << 2,
193 (uint16_t)ip->endhead, (uint16_t)ip->endsect & 0x3f);
194
195 mdb_printf("%-4llu %-21s %#-7x %-11s %-11s %-10u %-9u\n",
196 nr, typestr, ip->bootid, begchs, endchs, ip->relsect, ip->numsect);
197 }
198
199 /*
200 * Based on pcfs driver and:
201 * "Microsoft Extensible Firmware Initiative FAT32 File System Specification,
202 * FAT: General Overview of On-Disk Format"
203 *
204 * http://download.microsoft.com/download/1/6/1/
205 * 161ba512-40e2-4cc9-843a-923143f3456c/fatgen103.doc
206 */
207 static void
show_bpb(char * bpb)208 show_bpb(char *bpb)
209 {
210 bool fat32 = false;
211 uint32_t reserved, rdirsec, spc;
212 uint32_t rec, fsisec, bkbootsec;
213 uint32_t totsec16, totsec32, totsec;
214 uint32_t fatsec16, fatsec32, fatsec;
215 uint32_t numfat, datasec;
216 uint32_t ncl;
217 char buf[12];
218
219 mdb_printf("\n");
220 mdb_printf("BPB JMP: ");
221 if (VALID_JMPBOOT(bpb_jmpBoot(bpb))) {
222 mdb_printf("valid");
223 } else {
224 mdb_printf("invalid");
225 }
226 mdb_printf("\n");
227
228 mdb_printf("BPB OEMName: ");
229 if (VALID_OEMNAME(bpb_OEMName(bpb))) {
230 mdb_printf("valid");
231 mdb_printf(" : %*s", 8, bpb_OEMName(bpb));
232 } else {
233 mdb_printf("invalid");
234 }
235 mdb_printf("\n");
236
237 mdb_printf("BPB Bytes per Sector: ");
238 if (VALID_SECSIZE(bpb_get_BytesPerSec(bpb))) {
239 mdb_printf("valid");
240 } else {
241 mdb_printf("invalid");
242 }
243 mdb_printf(" : %hu", bpb_get_BytesPerSec(bpb));
244 mdb_printf("\n");
245
246 mdb_printf("BPB Sectors per Cluster: ");
247 spc = bpb_get_SecPerClus(bpb);
248 if (VALID_SPCL(spc)) {
249 mdb_printf("valid");
250 } else {
251 mdb_printf("invalid");
252 }
253 mdb_printf(" : %u", spc);
254 mdb_printf("\n");
255
256 reserved = bpb_get_RsvdSecCnt(bpb);
257 mdb_printf("BPB Reserved Sectors: ");
258 if (VALID_RSVDSEC(reserved)) {
259 mdb_printf("valid");
260 } else {
261 mdb_printf("invalid");
262 }
263 mdb_printf(" : %u", reserved);
264 mdb_printf("\n");
265
266 mdb_printf("BPB Number of FATs: ");
267 numfat = bpb_get_NumFATs(bpb);
268 if (VALID_NUMFATS(numfat)) {
269 mdb_printf("valid");
270 } else {
271 mdb_printf("invalid");
272 }
273 mdb_printf(" : %u", numfat);
274 mdb_printf("\n");
275
276 rec = bpb_get_RootEntCnt(bpb);
277 mdb_printf("BPB Root Entry Count: ");
278 mdb_printf("%u", rec);
279 mdb_printf("\n");
280
281 totsec16 = bpb_get_TotSec16(bpb);
282 mdb_printf("BPB Total Sectors 16: ");
283 mdb_printf("%u", totsec16);
284 mdb_printf("\n");
285
286 mdb_printf("BPB Media Type: ");
287 if (VALID_MEDIA(bpb_get_Media(bpb))) {
288 mdb_printf("valid");
289 } else {
290 mdb_printf("invalid");
291 }
292 mdb_printf(" : 0x%02x", bpb_get_Media(bpb));
293 mdb_printf("\n");
294
295 fatsec16 = bpb_get_FatSz16(bpb);
296 mdb_printf("BPB FAT Sectors 16: ");
297 mdb_printf("%u", fatsec16);
298 mdb_printf("\n");
299
300 mdb_printf("BPB Sectors Per Track: ");
301 mdb_printf("%u", bpb_get_SecPerTrk(bpb));
302 mdb_printf("\n");
303
304 mdb_printf("BPB Number of Heads: ");
305 mdb_printf("%u", bpb_get_NumHeads(bpb));
306 mdb_printf("\n");
307
308 mdb_printf("BPB Hidden Sectors: ");
309 mdb_printf("%u", bpb_get_HiddSec(bpb));
310 mdb_printf("\n");
311
312 totsec32 = bpb_get_TotSec32(bpb);
313 mdb_printf("BPB Total Sectors 32: ");
314 mdb_printf("%u", totsec32);
315 mdb_printf("\n");
316
317 mdb_printf("BPB Signature: ");
318 if (VALID_BPBSIG(bpb_get_BPBSig(bpb))) {
319 mdb_printf("valid");
320 } else {
321 mdb_printf("invalid");
322 }
323 mdb_printf("\n");
324 /*
325 * This does conclude the legacy BPB fields.
326 * FAT12/FAT16 and FAT32 have overlapping fields,
327 * and we need to determine the fat type to decide which
328 * alternative we will print (if any).
329 */
330 fatsec32 = bpb_get_FatSz32(bpb);
331 if (totsec16 == 0 && fatsec16 == 0) {
332 totsec = totsec32;
333 fatsec = fatsec32;
334 fat32 = true; /* there is no FAT12/FAT16 */
335 } else {
336 totsec = totsec16;
337 fatsec = fatsec16;
338 }
339 if (totsec == 0 || fatsec == 0) {
340 mdb_printf("There is no FAT file system\n");
341 return;
342 }
343 if (!fat32) {
344 mdb_printf("BPB Drive Number: ");
345 mdb_printf("0x%02x", bpb_get_DrvNum16(bpb));
346 mdb_printf("\n");
347
348 mdb_printf("BPB Extended Boot Signature: ");
349 mdb_printf("0x%02x", bpb_get_BootSig16(bpb));
350 mdb_printf("\n");
351
352 if (bpb_get_BootSig16(bpb) == 0x29) {
353 mdb_printf("BPB Volume ID: ");
354 mdb_printf("0x%04x", bpb_get_VolID16(bpb));
355 mdb_printf("\n");
356
357 mdb_printf("BPB Volume Label: ");
358 bcopy(bpb_VolLab16(bpb), buf, 11);
359 buf[11] = '\0';
360 mdb_printf("\"%s\"", buf);
361 mdb_printf("\n");
362
363 mdb_printf("BPB File System Type: ");
364 if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb))) {
365 mdb_printf("valid");
366 } else {
367 mdb_printf("invalid");
368 }
369 bcopy(bpb_FilSysType16(bpb), buf, 8);
370 buf[8] = '\0';
371 mdb_printf(" : \"%s\"", buf);
372 mdb_printf("\n");
373 }
374 } else {
375 mdb_printf("BPB FAT Sectors 32: ");
376 mdb_printf("%u", fatsec32);
377 mdb_printf("\n");
378
379 mdb_printf("BPB Extended Flags 32: ");
380 mdb_printf("0x%04x", bpb_get_ExtFlags32(bpb));
381 mdb_printf("\n");
382
383 mdb_printf("BPB FS Version: ");
384 mdb_printf("0x%02x", bpb_get_FSVer32(bpb));
385 mdb_printf("\n");
386
387 mdb_printf("BPB Root Cluster: ");
388 mdb_printf("%u", bpb_get_RootClus32(bpb));
389 mdb_printf("\n");
390
391 fsisec = bpb_get_FSInfo32(bpb);
392 mdb_printf("BPB FS Info Sector: ");
393 mdb_printf("%u", fsisec);
394 mdb_printf("\n");
395
396 bkbootsec = bpb_get_BkBootSec32(bpb);
397 mdb_printf("BPB Backup Boot Sector: ");
398 mdb_printf("%u", bkbootsec);
399 mdb_printf("\n");
400
401 mdb_printf("BPB Drive Number: ");
402 mdb_printf("0x%02x", bpb_get_DrvNum32(bpb));
403 mdb_printf("\n");
404
405 mdb_printf("BPB Boot Signature: ");
406 mdb_printf("0x%02x", bpb_get_BootSig32(bpb));
407 mdb_printf("\n");
408
409 if (bpb_get_BootSig32(bpb) == 0x29) {
410 mdb_printf("BPB Volume ID: ");
411 mdb_printf("0x%04x", bpb_get_VolID32(bpb));
412 mdb_printf("\n");
413
414 mdb_printf("BPB Volume Label: ");
415 bcopy(bpb_VolLab32(bpb), buf, 11);
416 buf[11] = '\0';
417 mdb_printf("\"%s\"", buf);
418 mdb_printf("\n");
419
420 mdb_printf("BPB File System Type: ");
421 if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb))) {
422 mdb_printf("valid");
423 } else {
424 mdb_printf("invalid");
425 }
426 bcopy(bpb_FilSysType32(bpb), buf, 8);
427 buf[8] = '\0';
428 mdb_printf(" : \"%s\"", buf);
429 mdb_printf("\n");
430 }
431 }
432
433 rdirsec = (rec * 32 + (sector_size - 1)) / sector_size;
434 datasec = totsec - fatsec * numfat - rdirsec - reserved;
435 ncl = datasec / spc;
436 mdb_printf("count of clusters on the volume: %u\n", ncl);
437 if (ncl < 4085)
438 mdb_printf("Volume should be FAT12\n");
439 else if (ncl < 65525)
440 mdb_printf("Volume should be FAT16\n");
441 else
442 mdb_printf("Volume should be FAT32\n");
443 }
444
445 static mbr_type_t
mbr_info(struct mboot * mbr,uint_t bpb)446 mbr_info(struct mboot *mbr, uint_t bpb)
447 {
448 mbr_type_t type = MBR_TYPE_UNKNOWN;
449
450 if (*((uint16_t *)&mbr->bootinst[GRUB_VERSION_OFF]) == GRUB_VERSION) {
451 type = MBR_TYPE_GRUB1;
452 } else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_VERSION) {
453 type = MBR_TYPE_LOADER;
454 } else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_JOYENT_VERSION) {
455 type = MBR_TYPE_LOADER_JOYENT;
456 }
457
458 switch (type) {
459 case MBR_TYPE_UNKNOWN:
460 mdb_printf("Format: unknown\n");
461 break;
462 case MBR_TYPE_GRUB1:
463 mdb_printf("Format: grub1\n");
464 break;
465 case MBR_TYPE_LOADER:
466 mdb_printf("Format: loader (illumos)\n");
467 break;
468 case MBR_TYPE_LOADER_JOYENT:
469 mdb_printf("Format: loader (joyent)\n");
470 break;
471 }
472
473 mdb_printf("Signature: 0x%hx (%s)\n", mbr->signature,
474 mbr->signature == MBB_MAGIC ? "valid" : "invalid");
475
476 mdb_printf("UniqueMBRDiskSignature: %#lx\n",
477 *(uint32_t *)&mbr->bootinst[STAGE1_SIG]);
478
479 if (type == MBR_TYPE_LOADER || type == MBR_TYPE_LOADER_JOYENT) {
480 char uuid[UUID_PRINTABLE_STRING_LENGTH];
481
482 mdb_printf("Loader STAGE1_STAGE2_LBA: %llu\n",
483 *(uint64_t *)&mbr->bootinst[STAGE1_STAGE2_LBA]);
484
485 mdb_printf("Loader STAGE1_STAGE2_SIZE: %hu\n",
486 *(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE]);
487
488 uuid_unparse((uchar_t *)&mbr->bootinst[STAGE1_STAGE2_UUID],
489 uuid);
490
491 mdb_printf("Loader STAGE1_STAGE2_UUID: %s\n", uuid);
492 }
493
494 if (bpb) {
495 show_bpb(mbr->bootinst);
496 }
497 return (type);
498 }
499
500 static void
mbr_help(void)501 mbr_help(void)
502 {
503 mdb_printf("Display a Master Boot Record.\n\n"
504 "-b Show BIOS Parameter Block (BPB)\n");
505 }
506
507 static int
cmd_mbr(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)508 cmd_mbr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
509 {
510 struct mboot *mbr;
511 mbr_type_t type;
512 uint_t opt_b = FALSE;
513
514 CTASSERT(sizeof (*mbr) == SECTOR_SIZE);
515
516 if (mdb_getopts(argc, argv,
517 'b', MDB_OPT_SETBITS, TRUE, &opt_b, NULL) != argc)
518 return (DCMD_USAGE);
519
520 if (!(flags & DCMD_ADDRSPEC))
521 addr = 0;
522
523 mbr = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
524
525 if (mdb_vread(mbr, sector_size, addr) == -1) {
526 mdb_warn("failed to read MBR");
527 return (DCMD_ERR);
528 }
529
530 if (VALID_SECSIZE(bpb_get_BytesPerSec(mbr->bootinst))) {
531 sector_size = bpb_get_BytesPerSec(mbr->bootinst);
532 }
533 type = mbr_info(mbr, opt_b);
534
535 /* If the magic is wrong, stop here. */
536 if (mbr->signature != MBB_MAGIC)
537 return (DCMD_ERR);
538
539 /* Also print volume boot record */
540 switch (type) {
541 case MBR_TYPE_LOADER:
542 case MBR_TYPE_LOADER_JOYENT:
543 if (*(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE] == 1) {
544 struct mboot vbr;
545 uintptr_t vbrp;
546
547 vbrp = *(uint64_t *)&mbr->bootinst[STAGE1_STAGE2_LBA];
548 vbrp *= sector_size;
549 vbrp += addr;
550 if (mdb_vread(&vbr, sizeof (vbr), vbrp) == -1) {
551 mdb_warn("failed to read VBR");
552 } else {
553 mdb_printf("\nSTAGE1 in VBR:\n");
554 (void) mbr_info(&vbr, opt_b);
555 }
556 }
557 break;
558 default:
559 break;
560 }
561
562 if (*(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE] == 1) {
563 /*
564 * This is MBR, display partition information.
565 */
566 mdb_printf(
567 "\n%<u>%-4s %-21s %-7s %-11s %-11s %-10s %-9s%</u>\n",
568 "PART", "TYPE", "ACTIVE", "STARTCHS", "ENDCHS",
569 "SECTOR", "NUMSECT");
570
571 for (size_t i = 0; i < FD_NUMPART; i++) {
572 struct ipart *ip = (struct ipart *)
573 (mbr->parts + (sizeof (struct ipart) * i));
574 print_fdisk_part(ip, i);
575 }
576 }
577
578 return (DCMD_OK);
579 }
580
581 static unsigned int crc32_tab[] = { CRC32_TABLE };
582
583 static unsigned int
efi_crc32(const unsigned char * s,unsigned int len)584 efi_crc32(const unsigned char *s, unsigned int len)
585 {
586 unsigned int crc32val;
587
588 CRC32(crc32val, s, len, -1U, crc32_tab);
589
590 return (crc32val ^ -1U);
591 }
592
593 typedef struct {
594 struct uuid eg_uuid;
595 const char *eg_name;
596 } efi_guid_t;
597
598 static efi_guid_t efi_guids[] = {
599 { EFI_UNUSED, "EFI_UNUSED" },
600 { EFI_RESV1, "EFI_RESV1" },
601 { EFI_BOOT, "EFI_BOOT" },
602 { EFI_ROOT, "EFI_ROOT" },
603 { EFI_SWAP, "EFI_SWAP" },
604 { EFI_USR, "EFI_USR" },
605 { EFI_BACKUP, "EFI_BACKUP" },
606 { EFI_RESV2, "EFI_RESV2" },
607 { EFI_VAR, "EFI_VAR" },
608 { EFI_HOME, "EFI_HOME" },
609 { EFI_ALTSCTR, "EFI_ALTSCTR" },
610 { EFI_RESERVED, "EFI_RESERVED" },
611 { EFI_SYSTEM, "EFI_SYSTEM" },
612 { EFI_LEGACY_MBR, "EFI_LEGACY_MBR" },
613 { EFI_SYMC_PUB, "EFI_SYMC_PUB" },
614 { EFI_SYMC_CDS, "EFI_SYMC_CDS" },
615 { EFI_MSFT_RESV, "EFI_MSFT_RESV" },
616 { EFI_DELL_BASIC, "EFI_DELL_BASIC" },
617 { EFI_DELL_RAID, "EFI_DELL_RAID" },
618 { EFI_DELL_SWAP, "EFI_DELL_SWAP" },
619 { EFI_DELL_LVM, "EFI_DELL_LVM" },
620 { EFI_DELL_RESV, "EFI_DELL_RESV" },
621 { EFI_AAPL_BOOT, "EFI_AAPL_BOOT" },
622 { EFI_AAPL_HFS, "EFI_AAPL_HFS" },
623 { EFI_AAPL_UFS, "EFI_AAPL_UFS" },
624 { EFI_AAPL_ZFS, "EFI_AAPL_ZFS" },
625 { EFI_AAPL_APFS, "EFI_AAPL_APFS" },
626 { EFI_FREEBSD_BOOT, "EFI_FREEBSD_BOOT" },
627 { EFI_FREEBSD_NANDFS, "EFI_FREEBSD_NANDFS" },
628 { EFI_FREEBSD_SWAP, "EFI_FREEBSD_SWAP" },
629 { EFI_FREEBSD_UFS, "EFI_FREEBSD_UFS" },
630 { EFI_FREEBSD_VINUM, "EFI_FREEBSD_VINUM" },
631 { EFI_FREEBSD_ZFS, "EFI_FREEBSD_ZFS" },
632 { EFI_BIOS_BOOT, "EFI_BIOS_BOOT" },
633 };
634
635 static void
print_gpe(efi_gpe_t * gpe,size_t nr,int show_guid)636 print_gpe(efi_gpe_t *gpe, size_t nr, int show_guid)
637 {
638 const char *type = "unknown";
639
640 for (size_t i = 0; i < ARRAY_SIZE(efi_guids); i++) {
641 if (memcmp((void *)&efi_guids[i].eg_uuid,
642 (void *)&gpe->efi_gpe_PartitionTypeGUID,
643 sizeof (efi_guids[i].eg_uuid)) == 0) {
644 type = efi_guids[i].eg_name;
645 break;
646 }
647 }
648
649 if (strcmp(type, "EFI_UNUSED") == 0) {
650 mdb_printf("%-4u %-19s\n", nr, type);
651 return;
652 }
653
654 if (show_guid) {
655 char guid[UUID_PRINTABLE_STRING_LENGTH];
656
657 uuid_unparse((uchar_t *)&gpe->efi_gpe_UniquePartitionGUID,
658 guid);
659
660 mdb_printf("%-4u %-19s %s\n", nr, type, guid);
661 } else {
662 char name[EFI_PART_NAME_LEN + 1] = "";
663
664 /*
665 * Hopefully, ASCII is sufficient for any naming we care about.
666 */
667 for (size_t i = 0; i < sizeof (name); i++) {
668 ushort_t wchar = gpe->efi_gpe_PartitionName[i];
669
670 name[i] = (char)(isascii(wchar) ? wchar : '?');
671 }
672
673 mdb_printf("%-4u %-19s %-13llu %-13llu %#-8llx %s\n",
674 nr, type, gpe->efi_gpe_StartingLBA, gpe->efi_gpe_EndingLBA,
675 gpe->efi_gpe_Attributes, name);
676 }
677 }
678
679 static int
cmd_gpt(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv __unused)680 cmd_gpt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv __unused)
681 {
682 char uuid[UUID_PRINTABLE_STRING_LENGTH];
683 int show_alternate = B_FALSE;
684 int show_guid = B_FALSE;
685 efi_gpt_t *altheader;
686 size_t table_size;
687 efi_gpt_t *header;
688 efi_gpe_t *gpet;
689 uint_t orig_crc;
690 uint_t crc;
691
692 if (mdb_getopts(argc, argv,
693 'a', MDB_OPT_SETBITS, TRUE, &show_alternate,
694 'g', MDB_OPT_SETBITS, TRUE, &show_guid,
695 NULL) != argc)
696 return (DCMD_USAGE);
697
698 /* Primary header is at LBA 1. */
699 if (!(flags & DCMD_ADDRSPEC))
700 addr = sector_size;
701
702 header = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
703 if (mdb_vread(header, sector_size, addr) == -1) {
704 mdb_warn("failed to read GPT header");
705 return (DCMD_ERR);
706 }
707
708 if (show_alternate) {
709 addr = header->efi_gpt_AlternateLBA * sector_size;
710
711 if (mdb_vread(header, sector_size, addr) == -1) {
712 mdb_warn("failed to read GPT header");
713 return (DCMD_ERR);
714 }
715 }
716
717 mdb_printf("Signature: %s (%s)\n", (char *)&header->efi_gpt_Signature,
718 strncmp((char *)&header->efi_gpt_Signature, "EFI PART", 8) == 0 ?
719 "valid" : "invalid");
720
721 mdb_printf("Revision: %hu.%hu\n", header->efi_gpt_Revision >> 16,
722 header->efi_gpt_Revision);
723
724 mdb_printf("HeaderSize: %u bytes\n", header->efi_gpt_HeaderSize);
725
726 if (header->efi_gpt_HeaderSize > SECTOR_SIZE) {
727 mdb_warn("invalid header size: skipping CRC\n");
728 } else {
729 orig_crc = header->efi_gpt_HeaderCRC32;
730
731 header->efi_gpt_HeaderCRC32 = 0;
732
733 crc = efi_crc32((unsigned char *)header,
734 header->efi_gpt_HeaderSize);
735
736 mdb_printf("HeaderCRC32: %#x (should be %#x)\n", orig_crc, crc);
737 }
738
739 mdb_printf("Reserved1: %#x (should be 0x0)\n",
740 header->efi_gpt_Reserved1);
741
742 mdb_printf("MyLBA: %llu (should be %llu)\n",
743 header->efi_gpt_MyLBA, addr / sector_size);
744
745 mdb_printf("AlternateLBA: %llu\n", header->efi_gpt_AlternateLBA);
746 mdb_printf("FirstUsableLBA: %llu\n", header->efi_gpt_FirstUsableLBA);
747 mdb_printf("LastUsableLBA: %llu\n", header->efi_gpt_LastUsableLBA);
748
749 if (header->efi_gpt_MyLBA >= header->efi_gpt_FirstUsableLBA &&
750 header->efi_gpt_MyLBA <= header->efi_gpt_LastUsableLBA) {
751 mdb_warn("MyLBA is within usable LBA range\n");
752 }
753
754 if (header->efi_gpt_AlternateLBA >= header->efi_gpt_FirstUsableLBA &&
755 header->efi_gpt_AlternateLBA <= header->efi_gpt_LastUsableLBA) {
756 mdb_warn("AlternateLBA is within usable LBA range\n");
757 }
758
759 altheader = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
760 if (mdb_vread(altheader, sector_size,
761 header->efi_gpt_AlternateLBA * sector_size) == -1) {
762 mdb_warn("failed to read alternate GPT header");
763 } else {
764 if (strncmp((char *)&altheader->efi_gpt_Signature,
765 "EFI PART", 8) != 0) {
766 mdb_warn("found invalid alternate GPT header with "
767 "Signature: %s\n",
768 (char *)&altheader->efi_gpt_Signature);
769 }
770
771 if (altheader->efi_gpt_MyLBA != header->efi_gpt_AlternateLBA) {
772 mdb_warn("alternate GPT header at offset %#llx has "
773 "invalid MyLBA %llu\n",
774 header->efi_gpt_AlternateLBA * sector_size,
775 altheader->efi_gpt_MyLBA);
776 }
777
778 if (altheader->efi_gpt_AlternateLBA != header->efi_gpt_MyLBA) {
779 mdb_warn("alternate GPT header at offset %#llx has "
780 "invalid AlternateLBA %llu\n",
781 header->efi_gpt_AlternateLBA * sector_size,
782 altheader->efi_gpt_AlternateLBA);
783 }
784
785 /*
786 * We could go ahead and verify all the alternate checksums,
787 * etc. here too...
788 */
789 }
790
791 uuid_unparse((uchar_t *)&header->efi_gpt_DiskGUID, uuid);
792 mdb_printf("DiskGUID: %s\n", uuid);
793
794 mdb_printf("PartitionEntryLBA: %llu\n",
795 header->efi_gpt_PartitionEntryLBA);
796
797 mdb_printf("NumberOfPartitionEntries: %u\n",
798 header->efi_gpt_NumberOfPartitionEntries);
799
800 /*
801 * While the spec allows a different size, in practice the table
802 * is always packed.
803 */
804 if (header->efi_gpt_SizeOfPartitionEntry != sizeof (efi_gpe_t)) {
805 mdb_warn("SizeOfPartitionEntry: %#x bytes "
806 "(expected %#x bytes)\n",
807 header->efi_gpt_SizeOfPartitionEntry, sizeof (efi_gpe_t));
808 return (DCMD_ERR);
809 }
810
811 mdb_printf("SizeOfPartitionEntry: %#x bytes\n",
812 header->efi_gpt_SizeOfPartitionEntry);
813
814 table_size = header->efi_gpt_SizeOfPartitionEntry *
815 header->efi_gpt_NumberOfPartitionEntries;
816
817 /*
818 * While this is a minimum reservation, it serves us ably as a
819 * maximum value to reasonably expect.
820 */
821 if (table_size > EFI_MIN_ARRAY_SIZE) {
822 mdb_warn("Skipping GPT array of %#lx bytes.\n", table_size);
823 return (DCMD_ERR);
824 }
825
826 table_size = P2ROUNDUP(table_size, sector_size);
827 gpet = mdb_alloc(table_size, UM_SLEEP | UM_GC);
828
829 if (mdb_vread(gpet, table_size,
830 header->efi_gpt_PartitionEntryLBA * sector_size) == -1) {
831 mdb_warn("couldn't read GPT array");
832 return (DCMD_ERR);
833 }
834
835 crc = efi_crc32((unsigned char *)gpet,
836 header->efi_gpt_SizeOfPartitionEntry *
837 header->efi_gpt_NumberOfPartitionEntries);
838
839 mdb_printf("PartitionEntryArrayCRC32: %#x (should be %#x)\n",
840 header->efi_gpt_PartitionEntryArrayCRC32, crc);
841
842 if (show_guid) {
843 mdb_printf("\n%<u>%-4s %-19s %-37s%</u>\n",
844 "PART", "TYPE", "GUID");
845 } else {
846 mdb_printf("\n%<u>%-4s %-19s %-13s %-13s %-8s %s%</u>\n",
847 "PART", "TYPE", "STARTLBA", "ENDLBA", "ATTR", "NAME");
848 }
849
850 for (size_t i = 0; i < header->efi_gpt_NumberOfPartitionEntries; i++)
851 print_gpe(&gpet[i], i, show_guid);
852
853 return (DCMD_OK);
854 }
855
856 static void
gpt_help(void)857 gpt_help(void)
858 {
859 mdb_printf("Display an EFI GUID Partition Table.\n\n"
860 "-a Display the alternate GPT\n"
861 "-g Show unique GUID for each table entry\n");
862 }
863
864 static int
cmd_vtoc(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)865 cmd_vtoc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
866 {
867 uint8_t *buf;
868 struct dk_label *dl;
869 struct dk_vtoc *dv;
870 uintptr_t vaddr;
871 int i, tag_width, cyl_width;
872 int show_absolute = B_TRUE;
873 int show_sectors = B_TRUE;
874 uint32_t cyl;
875
876 if (mdb_getopts(argc, argv,
877 'c', MDB_OPT_CLRBITS, TRUE, &show_sectors,
878 'r', MDB_OPT_CLRBITS, TRUE, &show_absolute,
879 NULL) != argc)
880 return (DCMD_USAGE);
881
882 if (!(flags & DCMD_ADDRSPEC))
883 addr = 0;
884 else
885 addr *= sector_size;
886
887 buf = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
888
889 #if defined(_SUNOS_VTOC_16)
890 if (mdb_vread(buf, sector_size, addr) == -1) {
891 mdb_warn("failed to read VBR");
892 return (DCMD_ERR);
893 }
894
895 mdb_printf("VBR info:\n");
896 (void) mbr_info((struct mboot *)buf, FALSE);
897 #endif
898
899 vaddr = addr + DK_LABEL_LOC * sector_size;
900
901 if (mdb_vread(buf, sector_size, vaddr) == -1) {
902 mdb_warn("failed to read VTOC");
903 return (DCMD_ERR);
904 }
905
906 dl = (struct dk_label *)buf;
907 dv = (struct dk_vtoc *)&dl->dkl_vtoc;
908
909 mdb_printf("Label magic: 0x%hx (%s)\n", dl->dkl_magic,
910 dl->dkl_magic == DKL_MAGIC ? "valid" : "invalid");
911 if (dl->dkl_magic != DKL_MAGIC)
912 return (DCMD_ERR);
913 mdb_printf("Label %s sane\n", dv->v_sanity == VTOC_SANE ?
914 "is" : "is not");
915
916 mdb_printf("Label version: %#x\n", dv->v_version);
917 mdb_printf("Volume name = <%s>\n", dv->v_volume);
918 mdb_printf("ASCII name = <%s>\n", dv->v_asciilabel);
919 mdb_printf("pcyl = %4d\n", dl->dkl_pcyl);
920 mdb_printf("ncyl = %4d\n", dl->dkl_ncyl);
921 mdb_printf("acyl = %4d\n", dl->dkl_acyl);
922
923 #if defined(_SUNOS_VTOC_16)
924 mdb_printf("bcyl = %4d\n", dl->dkl_bcyl);
925 #endif /* defined(_SUNOS_VTOC_16) */
926
927 mdb_printf("nhead = %4d\n", dl->dkl_nhead);
928 mdb_printf("nsect = %4d\n", dl->dkl_nsect);
929
930
931 if (!show_absolute)
932 addr = 0;
933 cyl = dl->dkl_nhead * dl->dkl_nsect;
934 if (show_sectors)
935 cyl = 1;
936 else
937 addr /= (cyl * sector_size);
938
939 tag_width = array_widest_str(ptag_array);
940
941 cyl_width = sizeof ("CYLINDERS");
942 for (i = 0; i < dv->v_nparts; i++) {
943 uint32_t start, end, size;
944 int w;
945
946 #if defined(_SUNOS_VTOC_16)
947 start = addr + (dv->v_part[i].p_start / cyl);
948 size = dv->v_part[i].p_size;
949 #elif defined(_SUNOS_VTOC_8)
950 start = dl->dkl_map[i].dkl_cylno;
951 start *= dl->dkl_nhead * dl->dkl_nsect; /* compute bytes */
952 start /= cyl;
953 start += addr;
954 size = dl->dkl_map[i].dkl_nblk;
955 #else
956 #error "No VTOC format defined."
957 #endif
958 if (size == 0)
959 end = start = 0;
960 else
961 end = start + size / cyl - 1;
962
963 w = mdb_snprintf(NULL, 0, "%u - %u", start, end);
964 if (w > cyl_width)
965 cyl_width = w;
966 }
967
968 if (show_sectors == B_TRUE) {
969 mdb_printf("\n%<u>%-4s %-*s %-7s %-11s %-11s %-*s "
970 "%-10s%</u>\n", "PART", tag_width, "TAG", "FLAG",
971 "STARTLBA", "ENDLBA", MDB_NICENUM_BUFLEN, "SIZE", "BLOCKS");
972 } else {
973 mdb_printf("\n%<u>%-4s %-*s %-7s %-*s %-*s %-10s%</u>\n",
974 "PART", tag_width, "TAG", "FLAG", cyl_width, "CYLINDERS",
975 MDB_NICENUM_BUFLEN, "SIZE", "BLOCKS");
976 }
977
978 for (i = 0; i < dv->v_nparts; i++) {
979 uint16_t tag, flag;
980 uint32_t start, end, size;
981 const char *stag, *sflag;
982 char nnum[MDB_NICENUM_BUFLEN];
983
984 #if defined(_SUNOS_VTOC_16)
985 tag = dv->v_part[i].p_tag;
986 flag = dv->v_part[i].p_flag;
987 start = addr + (dv->v_part[i].p_start / cyl);
988 size = dv->v_part[i].p_size;
989 #elif defined(_SUNOS_VTOC_8)
990 tag = dv->v_part[i].p_tag;
991 flag = dv->v_part[i].p_flag;
992 start = dl->dkl_map[i].dkl_cylno;
993 start *= dl->dkl_nhead * dl->dkl_nsect; /* compute bytes */
994 start /= cyl;
995 start += addr;
996 size = dl->dkl_map[i].dkl_nblk;
997 #else
998 #error "No VTOC format defined."
999 #endif
1000 if (size == 0)
1001 end = start = 0;
1002 else
1003 end = start + size / cyl - 1;
1004
1005 stag = array_find_string(ptag_array, tag);
1006 if (stag == NULL)
1007 stag = "?";
1008 sflag = array_find_string(pflag_array, flag);
1009 if (sflag == NULL)
1010 sflag = "?";
1011
1012 mdb_printf("%-4d %-*s %-7s ", i, tag_width, stag, sflag);
1013 mdb_nicenum(size * sector_size, nnum);
1014 if (show_sectors) {
1015 mdb_printf("%-11u %-11u %-*s %-10u\n", start, end,
1016 MDB_NICENUM_BUFLEN, nnum, size);
1017 } else {
1018 char cyls[10 * 2 + 4];
1019
1020 if (size == 0) {
1021 mdb_snprintf(cyls, sizeof (cyls), "%-*u",
1022 cyl_width, size);
1023 } else {
1024 mdb_snprintf(cyls, sizeof (cyls), "%u - %u",
1025 start, end);
1026 }
1027 mdb_printf("%-*s %-*s %-10u\n", cyl_width, cyls,
1028 MDB_NICENUM_BUFLEN, nnum, size);
1029 }
1030 }
1031
1032 return (DCMD_OK);
1033 }
1034
1035 static void
vtoc_help(void)1036 vtoc_help(void)
1037 {
1038 mdb_printf("Display a Virtual Table of Content (VTOC).\n\n"
1039 "-r Display relative addresses\n"
1040 "-c Use cylinder based addressing\n");
1041 mdb_printf("\nThe addr is in %u-byte disk blocks.\n", sector_size);
1042 }
1043
1044 static int
cmd_sect(uintptr_t addr __unused,uint_t flags __unused,int argc,const mdb_arg_t * argv)1045 cmd_sect(uintptr_t addr __unused, uint_t flags __unused, int argc,
1046 const mdb_arg_t *argv)
1047 {
1048 uint64_t size = SECTOR_SIZE;
1049
1050 if (argc < 1) {
1051 mdb_printf("Current sector size is %u (%#x)\n", sector_size,
1052 sector_size);
1053 return (DCMD_OK);
1054 }
1055
1056 if (argc != 1)
1057 return (DCMD_USAGE);
1058
1059 switch (argv[0].a_type) {
1060 case MDB_TYPE_STRING:
1061 size = mdb_strtoull(argv[0].a_un.a_str);
1062 break;
1063 case MDB_TYPE_IMMEDIATE:
1064 size = argv[0].a_un.a_val;
1065 break;
1066 default:
1067 return (DCMD_USAGE);
1068 }
1069
1070 if (!ISP2(size)) {
1071 mdb_printf("sector size must be power of 2\n");
1072 return (DCMD_USAGE);
1073 }
1074 sector_size = size;
1075 return (DCMD_OK);
1076 }
1077
1078 static void
sect_help(void)1079 sect_help(void)
1080 {
1081 mdb_printf("Show or set sector size.\n");
1082 }
1083
1084 static const mdb_dcmd_t dcmds[] = {
1085 { "mbr", "?[-b]", "dump Master Boot Record information", cmd_mbr,
1086 mbr_help },
1087 { "gpt", "?[-ag]", "dump an EFI GPT", cmd_gpt, gpt_help },
1088 { "vtoc", "?[-cr]", "dump VTOC information", cmd_vtoc, vtoc_help },
1089 { "sectorsize", NULL, "set or show sector size", cmd_sect, sect_help },
1090 { NULL }
1091 };
1092
1093 static const mdb_modinfo_t modinfo = {
1094 MDB_API_VERSION, dcmds, NULL
1095 };
1096
1097 const mdb_modinfo_t *
_mdb_init(void)1098 _mdb_init(void)
1099 {
1100 return (&modinfo);
1101 }
1102