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