1*446c407dSJohn Levon /*
2*446c407dSJohn Levon  * This file and its contents are supplied under the terms of the
3*446c407dSJohn Levon  * Common Development and Distribution License ("CDDL"), version 1.0.
4*446c407dSJohn Levon  * You may only use this file in accordance with the terms of version
5*446c407dSJohn Levon  * 1.0 of the CDDL.
6*446c407dSJohn Levon  *
7*446c407dSJohn Levon  * A full copy of the text of the CDDL should have accompanied this
8*446c407dSJohn Levon  * source.  A copy of the CDDL is also available via the Internet at
9*446c407dSJohn Levon  * http://www.illumos.org/license/CDDL.
10*446c407dSJohn Levon  */
11*446c407dSJohn Levon 
12*446c407dSJohn Levon /*
13*446c407dSJohn Levon  * Copyright (c) 2019, Joyent, Inc.
14*446c407dSJohn Levon  */
15*446c407dSJohn Levon 
16*446c407dSJohn Levon /*
17*446c407dSJohn Levon  * The on-disk elements here are all little-endian, and this code doesn't make
18*446c407dSJohn Levon  * any attempt to adjust for running on a big-endian system.
19*446c407dSJohn Levon  *
20*446c407dSJohn Levon  * We also currently assume a 512-byte sized logical block.
21*446c407dSJohn Levon  */
22*446c407dSJohn Levon 
23*446c407dSJohn Levon #include <sys/types.h>
24*446c407dSJohn Levon #include <sys/crc32.h>
25*446c407dSJohn Levon #include <sys/debug.h>
26*446c407dSJohn Levon #include <sys/sysmacros.h>
27*446c407dSJohn Levon #include <sys/dktp/fdisk.h>
28*446c407dSJohn Levon #include <sys/efi_partition.h>
29*446c407dSJohn Levon 
30*446c407dSJohn Levon #include <assert.h>
31*446c407dSJohn Levon #include <ctype.h>
32*446c407dSJohn Levon #include <uuid/uuid.h>
33*446c407dSJohn Levon 
34*446c407dSJohn Levon #include <mdb/mdb_modapi.h>
35*446c407dSJohn Levon #include <mdb/mdb_debug.h>
36*446c407dSJohn Levon 
37*446c407dSJohn Levon #include "installboot.h"
38*446c407dSJohn Levon 
39*446c407dSJohn Levon #ifdef _BIG_ENDIAN
40*446c407dSJohn Levon #error needs porting for big-endian system
41*446c407dSJohn Levon #endif
42*446c407dSJohn Levon 
43*446c407dSJohn Levon /* See usr/src/grub/grub-0.97/stage1/stage1.h */
44*446c407dSJohn Levon #define	GRUB_VERSION_OFF (0x3e)
45*446c407dSJohn Levon #define	GRUB_COMPAT_VERSION_MAJOR 3
46*446c407dSJohn Levon #define	GRUB_COMPAT_VERSION_MINOR 2
47*446c407dSJohn Levon #define	GRUB_VERSION (2 << 8 | 3) /* 3.2 */
48*446c407dSJohn Levon 
49*446c407dSJohn Levon #define	LOADER_VERSION (1)
50*446c407dSJohn Levon #define	LOADER_JOYENT_VERSION (2)
51*446c407dSJohn Levon 
52*446c407dSJohn Levon typedef enum {
53*446c407dSJohn Levon 	MBR_TYPE_UNKNOWN,
54*446c407dSJohn Levon 	MBR_TYPE_GRUB1,
55*446c407dSJohn Levon 	MBR_TYPE_LOADER,
56*446c407dSJohn Levon 	MBR_TYPE_LOADER_JOYENT,
57*446c407dSJohn Levon } mbr_type_t;
58*446c407dSJohn Levon 
59*446c407dSJohn Levon static void
60*446c407dSJohn Levon print_fdisk_part(struct ipart *ip, size_t nr)
61*446c407dSJohn Levon {
62*446c407dSJohn Levon 	char typestr[128];
63*446c407dSJohn Levon 	char begchs[128];
64*446c407dSJohn Levon 	char endchs[128];
65*446c407dSJohn Levon 	char *c = NULL;
66*446c407dSJohn Levon 
67*446c407dSJohn Levon 	if (ip->systid == UNUSED) {
68*446c407dSJohn Levon 		mdb_printf("%-4llu %s:%#lx\n", nr, "UNUSED", ip->systid);
69*446c407dSJohn Levon 		return;
70*446c407dSJohn Levon 	}
71*446c407dSJohn Levon 
72*446c407dSJohn Levon 	switch (ip->systid) {
73*446c407dSJohn Levon 	case DOSOS12: c = "DOSOS12"; break;
74*446c407dSJohn Levon 	case PCIXOS: c = "PCIXOS"; break;
75*446c407dSJohn Levon 	case DOSOS16: c = "DOSOS16"; break;
76*446c407dSJohn Levon 	case EXTDOS: c = "EXTDOS"; break;
77*446c407dSJohn Levon 	case DOSHUGE: c = "DOSHUGE"; break;
78*446c407dSJohn Levon 	case FDISK_IFS: c = "FDISK_IFS"; break;
79*446c407dSJohn Levon 	case FDISK_AIXBOOT: c = "FDISK_AIXBOOT"; break;
80*446c407dSJohn Levon 	case FDISK_AIXDATA: c = "FDISK_AIXDATA"; break;
81*446c407dSJohn Levon 	case FDISK_OS2BOOT: c = "FDISK_OS2BOOT"; break;
82*446c407dSJohn Levon 	case FDISK_WINDOWS: c = "FDISK_WINDOWS"; break;
83*446c407dSJohn Levon 	case FDISK_EXT_WIN: c = "FDISK_EXT_WIN"; break;
84*446c407dSJohn Levon 	case FDISK_FAT95: c = "FDISK_FAT95"; break;
85*446c407dSJohn Levon 	case FDISK_EXTLBA: c = "FDISK_EXTLBA"; break;
86*446c407dSJohn Levon 	case DIAGPART: c = "DIAGPART"; break;
87*446c407dSJohn Levon 	case FDISK_LINUX: c = "FDISK_LINUX"; break;
88*446c407dSJohn Levon 	case FDISK_LINUXDSWAP: c = "FDISK_LINUXDSWAP"; break;
89*446c407dSJohn Levon 	case FDISK_LINUXDNAT: c = "FDISK_LINUXDNAT"; break;
90*446c407dSJohn Levon 	case FDISK_CPM: c = "FDISK_CPM"; break;
91*446c407dSJohn Levon 	case DOSDATA: c = "DOSDATA"; break;
92*446c407dSJohn Levon 	case OTHEROS: c = "OTHEROS"; break;
93*446c407dSJohn Levon 	case UNIXOS: c = "UNIXOS"; break;
94*446c407dSJohn Levon 	case FDISK_NOVELL2: c = "FDISK_NOVELL2"; break;
95*446c407dSJohn Levon 	case FDISK_NOVELL3: c = "FDISK_NOVELL3"; break;
96*446c407dSJohn Levon 	case FDISK_QNX4: c = "FDISK_QNX4"; break;
97*446c407dSJohn Levon 	case FDISK_QNX42: c = "FDISK_QNX42"; break;
98*446c407dSJohn Levon 	case FDISK_QNX43: c = "FDISK_QNX43"; break;
99*446c407dSJohn Levon 	case SUNIXOS: c = "SUNIXOS"; break;
100*446c407dSJohn Levon 	case FDISK_LINUXNAT: c = "FDISK_LINUXNAT"; break;
101*446c407dSJohn Levon 	case FDISK_NTFSVOL1: c = "FDISK_NTFSVOL1"; break;
102*446c407dSJohn Levon 	case FDISK_NTFSVOL2: c = "FDISK_NTFSVOL2"; break;
103*446c407dSJohn Levon 	case FDISK_BSD: c = "FDISK_BSD"; break;
104*446c407dSJohn Levon 	case FDISK_NEXTSTEP: c = "FDISK_NEXTSTEP"; break;
105*446c407dSJohn Levon 	case FDISK_BSDIFS: c = "FDISK_BSDIFS"; break;
106*446c407dSJohn Levon 	case FDISK_BSDISWAP: c = "FDISK_BSDISWAP"; break;
107*446c407dSJohn Levon 	case X86BOOT: c = "X86BOOT"; break;
108*446c407dSJohn Levon 	case SUNIXOS2: c = "SUNIXOS2"; break;
109*446c407dSJohn Levon 	case EFI_PMBR: c = "EFI_PMBR"; break;
110*446c407dSJohn Levon 	case EFI_FS: c = "EFI_FS"; break;
111*446c407dSJohn Levon 	default: c = NULL; break;
112*446c407dSJohn Levon 	}
113*446c407dSJohn Levon 
114*446c407dSJohn Levon 	if (c != NULL) {
115*446c407dSJohn Levon 		mdb_snprintf(typestr, sizeof (typestr), "%s:%#lx",
116*446c407dSJohn Levon 		    c, ip->systid);
117*446c407dSJohn Levon 	} else {
118*446c407dSJohn Levon 		mdb_snprintf(typestr, sizeof (typestr), "%#lx", ip->systid);
119*446c407dSJohn Levon 	}
120*446c407dSJohn Levon 
121*446c407dSJohn Levon 	mdb_snprintf(begchs, sizeof (begchs), "%hu/%hu/%hu",
122*446c407dSJohn Levon 	    (uint16_t)ip->begcyl | (uint16_t)(ip->begsect & ~0x3f) << 2,
123*446c407dSJohn Levon 	    (uint16_t)ip->beghead, (uint16_t)ip->begsect & 0x3f);
124*446c407dSJohn Levon 	mdb_snprintf(endchs, sizeof (endchs), "%hu/%hu/%hu",
125*446c407dSJohn Levon 	    (uint16_t)ip->endcyl | (uint16_t)(ip->endsect & ~0x3f) << 2,
126*446c407dSJohn Levon 	    (uint16_t)ip->endhead, (uint16_t)ip->endsect & 0x3f);
127*446c407dSJohn Levon 
128*446c407dSJohn Levon 	mdb_printf("%-4llu %-21s %#-7x %-11s %-11s %-10u %-9u\n",
129*446c407dSJohn Levon 	    nr, typestr, ip->bootid, begchs, endchs, ip->relsect, ip->numsect);
130*446c407dSJohn Levon }
131*446c407dSJohn Levon 
132*446c407dSJohn Levon static int
133*446c407dSJohn Levon cmd_mbr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv __unused)
134*446c407dSJohn Levon {
135*446c407dSJohn Levon 	struct mboot mbr;
136*446c407dSJohn Levon 	mbr_type_t type = MBR_TYPE_UNKNOWN;
137*446c407dSJohn Levon 
138*446c407dSJohn Levon 	CTASSERT(sizeof (mbr) == SECTOR_SIZE);
139*446c407dSJohn Levon 
140*446c407dSJohn Levon 	if (argc != 0)
141*446c407dSJohn Levon 		return (DCMD_USAGE);
142*446c407dSJohn Levon 
143*446c407dSJohn Levon 	if (!(flags & DCMD_ADDRSPEC))
144*446c407dSJohn Levon 		addr = 0;
145*446c407dSJohn Levon 
146*446c407dSJohn Levon 	if (mdb_vread(&mbr, sizeof (mbr), addr) == -1) {
147*446c407dSJohn Levon 		mdb_warn("failed to read MBR");
148*446c407dSJohn Levon 		return (DCMD_ERR);
149*446c407dSJohn Levon 	}
150*446c407dSJohn Levon 
151*446c407dSJohn Levon 	if (*((uint16_t *)&mbr.bootinst[GRUB_VERSION_OFF]) == GRUB_VERSION) {
152*446c407dSJohn Levon 		type = MBR_TYPE_GRUB1;
153*446c407dSJohn Levon 	} else if (mbr.bootinst[STAGE1_MBR_VERSION] == LOADER_VERSION) {
154*446c407dSJohn Levon 		type = MBR_TYPE_LOADER;
155*446c407dSJohn Levon 	} else if (mbr.bootinst[STAGE1_MBR_VERSION] == LOADER_JOYENT_VERSION) {
156*446c407dSJohn Levon 		type = MBR_TYPE_LOADER_JOYENT;
157*446c407dSJohn Levon 	}
158*446c407dSJohn Levon 
159*446c407dSJohn Levon 	switch (type) {
160*446c407dSJohn Levon 	case MBR_TYPE_UNKNOWN:
161*446c407dSJohn Levon 		mdb_printf("Format: unknown\n");
162*446c407dSJohn Levon 		break;
163*446c407dSJohn Levon 	case MBR_TYPE_GRUB1:
164*446c407dSJohn Levon 		mdb_printf("Format: grub1\n");
165*446c407dSJohn Levon 		break;
166*446c407dSJohn Levon 	case MBR_TYPE_LOADER:
167*446c407dSJohn Levon 		mdb_printf("Format: loader (illumos)\n");
168*446c407dSJohn Levon 		break;
169*446c407dSJohn Levon 	case MBR_TYPE_LOADER_JOYENT:
170*446c407dSJohn Levon 		mdb_printf("Format: loader (joyent)\n");
171*446c407dSJohn Levon 		break;
172*446c407dSJohn Levon 	}
173*446c407dSJohn Levon 
174*446c407dSJohn Levon 	mdb_printf("Signature: 0x%hx (%s)\n", mbr.signature,
175*446c407dSJohn Levon 	    mbr.signature == MBB_MAGIC ? "valid" : "invalid");
176*446c407dSJohn Levon 
177*446c407dSJohn Levon 	mdb_printf("UniqueMBRDiskSignature: %#lx\n",
178*446c407dSJohn Levon 	    *(uint32_t *)&mbr.bootinst[STAGE1_SIG]);
179*446c407dSJohn Levon 
180*446c407dSJohn Levon 	if (type == MBR_TYPE_LOADER || type == MBR_TYPE_LOADER_JOYENT) {
181*446c407dSJohn Levon 		char uuid[UUID_PRINTABLE_STRING_LENGTH];
182*446c407dSJohn Levon 
183*446c407dSJohn Levon 		mdb_printf("Loader STAGE1_STAGE2_LBA: %llu\n",
184*446c407dSJohn Levon 		    *(uint64_t *)&mbr.bootinst[STAGE1_STAGE2_LBA]);
185*446c407dSJohn Levon 
186*446c407dSJohn Levon 		mdb_printf("Loader STAGE1_STAGE2_SIZE: %hu\n",
187*446c407dSJohn Levon 		    *(uint16_t *)&mbr.bootinst[STAGE1_STAGE2_SIZE]);
188*446c407dSJohn Levon 
189*446c407dSJohn Levon 		uuid_unparse((uchar_t *)&mbr.bootinst[STAGE1_STAGE2_UUID],
190*446c407dSJohn Levon 		    uuid);
191*446c407dSJohn Levon 
192*446c407dSJohn Levon 		mdb_printf("Loader STAGE1_STAGE2_UUID: %s\n", uuid);
193*446c407dSJohn Levon 	}
194*446c407dSJohn Levon 
195*446c407dSJohn Levon 	mdb_printf("\n%<u>%-4s %-21s %-7s %-11s %-11s %-10s %-9s%</u>\n",
196*446c407dSJohn Levon 	    "PART", "TYPE", "ACTIVE", "STARTCHS", "ENDCHS",
197*446c407dSJohn Levon 	    "SECTOR", "NUMSECT");
198*446c407dSJohn Levon 
199*446c407dSJohn Levon 	for (size_t i = 0; i < FD_NUMPART; i++) {
200*446c407dSJohn Levon 		struct ipart *ip = (struct ipart *)
201*446c407dSJohn Levon 		    (mbr.parts + (sizeof (struct ipart) * i));
202*446c407dSJohn Levon 		print_fdisk_part(ip, i);
203*446c407dSJohn Levon 	}
204*446c407dSJohn Levon 
205*446c407dSJohn Levon 	return (DCMD_OK);
206*446c407dSJohn Levon }
207*446c407dSJohn Levon 
208*446c407dSJohn Levon static unsigned int crc32_tab[] = { CRC32_TABLE };
209*446c407dSJohn Levon 
210*446c407dSJohn Levon static unsigned int
211*446c407dSJohn Levon efi_crc32(const unsigned char *s, unsigned int len)
212*446c407dSJohn Levon {
213*446c407dSJohn Levon 	unsigned int crc32val;
214*446c407dSJohn Levon 
215*446c407dSJohn Levon 	CRC32(crc32val, s, len, -1U, crc32_tab);
216*446c407dSJohn Levon 
217*446c407dSJohn Levon 	return (crc32val ^ -1U);
218*446c407dSJohn Levon }
219*446c407dSJohn Levon 
220*446c407dSJohn Levon typedef struct {
221*446c407dSJohn Levon 	struct uuid eg_uuid;
222*446c407dSJohn Levon 	const char *eg_name;
223*446c407dSJohn Levon } efi_guid_t;
224*446c407dSJohn Levon 
225*446c407dSJohn Levon static efi_guid_t efi_guids[] = {
226*446c407dSJohn Levon 	{ EFI_UNUSED, "EFI_UNUSED" },
227*446c407dSJohn Levon 	{ EFI_RESV1, "EFI_RESV1" },
228*446c407dSJohn Levon 	{ EFI_BOOT, "EFI_BOOT" },
229*446c407dSJohn Levon 	{ EFI_ROOT, "EFI_ROOT" },
230*446c407dSJohn Levon 	{ EFI_SWAP, "EFI_SWAP" },
231*446c407dSJohn Levon 	{ EFI_USR, "EFI_USR" },
232*446c407dSJohn Levon 	{ EFI_BACKUP, "EFI_BACKUP" },
233*446c407dSJohn Levon 	{ EFI_RESV2, "EFI_RESV2" },
234*446c407dSJohn Levon 	{ EFI_VAR, "EFI_VAR" },
235*446c407dSJohn Levon 	{ EFI_HOME, "EFI_HOME" },
236*446c407dSJohn Levon 	{ EFI_ALTSCTR, "EFI_ALTSCTR" },
237*446c407dSJohn Levon 	{ EFI_RESERVED, "EFI_RESERVED" },
238*446c407dSJohn Levon 	{ EFI_SYSTEM, "EFI_SYSTEM" },
239*446c407dSJohn Levon 	{ EFI_LEGACY_MBR, "EFI_LEGACY_MBR" },
240*446c407dSJohn Levon 	{ EFI_SYMC_PUB, "EFI_SYMC_PUB" },
241*446c407dSJohn Levon 	{ EFI_SYMC_CDS, "EFI_SYMC_CDS" },
242*446c407dSJohn Levon 	{ EFI_MSFT_RESV, "EFI_MSFT_RESV" },
243*446c407dSJohn Levon 	{ EFI_DELL_BASIC, "EFI_DELL_BASIC" },
244*446c407dSJohn Levon 	{ EFI_DELL_RAID, "EFI_DELL_RAID" },
245*446c407dSJohn Levon 	{ EFI_DELL_SWAP, "EFI_DELL_SWAP" },
246*446c407dSJohn Levon 	{ EFI_DELL_LVM, "EFI_DELL_LVM" },
247*446c407dSJohn Levon 	{ EFI_DELL_RESV, "EFI_DELL_RESV" },
248*446c407dSJohn Levon 	{ EFI_AAPL_BOOT, "EFI_AAPL_BOOT" },
249*446c407dSJohn Levon 	{ EFI_AAPL_HFS, "EFI_AAPL_HFS" },
250*446c407dSJohn Levon 	{ EFI_AAPL_UFS, "EFI_AAPL_UFS" },
251*446c407dSJohn Levon 	{ EFI_AAPL_ZFS, "EFI_AAPL_ZFS" },
252*446c407dSJohn Levon 	{ EFI_AAPL_APFS, "EFI_AAPL_APFS" },
253*446c407dSJohn Levon 	{ EFI_FREEBSD_BOOT, "EFI_FREEBSD_BOOT" },
254*446c407dSJohn Levon 	{ EFI_FREEBSD_NANDFS, "EFI_FREEBSD_NANDFS" },
255*446c407dSJohn Levon 	{ EFI_FREEBSD_SWAP, "EFI_FREEBSD_SWAP" },
256*446c407dSJohn Levon 	{ EFI_FREEBSD_UFS, "EFI_FREEBSD_UFS" },
257*446c407dSJohn Levon 	{ EFI_FREEBSD_VINUM, "EFI_FREEBSD_VINUM" },
258*446c407dSJohn Levon 	{ EFI_FREEBSD_ZFS, "EFI_FREEBSD_ZFS" },
259*446c407dSJohn Levon 	{ EFI_BIOS_BOOT, "EFI_BIOS_BOOT" },
260*446c407dSJohn Levon };
261*446c407dSJohn Levon 
262*446c407dSJohn Levon static void
263*446c407dSJohn Levon print_gpe(efi_gpe_t *gpe, size_t nr, int show_guid)
264*446c407dSJohn Levon {
265*446c407dSJohn Levon 	const char *type = "unknown";
266*446c407dSJohn Levon 
267*446c407dSJohn Levon 	for (size_t i = 0; i < ARRAY_SIZE(efi_guids); i++) {
268*446c407dSJohn Levon 		if (memcmp((void *)&efi_guids[i].eg_uuid,
269*446c407dSJohn Levon 		    (void *)&gpe->efi_gpe_PartitionTypeGUID,
270*446c407dSJohn Levon 		    sizeof (efi_guids[i].eg_uuid)) == 0) {
271*446c407dSJohn Levon 			type = efi_guids[i].eg_name;
272*446c407dSJohn Levon 			break;
273*446c407dSJohn Levon 		}
274*446c407dSJohn Levon 	}
275*446c407dSJohn Levon 
276*446c407dSJohn Levon 	if (strcmp(type, "EFI_UNUSED") == 0) {
277*446c407dSJohn Levon 		mdb_printf("%-4u %-19s\n", nr, type);
278*446c407dSJohn Levon 		return;
279*446c407dSJohn Levon 	}
280*446c407dSJohn Levon 
281*446c407dSJohn Levon 	if (show_guid) {
282*446c407dSJohn Levon 		char guid[UUID_PRINTABLE_STRING_LENGTH];
283*446c407dSJohn Levon 
284*446c407dSJohn Levon 		uuid_unparse((uchar_t *)&gpe->efi_gpe_UniquePartitionGUID,
285*446c407dSJohn Levon 		    guid);
286*446c407dSJohn Levon 
287*446c407dSJohn Levon 		mdb_printf("%-4u %-19s %s\n", nr, type, guid);
288*446c407dSJohn Levon 	} else {
289*446c407dSJohn Levon 		char name[EFI_PART_NAME_LEN + 1] = "";
290*446c407dSJohn Levon 
291*446c407dSJohn Levon 		/*
292*446c407dSJohn Levon 		 * Hopefully, ASCII is sufficient for any naming we care about.
293*446c407dSJohn Levon 		 */
294*446c407dSJohn Levon 		for (size_t i = 0; i < sizeof (name); i++) {
295*446c407dSJohn Levon 			ushort_t wchar = gpe->efi_gpe_PartitionName[i];
296*446c407dSJohn Levon 
297*446c407dSJohn Levon 			name[i] = (char)(isascii(wchar) ? wchar : '?');
298*446c407dSJohn Levon 		}
299*446c407dSJohn Levon 
300*446c407dSJohn Levon 		mdb_printf("%-4u %-19s %-13llu %-13llu %#-8llx %s\n",
301*446c407dSJohn Levon 		    nr, type, gpe->efi_gpe_StartingLBA, gpe->efi_gpe_EndingLBA,
302*446c407dSJohn Levon 		    gpe->efi_gpe_Attributes, name);
303*446c407dSJohn Levon 	}
304*446c407dSJohn Levon }
305*446c407dSJohn Levon 
306*446c407dSJohn Levon static int
307*446c407dSJohn Levon cmd_gpt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv __unused)
308*446c407dSJohn Levon {
309*446c407dSJohn Levon 	char uuid[UUID_PRINTABLE_STRING_LENGTH];
310*446c407dSJohn Levon 	int show_alternate = B_FALSE;
311*446c407dSJohn Levon 	int show_guid = B_FALSE;
312*446c407dSJohn Levon 	efi_gpt_t altheader;
313*446c407dSJohn Levon 	size_t table_size;
314*446c407dSJohn Levon 	efi_gpt_t header;
315*446c407dSJohn Levon 	efi_gpe_t *gpet;
316*446c407dSJohn Levon 	uint_t orig_crc;
317*446c407dSJohn Levon 	uint_t crc;
318*446c407dSJohn Levon 
319*446c407dSJohn Levon 	if (mdb_getopts(argc, argv,
320*446c407dSJohn Levon 	    'a', MDB_OPT_SETBITS, TRUE, &show_alternate,
321*446c407dSJohn Levon 	    'g', MDB_OPT_SETBITS, TRUE, &show_guid,
322*446c407dSJohn Levon 	    NULL) != argc)
323*446c407dSJohn Levon 		return (DCMD_USAGE);
324*446c407dSJohn Levon 
325*446c407dSJohn Levon 	/* Primary header is at LBA 1. */
326*446c407dSJohn Levon 	if (!(flags & DCMD_ADDRSPEC))
327*446c407dSJohn Levon 		addr = SECTOR_SIZE;
328*446c407dSJohn Levon 
329*446c407dSJohn Levon 	if (mdb_vread(&header, sizeof (header), addr) == -1) {
330*446c407dSJohn Levon 		mdb_warn("failed to read GPT header");
331*446c407dSJohn Levon 		return (DCMD_ERR);
332*446c407dSJohn Levon 	}
333*446c407dSJohn Levon 
334*446c407dSJohn Levon 	if (show_alternate) {
335*446c407dSJohn Levon 		addr = header.efi_gpt_AlternateLBA * SECTOR_SIZE;
336*446c407dSJohn Levon 
337*446c407dSJohn Levon 		if (mdb_vread(&header, sizeof (header), addr) == -1) {
338*446c407dSJohn Levon 			mdb_warn("failed to read GPT header");
339*446c407dSJohn Levon 			return (DCMD_ERR);
340*446c407dSJohn Levon 		}
341*446c407dSJohn Levon 	}
342*446c407dSJohn Levon 
343*446c407dSJohn Levon 	mdb_printf("Signature: %s (%s)\n", (char *)&header.efi_gpt_Signature,
344*446c407dSJohn Levon 	    strncmp((char *)&header.efi_gpt_Signature, "EFI PART", 8) == 0 ?
345*446c407dSJohn Levon 	    "valid" : "invalid");
346*446c407dSJohn Levon 
347*446c407dSJohn Levon 	mdb_printf("Revision: %hu.%hu\n", header.efi_gpt_Revision >> 16,
348*446c407dSJohn Levon 	    header.efi_gpt_Revision);
349*446c407dSJohn Levon 
350*446c407dSJohn Levon 	mdb_printf("HeaderSize: %u bytes\n", header.efi_gpt_HeaderSize);
351*446c407dSJohn Levon 
352*446c407dSJohn Levon 	if (header.efi_gpt_HeaderSize > SECTOR_SIZE) {
353*446c407dSJohn Levon 		mdb_warn("invalid header size: skipping CRC\n");
354*446c407dSJohn Levon 	} else {
355*446c407dSJohn Levon 		orig_crc = header.efi_gpt_HeaderCRC32;
356*446c407dSJohn Levon 
357*446c407dSJohn Levon 		header.efi_gpt_HeaderCRC32 = 0;
358*446c407dSJohn Levon 
359*446c407dSJohn Levon 		crc = efi_crc32((unsigned char *)&header,
360*446c407dSJohn Levon 		    header.efi_gpt_HeaderSize);
361*446c407dSJohn Levon 
362*446c407dSJohn Levon 		mdb_printf("HeaderCRC32: %#x (should be %#x)\n", orig_crc, crc);
363*446c407dSJohn Levon 	}
364*446c407dSJohn Levon 
365*446c407dSJohn Levon 	mdb_printf("Reserved1: %#x (should be 0x0)\n",
366*446c407dSJohn Levon 	    header.efi_gpt_Reserved1);
367*446c407dSJohn Levon 
368*446c407dSJohn Levon 	mdb_printf("MyLBA: %llu (should be %llu)\n",
369*446c407dSJohn Levon 	    header.efi_gpt_MyLBA, addr / SECTOR_SIZE);
370*446c407dSJohn Levon 
371*446c407dSJohn Levon 	mdb_printf("AlternateLBA: %llu\n", header.efi_gpt_AlternateLBA);
372*446c407dSJohn Levon 	mdb_printf("FirstUsableLBA: %llu\n", header.efi_gpt_FirstUsableLBA);
373*446c407dSJohn Levon 	mdb_printf("LastUsableLBA: %llu\n", header.efi_gpt_LastUsableLBA);
374*446c407dSJohn Levon 
375*446c407dSJohn Levon 	if (header.efi_gpt_MyLBA >= header.efi_gpt_FirstUsableLBA &&
376*446c407dSJohn Levon 	    header.efi_gpt_MyLBA <= header.efi_gpt_LastUsableLBA) {
377*446c407dSJohn Levon 		mdb_warn("MyLBA is within usable LBA range\n");
378*446c407dSJohn Levon 	}
379*446c407dSJohn Levon 
380*446c407dSJohn Levon 	if (header.efi_gpt_AlternateLBA >= header.efi_gpt_FirstUsableLBA &&
381*446c407dSJohn Levon 	    header.efi_gpt_AlternateLBA <= header.efi_gpt_LastUsableLBA) {
382*446c407dSJohn Levon 		mdb_warn("AlternateLBA is within usable LBA range\n");
383*446c407dSJohn Levon 	}
384*446c407dSJohn Levon 
385*446c407dSJohn Levon 	if (mdb_vread(&altheader, sizeof (altheader),
386*446c407dSJohn Levon 	    header.efi_gpt_AlternateLBA * SECTOR_SIZE) == -1) {
387*446c407dSJohn Levon 		mdb_warn("failed to read alternate GPT header");
388*446c407dSJohn Levon 	} else {
389*446c407dSJohn Levon 		if (strncmp((char *)&altheader.efi_gpt_Signature,
390*446c407dSJohn Levon 		    "EFI PART", 8) != 0) {
391*446c407dSJohn Levon 			mdb_warn("found invalid alternate GPT header with "
392*446c407dSJohn Levon 			    "Signature: %s\n",
393*446c407dSJohn Levon 			    (char *)&altheader.efi_gpt_Signature);
394*446c407dSJohn Levon 		}
395*446c407dSJohn Levon 
396*446c407dSJohn Levon 		if (altheader.efi_gpt_MyLBA != header.efi_gpt_AlternateLBA) {
397*446c407dSJohn Levon 			mdb_warn("alternate GPT header at offset %#llx has "
398*446c407dSJohn Levon 			    "invalid MyLBA %llu\n",
399*446c407dSJohn Levon 			    header.efi_gpt_AlternateLBA * SECTOR_SIZE,
400*446c407dSJohn Levon 			    altheader.efi_gpt_MyLBA);
401*446c407dSJohn Levon 		}
402*446c407dSJohn Levon 
403*446c407dSJohn Levon 		if (altheader.efi_gpt_AlternateLBA != header.efi_gpt_MyLBA) {
404*446c407dSJohn Levon 			mdb_warn("alternate GPT header at offset %#llx has "
405*446c407dSJohn Levon 			    "invalid AlternateLBA %llu\n",
406*446c407dSJohn Levon 			    header.efi_gpt_AlternateLBA * SECTOR_SIZE,
407*446c407dSJohn Levon 			    altheader.efi_gpt_AlternateLBA);
408*446c407dSJohn Levon 		}
409*446c407dSJohn Levon 
410*446c407dSJohn Levon 		/*
411*446c407dSJohn Levon 		 * We could go ahead and verify all the alternate checksums,
412*446c407dSJohn Levon 		 * etc. here too...
413*446c407dSJohn Levon 		 */
414*446c407dSJohn Levon 	}
415*446c407dSJohn Levon 
416*446c407dSJohn Levon 	uuid_unparse((uchar_t *)&header.efi_gpt_DiskGUID, uuid);
417*446c407dSJohn Levon 	mdb_printf("DiskGUID: %s\n", uuid);
418*446c407dSJohn Levon 
419*446c407dSJohn Levon 	mdb_printf("PartitionEntryLBA: %llu\n",
420*446c407dSJohn Levon 	    header.efi_gpt_PartitionEntryLBA);
421*446c407dSJohn Levon 
422*446c407dSJohn Levon 	mdb_printf("NumberOfPartitionEntries: %u\n",
423*446c407dSJohn Levon 	    header.efi_gpt_NumberOfPartitionEntries);
424*446c407dSJohn Levon 
425*446c407dSJohn Levon 	/*
426*446c407dSJohn Levon 	 * While the spec allows a different size, in practice the table
427*446c407dSJohn Levon 	 * is always packed.
428*446c407dSJohn Levon 	 */
429*446c407dSJohn Levon 	if (header.efi_gpt_SizeOfPartitionEntry != sizeof (efi_gpe_t)) {
430*446c407dSJohn Levon 		mdb_warn("SizeOfPartitionEntry: %#x bytes "
431*446c407dSJohn Levon 		    "(expected %#x bytes)\n",
432*446c407dSJohn Levon 		    header.efi_gpt_SizeOfPartitionEntry, sizeof (efi_gpe_t));
433*446c407dSJohn Levon 		return (DCMD_ERR);
434*446c407dSJohn Levon 	}
435*446c407dSJohn Levon 
436*446c407dSJohn Levon 	mdb_printf("SizeOfPartitionEntry: %#x bytes\n",
437*446c407dSJohn Levon 	    header.efi_gpt_SizeOfPartitionEntry);
438*446c407dSJohn Levon 
439*446c407dSJohn Levon 	table_size = header.efi_gpt_SizeOfPartitionEntry *
440*446c407dSJohn Levon 	    header.efi_gpt_NumberOfPartitionEntries;
441*446c407dSJohn Levon 
442*446c407dSJohn Levon 	/*
443*446c407dSJohn Levon 	 * While this is a minimum reservation, it serves us ably as a
444*446c407dSJohn Levon 	 * maximum value to reasonably expect.
445*446c407dSJohn Levon 	 */
446*446c407dSJohn Levon 	if (table_size > EFI_MIN_ARRAY_SIZE) {
447*446c407dSJohn Levon 		mdb_warn("Skipping GPT array of %#lx bytes.\n", table_size);
448*446c407dSJohn Levon 		return (DCMD_ERR);
449*446c407dSJohn Levon 	}
450*446c407dSJohn Levon 
451*446c407dSJohn Levon 	gpet = mdb_alloc(header.efi_gpt_SizeOfPartitionEntry *
452*446c407dSJohn Levon 	    header.efi_gpt_NumberOfPartitionEntries, UM_SLEEP | UM_GC);
453*446c407dSJohn Levon 
454*446c407dSJohn Levon 	if (mdb_vread(gpet, table_size,
455*446c407dSJohn Levon 	    header.efi_gpt_PartitionEntryLBA * SECTOR_SIZE) == -1) {
456*446c407dSJohn Levon 		mdb_warn("couldn't read GPT array");
457*446c407dSJohn Levon 		return (DCMD_ERR);
458*446c407dSJohn Levon 	}
459*446c407dSJohn Levon 
460*446c407dSJohn Levon 	crc = efi_crc32((unsigned char *)gpet, table_size);
461*446c407dSJohn Levon 
462*446c407dSJohn Levon 	mdb_printf("PartitionEntryArrayCRC32: %#x (should be %#x)\n",
463*446c407dSJohn Levon 	    header.efi_gpt_PartitionEntryArrayCRC32, crc);
464*446c407dSJohn Levon 
465*446c407dSJohn Levon 	if (show_guid) {
466*446c407dSJohn Levon 		mdb_printf("\n%<u>%-4s %-19s %-37s%</u>\n",
467*446c407dSJohn Levon 		    "PART", "TYPE", "GUID");
468*446c407dSJohn Levon 	} else {
469*446c407dSJohn Levon 		mdb_printf("\n%<u>%-4s %-19s %-13s %-13s %-8s %s%</u>\n",
470*446c407dSJohn Levon 		    "PART", "TYPE", "STARTLBA", "ENDLBA", "ATTR", "NAME");
471*446c407dSJohn Levon 	}
472*446c407dSJohn Levon 
473*446c407dSJohn Levon 	for (size_t i = 0; i < header.efi_gpt_NumberOfPartitionEntries; i++)
474*446c407dSJohn Levon 		print_gpe(&gpet[i], i, show_guid);
475*446c407dSJohn Levon 
476*446c407dSJohn Levon 	return (DCMD_OK);
477*446c407dSJohn Levon }
478*446c407dSJohn Levon 
479*446c407dSJohn Levon void
480*446c407dSJohn Levon gpt_help(void)
481*446c407dSJohn Levon {
482*446c407dSJohn Levon 	mdb_printf("Display an EFI GUID Partition Table.\n\n"
483*446c407dSJohn Levon 	    "-a Display the alternate GPT\n"
484*446c407dSJohn Levon 	    "-g Show unique GUID for each table entry\n");
485*446c407dSJohn Levon }
486*446c407dSJohn Levon 
487*446c407dSJohn Levon static const mdb_dcmd_t dcmds[] = {
488*446c407dSJohn Levon 	{ "mbr", NULL, "dump Master Boot Record information", cmd_mbr },
489*446c407dSJohn Levon 	{ "gpt", "?[-ag]", "dump an EFI GPT", cmd_gpt, gpt_help },
490*446c407dSJohn Levon 	{ NULL }
491*446c407dSJohn Levon };
492*446c407dSJohn Levon 
493*446c407dSJohn Levon static const mdb_modinfo_t modinfo = {
494*446c407dSJohn Levon 	MDB_API_VERSION, dcmds, NULL
495*446c407dSJohn Levon };
496*446c407dSJohn Levon 
497*446c407dSJohn Levon const mdb_modinfo_t *
498*446c407dSJohn Levon _mdb_init(void)
499*446c407dSJohn Levon {
500*446c407dSJohn Levon 	return (&modinfo);
501*446c407dSJohn Levon }
502