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 *
array_find_string(stringval_t * array,int match_value)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
array_widest_str(stringval_t * array)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
print_fdisk_part(struct ipart * ip,size_t nr)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
mbr_info(struct mboot * mbr)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
cmd_mbr(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv __unused)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
efi_crc32(const unsigned char * s,unsigned int len)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
print_gpe(efi_gpe_t * gpe,size_t nr,int show_guid)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
cmd_gpt(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv __unused)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,
566 header->efi_gpt_SizeOfPartitionEntry *
567 header->efi_gpt_NumberOfPartitionEntries);
568
569 mdb_printf("PartitionEntryArrayCRC32: %#x (should be %#x)\n",
570 header->efi_gpt_PartitionEntryArrayCRC32, crc);
571
572 if (show_guid) {
573 mdb_printf("\n%<u>%-4s %-19s %-37s%</u>\n",
574 "PART", "TYPE", "GUID");
575 } else {
576 mdb_printf("\n%<u>%-4s %-19s %-13s %-13s %-8s %s%</u>\n",
577 "PART", "TYPE", "STARTLBA", "ENDLBA", "ATTR", "NAME");
578 }
579
580 for (size_t i = 0; i < header->efi_gpt_NumberOfPartitionEntries; i++)
581 print_gpe(&gpet[i], i, show_guid);
582
583 return (DCMD_OK);
584 }
585
586 void
gpt_help(void)587 gpt_help(void)
588 {
589 mdb_printf("Display an EFI GUID Partition Table.\n\n"
590 "-a Display the alternate GPT\n"
591 "-g Show unique GUID for each table entry\n");
592 }
593
594 static int
cmd_vtoc(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)595 cmd_vtoc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
596 {
597 uint8_t *buf;
598 struct dk_label *dl;
599 struct dk_vtoc *dv;
600 uintptr_t vaddr;
601 int i, tag_width, cyl_width;
602 int show_absolute = B_TRUE;
603 int show_sectors = B_TRUE;
604 uint32_t cyl;
605
606 if (mdb_getopts(argc, argv,
607 'c', MDB_OPT_CLRBITS, TRUE, &show_sectors,
608 'r', MDB_OPT_CLRBITS, TRUE, &show_absolute,
609 NULL) != argc)
610 return (DCMD_USAGE);
611
612 if (!(flags & DCMD_ADDRSPEC))
613 addr = 0;
614 else
615 addr *= sector_size;
616
617 buf = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
618
619 #if defined(_SUNOS_VTOC_16)
620 if (mdb_vread(buf, sector_size, addr) == -1) {
621 mdb_warn("failed to read VBR");
622 return (DCMD_ERR);
623 }
624
625 mdb_printf("VBR info:\n");
626 (void) mbr_info((struct mboot *)buf);
627 #endif
628
629 vaddr = addr + DK_LABEL_LOC * sector_size;
630
631 if (mdb_vread(buf, sector_size, vaddr) == -1) {
632 mdb_warn("failed to read VTOC");
633 return (DCMD_ERR);
634 }
635
636 dl = (struct dk_label *)buf;
637 dv = (struct dk_vtoc *)&dl->dkl_vtoc;
638
639 mdb_printf("Label magic: 0x%hx (%s)\n", dl->dkl_magic,
640 dl->dkl_magic == DKL_MAGIC ? "valid" : "invalid");
641 if (dl->dkl_magic != DKL_MAGIC)
642 return (DCMD_ERR);
643 mdb_printf("Label %s sane\n", dv->v_sanity == VTOC_SANE ?
644 "is" : "is not");
645
646 mdb_printf("Label version: %#x\n", dv->v_version);
647 mdb_printf("Volume name = <%s>\n", dv->v_volume);
648 mdb_printf("ASCII name = <%s>\n", dv->v_asciilabel);
649 mdb_printf("pcyl = %4d\n", dl->dkl_pcyl);
650 mdb_printf("ncyl = %4d\n", dl->dkl_ncyl);
651 mdb_printf("acyl = %4d\n", dl->dkl_acyl);
652
653 #if defined(_SUNOS_VTOC_16)
654 mdb_printf("bcyl = %4d\n", dl->dkl_bcyl);
655 #endif /* defined(_SUNOS_VTOC_16) */
656
657 mdb_printf("nhead = %4d\n", dl->dkl_nhead);
658 mdb_printf("nsect = %4d\n", dl->dkl_nsect);
659
660
661 if (!show_absolute)
662 addr = 0;
663 cyl = dl->dkl_nhead * dl->dkl_nsect;
664 if (show_sectors)
665 cyl = 1;
666 else
667 addr /= (cyl * sector_size);
668
669 tag_width = array_widest_str(ptag_array);
670
671 cyl_width = sizeof ("CYLINDERS");
672 for (i = 0; i < dv->v_nparts; i++) {
673 uint32_t start, end, size;
674 int w;
675
676 #if defined(_SUNOS_VTOC_16)
677 start = addr + (dv->v_part[i].p_start / cyl);
678 size = dv->v_part[i].p_size;
679 #elif defined(_SUNOS_VTOC_8)
680 start = dl->dkl_map[i].dkl_cylno;
681 start *= dl->dkl_nhead * dl->dkl_nsect; /* compute bytes */
682 start /= cyl;
683 start += addr;
684 size = dl->dkl_map[i].dkl_nblk;
685 #else
686 #error "No VTOC format defined."
687 #endif
688 if (size == 0)
689 end = start = 0;
690 else
691 end = start + size / cyl - 1;
692
693 w = mdb_snprintf(NULL, 0, "%u - %u", start, end);
694 if (w > cyl_width)
695 cyl_width = w;
696 }
697
698 if (show_sectors == B_TRUE) {
699 mdb_printf("\n%<u>%-4s %-*s %-7s %-11s %-11s %-*s "
700 "%-10s%</u>\n", "PART", tag_width, "TAG", "FLAG",
701 "STARTLBA", "ENDLBA", MDB_NICENUM_BUFLEN, "SIZE", "BLOCKS");
702 } else {
703 mdb_printf("\n%<u>%-4s %-*s %-7s %-*s %-*s %-10s%</u>\n",
704 "PART", tag_width, "TAG", "FLAG", cyl_width, "CYLINDERS",
705 MDB_NICENUM_BUFLEN, "SIZE", "BLOCKS");
706 }
707
708 for (i = 0; i < dv->v_nparts; i++) {
709 uint16_t tag, flag;
710 uint32_t start, end, size;
711 const char *stag, *sflag;
712 char nnum[MDB_NICENUM_BUFLEN];
713
714 #if defined(_SUNOS_VTOC_16)
715 tag = dv->v_part[i].p_tag;
716 flag = dv->v_part[i].p_flag;
717 start = addr + (dv->v_part[i].p_start / cyl);
718 size = dv->v_part[i].p_size;
719 #elif defined(_SUNOS_VTOC_8)
720 tag = dv->v_part[i].p_tag;
721 flag = dv->v_part[i].p_flag;
722 start = dl->dkl_map[i].dkl_cylno;
723 start *= dl->dkl_nhead * dl->dkl_nsect; /* compute bytes */
724 start /= cyl;
725 start += addr;
726 size = dl->dkl_map[i].dkl_nblk;
727 #else
728 #error "No VTOC format defined."
729 #endif
730 if (size == 0)
731 end = start = 0;
732 else
733 end = start + size / cyl - 1;
734
735 stag = array_find_string(ptag_array, tag);
736 if (stag == NULL)
737 stag = "?";
738 sflag = array_find_string(pflag_array, flag);
739 if (sflag == NULL)
740 sflag = "?";
741
742 mdb_printf("%-4d %-*s %-7s ", i, tag_width, stag, sflag);
743 mdb_nicenum(size * sector_size, nnum);
744 if (show_sectors) {
745 mdb_printf("%-11u %-11u %-*s %-10u\n", start, end,
746 MDB_NICENUM_BUFLEN, nnum, size);
747 } else {
748 char cyls[10 * 2 + 4];
749
750 if (size == 0) {
751 mdb_snprintf(cyls, sizeof (cyls), "%-*u",
752 cyl_width, size);
753 } else {
754 mdb_snprintf(cyls, sizeof (cyls), "%u - %u",
755 start, end);
756 }
757 mdb_printf("%-*s %-*s %-10u\n", cyl_width, cyls,
758 MDB_NICENUM_BUFLEN, nnum, size);
759 }
760 }
761
762 return (DCMD_OK);
763 }
764
765 void
vtoc_help(void)766 vtoc_help(void)
767 {
768 mdb_printf("Display a Virtual Table of Content (VTOC).\n\n"
769 "-r Display relative addresses\n"
770 "-c Use cylinder based addressing\n");
771 mdb_printf("\nThe addr is in %u-byte disk blocks.\n", sector_size);
772 }
773
774 static int
cmd_sect(uintptr_t addr __unused,uint_t flags __unused,int argc,const mdb_arg_t * argv)775 cmd_sect(uintptr_t addr __unused, uint_t flags __unused, int argc,
776 const mdb_arg_t *argv)
777 {
778 uint64_t size = SECTOR_SIZE;
779
780 if (argc < 1) {
781 mdb_printf("Current sector size is %u (%#x)\n", sector_size,
782 sector_size);
783 return (DCMD_OK);
784 }
785
786 if (argc != 1)
787 return (DCMD_USAGE);
788
789 switch (argv[0].a_type) {
790 case MDB_TYPE_STRING:
791 size = mdb_strtoull(argv[0].a_un.a_str);
792 break;
793 case MDB_TYPE_IMMEDIATE:
794 size = argv[0].a_un.a_val;
795 break;
796 default:
797 return (DCMD_USAGE);
798 }
799
800 if (!ISP2(size)) {
801 mdb_printf("sector size must be power of 2\n");
802 return (DCMD_USAGE);
803 }
804 sector_size = size;
805 return (DCMD_OK);
806 }
807
808 void
sect_help(void)809 sect_help(void)
810 {
811 mdb_printf("Show or set sector size.\n");
812 }
813
814 static const mdb_dcmd_t dcmds[] = {
815 { "mbr", NULL, "dump Master Boot Record information", cmd_mbr },
816 { "gpt", "?[-ag]", "dump an EFI GPT", cmd_gpt, gpt_help },
817 { "vtoc", "?[-cr]", "dump VTOC information", cmd_vtoc, vtoc_help },
818 { "sectorsize", NULL, "set or show sector size", cmd_sect, sect_help },
819 { NULL }
820 };
821
822 static const mdb_modinfo_t modinfo = {
823 MDB_API_VERSION, dcmds, NULL
824 };
825
826 const mdb_modinfo_t *
_mdb_init(void)827 _mdb_init(void)
828 {
829 return (&modinfo);
830 }
831