xref: /illumos-gate/usr/src/cmd/mdb/common/modules/disk_label/disk_label.c (revision c1a180511b31c8284f28e3c3a69ed55e290930e2)
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