1ca987d46SWarner Losh /*- 2ca987d46SWarner Losh * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org> 3ca987d46SWarner Losh * All rights reserved. 4ca987d46SWarner Losh * 5ca987d46SWarner Losh * Redistribution and use in source and binary forms, with or without 6ca987d46SWarner Losh * modification, are permitted provided that the following conditions 7ca987d46SWarner Losh * are met: 8ca987d46SWarner Losh * 1. Redistributions of source code must retain the above copyright 9ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer. 10ca987d46SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 11ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer in the 12ca987d46SWarner Losh * documentation and/or other materials provided with the distribution. 13ca987d46SWarner Losh * 14ca987d46SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15ca987d46SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16ca987d46SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ca987d46SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18ca987d46SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19ca987d46SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20ca987d46SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21ca987d46SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22ca987d46SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ca987d46SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ca987d46SWarner Losh * SUCH DAMAGE. 25ca987d46SWarner Losh */ 26ca987d46SWarner Losh 27ca987d46SWarner Losh #include <sys/cdefs.h> 28ca987d46SWarner Losh __FBSDID("$FreeBSD$"); 29ca987d46SWarner Losh 30ca987d46SWarner Losh #include <stand.h> 31ca987d46SWarner Losh #include <sys/param.h> 32ca987d46SWarner Losh #include <sys/diskmbr.h> 33ca987d46SWarner Losh #include <sys/disklabel.h> 34ca987d46SWarner Losh #include <sys/endian.h> 35ca987d46SWarner Losh #include <sys/gpt.h> 36ca987d46SWarner Losh #include <sys/stddef.h> 37ca987d46SWarner Losh #include <sys/queue.h> 38ca987d46SWarner Losh #include <sys/vtoc.h> 39ca987d46SWarner Losh 4048990fceSBenno Rice #include <fs/cd9660/iso.h> 4148990fceSBenno Rice 42ca987d46SWarner Losh #include <crc32.h> 43ca987d46SWarner Losh #include <part.h> 44ca987d46SWarner Losh #include <uuid.h> 45ca987d46SWarner Losh 46ca987d46SWarner Losh #ifdef PART_DEBUG 477325df02SKyle Evans #define DPRINTF(fmt, args...) printf("%s: " fmt "\n", __func__, ## args) 48ca987d46SWarner Losh #else 4982c29d4fSToomas Soome #define DPRINTF(fmt, args...) ((void)0) 50ca987d46SWarner Losh #endif 51ca987d46SWarner Losh 52ca987d46SWarner Losh #ifdef LOADER_GPT_SUPPORT 53ca987d46SWarner Losh #define MAXTBLSZ 64 54ca987d46SWarner Losh static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED; 55ca987d46SWarner Losh static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA; 56ca987d46SWarner Losh static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS; 57ca987d46SWarner Losh static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI; 58ca987d46SWarner Losh static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD; 59ca987d46SWarner Losh static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT; 60ca987d46SWarner Losh static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP; 61ca987d46SWarner Losh static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS; 62ca987d46SWarner Losh static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM; 63*124003d5SToomas Soome static const uuid_t gpt_uuid_apple_apfs = GPT_ENT_TYPE_APPLE_APFS; 64ca987d46SWarner Losh #endif 65ca987d46SWarner Losh 66ca987d46SWarner Losh struct pentry { 67ca987d46SWarner Losh struct ptable_entry part; 68ca987d46SWarner Losh uint64_t flags; 69ca987d46SWarner Losh union { 70ca987d46SWarner Losh uint8_t bsd; 71ca987d46SWarner Losh uint8_t mbr; 72ca987d46SWarner Losh uuid_t gpt; 73ca987d46SWarner Losh uint16_t vtoc8; 74ca987d46SWarner Losh } type; 75ca987d46SWarner Losh STAILQ_ENTRY(pentry) entry; 76ca987d46SWarner Losh }; 77ca987d46SWarner Losh 78ca987d46SWarner Losh struct ptable { 79ca987d46SWarner Losh enum ptable_type type; 80ca987d46SWarner Losh uint16_t sectorsize; 81ca987d46SWarner Losh uint64_t sectors; 82ca987d46SWarner Losh 83ca987d46SWarner Losh STAILQ_HEAD(, pentry) entries; 84ca987d46SWarner Losh }; 85ca987d46SWarner Losh 86ca987d46SWarner Losh static struct parttypes { 87ca987d46SWarner Losh enum partition_type type; 88ca987d46SWarner Losh const char *desc; 89ca987d46SWarner Losh } ptypes[] = { 90ca987d46SWarner Losh { PART_UNKNOWN, "Unknown" }, 91ca987d46SWarner Losh { PART_EFI, "EFI" }, 92ca987d46SWarner Losh { PART_FREEBSD, "FreeBSD" }, 93ca987d46SWarner Losh { PART_FREEBSD_BOOT, "FreeBSD boot" }, 94ca987d46SWarner Losh { PART_FREEBSD_UFS, "FreeBSD UFS" }, 95ca987d46SWarner Losh { PART_FREEBSD_ZFS, "FreeBSD ZFS" }, 96ca987d46SWarner Losh { PART_FREEBSD_SWAP, "FreeBSD swap" }, 97ca987d46SWarner Losh { PART_FREEBSD_VINUM, "FreeBSD vinum" }, 98ca987d46SWarner Losh { PART_LINUX, "Linux" }, 99ca987d46SWarner Losh { PART_LINUX_SWAP, "Linux swap" }, 100ca987d46SWarner Losh { PART_DOS, "DOS/Windows" }, 10148990fceSBenno Rice { PART_ISO9660, "ISO9660" }, 102*124003d5SToomas Soome { PART_APFS, "APFS" }, 103ca987d46SWarner Losh }; 104ca987d46SWarner Losh 105ca987d46SWarner Losh const char * 106ca987d46SWarner Losh parttype2str(enum partition_type type) 107ca987d46SWarner Losh { 108ca987d46SWarner Losh size_t i; 109ca987d46SWarner Losh 110ca987d46SWarner Losh for (i = 0; i < nitems(ptypes); i++) 111ca987d46SWarner Losh if (ptypes[i].type == type) 112ca987d46SWarner Losh return (ptypes[i].desc); 113ca987d46SWarner Losh return (ptypes[0].desc); 114ca987d46SWarner Losh } 115ca987d46SWarner Losh 116ca987d46SWarner Losh #ifdef LOADER_GPT_SUPPORT 117ca987d46SWarner Losh static void 118ca987d46SWarner Losh uuid_letoh(uuid_t *uuid) 119ca987d46SWarner Losh { 120ca987d46SWarner Losh 121ca987d46SWarner Losh uuid->time_low = le32toh(uuid->time_low); 122ca987d46SWarner Losh uuid->time_mid = le16toh(uuid->time_mid); 123ca987d46SWarner Losh uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version); 124ca987d46SWarner Losh } 125ca987d46SWarner Losh 126ca987d46SWarner Losh static enum partition_type 127ca987d46SWarner Losh gpt_parttype(uuid_t type) 128ca987d46SWarner Losh { 129ca987d46SWarner Losh 130ca987d46SWarner Losh if (uuid_equal(&type, &gpt_uuid_efi, NULL)) 131ca987d46SWarner Losh return (PART_EFI); 132ca987d46SWarner Losh else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL)) 133ca987d46SWarner Losh return (PART_DOS); 134ca987d46SWarner Losh else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL)) 135ca987d46SWarner Losh return (PART_FREEBSD_BOOT); 136ca987d46SWarner Losh else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL)) 137ca987d46SWarner Losh return (PART_FREEBSD_UFS); 138ca987d46SWarner Losh else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL)) 139ca987d46SWarner Losh return (PART_FREEBSD_ZFS); 140ca987d46SWarner Losh else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL)) 141ca987d46SWarner Losh return (PART_FREEBSD_SWAP); 142ca987d46SWarner Losh else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL)) 143ca987d46SWarner Losh return (PART_FREEBSD_VINUM); 144ca987d46SWarner Losh else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL)) 145ca987d46SWarner Losh return (PART_FREEBSD); 146*124003d5SToomas Soome else if (uuid_equal(&type, &gpt_uuid_apple_apfs, NULL)) 147*124003d5SToomas Soome return (PART_APFS); 148ca987d46SWarner Losh return (PART_UNKNOWN); 149ca987d46SWarner Losh } 150ca987d46SWarner Losh 151ca987d46SWarner Losh static struct gpt_hdr * 152ca987d46SWarner Losh gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last, 153ca987d46SWarner Losh uint16_t sectorsize) 154ca987d46SWarner Losh { 155ca987d46SWarner Losh uint32_t sz, crc; 156ca987d46SWarner Losh 157ca987d46SWarner Losh if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) { 1587325df02SKyle Evans DPRINTF("no GPT signature"); 159ca987d46SWarner Losh return (NULL); 160ca987d46SWarner Losh } 161ca987d46SWarner Losh sz = le32toh(hdr->hdr_size); 162ca987d46SWarner Losh if (sz < 92 || sz > sectorsize) { 1637325df02SKyle Evans DPRINTF("invalid GPT header size: %d", sz); 164ca987d46SWarner Losh return (NULL); 165ca987d46SWarner Losh } 166ca987d46SWarner Losh crc = le32toh(hdr->hdr_crc_self); 167ca987d46SWarner Losh hdr->hdr_crc_self = 0; 168ca987d46SWarner Losh if (crc32(hdr, sz) != crc) { 1697325df02SKyle Evans DPRINTF("GPT header's CRC doesn't match"); 170ca987d46SWarner Losh return (NULL); 171ca987d46SWarner Losh } 172ca987d46SWarner Losh hdr->hdr_crc_self = crc; 173ca987d46SWarner Losh hdr->hdr_revision = le32toh(hdr->hdr_revision); 174ca987d46SWarner Losh if (hdr->hdr_revision < GPT_HDR_REVISION) { 1757325df02SKyle Evans DPRINTF("unsupported GPT revision %d", hdr->hdr_revision); 176ca987d46SWarner Losh return (NULL); 177ca987d46SWarner Losh } 178ca987d46SWarner Losh hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self); 179ca987d46SWarner Losh if (hdr->hdr_lba_self != lba_self) { 1807325df02SKyle Evans DPRINTF("self LBA doesn't match"); 181ca987d46SWarner Losh return (NULL); 182ca987d46SWarner Losh } 183ca987d46SWarner Losh hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt); 184ca987d46SWarner Losh if (hdr->hdr_lba_alt == hdr->hdr_lba_self) { 1857325df02SKyle Evans DPRINTF("invalid alternate LBA"); 186ca987d46SWarner Losh return (NULL); 187ca987d46SWarner Losh } 188ca987d46SWarner Losh hdr->hdr_entries = le32toh(hdr->hdr_entries); 189ca987d46SWarner Losh hdr->hdr_entsz = le32toh(hdr->hdr_entsz); 190ca987d46SWarner Losh if (hdr->hdr_entries == 0 || 191ca987d46SWarner Losh hdr->hdr_entsz < sizeof(struct gpt_ent) || 192ca987d46SWarner Losh sectorsize % hdr->hdr_entsz != 0) { 1937325df02SKyle Evans DPRINTF("invalid entry size or number of entries"); 194ca987d46SWarner Losh return (NULL); 195ca987d46SWarner Losh } 196ca987d46SWarner Losh hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start); 197ca987d46SWarner Losh hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end); 198ca987d46SWarner Losh hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table); 199ca987d46SWarner Losh hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table); 200ca987d46SWarner Losh uuid_letoh(&hdr->hdr_uuid); 201ca987d46SWarner Losh return (hdr); 202ca987d46SWarner Losh } 203ca987d46SWarner Losh 204ca987d46SWarner Losh static int 205ca987d46SWarner Losh gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, size_t size, 206ca987d46SWarner Losh uint64_t lba_last) 207ca987d46SWarner Losh { 208ca987d46SWarner Losh struct gpt_ent *ent; 209ca987d46SWarner Losh uint32_t i, cnt; 210ca987d46SWarner Losh 211ca987d46SWarner Losh cnt = size / hdr->hdr_entsz; 212ca987d46SWarner Losh if (hdr->hdr_entries <= cnt) { 213ca987d46SWarner Losh cnt = hdr->hdr_entries; 214ca987d46SWarner Losh /* Check CRC only when buffer size is enough for table. */ 215ca987d46SWarner Losh if (hdr->hdr_crc_table != 216ca987d46SWarner Losh crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) { 2177325df02SKyle Evans DPRINTF("GPT table's CRC doesn't match"); 218ca987d46SWarner Losh return (-1); 219ca987d46SWarner Losh } 220ca987d46SWarner Losh } 221ca987d46SWarner Losh for (i = 0; i < cnt; i++) { 222ca987d46SWarner Losh ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz); 223ca987d46SWarner Losh uuid_letoh(&ent->ent_type); 224ca987d46SWarner Losh if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) 225ca987d46SWarner Losh continue; 226ca987d46SWarner Losh ent->ent_lba_start = le64toh(ent->ent_lba_start); 227ca987d46SWarner Losh ent->ent_lba_end = le64toh(ent->ent_lba_end); 228ca987d46SWarner Losh } 229ca987d46SWarner Losh return (0); 230ca987d46SWarner Losh } 231ca987d46SWarner Losh 232ca987d46SWarner Losh static struct ptable * 233ca987d46SWarner Losh ptable_gptread(struct ptable *table, void *dev, diskread_t dread) 234ca987d46SWarner Losh { 235ca987d46SWarner Losh struct pentry *entry; 236ca987d46SWarner Losh struct gpt_hdr *phdr, hdr; 237ca987d46SWarner Losh struct gpt_ent *ent; 238ca987d46SWarner Losh uint8_t *buf, *tbl; 239ca987d46SWarner Losh uint64_t offset; 240ca987d46SWarner Losh int pri, sec; 241ca987d46SWarner Losh size_t size, i; 242ca987d46SWarner Losh 243ca987d46SWarner Losh buf = malloc(table->sectorsize); 244ca987d46SWarner Losh if (buf == NULL) 245ca987d46SWarner Losh return (NULL); 246ca987d46SWarner Losh tbl = malloc(table->sectorsize * MAXTBLSZ); 247ca987d46SWarner Losh if (tbl == NULL) { 248ca987d46SWarner Losh free(buf); 249ca987d46SWarner Losh return (NULL); 250ca987d46SWarner Losh } 251ca987d46SWarner Losh /* Read the primary GPT header. */ 252ca987d46SWarner Losh if (dread(dev, buf, 1, 1) != 0) { 253ca987d46SWarner Losh ptable_close(table); 254ca987d46SWarner Losh table = NULL; 255ca987d46SWarner Losh goto out; 256ca987d46SWarner Losh } 257ca987d46SWarner Losh pri = sec = 0; 258ca987d46SWarner Losh /* Check the primary GPT header. */ 259ca987d46SWarner Losh phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1, 260ca987d46SWarner Losh table->sectorsize); 261ca987d46SWarner Losh if (phdr != NULL) { 262ca987d46SWarner Losh /* Read the primary GPT table. */ 263ca987d46SWarner Losh size = MIN(MAXTBLSZ, 264ca987d46SWarner Losh howmany(phdr->hdr_entries * phdr->hdr_entsz, 265ca987d46SWarner Losh table->sectorsize)); 266ca987d46SWarner Losh if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && 267ca987d46SWarner Losh gpt_checktbl(phdr, tbl, size * table->sectorsize, 268ca987d46SWarner Losh table->sectors - 1) == 0) { 269ca987d46SWarner Losh memcpy(&hdr, phdr, sizeof(hdr)); 270ca987d46SWarner Losh pri = 1; 271ca987d46SWarner Losh } 272ca987d46SWarner Losh } 273ca987d46SWarner Losh offset = pri ? hdr.hdr_lba_alt: table->sectors - 1; 274ca987d46SWarner Losh /* Read the backup GPT header. */ 275ca987d46SWarner Losh if (dread(dev, buf, 1, offset) != 0) 276ca987d46SWarner Losh phdr = NULL; 277ca987d46SWarner Losh else 278ca987d46SWarner Losh phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset, 279ca987d46SWarner Losh table->sectors - 1, table->sectorsize); 280ca987d46SWarner Losh if (phdr != NULL) { 281ca987d46SWarner Losh /* 282ca987d46SWarner Losh * Compare primary and backup headers. 283ca987d46SWarner Losh * If they are equal, then we do not need to read backup 284ca987d46SWarner Losh * table. If they are different, then prefer backup header 285ca987d46SWarner Losh * and try to read backup table. 286ca987d46SWarner Losh */ 287ca987d46SWarner Losh if (pri == 0 || 288ca987d46SWarner Losh uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 || 289ca987d46SWarner Losh hdr.hdr_revision != phdr->hdr_revision || 290ca987d46SWarner Losh hdr.hdr_size != phdr->hdr_size || 291ca987d46SWarner Losh hdr.hdr_lba_start != phdr->hdr_lba_start || 292ca987d46SWarner Losh hdr.hdr_lba_end != phdr->hdr_lba_end || 293ca987d46SWarner Losh hdr.hdr_entries != phdr->hdr_entries || 294ca987d46SWarner Losh hdr.hdr_entsz != phdr->hdr_entsz || 295ca987d46SWarner Losh hdr.hdr_crc_table != phdr->hdr_crc_table) { 296ca987d46SWarner Losh /* Read the backup GPT table. */ 297ca987d46SWarner Losh size = MIN(MAXTBLSZ, 298ca987d46SWarner Losh howmany(phdr->hdr_entries * phdr->hdr_entsz, 299ca987d46SWarner Losh table->sectorsize)); 300ca987d46SWarner Losh if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 && 301ca987d46SWarner Losh gpt_checktbl(phdr, tbl, size * table->sectorsize, 302ca987d46SWarner Losh table->sectors - 1) == 0) { 303ca987d46SWarner Losh memcpy(&hdr, phdr, sizeof(hdr)); 304ca987d46SWarner Losh sec = 1; 305ca987d46SWarner Losh } 306ca987d46SWarner Losh } 307ca987d46SWarner Losh } 308ca987d46SWarner Losh if (pri == 0 && sec == 0) { 309ca987d46SWarner Losh /* Both primary and backup tables are invalid. */ 310ca987d46SWarner Losh table->type = PTABLE_NONE; 311ca987d46SWarner Losh goto out; 312ca987d46SWarner Losh } 3137325df02SKyle Evans DPRINTF("GPT detected"); 314ca987d46SWarner Losh size = MIN(hdr.hdr_entries * hdr.hdr_entsz, 315ca987d46SWarner Losh MAXTBLSZ * table->sectorsize); 316ca987d46SWarner Losh 317ca987d46SWarner Losh /* 318ca987d46SWarner Losh * If the disk's sector count is smaller than the sector count recorded 319ca987d46SWarner Losh * in the disk's GPT table header, set the table->sectors to the value 320ca987d46SWarner Losh * recorded in GPT tables. This is done to work around buggy firmware 321ca987d46SWarner Losh * that returns truncated disk sizes. 322ca987d46SWarner Losh * 323ca987d46SWarner Losh * Note, this is still not a foolproof way to get disk's size. For 324ca987d46SWarner Losh * example, an image file can be truncated when copied to smaller media. 325ca987d46SWarner Losh */ 326ca987d46SWarner Losh table->sectors = hdr.hdr_lba_alt + 1; 327ca987d46SWarner Losh 328ca987d46SWarner Losh for (i = 0; i < size / hdr.hdr_entsz; i++) { 329ca987d46SWarner Losh ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz); 330ca987d46SWarner Losh if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL)) 331ca987d46SWarner Losh continue; 332ca987d46SWarner Losh 333ca987d46SWarner Losh /* Simple sanity checks. */ 334ca987d46SWarner Losh if (ent->ent_lba_start < hdr.hdr_lba_start || 335ca987d46SWarner Losh ent->ent_lba_end > hdr.hdr_lba_end || 336ca987d46SWarner Losh ent->ent_lba_start > ent->ent_lba_end) 337ca987d46SWarner Losh continue; 338ca987d46SWarner Losh 339ca987d46SWarner Losh entry = malloc(sizeof(*entry)); 340ca987d46SWarner Losh if (entry == NULL) 341ca987d46SWarner Losh break; 342ca987d46SWarner Losh entry->part.start = ent->ent_lba_start; 343ca987d46SWarner Losh entry->part.end = ent->ent_lba_end; 344ca987d46SWarner Losh entry->part.index = i + 1; 345ca987d46SWarner Losh entry->part.type = gpt_parttype(ent->ent_type); 346ca987d46SWarner Losh entry->flags = le64toh(ent->ent_attr); 347ca987d46SWarner Losh memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t)); 348ca987d46SWarner Losh STAILQ_INSERT_TAIL(&table->entries, entry, entry); 3497325df02SKyle Evans DPRINTF("new GPT partition added"); 350ca987d46SWarner Losh } 351ca987d46SWarner Losh out: 352ca987d46SWarner Losh free(buf); 353ca987d46SWarner Losh free(tbl); 354ca987d46SWarner Losh return (table); 355ca987d46SWarner Losh } 356ca987d46SWarner Losh #endif /* LOADER_GPT_SUPPORT */ 357ca987d46SWarner Losh 358ca987d46SWarner Losh #ifdef LOADER_MBR_SUPPORT 359ca987d46SWarner Losh /* We do not need to support too many EBR partitions in the loader */ 360ca987d46SWarner Losh #define MAXEBRENTRIES 8 361ca987d46SWarner Losh static enum partition_type 362ca987d46SWarner Losh mbr_parttype(uint8_t type) 363ca987d46SWarner Losh { 364ca987d46SWarner Losh 365ca987d46SWarner Losh switch (type) { 366ca987d46SWarner Losh case DOSPTYP_386BSD: 367ca987d46SWarner Losh return (PART_FREEBSD); 368ca987d46SWarner Losh case DOSPTYP_LINSWP: 369ca987d46SWarner Losh return (PART_LINUX_SWAP); 370ca987d46SWarner Losh case DOSPTYP_LINUX: 371ca987d46SWarner Losh return (PART_LINUX); 372ca987d46SWarner Losh case 0x01: 373ca987d46SWarner Losh case 0x04: 374ca987d46SWarner Losh case 0x06: 375ca987d46SWarner Losh case 0x07: 376ca987d46SWarner Losh case 0x0b: 377ca987d46SWarner Losh case 0x0c: 378ca987d46SWarner Losh case 0x0e: 379ca987d46SWarner Losh return (PART_DOS); 380ca987d46SWarner Losh } 381ca987d46SWarner Losh return (PART_UNKNOWN); 382ca987d46SWarner Losh } 383ca987d46SWarner Losh 384ca987d46SWarner Losh static struct ptable * 385ca987d46SWarner Losh ptable_ebrread(struct ptable *table, void *dev, diskread_t dread) 386ca987d46SWarner Losh { 387ca987d46SWarner Losh struct dos_partition *dp; 388ca987d46SWarner Losh struct pentry *e1, *entry; 389ca987d46SWarner Losh uint32_t start, end, offset; 390ca987d46SWarner Losh u_char *buf; 391ca987d46SWarner Losh int i, index; 392ca987d46SWarner Losh 393ca987d46SWarner Losh STAILQ_FOREACH(e1, &table->entries, entry) { 394ca987d46SWarner Losh if (e1->type.mbr == DOSPTYP_EXT || 395ca987d46SWarner Losh e1->type.mbr == DOSPTYP_EXTLBA) 396ca987d46SWarner Losh break; 397ca987d46SWarner Losh } 398ca987d46SWarner Losh if (e1 == NULL) 399ca987d46SWarner Losh return (table); 400ca987d46SWarner Losh index = 5; 401ca987d46SWarner Losh offset = e1->part.start; 402ca987d46SWarner Losh buf = malloc(table->sectorsize); 403ca987d46SWarner Losh if (buf == NULL) 404ca987d46SWarner Losh return (table); 4057325df02SKyle Evans DPRINTF("EBR detected"); 406ca987d46SWarner Losh for (i = 0; i < MAXEBRENTRIES; i++) { 407ca987d46SWarner Losh #if 0 /* Some BIOSes return an incorrect number of sectors */ 408ca987d46SWarner Losh if (offset >= table->sectors) 409ca987d46SWarner Losh break; 410ca987d46SWarner Losh #endif 411ca987d46SWarner Losh if (dread(dev, buf, 1, offset) != 0) 412ca987d46SWarner Losh break; 413ca987d46SWarner Losh dp = (struct dos_partition *)(buf + DOSPARTOFF); 414ca987d46SWarner Losh if (dp[0].dp_typ == 0) 415ca987d46SWarner Losh break; 416ca987d46SWarner Losh start = le32toh(dp[0].dp_start); 417ca987d46SWarner Losh if (dp[0].dp_typ == DOSPTYP_EXT && 418ca987d46SWarner Losh dp[1].dp_typ == 0) { 419ca987d46SWarner Losh offset = e1->part.start + start; 420ca987d46SWarner Losh continue; 421ca987d46SWarner Losh } 422ca987d46SWarner Losh end = le32toh(dp[0].dp_size); 423ca987d46SWarner Losh entry = malloc(sizeof(*entry)); 424ca987d46SWarner Losh if (entry == NULL) 425ca987d46SWarner Losh break; 426ca987d46SWarner Losh entry->part.start = offset + start; 427ca987d46SWarner Losh entry->part.end = entry->part.start + end - 1; 428ca987d46SWarner Losh entry->part.index = index++; 429ca987d46SWarner Losh entry->part.type = mbr_parttype(dp[0].dp_typ); 430ca987d46SWarner Losh entry->flags = dp[0].dp_flag; 431ca987d46SWarner Losh entry->type.mbr = dp[0].dp_typ; 432ca987d46SWarner Losh STAILQ_INSERT_TAIL(&table->entries, entry, entry); 4337325df02SKyle Evans DPRINTF("new EBR partition added"); 434ca987d46SWarner Losh if (dp[1].dp_typ == 0) 435ca987d46SWarner Losh break; 436ca987d46SWarner Losh offset = e1->part.start + le32toh(dp[1].dp_start); 437ca987d46SWarner Losh } 438ca987d46SWarner Losh free(buf); 439ca987d46SWarner Losh return (table); 440ca987d46SWarner Losh } 441ca987d46SWarner Losh #endif /* LOADER_MBR_SUPPORT */ 442ca987d46SWarner Losh 443ca987d46SWarner Losh static enum partition_type 444ca987d46SWarner Losh bsd_parttype(uint8_t type) 445ca987d46SWarner Losh { 446ca987d46SWarner Losh 447ca987d46SWarner Losh switch (type) { 448ca987d46SWarner Losh case FS_SWAP: 449ca987d46SWarner Losh return (PART_FREEBSD_SWAP); 450ca987d46SWarner Losh case FS_BSDFFS: 451ca987d46SWarner Losh return (PART_FREEBSD_UFS); 452ca987d46SWarner Losh case FS_VINUM: 453ca987d46SWarner Losh return (PART_FREEBSD_VINUM); 454ca987d46SWarner Losh case FS_ZFS: 455ca987d46SWarner Losh return (PART_FREEBSD_ZFS); 456ca987d46SWarner Losh } 457ca987d46SWarner Losh return (PART_UNKNOWN); 458ca987d46SWarner Losh } 459ca987d46SWarner Losh 460ca987d46SWarner Losh static struct ptable * 461ca987d46SWarner Losh ptable_bsdread(struct ptable *table, void *dev, diskread_t dread) 462ca987d46SWarner Losh { 463ca987d46SWarner Losh struct disklabel *dl; 464ca987d46SWarner Losh struct partition *part; 465ca987d46SWarner Losh struct pentry *entry; 466ca987d46SWarner Losh uint8_t *buf; 467ca987d46SWarner Losh uint32_t raw_offset; 468ca987d46SWarner Losh int i; 469ca987d46SWarner Losh 470ca987d46SWarner Losh if (table->sectorsize < sizeof(struct disklabel)) { 4717325df02SKyle Evans DPRINTF("Too small sectorsize"); 472ca987d46SWarner Losh return (table); 473ca987d46SWarner Losh } 474ca987d46SWarner Losh buf = malloc(table->sectorsize); 475ca987d46SWarner Losh if (buf == NULL) 476ca987d46SWarner Losh return (table); 477ca987d46SWarner Losh if (dread(dev, buf, 1, 1) != 0) { 4787325df02SKyle Evans DPRINTF("read failed"); 479ca987d46SWarner Losh ptable_close(table); 480ca987d46SWarner Losh table = NULL; 481ca987d46SWarner Losh goto out; 482ca987d46SWarner Losh } 483ca987d46SWarner Losh dl = (struct disklabel *)buf; 484ca987d46SWarner Losh if (le32toh(dl->d_magic) != DISKMAGIC && 485ca987d46SWarner Losh le32toh(dl->d_magic2) != DISKMAGIC) 486ca987d46SWarner Losh goto out; 487ca987d46SWarner Losh if (le32toh(dl->d_secsize) != table->sectorsize) { 4887325df02SKyle Evans DPRINTF("unsupported sector size"); 489ca987d46SWarner Losh goto out; 490ca987d46SWarner Losh } 491ca987d46SWarner Losh dl->d_npartitions = le16toh(dl->d_npartitions); 492ca987d46SWarner Losh if (dl->d_npartitions > 20 || dl->d_npartitions < 8) { 4937325df02SKyle Evans DPRINTF("invalid number of partitions"); 494ca987d46SWarner Losh goto out; 495ca987d46SWarner Losh } 4967325df02SKyle Evans DPRINTF("BSD detected"); 497ca987d46SWarner Losh part = &dl->d_partitions[0]; 498ca987d46SWarner Losh raw_offset = le32toh(part[RAW_PART].p_offset); 499ca987d46SWarner Losh for (i = 0; i < dl->d_npartitions; i++, part++) { 500ca987d46SWarner Losh if (i == RAW_PART) 501ca987d46SWarner Losh continue; 502ca987d46SWarner Losh if (part->p_size == 0) 503ca987d46SWarner Losh continue; 504ca987d46SWarner Losh entry = malloc(sizeof(*entry)); 505ca987d46SWarner Losh if (entry == NULL) 506ca987d46SWarner Losh break; 507ca987d46SWarner Losh entry->part.start = le32toh(part->p_offset) - raw_offset; 508ca987d46SWarner Losh entry->part.end = entry->part.start + 509ca987d46SWarner Losh le32toh(part->p_size) - 1; 510ca987d46SWarner Losh entry->part.type = bsd_parttype(part->p_fstype); 511ca987d46SWarner Losh entry->part.index = i; /* starts from zero */ 512ca987d46SWarner Losh entry->type.bsd = part->p_fstype; 513ca987d46SWarner Losh STAILQ_INSERT_TAIL(&table->entries, entry, entry); 5147325df02SKyle Evans DPRINTF("new BSD partition added"); 515ca987d46SWarner Losh } 516ca987d46SWarner Losh table->type = PTABLE_BSD; 517ca987d46SWarner Losh out: 518ca987d46SWarner Losh free(buf); 519ca987d46SWarner Losh return (table); 520ca987d46SWarner Losh } 521ca987d46SWarner Losh 522ca987d46SWarner Losh #ifdef LOADER_VTOC8_SUPPORT 523ca987d46SWarner Losh static enum partition_type 524ca987d46SWarner Losh vtoc8_parttype(uint16_t type) 525ca987d46SWarner Losh { 526ca987d46SWarner Losh 527ca987d46SWarner Losh switch (type) { 528ca987d46SWarner Losh case VTOC_TAG_FREEBSD_SWAP: 529ca987d46SWarner Losh return (PART_FREEBSD_SWAP); 530ca987d46SWarner Losh case VTOC_TAG_FREEBSD_UFS: 531ca987d46SWarner Losh return (PART_FREEBSD_UFS); 532ca987d46SWarner Losh case VTOC_TAG_FREEBSD_VINUM: 533ca987d46SWarner Losh return (PART_FREEBSD_VINUM); 534ca987d46SWarner Losh case VTOC_TAG_FREEBSD_ZFS: 535ca987d46SWarner Losh return (PART_FREEBSD_ZFS); 536ca987d46SWarner Losh } 537ca987d46SWarner Losh return (PART_UNKNOWN); 538ca987d46SWarner Losh } 539ca987d46SWarner Losh 540ca987d46SWarner Losh static struct ptable * 541ca987d46SWarner Losh ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread) 542ca987d46SWarner Losh { 543ca987d46SWarner Losh struct pentry *entry; 544ca987d46SWarner Losh struct vtoc8 *dl; 545ca987d46SWarner Losh uint8_t *buf; 546ca987d46SWarner Losh uint16_t sum, heads, sectors; 547ca987d46SWarner Losh int i; 548ca987d46SWarner Losh 549ca987d46SWarner Losh if (table->sectorsize != sizeof(struct vtoc8)) 550ca987d46SWarner Losh return (table); 551ca987d46SWarner Losh buf = malloc(table->sectorsize); 552ca987d46SWarner Losh if (buf == NULL) 553ca987d46SWarner Losh return (table); 554ca987d46SWarner Losh if (dread(dev, buf, 1, 0) != 0) { 5557325df02SKyle Evans DPRINTF("read failed"); 556ca987d46SWarner Losh ptable_close(table); 557ca987d46SWarner Losh table = NULL; 558ca987d46SWarner Losh goto out; 559ca987d46SWarner Losh } 560ca987d46SWarner Losh dl = (struct vtoc8 *)buf; 561ca987d46SWarner Losh /* Check the sum */ 562ca987d46SWarner Losh for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum)) 563ca987d46SWarner Losh sum ^= be16dec(buf + i); 564ca987d46SWarner Losh if (sum != 0) { 5657325df02SKyle Evans DPRINTF("incorrect checksum"); 566ca987d46SWarner Losh goto out; 567ca987d46SWarner Losh } 568ca987d46SWarner Losh if (be16toh(dl->nparts) != VTOC8_NPARTS) { 5697325df02SKyle Evans DPRINTF("invalid number of entries"); 570ca987d46SWarner Losh goto out; 571ca987d46SWarner Losh } 572ca987d46SWarner Losh sectors = be16toh(dl->nsecs); 573ca987d46SWarner Losh heads = be16toh(dl->nheads); 574ca987d46SWarner Losh if (sectors * heads == 0) { 5757325df02SKyle Evans DPRINTF("invalid geometry"); 576ca987d46SWarner Losh goto out; 577ca987d46SWarner Losh } 5787325df02SKyle Evans DPRINTF("VTOC8 detected"); 579ca987d46SWarner Losh for (i = 0; i < VTOC8_NPARTS; i++) { 580ca987d46SWarner Losh dl->part[i].tag = be16toh(dl->part[i].tag); 581ca987d46SWarner Losh if (i == VTOC_RAW_PART || 582ca987d46SWarner Losh dl->part[i].tag == VTOC_TAG_UNASSIGNED) 583ca987d46SWarner Losh continue; 584ca987d46SWarner Losh entry = malloc(sizeof(*entry)); 585ca987d46SWarner Losh if (entry == NULL) 586ca987d46SWarner Losh break; 587ca987d46SWarner Losh entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors; 588ca987d46SWarner Losh entry->part.end = be32toh(dl->map[i].nblks) + 589ca987d46SWarner Losh entry->part.start - 1; 590ca987d46SWarner Losh entry->part.type = vtoc8_parttype(dl->part[i].tag); 591ca987d46SWarner Losh entry->part.index = i; /* starts from zero */ 592ca987d46SWarner Losh entry->type.vtoc8 = dl->part[i].tag; 593ca987d46SWarner Losh STAILQ_INSERT_TAIL(&table->entries, entry, entry); 5947325df02SKyle Evans DPRINTF("new VTOC8 partition added"); 595ca987d46SWarner Losh } 596ca987d46SWarner Losh table->type = PTABLE_VTOC8; 597ca987d46SWarner Losh out: 598ca987d46SWarner Losh free(buf); 599ca987d46SWarner Losh return (table); 600ca987d46SWarner Losh 601ca987d46SWarner Losh } 602ca987d46SWarner Losh #endif /* LOADER_VTOC8_SUPPORT */ 603ca987d46SWarner Losh 60448990fceSBenno Rice #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / table->sectorsize) 60548990fceSBenno Rice 60648990fceSBenno Rice static struct ptable * 60748990fceSBenno Rice ptable_iso9660read(struct ptable *table, void *dev, diskread_t dread) 60848990fceSBenno Rice { 60948990fceSBenno Rice uint8_t *buf; 61048990fceSBenno Rice struct iso_primary_descriptor *vd; 61148990fceSBenno Rice struct pentry *entry; 61248990fceSBenno Rice 61348990fceSBenno Rice buf = malloc(table->sectorsize); 61448990fceSBenno Rice if (buf == NULL) 61548990fceSBenno Rice return (table); 61648990fceSBenno Rice 61748990fceSBenno Rice if (dread(dev, buf, 1, cdb2devb(16)) != 0) { 6187325df02SKyle Evans DPRINTF("read failed"); 61948990fceSBenno Rice ptable_close(table); 62048990fceSBenno Rice table = NULL; 62148990fceSBenno Rice goto out; 62248990fceSBenno Rice } 62348990fceSBenno Rice vd = (struct iso_primary_descriptor *)buf; 62448990fceSBenno Rice if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0) 62548990fceSBenno Rice goto out; 62648990fceSBenno Rice 62748990fceSBenno Rice entry = malloc(sizeof(*entry)); 62848990fceSBenno Rice if (entry == NULL) 62948990fceSBenno Rice goto out; 63048990fceSBenno Rice entry->part.start = 0; 63148990fceSBenno Rice entry->part.end = table->sectors; 63248990fceSBenno Rice entry->part.type = PART_ISO9660; 63348990fceSBenno Rice entry->part.index = 0; 63448990fceSBenno Rice STAILQ_INSERT_TAIL(&table->entries, entry, entry); 63548990fceSBenno Rice 63648990fceSBenno Rice table->type = PTABLE_ISO9660; 63748990fceSBenno Rice 63848990fceSBenno Rice out: 63948990fceSBenno Rice free(buf); 64048990fceSBenno Rice return (table); 64148990fceSBenno Rice } 64248990fceSBenno Rice 643ca987d46SWarner Losh struct ptable * 644ca987d46SWarner Losh ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize, 645ca987d46SWarner Losh diskread_t *dread) 646ca987d46SWarner Losh { 647ca987d46SWarner Losh struct dos_partition *dp; 648ca987d46SWarner Losh struct ptable *table; 649ca987d46SWarner Losh uint8_t *buf; 650ca987d46SWarner Losh int i, count; 651ca987d46SWarner Losh #ifdef LOADER_MBR_SUPPORT 652ca987d46SWarner Losh struct pentry *entry; 653ca987d46SWarner Losh uint32_t start, end; 654ca987d46SWarner Losh int has_ext; 655ca987d46SWarner Losh #endif 656ca987d46SWarner Losh table = NULL; 657ca987d46SWarner Losh buf = malloc(sectorsize); 658ca987d46SWarner Losh if (buf == NULL) 659ca987d46SWarner Losh return (NULL); 660ca987d46SWarner Losh /* First, read the MBR. */ 661ca987d46SWarner Losh if (dread(dev, buf, 1, DOSBBSECTOR) != 0) { 6627325df02SKyle Evans DPRINTF("read failed"); 663ca987d46SWarner Losh goto out; 664ca987d46SWarner Losh } 665ca987d46SWarner Losh 666ca987d46SWarner Losh table = malloc(sizeof(*table)); 667ca987d46SWarner Losh if (table == NULL) 668ca987d46SWarner Losh goto out; 669ca987d46SWarner Losh table->sectors = sectors; 670ca987d46SWarner Losh table->sectorsize = sectorsize; 671ca987d46SWarner Losh table->type = PTABLE_NONE; 672ca987d46SWarner Losh STAILQ_INIT(&table->entries); 673ca987d46SWarner Losh 674a3fd276bSToomas Soome if (ptable_iso9660read(table, dev, dread) == NULL) { 675a3fd276bSToomas Soome /* Read error. */ 676a3fd276bSToomas Soome table = NULL; 67748990fceSBenno Rice goto out; 678a3fd276bSToomas Soome } else if (table->type == PTABLE_ISO9660) 679a3fd276bSToomas Soome goto out; 68048990fceSBenno Rice 681ca987d46SWarner Losh #ifdef LOADER_VTOC8_SUPPORT 682ca987d46SWarner Losh if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) { 683ca987d46SWarner Losh if (ptable_vtoc8read(table, dev, dread) == NULL) { 684ca987d46SWarner Losh /* Read error. */ 685ca987d46SWarner Losh table = NULL; 686ca987d46SWarner Losh goto out; 687ca987d46SWarner Losh } else if (table->type == PTABLE_VTOC8) 688ca987d46SWarner Losh goto out; 689ca987d46SWarner Losh } 690ca987d46SWarner Losh #endif 691ca987d46SWarner Losh /* Check the BSD label. */ 692ca987d46SWarner Losh if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */ 693ca987d46SWarner Losh table = NULL; 694ca987d46SWarner Losh goto out; 695ca987d46SWarner Losh } else if (table->type == PTABLE_BSD) 696ca987d46SWarner Losh goto out; 697ca987d46SWarner Losh 698ca987d46SWarner Losh #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT) 699ca987d46SWarner Losh /* Check the MBR magic. */ 700ca987d46SWarner Losh if (buf[DOSMAGICOFFSET] != 0x55 || 701ca987d46SWarner Losh buf[DOSMAGICOFFSET + 1] != 0xaa) { 7027325df02SKyle Evans DPRINTF("magic sequence not found"); 703ca987d46SWarner Losh #if defined(LOADER_GPT_SUPPORT) 704ca987d46SWarner Losh /* There is no PMBR, check that we have backup GPT */ 705ca987d46SWarner Losh table->type = PTABLE_GPT; 706ca987d46SWarner Losh table = ptable_gptread(table, dev, dread); 707ca987d46SWarner Losh #endif 708ca987d46SWarner Losh goto out; 709ca987d46SWarner Losh } 710ca987d46SWarner Losh /* Check that we have PMBR. Also do some validation. */ 711ca987d46SWarner Losh dp = (struct dos_partition *)(buf + DOSPARTOFF); 712ca987d46SWarner Losh for (i = 0, count = 0; i < NDOSPART; i++) { 713ca987d46SWarner Losh if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) { 7147325df02SKyle Evans DPRINTF("invalid partition flag %x", dp[i].dp_flag); 715ca987d46SWarner Losh goto out; 716ca987d46SWarner Losh } 717ca987d46SWarner Losh #ifdef LOADER_GPT_SUPPORT 718ca987d46SWarner Losh if (dp[i].dp_typ == DOSPTYP_PMBR) { 719ca987d46SWarner Losh table->type = PTABLE_GPT; 7207325df02SKyle Evans DPRINTF("PMBR detected"); 721ca987d46SWarner Losh } 722ca987d46SWarner Losh #endif 723ca987d46SWarner Losh if (dp[i].dp_typ != 0) 724ca987d46SWarner Losh count++; 725ca987d46SWarner Losh } 726ca987d46SWarner Losh /* Do we have some invalid values? */ 727ca987d46SWarner Losh if (table->type == PTABLE_GPT && count > 1) { 728ca987d46SWarner Losh if (dp[1].dp_typ != DOSPTYP_HFS) { 729ca987d46SWarner Losh table->type = PTABLE_NONE; 7307325df02SKyle Evans DPRINTF("Incorrect PMBR, ignore it"); 731ca987d46SWarner Losh } else { 7327325df02SKyle Evans DPRINTF("Bootcamp detected"); 733ca987d46SWarner Losh } 734ca987d46SWarner Losh } 735ca987d46SWarner Losh #ifdef LOADER_GPT_SUPPORT 736ca987d46SWarner Losh if (table->type == PTABLE_GPT) { 737ca987d46SWarner Losh table = ptable_gptread(table, dev, dread); 738ca987d46SWarner Losh goto out; 739ca987d46SWarner Losh } 740ca987d46SWarner Losh #endif 741ca987d46SWarner Losh #ifdef LOADER_MBR_SUPPORT 742ca987d46SWarner Losh /* Read MBR. */ 7437325df02SKyle Evans DPRINTF("MBR detected"); 744ca987d46SWarner Losh table->type = PTABLE_MBR; 745ca987d46SWarner Losh for (i = has_ext = 0; i < NDOSPART; i++) { 746ca987d46SWarner Losh if (dp[i].dp_typ == 0) 747ca987d46SWarner Losh continue; 748ca987d46SWarner Losh start = le32dec(&(dp[i].dp_start)); 749ca987d46SWarner Losh end = le32dec(&(dp[i].dp_size)); 750ca987d46SWarner Losh if (start == 0 || end == 0) 751ca987d46SWarner Losh continue; 752ca987d46SWarner Losh #if 0 /* Some BIOSes return an incorrect number of sectors */ 753ca987d46SWarner Losh if (start + end - 1 >= sectors) 754ca987d46SWarner Losh continue; /* XXX: ignore */ 755ca987d46SWarner Losh #endif 756ca987d46SWarner Losh if (dp[i].dp_typ == DOSPTYP_EXT || 757ca987d46SWarner Losh dp[i].dp_typ == DOSPTYP_EXTLBA) 758ca987d46SWarner Losh has_ext = 1; 759ca987d46SWarner Losh entry = malloc(sizeof(*entry)); 760ca987d46SWarner Losh if (entry == NULL) 761ca987d46SWarner Losh break; 762ca987d46SWarner Losh entry->part.start = start; 763ca987d46SWarner Losh entry->part.end = start + end - 1; 764ca987d46SWarner Losh entry->part.index = i + 1; 765ca987d46SWarner Losh entry->part.type = mbr_parttype(dp[i].dp_typ); 766ca987d46SWarner Losh entry->flags = dp[i].dp_flag; 767ca987d46SWarner Losh entry->type.mbr = dp[i].dp_typ; 768ca987d46SWarner Losh STAILQ_INSERT_TAIL(&table->entries, entry, entry); 7697325df02SKyle Evans DPRINTF("new MBR partition added"); 770ca987d46SWarner Losh } 771ca987d46SWarner Losh if (has_ext) { 772ca987d46SWarner Losh table = ptable_ebrread(table, dev, dread); 773ca987d46SWarner Losh /* FALLTHROUGH */ 774ca987d46SWarner Losh } 775ca987d46SWarner Losh #endif /* LOADER_MBR_SUPPORT */ 776ca987d46SWarner Losh #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */ 777ca987d46SWarner Losh out: 778ca987d46SWarner Losh free(buf); 779ca987d46SWarner Losh return (table); 780ca987d46SWarner Losh } 781ca987d46SWarner Losh 782ca987d46SWarner Losh void 783ca987d46SWarner Losh ptable_close(struct ptable *table) 784ca987d46SWarner Losh { 785ca987d46SWarner Losh struct pentry *entry; 786ca987d46SWarner Losh 78763f582e0SToomas Soome if (table == NULL) 78863f582e0SToomas Soome return; 78963f582e0SToomas Soome 790ca987d46SWarner Losh while (!STAILQ_EMPTY(&table->entries)) { 791ca987d46SWarner Losh entry = STAILQ_FIRST(&table->entries); 792ca987d46SWarner Losh STAILQ_REMOVE_HEAD(&table->entries, entry); 793ca987d46SWarner Losh free(entry); 794ca987d46SWarner Losh } 795ca987d46SWarner Losh free(table); 796ca987d46SWarner Losh } 797ca987d46SWarner Losh 798ca987d46SWarner Losh enum ptable_type 799ca987d46SWarner Losh ptable_gettype(const struct ptable *table) 800ca987d46SWarner Losh { 801ca987d46SWarner Losh 802ca987d46SWarner Losh return (table->type); 803ca987d46SWarner Losh } 804ca987d46SWarner Losh 805ca987d46SWarner Losh int 806ca987d46SWarner Losh ptable_getsize(const struct ptable *table, uint64_t *sizep) 807ca987d46SWarner Losh { 808ca987d46SWarner Losh uint64_t tmp = table->sectors * table->sectorsize; 809ca987d46SWarner Losh 810ca987d46SWarner Losh if (tmp < table->sectors) 811ca987d46SWarner Losh return (EOVERFLOW); 812ca987d46SWarner Losh 813ca987d46SWarner Losh if (sizep != NULL) 814ca987d46SWarner Losh *sizep = tmp; 815ca987d46SWarner Losh return (0); 816ca987d46SWarner Losh } 817ca987d46SWarner Losh 818ca987d46SWarner Losh int 819ca987d46SWarner Losh ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index) 820ca987d46SWarner Losh { 821ca987d46SWarner Losh struct pentry *entry; 822ca987d46SWarner Losh 823ca987d46SWarner Losh if (part == NULL || table == NULL) 824ca987d46SWarner Losh return (EINVAL); 825ca987d46SWarner Losh 826ca987d46SWarner Losh STAILQ_FOREACH(entry, &table->entries, entry) { 827ca987d46SWarner Losh if (entry->part.index != index) 828ca987d46SWarner Losh continue; 829ca987d46SWarner Losh memcpy(part, &entry->part, sizeof(*part)); 830ca987d46SWarner Losh return (0); 831ca987d46SWarner Losh } 832ca987d46SWarner Losh return (ENOENT); 833ca987d46SWarner Losh } 834ca987d46SWarner Losh 835ca987d46SWarner Losh /* 836ca987d46SWarner Losh * Search for a slice with the following preferences: 837ca987d46SWarner Losh * 838ca987d46SWarner Losh * 1: Active FreeBSD slice 839ca987d46SWarner Losh * 2: Non-active FreeBSD slice 840ca987d46SWarner Losh * 3: Active Linux slice 841ca987d46SWarner Losh * 4: non-active Linux slice 842ca987d46SWarner Losh * 5: Active FAT/FAT32 slice 843ca987d46SWarner Losh * 6: non-active FAT/FAT32 slice 844ca987d46SWarner Losh */ 845ca987d46SWarner Losh #define PREF_RAWDISK 0 846ca987d46SWarner Losh #define PREF_FBSD_ACT 1 847ca987d46SWarner Losh #define PREF_FBSD 2 848ca987d46SWarner Losh #define PREF_LINUX_ACT 3 849ca987d46SWarner Losh #define PREF_LINUX 4 850ca987d46SWarner Losh #define PREF_DOS_ACT 5 851ca987d46SWarner Losh #define PREF_DOS 6 852ca987d46SWarner Losh #define PREF_NONE 7 853ca987d46SWarner Losh int 854ca987d46SWarner Losh ptable_getbestpart(const struct ptable *table, struct ptable_entry *part) 855ca987d46SWarner Losh { 856ca987d46SWarner Losh struct pentry *entry, *best; 857ca987d46SWarner Losh int pref, preflevel; 858ca987d46SWarner Losh 859ca987d46SWarner Losh if (part == NULL || table == NULL) 860ca987d46SWarner Losh return (EINVAL); 861ca987d46SWarner Losh 862ca987d46SWarner Losh best = NULL; 863ca987d46SWarner Losh preflevel = pref = PREF_NONE; 864ca987d46SWarner Losh STAILQ_FOREACH(entry, &table->entries, entry) { 865ca987d46SWarner Losh #ifdef LOADER_MBR_SUPPORT 866ca987d46SWarner Losh if (table->type == PTABLE_MBR) { 867ca987d46SWarner Losh switch (entry->type.mbr) { 868ca987d46SWarner Losh case DOSPTYP_386BSD: 869ca987d46SWarner Losh pref = entry->flags & 0x80 ? PREF_FBSD_ACT: 870ca987d46SWarner Losh PREF_FBSD; 871ca987d46SWarner Losh break; 872ca987d46SWarner Losh case DOSPTYP_LINUX: 873ca987d46SWarner Losh pref = entry->flags & 0x80 ? PREF_LINUX_ACT: 874ca987d46SWarner Losh PREF_LINUX; 875ca987d46SWarner Losh break; 876ca987d46SWarner Losh case 0x01: /* DOS/Windows */ 877ca987d46SWarner Losh case 0x04: 878ca987d46SWarner Losh case 0x06: 879ca987d46SWarner Losh case 0x0c: 880ca987d46SWarner Losh case 0x0e: 881ca987d46SWarner Losh case DOSPTYP_FAT32: 882ca987d46SWarner Losh pref = entry->flags & 0x80 ? PREF_DOS_ACT: 883ca987d46SWarner Losh PREF_DOS; 884ca987d46SWarner Losh break; 885ca987d46SWarner Losh default: 886ca987d46SWarner Losh pref = PREF_NONE; 887ca987d46SWarner Losh } 888ca987d46SWarner Losh } 889ca987d46SWarner Losh #endif /* LOADER_MBR_SUPPORT */ 890ca987d46SWarner Losh #ifdef LOADER_GPT_SUPPORT 891ca987d46SWarner Losh if (table->type == PTABLE_GPT) { 892ca987d46SWarner Losh if (entry->part.type == PART_DOS) 893ca987d46SWarner Losh pref = PREF_DOS; 894ca987d46SWarner Losh else if (entry->part.type == PART_FREEBSD_UFS || 895ca987d46SWarner Losh entry->part.type == PART_FREEBSD_ZFS) 896ca987d46SWarner Losh pref = PREF_FBSD; 897ca987d46SWarner Losh else 898ca987d46SWarner Losh pref = PREF_NONE; 899ca987d46SWarner Losh } 900ca987d46SWarner Losh #endif /* LOADER_GPT_SUPPORT */ 901ca987d46SWarner Losh if (pref < preflevel) { 902ca987d46SWarner Losh preflevel = pref; 903ca987d46SWarner Losh best = entry; 904ca987d46SWarner Losh } 905ca987d46SWarner Losh } 906ca987d46SWarner Losh if (best != NULL) { 907ca987d46SWarner Losh memcpy(part, &best->part, sizeof(*part)); 908ca987d46SWarner Losh return (0); 909ca987d46SWarner Losh } 910ca987d46SWarner Losh return (ENOENT); 911ca987d46SWarner Losh } 912ca987d46SWarner Losh 913ca987d46SWarner Losh int 914ca987d46SWarner Losh ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter) 915ca987d46SWarner Losh { 916ca987d46SWarner Losh struct pentry *entry; 917ca987d46SWarner Losh char name[32]; 918ca987d46SWarner Losh int ret = 0; 919ca987d46SWarner Losh 920ca987d46SWarner Losh name[0] = '\0'; 921ca987d46SWarner Losh STAILQ_FOREACH(entry, &table->entries, entry) { 922ca987d46SWarner Losh #ifdef LOADER_MBR_SUPPORT 923ca987d46SWarner Losh if (table->type == PTABLE_MBR) 924ca987d46SWarner Losh sprintf(name, "s%d", entry->part.index); 925ca987d46SWarner Losh else 926ca987d46SWarner Losh #endif 927ca987d46SWarner Losh #ifdef LOADER_GPT_SUPPORT 928ca987d46SWarner Losh if (table->type == PTABLE_GPT) 929ca987d46SWarner Losh sprintf(name, "p%d", entry->part.index); 930ca987d46SWarner Losh else 931ca987d46SWarner Losh #endif 932ca987d46SWarner Losh #ifdef LOADER_VTOC8_SUPPORT 933ca987d46SWarner Losh if (table->type == PTABLE_VTOC8) 934ca987d46SWarner Losh sprintf(name, "%c", (uint8_t) 'a' + 935ca987d46SWarner Losh entry->part.index); 936ca987d46SWarner Losh else 937ca987d46SWarner Losh #endif 938ca987d46SWarner Losh if (table->type == PTABLE_BSD) 939ca987d46SWarner Losh sprintf(name, "%c", (uint8_t) 'a' + 940ca987d46SWarner Losh entry->part.index); 941ca987d46SWarner Losh if ((ret = iter(arg, name, &entry->part)) != 0) 942ca987d46SWarner Losh return (ret); 943ca987d46SWarner Losh } 944ca987d46SWarner Losh return (ret); 945ca987d46SWarner Losh } 946