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 * We also currently assume a 512-byte sized logical block. 21 */ 22 23 #include <sys/types.h> 24 #include <sys/crc32.h> 25 #include <sys/debug.h> 26 #include <sys/sysmacros.h> 27 #include <sys/dktp/fdisk.h> 28 #include <sys/efi_partition.h> 29 #include <sys/vtoc.h> 30 31 #include <assert.h> 32 #include <ctype.h> 33 #include <uuid/uuid.h> 34 35 #include <mdb/mdb_modapi.h> 36 #include <mdb/mdb_debug.h> 37 38 #include "installboot.h" 39 40 #ifdef _BIG_ENDIAN 41 #error needs porting for big-endian system 42 #endif 43 44 /* See usr/src/grub/grub-0.97/stage1/stage1.h */ 45 #define GRUB_VERSION_OFF (0x3e) 46 #define GRUB_COMPAT_VERSION_MAJOR 3 47 #define GRUB_COMPAT_VERSION_MINOR 2 48 #define GRUB_VERSION (2 << 8 | 3) /* 3.2 */ 49 50 #define LOADER_VERSION (1) 51 #define LOADER_JOYENT_VERSION (2) 52 53 typedef enum { 54 MBR_TYPE_UNKNOWN, 55 MBR_TYPE_GRUB1, 56 MBR_TYPE_LOADER, 57 MBR_TYPE_LOADER_JOYENT, 58 } mbr_type_t; 59 60 typedef struct stringval { 61 const char *sv_text; 62 int sv_value; 63 } stringval_t; 64 65 stringval_t ptag_array[] = { 66 { "unassigned", V_UNASSIGNED }, 67 { "boot", V_BOOT }, 68 { "root", V_ROOT }, 69 { "swap", V_SWAP }, 70 { "usr", V_USR }, 71 { "backup", V_BACKUP }, 72 { "stand", V_STAND }, 73 { "var", V_VAR }, 74 { "home", V_HOME }, 75 { "alternates", V_ALTSCTR }, 76 { "reserved", V_RESERVED }, 77 { "system", V_SYSTEM }, 78 { "BIOS_boot", V_BIOS_BOOT }, 79 { "FreeBSD boot", V_FREEBSD_BOOT }, 80 { "FreeBSD swap", V_FREEBSD_SWAP }, 81 { "FreeBSD UFS", V_FREEBSD_UFS }, 82 { "FreeBSD ZFS", V_FREEBSD_ZFS }, 83 { "FreeBSD NANDFS", V_FREEBSD_NANDFS }, 84 85 { NULL } 86 }; 87 88 stringval_t pflag_array[] = { 89 { "wm", 0 }, 90 { "wu", V_UNMNT }, 91 { "rm", V_RONLY }, 92 { "ru", V_RONLY | V_UNMNT }, 93 { NULL } 94 }; 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 if (mdb_vread(&mbr, sizeof (mbr), addr) == -1) { 263 mdb_warn("failed to read MBR"); 264 return (DCMD_ERR); 265 } 266 267 type = mbr_info(&mbr); 268 269 /* If the magic is wrong, stop here. */ 270 if (mbr.signature != MBB_MAGIC) 271 return (DCMD_ERR); 272 273 /* Also print volume boot record */ 274 switch (type) { 275 case MBR_TYPE_LOADER: 276 case MBR_TYPE_LOADER_JOYENT: 277 if (*(uint16_t *)&mbr.bootinst[STAGE1_STAGE2_SIZE] == 1) { 278 struct mboot vbr; 279 uintptr_t vbrp; 280 281 vbrp = *(uint64_t *)&mbr.bootinst[STAGE1_STAGE2_LBA]; 282 vbrp *= SECTOR_SIZE; 283 vbrp += addr; 284 if (mdb_vread(&vbr, sizeof (vbr), vbrp) == -1) { 285 mdb_warn("failed to read VBR"); 286 } else { 287 mdb_printf("\nSTAGE1 in VBR:\n"); 288 (void) mbr_info(&vbr); 289 } 290 } 291 break; 292 default: 293 break; 294 } 295 296 mdb_printf("\n%<u>%-4s %-21s %-7s %-11s %-11s %-10s %-9s%</u>\n", 297 "PART", "TYPE", "ACTIVE", "STARTCHS", "ENDCHS", 298 "SECTOR", "NUMSECT"); 299 300 for (size_t i = 0; i < FD_NUMPART; i++) { 301 struct ipart *ip = (struct ipart *) 302 (mbr.parts + (sizeof (struct ipart) * i)); 303 print_fdisk_part(ip, i); 304 } 305 306 return (DCMD_OK); 307 } 308 309 static unsigned int crc32_tab[] = { CRC32_TABLE }; 310 311 static unsigned int 312 efi_crc32(const unsigned char *s, unsigned int len) 313 { 314 unsigned int crc32val; 315 316 CRC32(crc32val, s, len, -1U, crc32_tab); 317 318 return (crc32val ^ -1U); 319 } 320 321 typedef struct { 322 struct uuid eg_uuid; 323 const char *eg_name; 324 } efi_guid_t; 325 326 static efi_guid_t efi_guids[] = { 327 { EFI_UNUSED, "EFI_UNUSED" }, 328 { EFI_RESV1, "EFI_RESV1" }, 329 { EFI_BOOT, "EFI_BOOT" }, 330 { EFI_ROOT, "EFI_ROOT" }, 331 { EFI_SWAP, "EFI_SWAP" }, 332 { EFI_USR, "EFI_USR" }, 333 { EFI_BACKUP, "EFI_BACKUP" }, 334 { EFI_RESV2, "EFI_RESV2" }, 335 { EFI_VAR, "EFI_VAR" }, 336 { EFI_HOME, "EFI_HOME" }, 337 { EFI_ALTSCTR, "EFI_ALTSCTR" }, 338 { EFI_RESERVED, "EFI_RESERVED" }, 339 { EFI_SYSTEM, "EFI_SYSTEM" }, 340 { EFI_LEGACY_MBR, "EFI_LEGACY_MBR" }, 341 { EFI_SYMC_PUB, "EFI_SYMC_PUB" }, 342 { EFI_SYMC_CDS, "EFI_SYMC_CDS" }, 343 { EFI_MSFT_RESV, "EFI_MSFT_RESV" }, 344 { EFI_DELL_BASIC, "EFI_DELL_BASIC" }, 345 { EFI_DELL_RAID, "EFI_DELL_RAID" }, 346 { EFI_DELL_SWAP, "EFI_DELL_SWAP" }, 347 { EFI_DELL_LVM, "EFI_DELL_LVM" }, 348 { EFI_DELL_RESV, "EFI_DELL_RESV" }, 349 { EFI_AAPL_BOOT, "EFI_AAPL_BOOT" }, 350 { EFI_AAPL_HFS, "EFI_AAPL_HFS" }, 351 { EFI_AAPL_UFS, "EFI_AAPL_UFS" }, 352 { EFI_AAPL_ZFS, "EFI_AAPL_ZFS" }, 353 { EFI_AAPL_APFS, "EFI_AAPL_APFS" }, 354 { EFI_FREEBSD_BOOT, "EFI_FREEBSD_BOOT" }, 355 { EFI_FREEBSD_NANDFS, "EFI_FREEBSD_NANDFS" }, 356 { EFI_FREEBSD_SWAP, "EFI_FREEBSD_SWAP" }, 357 { EFI_FREEBSD_UFS, "EFI_FREEBSD_UFS" }, 358 { EFI_FREEBSD_VINUM, "EFI_FREEBSD_VINUM" }, 359 { EFI_FREEBSD_ZFS, "EFI_FREEBSD_ZFS" }, 360 { EFI_BIOS_BOOT, "EFI_BIOS_BOOT" }, 361 }; 362 363 static void 364 print_gpe(efi_gpe_t *gpe, size_t nr, int show_guid) 365 { 366 const char *type = "unknown"; 367 368 for (size_t i = 0; i < ARRAY_SIZE(efi_guids); i++) { 369 if (memcmp((void *)&efi_guids[i].eg_uuid, 370 (void *)&gpe->efi_gpe_PartitionTypeGUID, 371 sizeof (efi_guids[i].eg_uuid)) == 0) { 372 type = efi_guids[i].eg_name; 373 break; 374 } 375 } 376 377 if (strcmp(type, "EFI_UNUSED") == 0) { 378 mdb_printf("%-4u %-19s\n", nr, type); 379 return; 380 } 381 382 if (show_guid) { 383 char guid[UUID_PRINTABLE_STRING_LENGTH]; 384 385 uuid_unparse((uchar_t *)&gpe->efi_gpe_UniquePartitionGUID, 386 guid); 387 388 mdb_printf("%-4u %-19s %s\n", nr, type, guid); 389 } else { 390 char name[EFI_PART_NAME_LEN + 1] = ""; 391 392 /* 393 * Hopefully, ASCII is sufficient for any naming we care about. 394 */ 395 for (size_t i = 0; i < sizeof (name); i++) { 396 ushort_t wchar = gpe->efi_gpe_PartitionName[i]; 397 398 name[i] = (char)(isascii(wchar) ? wchar : '?'); 399 } 400 401 mdb_printf("%-4u %-19s %-13llu %-13llu %#-8llx %s\n", 402 nr, type, gpe->efi_gpe_StartingLBA, gpe->efi_gpe_EndingLBA, 403 gpe->efi_gpe_Attributes, name); 404 } 405 } 406 407 static int 408 cmd_gpt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv __unused) 409 { 410 char uuid[UUID_PRINTABLE_STRING_LENGTH]; 411 int show_alternate = B_FALSE; 412 int show_guid = B_FALSE; 413 efi_gpt_t altheader; 414 size_t table_size; 415 efi_gpt_t header; 416 efi_gpe_t *gpet; 417 uint_t orig_crc; 418 uint_t crc; 419 420 if (mdb_getopts(argc, argv, 421 'a', MDB_OPT_SETBITS, TRUE, &show_alternate, 422 'g', MDB_OPT_SETBITS, TRUE, &show_guid, 423 NULL) != argc) 424 return (DCMD_USAGE); 425 426 /* Primary header is at LBA 1. */ 427 if (!(flags & DCMD_ADDRSPEC)) 428 addr = SECTOR_SIZE; 429 430 if (mdb_vread(&header, sizeof (header), addr) == -1) { 431 mdb_warn("failed to read GPT header"); 432 return (DCMD_ERR); 433 } 434 435 if (show_alternate) { 436 addr = header.efi_gpt_AlternateLBA * SECTOR_SIZE; 437 438 if (mdb_vread(&header, sizeof (header), addr) == -1) { 439 mdb_warn("failed to read GPT header"); 440 return (DCMD_ERR); 441 } 442 } 443 444 mdb_printf("Signature: %s (%s)\n", (char *)&header.efi_gpt_Signature, 445 strncmp((char *)&header.efi_gpt_Signature, "EFI PART", 8) == 0 ? 446 "valid" : "invalid"); 447 448 mdb_printf("Revision: %hu.%hu\n", header.efi_gpt_Revision >> 16, 449 header.efi_gpt_Revision); 450 451 mdb_printf("HeaderSize: %u bytes\n", header.efi_gpt_HeaderSize); 452 453 if (header.efi_gpt_HeaderSize > SECTOR_SIZE) { 454 mdb_warn("invalid header size: skipping CRC\n"); 455 } else { 456 orig_crc = header.efi_gpt_HeaderCRC32; 457 458 header.efi_gpt_HeaderCRC32 = 0; 459 460 crc = efi_crc32((unsigned char *)&header, 461 header.efi_gpt_HeaderSize); 462 463 mdb_printf("HeaderCRC32: %#x (should be %#x)\n", orig_crc, crc); 464 } 465 466 mdb_printf("Reserved1: %#x (should be 0x0)\n", 467 header.efi_gpt_Reserved1); 468 469 mdb_printf("MyLBA: %llu (should be %llu)\n", 470 header.efi_gpt_MyLBA, addr / SECTOR_SIZE); 471 472 mdb_printf("AlternateLBA: %llu\n", header.efi_gpt_AlternateLBA); 473 mdb_printf("FirstUsableLBA: %llu\n", header.efi_gpt_FirstUsableLBA); 474 mdb_printf("LastUsableLBA: %llu\n", header.efi_gpt_LastUsableLBA); 475 476 if (header.efi_gpt_MyLBA >= header.efi_gpt_FirstUsableLBA && 477 header.efi_gpt_MyLBA <= header.efi_gpt_LastUsableLBA) { 478 mdb_warn("MyLBA is within usable LBA range\n"); 479 } 480 481 if (header.efi_gpt_AlternateLBA >= header.efi_gpt_FirstUsableLBA && 482 header.efi_gpt_AlternateLBA <= header.efi_gpt_LastUsableLBA) { 483 mdb_warn("AlternateLBA is within usable LBA range\n"); 484 } 485 486 if (mdb_vread(&altheader, sizeof (altheader), 487 header.efi_gpt_AlternateLBA * SECTOR_SIZE) == -1) { 488 mdb_warn("failed to read alternate GPT header"); 489 } else { 490 if (strncmp((char *)&altheader.efi_gpt_Signature, 491 "EFI PART", 8) != 0) { 492 mdb_warn("found invalid alternate GPT header with " 493 "Signature: %s\n", 494 (char *)&altheader.efi_gpt_Signature); 495 } 496 497 if (altheader.efi_gpt_MyLBA != header.efi_gpt_AlternateLBA) { 498 mdb_warn("alternate GPT header at offset %#llx has " 499 "invalid MyLBA %llu\n", 500 header.efi_gpt_AlternateLBA * SECTOR_SIZE, 501 altheader.efi_gpt_MyLBA); 502 } 503 504 if (altheader.efi_gpt_AlternateLBA != header.efi_gpt_MyLBA) { 505 mdb_warn("alternate GPT header at offset %#llx has " 506 "invalid AlternateLBA %llu\n", 507 header.efi_gpt_AlternateLBA * SECTOR_SIZE, 508 altheader.efi_gpt_AlternateLBA); 509 } 510 511 /* 512 * We could go ahead and verify all the alternate checksums, 513 * etc. here too... 514 */ 515 } 516 517 uuid_unparse((uchar_t *)&header.efi_gpt_DiskGUID, uuid); 518 mdb_printf("DiskGUID: %s\n", uuid); 519 520 mdb_printf("PartitionEntryLBA: %llu\n", 521 header.efi_gpt_PartitionEntryLBA); 522 523 mdb_printf("NumberOfPartitionEntries: %u\n", 524 header.efi_gpt_NumberOfPartitionEntries); 525 526 /* 527 * While the spec allows a different size, in practice the table 528 * is always packed. 529 */ 530 if (header.efi_gpt_SizeOfPartitionEntry != sizeof (efi_gpe_t)) { 531 mdb_warn("SizeOfPartitionEntry: %#x bytes " 532 "(expected %#x bytes)\n", 533 header.efi_gpt_SizeOfPartitionEntry, sizeof (efi_gpe_t)); 534 return (DCMD_ERR); 535 } 536 537 mdb_printf("SizeOfPartitionEntry: %#x bytes\n", 538 header.efi_gpt_SizeOfPartitionEntry); 539 540 table_size = header.efi_gpt_SizeOfPartitionEntry * 541 header.efi_gpt_NumberOfPartitionEntries; 542 543 /* 544 * While this is a minimum reservation, it serves us ably as a 545 * maximum value to reasonably expect. 546 */ 547 if (table_size > EFI_MIN_ARRAY_SIZE) { 548 mdb_warn("Skipping GPT array of %#lx bytes.\n", table_size); 549 return (DCMD_ERR); 550 } 551 552 gpet = mdb_alloc(header.efi_gpt_SizeOfPartitionEntry * 553 header.efi_gpt_NumberOfPartitionEntries, UM_SLEEP | UM_GC); 554 555 if (mdb_vread(gpet, table_size, 556 header.efi_gpt_PartitionEntryLBA * SECTOR_SIZE) == -1) { 557 mdb_warn("couldn't read GPT array"); 558 return (DCMD_ERR); 559 } 560 561 crc = efi_crc32((unsigned char *)gpet, table_size); 562 563 mdb_printf("PartitionEntryArrayCRC32: %#x (should be %#x)\n", 564 header.efi_gpt_PartitionEntryArrayCRC32, crc); 565 566 if (show_guid) { 567 mdb_printf("\n%<u>%-4s %-19s %-37s%</u>\n", 568 "PART", "TYPE", "GUID"); 569 } else { 570 mdb_printf("\n%<u>%-4s %-19s %-13s %-13s %-8s %s%</u>\n", 571 "PART", "TYPE", "STARTLBA", "ENDLBA", "ATTR", "NAME"); 572 } 573 574 for (size_t i = 0; i < header.efi_gpt_NumberOfPartitionEntries; i++) 575 print_gpe(&gpet[i], i, show_guid); 576 577 return (DCMD_OK); 578 } 579 580 void 581 gpt_help(void) 582 { 583 mdb_printf("Display an EFI GUID Partition Table.\n\n" 584 "-a Display the alternate GPT\n" 585 "-g Show unique GUID for each table entry\n"); 586 } 587 588 static int 589 cmd_vtoc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 590 { 591 uint8_t buf[SECTOR_SIZE]; 592 struct dk_label *dl; 593 struct dk_vtoc *dv; 594 uintptr_t vaddr; 595 int i, tag_width, cyl_width; 596 int show_absolute = B_TRUE; 597 int show_sectors = B_TRUE; 598 uint32_t cyl; 599 600 if (mdb_getopts(argc, argv, 601 'c', MDB_OPT_CLRBITS, TRUE, &show_sectors, 602 'r', MDB_OPT_CLRBITS, TRUE, &show_absolute, 603 NULL) != argc) 604 return (DCMD_USAGE); 605 606 if (!(flags & DCMD_ADDRSPEC)) 607 addr = 0; 608 else 609 addr *= SECTOR_SIZE; 610 611 #if defined(_SUNOS_VTOC_16) 612 if (mdb_vread(&buf, sizeof (buf), addr) == -1) { 613 mdb_warn("failed to read VBR"); 614 return (DCMD_ERR); 615 } 616 617 mdb_printf("VBR info:\n"); 618 (void) mbr_info((struct mboot *)buf); 619 #endif 620 621 vaddr = addr + DK_LABEL_LOC * SECTOR_SIZE; 622 623 if (mdb_vread(&buf, sizeof (buf), vaddr) == -1) { 624 mdb_warn("failed to read VTOC"); 625 return (DCMD_ERR); 626 } 627 628 dl = (struct dk_label *)&buf; 629 dv = (struct dk_vtoc *)&dl->dkl_vtoc; 630 631 mdb_printf("Label magic: 0x%hx (%s)\n", dl->dkl_magic, 632 dl->dkl_magic == DKL_MAGIC ? "valid" : "invalid"); 633 if (dl->dkl_magic != DKL_MAGIC) 634 return (DCMD_ERR); 635 mdb_printf("Label %s sane\n", dv->v_sanity == VTOC_SANE ? 636 "is" : "is not"); 637 638 mdb_printf("Label version: %#x\n", dv->v_version); 639 mdb_printf("Volume name = <%s>\n", dv->v_volume); 640 mdb_printf("ASCII name = <%s>\n", dv->v_asciilabel); 641 mdb_printf("pcyl = %4d\n", dl->dkl_pcyl); 642 mdb_printf("ncyl = %4d\n", dl->dkl_ncyl); 643 mdb_printf("acyl = %4d\n", dl->dkl_acyl); 644 645 #if defined(_SUNOS_VTOC_16) 646 mdb_printf("bcyl = %4d\n", dl->dkl_bcyl); 647 #endif /* defined(_SUNOS_VTOC_16) */ 648 649 mdb_printf("nhead = %4d\n", dl->dkl_nhead); 650 mdb_printf("nsect = %4d\n", dl->dkl_nsect); 651 652 653 if (!show_absolute) 654 addr = 0; 655 cyl = dl->dkl_nhead * dl->dkl_nsect; 656 if (show_sectors) 657 cyl = 1; 658 else 659 addr /= (cyl * SECTOR_SIZE); 660 661 tag_width = array_widest_str(ptag_array); 662 663 cyl_width = sizeof ("CYLINDERS"); 664 for (i = 0; i < dv->v_nparts; i++) { 665 uint32_t start, end, size; 666 int w; 667 668 #if defined(_SUNOS_VTOC_16) 669 start = addr + (dv->v_part[i].p_start / cyl); 670 size = dv->v_part[i].p_size; 671 #elif defined(_SUNOS_VTOC_8) 672 start = dl->dkl_map[i].dkl_cylno; 673 start *= dl->dkl_nhead * dl->dkl_nsect; /* compute bytes */ 674 start /= cyl; 675 start += addr; 676 size = dl->dkl_map[i].dkl_nblk; 677 #else 678 #error "No VTOC format defined." 679 #endif 680 if (size == 0) 681 end = start = 0; 682 else 683 end = start + size / cyl - 1; 684 685 w = mdb_snprintf(NULL, 0, "%u - %u", start, end); 686 if (w > cyl_width) 687 cyl_width = w; 688 } 689 690 if (show_sectors == B_TRUE) { 691 mdb_printf("\n%<u>%-4s %-*s %-7s %-11s %-11s %-*s " 692 "%-10s%</u>\n", "PART", tag_width, "TAG", "FLAG", 693 "STARTLBA", "ENDLBA", MDB_NICENUM_BUFLEN, "SIZE", "BLOCKS"); 694 } else { 695 mdb_printf("\n%<u>%-4s %-*s %-7s %-*s %-*s %-10s%</u>\n", 696 "PART", tag_width, "TAG", "FLAG", cyl_width, "CYLINDERS", 697 MDB_NICENUM_BUFLEN, "SIZE", "BLOCKS"); 698 } 699 700 for (i = 0; i < dv->v_nparts; i++) { 701 uint16_t tag, flag; 702 uint32_t start, end, size; 703 const char *stag, *sflag; 704 char nnum[MDB_NICENUM_BUFLEN]; 705 706 #if defined(_SUNOS_VTOC_16) 707 tag = dv->v_part[i].p_tag; 708 flag = dv->v_part[i].p_flag; 709 start = addr + (dv->v_part[i].p_start / cyl); 710 size = dv->v_part[i].p_size; 711 #elif defined(_SUNOS_VTOC_8) 712 tag = dv->v_part[i].p_tag; 713 flag = dv->v_part[i].p_flag; 714 start = dl->dkl_map[i].dkl_cylno; 715 start *= dl->dkl_nhead * dl->dkl_nsect; /* compute bytes */ 716 start /= cyl; 717 start += addr; 718 size = dl->dkl_map[i].dkl_nblk; 719 #else 720 #error "No VTOC format defined." 721 #endif 722 if (size == 0) 723 end = start = 0; 724 else 725 end = start + size / cyl - 1; 726 727 stag = array_find_string(ptag_array, tag); 728 if (stag == NULL) 729 stag = "?"; 730 sflag = array_find_string(pflag_array, flag); 731 if (sflag == NULL) 732 sflag = "?"; 733 734 mdb_printf("%-4d %-*s %-7s ", i, tag_width, stag, sflag); 735 mdb_nicenum(size * SECTOR_SIZE, nnum); 736 if (show_sectors) { 737 mdb_printf("%-11u %-11u %-*s %-10u\n", start, end, 738 MDB_NICENUM_BUFLEN, nnum, size); 739 } else { 740 char cyls[10 * 2 + 4]; 741 742 if (size == 0) { 743 mdb_snprintf(cyls, sizeof (cyls), "%-*u", 744 cyl_width, size); 745 } else { 746 mdb_snprintf(cyls, sizeof (cyls), "%u - %u", 747 start, end); 748 } 749 mdb_printf("%-*s %-*s %-10u\n", cyl_width, cyls, 750 MDB_NICENUM_BUFLEN, nnum, size); 751 } 752 } 753 754 return (DCMD_OK); 755 } 756 757 void 758 vtoc_help(void) 759 { 760 mdb_printf("Display a Virtual Table of Content (VTOC).\n\n" 761 "-r Display relative addresses\n" 762 "-c Use cylinder based addressing\n"); 763 mdb_printf("\nThe addr is in 512-byte disk blocks.\n"); 764 } 765 766 static const mdb_dcmd_t dcmds[] = { 767 { "mbr", NULL, "dump Master Boot Record information", cmd_mbr }, 768 { "gpt", "?[-ag]", "dump an EFI GPT", cmd_gpt, gpt_help }, 769 { "vtoc", "?[-cr]", "dump VTOC information", cmd_vtoc, vtoc_help }, 770 { NULL } 771 }; 772 773 static const mdb_modinfo_t modinfo = { 774 MDB_API_VERSION, dcmds, NULL 775 }; 776 777 const mdb_modinfo_t * 778 _mdb_init(void) 779 { 780 return (&modinfo); 781 } 782