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