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