part.c revision 256281
1141296Sdas/*-
22116Sjkh * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
32116Sjkh * All rights reserved.
42116Sjkh *
52116Sjkh * Redistribution and use in source and binary forms, with or without
6141296Sdas * modification, are permitted provided that the following conditions
72116Sjkh * are met:
8284810Stijl * 1. Redistributions of source code must retain the above copyright
92116Sjkh *    notice, this list of conditions and the following disclaimer.
102116Sjkh * 2. Redistributions in binary form must reproduce the above copyright
112116Sjkh *    notice, this list of conditions and the following disclaimer in the
122116Sjkh *    documentation and/or other materials provided with the distribution.
13176451Sdas *
14176451Sdas * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
152116Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
162116Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17284810Stijl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18284810Stijl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
192116Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
202116Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
212116Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22284810Stijl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
232116Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
242116Sjkh * SUCH DAMAGE.
252116Sjkh */
262116Sjkh
272116Sjkh#include <sys/cdefs.h>
282116Sjkh__FBSDID("$FreeBSD: stable/10/sys/boot/common/part.c 254092 2013-08-08 11:24:25Z ae $");
292116Sjkh
302116Sjkh#include <stand.h>
312116Sjkh#include <sys/param.h>
322116Sjkh#include <sys/diskmbr.h>
332116Sjkh#include <sys/disklabel.h>
342116Sjkh#include <sys/endian.h>
352116Sjkh#include <sys/gpt.h>
362116Sjkh#include <sys/stddef.h>
372116Sjkh#include <sys/queue.h>
382116Sjkh#include <sys/vtoc.h>
392116Sjkh
402116Sjkh#include <crc32.h>
412116Sjkh#include <part.h>
422116Sjkh#include <uuid.h>
432116Sjkh
442116Sjkh#ifdef PART_DEBUG
452116Sjkh#define	DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
462116Sjkh#else
472116Sjkh#define	DEBUG(fmt, args...)
482116Sjkh#endif
492116Sjkh
502116Sjkh#ifdef LOADER_GPT_SUPPORT
512116Sjkh#define	MAXTBLSZ	64
522116Sjkhstatic const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
532116Sjkhstatic const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
542116Sjkhstatic const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
552116Sjkhstatic const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI;
562116Sjkhstatic const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
572116Sjkhstatic const uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS;
582116Sjkhstatic const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
592116Sjkhstatic const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
60284810Stijlstatic const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
612116Sjkh#endif
62284810Stijl
632116Sjkhstruct pentry {
642116Sjkh	struct ptable_entry	part;
652116Sjkh	uint64_t		flags;
662116Sjkh	union {
672116Sjkh		uint8_t bsd;
68284810Stijl		uint8_t	mbr;
692116Sjkh		uuid_t	gpt;
702116Sjkh		uint16_t vtoc8;
71284810Stijl	} type;
722116Sjkh	STAILQ_ENTRY(pentry)	entry;
73284810Stijl};
742116Sjkh
752116Sjkhstruct ptable {
76169220Sbde	enum ptable_type	type;
77169220Sbde	uint16_t		sectorsize;
78169220Sbde	uint64_t		sectors;
79169220Sbde
80169220Sbde	STAILQ_HEAD(, pentry)	entries;
812116Sjkh};
822116Sjkh
83284810Stijlstatic struct parttypes {
84284810Stijl	enum partition_type	type;
852116Sjkh	const char		*desc;
862116Sjkh} ptypes[] = {
872116Sjkh	{ PART_UNKNOWN,		"Unknown" },
88271779Stijl	{ PART_EFI,		"EFI" },
89271779Stijl	{ PART_FREEBSD,		"FreeBSD" },
908870Srgrimes	{ PART_FREEBSD_BOOT,	"FreeBSD boot" },
91271779Stijl	{ PART_FREEBSD_NANDFS,	"FreeBSD nandfs" },
922116Sjkh	{ PART_FREEBSD_UFS,	"FreeBSD UFS" },
932116Sjkh	{ PART_FREEBSD_ZFS,	"FreeBSD ZFS" },
942116Sjkh	{ PART_FREEBSD_SWAP,	"FreeBSD swap" },
952116Sjkh	{ PART_FREEBSD_VINUM,	"FreeBSD vinum" },
962116Sjkh	{ PART_LINUX,		"Linux" },
972116Sjkh	{ PART_LINUX_SWAP,	"Linux swap" },
982116Sjkh	{ PART_DOS,		"DOS/Windows" },
992116Sjkh};
1002116Sjkh
1012116Sjkhconst char *
1022116Sjkhparttype2str(enum partition_type type)
1032116Sjkh{
1042116Sjkh	int i;
1052116Sjkh
1062116Sjkh	for (i = 0; i < sizeof(ptypes) / sizeof(ptypes[0]); i++)
1072116Sjkh		if (ptypes[i].type == type)
1082116Sjkh			return (ptypes[i].desc);
1092116Sjkh	return (ptypes[0].desc);
1102116Sjkh}
1112116Sjkh
1122116Sjkh#ifdef LOADER_GPT_SUPPORT
1132116Sjkhstatic void
1142116Sjkhuuid_letoh(uuid_t *uuid)
1152116Sjkh{
1162116Sjkh
1172116Sjkh	uuid->time_low = le32toh(uuid->time_low);
1182116Sjkh	uuid->time_mid = le16toh(uuid->time_mid);
1192116Sjkh	uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version);
1202116Sjkh}
1212116Sjkh
1222116Sjkhstatic enum partition_type
1232116Sjkhgpt_parttype(uuid_t type)
1242116Sjkh{
1252116Sjkh
1262116Sjkh	if (uuid_equal(&type, &gpt_uuid_efi, NULL))
1272116Sjkh		return (PART_EFI);
1282116Sjkh	else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL))
1292116Sjkh		return (PART_DOS);
1302116Sjkh	else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL))
1312116Sjkh		return (PART_FREEBSD_BOOT);
1322116Sjkh	else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL))
1332116Sjkh		return (PART_FREEBSD_UFS);
1342116Sjkh	else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL))
1352116Sjkh		return (PART_FREEBSD_ZFS);
1362116Sjkh	else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL))
1372116Sjkh		return (PART_FREEBSD_SWAP);
1382116Sjkh	else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL))
1392116Sjkh		return (PART_FREEBSD_VINUM);
1402116Sjkh	else if (uuid_equal(&type, &gpt_uuid_freebsd_nandfs, NULL))
1412116Sjkh		return (PART_FREEBSD_NANDFS);
1422116Sjkh	return (PART_UNKNOWN);
1432116Sjkh}
1442116Sjkh
1452116Sjkhstatic struct gpt_hdr*
1462116Sjkhgpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last,
1472116Sjkh    uint16_t sectorsize)
1482116Sjkh{
1492116Sjkh	uint32_t sz, crc;
1502116Sjkh
1512116Sjkh	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) {
1522116Sjkh		DEBUG("no GPT signature");
1532116Sjkh		return (NULL);
1542116Sjkh	}
1552116Sjkh	sz = le32toh(hdr->hdr_size);
1562116Sjkh	if (sz < 92 || sz > sectorsize) {
1572116Sjkh		DEBUG("invalid GPT header size: %d", sz);
158271779Stijl		return (NULL);
159271779Stijl	}
160271779Stijl	crc = le32toh(hdr->hdr_crc_self);
161271779Stijl	hdr->hdr_crc_self = 0;
162271779Stijl	if (crc32(hdr, sz) != crc) {
163271779Stijl		DEBUG("GPT header's CRC doesn't match");
164271779Stijl		return (NULL);
1652116Sjkh	}
166271779Stijl	hdr->hdr_crc_self = crc;
1672116Sjkh	hdr->hdr_revision = le32toh(hdr->hdr_revision);
168271779Stijl	if (hdr->hdr_revision < GPT_HDR_REVISION) {
1692116Sjkh		DEBUG("unsupported GPT revision %d", hdr->hdr_revision);
170271779Stijl		return (NULL);
1712116Sjkh	}
172271779Stijl	hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
173271779Stijl	if (hdr->hdr_lba_self != lba_self) {
174271779Stijl		DEBUG("self LBA doesn't match");
175271779Stijl		return (NULL);
1762116Sjkh	}
177271779Stijl	hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
178271779Stijl	if (hdr->hdr_lba_alt == hdr->hdr_lba_self) {
179271779Stijl		DEBUG("invalid alternate LBA");
180271779Stijl		return (NULL);
181271779Stijl	}
182271779Stijl	hdr->hdr_entries = le32toh(hdr->hdr_entries);
183271779Stijl	hdr->hdr_entsz = le32toh(hdr->hdr_entsz);
184271779Stijl	if (hdr->hdr_entries == 0 ||
185271779Stijl	    hdr->hdr_entsz < sizeof(struct gpt_ent) ||
186271779Stijl	    sectorsize % hdr->hdr_entsz != 0) {
1872116Sjkh		DEBUG("invalid entry size or number of entries");
1882116Sjkh		return (NULL);
189284810Stijl	}
1902116Sjkh	hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
191284810Stijl	hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end);
1922116Sjkh	hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table);
1932116Sjkh	hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table);
1942116Sjkh	uuid_letoh(&hdr->hdr_uuid);
1952116Sjkh	return (hdr);
1962116Sjkh}
1972116Sjkh
1982116Sjkhstatic int
1992116Sjkhgpt_checktbl(const struct gpt_hdr *hdr, u_char *tbl, size_t size,
2002116Sjkh    uint64_t lba_last)
20197413Salfred{
20297413Salfred	struct gpt_ent *ent;
2032116Sjkh	int i, cnt;
204284810Stijl
205169220Sbde	cnt = size / hdr->hdr_entsz;
206271779Stijl	if (hdr->hdr_entries <= cnt) {
2072116Sjkh		cnt = hdr->hdr_entries;
2082116Sjkh		/* Check CRC only when buffer size is enough for table. */
2092116Sjkh		if (hdr->hdr_crc_table !=
210284810Stijl		    crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) {
2112116Sjkh			DEBUG("GPT table's CRC doesn't match");
2122116Sjkh			return (-1);
2132116Sjkh		}
214284810Stijl	}
215284810Stijl	ent = (struct gpt_ent *)tbl;
216284810Stijl	for (i = 0; i < cnt; i++, ent++) {
217284810Stijl		uuid_letoh(&ent->ent_type);
218284810Stijl		if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
219284810Stijl			continue;
220284810Stijl		ent->ent_lba_start = le64toh(ent->ent_lba_start);
2212116Sjkh		ent->ent_lba_end = le64toh(ent->ent_lba_end);
222284810Stijl	}
223284810Stijl	return (0);
2242116Sjkh}
225284810Stijl
2262116Sjkhstatic struct ptable*
227271779Stijlptable_gptread(struct ptable *table, void *dev, diskread_t dread)
2282116Sjkh{
229271779Stijl	struct pentry *entry;
2302116Sjkh	struct gpt_hdr *phdr, hdr;
2312116Sjkh	struct gpt_ent *ent;
2322116Sjkh	u_char *buf, *tbl;
2332116Sjkh	uint64_t offset;
2342116Sjkh	int pri, sec, i;
235284810Stijl	size_t size;
2362116Sjkh
2372116Sjkh	buf = malloc(table->sectorsize);
2382116Sjkh	if (buf == NULL)
2392116Sjkh		return (NULL);
2402116Sjkh	tbl = malloc(table->sectorsize * MAXTBLSZ);
2412116Sjkh	if (tbl == NULL) {
2422116Sjkh		free(buf);
2432116Sjkh		return (NULL);
2442116Sjkh	}
2452116Sjkh	/* Read the primary GPT header. */
2462116Sjkh	if (dread(dev, buf, 1, 1) != 0) {
2472116Sjkh		ptable_close(table);
2482116Sjkh		table = NULL;
2492116Sjkh		goto out;
2502116Sjkh	}
2512116Sjkh	pri = sec = 0;
2522116Sjkh	/* Check the primary GPT header. */
2532116Sjkh	phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1,
2542116Sjkh	    table->sectorsize);
2552116Sjkh	if (phdr != NULL) {
256284810Stijl		/* Read the primary GPT table. */
2572116Sjkh		size = MIN(MAXTBLSZ,
2582116Sjkh		    phdr->hdr_entries * phdr->hdr_entsz / table->sectorsize);
2592116Sjkh		if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
2602116Sjkh		    gpt_checktbl(phdr, tbl, size * table->sectorsize,
2612116Sjkh		    table->sectors - 1) == 0) {
2622116Sjkh			memcpy(&hdr, phdr, sizeof(hdr));
2632116Sjkh			pri = 1;
264284810Stijl		}
265284810Stijl	}
2662116Sjkh	offset = pri ? hdr.hdr_lba_alt: table->sectors - 1;
2672116Sjkh	/* Read the backup GPT header. */
268284810Stijl	if (dread(dev, buf, 1, offset) != 0)
2692116Sjkh		phdr = NULL;
2702116Sjkh	else
271284810Stijl		phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset,
272284810Stijl		    table->sectors - 1, table->sectorsize);
273284810Stijl	if (phdr != NULL) {
274284810Stijl		/*
2752116Sjkh		 * Compare primary and backup headers.
2762116Sjkh		 * If they are equal, then we do not need to read backup
277284810Stijl		 * table. If they are different, then prefer backup header
2782116Sjkh		 * and try to read backup table.
2792116Sjkh		 */
280284810Stijl		if (pri == 0 ||
281284810Stijl		    uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 ||
282284810Stijl		    hdr.hdr_revision != phdr->hdr_revision ||
283284810Stijl		    hdr.hdr_size != phdr->hdr_size ||
284284810Stijl		    hdr.hdr_lba_start != phdr->hdr_lba_start ||
2852116Sjkh		    hdr.hdr_lba_end != phdr->hdr_lba_end ||
2862116Sjkh		    hdr.hdr_entries != phdr->hdr_entries ||
287284810Stijl		    hdr.hdr_entsz != phdr->hdr_entsz ||
288284810Stijl		    hdr.hdr_crc_table != phdr->hdr_crc_table) {
2892116Sjkh			/* Read the backup GPT table. */
2902116Sjkh			size = MIN(MAXTBLSZ, phdr->hdr_entries *
2912116Sjkh			    phdr->hdr_entsz / table->sectorsize);
2922116Sjkh			if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
2932116Sjkh			    gpt_checktbl(phdr, tbl, size * table->sectorsize,
294284810Stijl			    table->sectors - 1) == 0) {
295284810Stijl				memcpy(&hdr, phdr, sizeof(hdr));
2962116Sjkh				sec = 1;
2972116Sjkh			}
2982116Sjkh		}
2992116Sjkh	}
300284810Stijl	if (pri == 0 && sec == 0) {
301284810Stijl		/* Both primary and backup tables are invalid. */
302284810Stijl		table->type = PTABLE_NONE;
303284810Stijl		goto out;
304	}
305	ent = (struct gpt_ent *)tbl;
306	size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
307	    MAXTBLSZ * table->sectorsize);
308	for (i = 0; i < size / hdr.hdr_entsz; i++, ent++) {
309		if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
310			continue;
311		entry = malloc(sizeof(*entry));
312		if (entry == NULL)
313			break;
314		entry->part.start = ent->ent_lba_start;
315		entry->part.end = ent->ent_lba_end;
316		entry->part.index = i + 1;
317		entry->part.type = gpt_parttype(ent->ent_type);
318		entry->flags = le64toh(ent->ent_attr);
319		memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t));
320		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
321		DEBUG("new GPT partition added");
322	}
323out:
324	free(buf);
325	free(tbl);
326	return (table);
327}
328#endif /* LOADER_GPT_SUPPORT */
329
330#ifdef LOADER_MBR_SUPPORT
331/* We do not need to support too many EBR partitions in the loader */
332#define	MAXEBRENTRIES		8
333static enum partition_type
334mbr_parttype(uint8_t type)
335{
336
337	switch (type) {
338	case DOSPTYP_386BSD:
339		return (PART_FREEBSD);
340	case DOSPTYP_LINSWP:
341		return (PART_LINUX_SWAP);
342	case DOSPTYP_LINUX:
343		return (PART_LINUX);
344	case 0x01:
345	case 0x04:
346	case 0x06:
347	case 0x07:
348	case 0x0b:
349	case 0x0c:
350	case 0x0e:
351		return (PART_DOS);
352	}
353	return (PART_UNKNOWN);
354}
355
356struct ptable*
357ptable_ebrread(struct ptable *table, void *dev, diskread_t dread)
358{
359	struct dos_partition *dp;
360	struct pentry *e1, *entry;
361	uint32_t start, end, offset;
362	u_char *buf;
363	int i, index;
364
365	STAILQ_FOREACH(e1, &table->entries, entry) {
366		if (e1->type.mbr == DOSPTYP_EXT ||
367		    e1->type.mbr == DOSPTYP_EXTLBA)
368			break;
369	}
370	if (e1 == NULL)
371		return (table);
372	index = 5;
373	offset = e1->part.start;
374	buf = malloc(table->sectorsize);
375	if (buf == NULL)
376		return (table);
377	for (i = 0; i < MAXEBRENTRIES; i++) {
378#if 0	/* Some BIOSes return an incorrect number of sectors */
379		if (offset >= table->sectors)
380			break;
381#endif
382		if (dread(dev, buf, 1, offset) != 0)
383			break;
384		dp = (struct dos_partition *)(buf + DOSPARTOFF);
385		if (dp[0].dp_typ == 0)
386			break;
387		start = le32toh(dp[0].dp_start);
388		if (dp[0].dp_typ == DOSPTYP_EXT &&
389		    dp[1].dp_typ == 0) {
390			offset = e1->part.start + start;
391			continue;
392		}
393		end = le32toh(dp[0].dp_size);
394		entry = malloc(sizeof(*entry));
395		if (entry == NULL)
396			break;
397		entry->part.start = offset + start;
398		entry->part.end = entry->part.start + end - 1;
399		entry->part.index = index++;
400		entry->part.type = mbr_parttype(dp[0].dp_typ);
401		entry->flags = dp[0].dp_flag;
402		entry->type.mbr = dp[0].dp_typ;
403		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
404		DEBUG("new EBR partition added");
405		if (dp[1].dp_typ == 0)
406			break;
407		offset = e1->part.start + le32toh(dp[1].dp_start);
408	}
409	free(buf);
410	return (table);
411}
412#endif /* LOADER_MBR_SUPPORT */
413
414static enum partition_type
415bsd_parttype(uint8_t type)
416{
417
418	switch (type) {
419	case FS_NANDFS:
420		return (PART_FREEBSD_NANDFS);
421	case FS_SWAP:
422		return (PART_FREEBSD_SWAP);
423	case FS_BSDFFS:
424		return (PART_FREEBSD_UFS);
425	case FS_VINUM:
426		return (PART_FREEBSD_VINUM);
427	case FS_ZFS:
428		return (PART_FREEBSD_ZFS);
429	}
430	return (PART_UNKNOWN);
431}
432
433struct ptable*
434ptable_bsdread(struct ptable *table, void *dev, diskread_t dread)
435{
436	struct disklabel *dl;
437	struct partition *part;
438	struct pentry *entry;
439	u_char *buf;
440	uint32_t raw_offset;
441	int i;
442
443	if (table->sectorsize < sizeof(struct disklabel)) {
444		DEBUG("Too small sectorsize");
445		return (table);
446	}
447	buf = malloc(table->sectorsize);
448	if (buf == NULL)
449		return (table);
450	if (dread(dev, buf, 1, 1) != 0) {
451		DEBUG("read failed");
452		ptable_close(table);
453		table = NULL;
454		goto out;
455	}
456	dl = (struct disklabel *)buf;
457	if (le32toh(dl->d_magic) != DISKMAGIC &&
458	    le32toh(dl->d_magic2) != DISKMAGIC)
459		goto out;
460	if (le32toh(dl->d_secsize) != table->sectorsize) {
461		DEBUG("unsupported sector size");
462		goto out;
463	}
464	dl->d_npartitions = le16toh(dl->d_npartitions);
465	if (dl->d_npartitions > 20 || dl->d_npartitions < 8) {
466		DEBUG("invalid number of partitions");
467		goto out;
468	}
469	part = &dl->d_partitions[0];
470	raw_offset = le32toh(part[RAW_PART].p_offset);
471	for (i = 0; i < dl->d_npartitions; i++, part++) {
472		if (i == RAW_PART)
473			continue;
474		if (part->p_size == 0)
475			continue;
476		entry = malloc(sizeof(*entry));
477		if (entry == NULL)
478			break;
479		entry->part.start = le32toh(part->p_offset) - raw_offset;
480		entry->part.end = entry->part.start +
481		    le32toh(part->p_size) + 1;
482		entry->part.type = bsd_parttype(part->p_fstype);
483		entry->part.index = i; /* starts from zero */
484		entry->type.bsd = part->p_fstype;
485		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
486		DEBUG("new BSD partition added");
487	}
488	table->type = PTABLE_BSD;
489out:
490	free(buf);
491	return (table);
492}
493
494#ifdef LOADER_VTOC8_SUPPORT
495static enum partition_type
496vtoc8_parttype(uint16_t type)
497{
498
499	switch (type) {
500	case VTOC_TAG_FREEBSD_NANDFS:
501		return (PART_FREEBSD_NANDFS);
502	case VTOC_TAG_FREEBSD_SWAP:
503		return (PART_FREEBSD_SWAP);
504	case VTOC_TAG_FREEBSD_UFS:
505		return (PART_FREEBSD_UFS);
506	case VTOC_TAG_FREEBSD_VINUM:
507		return (PART_FREEBSD_VINUM);
508	case VTOC_TAG_FREEBSD_ZFS:
509		return (PART_FREEBSD_ZFS);
510	};
511	return (PART_UNKNOWN);
512}
513
514static struct ptable*
515ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread)
516{
517	struct pentry *entry;
518	struct vtoc8 *dl;
519	u_char *buf;
520	uint16_t sum, heads, sectors;
521	int i;
522
523	if (table->sectorsize != sizeof(struct vtoc8))
524		return (table);
525	buf = malloc(table->sectorsize);
526	if (buf == NULL)
527		return (table);
528	if (dread(dev, buf, 1, 0) != 0) {
529		DEBUG("read failed");
530		ptable_close(table);
531		table = NULL;
532		goto out;
533	}
534	dl = (struct vtoc8 *)buf;
535	/* Check the sum */
536	for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum))
537		sum ^= be16dec(buf + i);
538	if (sum != 0) {
539		DEBUG("incorrect checksum");
540		goto out;
541	}
542	if (be16toh(dl->nparts) != VTOC8_NPARTS) {
543		DEBUG("invalid number of entries");
544		goto out;
545	}
546	sectors = be16toh(dl->nsecs);
547	heads = be16toh(dl->nheads);
548	if (sectors * heads == 0) {
549		DEBUG("invalid geometry");
550		goto out;
551	}
552	for (i = 0; i < VTOC8_NPARTS; i++) {
553		dl->part[i].tag = be16toh(dl->part[i].tag);
554		if (i == VTOC_RAW_PART ||
555		    dl->part[i].tag == VTOC_TAG_UNASSIGNED)
556			continue;
557		entry = malloc(sizeof(*entry));
558		if (entry == NULL)
559			break;
560		entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors;
561		entry->part.end = be32toh(dl->map[i].nblks) +
562		    entry->part.start - 1;
563		entry->part.type = vtoc8_parttype(dl->part[i].tag);
564		entry->part.index = i; /* starts from zero */
565		entry->type.vtoc8 = dl->part[i].tag;
566		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
567		DEBUG("new VTOC8 partition added");
568	}
569	table->type = PTABLE_VTOC8;
570out:
571	free(buf);
572	return (table);
573
574}
575#endif /* LOADER_VTOC8_SUPPORT */
576
577struct ptable*
578ptable_open(void *dev, off_t sectors, uint16_t sectorsize,
579    diskread_t *dread)
580{
581	struct dos_partition *dp;
582	struct ptable *table;
583	u_char *buf;
584	int i, count;
585#ifdef LOADER_MBR_SUPPORT
586	struct pentry *entry;
587	uint32_t start, end;
588	int has_ext;
589#endif
590	table = NULL;
591	buf = malloc(sectorsize);
592	if (buf == NULL)
593		return (NULL);
594	/* First, read the MBR. */
595	if (dread(dev, buf, 1, DOSBBSECTOR) != 0) {
596		DEBUG("read failed");
597		goto out;
598	}
599
600	table = malloc(sizeof(*table));
601	if (table == NULL)
602		goto out;
603	table->sectors = sectors;
604	table->sectorsize = sectorsize;
605	table->type = PTABLE_NONE;
606	STAILQ_INIT(&table->entries);
607
608#ifdef LOADER_VTOC8_SUPPORT
609	if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) {
610		if (ptable_vtoc8read(table, dev, dread) == NULL) {
611			/* Read error. */
612			table = NULL;
613			goto out;
614		} else if (table->type == PTABLE_VTOC8)
615			goto out;
616	}
617#endif
618	/* Check the BSD label. */
619	if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */
620		table = NULL;
621		goto out;
622	} else if (table->type == PTABLE_BSD)
623		goto out;
624
625#if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
626	/* Check the MBR magic. */
627	if (buf[DOSMAGICOFFSET] != 0x55 ||
628	    buf[DOSMAGICOFFSET + 1] != 0xaa) {
629		DEBUG("magic sequence not found");
630		goto out;
631	}
632	/* Check that we have PMBR. Also do some validation. */
633	dp = (struct dos_partition *)(buf + DOSPARTOFF);
634	for (i = 0, count = 0; i < NDOSPART; i++) {
635		if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) {
636			DEBUG("invalid partition flag %x", dp[i].dp_flag);
637			break;
638		}
639#ifdef LOADER_GPT_SUPPORT
640		if (dp[i].dp_typ == DOSPTYP_PMBR) {
641			table->type = PTABLE_GPT;
642			DEBUG("PMBR detected");
643		}
644#endif
645		if (dp[i].dp_typ != 0)
646			count++;
647	}
648	/* Do we have some invalid values? */
649	if (i != NDOSPART ||
650	    (table->type == PTABLE_GPT && count > 1)) {
651		if (dp[1].dp_typ != DOSPTYP_HFS) {
652			table->type = PTABLE_NONE;
653			DEBUG("invalid values detected, ignore "
654			    "partition table");
655			goto out;
656		}
657		DEBUG("Bootcamp detected");
658	}
659#ifdef LOADER_GPT_SUPPORT
660	if (table->type == PTABLE_GPT) {
661		table = ptable_gptread(table, dev, dread);
662		goto out;
663	}
664#endif
665#ifdef LOADER_MBR_SUPPORT
666	/* Read MBR. */
667	table->type = PTABLE_MBR;
668	for (i = has_ext = 0; i < NDOSPART; i++) {
669		if (dp[i].dp_typ == 0)
670			continue;
671		start = le32dec(&(dp[i].dp_start));
672		end = le32dec(&(dp[i].dp_size));
673		if (start == 0 || end == 0)
674			continue;
675#if 0	/* Some BIOSes return an incorrect number of sectors */
676		if (start + end - 1 >= sectors)
677			continue;	/* XXX: ignore */
678#endif
679		if (dp[i].dp_typ == DOSPTYP_EXT ||
680		    dp[i].dp_typ == DOSPTYP_EXTLBA)
681			has_ext = 1;
682		entry = malloc(sizeof(*entry));
683		if (entry == NULL)
684			break;
685		entry->part.start = start;
686		entry->part.end = start + end - 1;
687		entry->part.index = i + 1;
688		entry->part.type = mbr_parttype(dp[i].dp_typ);
689		entry->flags = dp[i].dp_flag;
690		entry->type.mbr = dp[i].dp_typ;
691		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
692		DEBUG("new MBR partition added");
693	}
694	if (has_ext) {
695		table = ptable_ebrread(table, dev, dread);
696		/* FALLTHROUGH */
697	}
698#endif /* LOADER_MBR_SUPPORT */
699#endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
700out:
701	free(buf);
702	return (table);
703}
704
705void
706ptable_close(struct ptable *table)
707{
708	struct pentry *entry;
709
710	while (!STAILQ_EMPTY(&table->entries)) {
711		entry = STAILQ_FIRST(&table->entries);
712		STAILQ_REMOVE_HEAD(&table->entries, entry);
713		free(entry);
714	}
715	free(table);
716}
717
718enum ptable_type
719ptable_gettype(const struct ptable *table)
720{
721
722	return (table->type);
723}
724
725int
726ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index)
727{
728	struct pentry *entry;
729
730	if (part == NULL || table == NULL)
731		return (EINVAL);
732
733	STAILQ_FOREACH(entry, &table->entries, entry) {
734		if (entry->part.index != index)
735			continue;
736		memcpy(part, &entry->part, sizeof(*part));
737		return (0);
738	}
739	return (ENOENT);
740}
741
742/*
743 * Search for a slice with the following preferences:
744 *
745 * 1: Active FreeBSD slice
746 * 2: Non-active FreeBSD slice
747 * 3: Active Linux slice
748 * 4: non-active Linux slice
749 * 5: Active FAT/FAT32 slice
750 * 6: non-active FAT/FAT32 slice
751 */
752#define PREF_RAWDISK	0
753#define PREF_FBSD_ACT	1
754#define PREF_FBSD	2
755#define PREF_LINUX_ACT	3
756#define PREF_LINUX	4
757#define PREF_DOS_ACT	5
758#define PREF_DOS	6
759#define PREF_NONE	7
760int
761ptable_getbestpart(const struct ptable *table, struct ptable_entry *part)
762{
763	struct pentry *entry, *best;
764	int pref, preflevel;
765
766	if (part == NULL || table == NULL)
767		return (EINVAL);
768
769	best = NULL;
770	preflevel = pref = PREF_NONE;
771	STAILQ_FOREACH(entry, &table->entries, entry) {
772#ifdef LOADER_MBR_SUPPORT
773		if (table->type == PTABLE_MBR) {
774			switch (entry->type.mbr) {
775			case DOSPTYP_386BSD:
776				pref = entry->flags & 0x80 ? PREF_FBSD_ACT:
777				    PREF_FBSD;
778				break;
779			case DOSPTYP_LINUX:
780				pref = entry->flags & 0x80 ? PREF_LINUX_ACT:
781				    PREF_LINUX;
782				break;
783			case 0x01:		/* DOS/Windows */
784			case 0x04:
785			case 0x06:
786			case 0x0c:
787			case 0x0e:
788			case DOSPTYP_FAT32:
789				pref = entry->flags & 0x80 ? PREF_DOS_ACT:
790				    PREF_DOS;
791				break;
792			default:
793				pref = PREF_NONE;
794			}
795		}
796#endif /* LOADER_MBR_SUPPORT */
797#ifdef LOADER_GPT_SUPPORT
798		if (table->type == PTABLE_GPT) {
799			if (entry->part.type == PART_DOS)
800				pref = PREF_DOS;
801			else if (entry->part.type == PART_FREEBSD_UFS ||
802			    entry->part.type == PART_FREEBSD_ZFS)
803				pref = PREF_FBSD;
804			else
805				pref = PREF_NONE;
806		}
807#endif /* LOADER_GPT_SUPPORT */
808		if (pref < preflevel) {
809			preflevel = pref;
810			best = entry;
811		}
812	}
813	if (best != NULL) {
814		memcpy(part, &best->part, sizeof(*part));
815		return (0);
816	}
817	return (ENOENT);
818}
819
820void
821ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter)
822{
823	struct pentry *entry;
824	char name[32];
825
826	name[0] = '\0';
827	STAILQ_FOREACH(entry, &table->entries, entry) {
828#ifdef LOADER_MBR_SUPPORT
829		if (table->type == PTABLE_MBR)
830			sprintf(name, "s%d", entry->part.index);
831		else
832#endif
833#ifdef LOADER_GPT_SUPPORT
834		if (table->type == PTABLE_GPT)
835			sprintf(name, "p%d", entry->part.index);
836		else
837#endif
838#ifdef LOADER_VTOC8_SUPPORT
839		if (table->type == PTABLE_VTOC8)
840			sprintf(name, "%c", (u_char) 'a' +
841			    entry->part.index);
842		else
843#endif
844		if (table->type == PTABLE_BSD)
845			sprintf(name, "%c", (u_char) 'a' +
846			    entry->part.index);
847		iter(arg, name, &entry->part);
848	}
849}
850
851