part.c revision 263964
1294288Sdelphij/*- 2141261Sdelphij * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org> 3141261Sdelphij * All rights reserved. 4141261Sdelphij * 5141261Sdelphij * Redistribution and use in source and binary forms, with or without 6141261Sdelphij * modification, are permitted provided that the following conditions 7141261Sdelphij * are met: 8141261Sdelphij * 1. Redistributions of source code must retain the above copyright 9141261Sdelphij * notice, this list of conditions and the following disclaimer. 10141261Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 11141261Sdelphij * notice, this list of conditions and the following disclaimer in the 12141261Sdelphij * documentation and/or other materials provided with the distribution. 13141261Sdelphij * 14141261Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15141261Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16141261Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17141261Sdelphij * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18141261Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19141261Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20141261Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21141261Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22141261Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23141261Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24141261Sdelphij * SUCH DAMAGE. 25141261Sdelphij */ 26141261Sdelphij 27141394Sdelphij#include <sys/cdefs.h> 28141394Sdelphij__FBSDID("$FreeBSD: stable/10/sys/boot/common/part.c 263964 2014-03-31 09:34:46Z ae $"); 29141261Sdelphij 30141261Sdelphij#include <stand.h> 31141261Sdelphij#include <sys/param.h> 32141261Sdelphij#include <sys/diskmbr.h> 33141261Sdelphij#include <sys/disklabel.h> 34141261Sdelphij#include <sys/endian.h> 35141261Sdelphij#include <sys/gpt.h> 36141394Sdelphij#include <sys/stddef.h> 37141261Sdelphij#include <sys/queue.h> 38141261Sdelphij#include <sys/vtoc.h> 39202640Sdelphij 40141261Sdelphij#include <crc32.h> 41264911Sdelphij#include <part.h> 42141261Sdelphij#include <uuid.h> 43141261Sdelphij 44141261Sdelphij#ifdef PART_DEBUG 45141394Sdelphij#define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args) 46171135Sgnn#else 47141394Sdelphij#define DEBUG(fmt, args...) 48141261Sdelphij#endif 49158798Sdelphij 50141261Sdelphij#ifdef LOADER_GPT_SUPPORT 51141261Sdelphij#define MAXTBLSZ 64 52141261Sdelphijstatic const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED; 53141261Sdelphijstatic const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; 54186343Sdelphijstatic const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; 55294288Sdelphijstatic const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI; 56294288Sdelphijstatic const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; 57141261Sdelphijstatic const uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS; 58141261Sdelphijstatic const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; 59294288Sdelphijstatic const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; 60141261Sdelphijstatic const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; 61141261Sdelphij#endif 62141261Sdelphij 63141261Sdelphijstruct pentry { 64141261Sdelphij struct ptable_entry part; 65158798Sdelphij uint64_t flags; 66141261Sdelphij union { 67141261Sdelphij uint8_t bsd; 68141261Sdelphij uint8_t mbr; 69141261Sdelphij uuid_t gpt; 70141261Sdelphij uint16_t vtoc8; 71141261Sdelphij } type; 72141261Sdelphij STAILQ_ENTRY(pentry) entry; 73141261Sdelphij}; 74221793Sdelphij 75141261Sdelphijstruct ptable { 76283270Sdelphij enum ptable_type type; 77283270Sdelphij uint16_t sectorsize; 78283270Sdelphij uint64_t sectors; 79283270Sdelphij 80283270Sdelphij STAILQ_HEAD(, pentry) entries; 81283270Sdelphij}; 82141261Sdelphij 83141261Sdelphijstatic struct parttypes { 84264911Sdelphij enum partition_type type; 85167964Sdelphij const char *desc; 86141261Sdelphij} ptypes[] = { 87141261Sdelphij { PART_UNKNOWN, "Unknown" }, 88249499Sdelphij { PART_EFI, "EFI" }, 89141261Sdelphij { PART_FREEBSD, "FreeBSD" }, 90186343Sdelphij { PART_FREEBSD_BOOT, "FreeBSD boot" }, 91158798Sdelphij { PART_FREEBSD_NANDFS, "FreeBSD nandfs" }, 92141261Sdelphij { PART_FREEBSD_UFS, "FreeBSD UFS" }, 93141261Sdelphij { PART_FREEBSD_ZFS, "FreeBSD ZFS" }, 94141261Sdelphij { PART_FREEBSD_SWAP, "FreeBSD swap" }, 95141261Sdelphij { PART_FREEBSD_VINUM, "FreeBSD vinum" }, 96141261Sdelphij { PART_LINUX, "Linux" }, 97141261Sdelphij { PART_LINUX_SWAP, "Linux swap" }, 98141261Sdelphij { PART_DOS, "DOS/Windows" }, 99141261Sdelphij}; 100141261Sdelphij 101186343Sdelphijconst char * 102186343Sdelphijparttype2str(enum partition_type type) 103141261Sdelphij{ 104158798Sdelphij int i; 105264911Sdelphij 106141261Sdelphij for (i = 0; i < sizeof(ptypes) / sizeof(ptypes[0]); i++) 107141261Sdelphij if (ptypes[i].type == type) 108141261Sdelphij return (ptypes[i].desc); 109141261Sdelphij return (ptypes[0].desc); 110221793Sdelphij} 111141261Sdelphij 112141261Sdelphij#ifdef LOADER_GPT_SUPPORT 113141261Sdelphijstatic void 114141261Sdelphijuuid_letoh(uuid_t *uuid) 115141261Sdelphij{ 116141261Sdelphij 117264911Sdelphij uuid->time_low = le32toh(uuid->time_low); 118158798Sdelphij uuid->time_mid = le16toh(uuid->time_mid); 119235037Sdelphij uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version); 120158798Sdelphij} 121158798Sdelphij 122141261Sdelphijstatic enum partition_type 123221793Sdelphijgpt_parttype(uuid_t type) 124141261Sdelphij{ 125141261Sdelphij 126283270Sdelphij if (uuid_equal(&type, &gpt_uuid_efi, NULL)) 127235037Sdelphij return (PART_EFI); 128241906Sdelphij else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL)) 129141261Sdelphij return (PART_DOS); 130283270Sdelphij else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL)) 131283270Sdelphij return (PART_FREEBSD_BOOT); 132141261Sdelphij else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL)) 133141394Sdelphij return (PART_FREEBSD_UFS); 134141394Sdelphij else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL)) 135141394Sdelphij return (PART_FREEBSD_ZFS); 136141394Sdelphij else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL)) 137141394Sdelphij return (PART_FREEBSD_SWAP); 138141394Sdelphij else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL)) 139141261Sdelphij return (PART_FREEBSD_VINUM); 140141261Sdelphij else if (uuid_equal(&type, &gpt_uuid_freebsd_nandfs, NULL)) 141141261Sdelphij return (PART_FREEBSD_NANDFS); 142141394Sdelphij return (PART_UNKNOWN); 143202640Sdelphij} 144202640Sdelphij 145167964Sdelphijstatic struct gpt_hdr* 146141261Sdelphijgpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last, 147141261Sdelphij uint16_t sectorsize) 148141261Sdelphij{ 149141261Sdelphij uint32_t sz, crc; 150141261Sdelphij 151167964Sdelphij if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) { 152141261Sdelphij DEBUG("no GPT signature"); 153221793Sdelphij return (NULL); 154186343Sdelphij } 155186343Sdelphij sz = le32toh(hdr->hdr_size); 156186343Sdelphij if (sz < 92 || sz > sectorsize) { 157186343Sdelphij DEBUG("invalid GPT header size: %d", sz); 158141261Sdelphij return (NULL); 159141261Sdelphij } 160141394Sdelphij crc = le32toh(hdr->hdr_crc_self); 161141261Sdelphij hdr->hdr_crc_self = 0; 162141261Sdelphij if (crc32(hdr, sz) != crc) { 163141261Sdelphij DEBUG("GPT header's CRC doesn't match"); 164141261Sdelphij return (NULL); 165141261Sdelphij } 166141261Sdelphij hdr->hdr_crc_self = crc; 167294288Sdelphij hdr->hdr_revision = le32toh(hdr->hdr_revision); 168294288Sdelphij if (hdr->hdr_revision < GPT_HDR_REVISION) { 169186343Sdelphij DEBUG("unsupported GPT revision %d", hdr->hdr_revision); 170264911Sdelphij return (NULL); 171186343Sdelphij } 172141261Sdelphij hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self); 173141261Sdelphij if (hdr->hdr_lba_self != lba_self) { 174141261Sdelphij DEBUG("self LBA doesn't match"); 175141261Sdelphij return (NULL); 176141261Sdelphij } 177141261Sdelphij hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt); 178141261Sdelphij if (hdr->hdr_lba_alt == hdr->hdr_lba_self) { 179141261Sdelphij DEBUG("invalid alternate LBA"); 180141261Sdelphij return (NULL); 181141261Sdelphij } 182141261Sdelphij hdr->hdr_entries = le32toh(hdr->hdr_entries); 183141261Sdelphij hdr->hdr_entsz = le32toh(hdr->hdr_entsz); 184141261Sdelphij if (hdr->hdr_entries == 0 || 185141261Sdelphij hdr->hdr_entsz < sizeof(struct gpt_ent) || 186141261Sdelphij sectorsize % hdr->hdr_entsz != 0) { 187141261Sdelphij DEBUG("invalid entry size or number of entries"); 188141261Sdelphij return (NULL); 189141261Sdelphij } 190141261Sdelphij hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start); 191141261Sdelphij hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end); 192141261Sdelphij hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table); 193141261Sdelphij hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table); 194141261Sdelphij uuid_letoh(&hdr->hdr_uuid); 195141394Sdelphij return (hdr); 196141394Sdelphij} 197141394Sdelphij 198141394Sdelphijstatic int 199141394Sdelphijgpt_checktbl(const struct gpt_hdr *hdr, u_char *tbl, size_t size, 200141394Sdelphij uint64_t lba_last) 201141394Sdelphij{ 202141394Sdelphij struct gpt_ent *ent; 203141394Sdelphij int i, cnt; 204141394Sdelphij 205141394Sdelphij cnt = size / hdr->hdr_entsz; 206141394Sdelphij if (hdr->hdr_entries <= cnt) { 207141394Sdelphij cnt = hdr->hdr_entries; 208141394Sdelphij /* Check CRC only when buffer size is enough for table. */ 209141394Sdelphij if (hdr->hdr_crc_table != 210264911Sdelphij crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) { 211264911Sdelphij DEBUG("GPT table's CRC doesn't match"); 212264911Sdelphij return (-1); 213141261Sdelphij } 214141261Sdelphij } 215141261Sdelphij ent = (struct gpt_ent *)tbl; 216141261Sdelphij for (i = 0; i < cnt; i++, ent++) { 217167964Sdelphij uuid_letoh(&ent->ent_type); 218167964Sdelphij if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) 219167964Sdelphij continue; 220141261Sdelphij ent->ent_lba_start = le64toh(ent->ent_lba_start); 221141261Sdelphij ent->ent_lba_end = le64toh(ent->ent_lba_end); 222141261Sdelphij } 223141261Sdelphij return (0); 224141261Sdelphij} 225141261Sdelphij 226141261Sdelphijstatic struct ptable* 227249499Sdelphijptable_gptread(struct ptable *table, void *dev, diskread_t dread) 228249499Sdelphij{ 229249499Sdelphij struct pentry *entry; 230141261Sdelphij struct gpt_hdr *phdr, hdr; 231141261Sdelphij struct gpt_ent *ent; 232141261Sdelphij u_char *buf, *tbl; 233141394Sdelphij uint64_t offset; 234206675Sdelphij int pri, sec, i; 235141394Sdelphij size_t size; 236158798Sdelphij 237158798Sdelphij buf = malloc(table->sectorsize); 238158798Sdelphij if (buf == NULL) 239141261Sdelphij return (NULL); 240141261Sdelphij tbl = malloc(table->sectorsize * MAXTBLSZ); 241141261Sdelphij if (tbl == NULL) { 242141261Sdelphij free(buf); 243141261Sdelphij return (NULL); 244141261Sdelphij } 245141261Sdelphij /* Read the primary GPT header. */ 246141261Sdelphij if (dread(dev, buf, 1, 1) != 0) { 247141261Sdelphij ptable_close(table); 248141261Sdelphij table = NULL; 249141261Sdelphij goto out; 250141261Sdelphij } 251141261Sdelphij pri = sec = 0; 252141261Sdelphij /* Check the primary GPT header. */ 253141261Sdelphij phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1, 254202640Sdelphij table->sectorsize); 255202640Sdelphij if (phdr != NULL) { 256202640Sdelphij /* Read the primary GPT table. */ 257264911Sdelphij size = MIN(MAXTBLSZ, 258202640Sdelphij phdr->hdr_entries * phdr->hdr_entsz / table->sectorsize); 259202640Sdelphij if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && 260214047Sdelphij gpt_checktbl(phdr, tbl, size * table->sectorsize, 261202640Sdelphij table->sectors - 1) == 0) { 262141261Sdelphij memcpy(&hdr, phdr, sizeof(hdr)); 263141261Sdelphij pri = 1; 264141261Sdelphij } 265141261Sdelphij } 266167964Sdelphij offset = pri ? hdr.hdr_lba_alt: table->sectors - 1; 267167964Sdelphij /* Read the backup GPT header. */ 268167964Sdelphij if (dread(dev, buf, 1, offset) != 0) 269141261Sdelphij phdr = NULL; 270141261Sdelphij else 271141261Sdelphij phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset, 272141261Sdelphij table->sectors - 1, table->sectorsize); 273141261Sdelphij if (phdr != NULL) { 274141261Sdelphij /* 275141261Sdelphij * Compare primary and backup headers. 276141261Sdelphij * If they are equal, then we do not need to read backup 277141261Sdelphij * table. If they are different, then prefer backup header 278141261Sdelphij * and try to read backup table. 279141261Sdelphij */ 280141261Sdelphij if (pri == 0 || 281141261Sdelphij uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 || 282186343Sdelphij hdr.hdr_revision != phdr->hdr_revision || 283186343Sdelphij hdr.hdr_size != phdr->hdr_size || 284186343Sdelphij hdr.hdr_lba_start != phdr->hdr_lba_start || 285186343Sdelphij hdr.hdr_lba_end != phdr->hdr_lba_end || 286186343Sdelphij hdr.hdr_entries != phdr->hdr_entries || 287186343Sdelphij hdr.hdr_entsz != phdr->hdr_entsz || 288186343Sdelphij hdr.hdr_crc_table != phdr->hdr_crc_table) { 289186343Sdelphij /* Read the backup GPT table. */ 290186343Sdelphij size = MIN(MAXTBLSZ, phdr->hdr_entries * 291186343Sdelphij phdr->hdr_entsz / table->sectorsize); 292186343Sdelphij if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && 293186343Sdelphij gpt_checktbl(phdr, tbl, size * table->sectorsize, 294186343Sdelphij table->sectors - 1) == 0) { 295186343Sdelphij memcpy(&hdr, phdr, sizeof(hdr)); 296141261Sdelphij sec = 1; 297141261Sdelphij } 298141261Sdelphij } 299158798Sdelphij } 300235037Sdelphij if (pri == 0 && sec == 0) { 301235037Sdelphij /* Both primary and backup tables are invalid. */ 302235037Sdelphij table->type = PTABLE_NONE; 303235037Sdelphij goto out; 304235037Sdelphij } 305235037Sdelphij ent = (struct gpt_ent *)tbl; 306235037Sdelphij size = MIN(hdr.hdr_entries * hdr.hdr_entsz, 307235037Sdelphij MAXTBLSZ * table->sectorsize); 308235037Sdelphij for (i = 0; i < size / hdr.hdr_entsz; i++, ent++) { 309235037Sdelphij if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) 310235037Sdelphij continue; 311235037Sdelphij entry = malloc(sizeof(*entry)); 312158798Sdelphij if (entry == NULL) 313141261Sdelphij break; 314141261Sdelphij entry->part.start = ent->ent_lba_start; 315141261Sdelphij entry->part.end = ent->ent_lba_end; 316141261Sdelphij entry->part.index = i + 1; 317141261Sdelphij entry->part.type = gpt_parttype(ent->ent_type); 318141261Sdelphij entry->flags = le64toh(ent->ent_attr); 319141261Sdelphij memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t)); 320141261Sdelphij STAILQ_INSERT_TAIL(&table->entries, entry, entry); 321141261Sdelphij DEBUG("new GPT partition added"); 322141261Sdelphij } 323141261Sdelphijout: 324141261Sdelphij free(buf); 325141261Sdelphij free(tbl); 326141261Sdelphij return (table); 327141261Sdelphij} 328141261Sdelphij#endif /* LOADER_GPT_SUPPORT */ 329141261Sdelphij 330141261Sdelphij#ifdef LOADER_MBR_SUPPORT 331141261Sdelphij/* We do not need to support too many EBR partitions in the loader */ 332141261Sdelphij#define MAXEBRENTRIES 8 333141261Sdelphijstatic enum partition_type 334141261Sdelphijmbr_parttype(uint8_t type) 335141261Sdelphij{ 336141261Sdelphij 337141261Sdelphij switch (type) { 338141261Sdelphij case DOSPTYP_386BSD: 339141261Sdelphij return (PART_FREEBSD); 340141261Sdelphij case DOSPTYP_LINSWP: 341141261Sdelphij return (PART_LINUX_SWAP); 342141261Sdelphij case DOSPTYP_LINUX: 343141261Sdelphij return (PART_LINUX); 344221793Sdelphij case 0x01: 345221793Sdelphij case 0x04: 346221793Sdelphij case 0x06: 347221793Sdelphij case 0x07: 348221793Sdelphij case 0x0b: 349221793Sdelphij case 0x0c: 350221793Sdelphij case 0x0e: 351221793Sdelphij return (PART_DOS); 352221793Sdelphij } 353221793Sdelphij return (PART_UNKNOWN); 354221793Sdelphij} 355221793Sdelphij 356221793Sdelphijstruct ptable* 357141261Sdelphijptable_ebrread(struct ptable *table, void *dev, diskread_t dread) 358141261Sdelphij{ 359141261Sdelphij struct dos_partition *dp; 360141261Sdelphij struct pentry *e1, *entry; 361141261Sdelphij uint32_t start, end, offset; 362141261Sdelphij u_char *buf; 363141261Sdelphij int i, index; 364141261Sdelphij 365141261Sdelphij STAILQ_FOREACH(e1, &table->entries, entry) { 366141261Sdelphij if (e1->type.mbr == DOSPTYP_EXT || 367141261Sdelphij e1->type.mbr == DOSPTYP_EXTLBA) 368141261Sdelphij break; 369141261Sdelphij } 370141261Sdelphij if (e1 == NULL) 371141261Sdelphij return (table); 372141261Sdelphij index = 5; 373141261Sdelphij offset = e1->part.start; 374141261Sdelphij buf = malloc(table->sectorsize); 375141261Sdelphij if (buf == NULL) 376141261Sdelphij return (table); 377141261Sdelphij for (i = 0; i < MAXEBRENTRIES; i++) { 378141261Sdelphij#if 0 /* Some BIOSes return an incorrect number of sectors */ 379141261Sdelphij if (offset >= table->sectors) 380141261Sdelphij break; 381141261Sdelphij#endif 382141261Sdelphij if (dread(dev, buf, 1, offset) != 0) 383141261Sdelphij break; 384141261Sdelphij dp = (struct dos_partition *)(buf + DOSPARTOFF); 385141261Sdelphij if (dp[0].dp_typ == 0) 386141261Sdelphij break; 387141261Sdelphij start = le32toh(dp[0].dp_start); 388141261Sdelphij if (dp[0].dp_typ == DOSPTYP_EXT && 389141261Sdelphij dp[1].dp_typ == 0) { 390141261Sdelphij offset = e1->part.start + start; 391141261Sdelphij continue; 392141261Sdelphij } 393141261Sdelphij end = le32toh(dp[0].dp_size); 394141261Sdelphij entry = malloc(sizeof(*entry)); 395141261Sdelphij if (entry == NULL) 396141261Sdelphij break; 397141261Sdelphij entry->part.start = offset + start; 398141261Sdelphij entry->part.end = entry->part.start + end - 1; 399221793Sdelphij entry->part.index = index++; 400221793Sdelphij entry->part.type = mbr_parttype(dp[0].dp_typ); 401221793Sdelphij entry->flags = dp[0].dp_flag; 402221793Sdelphij entry->type.mbr = dp[0].dp_typ; 403221793Sdelphij STAILQ_INSERT_TAIL(&table->entries, entry, entry); 404221793Sdelphij DEBUG("new EBR partition added"); 405141261Sdelphij if (dp[1].dp_typ == 0) 406141261Sdelphij break; 407141261Sdelphij offset = e1->part.start + le32toh(dp[1].dp_start); 408141261Sdelphij } 409141261Sdelphij free(buf); 410141261Sdelphij return (table); 411141261Sdelphij} 412141261Sdelphij#endif /* LOADER_MBR_SUPPORT */ 413241906Sdelphij 414241906Sdelphijstatic enum partition_type 415141261Sdelphijbsd_parttype(uint8_t type) 416241906Sdelphij{ 417241906Sdelphij 418241906Sdelphij switch (type) { 419241906Sdelphij case FS_NANDFS: 420241906Sdelphij return (PART_FREEBSD_NANDFS); 421241906Sdelphij case FS_SWAP: 422241906Sdelphij return (PART_FREEBSD_SWAP); 423241906Sdelphij case FS_BSDFFS: 424158798Sdelphij return (PART_FREEBSD_UFS); 425214047Sdelphij case FS_VINUM: 426141261Sdelphij return (PART_FREEBSD_VINUM); 427141261Sdelphij case FS_ZFS: 428141261Sdelphij return (PART_FREEBSD_ZFS); 429241906Sdelphij } 430158798Sdelphij return (PART_UNKNOWN); 431141261Sdelphij} 432141261Sdelphij 433141261Sdelphijstruct ptable* 434141261Sdelphijptable_bsdread(struct ptable *table, void *dev, diskread_t dread) 435141261Sdelphij{ 436141261Sdelphij struct disklabel *dl; 437141261Sdelphij struct partition *part; 438141261Sdelphij struct pentry *entry; 439241906Sdelphij u_char *buf; 440241906Sdelphij uint32_t raw_offset; 441241906Sdelphij int i; 442221793Sdelphij 443141261Sdelphij if (table->sectorsize < sizeof(struct disklabel)) { 444158798Sdelphij DEBUG("Too small sectorsize"); 445141261Sdelphij return (table); 446141261Sdelphij } 447249499Sdelphij buf = malloc(table->sectorsize); 448249499Sdelphij if (buf == NULL) 449283270Sdelphij return (table); 450249499Sdelphij if (dread(dev, buf, 1, 1) != 0) { 451241906Sdelphij DEBUG("read failed"); 452241906Sdelphij ptable_close(table); 453241906Sdelphij table = NULL; 454221793Sdelphij goto out; 455221793Sdelphij } 456141261Sdelphij dl = (struct disklabel *)buf; 457141261Sdelphij if (le32toh(dl->d_magic) != DISKMAGIC && 458141261Sdelphij le32toh(dl->d_magic2) != DISKMAGIC) 459141261Sdelphij goto out; 460221793Sdelphij if (le32toh(dl->d_secsize) != table->sectorsize) { 461221793Sdelphij DEBUG("unsupported sector size"); 462221793Sdelphij goto out; 463221793Sdelphij } 464141261Sdelphij dl->d_npartitions = le16toh(dl->d_npartitions); 465141261Sdelphij if (dl->d_npartitions > 20 || dl->d_npartitions < 8) { 466141261Sdelphij DEBUG("invalid number of partitions"); 467141261Sdelphij goto out; 468141261Sdelphij } 469141261Sdelphij part = &dl->d_partitions[0]; 470141261Sdelphij raw_offset = le32toh(part[RAW_PART].p_offset); 471141261Sdelphij for (i = 0; i < dl->d_npartitions; i++, part++) { 472141261Sdelphij if (i == RAW_PART) 473141261Sdelphij continue; 474141261Sdelphij if (part->p_size == 0) 475141261Sdelphij continue; 476141261Sdelphij entry = malloc(sizeof(*entry)); 477221793Sdelphij if (entry == NULL) 478221793Sdelphij break; 479141261Sdelphij entry->part.start = le32toh(part->p_offset) - raw_offset; 480141261Sdelphij entry->part.end = entry->part.start + 481141261Sdelphij le32toh(part->p_size) + 1; 482141261Sdelphij entry->part.type = bsd_parttype(part->p_fstype); 483141261Sdelphij entry->part.index = i; /* starts from zero */ 484141261Sdelphij entry->type.bsd = part->p_fstype; 485141261Sdelphij STAILQ_INSERT_TAIL(&table->entries, entry, entry); 486141261Sdelphij DEBUG("new BSD partition added"); 487141261Sdelphij } 488141261Sdelphij table->type = PTABLE_BSD; 489141261Sdelphijout: 490141261Sdelphij free(buf); 491141261Sdelphij return (table); 492141261Sdelphij} 493141261Sdelphij 494158798Sdelphij#ifdef LOADER_VTOC8_SUPPORT 495158798Sdelphijstatic enum partition_type 496141261Sdelphijvtoc8_parttype(uint16_t type) 497141261Sdelphij{ 498141261Sdelphij 499141261Sdelphij switch (type) { 500141261Sdelphij case VTOC_TAG_FREEBSD_NANDFS: 501141261Sdelphij return (PART_FREEBSD_NANDFS); 502141261Sdelphij case VTOC_TAG_FREEBSD_SWAP: 503141261Sdelphij return (PART_FREEBSD_SWAP); 504141261Sdelphij case VTOC_TAG_FREEBSD_UFS: 505141261Sdelphij return (PART_FREEBSD_UFS); 506141261Sdelphij case VTOC_TAG_FREEBSD_VINUM: 507141261Sdelphij return (PART_FREEBSD_VINUM); 508141261Sdelphij case VTOC_TAG_FREEBSD_ZFS: 509141261Sdelphij return (PART_FREEBSD_ZFS); 510141261Sdelphij }; 511141261Sdelphij return (PART_UNKNOWN); 512141261Sdelphij} 513141261Sdelphij 514141261Sdelphijstatic struct ptable* 515141261Sdelphijptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread) 516141261Sdelphij{ 517141261Sdelphij struct pentry *entry; 518141261Sdelphij struct vtoc8 *dl; 519141261Sdelphij u_char *buf; 520141261Sdelphij uint16_t sum, heads, sectors; 521205561Sdelphij int i; 522205561Sdelphij 523205561Sdelphij if (table->sectorsize != sizeof(struct vtoc8)) 524205561Sdelphij return (table); 525141261Sdelphij buf = malloc(table->sectorsize); 526141261Sdelphij if (buf == NULL) 527264911Sdelphij return (table); 528264911Sdelphij if (dread(dev, buf, 1, 0) != 0) { 529264911Sdelphij DEBUG("read failed"); 530141261Sdelphij ptable_close(table); 531141261Sdelphij table = NULL; 532141261Sdelphij goto out; 533141261Sdelphij } 534141261Sdelphij dl = (struct vtoc8 *)buf; 535141261Sdelphij /* Check the sum */ 536141261Sdelphij for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum)) 537141261Sdelphij sum ^= be16dec(buf + i); 538141261Sdelphij if (sum != 0) { 539141261Sdelphij DEBUG("incorrect checksum"); 540141261Sdelphij goto out; 541221793Sdelphij } 542221793Sdelphij if (be16toh(dl->nparts) != VTOC8_NPARTS) { 543141261Sdelphij DEBUG("invalid number of entries"); 544141261Sdelphij goto out; 545221793Sdelphij } 546141261Sdelphij sectors = be16toh(dl->nsecs); 547141261Sdelphij heads = be16toh(dl->nheads); 548141261Sdelphij if (sectors * heads == 0) { 549141261Sdelphij DEBUG("invalid geometry"); 550221793Sdelphij goto out; 551221793Sdelphij } 552221793Sdelphij for (i = 0; i < VTOC8_NPARTS; i++) { 553141261Sdelphij dl->part[i].tag = be16toh(dl->part[i].tag); 554141261Sdelphij if (i == VTOC_RAW_PART || 555141261Sdelphij dl->part[i].tag == VTOC_TAG_UNASSIGNED) 556141261Sdelphij continue; 557141261Sdelphij entry = malloc(sizeof(*entry)); 558141261Sdelphij if (entry == NULL) 559141261Sdelphij break; 560141261Sdelphij entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors; 561141261Sdelphij entry->part.end = be32toh(dl->map[i].nblks) + 562141261Sdelphij entry->part.start - 1; 563141261Sdelphij entry->part.type = vtoc8_parttype(dl->part[i].tag); 564221793Sdelphij entry->part.index = i; /* starts from zero */ 565221793Sdelphij entry->type.vtoc8 = dl->part[i].tag; 566141261Sdelphij STAILQ_INSERT_TAIL(&table->entries, entry, entry); 567141261Sdelphij DEBUG("new VTOC8 partition added"); 568141261Sdelphij } 569141261Sdelphij table->type = PTABLE_VTOC8; 570141261Sdelphijout: 571141261Sdelphij free(buf); 572141261Sdelphij return (table); 573221793Sdelphij 574221793Sdelphij} 575141261Sdelphij#endif /* LOADER_VTOC8_SUPPORT */ 576141261Sdelphij 577221793Sdelphijstruct ptable* 578141261Sdelphijptable_open(void *dev, off_t sectors, uint16_t sectorsize, 579141261Sdelphij diskread_t *dread) 580141261Sdelphij{ 581141261Sdelphij struct dos_partition *dp; 582221793Sdelphij struct ptable *table; 583221793Sdelphij u_char *buf; 584221793Sdelphij int i, count; 585221793Sdelphij#ifdef LOADER_MBR_SUPPORT 586221793Sdelphij struct pentry *entry; 587221793Sdelphij uint32_t start, end; 588221793Sdelphij int has_ext; 589264911Sdelphij#endif 590141261Sdelphij table = NULL; 591141261Sdelphij buf = malloc(sectorsize); 592141261Sdelphij if (buf == NULL) 593141261Sdelphij return (NULL); 594141261Sdelphij /* First, read the MBR. */ 595141261Sdelphij if (dread(dev, buf, 1, DOSBBSECTOR) != 0) { 596141261Sdelphij DEBUG("read failed"); 597141261Sdelphij goto out; 598141261Sdelphij } 599141261Sdelphij 600221793Sdelphij table = malloc(sizeof(*table)); 601141261Sdelphij if (table == NULL) 602141261Sdelphij goto out; 603141261Sdelphij table->sectors = sectors; 604221793Sdelphij table->sectorsize = sectorsize; 605141261Sdelphij table->type = PTABLE_NONE; 606221793Sdelphij STAILQ_INIT(&table->entries); 607221793Sdelphij 608221793Sdelphij#ifdef LOADER_VTOC8_SUPPORT 609221793Sdelphij if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) { 610221793Sdelphij if (ptable_vtoc8read(table, dev, dread) == NULL) { 611221793Sdelphij /* Read error. */ 612221793Sdelphij table = NULL; 613221793Sdelphij goto out; 614221793Sdelphij } else if (table->type == PTABLE_VTOC8) 615221793Sdelphij goto out; 616221793Sdelphij } 617221793Sdelphij#endif 618221793Sdelphij /* Check the BSD label. */ 619141261Sdelphij if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */ 620141261Sdelphij table = NULL; 621141261Sdelphij goto out; 622141261Sdelphij } else if (table->type == PTABLE_BSD) 623141261Sdelphij goto out; 624141261Sdelphij 625141261Sdelphij#if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT) 626141261Sdelphij /* Check the MBR magic. */ 627141261Sdelphij if (buf[DOSMAGICOFFSET] != 0x55 || 628141261Sdelphij buf[DOSMAGICOFFSET + 1] != 0xaa) { 629141261Sdelphij DEBUG("magic sequence not found"); 630141261Sdelphij goto out; 631141261Sdelphij } 632158798Sdelphij /* Check that we have PMBR. Also do some validation. */ 633141261Sdelphij dp = (struct dos_partition *)(buf + DOSPARTOFF); 634141261Sdelphij for (i = 0, count = 0; i < NDOSPART; i++) { 635186343Sdelphij if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) { 636141261Sdelphij DEBUG("invalid partition flag %x", dp[i].dp_flag); 637141261Sdelphij goto out; 638141261Sdelphij } 639141261Sdelphij#ifdef LOADER_GPT_SUPPORT 640141261Sdelphij if (dp[i].dp_typ == DOSPTYP_PMBR) { 641141261Sdelphij table->type = PTABLE_GPT; 642141261Sdelphij DEBUG("PMBR detected"); 643141261Sdelphij } 644141261Sdelphij#endif 645141394Sdelphij if (dp[i].dp_typ != 0) 646141394Sdelphij count++; 647141394Sdelphij } 648141394Sdelphij /* Do we have some invalid values? */ 649141394Sdelphij if (table->type == PTABLE_GPT && count > 1) { 650141394Sdelphij if (dp[1].dp_typ != DOSPTYP_HFS) { 651141261Sdelphij table->type = PTABLE_NONE; 652264911Sdelphij DEBUG("Incorrect PMBR, ignore it"); 653264911Sdelphij } else 654264911Sdelphij DEBUG("Bootcamp detected"); 655202640Sdelphij } 656141261Sdelphij#ifdef LOADER_GPT_SUPPORT 657141261Sdelphij if (table->type == PTABLE_GPT) { 658141261Sdelphij table = ptable_gptread(table, dev, dread); 659141261Sdelphij goto out; 660206689Sdelphij } 661206689Sdelphij#endif 662141261Sdelphij#ifdef LOADER_MBR_SUPPORT 663141261Sdelphij /* Read MBR. */ 664141261Sdelphij table->type = PTABLE_MBR; 665141261Sdelphij for (i = has_ext = 0; i < NDOSPART; i++) { 666141261Sdelphij if (dp[i].dp_typ == 0) 667141261Sdelphij continue; 668141261Sdelphij start = le32dec(&(dp[i].dp_start)); 669141261Sdelphij end = le32dec(&(dp[i].dp_size)); 670141261Sdelphij if (start == 0 || end == 0) 671141261Sdelphij continue; 672274263Sdelphij#if 0 /* Some BIOSes return an incorrect number of sectors */ 673141261Sdelphij if (start + end - 1 >= sectors) 674141261Sdelphij continue; /* XXX: ignore */ 675141261Sdelphij#endif 676283270Sdelphij if (dp[i].dp_typ == DOSPTYP_EXT || 677158798Sdelphij dp[i].dp_typ == DOSPTYP_EXTLBA) 678235037Sdelphij has_ext = 1; 679141261Sdelphij entry = malloc(sizeof(*entry)); 680141261Sdelphij if (entry == NULL) 681141261Sdelphij break; 682141261Sdelphij entry->part.start = start; 683141261Sdelphij entry->part.end = start + end - 1; 684141261Sdelphij entry->part.index = i + 1; 685141261Sdelphij entry->part.type = mbr_parttype(dp[i].dp_typ); 686141261Sdelphij entry->flags = dp[i].dp_flag; 687141261Sdelphij entry->type.mbr = dp[i].dp_typ; 688141261Sdelphij STAILQ_INSERT_TAIL(&table->entries, entry, entry); 689141261Sdelphij DEBUG("new MBR partition added"); 690141261Sdelphij } 691141261Sdelphij if (has_ext) { 692141261Sdelphij table = ptable_ebrread(table, dev, dread); 693235037Sdelphij /* FALLTHROUGH */ 694235037Sdelphij } 695235037Sdelphij#endif /* LOADER_MBR_SUPPORT */ 696235037Sdelphij#endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */ 697235037Sdelphijout: 698235037Sdelphij free(buf); 699235037Sdelphij return (table); 700235037Sdelphij} 701235037Sdelphij 702235037Sdelphijvoid 703235037Sdelphijptable_close(struct ptable *table) 704235037Sdelphij{ 705235037Sdelphij struct pentry *entry; 706235037Sdelphij 707235037Sdelphij while (!STAILQ_EMPTY(&table->entries)) { 708235037Sdelphij entry = STAILQ_FIRST(&table->entries); 709235037Sdelphij STAILQ_REMOVE_HEAD(&table->entries, entry); 710235037Sdelphij free(entry); 711235037Sdelphij } 712235037Sdelphij free(table); 713235037Sdelphij} 714235037Sdelphij 715235037Sdelphijenum ptable_type 716235037Sdelphijptable_gettype(const struct ptable *table) 717235037Sdelphij{ 718235037Sdelphij 719235037Sdelphij return (table->type); 720235037Sdelphij} 721235037Sdelphij 722235037Sdelphijint 723235037Sdelphijptable_getpart(const struct ptable *table, struct ptable_entry *part, int index) 724235037Sdelphij{ 725235037Sdelphij struct pentry *entry; 726235037Sdelphij 727235037Sdelphij if (part == NULL || table == NULL) 728235037Sdelphij return (EINVAL); 729235037Sdelphij 730141261Sdelphij STAILQ_FOREACH(entry, &table->entries, entry) { 731141261Sdelphij if (entry->part.index != index) 732141261Sdelphij continue; 733141261Sdelphij memcpy(part, &entry->part, sizeof(*part)); 734141261Sdelphij return (0); 735141261Sdelphij } 736141261Sdelphij return (ENOENT); 737141261Sdelphij} 738141261Sdelphij 739141261Sdelphij/* 740141261Sdelphij * Search for a slice with the following preferences: 741141261Sdelphij * 742141261Sdelphij * 1: Active FreeBSD slice 743141261Sdelphij * 2: Non-active FreeBSD slice 744141261Sdelphij * 3: Active Linux slice 745141261Sdelphij * 4: non-active Linux slice 746141261Sdelphij * 5: Active FAT/FAT32 slice 747141261Sdelphij * 6: non-active FAT/FAT32 slice 748141261Sdelphij */ 749141261Sdelphij#define PREF_RAWDISK 0 750141261Sdelphij#define PREF_FBSD_ACT 1 751141261Sdelphij#define PREF_FBSD 2 752141261Sdelphij#define PREF_LINUX_ACT 3 753141261Sdelphij#define PREF_LINUX 4 754141261Sdelphij#define PREF_DOS_ACT 5 755141261Sdelphij#define PREF_DOS 6 756141261Sdelphij#define PREF_NONE 7 757141261Sdelphijint 758158798Sdelphijptable_getbestpart(const struct ptable *table, struct ptable_entry *part) 759141261Sdelphij{ 760141261Sdelphij struct pentry *entry, *best; 761274263Sdelphij int pref, preflevel; 762264911Sdelphij 763264911Sdelphij if (part == NULL || table == NULL) 764202640Sdelphij return (EINVAL); 765141261Sdelphij 766141261Sdelphij best = NULL; 767141261Sdelphij preflevel = pref = PREF_NONE; 768141394Sdelphij STAILQ_FOREACH(entry, &table->entries, entry) { 769141394Sdelphij#ifdef LOADER_MBR_SUPPORT 770141394Sdelphij if (table->type == PTABLE_MBR) { 771141394Sdelphij switch (entry->type.mbr) { 772141394Sdelphij case DOSPTYP_386BSD: 773141394Sdelphij pref = entry->flags & 0x80 ? PREF_FBSD_ACT: 774186343Sdelphij PREF_FBSD; 775177837Sbms break; 776186343Sdelphij case DOSPTYP_LINUX: 777177837Sbms pref = entry->flags & 0x80 ? PREF_LINUX_ACT: 778177837Sbms PREF_LINUX; 779141261Sdelphij break; 780283270Sdelphij case 0x01: /* DOS/Windows */ 781283270Sdelphij case 0x04: 782141261Sdelphij case 0x06: 783141261Sdelphij case 0x0c: 784141261Sdelphij case 0x0e: 785141261Sdelphij case DOSPTYP_FAT32: 786141261Sdelphij pref = entry->flags & 0x80 ? PREF_DOS_ACT: 787141261Sdelphij PREF_DOS; 788141261Sdelphij break; 789141261Sdelphij default: 790141261Sdelphij pref = PREF_NONE; 791141261Sdelphij } 792141261Sdelphij } 793141261Sdelphij#endif /* LOADER_MBR_SUPPORT */ 794141261Sdelphij#ifdef LOADER_GPT_SUPPORT 795141261Sdelphij if (table->type == PTABLE_GPT) { 796141261Sdelphij if (entry->part.type == PART_DOS) 797141261Sdelphij pref = PREF_DOS; 798141261Sdelphij else if (entry->part.type == PART_FREEBSD_UFS || 799141261Sdelphij entry->part.type == PART_FREEBSD_ZFS) 800141261Sdelphij pref = PREF_FBSD; 801141261Sdelphij else 802141261Sdelphij pref = PREF_NONE; 803141261Sdelphij } 804141261Sdelphij#endif /* LOADER_GPT_SUPPORT */ 805283270Sdelphij if (pref < preflevel) { 806141261Sdelphij preflevel = pref; 807283270Sdelphij best = entry; 808283270Sdelphij } 809283270Sdelphij } 810283270Sdelphij if (best != NULL) { 811283270Sdelphij memcpy(part, &best->part, sizeof(*part)); 812283270Sdelphij return (0); 813283270Sdelphij } 814283270Sdelphij return (ENOENT); 815283270Sdelphij} 816141261Sdelphij 817283270Sdelphijvoid 818283270Sdelphijptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter) 819283270Sdelphij{ 820158798Sdelphij struct pentry *entry; 821283270Sdelphij char name[32]; 822283270Sdelphij 823283270Sdelphij name[0] = '\0'; 824141261Sdelphij STAILQ_FOREACH(entry, &table->entries, entry) { 825283270Sdelphij#ifdef LOADER_MBR_SUPPORT 826283270Sdelphij if (table->type == PTABLE_MBR) 827283270Sdelphij sprintf(name, "s%d", entry->part.index); 828141261Sdelphij else 829283270Sdelphij#endif 830283270Sdelphij#ifdef LOADER_GPT_SUPPORT 831283270Sdelphij if (table->type == PTABLE_GPT) 832283270Sdelphij sprintf(name, "p%d", entry->part.index); 833283270Sdelphij else 834283270Sdelphij#endif 835283270Sdelphij#ifdef LOADER_VTOC8_SUPPORT 836283270Sdelphij if (table->type == PTABLE_VTOC8) 837283270Sdelphij sprintf(name, "%c", (u_char) 'a' + 838283270Sdelphij entry->part.index); 839283270Sdelphij else 840283270Sdelphij#endif 841283270Sdelphij if (table->type == PTABLE_BSD) 842283270Sdelphij sprintf(name, "%c", (u_char) 'a' + 843283270Sdelphij entry->part.index); 844283270Sdelphij iter(arg, name, &entry->part); 845283270Sdelphij } 846283270Sdelphij} 847283270Sdelphij 848283270Sdelphij