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 }