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$");
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 == 0 ||
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	if (hdr->hdr_entries <= cnt) {
207		cnt = hdr->hdr_entries;
208		/* Check CRC only when buffer size is enough for table. */
209		if (hdr->hdr_crc_table !=
210		    crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) {
211			DEBUG("GPT table's CRC doesn't match");
212			return (-1);
213		}
214	}
215	ent = (struct gpt_ent *)tbl;
216	for (i = 0; i < cnt; i++, ent++) {
217		uuid_letoh(&ent->ent_type);
218		if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
219			continue;
220		ent->ent_lba_start = le64toh(ent->ent_lba_start);
221		ent->ent_lba_end = le64toh(ent->ent_lba_end);
222	}
223	return (0);
224}
225
226static struct ptable*
227ptable_gptread(struct ptable *table, void *dev, diskread_t dread)
228{
229	struct pentry *entry;
230	struct gpt_hdr *phdr, hdr;
231	struct gpt_ent *ent;
232	u_char *buf, *tbl;
233	uint64_t offset;
234	int pri, sec, i;
235	size_t size;
236
237	buf = malloc(table->sectorsize);
238	if (buf == NULL)
239		return (NULL);
240	tbl = malloc(table->sectorsize * MAXTBLSZ);
241	if (tbl == NULL) {
242		free(buf);
243		return (NULL);
244	}
245	/* Read the primary GPT header. */
246	if (dread(dev, buf, 1, 1) != 0) {
247		ptable_close(table);
248		table = NULL;
249		goto out;
250	}
251	pri = sec = 0;
252	/* Check the primary GPT header. */
253	phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1,
254	    table->sectorsize);
255	if (phdr != NULL) {
256		/* Read the primary GPT table. */
257		size = MIN(MAXTBLSZ,
258		    phdr->hdr_entries * phdr->hdr_entsz / table->sectorsize);
259		if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
260		    gpt_checktbl(phdr, tbl, size * table->sectorsize,
261		    table->sectors - 1) == 0) {
262			memcpy(&hdr, phdr, sizeof(hdr));
263			pri = 1;
264		}
265	}
266	offset = pri ? hdr.hdr_lba_alt: table->sectors - 1;
267	/* Read the backup GPT header. */
268	if (dread(dev, buf, 1, offset) != 0)
269		phdr = NULL;
270	else
271		phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset,
272		    table->sectors - 1, table->sectorsize);
273	if (phdr != NULL) {
274		/*
275		 * Compare primary and backup headers.
276		 * If they are equal, then we do not need to read backup
277		 * table. If they are different, then prefer backup header
278		 * and try to read backup table.
279		 */
280		if (pri == 0 ||
281		    uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 ||
282		    hdr.hdr_revision != phdr->hdr_revision ||
283		    hdr.hdr_size != phdr->hdr_size ||
284		    hdr.hdr_lba_start != phdr->hdr_lba_start ||
285		    hdr.hdr_lba_end != phdr->hdr_lba_end ||
286		    hdr.hdr_entries != phdr->hdr_entries ||
287		    hdr.hdr_entsz != phdr->hdr_entsz ||
288		    hdr.hdr_crc_table != phdr->hdr_crc_table) {
289			/* Read the backup GPT table. */
290			size = MIN(MAXTBLSZ, phdr->hdr_entries *
291			    phdr->hdr_entsz / table->sectorsize);
292			if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
293			    gpt_checktbl(phdr, tbl, size * table->sectorsize,
294			    table->sectors - 1) == 0) {
295				memcpy(&hdr, phdr, sizeof(hdr));
296				sec = 1;
297			}
298		}
299	}
300	if (pri == 0 && sec == 0) {
301		/* Both primary and backup tables are invalid. */
302		table->type = PTABLE_NONE;
303		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			goto out;
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 (table->type == PTABLE_GPT && count > 1) {
650		if (dp[1].dp_typ != DOSPTYP_HFS) {
651			table->type = PTABLE_NONE;
652			DEBUG("Incorrect PMBR, ignore it");
653		} else
654			DEBUG("Bootcamp detected");
655	}
656#ifdef LOADER_GPT_SUPPORT
657	if (table->type == PTABLE_GPT) {
658		table = ptable_gptread(table, dev, dread);
659		goto out;
660	}
661#endif
662#ifdef LOADER_MBR_SUPPORT
663	/* Read MBR. */
664	table->type = PTABLE_MBR;
665	for (i = has_ext = 0; i < NDOSPART; i++) {
666		if (dp[i].dp_typ == 0)
667			continue;
668		start = le32dec(&(dp[i].dp_start));
669		end = le32dec(&(dp[i].dp_size));
670		if (start == 0 || end == 0)
671			continue;
672#if 0	/* Some BIOSes return an incorrect number of sectors */
673		if (start + end - 1 >= sectors)
674			continue;	/* XXX: ignore */
675#endif
676		if (dp[i].dp_typ == DOSPTYP_EXT ||
677		    dp[i].dp_typ == DOSPTYP_EXTLBA)
678			has_ext = 1;
679		entry = malloc(sizeof(*entry));
680		if (entry == NULL)
681			break;
682		entry->part.start = start;
683		entry->part.end = start + end - 1;
684		entry->part.index = i + 1;
685		entry->part.type = mbr_parttype(dp[i].dp_typ);
686		entry->flags = dp[i].dp_flag;
687		entry->type.mbr = dp[i].dp_typ;
688		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
689		DEBUG("new MBR partition added");
690	}
691	if (has_ext) {
692		table = ptable_ebrread(table, dev, dread);
693		/* FALLTHROUGH */
694	}
695#endif /* LOADER_MBR_SUPPORT */
696#endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
697out:
698	free(buf);
699	return (table);
700}
701
702void
703ptable_close(struct ptable *table)
704{
705	struct pentry *entry;
706
707	while (!STAILQ_EMPTY(&table->entries)) {
708		entry = STAILQ_FIRST(&table->entries);
709		STAILQ_REMOVE_HEAD(&table->entries, entry);
710		free(entry);
711	}
712	free(table);
713}
714
715enum ptable_type
716ptable_gettype(const struct ptable *table)
717{
718
719	return (table->type);
720}
721
722int
723ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index)
724{
725	struct pentry *entry;
726
727	if (part == NULL || table == NULL)
728		return (EINVAL);
729
730	STAILQ_FOREACH(entry, &table->entries, entry) {
731		if (entry->part.index != index)
732			continue;
733		memcpy(part, &entry->part, sizeof(*part));
734		return (0);
735	}
736	return (ENOENT);
737}
738
739/*
740 * Search for a slice with the following preferences:
741 *
742 * 1: Active FreeBSD slice
743 * 2: Non-active FreeBSD slice
744 * 3: Active Linux slice
745 * 4: non-active Linux slice
746 * 5: Active FAT/FAT32 slice
747 * 6: non-active FAT/FAT32 slice
748 */
749#define PREF_RAWDISK	0
750#define PREF_FBSD_ACT	1
751#define PREF_FBSD	2
752#define PREF_LINUX_ACT	3
753#define PREF_LINUX	4
754#define PREF_DOS_ACT	5
755#define PREF_DOS	6
756#define PREF_NONE	7
757int
758ptable_getbestpart(const struct ptable *table, struct ptable_entry *part)
759{
760	struct pentry *entry, *best;
761	int pref, preflevel;
762
763	if (part == NULL || table == NULL)
764		return (EINVAL);
765
766	best = NULL;
767	preflevel = pref = PREF_NONE;
768	STAILQ_FOREACH(entry, &table->entries, entry) {
769#ifdef LOADER_MBR_SUPPORT
770		if (table->type == PTABLE_MBR) {
771			switch (entry->type.mbr) {
772			case DOSPTYP_386BSD:
773				pref = entry->flags & 0x80 ? PREF_FBSD_ACT:
774				    PREF_FBSD;
775				break;
776			case DOSPTYP_LINUX:
777				pref = entry->flags & 0x80 ? PREF_LINUX_ACT:
778				    PREF_LINUX;
779				break;
780			case 0x01:		/* DOS/Windows */
781			case 0x04:
782			case 0x06:
783			case 0x0c:
784			case 0x0e:
785			case DOSPTYP_FAT32:
786				pref = entry->flags & 0x80 ? PREF_DOS_ACT:
787				    PREF_DOS;
788				break;
789			default:
790				pref = PREF_NONE;
791			}
792		}
793#endif /* LOADER_MBR_SUPPORT */
794#ifdef LOADER_GPT_SUPPORT
795		if (table->type == PTABLE_GPT) {
796			if (entry->part.type == PART_DOS)
797				pref = PREF_DOS;
798			else if (entry->part.type == PART_FREEBSD_UFS ||
799			    entry->part.type == PART_FREEBSD_ZFS)
800				pref = PREF_FBSD;
801			else
802				pref = PREF_NONE;
803		}
804#endif /* LOADER_GPT_SUPPORT */
805		if (pref < preflevel) {
806			preflevel = pref;
807			best = entry;
808		}
809	}
810	if (best != NULL) {
811		memcpy(part, &best->part, sizeof(*part));
812		return (0);
813	}
814	return (ENOENT);
815}
816
817void
818ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter)
819{
820	struct pentry *entry;
821	char name[32];
822
823	name[0] = '\0';
824	STAILQ_FOREACH(entry, &table->entries, entry) {
825#ifdef LOADER_MBR_SUPPORT
826		if (table->type == PTABLE_MBR)
827			sprintf(name, "s%d", entry->part.index);
828		else
829#endif
830#ifdef LOADER_GPT_SUPPORT
831		if (table->type == PTABLE_GPT)
832			sprintf(name, "p%d", entry->part.index);
833		else
834#endif
835#ifdef LOADER_VTOC8_SUPPORT
836		if (table->type == PTABLE_VTOC8)
837			sprintf(name, "%c", (u_char) 'a' +
838			    entry->part.index);
839		else
840#endif
841		if (table->type == PTABLE_BSD)
842			sprintf(name, "%c", (u_char) 'a' +
843			    entry->part.index);
844		iter(arg, name, &entry->part);
845	}
846}
847
848