disk.c revision 223695
1139749Simp/*- 2119815Smarcel * Copyright (c) 1998 Michael Smith <msmith@freebsd.org> 3119815Smarcel * All rights reserved. 4119815Smarcel * 5119815Smarcel * Redistribution and use in source and binary forms, with or without 6119815Smarcel * modification, are permitted provided that the following conditions 7119815Smarcel * are met: 8119815Smarcel * 1. Redistributions of source code must retain the above copyright 9119815Smarcel * notice, this list of conditions and the following disclaimer. 10119815Smarcel * 2. Redistributions in binary form must reproduce the above copyright 11119815Smarcel * notice, this list of conditions and the following disclaimer in the 12119815Smarcel * documentation and/or other materials provided with the distribution. 13119815Smarcel * 14119815Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15119815Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16119815Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17119815Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18119815Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19119815Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20119815Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21119815Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22119815Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23119815Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24119815Smarcel * SUCH DAMAGE. 25119815Smarcel */ 26119815Smarcel 27119815Smarcel#include <sys/cdefs.h> 28119815Smarcel__FBSDID("$FreeBSD: head/sys/boot/common/disk.c 223695 2011-06-30 16:08:56Z dfr $"); 29119815Smarcel 30119815Smarcel/* 31119815Smarcel * MBR/GPT partitioned disk device handling. 32119815Smarcel * 33119815Smarcel * Ideas and algorithms from: 34119815Smarcel * 35119815Smarcel * - NetBSD libi386/biosdisk.c 36119815Smarcel * - FreeBSD biosboot/disk.c 37119815Smarcel * 38119815Smarcel */ 39119815Smarcel 40137949Smarcel#include <stand.h> 41137949Smarcel 42119815Smarcel#include <sys/diskmbr.h> 43119815Smarcel#include <sys/disklabel.h> 44119815Smarcel#include <sys/gpt.h> 45119815Smarcel 46119815Smarcel#include <stdarg.h> 47119815Smarcel#include <uuid.h> 48119815Smarcel 49119815Smarcel#include <bootstrap.h> 50119815Smarcel 51119815Smarcel#include "disk.h" 52119815Smarcel 53119815Smarcel#ifdef DISK_DEBUG 54119815Smarcel# define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 55119815Smarcel#else 56119815Smarcel# define DEBUG(fmt, args...) 57119815Smarcel#endif 58119815Smarcel 59119815Smarcel/* 60119815Smarcel * Search for a slice with the following preferences: 61119815Smarcel * 62119815Smarcel * 1: Active FreeBSD slice 63119815Smarcel * 2: Non-active FreeBSD slice 64119815Smarcel * 3: Active Linux slice 65119815Smarcel * 4: non-active Linux slice 66119815Smarcel * 5: Active FAT/FAT32 slice 67119815Smarcel * 6: non-active FAT/FAT32 slice 68119815Smarcel */ 69119815Smarcel#define PREF_RAWDISK 0 70119815Smarcel#define PREF_FBSD_ACT 1 71119815Smarcel#define PREF_FBSD 2 72119815Smarcel#define PREF_LINUX_ACT 3 73119815Smarcel#define PREF_LINUX 4 74119815Smarcel#define PREF_DOS_ACT 5 75119815Smarcel#define PREF_DOS 6 76119815Smarcel#define PREF_NONE 7 77119815Smarcel 78119815Smarcel#ifdef LOADER_GPT_SUPPORT 79119815Smarcel 80119815Smarcelstruct gpt_part { 81119815Smarcel int gp_index; 82119815Smarcel uuid_t gp_type; 83119815Smarcel uint64_t gp_start; 84119815Smarcel uint64_t gp_end; 85119815Smarcel}; 86119815Smarcel 87119815Smarcelstatic uuid_t efi = GPT_ENT_TYPE_EFI; 88119815Smarcelstatic uuid_t freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; 89119815Smarcelstatic uuid_t freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; 90119815Smarcelstatic uuid_t freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; 91119815Smarcelstatic uuid_t freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; 92119815Smarcelstatic uuid_t ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; 93119815Smarcel 94119815Smarcel#endif 95119815Smarcel 96119815Smarcel/* Given a size in 512 byte sectors, convert it to a human-readable number. */ 97119815Smarcelstatic char * 98119815Smarceldisplay_size(uint64_t size) 99119815Smarcel{ 100119815Smarcel static char buf[80]; 101119815Smarcel char unit; 102119815Smarcel 103119815Smarcel size /= 2; 104119815Smarcel unit = 'K'; 105119815Smarcel if (size >= 10485760000LL) { 106119815Smarcel size /= 1073741824; 107119815Smarcel unit = 'T'; 108119815Smarcel } else if (size >= 10240000) { 109119815Smarcel size /= 1048576; 110119815Smarcel unit = 'G'; 111119815Smarcel } else if (size >= 10000) { 112119815Smarcel size /= 1024; 113119815Smarcel unit = 'M'; 114119815Smarcel } 115119815Smarcel sprintf(buf, "%.6ld%cB", (long)size, unit); 116119815Smarcel return (buf); 117119815Smarcel} 118119815Smarcel 119119815Smarcelstatic void 120119815Smarceldisk_checkextended(struct disk_devdesc *dev, 121119815Smarcel struct dos_partition *slicetab, int slicenum, int *nslicesp) 122119815Smarcel{ 123119815Smarcel uint8_t buf[DISK_SECSIZE]; 124119815Smarcel struct dos_partition *dp; 125119815Smarcel uint32_t base; 126119815Smarcel int rc, i, start, end; 127119815Smarcel 128119815Smarcel dp = &slicetab[slicenum]; 129119815Smarcel start = *nslicesp; 130119815Smarcel 131119815Smarcel if (dp->dp_size == 0) 132119815Smarcel goto done; 133119815Smarcel if (dp->dp_typ != DOSPTYP_EXT) 134119815Smarcel goto done; 135119815Smarcel rc = dev->d_dev->dv_strategy(dev, F_READ, dp->dp_start, DISK_SECSIZE, 136119815Smarcel (char *) buf, NULL); 137119815Smarcel if (rc) 138119815Smarcel goto done; 139119815Smarcel if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) { 140119815Smarcel DEBUG("no magic in extended table"); 141119815Smarcel goto done; 142119815Smarcel } 143119815Smarcel base = dp->dp_start; 144119815Smarcel dp = (struct dos_partition *) &buf[DOSPARTOFF]; 145119815Smarcel for (i = 0; i < NDOSPART; i++, dp++) { 146119815Smarcel if (dp->dp_size == 0) 147119815Smarcel continue; 148119815Smarcel if (*nslicesp == NEXTDOSPART) 149119815Smarcel goto done; 150119815Smarcel dp->dp_start += base; 151119815Smarcel bcopy(dp, &slicetab[*nslicesp], sizeof(*dp)); 152119815Smarcel (*nslicesp)++; 153119815Smarcel } 154119815Smarcel end = *nslicesp; 155119815Smarcel 156119815Smarcel /* 157119815Smarcel * now, recursively check the slices we just added 158119815Smarcel */ 159119815Smarcel for (i = start; i < end; i++) 160119815Smarcel disk_checkextended(dev, slicetab, i, nslicesp); 161119815Smarceldone: 162119815Smarcel return; 163119815Smarcel} 164119815Smarcel 165119815Smarcelstatic int 166119815Smarceldisk_readslicetab(struct disk_devdesc *dev, 167119815Smarcel struct dos_partition **slicetabp, int *nslicesp) 168119815Smarcel{ 169119815Smarcel struct dos_partition *slicetab = NULL; 170119815Smarcel int nslices, i; 171119815Smarcel int rc; 172119815Smarcel uint8_t buf[DISK_SECSIZE]; 173119815Smarcel 174119815Smarcel /* 175119815Smarcel * Find the slice in the DOS slice table. 176119815Smarcel */ 177119815Smarcel rc = dev->d_dev->dv_strategy(dev, F_READ, 0, DISK_SECSIZE, 178119815Smarcel (char *) buf, NULL); 179119815Smarcel if (rc) { 180119815Smarcel DEBUG("error reading MBR"); 181119815Smarcel return (rc); 182119815Smarcel } 183119815Smarcel 184119815Smarcel /* 185119815Smarcel * Check the slice table magic. 186119815Smarcel */ 187119815Smarcel if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) { 188119815Smarcel DEBUG("no slice table/MBR (no magic)"); 189119815Smarcel return (rc); 190119815Smarcel } 191119815Smarcel 192119815Smarcel /* 193119815Smarcel * copy the partition table, then pick up any extended partitions. 194119815Smarcel */ 195119815Smarcel slicetab = malloc(NEXTDOSPART * sizeof(struct dos_partition)); 196119815Smarcel bcopy(buf + DOSPARTOFF, slicetab, 197119815Smarcel sizeof(struct dos_partition) * NDOSPART); 198119815Smarcel nslices = NDOSPART; /* extended slices start here */ 199119815Smarcel for (i = 0; i < NDOSPART; i++) 200157989Smarcel disk_checkextended(dev, slicetab, i, &nslices); 201157989Smarcel 202119815Smarcel *slicetabp = slicetab; 203119815Smarcel *nslicesp = nslices; 204119815Smarcel return (0); 205119815Smarcel} 206119815Smarcel 207119815Smarcel/* 208119815Smarcel * Search for the best MBR slice (typically the first FreeBSD slice). 209119815Smarcel */ 210119815Smarcelstatic int 211119815Smarceldisk_bestslice(struct dos_partition *slicetab, int nslices) 212119815Smarcel{ 213119815Smarcel struct dos_partition *dp; 214119815Smarcel int pref, preflevel; 215119815Smarcel int i, prefslice; 216119815Smarcel 217119815Smarcel prefslice = 0; 218119815Smarcel preflevel = PREF_NONE; 219119815Smarcel 220157380Smarcel dp = &slicetab[0]; 221119815Smarcel for (i = 0; i < nslices; i++, dp++) { 222119815Smarcel switch (dp->dp_typ) { 223119815Smarcel case DOSPTYP_386BSD: /* FreeBSD */ 224119815Smarcel pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD; 225119815Smarcel break; 226119815Smarcel 227119815Smarcel case DOSPTYP_LINUX: 228119815Smarcel pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX; 229119815Smarcel break; 230119815Smarcel 231119815Smarcel case 0x01: /* DOS/Windows */ 232119815Smarcel case 0x04: 233119815Smarcel case 0x06: 234119815Smarcel case 0x0b: 235119815Smarcel case 0x0c: 236119815Smarcel case 0x0e: 237119815Smarcel pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS; 238119815Smarcel break; 239119815Smarcel 240119815Smarcel default: 241119815Smarcel pref = PREF_NONE; 242119815Smarcel } 243119815Smarcel if (pref < preflevel) { 244119815Smarcel preflevel = pref; 245119815Smarcel prefslice = i + 1; 246119815Smarcel } 247119815Smarcel } 248119815Smarcel return (prefslice); 249119815Smarcel} 250119815Smarcel 251119815Smarcelstatic int 252119815Smarceldisk_openmbr(struct disk_devdesc *dev) 253119815Smarcel{ 254119815Smarcel struct dos_partition *slicetab = NULL, *dptr; 255119815Smarcel int nslices, sector, slice; 256119815Smarcel int rc; 257119815Smarcel uint8_t buf[DISK_SECSIZE]; 258119815Smarcel struct disklabel *lp; 259119815Smarcel 260119815Smarcel /* 261119815Smarcel * Following calculations attempt to determine the correct value 262119815Smarcel * for dev->d_offset by looking for the slice and partition specified, 263119815Smarcel * or searching for reasonable defaults. 264119815Smarcel */ 265119815Smarcel rc = disk_readslicetab(dev, &slicetab, &nslices); 266119815Smarcel if (rc) 267119815Smarcel return (rc); 268119815Smarcel 269119815Smarcel /* 270119815Smarcel * if a slice number was supplied but not found, this is an error. 271119815Smarcel */ 272119815Smarcel if (dev->d_slice > 0) { 273119815Smarcel slice = dev->d_slice - 1; 274119815Smarcel if (slice >= nslices) { 275119815Smarcel DEBUG("slice %d not found", slice); 276119815Smarcel rc = EPART; 277119815Smarcel goto out; 278119815Smarcel } 279119815Smarcel } 280119815Smarcel 281119815Smarcel /* 282119815Smarcel * Check for the historically bogus MBR found on true dedicated disks 283119815Smarcel */ 284119815Smarcel if (slicetab[3].dp_typ == DOSPTYP_386BSD && 285119815Smarcel slicetab[3].dp_start == 0 && slicetab[3].dp_size == 50000) { 286119815Smarcel sector = 0; 287119815Smarcel goto unsliced; 288119815Smarcel } 289119815Smarcel 290119815Smarcel /* 291119815Smarcel * Try to auto-detect the best slice; this should always give 292119815Smarcel * a slice number 293119815Smarcel */ 294119815Smarcel if (dev->d_slice == 0) { 295119815Smarcel slice = disk_bestslice(slicetab, nslices); 296119815Smarcel if (slice == -1) { 297119815Smarcel rc = ENOENT; 298119815Smarcel goto out; 299119815Smarcel } 300119815Smarcel dev->d_slice = slice; 301119815Smarcel } 302119815Smarcel 303119815Smarcel /* 304119815Smarcel * Accept the supplied slice number unequivocally (we may be looking 305119815Smarcel * at a DOS partition). 306119815Smarcel * Note: we number 1-4, offsets are 0-3 307119815Smarcel */ 308127742Smarcel dptr = &slicetab[dev->d_slice - 1]; 309119815Smarcel sector = dptr->dp_start; 310119815Smarcel DEBUG("slice entry %d at %d, %d sectors", 311119815Smarcel dev->d_slice - 1, sector, dptr->dp_size); 312119815Smarcel 313119815Smarcelunsliced: 314119815Smarcel /* 315119815Smarcel * Now we have the slice offset, look for the partition in the 316119815Smarcel * disklabel if we have a partition to start with. 317119815Smarcel * 318119815Smarcel * XXX we might want to check the label checksum. 319119815Smarcel */ 320119815Smarcel if (dev->d_partition < 0) { 321119815Smarcel /* no partition, must be after the slice */ 322119815Smarcel DEBUG("opening raw slice"); 323119815Smarcel dev->d_offset = sector; 324157380Smarcel rc = 0; 325119815Smarcel goto out; 326157380Smarcel } 327119815Smarcel 328157380Smarcel rc = dev->d_dev->dv_strategy(dev, F_READ, sector + LABELSECTOR, 329157380Smarcel DISK_SECSIZE, (char *) buf, NULL); 330119815Smarcel if (rc) { 331119815Smarcel DEBUG("error reading disklabel"); 332119815Smarcel goto out; 333157380Smarcel } 334157380Smarcel 335119815Smarcel lp = (struct disklabel *) buf; 336157380Smarcel 337157380Smarcel if (lp->d_magic != DISKMAGIC) { 338157380Smarcel DEBUG("no disklabel"); 339157380Smarcel rc = ENOENT; 340157380Smarcel goto out; 341157380Smarcel } 342157380Smarcel if (dev->d_partition >= lp->d_npartitions) { 343157380Smarcel DEBUG("partition '%c' exceeds partitions in table (a-'%c')", 344119815Smarcel 'a' + dev->d_partition, 345119815Smarcel 'a' + lp->d_npartitions); 346119815Smarcel rc = EPART; 347119815Smarcel goto out; 348119815Smarcel } 349119815Smarcel 350119815Smarcel dev->d_offset = 351119815Smarcel lp->d_partitions[dev->d_partition].p_offset - 352119815Smarcel lp->d_partitions[RAW_PART].p_offset + 353119815Smarcel sector; 354119815Smarcel rc = 0; 355119815Smarcel 356119815Smarcelout: 357119815Smarcel if (slicetab) 358119815Smarcel free(slicetab); 359119815Smarcel return (rc); 360119815Smarcel} 361119815Smarcel 362119815Smarcel/* 363119815Smarcel * Print out each valid partition in the disklabel of a FreeBSD slice. 364119815Smarcel * For size calculations, we assume a 512 byte sector size. 365119815Smarcel */ 366119815Smarcelstatic void 367119815Smarceldisk_printbsdslice(struct disk_devdesc *dev, daddr_t offset, 368119815Smarcel char *prefix, int verbose) 369119815Smarcel{ 370119815Smarcel char line[80]; 371119815Smarcel char buf[DISK_SECSIZE]; 372119815Smarcel struct disklabel *lp; 373119815Smarcel int i, rc, fstype; 374119815Smarcel 375119815Smarcel /* read disklabel */ 376119815Smarcel rc = dev->d_dev->dv_strategy(dev, F_READ, offset + LABELSECTOR, 377119815Smarcel DISK_SECSIZE, (char *) buf, NULL); 378119815Smarcel if (rc) 379119815Smarcel return; 380119815Smarcel lp =(struct disklabel *)(&buf[0]); 381119815Smarcel if (lp->d_magic != DISKMAGIC) { 382119815Smarcel sprintf(line, "%s: FFS bad disklabel\n", prefix); 383119815Smarcel pager_output(line); 384119815Smarcel return; 385119815Smarcel } 386119815Smarcel 387119815Smarcel /* Print partitions */ 388119815Smarcel for (i = 0; i < lp->d_npartitions; i++) { 389119815Smarcel /* 390119815Smarcel * For each partition, make sure we know what type of fs it 391119815Smarcel * is. If not, then skip it. 392119815Smarcel */ 393119815Smarcel fstype = lp->d_partitions[i].p_fstype; 394119815Smarcel if (fstype != FS_BSDFFS && 395119815Smarcel fstype != FS_SWAP && 396119815Smarcel fstype != FS_VINUM) 397119815Smarcel continue; 398119815Smarcel 399119815Smarcel /* Only print out statistics in verbose mode */ 400119815Smarcel if (verbose) 401119815Smarcel sprintf(line, " %s%c: %s %s (%d - %d)\n", 402119815Smarcel prefix, 'a' + i, 403119815Smarcel (fstype == FS_SWAP) ? "swap " : 404119815Smarcel (fstype == FS_VINUM) ? "vinum" : 405119815Smarcel "FFS ", 406119815Smarcel display_size(lp->d_partitions[i].p_size), 407119815Smarcel lp->d_partitions[i].p_offset, 408119815Smarcel (lp->d_partitions[i].p_offset 409119815Smarcel + lp->d_partitions[i].p_size)); 410119815Smarcel else 411119815Smarcel sprintf(line, " %s%c: %s\n", prefix, 'a' + i, 412119815Smarcel (fstype == FS_SWAP) ? "swap" : 413131043Sphk (fstype == FS_VINUM) ? "vinum" : 414119815Smarcel "FFS"); 415131043Sphk pager_output(line); 416119815Smarcel } 417119815Smarcel} 418119815Smarcel 419119815Smarcelstatic void 420119815Smarceldisk_printslice(struct disk_devdesc *dev, int slice, 421119815Smarcel struct dos_partition *dp, char *prefix, int verbose) 422119815Smarcel{ 423119815Smarcel char stats[80]; 424119815Smarcel char line[80]; 425119815Smarcel 426119815Smarcel if (verbose) 427119815Smarcel sprintf(stats, " %s (%d - %d)", display_size(dp->dp_size), 428119815Smarcel dp->dp_start, dp->dp_start + dp->dp_size); 429119815Smarcel else 430119815Smarcel stats[0] = '\0'; 431119815Smarcel 432119815Smarcel switch (dp->dp_typ) { 433119815Smarcel case DOSPTYP_386BSD: 434119815Smarcel disk_printbsdslice(dev, (daddr_t)dp->dp_start, 435119815Smarcel prefix, verbose); 436119815Smarcel return; 437119815Smarcel case DOSPTYP_LINSWP: 438119815Smarcel sprintf(line, "%s: Linux swap%s\n", prefix, stats); 439119815Smarcel break; 440119815Smarcel case DOSPTYP_LINUX: 441119815Smarcel /* 442120143Smarcel * XXX 443119815Smarcel * read the superblock to confirm this is an ext2fs partition? 444119815Smarcel */ 445157300Smarcel sprintf(line, "%s: ext2fs%s\n", prefix, stats); 446157418Smarcel break; 447119815Smarcel case 0x00: /* unused partition */ 448119815Smarcel case DOSPTYP_EXT: 449119815Smarcel return; 450120143Smarcel case 0x01: 451120143Smarcel sprintf(line, "%s: FAT-12%s\n", prefix, stats); 452120143Smarcel break; 453157300Smarcel case 0x04: 454120143Smarcel case 0x06: 455119815Smarcel case 0x0e: 456119815Smarcel sprintf(line, "%s: FAT-16%s\n", prefix, stats); 457119815Smarcel break; 458119815Smarcel case 0x07: 459119815Smarcel sprintf(line, "%s: NTFS/HPFS%s\n", prefix, stats); 460119815Smarcel break; 461119815Smarcel case 0x0b: 462119815Smarcel case 0x0c: 463119815Smarcel sprintf(line, "%s: FAT-32%s\n", prefix, stats); 464119815Smarcel break; 465119815Smarcel default: 466157300Smarcel sprintf(line, "%s: Unknown fs: 0x%x %s\n", prefix, dp->dp_typ, 467119815Smarcel stats); 468157300Smarcel } 469131043Sphk pager_output(line); 470131043Sphk} 471131043Sphk 472131043Sphkint 473155973Smarceldisk_printmbr(struct disk_devdesc *dev, char *prefix, int verbose) 474119815Smarcel{ 475119815Smarcel struct dos_partition *slicetab; 476119815Smarcel int nslices, i; 477119815Smarcel int rc; 478119815Smarcel char line[80]; 479119815Smarcel 480119815Smarcel rc = disk_readslicetab(dev, &slicetab, &nslices); 481119815Smarcel if (rc) 482137709Smarcel return (rc); 483120022Smarcel for (i = 0; i < nslices; i++) { 484119815Smarcel sprintf(line, "%ss%d", prefix, i + 1); 485119815Smarcel disk_printslice(dev, i, &slicetab[i], line, verbose); 486120143Smarcel } 487157300Smarcel free(slicetab); 488119815Smarcel return (0); 489119815Smarcel} 490119815Smarcel 491119815Smarcel#ifdef LOADER_GPT_SUPPORT 492119815Smarcel 493119815Smarcelstatic int 494119815Smarceldisk_readgpt(struct disk_devdesc *dev, struct gpt_part **gptp, int *ngptp) 495119815Smarcel{ 496119815Smarcel struct dos_partition *dp; 497119815Smarcel struct gpt_hdr *hdr; 498120022Smarcel struct gpt_ent *ent; 499120022Smarcel struct gpt_part *gptab = NULL; 500120022Smarcel int entries_per_sec, rc, i, part; 501120022Smarcel daddr_t lba, elba; 502120022Smarcel uint8_t gpt[DISK_SECSIZE], tbl[DISK_SECSIZE]; 503120022Smarcel 504120022Smarcel /* 505120022Smarcel * Following calculations attempt to determine the correct value 506120022Smarcel * for dev->d_offset by looking for the slice and partition specified, 507120022Smarcel * or searching for reasonable defaults. 508120022Smarcel */ 509120022Smarcel rc = 0; 510120022Smarcel 511120022Smarcel /* First, read the MBR and see if we have a PMBR. */ 512120022Smarcel rc = dev->d_dev->dv_strategy(dev, F_READ, 0, DISK_SECSIZE, 513120022Smarcel (char *) tbl, NULL); 514120022Smarcel if (rc) { 515120022Smarcel DEBUG("error reading MBR"); 516120022Smarcel return (EIO); 517120022Smarcel } 518120022Smarcel 519120022Smarcel /* Check the slice table magic. */ 520120022Smarcel if (tbl[0x1fe] != 0x55 || tbl[0x1ff] != 0xaa) 521120022Smarcel return (ENXIO); 522120022Smarcel 523120022Smarcel /* Check for GPT slice. */ 524120022Smarcel part = 0; 525120022Smarcel dp = (struct dos_partition *)(tbl + DOSPARTOFF); 526120022Smarcel for (i = 0; i < NDOSPART; i++) { 527120022Smarcel if (dp[i].dp_typ == 0xee) 528137707Smarcel part++; 529137707Smarcel else if ((part != 1) && (dp[i].dp_typ != 0x00)) 530137707Smarcel return (EINVAL); 531137707Smarcel } 532137707Smarcel if (part != 1) 533137707Smarcel return (EINVAL); 534137707Smarcel 535137707Smarcel /* Read primary GPT table header. */ 536137709Smarcel rc = dev->d_dev->dv_strategy(dev, F_READ, 1, DISK_SECSIZE, 537137709Smarcel (char *) gpt, NULL); 538137709Smarcel if (rc) { 539137709Smarcel DEBUG("error reading GPT header"); 540137709Smarcel return (EIO); 541137707Smarcel } 542119815Smarcel hdr = (struct gpt_hdr *)gpt; 543120143Smarcel if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 || 544120143Smarcel hdr->hdr_lba_self != 1 || hdr->hdr_revision < 0x00010000 || 545119815Smarcel hdr->hdr_entsz < sizeof(*ent) || 546157300Smarcel DISK_SECSIZE % hdr->hdr_entsz != 0) { 547120143Smarcel DEBUG("Invalid GPT header\n"); 548119815Smarcel return (EINVAL); 549119815Smarcel } 550119815Smarcel 551119815Smarcel /* Walk the partition table to count valid partitions. */ 552119815Smarcel part = 0; 553119815Smarcel entries_per_sec = DISK_SECSIZE / hdr->hdr_entsz; 554119815Smarcel elba = hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec; 555119815Smarcel for (lba = hdr->hdr_lba_table; lba < elba; lba++) { 556119815Smarcel rc = dev->d_dev->dv_strategy(dev, F_READ, lba, DISK_SECSIZE, 557119815Smarcel (char *) tbl, NULL); 558157300Smarcel if (rc) { 559119815Smarcel DEBUG("error reading GPT table"); 560120143Smarcel return (EIO); 561157300Smarcel } 562119815Smarcel for (i = 0; i < entries_per_sec; i++) { 563120143Smarcel ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz); 564119815Smarcel if (uuid_is_nil(&ent->ent_type, NULL) || 565119815Smarcel ent->ent_lba_start == 0 || 566119815Smarcel ent->ent_lba_end < ent->ent_lba_start) 567157300Smarcel continue; 568119815Smarcel part++; 569155971Smarcel } 570119815Smarcel } 571155971Smarcel 572119815Smarcel /* Save the important information about all the valid partitions. */ 573155971Smarcel if (part != 0) { 574119815Smarcel gptab = malloc(part * sizeof(struct gpt_part)); 575157300Smarcel part = 0; 576119815Smarcel for (lba = hdr->hdr_lba_table; lba < elba; lba++) { 577155971Smarcel rc = dev->d_dev->dv_strategy(dev, F_READ, lba, DISK_SECSIZE, 578119815Smarcel (char *) tbl, NULL); 579155971Smarcel if (rc) { 580119815Smarcel DEBUG("error reading GPT table"); 581119815Smarcel free(gptab); 582119815Smarcel return (EIO); 583119815Smarcel } 584119815Smarcel for (i = 0; i < entries_per_sec; i++) { 585119815Smarcel ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz); 586119815Smarcel if (uuid_is_nil(&ent->ent_type, NULL) || 587119815Smarcel ent->ent_lba_start == 0 || 588119815Smarcel ent->ent_lba_end < ent->ent_lba_start) 589120143Smarcel continue; 590119815Smarcel gptab[part].gp_index = (lba - hdr->hdr_lba_table) * 591119815Smarcel entries_per_sec + i + 1; 592157300Smarcel gptab[part].gp_type = ent->ent_type; 593120143Smarcel gptab[part].gp_start = ent->ent_lba_start; 594157300Smarcel gptab[part].gp_end = ent->ent_lba_end; 595120143Smarcel part++; 596119815Smarcel } 597119815Smarcel } 598119815Smarcel } 599119815Smarcel 600119815Smarcel *gptp = gptab; 601119815Smarcel *ngptp = part; 602119815Smarcel return (0); 603129757Stmm} 604119815Smarcel 605119815Smarcelstatic struct gpt_part * 606119815Smarceldisk_bestgpt(struct gpt_part *gpt, int ngpt) 607119815Smarcel{ 608119815Smarcel struct gpt_part *gp, *prefpart; 609119815Smarcel int i, pref, preflevel; 610119815Smarcel 611119815Smarcel prefpart = NULL; 612119815Smarcel preflevel = PREF_NONE; 613119815Smarcel 614119815Smarcel gp = gpt; 615119815Smarcel for (i = 0; i < ngpt; i++, gp++) { 616119815Smarcel /* Windows. XXX: Also Linux. */ 617119815Smarcel if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL)) 618119815Smarcel pref = PREF_DOS; 619119815Smarcel /* FreeBSD */ 620119815Smarcel else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL) || 621119815Smarcel uuid_equal(&gp->gp_type, &freebsd_zfs, NULL)) 622119815Smarcel pref = PREF_FBSD; 623119815Smarcel else 624119815Smarcel pref = PREF_NONE; 625119815Smarcel if (pref < preflevel) { 626129757Stmm preflevel = pref; 627119815Smarcel prefpart = gp; 628129757Stmm } 629119815Smarcel } 630119815Smarcel return (prefpart); 631119815Smarcel} 632119815Smarcel 633129757Stmmint 634129757Stmmdisk_opengpt(struct disk_devdesc *dev) 635119815Smarcel{ 636119815Smarcel struct gpt_part *gpt = NULL, *gp; 637119815Smarcel int rc, ngpt, i; 638157418Smarcel 639119815Smarcel rc = disk_readgpt(dev, &gpt, &ngpt); 640119815Smarcel if (rc) 641119815Smarcel return (rc); 642119815Smarcel 643119815Smarcel /* Is this a request for the whole disk? */ 644119815Smarcel if (dev->d_slice < 0) { 645157418Smarcel dev->d_offset = 0; 646119815Smarcel rc = 0; 647119815Smarcel goto out; 648119815Smarcel } 649119815Smarcel 650129757Stmm /* 651119815Smarcel * If a partition number was supplied, then the user is trying to use 652119815Smarcel * an MBR address rather than a GPT address, so fail. 653119815Smarcel */ 654119815Smarcel if (dev->d_partition != 0xff) { 655119815Smarcel rc = ENOENT; 656119815Smarcel goto out; 657119815Smarcel } 658119815Smarcel 659119815Smarcel /* If a slice number was supplied but not found, this is an error. */ 660119815Smarcel gp = NULL; 661119815Smarcel if (dev->d_slice > 0) { 662119815Smarcel for (i = 0; i < ngpt; i++) { 663119815Smarcel if (gpt[i].gp_index == dev->d_slice) { 664119815Smarcel gp = &gpt[i]; 665119815Smarcel break; 666119815Smarcel } 667119815Smarcel } 668129757Stmm if (gp == NULL) { 669129757Stmm DEBUG("partition %d not found", dev->d_slice); 670129757Stmm rc = ENOENT; 671119815Smarcel goto out; 672129757Stmm } 673119815Smarcel } 674119815Smarcel 675119815Smarcel /* Try to auto-detect the best partition. */ 676119815Smarcel if (dev->d_slice == 0) { 677119815Smarcel gp = disk_bestgpt(gpt, ngpt); 678129757Stmm if (gp == NULL) { 679129757Stmm rc = ENOENT; 680129757Stmm goto out; 681129757Stmm } 682129757Stmm dev->d_slice = gp->gp_index; 683129757Stmm } 684129757Stmm 685119815Smarcel dev->d_offset = gp->gp_start; 686119815Smarcel rc = 0; 687119815Smarcel 688119815Smarcelout: 689119815Smarcel if (gpt) 690119815Smarcel free(gpt); 691119815Smarcel return (rc); 692119815Smarcel} 693119815Smarcel 694132650Smarcelstatic void 695129757Stmmdisk_printgptpart(struct disk_devdesc *dev, struct gpt_part *gp, 696119815Smarcel char *prefix, int verbose) 697119815Smarcel{ 698119815Smarcel char stats[80]; 699119815Smarcel char line[96]; 700119815Smarcel 701119815Smarcel if (verbose) 702119815Smarcel sprintf(stats, " %s", 703129757Stmm display_size(gp->gp_end + 1 - gp->gp_start)); 704119815Smarcel else 705119815Smarcel stats[0] = '\0'; 706129757Stmm 707119815Smarcel if (uuid_equal(&gp->gp_type, &efi, NULL)) 708119815Smarcel sprintf(line, "%s: EFI %s\n", prefix, stats); 709129757Stmm else if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL)) 710119815Smarcel sprintf(line, "%s: FAT/NTFS %s\n", prefix, stats); 711119815Smarcel else if (uuid_equal(&gp->gp_type, &freebsd_boot, NULL)) 712129757Stmm sprintf(line, "%s: FreeBSD boot%s\n", prefix, stats); 713119815Smarcel else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL)) 714119815Smarcel sprintf(line, "%s: FreeBSD UFS %s\n", prefix, stats); 715119815Smarcel else if (uuid_equal(&gp->gp_type, &freebsd_zfs, NULL)) 716119943Smarcel sprintf(line, "%s: FreeBSD ZFS %s\n", prefix, stats); 717119815Smarcel else if (uuid_equal(&gp->gp_type, &freebsd_swap, NULL)) 718119815Smarcel sprintf(line, "%s: FreeBSD swap%s\n", prefix, stats); 719119815Smarcel else 720119815Smarcel sprintf(line, 721119815Smarcel "%s: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x%s\n", 722119815Smarcel prefix, 723119815Smarcel gp->gp_type.time_low, gp->gp_type.time_mid, 724119815Smarcel gp->gp_type.time_hi_and_version, 725119815Smarcel gp->gp_type.clock_seq_hi_and_reserved, 726119815Smarcel gp->gp_type.clock_seq_low, 727119815Smarcel gp->gp_type.node[0], 728133220Smarcel gp->gp_type.node[1], 729133220Smarcel gp->gp_type.node[2], 730133220Smarcel gp->gp_type.node[3], 731133220Smarcel gp->gp_type.node[4], 732133220Smarcel gp->gp_type.node[5], 733133220Smarcel stats); 734133220Smarcel pager_output(line); 735120022Smarcel} 736120022Smarcel 737120022Smarcelstatic int 738120022Smarceldisk_printgpt(struct disk_devdesc *dev, char *prefix, int verbose) 739120022Smarcel{ 740133220Smarcel struct gpt_part *gpt = NULL; 741120022Smarcel int rc, ngpt, i; 742119815Smarcel char line[80]; 743119815Smarcel 744119815Smarcel rc = disk_readgpt(dev, &gpt, &ngpt); 745119815Smarcel if (rc) 746119815Smarcel return (rc); 747119815Smarcel for (i = 0; i < ngpt; i++) { 748119815Smarcel sprintf(line, "%sp%d", prefix, i + 1); 749119815Smarcel disk_printgptpart(dev, &gpt[i], line, verbose); 750119815Smarcel } 751119815Smarcel free(gpt); 752119815Smarcel return (0); 753157300Smarcel} 754120146Smarcel 755120146Smarcel#endif 756120146Smarcel 757120146Smarcelint 758119815Smarceldisk_open(struct disk_devdesc *dev) 759120146Smarcel{ 760119815Smarcel int rc; 761119815Smarcel 762119815Smarcel /* 763119815Smarcel * While we are reading disk metadata, make sure we do it relative 764119815Smarcel * to the start of the disk 765119815Smarcel */ 766120146Smarcel dev->d_offset = 0; 767119815Smarcel 768120146Smarcel#ifdef LOADER_GPT_SUPPORT 769120146Smarcel rc = disk_opengpt(dev); 770120146Smarcel if (rc) 771120146Smarcel#endif 772120146Smarcel rc = disk_openmbr(dev); 773120146Smarcel 774157300Smarcel return (rc); 775119815Smarcel} 776119815Smarcel 777119815Smarcelvoid 778119815Smarceldisk_print(struct disk_devdesc *dev, char *prefix, int verbose) 779119815Smarcel{ 780119815Smarcel int rc; 781119815Smarcel 782119815Smarcel#ifdef LOADER_GPT_SUPPORT 783119815Smarcel rc = disk_printgpt(dev, prefix, verbose); 784119815Smarcel if (rc == 0) 785119815Smarcel return; 786119815Smarcel#endif 787119815Smarcel disk_printmbr(dev, prefix, verbose); 788119815Smarcel} 789131043Sphk