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  */
15 
16 /*
17  * The on-disk elements here are all little-endian, and this code doesn't make
18  * any attempt to adjust for running on a big-endian system.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/crc32.h>
23 #include <sys/debug.h>
24 #include <sys/sysmacros.h>
25 #include <sys/dktp/fdisk.h>
26 #include <sys/efi_partition.h>
27 #include <sys/vtoc.h>
28 
29 #include <assert.h>
30 #include <ctype.h>
31 #include <uuid/uuid.h>
32 
33 #include <mdb/mdb_modapi.h>
34 #include <mdb/mdb_debug.h>
35 
36 #include "installboot.h"
37 
38 #ifdef _BIG_ENDIAN
39 #error needs porting for big-endian system
40 #endif
41 
42 /* See usr/src/grub/grub-0.97/stage1/stage1.h */
43 #define	GRUB_VERSION_OFF (0x3e)
44 #define	GRUB_COMPAT_VERSION_MAJOR 3
45 #define	GRUB_COMPAT_VERSION_MINOR 2
46 #define	GRUB_VERSION (2 << 8 | 3) /* 3.2 */
47 
48 #define	LOADER_VERSION (1)
49 #define	LOADER_JOYENT_VERSION (2)
50 
51 typedef enum {
52 	MBR_TYPE_UNKNOWN,
53 	MBR_TYPE_GRUB1,
54 	MBR_TYPE_LOADER,
55 	MBR_TYPE_LOADER_JOYENT,
56 } mbr_type_t;
57 
58 typedef struct stringval {
59 	const char	*sv_text;
60 	int		sv_value;
61 } stringval_t;
62 
63 stringval_t ptag_array[] = {
64 	{ "unassigned",		V_UNASSIGNED	},
65 	{ "boot",		V_BOOT		},
66 	{ "root",		V_ROOT		},
67 	{ "swap",		V_SWAP		},
68 	{ "usr",		V_USR		},
69 	{ "backup",		V_BACKUP	},
70 	{ "stand",		V_STAND		},
71 	{ "var",		V_VAR		},
72 	{ "home",		V_HOME		},
73 	{ "alternates",		V_ALTSCTR	},
74 	{ "reserved",		V_RESERVED	},
75 	{ "system",		V_SYSTEM	},
76 	{ "BIOS_boot",		V_BIOS_BOOT	},
77 	{ "FreeBSD boot",	V_FREEBSD_BOOT	},
78 	{ "FreeBSD swap",	V_FREEBSD_SWAP	},
79 	{ "FreeBSD UFS",	V_FREEBSD_UFS	},
80 	{ "FreeBSD ZFS",	V_FREEBSD_ZFS	},
81 	{ "FreeBSD NANDFS",	V_FREEBSD_NANDFS },
82 
83 	{ NULL }
84 };
85 
86 stringval_t pflag_array[] = {
87 	{ "wm", 0			},
88 	{ "wu", V_UNMNT			},
89 	{ "rm", V_RONLY			},
90 	{ "ru", V_RONLY | V_UNMNT	},
91 	{ NULL }
92 };
93 
94 size_t sector_size = SECTOR_SIZE;
95 
96 static const char *
97 array_find_string(stringval_t *array, int match_value)
98 {
99 	for (; array->sv_text != NULL; array++) {
100 		if (array->sv_value == match_value) {
101 			return (array->sv_text);
102 		}
103 	}
104 
105 	return (NULL);
106 }
107 
108 static int
109 array_widest_str(stringval_t *array)
110 {
111 	int	i;
112 	int	width;
113 
114 	width = 0;
115 	for (; array->sv_text != NULL; array++) {
116 		if ((i = strlen(array->sv_text)) > width)
117 			width = i;
118 	}
119 
120 	return (width);
121 }
122 
123 static void
124 print_fdisk_part(struct ipart *ip, size_t nr)
125 {
126 	char typestr[128];
127 	char begchs[128];
128 	char endchs[128];
129 	char *c = NULL;
130 
131 	if (ip->systid == UNUSED) {
132 		mdb_printf("%-4llu %s:%#lx\n", nr, "UNUSED", ip->systid);
133 		return;
134 	}
135 
136 	switch (ip->systid) {
137 	case DOSOS12: c = "DOSOS12"; break;
138 	case PCIXOS: c = "PCIXOS"; break;
139 	case DOSOS16: c = "DOSOS16"; break;
140 	case EXTDOS: c = "EXTDOS"; break;
141 	case DOSHUGE: c = "DOSHUGE"; break;
142 	case FDISK_IFS: c = "FDISK_IFS"; break;
143 	case FDISK_AIXBOOT: c = "FDISK_AIXBOOT"; break;
144 	case FDISK_AIXDATA: c = "FDISK_AIXDATA"; break;
145 	case FDISK_OS2BOOT: c = "FDISK_OS2BOOT"; break;
146 	case FDISK_WINDOWS: c = "FDISK_WINDOWS"; break;
147 	case FDISK_EXT_WIN: c = "FDISK_EXT_WIN"; break;
148 	case FDISK_FAT95: c = "FDISK_FAT95"; break;
149 	case FDISK_EXTLBA: c = "FDISK_EXTLBA"; break;
150 	case DIAGPART: c = "DIAGPART"; break;
151 	case FDISK_LINUX: c = "FDISK_LINUX"; break;
152 	case FDISK_LINUXDSWAP: c = "FDISK_LINUXDSWAP"; break;
153 	case FDISK_LINUXDNAT: c = "FDISK_LINUXDNAT"; break;
154 	case FDISK_CPM: c = "FDISK_CPM"; break;
155 	case DOSDATA: c = "DOSDATA"; break;
156 	case OTHEROS: c = "OTHEROS"; break;
157 	case UNIXOS: c = "UNIXOS"; break;
158 	case FDISK_NOVELL2: c = "FDISK_NOVELL2"; break;
159 	case FDISK_NOVELL3: c = "FDISK_NOVELL3"; break;
160 	case FDISK_QNX4: c = "FDISK_QNX4"; break;
161 	case FDISK_QNX42: c = "FDISK_QNX42"; break;
162 	case FDISK_QNX43: c = "FDISK_QNX43"; break;
163 	case SUNIXOS: c = "SUNIXOS"; break;
164 	case FDISK_LINUXNAT: c = "FDISK_LINUXNAT"; break;
165 	case FDISK_NTFSVOL1: c = "FDISK_NTFSVOL1"; break;
166 	case FDISK_NTFSVOL2: c = "FDISK_NTFSVOL2"; break;
167 	case FDISK_BSD: c = "FDISK_BSD"; break;
168 	case FDISK_NEXTSTEP: c = "FDISK_NEXTSTEP"; break;
169 	case FDISK_BSDIFS: c = "FDISK_BSDIFS"; break;
170 	case FDISK_BSDISWAP: c = "FDISK_BSDISWAP"; break;
171 	case X86BOOT: c = "X86BOOT"; break;
172 	case SUNIXOS2: c = "SUNIXOS2"; break;
173 	case EFI_PMBR: c = "EFI_PMBR"; break;
174 	case EFI_FS: c = "EFI_FS"; break;
175 	default: c = NULL; break;
176 	}
177 
178 	if (c != NULL) {
179 		mdb_snprintf(typestr, sizeof (typestr), "%s:%#lx",
180 		    c, ip->systid);
181 	} else {
182 		mdb_snprintf(typestr, sizeof (typestr), "%#lx", ip->systid);
183 	}
184 
185 	mdb_snprintf(begchs, sizeof (begchs), "%hu/%hu/%hu",
186 	    (uint16_t)ip->begcyl | (uint16_t)(ip->begsect & ~0x3f) << 2,
187 	    (uint16_t)ip->beghead, (uint16_t)ip->begsect & 0x3f);
188 	mdb_snprintf(endchs, sizeof (endchs), "%hu/%hu/%hu",
189 	    (uint16_t)ip->endcyl | (uint16_t)(ip->endsect & ~0x3f) << 2,
190 	    (uint16_t)ip->endhead, (uint16_t)ip->endsect & 0x3f);
191 
192 	mdb_printf("%-4llu %-21s %#-7x %-11s %-11s %-10u %-9u\n",
193 	    nr, typestr, ip->bootid, begchs, endchs, ip->relsect, ip->numsect);
194 }
195 
196 static mbr_type_t
197 mbr_info(struct mboot *mbr)
198 {
199 	mbr_type_t type = MBR_TYPE_UNKNOWN;
200 
201 	if (*((uint16_t *)&mbr->bootinst[GRUB_VERSION_OFF]) == GRUB_VERSION) {
202 		type = MBR_TYPE_GRUB1;
203 	} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_VERSION) {
204 		type = MBR_TYPE_LOADER;
205 	} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_JOYENT_VERSION) {
206 		type = MBR_TYPE_LOADER_JOYENT;
207 	}
208 
209 	switch (type) {
210 	case MBR_TYPE_UNKNOWN:
211 		mdb_printf("Format: unknown\n");
212 		break;
213 	case MBR_TYPE_GRUB1:
214 		mdb_printf("Format: grub1\n");
215 		break;
216 	case MBR_TYPE_LOADER:
217 		mdb_printf("Format: loader (illumos)\n");
218 		break;
219 	case MBR_TYPE_LOADER_JOYENT:
220 		mdb_printf("Format: loader (joyent)\n");
221 		break;
222 	}
223 
224 	mdb_printf("Signature: 0x%hx (%s)\n", mbr->signature,
225 	    mbr->signature == MBB_MAGIC ? "valid" : "invalid");
226 
227 	mdb_printf("UniqueMBRDiskSignature: %#lx\n",
228 	    *(uint32_t *)&mbr->bootinst[STAGE1_SIG]);
229 
230 	if (type == MBR_TYPE_LOADER || type == MBR_TYPE_LOADER_JOYENT) {
231 		char uuid[UUID_PRINTABLE_STRING_LENGTH];
232 
233 		mdb_printf("Loader STAGE1_STAGE2_LBA: %llu\n",
234 		    *(uint64_t *)&mbr->bootinst[STAGE1_STAGE2_LBA]);
235 
236 		mdb_printf("Loader STAGE1_STAGE2_SIZE: %hu\n",
237 		    *(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE]);
238 
239 		uuid_unparse((uchar_t *)&mbr->bootinst[STAGE1_STAGE2_UUID],
240 		    uuid);
241 
242 		mdb_printf("Loader STAGE1_STAGE2_UUID: %s\n", uuid);
243 	}
244 
245 	return (type);
246 }
247 
248 static int
249 cmd_mbr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv __unused)
250 {
251 	struct mboot *mbr;
252 	mbr_type_t type;
253 
254 	CTASSERT(sizeof (*mbr) == SECTOR_SIZE);
255 
256 	if (argc != 0)
257 		return (DCMD_USAGE);
258 
259 	if (!(flags & DCMD_ADDRSPEC))
260 		addr = 0;
261 
262 	mbr = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
263 
264 	if (mdb_vread(mbr, sector_size, addr) == -1) {
265 		mdb_warn("failed to read MBR");
266 		return (DCMD_ERR);
267 	}
268 
269 	type = mbr_info(mbr);
270 
271 	/* If the magic is wrong, stop here. */
272 	if (mbr->signature != MBB_MAGIC)
273 		return (DCMD_ERR);
274 
275 	/* Also print volume boot record */
276 	switch (type) {
277 	case MBR_TYPE_LOADER:
278 	case MBR_TYPE_LOADER_JOYENT:
279 		if (*(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE] == 1) {
280 			struct mboot vbr;
281 			uintptr_t vbrp;
282 
283 			vbrp = *(uint64_t *)&mbr->bootinst[STAGE1_STAGE2_LBA];
284 			vbrp *= sector_size;
285 			vbrp += addr;
286 			if (mdb_vread(&vbr, sizeof (vbr), vbrp) == -1) {
287 				mdb_warn("failed to read VBR");
288 			} else {
289 				mdb_printf("\nSTAGE1 in VBR:\n");
290 				(void) mbr_info(&vbr);
291 			}
292 		}
293 		break;
294 	default:
295 		break;
296 	}
297 
298 	mdb_printf("\n%<u>%-4s %-21s %-7s %-11s %-11s %-10s %-9s%</u>\n",
299 	    "PART", "TYPE", "ACTIVE", "STARTCHS", "ENDCHS",
300 	    "SECTOR", "NUMSECT");
301 
302 	for (size_t i = 0; i < FD_NUMPART; i++) {
303 		struct ipart *ip = (struct ipart *)
304 		    (mbr->parts + (sizeof (struct ipart) * i));
305 		print_fdisk_part(ip, i);
306 	}
307 
308 	return (DCMD_OK);
309 }
310 
311 static unsigned int crc32_tab[] = { CRC32_TABLE };
312 
313 static unsigned int
314 efi_crc32(const unsigned char *s, unsigned int len)
315 {
316 	unsigned int crc32val;
317 
318 	CRC32(crc32val, s, len, -1U, crc32_tab);
319 
320 	return (crc32val ^ -1U);
321 }
322 
323 typedef struct {
324 	struct uuid eg_uuid;
325 	const char *eg_name;
326 } efi_guid_t;
327 
328 static efi_guid_t efi_guids[] = {
329 	{ EFI_UNUSED, "EFI_UNUSED" },
330 	{ EFI_RESV1, "EFI_RESV1" },
331 	{ EFI_BOOT, "EFI_BOOT" },
332 	{ EFI_ROOT, "EFI_ROOT" },
333 	{ EFI_SWAP, "EFI_SWAP" },
334 	{ EFI_USR, "EFI_USR" },
335 	{ EFI_BACKUP, "EFI_BACKUP" },
336 	{ EFI_RESV2, "EFI_RESV2" },
337 	{ EFI_VAR, "EFI_VAR" },
338 	{ EFI_HOME, "EFI_HOME" },
339 	{ EFI_ALTSCTR, "EFI_ALTSCTR" },
340 	{ EFI_RESERVED, "EFI_RESERVED" },
341 	{ EFI_SYSTEM, "EFI_SYSTEM" },
342 	{ EFI_LEGACY_MBR, "EFI_LEGACY_MBR" },
343 	{ EFI_SYMC_PUB, "EFI_SYMC_PUB" },
344 	{ EFI_SYMC_CDS, "EFI_SYMC_CDS" },
345 	{ EFI_MSFT_RESV, "EFI_MSFT_RESV" },
346 	{ EFI_DELL_BASIC, "EFI_DELL_BASIC" },
347 	{ EFI_DELL_RAID, "EFI_DELL_RAID" },
348 	{ EFI_DELL_SWAP, "EFI_DELL_SWAP" },
349 	{ EFI_DELL_LVM, "EFI_DELL_LVM" },
350 	{ EFI_DELL_RESV, "EFI_DELL_RESV" },
351 	{ EFI_AAPL_BOOT, "EFI_AAPL_BOOT" },
352 	{ EFI_AAPL_HFS, "EFI_AAPL_HFS" },
353 	{ EFI_AAPL_UFS, "EFI_AAPL_UFS" },
354 	{ EFI_AAPL_ZFS, "EFI_AAPL_ZFS" },
355 	{ EFI_AAPL_APFS, "EFI_AAPL_APFS" },
356 	{ EFI_FREEBSD_BOOT, "EFI_FREEBSD_BOOT" },
357 	{ EFI_FREEBSD_NANDFS, "EFI_FREEBSD_NANDFS" },
358 	{ EFI_FREEBSD_SWAP, "EFI_FREEBSD_SWAP" },
359 	{ EFI_FREEBSD_UFS, "EFI_FREEBSD_UFS" },
360 	{ EFI_FREEBSD_VINUM, "EFI_FREEBSD_VINUM" },
361 	{ EFI_FREEBSD_ZFS, "EFI_FREEBSD_ZFS" },
362 	{ EFI_BIOS_BOOT, "EFI_BIOS_BOOT" },
363 };
364 
365 static void
366 print_gpe(efi_gpe_t *gpe, size_t nr, int show_guid)
367 {
368 	const char *type = "unknown";
369 
370 	for (size_t i = 0; i < ARRAY_SIZE(efi_guids); i++) {
371 		if (memcmp((void *)&efi_guids[i].eg_uuid,
372 		    (void *)&gpe->efi_gpe_PartitionTypeGUID,
373 		    sizeof (efi_guids[i].eg_uuid)) == 0) {
374 			type = efi_guids[i].eg_name;
375 			break;
376 		}
377 	}
378 
379 	if (strcmp(type, "EFI_UNUSED") == 0) {
380 		mdb_printf("%-4u %-19s\n", nr, type);
381 		return;
382 	}
383 
384 	if (show_guid) {
385 		char guid[UUID_PRINTABLE_STRING_LENGTH];
386 
387 		uuid_unparse((uchar_t *)&gpe->efi_gpe_UniquePartitionGUID,
388 		    guid);
389 
390 		mdb_printf("%-4u %-19s %s\n", nr, type, guid);
391 	} else {
392 		char name[EFI_PART_NAME_LEN + 1] = "";
393 
394 		/*
395 		 * Hopefully, ASCII is sufficient for any naming we care about.
396 		 */
397 		for (size_t i = 0; i < sizeof (name); i++) {
398 			ushort_t wchar = gpe->efi_gpe_PartitionName[i];
399 
400 			name[i] = (char)(isascii(wchar) ? wchar : '?');
401 		}
402 
403 		mdb_printf("%-4u %-19s %-13llu %-13llu %#-8llx %s\n",
404 		    nr, type, gpe->efi_gpe_StartingLBA, gpe->efi_gpe_EndingLBA,
405 		    gpe->efi_gpe_Attributes, name);
406 	}
407 }
408 
409 static int
410 cmd_gpt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv __unused)
411 {
412 	char uuid[UUID_PRINTABLE_STRING_LENGTH];
413 	int show_alternate = B_FALSE;
414 	int show_guid = B_FALSE;
415 	efi_gpt_t *altheader;
416 	size_t table_size;
417 	efi_gpt_t *header;
418 	efi_gpe_t *gpet;
419 	uint_t orig_crc;
420 	uint_t crc;
421 
422 	if (mdb_getopts(argc, argv,
423 	    'a', MDB_OPT_SETBITS, TRUE, &show_alternate,
424 	    'g', MDB_OPT_SETBITS, TRUE, &show_guid,
425 	    NULL) != argc)
426 		return (DCMD_USAGE);
427 
428 	/* Primary header is at LBA 1. */
429 	if (!(flags & DCMD_ADDRSPEC))
430 		addr = sector_size;
431 
432 	header = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
433 	if (mdb_vread(header, sector_size, addr) == -1) {
434 		mdb_warn("failed to read GPT header");
435 		return (DCMD_ERR);
436 	}
437 
438 	if (show_alternate) {
439 		addr = header->efi_gpt_AlternateLBA * sector_size;
440 
441 		if (mdb_vread(header, sector_size, addr) == -1) {
442 			mdb_warn("failed to read GPT header");
443 			return (DCMD_ERR);
444 		}
445 	}
446 
447 	mdb_printf("Signature: %s (%s)\n", (char *)&header->efi_gpt_Signature,
448 	    strncmp((char *)&header->efi_gpt_Signature, "EFI PART", 8) == 0 ?
449 	    "valid" : "invalid");
450 
451 	mdb_printf("Revision: %hu.%hu\n", header->efi_gpt_Revision >> 16,
452 	    header->efi_gpt_Revision);
453 
454 	mdb_printf("HeaderSize: %u bytes\n", header->efi_gpt_HeaderSize);
455 
456 	if (header->efi_gpt_HeaderSize > SECTOR_SIZE) {
457 		mdb_warn("invalid header size: skipping CRC\n");
458 	} else {
459 		orig_crc = header->efi_gpt_HeaderCRC32;
460 
461 		header->efi_gpt_HeaderCRC32 = 0;
462 
463 		crc = efi_crc32((unsigned char *)header,
464 		    header->efi_gpt_HeaderSize);
465 
466 		mdb_printf("HeaderCRC32: %#x (should be %#x)\n", orig_crc, crc);
467 	}
468 
469 	mdb_printf("Reserved1: %#x (should be 0x0)\n",
470 	    header->efi_gpt_Reserved1);
471 
472 	mdb_printf("MyLBA: %llu (should be %llu)\n",
473 	    header->efi_gpt_MyLBA, addr / sector_size);
474 
475 	mdb_printf("AlternateLBA: %llu\n", header->efi_gpt_AlternateLBA);
476 	mdb_printf("FirstUsableLBA: %llu\n", header->efi_gpt_FirstUsableLBA);
477 	mdb_printf("LastUsableLBA: %llu\n", header->efi_gpt_LastUsableLBA);
478 
479 	if (header->efi_gpt_MyLBA >= header->efi_gpt_FirstUsableLBA &&
480 	    header->efi_gpt_MyLBA <= header->efi_gpt_LastUsableLBA) {
481 		mdb_warn("MyLBA is within usable LBA range\n");
482 	}
483 
484 	if (header->efi_gpt_AlternateLBA >= header->efi_gpt_FirstUsableLBA &&
485 	    header->efi_gpt_AlternateLBA <= header->efi_gpt_LastUsableLBA) {
486 		mdb_warn("AlternateLBA is within usable LBA range\n");
487 	}
488 
489 	altheader = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
490 	if (mdb_vread(altheader, sector_size,
491 	    header->efi_gpt_AlternateLBA * sector_size) == -1) {
492 		mdb_warn("failed to read alternate GPT header");
493 	} else {
494 		if (strncmp((char *)&altheader->efi_gpt_Signature,
495 		    "EFI PART", 8) != 0) {
496 			mdb_warn("found invalid alternate GPT header with "
497 			    "Signature: %s\n",
498 			    (char *)&altheader->efi_gpt_Signature);
499 		}
500 
501 		if (altheader->efi_gpt_MyLBA != header->efi_gpt_AlternateLBA) {
502 			mdb_warn("alternate GPT header at offset %#llx has "
503 			    "invalid MyLBA %llu\n",
504 			    header->efi_gpt_AlternateLBA * sector_size,
505 			    altheader->efi_gpt_MyLBA);
506 		}
507 
508 		if (altheader->efi_gpt_AlternateLBA != header->efi_gpt_MyLBA) {
509 			mdb_warn("alternate GPT header at offset %#llx has "
510 			    "invalid AlternateLBA %llu\n",
511 			    header->efi_gpt_AlternateLBA * sector_size,
512 			    altheader->efi_gpt_AlternateLBA);
513 		}
514 
515 		/*
516 		 * We could go ahead and verify all the alternate checksums,
517 		 * etc. here too...
518 		 */
519 	}
520 
521 	uuid_unparse((uchar_t *)&header->efi_gpt_DiskGUID, uuid);
522 	mdb_printf("DiskGUID: %s\n", uuid);
523 
524 	mdb_printf("PartitionEntryLBA: %llu\n",
525 	    header->efi_gpt_PartitionEntryLBA);
526 
527 	mdb_printf("NumberOfPartitionEntries: %u\n",
528 	    header->efi_gpt_NumberOfPartitionEntries);
529 
530 	/*
531 	 * While the spec allows a different size, in practice the table
532 	 * is always packed.
533 	 */
534 	if (header->efi_gpt_SizeOfPartitionEntry != sizeof (efi_gpe_t)) {
535 		mdb_warn("SizeOfPartitionEntry: %#x bytes "
536 		    "(expected %#x bytes)\n",
537 		    header->efi_gpt_SizeOfPartitionEntry, sizeof (efi_gpe_t));
538 		return (DCMD_ERR);
539 	}
540 
541 	mdb_printf("SizeOfPartitionEntry: %#x bytes\n",
542 	    header->efi_gpt_SizeOfPartitionEntry);
543 
544 	table_size = header->efi_gpt_SizeOfPartitionEntry *
545 	    header->efi_gpt_NumberOfPartitionEntries;
546 
547 	/*
548 	 * While this is a minimum reservation, it serves us ably as a
549 	 * maximum value to reasonably expect.
550 	 */
551 	if (table_size > EFI_MIN_ARRAY_SIZE) {
552 		mdb_warn("Skipping GPT array of %#lx bytes.\n", table_size);
553 		return (DCMD_ERR);
554 	}
555 
556 	table_size = P2ROUNDUP(table_size, sector_size);
557 	gpet = mdb_alloc(table_size, UM_SLEEP | UM_GC);
558 
559 	if (mdb_vread(gpet, table_size,
560 	    header->efi_gpt_PartitionEntryLBA * sector_size) == -1) {
561 		mdb_warn("couldn't read GPT array");
562 		return (DCMD_ERR);
563 	}
564 
565 	crc = efi_crc32((unsigned char *)gpet, table_size);
566 
567 	mdb_printf("PartitionEntryArrayCRC32: %#x (should be %#x)\n",
568 	    header->efi_gpt_PartitionEntryArrayCRC32, crc);
569 
570 	if (show_guid) {
571 		mdb_printf("\n%<u>%-4s %-19s %-37s%</u>\n",
572 		    "PART", "TYPE", "GUID");
573 	} else {
574 		mdb_printf("\n%<u>%-4s %-19s %-13s %-13s %-8s %s%</u>\n",
575 		    "PART", "TYPE", "STARTLBA", "ENDLBA", "ATTR", "NAME");
576 	}
577 
578 	for (size_t i = 0; i < header->efi_gpt_NumberOfPartitionEntries; i++)
579 		print_gpe(&gpet[i], i, show_guid);
580 
581 	return (DCMD_OK);
582 }
583 
584 void
585 gpt_help(void)
586 {
587 	mdb_printf("Display an EFI GUID Partition Table.\n\n"
588 	    "-a Display the alternate GPT\n"
589 	    "-g Show unique GUID for each table entry\n");
590 }
591 
592 static int
593 cmd_vtoc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
594 {
595 	uint8_t *buf;
596 	struct dk_label *dl;
597 	struct dk_vtoc *dv;
598 	uintptr_t vaddr;
599 	int i, tag_width, cyl_width;
600 	int show_absolute = B_TRUE;
601 	int show_sectors = B_TRUE;
602 	uint32_t cyl;
603 
604 	if (mdb_getopts(argc, argv,
605 	    'c', MDB_OPT_CLRBITS, TRUE, &show_sectors,
606 	    'r', MDB_OPT_CLRBITS, TRUE, &show_absolute,
607 	    NULL) != argc)
608 		return (DCMD_USAGE);
609 
610 	if (!(flags & DCMD_ADDRSPEC))
611 		addr = 0;
612 	else
613 		addr *= sector_size;
614 
615 	buf = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
616 
617 #if defined(_SUNOS_VTOC_16)
618 	if (mdb_vread(buf, sector_size, addr) == -1) {
619 		mdb_warn("failed to read VBR");
620 		return (DCMD_ERR);
621 	}
622 
623 	mdb_printf("VBR info:\n");
624 	(void) mbr_info((struct mboot *)buf);
625 #endif
626 
627 	vaddr = addr + DK_LABEL_LOC * sector_size;
628 
629 	if (mdb_vread(buf, sector_size, vaddr) == -1) {
630 		mdb_warn("failed to read VTOC");
631 		return (DCMD_ERR);
632 	}
633 
634 	dl = (struct dk_label *)buf;
635 	dv = (struct dk_vtoc *)&dl->dkl_vtoc;
636 
637 	mdb_printf("Label magic: 0x%hx (%s)\n", dl->dkl_magic,
638 	    dl->dkl_magic == DKL_MAGIC ? "valid" : "invalid");
639 	if (dl->dkl_magic != DKL_MAGIC)
640 		return (DCMD_ERR);
641 	mdb_printf("Label %s sane\n", dv->v_sanity == VTOC_SANE ?
642 	    "is" : "is not");
643 
644 	mdb_printf("Label version: %#x\n", dv->v_version);
645 	mdb_printf("Volume name = <%s>\n", dv->v_volume);
646 	mdb_printf("ASCII name  = <%s>\n", dv->v_asciilabel);
647 	mdb_printf("pcyl        = %4d\n", dl->dkl_pcyl);
648 	mdb_printf("ncyl        = %4d\n", dl->dkl_ncyl);
649 	mdb_printf("acyl        = %4d\n", dl->dkl_acyl);
650 
651 #if defined(_SUNOS_VTOC_16)
652 	mdb_printf("bcyl        = %4d\n", dl->dkl_bcyl);
653 #endif /* defined(_SUNOS_VTOC_16) */
654 
655 	mdb_printf("nhead       = %4d\n", dl->dkl_nhead);
656 	mdb_printf("nsect       = %4d\n", dl->dkl_nsect);
657 
658 
659 	if (!show_absolute)
660 		addr = 0;
661 	cyl = dl->dkl_nhead * dl->dkl_nsect;
662 	if (show_sectors)
663 		cyl = 1;
664 	else
665 		addr /= (cyl * sector_size);
666 
667 	tag_width = array_widest_str(ptag_array);
668 
669 	cyl_width = sizeof ("CYLINDERS");
670 	for (i = 0; i < dv->v_nparts; i++) {
671 		uint32_t start, end, size;
672 		int w;
673 
674 #if defined(_SUNOS_VTOC_16)
675 		start = addr + (dv->v_part[i].p_start / cyl);
676 		size = dv->v_part[i].p_size;
677 #elif defined(_SUNOS_VTOC_8)
678 		start = dl->dkl_map[i].dkl_cylno;
679 		start *= dl->dkl_nhead * dl->dkl_nsect; /* compute bytes */
680 		start /= cyl;
681 		start += addr;
682 		size = dl->dkl_map[i].dkl_nblk;
683 #else
684 #error "No VTOC format defined."
685 #endif
686 		if (size == 0)
687 			end = start = 0;
688 		else
689 			end = start + size / cyl - 1;
690 
691 		w = mdb_snprintf(NULL, 0, "%u - %u", start, end);
692 		if (w > cyl_width)
693 			cyl_width = w;
694 	}
695 
696 	if (show_sectors == B_TRUE) {
697 		mdb_printf("\n%<u>%-4s %-*s %-7s %-11s %-11s %-*s "
698 		    "%-10s%</u>\n", "PART", tag_width, "TAG", "FLAG",
699 		    "STARTLBA", "ENDLBA", MDB_NICENUM_BUFLEN, "SIZE", "BLOCKS");
700 	} else {
701 		mdb_printf("\n%<u>%-4s %-*s %-7s %-*s %-*s %-10s%</u>\n",
702 		    "PART", tag_width, "TAG", "FLAG", cyl_width, "CYLINDERS",
703 		    MDB_NICENUM_BUFLEN, "SIZE", "BLOCKS");
704 	}
705 
706 	for (i = 0; i < dv->v_nparts; i++) {
707 		uint16_t tag, flag;
708 		uint32_t start, end, size;
709 		const char *stag, *sflag;
710 		char nnum[MDB_NICENUM_BUFLEN];
711 
712 #if defined(_SUNOS_VTOC_16)
713 		tag = dv->v_part[i].p_tag;
714 		flag = dv->v_part[i].p_flag;
715 		start = addr + (dv->v_part[i].p_start / cyl);
716 		size = dv->v_part[i].p_size;
717 #elif defined(_SUNOS_VTOC_8)
718 		tag = dv->v_part[i].p_tag;
719 		flag = dv->v_part[i].p_flag;
720 		start = dl->dkl_map[i].dkl_cylno;
721 		start *= dl->dkl_nhead * dl->dkl_nsect; /* compute bytes */
722 		start /= cyl;
723 		start += addr;
724 		size = dl->dkl_map[i].dkl_nblk;
725 #else
726 #error "No VTOC format defined."
727 #endif
728 		if (size == 0)
729 			end = start = 0;
730 		else
731 			end = start + size / cyl - 1;
732 
733 		stag = array_find_string(ptag_array, tag);
734 		if (stag == NULL)
735 			stag = "?";
736 		sflag = array_find_string(pflag_array, flag);
737 		if (sflag == NULL)
738 			sflag = "?";
739 
740 		mdb_printf("%-4d %-*s %-7s ", i, tag_width, stag, sflag);
741 		mdb_nicenum(size * sector_size, nnum);
742 		if (show_sectors) {
743 			mdb_printf("%-11u %-11u %-*s %-10u\n", start, end,
744 			    MDB_NICENUM_BUFLEN, nnum, size);
745 		} else {
746 			char cyls[10 * 2 + 4];
747 
748 			if (size == 0) {
749 				mdb_snprintf(cyls, sizeof (cyls), "%-*u",
750 				    cyl_width, size);
751 			} else {
752 				mdb_snprintf(cyls, sizeof (cyls), "%u - %u",
753 				    start, end);
754 			}
755 			mdb_printf("%-*s %-*s %-10u\n", cyl_width, cyls,
756 			    MDB_NICENUM_BUFLEN, nnum, size);
757 		}
758 	}
759 
760 	return (DCMD_OK);
761 }
762 
763 void
764 vtoc_help(void)
765 {
766 	mdb_printf("Display a Virtual Table of Content (VTOC).\n\n"
767 	    "-r Display relative addresses\n"
768 	    "-c Use cylinder based addressing\n");
769 	mdb_printf("\nThe addr is in %u-byte disk blocks.\n", sector_size);
770 }
771 
772 static int
773 cmd_sect(uintptr_t addr __unused, uint_t flags __unused, int argc,
774     const mdb_arg_t *argv)
775 {
776 	uint64_t size = SECTOR_SIZE;
777 
778 	if (argc < 1) {
779 		mdb_printf("Current sector size is %u (%#x)\n", sector_size,
780 		    sector_size);
781 		return (DCMD_OK);
782 	}
783 
784 	if (argc != 1)
785 		return (DCMD_USAGE);
786 
787 	switch (argv[0].a_type) {
788 	case MDB_TYPE_STRING:
789 		size = mdb_strtoull(argv[0].a_un.a_str);
790 		break;
791 	case MDB_TYPE_IMMEDIATE:
792 		size = argv[0].a_un.a_val;
793 		break;
794 	default:
795 		return (DCMD_USAGE);
796 	}
797 
798 	if (!ISP2(size)) {
799 		mdb_printf("sector size must be power of 2\n");
800 		return (DCMD_USAGE);
801 	}
802 	sector_size = size;
803 	return (DCMD_OK);
804 }
805 
806 void
807 sect_help(void)
808 {
809 	mdb_printf("Show or set sector size.\n");
810 }
811 
812 static const mdb_dcmd_t dcmds[] = {
813 	{ "mbr", NULL, "dump Master Boot Record information", cmd_mbr },
814 	{ "gpt", "?[-ag]", "dump an EFI GPT", cmd_gpt, gpt_help },
815 	{ "vtoc", "?[-cr]", "dump VTOC information", cmd_vtoc, vtoc_help },
816 	{ "sectorsize", NULL, "set or show sector size", cmd_sect, sect_help },
817 	{ NULL }
818 };
819 
820 static const mdb_modinfo_t modinfo = {
821 	MDB_API_VERSION, dcmds, NULL
822 };
823 
824 const mdb_modinfo_t *
825 _mdb_init(void)
826 {
827 	return (&modinfo);
828 }
829