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