1 /*- 2 * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <stand.h> 28 #include <sys/param.h> 29 #include <sys/diskmbr.h> 30 #include <sys/disklabel.h> 31 #include <sys/endian.h> 32 #include <sys/gpt.h> 33 #include <sys/stddef.h> 34 #include <sys/queue.h> 35 36 #include <fs/cd9660/iso.h> 37 38 #include <zlib.h> 39 #include <part.h> 40 #include <uuid.h> 41 42 #ifdef PART_DEBUG 43 #define DPRINTF(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) 44 #else 45 #define DPRINTF(fmt, args...) ((void)0) 46 #endif 47 48 #ifdef LOADER_GPT_SUPPORT 49 #define MAXTBLSZ 64 50 static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED; 51 static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; 52 static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; 53 static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI; 54 static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD; 55 static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; 56 static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; 57 static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; 58 static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; 59 static const uuid_t gpt_uuid_apple_apfs = GPT_ENT_TYPE_APPLE_APFS; 60 #endif 61 62 struct pentry { 63 struct ptable_entry part; 64 uint64_t flags; 65 union { 66 uint8_t bsd; 67 uint8_t mbr; 68 uuid_t gpt; 69 } type; 70 STAILQ_ENTRY(pentry) entry; 71 }; 72 73 struct ptable { 74 enum ptable_type type; 75 uint16_t sectorsize; 76 uint64_t sectors; 77 78 STAILQ_HEAD(, pentry) entries; 79 }; 80 81 static struct parttypes { 82 enum partition_type type; 83 const char *desc; 84 } ptypes[] = { 85 { PART_UNKNOWN, "Unknown" }, 86 { PART_EFI, "EFI" }, 87 { PART_FREEBSD, "FreeBSD" }, 88 { PART_FREEBSD_BOOT, "FreeBSD boot" }, 89 { PART_FREEBSD_UFS, "FreeBSD UFS" }, 90 { PART_FREEBSD_ZFS, "FreeBSD ZFS" }, 91 { PART_FREEBSD_SWAP, "FreeBSD swap" }, 92 { PART_FREEBSD_VINUM, "FreeBSD vinum" }, 93 { PART_LINUX, "Linux" }, 94 { PART_LINUX_SWAP, "Linux swap" }, 95 { PART_DOS, "DOS/Windows" }, 96 { PART_ISO9660, "ISO9660" }, 97 { PART_APFS, "APFS" }, 98 }; 99 100 const char * 101 parttype2str(enum partition_type type) 102 { 103 size_t i; 104 105 for (i = 0; i < nitems(ptypes); i++) 106 if (ptypes[i].type == type) 107 return (ptypes[i].desc); 108 return (ptypes[0].desc); 109 } 110 111 #ifdef LOADER_GPT_SUPPORT 112 static void 113 uuid_letoh(uuid_t *uuid) 114 { 115 116 uuid->time_low = le32toh(uuid->time_low); 117 uuid->time_mid = le16toh(uuid->time_mid); 118 uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version); 119 } 120 121 static enum partition_type 122 gpt_parttype(uuid_t type) 123 { 124 125 if (uuid_equal(&type, &gpt_uuid_efi, NULL)) 126 return (PART_EFI); 127 else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL)) 128 return (PART_DOS); 129 else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL)) 130 return (PART_FREEBSD_BOOT); 131 else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL)) 132 return (PART_FREEBSD_UFS); 133 else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL)) 134 return (PART_FREEBSD_ZFS); 135 else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL)) 136 return (PART_FREEBSD_SWAP); 137 else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL)) 138 return (PART_FREEBSD_VINUM); 139 else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL)) 140 return (PART_FREEBSD); 141 else if (uuid_equal(&type, &gpt_uuid_apple_apfs, NULL)) 142 return (PART_APFS); 143 return (PART_UNKNOWN); 144 } 145 146 static struct gpt_hdr * 147 gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last, 148 uint16_t sectorsize) 149 { 150 uint32_t sz, crc; 151 152 if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) { 153 DPRINTF("no GPT signature"); 154 return (NULL); 155 } 156 sz = le32toh(hdr->hdr_size); 157 if (sz < 92 || sz > sectorsize) { 158 DPRINTF("invalid GPT header size: %d", sz); 159 return (NULL); 160 } 161 crc = le32toh(hdr->hdr_crc_self); 162 hdr->hdr_crc_self = crc32(0, Z_NULL, 0); 163 if (crc32(hdr->hdr_crc_self, (const Bytef *)hdr, sz) != crc) { 164 DPRINTF("GPT header's CRC doesn't match"); 165 return (NULL); 166 } 167 hdr->hdr_crc_self = crc; 168 hdr->hdr_revision = le32toh(hdr->hdr_revision); 169 if (hdr->hdr_revision < GPT_HDR_REVISION) { 170 DPRINTF("unsupported GPT revision %d", hdr->hdr_revision); 171 return (NULL); 172 } 173 hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self); 174 if (hdr->hdr_lba_self != lba_self) { 175 DPRINTF("self LBA doesn't match"); 176 return (NULL); 177 } 178 hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt); 179 if (hdr->hdr_lba_alt == hdr->hdr_lba_self) { 180 DPRINTF("invalid alternate LBA"); 181 return (NULL); 182 } 183 hdr->hdr_entries = le32toh(hdr->hdr_entries); 184 hdr->hdr_entsz = le32toh(hdr->hdr_entsz); 185 if (hdr->hdr_entries == 0 || 186 hdr->hdr_entsz < sizeof(struct gpt_ent) || 187 sectorsize % hdr->hdr_entsz != 0) { 188 DPRINTF("invalid entry size or number of entries"); 189 return (NULL); 190 } 191 hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start); 192 hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end); 193 hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table); 194 hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table); 195 uuid_letoh(&hdr->hdr_uuid); 196 return (hdr); 197 } 198 199 static int 200 gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, size_t size, 201 uint64_t lba_last) 202 { 203 struct gpt_ent *ent; 204 uint32_t i, cnt; 205 206 cnt = size / hdr->hdr_entsz; 207 if (hdr->hdr_entries <= cnt) { 208 cnt = hdr->hdr_entries; 209 /* Check CRC only when buffer size is enough for table. */ 210 if (hdr->hdr_crc_table != 211 crc32(0, tbl, hdr->hdr_entries * hdr->hdr_entsz)) { 212 DPRINTF("GPT table's CRC doesn't match"); 213 return (-1); 214 } 215 } 216 for (i = 0; i < cnt; i++) { 217 ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz); 218 uuid_letoh(&ent->ent_type); 219 if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) 220 continue; 221 ent->ent_lba_start = le64toh(ent->ent_lba_start); 222 ent->ent_lba_end = le64toh(ent->ent_lba_end); 223 } 224 return (0); 225 } 226 227 static struct ptable * 228 ptable_gptread(struct ptable *table, void *dev, diskread_t dread) 229 { 230 struct pentry *entry; 231 struct gpt_hdr *phdr, hdr; 232 struct gpt_ent *ent; 233 uint8_t *buf, *tbl; 234 uint64_t offset; 235 int pri, sec; 236 size_t size, i; 237 238 buf = malloc(table->sectorsize); 239 if (buf == NULL) 240 return (NULL); 241 tbl = malloc(table->sectorsize * MAXTBLSZ); 242 if (tbl == NULL) { 243 free(buf); 244 return (NULL); 245 } 246 /* Read the primary GPT header. */ 247 if (dread(dev, buf, 1, 1) != 0) { 248 ptable_close(table); 249 table = NULL; 250 goto out; 251 } 252 pri = sec = 0; 253 /* Check the primary GPT header. */ 254 phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1, 255 table->sectorsize); 256 if (phdr != NULL) { 257 /* Read the primary GPT table. */ 258 size = MIN(MAXTBLSZ, 259 howmany(phdr->hdr_entries * phdr->hdr_entsz, 260 table->sectorsize)); 261 if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && 262 gpt_checktbl(phdr, tbl, size * table->sectorsize, 263 table->sectors - 1) == 0) { 264 memcpy(&hdr, phdr, sizeof(hdr)); 265 pri = 1; 266 } 267 } 268 offset = pri ? hdr.hdr_lba_alt: table->sectors - 1; 269 /* Read the backup GPT header. */ 270 if (dread(dev, buf, 1, offset) != 0) 271 phdr = NULL; 272 else 273 phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset, 274 table->sectors - 1, table->sectorsize); 275 if (phdr != NULL) { 276 /* 277 * Compare primary and backup headers. 278 * If they are equal, then we do not need to read backup 279 * table. If they are different, then prefer backup header 280 * and try to read backup table. 281 */ 282 if (pri == 0 || 283 uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 || 284 hdr.hdr_revision != phdr->hdr_revision || 285 hdr.hdr_size != phdr->hdr_size || 286 hdr.hdr_lba_start != phdr->hdr_lba_start || 287 hdr.hdr_lba_end != phdr->hdr_lba_end || 288 hdr.hdr_entries != phdr->hdr_entries || 289 hdr.hdr_entsz != phdr->hdr_entsz || 290 hdr.hdr_crc_table != phdr->hdr_crc_table) { 291 /* Read the backup GPT table. */ 292 size = MIN(MAXTBLSZ, 293 howmany(phdr->hdr_entries * phdr->hdr_entsz, 294 table->sectorsize)); 295 if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && 296 gpt_checktbl(phdr, tbl, size * table->sectorsize, 297 table->sectors - 1) == 0) { 298 memcpy(&hdr, phdr, sizeof(hdr)); 299 sec = 1; 300 } 301 } 302 } 303 if (pri == 0 && sec == 0) { 304 /* Both primary and backup tables are invalid. */ 305 table->type = PTABLE_NONE; 306 goto out; 307 } 308 DPRINTF("GPT detected"); 309 size = MIN(hdr.hdr_entries * hdr.hdr_entsz, 310 MAXTBLSZ * table->sectorsize); 311 312 /* 313 * If the disk's sector count is smaller than the sector count recorded 314 * in the disk's GPT table header, set the table->sectors to the value 315 * recorded in GPT tables. This is done to work around buggy firmware 316 * that returns truncated disk sizes. 317 * 318 * Note, this is still not a foolproof way to get disk's size. For 319 * example, an image file can be truncated when copied to smaller media. 320 */ 321 table->sectors = hdr.hdr_lba_alt + 1; 322 323 for (i = 0; i < size / hdr.hdr_entsz; i++) { 324 ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz); 325 if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) 326 continue; 327 328 /* Simple sanity checks. */ 329 if (ent->ent_lba_start < hdr.hdr_lba_start || 330 ent->ent_lba_end > hdr.hdr_lba_end || 331 ent->ent_lba_start > ent->ent_lba_end) 332 continue; 333 334 entry = malloc(sizeof(*entry)); 335 if (entry == NULL) 336 break; 337 entry->part.start = ent->ent_lba_start; 338 entry->part.end = ent->ent_lba_end; 339 entry->part.index = i + 1; 340 entry->part.type = gpt_parttype(ent->ent_type); 341 entry->flags = le64toh(ent->ent_attr); 342 memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t)); 343 STAILQ_INSERT_TAIL(&table->entries, entry, entry); 344 DPRINTF("new GPT partition added"); 345 } 346 out: 347 free(buf); 348 free(tbl); 349 return (table); 350 } 351 #endif /* LOADER_GPT_SUPPORT */ 352 353 #ifdef LOADER_MBR_SUPPORT 354 /* We do not need to support too many EBR partitions in the loader */ 355 #define MAXEBRENTRIES 8 356 static enum partition_type 357 mbr_parttype(uint8_t type) 358 { 359 360 switch (type) { 361 case DOSPTYP_386BSD: 362 return (PART_FREEBSD); 363 case DOSPTYP_LINSWP: 364 return (PART_LINUX_SWAP); 365 case DOSPTYP_LINUX: 366 return (PART_LINUX); 367 case 0x01: 368 case 0x04: 369 case 0x06: 370 case 0x07: 371 case 0x0b: 372 case 0x0c: 373 case 0x0e: 374 return (PART_DOS); 375 } 376 return (PART_UNKNOWN); 377 } 378 379 static struct ptable * 380 ptable_ebrread(struct ptable *table, void *dev, diskread_t dread) 381 { 382 struct dos_partition *dp; 383 struct pentry *e1, *entry; 384 uint32_t start, end, offset; 385 u_char *buf; 386 int i, index; 387 388 STAILQ_FOREACH(e1, &table->entries, entry) { 389 if (e1->type.mbr == DOSPTYP_EXT || 390 e1->type.mbr == DOSPTYP_EXTLBA) 391 break; 392 } 393 if (e1 == NULL) 394 return (table); 395 index = 5; 396 offset = e1->part.start; 397 buf = malloc(table->sectorsize); 398 if (buf == NULL) 399 return (table); 400 DPRINTF("EBR detected"); 401 for (i = 0; i < MAXEBRENTRIES; i++) { 402 #if 0 /* Some BIOSes return an incorrect number of sectors */ 403 if (offset >= table->sectors) 404 break; 405 #endif 406 if (dread(dev, buf, 1, offset) != 0) 407 break; 408 dp = (struct dos_partition *)(buf + DOSPARTOFF); 409 if (dp[0].dp_typ == 0) 410 break; 411 start = le32toh(dp[0].dp_start); 412 if (dp[0].dp_typ == DOSPTYP_EXT && 413 dp[1].dp_typ == 0) { 414 offset = e1->part.start + start; 415 continue; 416 } 417 end = le32toh(dp[0].dp_size); 418 entry = malloc(sizeof(*entry)); 419 if (entry == NULL) 420 break; 421 entry->part.start = offset + start; 422 entry->part.end = entry->part.start + end - 1; 423 entry->part.index = index++; 424 entry->part.type = mbr_parttype(dp[0].dp_typ); 425 entry->flags = dp[0].dp_flag; 426 entry->type.mbr = dp[0].dp_typ; 427 STAILQ_INSERT_TAIL(&table->entries, entry, entry); 428 DPRINTF("new EBR partition added"); 429 if (dp[1].dp_typ == 0) 430 break; 431 offset = e1->part.start + le32toh(dp[1].dp_start); 432 } 433 free(buf); 434 return (table); 435 } 436 #endif /* LOADER_MBR_SUPPORT */ 437 438 static enum partition_type 439 bsd_parttype(uint8_t type) 440 { 441 442 switch (type) { 443 case FS_SWAP: 444 return (PART_FREEBSD_SWAP); 445 case FS_BSDFFS: 446 return (PART_FREEBSD_UFS); 447 case FS_VINUM: 448 return (PART_FREEBSD_VINUM); 449 case FS_ZFS: 450 return (PART_FREEBSD_ZFS); 451 } 452 return (PART_UNKNOWN); 453 } 454 455 static struct ptable * 456 ptable_bsdread(struct ptable *table, void *dev, diskread_t dread) 457 { 458 struct disklabel *dl; 459 struct partition *part; 460 struct pentry *entry; 461 uint8_t *buf; 462 uint32_t raw_offset; 463 int i; 464 465 if (table->sectorsize < sizeof(struct disklabel)) { 466 DPRINTF("Too small sectorsize"); 467 return (table); 468 } 469 buf = malloc(table->sectorsize); 470 if (buf == NULL) 471 return (table); 472 if (dread(dev, buf, 1, 1) != 0) { 473 DPRINTF("read failed"); 474 ptable_close(table); 475 table = NULL; 476 goto out; 477 } 478 dl = (struct disklabel *)buf; 479 if (le32toh(dl->d_magic) != DISKMAGIC && 480 le32toh(dl->d_magic2) != DISKMAGIC) 481 goto out; 482 if (le32toh(dl->d_secsize) != table->sectorsize) { 483 DPRINTF("unsupported sector size"); 484 goto out; 485 } 486 dl->d_npartitions = le16toh(dl->d_npartitions); 487 if (dl->d_npartitions > 20 || dl->d_npartitions < 8) { 488 DPRINTF("invalid number of partitions"); 489 goto out; 490 } 491 DPRINTF("BSD detected"); 492 part = &dl->d_partitions[0]; 493 raw_offset = le32toh(part[RAW_PART].p_offset); 494 for (i = 0; i < dl->d_npartitions; i++, part++) { 495 if (i == RAW_PART) 496 continue; 497 if (part->p_size == 0) 498 continue; 499 entry = malloc(sizeof(*entry)); 500 if (entry == NULL) 501 break; 502 entry->part.start = le32toh(part->p_offset) - raw_offset; 503 entry->part.end = entry->part.start + 504 le32toh(part->p_size) - 1; 505 entry->part.type = bsd_parttype(part->p_fstype); 506 entry->part.index = i; /* starts from zero */ 507 entry->type.bsd = part->p_fstype; 508 STAILQ_INSERT_TAIL(&table->entries, entry, entry); 509 DPRINTF("new BSD partition added"); 510 } 511 table->type = PTABLE_BSD; 512 out: 513 free(buf); 514 return (table); 515 } 516 517 #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / table->sectorsize) 518 519 static struct ptable * 520 ptable_iso9660read(struct ptable *table, void *dev, diskread_t dread) 521 { 522 uint8_t *buf; 523 struct iso_primary_descriptor *vd; 524 struct pentry *entry; 525 526 buf = malloc(table->sectorsize); 527 if (buf == NULL) 528 return (table); 529 530 if (dread(dev, buf, 1, cdb2devb(16)) != 0) { 531 DPRINTF("read failed"); 532 ptable_close(table); 533 table = NULL; 534 goto out; 535 } 536 vd = (struct iso_primary_descriptor *)buf; 537 if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) 538 goto out; 539 540 entry = malloc(sizeof(*entry)); 541 if (entry == NULL) 542 goto out; 543 entry->part.start = 0; 544 entry->part.end = table->sectors; 545 entry->part.type = PART_ISO9660; 546 entry->part.index = 0; 547 STAILQ_INSERT_TAIL(&table->entries, entry, entry); 548 549 table->type = PTABLE_ISO9660; 550 551 out: 552 free(buf); 553 return (table); 554 } 555 556 struct ptable * 557 ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize, 558 diskread_t *dread) 559 { 560 struct dos_partition *dp; 561 struct ptable *table; 562 uint8_t *buf; 563 #ifdef LOADER_MBR_SUPPORT 564 struct pentry *entry; 565 uint32_t start, end; 566 int has_ext; 567 #endif 568 table = NULL; 569 dp = NULL; 570 buf = malloc(sectorsize); 571 if (buf == NULL) 572 return (NULL); 573 /* First, read the MBR. */ 574 if (dread(dev, buf, 1, DOSBBSECTOR) != 0) { 575 DPRINTF("read failed"); 576 goto out; 577 } 578 579 table = malloc(sizeof(*table)); 580 if (table == NULL) 581 goto out; 582 table->sectors = sectors; 583 table->sectorsize = sectorsize; 584 table->type = PTABLE_NONE; 585 STAILQ_INIT(&table->entries); 586 587 if (ptable_iso9660read(table, dev, dread) == NULL) { 588 /* Read error. */ 589 table = NULL; 590 goto out; 591 } else if (table->type == PTABLE_ISO9660) 592 goto out; 593 594 /* Check the BSD label. */ 595 if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */ 596 table = NULL; 597 goto out; 598 } else if (table->type == PTABLE_BSD) 599 goto out; 600 601 #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT) 602 /* Check the MBR magic. */ 603 if (buf[DOSMAGICOFFSET] != 0x55 || 604 buf[DOSMAGICOFFSET + 1] != 0xaa) { 605 DPRINTF("magic sequence not found"); 606 #if defined(LOADER_GPT_SUPPORT) 607 /* There is no PMBR, check that we have backup GPT */ 608 table->type = PTABLE_GPT; 609 table = ptable_gptread(table, dev, dread); 610 #endif 611 goto out; 612 } 613 /* Check that we have PMBR. Also do some validation. */ 614 dp = malloc(NDOSPART * sizeof(struct dos_partition)); 615 if (dp == NULL) 616 goto out; 617 bcopy(buf + DOSPARTOFF, dp, NDOSPART * sizeof(struct dos_partition)); 618 619 /* 620 * In mac we can have PMBR partition in hybrid MBR; 621 * that is, MBR partition which has DOSPTYP_PMBR entry defined as 622 * start sector 1. After DOSPTYP_PMBR, there may be other partitions. 623 * UEFI compliant PMBR has no other partitions. 624 */ 625 for (int i = 0; i < NDOSPART; i++) { 626 if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) { 627 DPRINTF("invalid partition flag %x", dp[i].dp_flag); 628 goto out; 629 } 630 #ifdef LOADER_GPT_SUPPORT 631 if (dp[i].dp_typ == DOSPTYP_PMBR && dp[i].dp_start == 1) { 632 table->type = PTABLE_GPT; 633 DPRINTF("PMBR detected"); 634 } 635 #endif 636 } 637 #ifdef LOADER_GPT_SUPPORT 638 if (table->type == PTABLE_GPT) { 639 table = ptable_gptread(table, dev, dread); 640 goto out; 641 } 642 #endif 643 #ifdef LOADER_MBR_SUPPORT 644 /* Read MBR. */ 645 DPRINTF("MBR detected"); 646 table->type = PTABLE_MBR; 647 for (int i = has_ext = 0; i < NDOSPART; i++) { 648 if (dp[i].dp_typ == 0) 649 continue; 650 start = le32dec(&(dp[i].dp_start)); 651 end = le32dec(&(dp[i].dp_size)); 652 if (start == 0 || end == 0) 653 continue; 654 #if 0 /* Some BIOSes return an incorrect number of sectors */ 655 if (start + end - 1 >= sectors) 656 continue; /* XXX: ignore */ 657 #endif 658 if (dp[i].dp_typ == DOSPTYP_EXT || 659 dp[i].dp_typ == DOSPTYP_EXTLBA) 660 has_ext = 1; 661 entry = malloc(sizeof(*entry)); 662 if (entry == NULL) 663 break; 664 entry->part.start = start; 665 entry->part.end = start + end - 1; 666 entry->part.index = i + 1; 667 entry->part.type = mbr_parttype(dp[i].dp_typ); 668 entry->flags = dp[i].dp_flag; 669 entry->type.mbr = dp[i].dp_typ; 670 STAILQ_INSERT_TAIL(&table->entries, entry, entry); 671 DPRINTF("new MBR partition added"); 672 } 673 if (has_ext) { 674 table = ptable_ebrread(table, dev, dread); 675 /* FALLTHROUGH */ 676 } 677 #endif /* LOADER_MBR_SUPPORT */ 678 #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */ 679 out: 680 free(dp); 681 free(buf); 682 return (table); 683 } 684 685 void 686 ptable_close(struct ptable *table) 687 { 688 struct pentry *entry; 689 690 if (table == NULL) 691 return; 692 693 while (!STAILQ_EMPTY(&table->entries)) { 694 entry = STAILQ_FIRST(&table->entries); 695 STAILQ_REMOVE_HEAD(&table->entries, entry); 696 free(entry); 697 } 698 free(table); 699 } 700 701 enum ptable_type 702 ptable_gettype(const struct ptable *table) 703 { 704 705 return (table->type); 706 } 707 708 int 709 ptable_getsize(const struct ptable *table, uint64_t *sizep) 710 { 711 uint64_t tmp = table->sectors * table->sectorsize; 712 713 if (tmp < table->sectors) 714 return (EOVERFLOW); 715 716 if (sizep != NULL) 717 *sizep = tmp; 718 return (0); 719 } 720 721 int 722 ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index) 723 { 724 struct pentry *entry; 725 726 if (part == NULL || table == NULL) 727 return (EINVAL); 728 729 STAILQ_FOREACH(entry, &table->entries, entry) { 730 if (entry->part.index != index) 731 continue; 732 memcpy(part, &entry->part, sizeof(*part)); 733 return (0); 734 } 735 return (ENOENT); 736 } 737 738 /* 739 * Search for a slice with the following preferences: 740 * 741 * 1: Active FreeBSD slice 742 * 2: Non-active FreeBSD slice 743 * 3: Active Linux slice 744 * 4: non-active Linux slice 745 * 5: Active FAT/FAT32 slice 746 * 6: non-active FAT/FAT32 slice 747 */ 748 #define PREF_RAWDISK 0 749 #define PREF_FBSD_ACT 1 750 #define PREF_FBSD 2 751 #define PREF_LINUX_ACT 3 752 #define PREF_LINUX 4 753 #define PREF_DOS_ACT 5 754 #define PREF_DOS 6 755 #define PREF_NONE 7 756 int 757 ptable_getbestpart(const struct ptable *table, struct ptable_entry *part) 758 { 759 struct pentry *entry, *best; 760 int pref, preflevel; 761 762 if (part == NULL || table == NULL) 763 return (EINVAL); 764 765 best = NULL; 766 preflevel = pref = PREF_NONE; 767 STAILQ_FOREACH(entry, &table->entries, entry) { 768 #ifdef LOADER_MBR_SUPPORT 769 if (table->type == PTABLE_MBR) { 770 switch (entry->type.mbr) { 771 case DOSPTYP_386BSD: 772 pref = entry->flags & 0x80 ? PREF_FBSD_ACT: 773 PREF_FBSD; 774 break; 775 case DOSPTYP_LINUX: 776 pref = entry->flags & 0x80 ? PREF_LINUX_ACT: 777 PREF_LINUX; 778 break; 779 case 0x01: /* DOS/Windows */ 780 case 0x04: 781 case 0x06: 782 case 0x0c: 783 case 0x0e: 784 case DOSPTYP_FAT32: 785 pref = entry->flags & 0x80 ? PREF_DOS_ACT: 786 PREF_DOS; 787 break; 788 default: 789 pref = PREF_NONE; 790 } 791 } 792 #endif /* LOADER_MBR_SUPPORT */ 793 #ifdef LOADER_GPT_SUPPORT 794 if (table->type == PTABLE_GPT) { 795 if (entry->part.type == PART_DOS) 796 pref = PREF_DOS; 797 else if (entry->part.type == PART_FREEBSD_UFS || 798 entry->part.type == PART_FREEBSD_ZFS) 799 pref = PREF_FBSD; 800 else 801 pref = PREF_NONE; 802 } 803 #endif /* LOADER_GPT_SUPPORT */ 804 if (pref < preflevel) { 805 preflevel = pref; 806 best = entry; 807 } 808 } 809 if (best != NULL) { 810 memcpy(part, &best->part, sizeof(*part)); 811 return (0); 812 } 813 return (ENOENT); 814 } 815 816 int 817 ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter) 818 { 819 struct pentry *entry; 820 char name[32]; 821 int ret = 0; 822 823 name[0] = '\0'; 824 STAILQ_FOREACH(entry, &table->entries, entry) { 825 #ifdef LOADER_MBR_SUPPORT 826 if (table->type == PTABLE_MBR) 827 sprintf(name, "s%d", entry->part.index); 828 else 829 #endif 830 #ifdef LOADER_GPT_SUPPORT 831 if (table->type == PTABLE_GPT) 832 sprintf(name, "p%d", entry->part.index); 833 else 834 #endif 835 if (table->type == PTABLE_BSD) 836 sprintf(name, "%c", (uint8_t) 'a' + 837 entry->part.index); 838 if ((ret = iter(arg, name, &entry->part)) != 0) 839 return (ret); 840 } 841 return (ret); 842 } 843