#include <sys/types.h>
#include <sys/crc32.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/dktp/fdisk.h>
#include <sys/efi_partition.h>
#include <sys/fs/pc_fs.h>
#include <sys/vtoc.h>
#include <assert.h>
#include <ctype.h>
#include <uuid/uuid.h>
#include <stdbool.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_debug.h>
#include "installboot.h"
#ifdef _BIG_ENDIAN
#error needs porting for big-endian system
#endif
#define GRUB_VERSION_OFF (0x3e)
#define GRUB_COMPAT_VERSION_MAJOR 3
#define GRUB_COMPAT_VERSION_MINOR 2
#define GRUB_VERSION (2 << 8 | 3)
#define LOADER_VERSION (1)
#define LOADER_JOYENT_VERSION (2)
typedef enum {
MBR_TYPE_UNKNOWN,
MBR_TYPE_GRUB1,
MBR_TYPE_LOADER,
MBR_TYPE_LOADER_JOYENT,
} mbr_type_t;
typedef struct stringval {
const char *sv_text;
int sv_value;
} stringval_t;
stringval_t ptag_array[] = {
{ "unassigned", V_UNASSIGNED },
{ "boot", V_BOOT },
{ "root", V_ROOT },
{ "swap", V_SWAP },
{ "usr", V_USR },
{ "backup", V_BACKUP },
{ "stand", V_STAND },
{ "var", V_VAR },
{ "home", V_HOME },
{ "alternates", V_ALTSCTR },
{ "reserved", V_RESERVED },
{ "system", V_SYSTEM },
{ "BIOS_boot", V_BIOS_BOOT },
{ "FreeBSD boot", V_FREEBSD_BOOT },
{ "FreeBSD swap", V_FREEBSD_SWAP },
{ "FreeBSD UFS", V_FREEBSD_UFS },
{ "FreeBSD ZFS", V_FREEBSD_ZFS },
{ "FreeBSD NANDFS", V_FREEBSD_NANDFS },
{ NULL }
};
stringval_t pflag_array[] = {
{ "wm", 0 },
{ "wu", V_UNMNT },
{ "rm", V_RONLY },
{ "ru", V_RONLY | V_UNMNT },
{ NULL }
};
size_t sector_size = SECTOR_SIZE;
static const char *
array_find_string(stringval_t *array, int match_value)
{
for (; array->sv_text != NULL; array++) {
if (array->sv_value == match_value) {
return (array->sv_text);
}
}
return (NULL);
}
static int
array_widest_str(stringval_t *array)
{
int i;
int width;
width = 0;
for (; array->sv_text != NULL; array++) {
if ((i = strlen(array->sv_text)) > width)
width = i;
}
return (width);
}
static void
print_fdisk_part(struct ipart *ip, size_t nr)
{
char typestr[128];
char begchs[128];
char endchs[128];
char *c = NULL;
if (ip->systid == UNUSED) {
mdb_printf("%-4llu %s:%#lx\n", nr, "UNUSED", ip->systid);
return;
}
switch (ip->systid) {
case DOSOS12: c = "DOSOS12"; break;
case PCIXOS: c = "PCIXOS"; break;
case DOSOS16: c = "DOSOS16"; break;
case EXTDOS: c = "EXTDOS"; break;
case DOSHUGE: c = "DOSHUGE"; break;
case FDISK_IFS: c = "FDISK_IFS"; break;
case FDISK_AIXBOOT: c = "FDISK_AIXBOOT"; break;
case FDISK_AIXDATA: c = "FDISK_AIXDATA"; break;
case FDISK_OS2BOOT: c = "FDISK_OS2BOOT"; break;
case FDISK_WINDOWS: c = "FDISK_WINDOWS"; break;
case FDISK_EXT_WIN: c = "FDISK_EXT_WIN"; break;
case FDISK_FAT95: c = "FDISK_FAT95"; break;
case FDISK_EXTLBA: c = "FDISK_EXTLBA"; break;
case DIAGPART: c = "DIAGPART"; break;
case FDISK_LINUX: c = "FDISK_LINUX"; break;
case FDISK_LINUXDSWAP: c = "FDISK_LINUXDSWAP"; break;
case FDISK_LINUXDNAT: c = "FDISK_LINUXDNAT"; break;
case FDISK_CPM: c = "FDISK_CPM"; break;
case DOSDATA: c = "DOSDATA"; break;
case OTHEROS: c = "OTHEROS"; break;
case UNIXOS: c = "UNIXOS"; break;
case FDISK_NOVELL2: c = "FDISK_NOVELL2"; break;
case FDISK_NOVELL3: c = "FDISK_NOVELL3"; break;
case FDISK_QNX4: c = "FDISK_QNX4"; break;
case FDISK_QNX42: c = "FDISK_QNX42"; break;
case FDISK_QNX43: c = "FDISK_QNX43"; break;
case SUNIXOS: c = "SUNIXOS"; break;
case FDISK_LINUXNAT: c = "FDISK_LINUXNAT"; break;
case FDISK_NTFSVOL1: c = "FDISK_NTFSVOL1"; break;
case FDISK_NTFSVOL2: c = "FDISK_NTFSVOL2"; break;
case FDISK_BSD: c = "FDISK_BSD"; break;
case FDISK_NEXTSTEP: c = "FDISK_NEXTSTEP"; break;
case FDISK_BSDIFS: c = "FDISK_BSDIFS"; break;
case FDISK_BSDISWAP: c = "FDISK_BSDISWAP"; break;
case X86BOOT: c = "X86BOOT"; break;
case SUNIXOS2: c = "SUNIXOS2"; break;
case EFI_PMBR: c = "EFI_PMBR"; break;
case EFI_FS: c = "EFI_FS"; break;
default: c = NULL; break;
}
if (c != NULL) {
mdb_snprintf(typestr, sizeof (typestr), "%s:%#lx",
c, ip->systid);
} else {
mdb_snprintf(typestr, sizeof (typestr), "%#lx", ip->systid);
}
mdb_snprintf(begchs, sizeof (begchs), "%hu/%hu/%hu",
(uint16_t)ip->begcyl | (uint16_t)(ip->begsect & ~0x3f) << 2,
(uint16_t)ip->beghead, (uint16_t)ip->begsect & 0x3f);
mdb_snprintf(endchs, sizeof (endchs), "%hu/%hu/%hu",
(uint16_t)ip->endcyl | (uint16_t)(ip->endsect & ~0x3f) << 2,
(uint16_t)ip->endhead, (uint16_t)ip->endsect & 0x3f);
mdb_printf("%-4llu %-21s %#-7x %-11s %-11s %-10u %-9u\n",
nr, typestr, ip->bootid, begchs, endchs, ip->relsect, ip->numsect);
}
static void
show_bpb(char *bpb)
{
bool fat32 = false;
uint32_t reserved, rdirsec, spc;
uint32_t rec, fsisec, bkbootsec;
uint32_t totsec16, totsec32, totsec;
uint32_t fatsec16, fatsec32, fatsec;
uint32_t numfat, datasec;
uint32_t ncl;
char buf[12];
mdb_printf("\n");
mdb_printf("BPB JMP: ");
if (VALID_JMPBOOT(bpb_jmpBoot(bpb))) {
mdb_printf("valid");
} else {
mdb_printf("invalid");
}
mdb_printf("\n");
mdb_printf("BPB OEMName: ");
if (VALID_OEMNAME(bpb_OEMName(bpb))) {
mdb_printf("valid");
mdb_printf(" : %*s", 8, bpb_OEMName(bpb));
} else {
mdb_printf("invalid");
}
mdb_printf("\n");
mdb_printf("BPB Bytes per Sector: ");
if (VALID_SECSIZE(bpb_get_BytesPerSec(bpb))) {
mdb_printf("valid");
} else {
mdb_printf("invalid");
}
mdb_printf(" : %hu", bpb_get_BytesPerSec(bpb));
mdb_printf("\n");
mdb_printf("BPB Sectors per Cluster: ");
spc = bpb_get_SecPerClus(bpb);
if (VALID_SPCL(spc)) {
mdb_printf("valid");
} else {
mdb_printf("invalid");
}
mdb_printf(" : %u", spc);
mdb_printf("\n");
reserved = bpb_get_RsvdSecCnt(bpb);
mdb_printf("BPB Reserved Sectors: ");
if (VALID_RSVDSEC(reserved)) {
mdb_printf("valid");
} else {
mdb_printf("invalid");
}
mdb_printf(" : %u", reserved);
mdb_printf("\n");
mdb_printf("BPB Number of FATs: ");
numfat = bpb_get_NumFATs(bpb);
if (VALID_NUMFATS(numfat)) {
mdb_printf("valid");
} else {
mdb_printf("invalid");
}
mdb_printf(" : %u", numfat);
mdb_printf("\n");
rec = bpb_get_RootEntCnt(bpb);
mdb_printf("BPB Root Entry Count: ");
mdb_printf("%u", rec);
mdb_printf("\n");
totsec16 = bpb_get_TotSec16(bpb);
mdb_printf("BPB Total Sectors 16: ");
mdb_printf("%u", totsec16);
mdb_printf("\n");
mdb_printf("BPB Media Type: ");
if (VALID_MEDIA(bpb_get_Media(bpb))) {
mdb_printf("valid");
} else {
mdb_printf("invalid");
}
mdb_printf(" : 0x%02x", bpb_get_Media(bpb));
mdb_printf("\n");
fatsec16 = bpb_get_FatSz16(bpb);
mdb_printf("BPB FAT Sectors 16: ");
mdb_printf("%u", fatsec16);
mdb_printf("\n");
mdb_printf("BPB Sectors Per Track: ");
mdb_printf("%u", bpb_get_SecPerTrk(bpb));
mdb_printf("\n");
mdb_printf("BPB Number of Heads: ");
mdb_printf("%u", bpb_get_NumHeads(bpb));
mdb_printf("\n");
mdb_printf("BPB Hidden Sectors: ");
mdb_printf("%u", bpb_get_HiddSec(bpb));
mdb_printf("\n");
totsec32 = bpb_get_TotSec32(bpb);
mdb_printf("BPB Total Sectors 32: ");
mdb_printf("%u", totsec32);
mdb_printf("\n");
mdb_printf("BPB Signature: ");
if (VALID_BPBSIG(bpb_get_BPBSig(bpb))) {
mdb_printf("valid");
} else {
mdb_printf("invalid");
}
mdb_printf("\n");
fatsec32 = bpb_get_FatSz32(bpb);
if (totsec16 == 0 && fatsec16 == 0) {
totsec = totsec32;
fatsec = fatsec32;
fat32 = true;
} else {
totsec = totsec16;
fatsec = fatsec16;
}
if (totsec == 0 || fatsec == 0) {
mdb_printf("There is no FAT file system\n");
return;
}
if (!fat32) {
mdb_printf("BPB Drive Number: ");
mdb_printf("0x%02x", bpb_get_DrvNum16(bpb));
mdb_printf("\n");
mdb_printf("BPB Extended Boot Signature: ");
mdb_printf("0x%02x", bpb_get_BootSig16(bpb));
mdb_printf("\n");
if (bpb_get_BootSig16(bpb) == 0x29) {
mdb_printf("BPB Volume ID: ");
mdb_printf("0x%04x", bpb_get_VolID16(bpb));
mdb_printf("\n");
mdb_printf("BPB Volume Label: ");
bcopy(bpb_VolLab16(bpb), buf, 11);
buf[11] = '\0';
mdb_printf("\"%s\"", buf);
mdb_printf("\n");
mdb_printf("BPB File System Type: ");
if (VALID_FSTYPSTR16(bpb_FilSysType16(bpb))) {
mdb_printf("valid");
} else {
mdb_printf("invalid");
}
bcopy(bpb_FilSysType16(bpb), buf, 8);
buf[8] = '\0';
mdb_printf(" : \"%s\"", buf);
mdb_printf("\n");
}
} else {
mdb_printf("BPB FAT Sectors 32: ");
mdb_printf("%u", fatsec32);
mdb_printf("\n");
mdb_printf("BPB Extended Flags 32: ");
mdb_printf("0x%04x", bpb_get_ExtFlags32(bpb));
mdb_printf("\n");
mdb_printf("BPB FS Version: ");
mdb_printf("0x%02x", bpb_get_FSVer32(bpb));
mdb_printf("\n");
mdb_printf("BPB Root Cluster: ");
mdb_printf("%u", bpb_get_RootClus32(bpb));
mdb_printf("\n");
fsisec = bpb_get_FSInfo32(bpb);
mdb_printf("BPB FS Info Sector: ");
mdb_printf("%u", fsisec);
mdb_printf("\n");
bkbootsec = bpb_get_BkBootSec32(bpb);
mdb_printf("BPB Backup Boot Sector: ");
mdb_printf("%u", bkbootsec);
mdb_printf("\n");
mdb_printf("BPB Drive Number: ");
mdb_printf("0x%02x", bpb_get_DrvNum32(bpb));
mdb_printf("\n");
mdb_printf("BPB Boot Signature: ");
mdb_printf("0x%02x", bpb_get_BootSig32(bpb));
mdb_printf("\n");
if (bpb_get_BootSig32(bpb) == 0x29) {
mdb_printf("BPB Volume ID: ");
mdb_printf("0x%04x", bpb_get_VolID32(bpb));
mdb_printf("\n");
mdb_printf("BPB Volume Label: ");
bcopy(bpb_VolLab32(bpb), buf, 11);
buf[11] = '\0';
mdb_printf("\"%s\"", buf);
mdb_printf("\n");
mdb_printf("BPB File System Type: ");
if (VALID_FSTYPSTR32(bpb_FilSysType32(bpb))) {
mdb_printf("valid");
} else {
mdb_printf("invalid");
}
bcopy(bpb_FilSysType32(bpb), buf, 8);
buf[8] = '\0';
mdb_printf(" : \"%s\"", buf);
mdb_printf("\n");
}
}
rdirsec = (rec * 32 + (sector_size - 1)) / sector_size;
datasec = totsec - fatsec * numfat - rdirsec - reserved;
ncl = datasec / spc;
mdb_printf("count of clusters on the volume: %u\n", ncl);
if (ncl < 4085)
mdb_printf("Volume should be FAT12\n");
else if (ncl < 65525)
mdb_printf("Volume should be FAT16\n");
else
mdb_printf("Volume should be FAT32\n");
}
static mbr_type_t
mbr_info(struct mboot *mbr, uint_t bpb)
{
mbr_type_t type = MBR_TYPE_UNKNOWN;
if (*((uint16_t *)&mbr->bootinst[GRUB_VERSION_OFF]) == GRUB_VERSION) {
type = MBR_TYPE_GRUB1;
} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_VERSION) {
type = MBR_TYPE_LOADER;
} else if (mbr->bootinst[STAGE1_MBR_VERSION] == LOADER_JOYENT_VERSION) {
type = MBR_TYPE_LOADER_JOYENT;
}
switch (type) {
case MBR_TYPE_UNKNOWN:
mdb_printf("Format: unknown\n");
break;
case MBR_TYPE_GRUB1:
mdb_printf("Format: grub1\n");
break;
case MBR_TYPE_LOADER:
mdb_printf("Format: loader (illumos)\n");
break;
case MBR_TYPE_LOADER_JOYENT:
mdb_printf("Format: loader (joyent)\n");
break;
}
mdb_printf("Signature: 0x%hx (%s)\n", mbr->signature,
mbr->signature == MBB_MAGIC ? "valid" : "invalid");
mdb_printf("UniqueMBRDiskSignature: %#lx\n",
*(uint32_t *)&mbr->bootinst[STAGE1_SIG]);
if (type == MBR_TYPE_LOADER || type == MBR_TYPE_LOADER_JOYENT) {
char uuid[UUID_PRINTABLE_STRING_LENGTH];
mdb_printf("Loader STAGE1_STAGE2_LBA: %llu\n",
*(uint64_t *)&mbr->bootinst[STAGE1_STAGE2_LBA]);
mdb_printf("Loader STAGE1_STAGE2_SIZE: %hu\n",
*(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE]);
uuid_unparse((uchar_t *)&mbr->bootinst[STAGE1_STAGE2_UUID],
uuid);
mdb_printf("Loader STAGE1_STAGE2_UUID: %s\n", uuid);
}
if (bpb) {
show_bpb(mbr->bootinst);
}
return (type);
}
static void
mbr_help(void)
{
mdb_printf("Display a Master Boot Record.\n\n"
"-b Show BIOS Parameter Block (BPB)\n");
}
static int
cmd_mbr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
struct mboot *mbr;
mbr_type_t type;
uint_t opt_b = FALSE;
CTASSERT(sizeof (*mbr) == SECTOR_SIZE);
if (mdb_getopts(argc, argv,
'b', MDB_OPT_SETBITS, TRUE, &opt_b, NULL) != argc)
return (DCMD_USAGE);
if (!(flags & DCMD_ADDRSPEC))
addr = 0;
mbr = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
if (mdb_vread(mbr, sector_size, addr) == -1) {
mdb_warn("failed to read MBR");
return (DCMD_ERR);
}
if (VALID_SECSIZE(bpb_get_BytesPerSec(mbr->bootinst))) {
sector_size = bpb_get_BytesPerSec(mbr->bootinst);
}
type = mbr_info(mbr, opt_b);
if (mbr->signature != MBB_MAGIC)
return (DCMD_ERR);
switch (type) {
case MBR_TYPE_LOADER:
case MBR_TYPE_LOADER_JOYENT:
if (*(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE] == 1) {
struct mboot vbr;
uintptr_t vbrp;
vbrp = *(uint64_t *)&mbr->bootinst[STAGE1_STAGE2_LBA];
vbrp *= sector_size;
vbrp += addr;
if (mdb_vread(&vbr, sizeof (vbr), vbrp) == -1) {
mdb_warn("failed to read VBR");
} else {
mdb_printf("\nSTAGE1 in VBR:\n");
(void) mbr_info(&vbr, opt_b);
}
}
break;
default:
break;
}
if (*(uint16_t *)&mbr->bootinst[STAGE1_STAGE2_SIZE] == 1) {
mdb_printf(
"\n%<u>%-4s %-21s %-7s %-11s %-11s %-10s %-9s%</u>\n",
"PART", "TYPE", "ACTIVE", "STARTCHS", "ENDCHS",
"SECTOR", "NUMSECT");
for (size_t i = 0; i < FD_NUMPART; i++) {
struct ipart *ip = (struct ipart *)
(mbr->parts + (sizeof (struct ipart) * i));
print_fdisk_part(ip, i);
}
}
return (DCMD_OK);
}
static unsigned int crc32_tab[] = { CRC32_TABLE };
static unsigned int
efi_crc32(const unsigned char *s, unsigned int len)
{
unsigned int crc32val;
CRC32(crc32val, s, len, -1U, crc32_tab);
return (crc32val ^ -1U);
}
typedef struct {
struct uuid eg_uuid;
const char *eg_name;
} efi_guid_t;
static efi_guid_t efi_guids[] = {
{ EFI_UNUSED, "EFI_UNUSED" },
{ EFI_RESV1, "EFI_RESV1" },
{ EFI_BOOT, "EFI_BOOT" },
{ EFI_ROOT, "EFI_ROOT" },
{ EFI_SWAP, "EFI_SWAP" },
{ EFI_USR, "EFI_USR" },
{ EFI_BACKUP, "EFI_BACKUP" },
{ EFI_RESV2, "EFI_RESV2" },
{ EFI_VAR, "EFI_VAR" },
{ EFI_HOME, "EFI_HOME" },
{ EFI_ALTSCTR, "EFI_ALTSCTR" },
{ EFI_RESERVED, "EFI_RESERVED" },
{ EFI_SYSTEM, "EFI_SYSTEM" },
{ EFI_LEGACY_MBR, "EFI_LEGACY_MBR" },
{ EFI_SYMC_PUB, "EFI_SYMC_PUB" },
{ EFI_SYMC_CDS, "EFI_SYMC_CDS" },
{ EFI_MSFT_RESV, "EFI_MSFT_RESV" },
{ EFI_DELL_BASIC, "EFI_DELL_BASIC" },
{ EFI_DELL_RAID, "EFI_DELL_RAID" },
{ EFI_DELL_SWAP, "EFI_DELL_SWAP" },
{ EFI_DELL_LVM, "EFI_DELL_LVM" },
{ EFI_DELL_RESV, "EFI_DELL_RESV" },
{ EFI_AAPL_BOOT, "EFI_AAPL_BOOT" },
{ EFI_AAPL_HFS, "EFI_AAPL_HFS" },
{ EFI_AAPL_UFS, "EFI_AAPL_UFS" },
{ EFI_AAPL_ZFS, "EFI_AAPL_ZFS" },
{ EFI_AAPL_APFS, "EFI_AAPL_APFS" },
{ EFI_FREEBSD_BOOT, "EFI_FREEBSD_BOOT" },
{ EFI_FREEBSD_NANDFS, "EFI_FREEBSD_NANDFS" },
{ EFI_FREEBSD_SWAP, "EFI_FREEBSD_SWAP" },
{ EFI_FREEBSD_UFS, "EFI_FREEBSD_UFS" },
{ EFI_FREEBSD_VINUM, "EFI_FREEBSD_VINUM" },
{ EFI_FREEBSD_ZFS, "EFI_FREEBSD_ZFS" },
{ EFI_BIOS_BOOT, "EFI_BIOS_BOOT" },
};
static void
print_gpe(efi_gpe_t *gpe, size_t nr, int show_guid)
{
const char *type = "unknown";
for (size_t i = 0; i < ARRAY_SIZE(efi_guids); i++) {
if (memcmp((void *)&efi_guids[i].eg_uuid,
(void *)&gpe->efi_gpe_PartitionTypeGUID,
sizeof (efi_guids[i].eg_uuid)) == 0) {
type = efi_guids[i].eg_name;
break;
}
}
if (strcmp(type, "EFI_UNUSED") == 0) {
mdb_printf("%-4u %-19s\n", nr, type);
return;
}
if (show_guid) {
char guid[UUID_PRINTABLE_STRING_LENGTH];
uuid_unparse((uchar_t *)&gpe->efi_gpe_UniquePartitionGUID,
guid);
mdb_printf("%-4u %-19s %s\n", nr, type, guid);
} else {
char name[EFI_PART_NAME_LEN + 1] = "";
for (size_t i = 0; i < sizeof (name); i++) {
ushort_t wchar = gpe->efi_gpe_PartitionName[i];
name[i] = (char)(isascii(wchar) ? wchar : '?');
}
mdb_printf("%-4u %-19s %-13llu %-13llu %#-8llx %s\n",
nr, type, gpe->efi_gpe_StartingLBA, gpe->efi_gpe_EndingLBA,
gpe->efi_gpe_Attributes, name);
}
}
static int
cmd_gpt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv __unused)
{
char uuid[UUID_PRINTABLE_STRING_LENGTH];
int show_alternate = B_FALSE;
int show_guid = B_FALSE;
efi_gpt_t *altheader;
size_t table_size;
efi_gpt_t *header;
efi_gpe_t *gpet;
uint_t orig_crc;
uint_t crc;
if (mdb_getopts(argc, argv,
'a', MDB_OPT_SETBITS, TRUE, &show_alternate,
'g', MDB_OPT_SETBITS, TRUE, &show_guid,
NULL) != argc)
return (DCMD_USAGE);
if (!(flags & DCMD_ADDRSPEC))
addr = sector_size;
header = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
if (mdb_vread(header, sector_size, addr) == -1) {
mdb_warn("failed to read GPT header");
return (DCMD_ERR);
}
if (show_alternate) {
addr = header->efi_gpt_AlternateLBA * sector_size;
if (mdb_vread(header, sector_size, addr) == -1) {
mdb_warn("failed to read GPT header");
return (DCMD_ERR);
}
}
mdb_printf("Signature: %s (%s)\n", (char *)&header->efi_gpt_Signature,
strncmp((char *)&header->efi_gpt_Signature, "EFI PART", 8) == 0 ?
"valid" : "invalid");
mdb_printf("Revision: %hu.%hu\n", header->efi_gpt_Revision >> 16,
header->efi_gpt_Revision);
mdb_printf("HeaderSize: %u bytes\n", header->efi_gpt_HeaderSize);
if (header->efi_gpt_HeaderSize > SECTOR_SIZE) {
mdb_warn("invalid header size: skipping CRC\n");
} else {
orig_crc = header->efi_gpt_HeaderCRC32;
header->efi_gpt_HeaderCRC32 = 0;
crc = efi_crc32((unsigned char *)header,
header->efi_gpt_HeaderSize);
mdb_printf("HeaderCRC32: %#x (should be %#x)\n", orig_crc, crc);
}
mdb_printf("Reserved1: %#x (should be 0x0)\n",
header->efi_gpt_Reserved1);
mdb_printf("MyLBA: %llu (should be %llu)\n",
header->efi_gpt_MyLBA, addr / sector_size);
mdb_printf("AlternateLBA: %llu\n", header->efi_gpt_AlternateLBA);
mdb_printf("FirstUsableLBA: %llu\n", header->efi_gpt_FirstUsableLBA);
mdb_printf("LastUsableLBA: %llu\n", header->efi_gpt_LastUsableLBA);
if (header->efi_gpt_MyLBA >= header->efi_gpt_FirstUsableLBA &&
header->efi_gpt_MyLBA <= header->efi_gpt_LastUsableLBA) {
mdb_warn("MyLBA is within usable LBA range\n");
}
if (header->efi_gpt_AlternateLBA >= header->efi_gpt_FirstUsableLBA &&
header->efi_gpt_AlternateLBA <= header->efi_gpt_LastUsableLBA) {
mdb_warn("AlternateLBA is within usable LBA range\n");
}
altheader = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
if (mdb_vread(altheader, sector_size,
header->efi_gpt_AlternateLBA * sector_size) == -1) {
mdb_warn("failed to read alternate GPT header");
} else {
if (strncmp((char *)&altheader->efi_gpt_Signature,
"EFI PART", 8) != 0) {
mdb_warn("found invalid alternate GPT header with "
"Signature: %s\n",
(char *)&altheader->efi_gpt_Signature);
}
if (altheader->efi_gpt_MyLBA != header->efi_gpt_AlternateLBA) {
mdb_warn("alternate GPT header at offset %#llx has "
"invalid MyLBA %llu\n",
header->efi_gpt_AlternateLBA * sector_size,
altheader->efi_gpt_MyLBA);
}
if (altheader->efi_gpt_AlternateLBA != header->efi_gpt_MyLBA) {
mdb_warn("alternate GPT header at offset %#llx has "
"invalid AlternateLBA %llu\n",
header->efi_gpt_AlternateLBA * sector_size,
altheader->efi_gpt_AlternateLBA);
}
}
uuid_unparse((uchar_t *)&header->efi_gpt_DiskGUID, uuid);
mdb_printf("DiskGUID: %s\n", uuid);
mdb_printf("PartitionEntryLBA: %llu\n",
header->efi_gpt_PartitionEntryLBA);
mdb_printf("NumberOfPartitionEntries: %u\n",
header->efi_gpt_NumberOfPartitionEntries);
if (header->efi_gpt_SizeOfPartitionEntry != sizeof (efi_gpe_t)) {
mdb_warn("SizeOfPartitionEntry: %#x bytes "
"(expected %#x bytes)\n",
header->efi_gpt_SizeOfPartitionEntry, sizeof (efi_gpe_t));
return (DCMD_ERR);
}
mdb_printf("SizeOfPartitionEntry: %#x bytes\n",
header->efi_gpt_SizeOfPartitionEntry);
table_size = header->efi_gpt_SizeOfPartitionEntry *
header->efi_gpt_NumberOfPartitionEntries;
if (table_size > EFI_MIN_ARRAY_SIZE) {
mdb_warn("Skipping GPT array of %#lx bytes.\n", table_size);
return (DCMD_ERR);
}
table_size = P2ROUNDUP(table_size, sector_size);
gpet = mdb_alloc(table_size, UM_SLEEP | UM_GC);
if (mdb_vread(gpet, table_size,
header->efi_gpt_PartitionEntryLBA * sector_size) == -1) {
mdb_warn("couldn't read GPT array");
return (DCMD_ERR);
}
crc = efi_crc32((unsigned char *)gpet,
header->efi_gpt_SizeOfPartitionEntry *
header->efi_gpt_NumberOfPartitionEntries);
mdb_printf("PartitionEntryArrayCRC32: %#x (should be %#x)\n",
header->efi_gpt_PartitionEntryArrayCRC32, crc);
if (show_guid) {
mdb_printf("\n%<u>%-4s %-19s %-37s%</u>\n",
"PART", "TYPE", "GUID");
} else {
mdb_printf("\n%<u>%-4s %-19s %-13s %-13s %-8s %s%</u>\n",
"PART", "TYPE", "STARTLBA", "ENDLBA", "ATTR", "NAME");
}
for (size_t i = 0; i < header->efi_gpt_NumberOfPartitionEntries; i++)
print_gpe(&gpet[i], i, show_guid);
return (DCMD_OK);
}
static void
gpt_help(void)
{
mdb_printf("Display an EFI GUID Partition Table.\n\n"
"-a Display the alternate GPT\n"
"-g Show unique GUID for each table entry\n");
}
static int
cmd_vtoc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
uint8_t *buf;
struct dk_label *dl;
struct dk_vtoc *dv;
uintptr_t vaddr;
int i, tag_width, cyl_width;
int show_absolute = B_TRUE;
int show_sectors = B_TRUE;
uint32_t cyl;
if (mdb_getopts(argc, argv,
'c', MDB_OPT_CLRBITS, TRUE, &show_sectors,
'r', MDB_OPT_CLRBITS, TRUE, &show_absolute,
NULL) != argc)
return (DCMD_USAGE);
if (!(flags & DCMD_ADDRSPEC))
addr = 0;
else
addr *= sector_size;
buf = mdb_zalloc(sector_size, UM_SLEEP | UM_GC);
#if defined(_SUNOS_VTOC_16)
if (mdb_vread(buf, sector_size, addr) == -1) {
mdb_warn("failed to read VBR");
return (DCMD_ERR);
}
mdb_printf("VBR info:\n");
(void) mbr_info((struct mboot *)buf, FALSE);
#endif
vaddr = addr + DK_LABEL_LOC * sector_size;
if (mdb_vread(buf, sector_size, vaddr) == -1) {
mdb_warn("failed to read VTOC");
return (DCMD_ERR);
}
dl = (struct dk_label *)buf;
dv = (struct dk_vtoc *)&dl->dkl_vtoc;
mdb_printf("Label magic: 0x%hx (%s)\n", dl->dkl_magic,
dl->dkl_magic == DKL_MAGIC ? "valid" : "invalid");
if (dl->dkl_magic != DKL_MAGIC)
return (DCMD_ERR);
mdb_printf("Label %s sane\n", dv->v_sanity == VTOC_SANE ?
"is" : "is not");
mdb_printf("Label version: %#x\n", dv->v_version);
mdb_printf("Volume name = <%s>\n", dv->v_volume);
mdb_printf("ASCII name = <%s>\n", dv->v_asciilabel);
mdb_printf("pcyl = %4d\n", dl->dkl_pcyl);
mdb_printf("ncyl = %4d\n", dl->dkl_ncyl);
mdb_printf("acyl = %4d\n", dl->dkl_acyl);
#if defined(_SUNOS_VTOC_16)
mdb_printf("bcyl = %4d\n", dl->dkl_bcyl);
#endif
mdb_printf("nhead = %4d\n", dl->dkl_nhead);
mdb_printf("nsect = %4d\n", dl->dkl_nsect);
if (!show_absolute)
addr = 0;
cyl = dl->dkl_nhead * dl->dkl_nsect;
if (show_sectors)
cyl = 1;
else
addr /= (cyl * sector_size);
tag_width = array_widest_str(ptag_array);
cyl_width = sizeof ("CYLINDERS");
for (i = 0; i < dv->v_nparts; i++) {
uint32_t start, end, size;
int w;
#if defined(_SUNOS_VTOC_16)
start = addr + (dv->v_part[i].p_start / cyl);
size = dv->v_part[i].p_size;
#elif defined(_SUNOS_VTOC_8)
start = dl->dkl_map[i].dkl_cylno;
start *= dl->dkl_nhead * dl->dkl_nsect;
start /= cyl;
start += addr;
size = dl->dkl_map[i].dkl_nblk;
#else
#error "No VTOC format defined."
#endif
if (size == 0)
end = start = 0;
else
end = start + size / cyl - 1;
w = mdb_snprintf(NULL, 0, "%u - %u", start, end);
if (w > cyl_width)
cyl_width = w;
}
if (show_sectors == B_TRUE) {
mdb_printf("\n%<u>%-4s %-*s %-7s %-11s %-11s %-*s "
"%-10s%</u>\n", "PART", tag_width, "TAG", "FLAG",
"STARTLBA", "ENDLBA", MDB_NICENUM_BUFLEN, "SIZE", "BLOCKS");
} else {
mdb_printf("\n%<u>%-4s %-*s %-7s %-*s %-*s %-10s%</u>\n",
"PART", tag_width, "TAG", "FLAG", cyl_width, "CYLINDERS",
MDB_NICENUM_BUFLEN, "SIZE", "BLOCKS");
}
for (i = 0; i < dv->v_nparts; i++) {
uint16_t tag, flag;
uint32_t start, end, size;
const char *stag, *sflag;
char nnum[MDB_NICENUM_BUFLEN];
#if defined(_SUNOS_VTOC_16)
tag = dv->v_part[i].p_tag;
flag = dv->v_part[i].p_flag;
start = addr + (dv->v_part[i].p_start / cyl);
size = dv->v_part[i].p_size;
#elif defined(_SUNOS_VTOC_8)
tag = dv->v_part[i].p_tag;
flag = dv->v_part[i].p_flag;
start = dl->dkl_map[i].dkl_cylno;
start *= dl->dkl_nhead * dl->dkl_nsect;
start /= cyl;
start += addr;
size = dl->dkl_map[i].dkl_nblk;
#else
#error "No VTOC format defined."
#endif
if (size == 0)
end = start = 0;
else
end = start + size / cyl - 1;
stag = array_find_string(ptag_array, tag);
if (stag == NULL)
stag = "?";
sflag = array_find_string(pflag_array, flag);
if (sflag == NULL)
sflag = "?";
mdb_printf("%-4d %-*s %-7s ", i, tag_width, stag, sflag);
mdb_nicenum(size * sector_size, nnum);
if (show_sectors) {
mdb_printf("%-11u %-11u %-*s %-10u\n", start, end,
MDB_NICENUM_BUFLEN, nnum, size);
} else {
char cyls[10 * 2 + 4];
if (size == 0) {
mdb_snprintf(cyls, sizeof (cyls), "%-*u",
cyl_width, size);
} else {
mdb_snprintf(cyls, sizeof (cyls), "%u - %u",
start, end);
}
mdb_printf("%-*s %-*s %-10u\n", cyl_width, cyls,
MDB_NICENUM_BUFLEN, nnum, size);
}
}
return (DCMD_OK);
}
static void
vtoc_help(void)
{
mdb_printf("Display a Virtual Table of Content (VTOC).\n\n"
"-r Display relative addresses\n"
"-c Use cylinder based addressing\n");
mdb_printf("\nThe addr is in %u-byte disk blocks.\n", sector_size);
}
static int
cmd_sect(uintptr_t addr __unused, uint_t flags __unused, int argc,
const mdb_arg_t *argv)
{
uint64_t size = SECTOR_SIZE;
if (argc < 1) {
mdb_printf("Current sector size is %u (%#x)\n", sector_size,
sector_size);
return (DCMD_OK);
}
if (argc != 1)
return (DCMD_USAGE);
switch (argv[0].a_type) {
case MDB_TYPE_STRING:
size = mdb_strtoull(argv[0].a_un.a_str);
break;
case MDB_TYPE_IMMEDIATE:
size = argv[0].a_un.a_val;
break;
default:
return (DCMD_USAGE);
}
if (!ISP2(size)) {
mdb_printf("sector size must be power of 2\n");
return (DCMD_USAGE);
}
sector_size = size;
return (DCMD_OK);
}
static void
sect_help(void)
{
mdb_printf("Show or set sector size.\n");
}
static const mdb_dcmd_t dcmds[] = {
{ "mbr", "?[-b]", "dump Master Boot Record information", cmd_mbr,
mbr_help },
{ "gpt", "?[-ag]", "dump an EFI GPT", cmd_gpt, gpt_help },
{ "vtoc", "?[-cr]", "dump VTOC information", cmd_vtoc, vtoc_help },
{ "sectorsize", NULL, "set or show sector size", cmd_sect, sect_help },
{ NULL }
};
static const mdb_modinfo_t modinfo = {
MDB_API_VERSION, dcmds, NULL
};
const mdb_modinfo_t *
_mdb_init(void)
{
return (&modinfo);
}