1239054Sae/*- 2239054Sae * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org> 3239054Sae * All rights reserved. 4239054Sae * 5239054Sae * Redistribution and use in source and binary forms, with or without 6239054Sae * modification, are permitted provided that the following conditions 7239054Sae * are met: 8239054Sae * 1. Redistributions of source code must retain the above copyright 9239054Sae * notice, this list of conditions and the following disclaimer. 10239054Sae * 2. Redistributions in binary form must reproduce the above copyright 11239054Sae * notice, this list of conditions and the following disclaimer in the 12239054Sae * documentation and/or other materials provided with the distribution. 13239054Sae * 14239054Sae * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15239054Sae * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16239054Sae * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17239054Sae * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18239054Sae * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19239054Sae * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20239054Sae * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21239054Sae * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22239054Sae * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23239054Sae * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24239054Sae * SUCH DAMAGE. 25239054Sae */ 26239054Sae 27239054Sae#include <sys/cdefs.h> 28239054Sae__FBSDID("$FreeBSD: releng/10.2/sys/boot/common/part.c 272933 2014-10-11 06:04:44Z ae $"); 29239054Sae 30239054Sae#include <stand.h> 31239054Sae#include <sys/param.h> 32239054Sae#include <sys/diskmbr.h> 33239054Sae#include <sys/disklabel.h> 34239054Sae#include <sys/endian.h> 35239054Sae#include <sys/gpt.h> 36239054Sae#include <sys/stddef.h> 37239054Sae#include <sys/queue.h> 38239054Sae#include <sys/vtoc.h> 39239054Sae 40239054Sae#include <crc32.h> 41239054Sae#include <part.h> 42239054Sae#include <uuid.h> 43239054Sae 44239054Sae#ifdef PART_DEBUG 45239054Sae#define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 46239054Sae#else 47239054Sae#define DEBUG(fmt, args...) 48239054Sae#endif 49239054Sae 50239054Sae#ifdef LOADER_GPT_SUPPORT 51239054Sae#define MAXTBLSZ 64 52239054Saestatic const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED; 53239054Saestatic const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; 54239054Saestatic const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; 55239054Saestatic const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI; 56272933Saestatic const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD; 57239054Saestatic const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; 58239054Saestatic const uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS; 59239054Saestatic const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; 60239054Saestatic const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; 61239054Saestatic const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; 62239054Sae#endif 63239054Sae 64239054Saestruct pentry { 65239054Sae struct ptable_entry part; 66239054Sae uint64_t flags; 67239054Sae union { 68239054Sae uint8_t bsd; 69239054Sae uint8_t mbr; 70239054Sae uuid_t gpt; 71239054Sae uint16_t vtoc8; 72239054Sae } type; 73239054Sae STAILQ_ENTRY(pentry) entry; 74239054Sae}; 75239054Sae 76239054Saestruct ptable { 77239054Sae enum ptable_type type; 78239054Sae uint16_t sectorsize; 79239054Sae uint64_t sectors; 80239054Sae 81239054Sae STAILQ_HEAD(, pentry) entries; 82239054Sae}; 83239054Sae 84239054Saestatic struct parttypes { 85239054Sae enum partition_type type; 86239054Sae const char *desc; 87239054Sae} ptypes[] = { 88239054Sae { PART_UNKNOWN, "Unknown" }, 89239054Sae { PART_EFI, "EFI" }, 90239054Sae { PART_FREEBSD, "FreeBSD" }, 91239054Sae { PART_FREEBSD_BOOT, "FreeBSD boot" }, 92239054Sae { PART_FREEBSD_NANDFS, "FreeBSD nandfs" }, 93239054Sae { PART_FREEBSD_UFS, "FreeBSD UFS" }, 94239054Sae { PART_FREEBSD_ZFS, "FreeBSD ZFS" }, 95239054Sae { PART_FREEBSD_SWAP, "FreeBSD swap" }, 96239054Sae { PART_FREEBSD_VINUM, "FreeBSD vinum" }, 97239054Sae { PART_LINUX, "Linux" }, 98239054Sae { PART_LINUX_SWAP, "Linux swap" }, 99239054Sae { PART_DOS, "DOS/Windows" }, 100239054Sae}; 101239054Sae 102239054Saeconst char * 103239054Saeparttype2str(enum partition_type type) 104239054Sae{ 105239054Sae int i; 106239054Sae 107239054Sae for (i = 0; i < sizeof(ptypes) / sizeof(ptypes[0]); i++) 108239054Sae if (ptypes[i].type == type) 109239054Sae return (ptypes[i].desc); 110239054Sae return (ptypes[0].desc); 111239054Sae} 112239054Sae 113239054Sae#ifdef LOADER_GPT_SUPPORT 114239054Saestatic void 115239054Saeuuid_letoh(uuid_t *uuid) 116239054Sae{ 117239054Sae 118239054Sae uuid->time_low = le32toh(uuid->time_low); 119239054Sae uuid->time_mid = le16toh(uuid->time_mid); 120239054Sae uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version); 121239054Sae} 122239054Sae 123239054Saestatic enum partition_type 124239054Saegpt_parttype(uuid_t type) 125239054Sae{ 126239054Sae 127239054Sae if (uuid_equal(&type, &gpt_uuid_efi, NULL)) 128239054Sae return (PART_EFI); 129239054Sae else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL)) 130239054Sae return (PART_DOS); 131239054Sae else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL)) 132239054Sae return (PART_FREEBSD_BOOT); 133239054Sae else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL)) 134239054Sae return (PART_FREEBSD_UFS); 135239054Sae else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL)) 136239054Sae return (PART_FREEBSD_ZFS); 137239054Sae else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL)) 138239054Sae return (PART_FREEBSD_SWAP); 139239054Sae else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL)) 140239054Sae return (PART_FREEBSD_VINUM); 141239054Sae else if (uuid_equal(&type, &gpt_uuid_freebsd_nandfs, NULL)) 142239054Sae return (PART_FREEBSD_NANDFS); 143272933Sae else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL)) 144272933Sae return (PART_FREEBSD); 145239054Sae return (PART_UNKNOWN); 146239054Sae} 147239054Sae 148239054Saestatic struct gpt_hdr* 149239054Saegpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last, 150239054Sae uint16_t sectorsize) 151239054Sae{ 152239054Sae uint32_t sz, crc; 153239054Sae 154239054Sae if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) { 155239054Sae DEBUG("no GPT signature"); 156239054Sae return (NULL); 157239054Sae } 158239054Sae sz = le32toh(hdr->hdr_size); 159239054Sae if (sz < 92 || sz > sectorsize) { 160239054Sae DEBUG("invalid GPT header size: %d", sz); 161239054Sae return (NULL); 162239054Sae } 163239054Sae crc = le32toh(hdr->hdr_crc_self); 164239054Sae hdr->hdr_crc_self = 0; 165239054Sae if (crc32(hdr, sz) != crc) { 166239054Sae DEBUG("GPT header's CRC doesn't match"); 167239054Sae return (NULL); 168239054Sae } 169239054Sae hdr->hdr_crc_self = crc; 170239054Sae hdr->hdr_revision = le32toh(hdr->hdr_revision); 171239054Sae if (hdr->hdr_revision < GPT_HDR_REVISION) { 172239054Sae DEBUG("unsupported GPT revision %d", hdr->hdr_revision); 173239054Sae return (NULL); 174239054Sae } 175239054Sae hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self); 176239054Sae if (hdr->hdr_lba_self != lba_self) { 177239054Sae DEBUG("self LBA doesn't match"); 178239054Sae return (NULL); 179239054Sae } 180239054Sae hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt); 181239054Sae if (hdr->hdr_lba_alt == hdr->hdr_lba_self) { 182239054Sae DEBUG("invalid alternate LBA"); 183239054Sae return (NULL); 184239054Sae } 185239054Sae hdr->hdr_entries = le32toh(hdr->hdr_entries); 186239054Sae hdr->hdr_entsz = le32toh(hdr->hdr_entsz); 187254092Sae if (hdr->hdr_entries == 0 || 188239054Sae hdr->hdr_entsz < sizeof(struct gpt_ent) || 189239054Sae sectorsize % hdr->hdr_entsz != 0) { 190239054Sae DEBUG("invalid entry size or number of entries"); 191239054Sae return (NULL); 192239054Sae } 193239054Sae hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start); 194239054Sae hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end); 195239054Sae hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table); 196239054Sae hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table); 197239054Sae uuid_letoh(&hdr->hdr_uuid); 198239054Sae return (hdr); 199239054Sae} 200239054Sae 201239054Saestatic int 202239054Saegpt_checktbl(const struct gpt_hdr *hdr, u_char *tbl, size_t size, 203239054Sae uint64_t lba_last) 204239054Sae{ 205239054Sae struct gpt_ent *ent; 206239054Sae int i, cnt; 207239054Sae 208239054Sae cnt = size / hdr->hdr_entsz; 209254092Sae if (hdr->hdr_entries <= cnt) { 210254092Sae cnt = hdr->hdr_entries; 211254092Sae /* Check CRC only when buffer size is enough for table. */ 212254092Sae if (hdr->hdr_crc_table != 213254092Sae crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) { 214254092Sae DEBUG("GPT table's CRC doesn't match"); 215254092Sae return (-1); 216254092Sae } 217239054Sae } 218270917Sae for (i = 0; i < cnt; i++) { 219270917Sae ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz); 220239054Sae uuid_letoh(&ent->ent_type); 221239054Sae if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) 222239054Sae continue; 223239054Sae ent->ent_lba_start = le64toh(ent->ent_lba_start); 224239054Sae ent->ent_lba_end = le64toh(ent->ent_lba_end); 225239054Sae } 226239054Sae return (0); 227239054Sae} 228239054Sae 229239054Saestatic struct ptable* 230239054Saeptable_gptread(struct ptable *table, void *dev, diskread_t dread) 231239054Sae{ 232239054Sae struct pentry *entry; 233239054Sae struct gpt_hdr *phdr, hdr; 234239054Sae struct gpt_ent *ent; 235239054Sae u_char *buf, *tbl; 236239054Sae uint64_t offset; 237239054Sae int pri, sec, i; 238239054Sae size_t size; 239239054Sae 240239054Sae buf = malloc(table->sectorsize); 241239054Sae if (buf == NULL) 242239054Sae return (NULL); 243239054Sae tbl = malloc(table->sectorsize * MAXTBLSZ); 244239054Sae if (tbl == NULL) { 245239054Sae free(buf); 246239054Sae return (NULL); 247239054Sae } 248239054Sae /* Read the primary GPT header. */ 249239054Sae if (dread(dev, buf, 1, 1) != 0) { 250239054Sae ptable_close(table); 251239054Sae table = NULL; 252239054Sae goto out; 253239054Sae } 254239054Sae pri = sec = 0; 255239054Sae /* Check the primary GPT header. */ 256239054Sae phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1, 257239054Sae table->sectorsize); 258239054Sae if (phdr != NULL) { 259239054Sae /* Read the primary GPT table. */ 260270917Sae size = MIN(MAXTBLSZ, (phdr->hdr_entries * phdr->hdr_entsz + 261270917Sae table->sectorsize - 1) / table->sectorsize); 262239054Sae if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && 263239054Sae gpt_checktbl(phdr, tbl, size * table->sectorsize, 264239054Sae table->sectors - 1) == 0) { 265239054Sae memcpy(&hdr, phdr, sizeof(hdr)); 266239054Sae pri = 1; 267239054Sae } 268239054Sae } 269239054Sae offset = pri ? hdr.hdr_lba_alt: table->sectors - 1; 270239054Sae /* Read the backup GPT header. */ 271239054Sae if (dread(dev, buf, 1, offset) != 0) 272239054Sae phdr = NULL; 273239054Sae else 274239054Sae phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset, 275239054Sae table->sectors - 1, table->sectorsize); 276239054Sae if (phdr != NULL) { 277239054Sae /* 278239054Sae * Compare primary and backup headers. 279239054Sae * If they are equal, then we do not need to read backup 280239054Sae * table. If they are different, then prefer backup header 281239054Sae * and try to read backup table. 282239054Sae */ 283239054Sae if (pri == 0 || 284239054Sae uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 || 285239054Sae hdr.hdr_revision != phdr->hdr_revision || 286239054Sae hdr.hdr_size != phdr->hdr_size || 287239054Sae hdr.hdr_lba_start != phdr->hdr_lba_start || 288239054Sae hdr.hdr_lba_end != phdr->hdr_lba_end || 289239054Sae hdr.hdr_entries != phdr->hdr_entries || 290239054Sae hdr.hdr_entsz != phdr->hdr_entsz || 291239054Sae hdr.hdr_crc_table != phdr->hdr_crc_table) { 292239054Sae /* Read the backup GPT table. */ 293270917Sae size = MIN(MAXTBLSZ, (phdr->hdr_entries * 294270917Sae phdr->hdr_entsz + table->sectorsize - 1) / 295270917Sae table->sectorsize); 296239054Sae if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && 297239054Sae gpt_checktbl(phdr, tbl, size * table->sectorsize, 298239054Sae table->sectors - 1) == 0) { 299239054Sae memcpy(&hdr, phdr, sizeof(hdr)); 300239054Sae sec = 1; 301239054Sae } 302239054Sae } 303239054Sae } 304239054Sae if (pri == 0 && sec == 0) { 305239054Sae /* Both primary and backup tables are invalid. */ 306239054Sae table->type = PTABLE_NONE; 307239054Sae goto out; 308239054Sae } 309239054Sae size = MIN(hdr.hdr_entries * hdr.hdr_entsz, 310239054Sae MAXTBLSZ * table->sectorsize); 311270917Sae for (i = 0; i < size / hdr.hdr_entsz; i++) { 312270917Sae ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz); 313239054Sae if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) 314239054Sae continue; 315239054Sae entry = malloc(sizeof(*entry)); 316239054Sae if (entry == NULL) 317239054Sae break; 318239054Sae entry->part.start = ent->ent_lba_start; 319239054Sae entry->part.end = ent->ent_lba_end; 320239054Sae entry->part.index = i + 1; 321239054Sae entry->part.type = gpt_parttype(ent->ent_type); 322239054Sae entry->flags = le64toh(ent->ent_attr); 323239054Sae memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t)); 324239054Sae STAILQ_INSERT_TAIL(&table->entries, entry, entry); 325239054Sae DEBUG("new GPT partition added"); 326239054Sae } 327239054Saeout: 328239054Sae free(buf); 329239054Sae free(tbl); 330239054Sae return (table); 331239054Sae} 332239054Sae#endif /* LOADER_GPT_SUPPORT */ 333239054Sae 334239054Sae#ifdef LOADER_MBR_SUPPORT 335239054Sae/* We do not need to support too many EBR partitions in the loader */ 336239054Sae#define MAXEBRENTRIES 8 337239054Saestatic enum partition_type 338239054Saembr_parttype(uint8_t type) 339239054Sae{ 340239054Sae 341239054Sae switch (type) { 342239054Sae case DOSPTYP_386BSD: 343239054Sae return (PART_FREEBSD); 344239054Sae case DOSPTYP_LINSWP: 345239054Sae return (PART_LINUX_SWAP); 346239054Sae case DOSPTYP_LINUX: 347239054Sae return (PART_LINUX); 348239054Sae case 0x01: 349239054Sae case 0x04: 350239054Sae case 0x06: 351239054Sae case 0x07: 352239054Sae case 0x0b: 353239054Sae case 0x0c: 354239054Sae case 0x0e: 355239054Sae return (PART_DOS); 356239054Sae } 357239054Sae return (PART_UNKNOWN); 358239054Sae} 359239054Sae 360239054Saestruct ptable* 361239054Saeptable_ebrread(struct ptable *table, void *dev, diskread_t dread) 362239054Sae{ 363239054Sae struct dos_partition *dp; 364239054Sae struct pentry *e1, *entry; 365239054Sae uint32_t start, end, offset; 366239054Sae u_char *buf; 367239054Sae int i, index; 368239054Sae 369239054Sae STAILQ_FOREACH(e1, &table->entries, entry) { 370239054Sae if (e1->type.mbr == DOSPTYP_EXT || 371239054Sae e1->type.mbr == DOSPTYP_EXTLBA) 372239054Sae break; 373239054Sae } 374239054Sae if (e1 == NULL) 375239054Sae return (table); 376239054Sae index = 5; 377239054Sae offset = e1->part.start; 378239054Sae buf = malloc(table->sectorsize); 379239054Sae if (buf == NULL) 380239054Sae return (table); 381239054Sae for (i = 0; i < MAXEBRENTRIES; i++) { 382239325Sae#if 0 /* Some BIOSes return an incorrect number of sectors */ 383239054Sae if (offset >= table->sectors) 384239054Sae break; 385239294Sae#endif 386239054Sae if (dread(dev, buf, 1, offset) != 0) 387239054Sae break; 388239054Sae dp = (struct dos_partition *)(buf + DOSPARTOFF); 389239054Sae if (dp[0].dp_typ == 0) 390239054Sae break; 391239054Sae start = le32toh(dp[0].dp_start); 392239054Sae if (dp[0].dp_typ == DOSPTYP_EXT && 393239054Sae dp[1].dp_typ == 0) { 394239054Sae offset = e1->part.start + start; 395239054Sae continue; 396239054Sae } 397239054Sae end = le32toh(dp[0].dp_size); 398239054Sae entry = malloc(sizeof(*entry)); 399239054Sae if (entry == NULL) 400239054Sae break; 401239088Sae entry->part.start = offset + start; 402239054Sae entry->part.end = entry->part.start + end - 1; 403239054Sae entry->part.index = index++; 404239054Sae entry->part.type = mbr_parttype(dp[0].dp_typ); 405239054Sae entry->flags = dp[0].dp_flag; 406239054Sae entry->type.mbr = dp[0].dp_typ; 407239054Sae STAILQ_INSERT_TAIL(&table->entries, entry, entry); 408239054Sae DEBUG("new EBR partition added"); 409239054Sae if (dp[1].dp_typ == 0) 410239054Sae break; 411239054Sae offset = e1->part.start + le32toh(dp[1].dp_start); 412239054Sae } 413239054Sae free(buf); 414239054Sae return (table); 415239054Sae} 416239054Sae#endif /* LOADER_MBR_SUPPORT */ 417239054Sae 418239054Saestatic enum partition_type 419239054Saebsd_parttype(uint8_t type) 420239054Sae{ 421239054Sae 422239054Sae switch (type) { 423239054Sae case FS_NANDFS: 424239054Sae return (PART_FREEBSD_NANDFS); 425239054Sae case FS_SWAP: 426239054Sae return (PART_FREEBSD_SWAP); 427239054Sae case FS_BSDFFS: 428239054Sae return (PART_FREEBSD_UFS); 429239054Sae case FS_VINUM: 430239054Sae return (PART_FREEBSD_VINUM); 431239054Sae case FS_ZFS: 432239054Sae return (PART_FREEBSD_ZFS); 433239054Sae } 434239054Sae return (PART_UNKNOWN); 435239054Sae} 436239054Sae 437239054Saestruct ptable* 438239054Saeptable_bsdread(struct ptable *table, void *dev, diskread_t dread) 439239054Sae{ 440239054Sae struct disklabel *dl; 441239054Sae struct partition *part; 442239054Sae struct pentry *entry; 443239054Sae u_char *buf; 444239054Sae uint32_t raw_offset; 445239054Sae int i; 446239054Sae 447239054Sae if (table->sectorsize < sizeof(struct disklabel)) { 448239054Sae DEBUG("Too small sectorsize"); 449239054Sae return (table); 450239054Sae } 451239054Sae buf = malloc(table->sectorsize); 452239054Sae if (buf == NULL) 453239054Sae return (table); 454239054Sae if (dread(dev, buf, 1, 1) != 0) { 455239054Sae DEBUG("read failed"); 456239054Sae ptable_close(table); 457239054Sae table = NULL; 458239054Sae goto out; 459239054Sae } 460239054Sae dl = (struct disklabel *)buf; 461239054Sae if (le32toh(dl->d_magic) != DISKMAGIC && 462239054Sae le32toh(dl->d_magic2) != DISKMAGIC) 463239054Sae goto out; 464239054Sae if (le32toh(dl->d_secsize) != table->sectorsize) { 465239054Sae DEBUG("unsupported sector size"); 466239054Sae goto out; 467239054Sae } 468239054Sae dl->d_npartitions = le16toh(dl->d_npartitions); 469239054Sae if (dl->d_npartitions > 20 || dl->d_npartitions < 8) { 470239054Sae DEBUG("invalid number of partitions"); 471239054Sae goto out; 472239054Sae } 473239054Sae part = &dl->d_partitions[0]; 474239054Sae raw_offset = le32toh(part[RAW_PART].p_offset); 475239054Sae for (i = 0; i < dl->d_npartitions; i++, part++) { 476239054Sae if (i == RAW_PART) 477239054Sae continue; 478239127Sae if (part->p_size == 0) 479239054Sae continue; 480239054Sae entry = malloc(sizeof(*entry)); 481239054Sae if (entry == NULL) 482239054Sae break; 483239054Sae entry->part.start = le32toh(part->p_offset) - raw_offset; 484239054Sae entry->part.end = entry->part.start + 485239054Sae le32toh(part->p_size) + 1; 486239054Sae entry->part.type = bsd_parttype(part->p_fstype); 487239054Sae entry->part.index = i; /* starts from zero */ 488239054Sae entry->type.bsd = part->p_fstype; 489239054Sae STAILQ_INSERT_TAIL(&table->entries, entry, entry); 490239054Sae DEBUG("new BSD partition added"); 491239054Sae } 492239054Sae table->type = PTABLE_BSD; 493239054Saeout: 494239054Sae free(buf); 495239054Sae return (table); 496239054Sae} 497239054Sae 498239054Sae#ifdef LOADER_VTOC8_SUPPORT 499239054Saestatic enum partition_type 500239054Saevtoc8_parttype(uint16_t type) 501239054Sae{ 502239054Sae 503239054Sae switch (type) { 504239054Sae case VTOC_TAG_FREEBSD_NANDFS: 505239054Sae return (PART_FREEBSD_NANDFS); 506239054Sae case VTOC_TAG_FREEBSD_SWAP: 507239054Sae return (PART_FREEBSD_SWAP); 508239054Sae case VTOC_TAG_FREEBSD_UFS: 509239054Sae return (PART_FREEBSD_UFS); 510239054Sae case VTOC_TAG_FREEBSD_VINUM: 511239054Sae return (PART_FREEBSD_VINUM); 512239054Sae case VTOC_TAG_FREEBSD_ZFS: 513239054Sae return (PART_FREEBSD_ZFS); 514239054Sae }; 515239054Sae return (PART_UNKNOWN); 516239054Sae} 517239054Sae 518239054Saestatic struct ptable* 519239054Saeptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread) 520239054Sae{ 521239054Sae struct pentry *entry; 522239054Sae struct vtoc8 *dl; 523239054Sae u_char *buf; 524239054Sae uint16_t sum, heads, sectors; 525239054Sae int i; 526239054Sae 527239054Sae if (table->sectorsize != sizeof(struct vtoc8)) 528239054Sae return (table); 529239054Sae buf = malloc(table->sectorsize); 530239054Sae if (buf == NULL) 531239054Sae return (table); 532239054Sae if (dread(dev, buf, 1, 0) != 0) { 533239054Sae DEBUG("read failed"); 534239054Sae ptable_close(table); 535239054Sae table = NULL; 536239054Sae goto out; 537239054Sae } 538239054Sae dl = (struct vtoc8 *)buf; 539239054Sae /* Check the sum */ 540239054Sae for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum)) 541239054Sae sum ^= be16dec(buf + i); 542239054Sae if (sum != 0) { 543239054Sae DEBUG("incorrect checksum"); 544239054Sae goto out; 545239054Sae } 546239054Sae if (be16toh(dl->nparts) != VTOC8_NPARTS) { 547239054Sae DEBUG("invalid number of entries"); 548239054Sae goto out; 549239054Sae } 550239054Sae sectors = be16toh(dl->nsecs); 551239054Sae heads = be16toh(dl->nheads); 552239054Sae if (sectors * heads == 0) { 553239054Sae DEBUG("invalid geometry"); 554239054Sae goto out; 555239054Sae } 556239054Sae for (i = 0; i < VTOC8_NPARTS; i++) { 557239054Sae dl->part[i].tag = be16toh(dl->part[i].tag); 558239054Sae if (i == VTOC_RAW_PART || 559239054Sae dl->part[i].tag == VTOC_TAG_UNASSIGNED) 560239054Sae continue; 561239054Sae entry = malloc(sizeof(*entry)); 562239054Sae if (entry == NULL) 563239054Sae break; 564239054Sae entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors; 565239054Sae entry->part.end = be32toh(dl->map[i].nblks) + 566239054Sae entry->part.start - 1; 567239054Sae entry->part.type = vtoc8_parttype(dl->part[i].tag); 568239054Sae entry->part.index = i; /* starts from zero */ 569239054Sae entry->type.vtoc8 = dl->part[i].tag; 570239054Sae STAILQ_INSERT_TAIL(&table->entries, entry, entry); 571239054Sae DEBUG("new VTOC8 partition added"); 572239054Sae } 573239054Sae table->type = PTABLE_VTOC8; 574239054Saeout: 575239054Sae free(buf); 576239054Sae return (table); 577239054Sae 578239054Sae} 579239054Sae#endif /* LOADER_VTOC8_SUPPORT */ 580239054Sae 581239054Saestruct ptable* 582239054Saeptable_open(void *dev, off_t sectors, uint16_t sectorsize, 583239054Sae diskread_t *dread) 584239054Sae{ 585239054Sae struct dos_partition *dp; 586239054Sae struct ptable *table; 587239054Sae u_char *buf; 588239054Sae int i, count; 589239054Sae#ifdef LOADER_MBR_SUPPORT 590239054Sae struct pentry *entry; 591239054Sae uint32_t start, end; 592239054Sae int has_ext; 593239054Sae#endif 594239054Sae table = NULL; 595239054Sae buf = malloc(sectorsize); 596239054Sae if (buf == NULL) 597239054Sae return (NULL); 598239054Sae /* First, read the MBR. */ 599239054Sae if (dread(dev, buf, 1, DOSBBSECTOR) != 0) { 600239054Sae DEBUG("read failed"); 601239054Sae goto out; 602239054Sae } 603239054Sae 604239054Sae table = malloc(sizeof(*table)); 605239054Sae if (table == NULL) 606239054Sae goto out; 607239054Sae table->sectors = sectors; 608239054Sae table->sectorsize = sectorsize; 609239054Sae table->type = PTABLE_NONE; 610239054Sae STAILQ_INIT(&table->entries); 611239054Sae 612239054Sae#ifdef LOADER_VTOC8_SUPPORT 613239054Sae if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) { 614239054Sae if (ptable_vtoc8read(table, dev, dread) == NULL) { 615239054Sae /* Read error. */ 616239054Sae table = NULL; 617239054Sae goto out; 618239054Sae } else if (table->type == PTABLE_VTOC8) 619239054Sae goto out; 620239054Sae } 621239054Sae#endif 622239054Sae /* Check the BSD label. */ 623239054Sae if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */ 624239054Sae table = NULL; 625239054Sae goto out; 626239054Sae } else if (table->type == PTABLE_BSD) 627239054Sae goto out; 628239054Sae 629239054Sae#if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT) 630239054Sae /* Check the MBR magic. */ 631239054Sae if (buf[DOSMAGICOFFSET] != 0x55 || 632239054Sae buf[DOSMAGICOFFSET + 1] != 0xaa) { 633239054Sae DEBUG("magic sequence not found"); 634239054Sae goto out; 635239054Sae } 636239054Sae /* Check that we have PMBR. Also do some validation. */ 637239054Sae dp = (struct dos_partition *)(buf + DOSPARTOFF); 638239054Sae for (i = 0, count = 0; i < NDOSPART; i++) { 639239054Sae if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) { 640239054Sae DEBUG("invalid partition flag %x", dp[i].dp_flag); 641263964Sae goto out; 642239054Sae } 643239054Sae#ifdef LOADER_GPT_SUPPORT 644239054Sae if (dp[i].dp_typ == DOSPTYP_PMBR) { 645239054Sae table->type = PTABLE_GPT; 646239054Sae DEBUG("PMBR detected"); 647239054Sae } 648239054Sae#endif 649239054Sae if (dp[i].dp_typ != 0) 650239054Sae count++; 651239054Sae } 652239054Sae /* Do we have some invalid values? */ 653263964Sae if (table->type == PTABLE_GPT && count > 1) { 654246630Sae if (dp[1].dp_typ != DOSPTYP_HFS) { 655246630Sae table->type = PTABLE_NONE; 656263964Sae DEBUG("Incorrect PMBR, ignore it"); 657263964Sae } else 658263964Sae DEBUG("Bootcamp detected"); 659239054Sae } 660239054Sae#ifdef LOADER_GPT_SUPPORT 661239054Sae if (table->type == PTABLE_GPT) { 662239054Sae table = ptable_gptread(table, dev, dread); 663239054Sae goto out; 664239054Sae } 665239054Sae#endif 666239054Sae#ifdef LOADER_MBR_SUPPORT 667239054Sae /* Read MBR. */ 668239054Sae table->type = PTABLE_MBR; 669239054Sae for (i = has_ext = 0; i < NDOSPART; i++) { 670239054Sae if (dp[i].dp_typ == 0) 671239054Sae continue; 672240481Skientzle start = le32dec(&(dp[i].dp_start)); 673240481Skientzle end = le32dec(&(dp[i].dp_size)); 674239054Sae if (start == 0 || end == 0) 675239054Sae continue; 676239325Sae#if 0 /* Some BIOSes return an incorrect number of sectors */ 677239054Sae if (start + end - 1 >= sectors) 678239054Sae continue; /* XXX: ignore */ 679239294Sae#endif 680239054Sae if (dp[i].dp_typ == DOSPTYP_EXT || 681239054Sae dp[i].dp_typ == DOSPTYP_EXTLBA) 682239054Sae has_ext = 1; 683239054Sae entry = malloc(sizeof(*entry)); 684239054Sae if (entry == NULL) 685239054Sae break; 686239054Sae entry->part.start = start; 687239054Sae entry->part.end = start + end - 1; 688239054Sae entry->part.index = i + 1; 689239054Sae entry->part.type = mbr_parttype(dp[i].dp_typ); 690239054Sae entry->flags = dp[i].dp_flag; 691239054Sae entry->type.mbr = dp[i].dp_typ; 692239054Sae STAILQ_INSERT_TAIL(&table->entries, entry, entry); 693239054Sae DEBUG("new MBR partition added"); 694239054Sae } 695239054Sae if (has_ext) { 696239054Sae table = ptable_ebrread(table, dev, dread); 697239054Sae /* FALLTHROUGH */ 698239054Sae } 699239054Sae#endif /* LOADER_MBR_SUPPORT */ 700239054Sae#endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */ 701239054Saeout: 702239054Sae free(buf); 703239054Sae return (table); 704239054Sae} 705239054Sae 706239054Saevoid 707239054Saeptable_close(struct ptable *table) 708239054Sae{ 709239054Sae struct pentry *entry; 710239054Sae 711239054Sae while (!STAILQ_EMPTY(&table->entries)) { 712239054Sae entry = STAILQ_FIRST(&table->entries); 713239054Sae STAILQ_REMOVE_HEAD(&table->entries, entry); 714239054Sae free(entry); 715239054Sae } 716239054Sae free(table); 717239054Sae} 718239054Sae 719239054Saeenum ptable_type 720239054Saeptable_gettype(const struct ptable *table) 721239054Sae{ 722239054Sae 723239054Sae return (table->type); 724239054Sae} 725239054Sae 726239054Saeint 727239054Saeptable_getpart(const struct ptable *table, struct ptable_entry *part, int index) 728239054Sae{ 729239054Sae struct pentry *entry; 730239054Sae 731239054Sae if (part == NULL || table == NULL) 732239054Sae return (EINVAL); 733239054Sae 734239054Sae STAILQ_FOREACH(entry, &table->entries, entry) { 735239054Sae if (entry->part.index != index) 736239054Sae continue; 737239054Sae memcpy(part, &entry->part, sizeof(*part)); 738239054Sae return (0); 739239054Sae } 740239054Sae return (ENOENT); 741239054Sae} 742239054Sae 743239054Sae/* 744239054Sae * Search for a slice with the following preferences: 745239054Sae * 746239054Sae * 1: Active FreeBSD slice 747239054Sae * 2: Non-active FreeBSD slice 748239054Sae * 3: Active Linux slice 749239054Sae * 4: non-active Linux slice 750239054Sae * 5: Active FAT/FAT32 slice 751239054Sae * 6: non-active FAT/FAT32 slice 752239054Sae */ 753239054Sae#define PREF_RAWDISK 0 754239054Sae#define PREF_FBSD_ACT 1 755239054Sae#define PREF_FBSD 2 756239054Sae#define PREF_LINUX_ACT 3 757239054Sae#define PREF_LINUX 4 758239054Sae#define PREF_DOS_ACT 5 759239054Sae#define PREF_DOS 6 760239054Sae#define PREF_NONE 7 761239054Saeint 762239054Saeptable_getbestpart(const struct ptable *table, struct ptable_entry *part) 763239054Sae{ 764239054Sae struct pentry *entry, *best; 765239054Sae int pref, preflevel; 766239054Sae 767239054Sae if (part == NULL || table == NULL) 768239054Sae return (EINVAL); 769239054Sae 770239054Sae best = NULL; 771239054Sae preflevel = pref = PREF_NONE; 772239054Sae STAILQ_FOREACH(entry, &table->entries, entry) { 773239054Sae#ifdef LOADER_MBR_SUPPORT 774239054Sae if (table->type == PTABLE_MBR) { 775239054Sae switch (entry->type.mbr) { 776239054Sae case DOSPTYP_386BSD: 777239054Sae pref = entry->flags & 0x80 ? PREF_FBSD_ACT: 778239054Sae PREF_FBSD; 779239054Sae break; 780239054Sae case DOSPTYP_LINUX: 781239054Sae pref = entry->flags & 0x80 ? PREF_LINUX_ACT: 782239054Sae PREF_LINUX; 783239054Sae break; 784239054Sae case 0x01: /* DOS/Windows */ 785239054Sae case 0x04: 786239054Sae case 0x06: 787239054Sae case 0x0c: 788239054Sae case 0x0e: 789239054Sae case DOSPTYP_FAT32: 790239054Sae pref = entry->flags & 0x80 ? PREF_DOS_ACT: 791239054Sae PREF_DOS; 792239054Sae break; 793239054Sae default: 794239054Sae pref = PREF_NONE; 795239054Sae } 796239054Sae } 797239054Sae#endif /* LOADER_MBR_SUPPORT */ 798239054Sae#ifdef LOADER_GPT_SUPPORT 799239054Sae if (table->type == PTABLE_GPT) { 800239054Sae if (entry->part.type == PART_DOS) 801239054Sae pref = PREF_DOS; 802239054Sae else if (entry->part.type == PART_FREEBSD_UFS || 803239054Sae entry->part.type == PART_FREEBSD_ZFS) 804239054Sae pref = PREF_FBSD; 805239054Sae else 806239054Sae pref = PREF_NONE; 807239054Sae } 808239054Sae#endif /* LOADER_GPT_SUPPORT */ 809239054Sae if (pref < preflevel) { 810239054Sae preflevel = pref; 811239054Sae best = entry; 812239054Sae } 813239054Sae } 814239054Sae if (best != NULL) { 815239054Sae memcpy(part, &best->part, sizeof(*part)); 816239054Sae return (0); 817239054Sae } 818239054Sae return (ENOENT); 819239054Sae} 820239054Sae 821239054Saevoid 822239054Saeptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter) 823239054Sae{ 824239054Sae struct pentry *entry; 825239054Sae char name[32]; 826239054Sae 827239054Sae name[0] = '\0'; 828239054Sae STAILQ_FOREACH(entry, &table->entries, entry) { 829239054Sae#ifdef LOADER_MBR_SUPPORT 830239054Sae if (table->type == PTABLE_MBR) 831239054Sae sprintf(name, "s%d", entry->part.index); 832239054Sae else 833239054Sae#endif 834239054Sae#ifdef LOADER_GPT_SUPPORT 835239054Sae if (table->type == PTABLE_GPT) 836239054Sae sprintf(name, "p%d", entry->part.index); 837239054Sae else 838239054Sae#endif 839239054Sae#ifdef LOADER_VTOC8_SUPPORT 840239054Sae if (table->type == PTABLE_VTOC8) 841239054Sae sprintf(name, "%c", (u_char) 'a' + 842239054Sae entry->part.index); 843239054Sae else 844239054Sae#endif 845239054Sae if (table->type == PTABLE_BSD) 846239054Sae sprintf(name, "%c", (u_char) 'a' + 847239054Sae entry->part.index); 848239054Sae iter(arg, name, &entry->part); 849239054Sae } 850239054Sae} 851239054Sae 852