xref: /illumos-gate/usr/src/boot/common/part.c (revision 22028508)
1686ab1b2SToomas Soome /*
2199767f8SToomas Soome  * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
3199767f8SToomas Soome  * All rights reserved.
4199767f8SToomas Soome  *
5199767f8SToomas Soome  * Redistribution and use in source and binary forms, with or without
6199767f8SToomas Soome  * modification, are permitted provided that the following conditions
7199767f8SToomas Soome  * are met:
8199767f8SToomas Soome  * 1. Redistributions of source code must retain the above copyright
9199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer.
10199767f8SToomas Soome  * 2. Redistributions in binary form must reproduce the above copyright
11199767f8SToomas Soome  *    notice, this list of conditions and the following disclaimer in the
12199767f8SToomas Soome  *    documentation and/or other materials provided with the distribution.
13199767f8SToomas Soome  *
14199767f8SToomas Soome  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15199767f8SToomas Soome  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16199767f8SToomas Soome  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17199767f8SToomas Soome  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18199767f8SToomas Soome  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19199767f8SToomas Soome  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20199767f8SToomas Soome  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21199767f8SToomas Soome  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22199767f8SToomas Soome  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23199767f8SToomas Soome  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24199767f8SToomas Soome  * SUCH DAMAGE.
25199767f8SToomas Soome  */
26199767f8SToomas Soome 
27199767f8SToomas Soome #include <sys/cdefs.h>
28199767f8SToomas Soome 
29199767f8SToomas Soome #include <stand.h>
30172f4465SToomas Soome #include <stddef.h>
31199767f8SToomas Soome #include <sys/param.h>
32199767f8SToomas Soome #include <sys/diskmbr.h>
33199767f8SToomas Soome #include <sys/disklabel.h>
34199767f8SToomas Soome #include <sys/endian.h>
35199767f8SToomas Soome #include <sys/gpt.h>
36199767f8SToomas Soome #include <sys/queue.h>
37199767f8SToomas Soome #include <sys/vtoc.h>
38199767f8SToomas Soome 
39922a2a16SToomas Soome #include <fs/cd9660/iso.h>
40922a2a16SToomas Soome 
41388010daSToomas Soome #include <zlib.h>
42199767f8SToomas Soome #include <part.h>
43199767f8SToomas Soome #include <uuid.h>
44199767f8SToomas Soome 
45199767f8SToomas Soome #ifdef PART_DEBUG
467efc4ab5SToomas Soome #define	DPRINTF(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
47199767f8SToomas Soome #else
487efc4ab5SToomas Soome #define	DPRINTF(fmt, args...)	((void)0)
49199767f8SToomas Soome #endif
50199767f8SToomas Soome 
51199767f8SToomas Soome #ifdef LOADER_GPT_SUPPORT
52199767f8SToomas Soome #define	MAXTBLSZ	64
53199767f8SToomas Soome static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
54199767f8SToomas Soome static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
55199767f8SToomas Soome static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
56199767f8SToomas Soome static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI;
57199767f8SToomas Soome static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
58199767f8SToomas Soome static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
59199767f8SToomas Soome static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
60199767f8SToomas Soome static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
61199767f8SToomas Soome static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
62b93266daSToomas Soome static const uuid_t gpt_uuid_illumos_boot = GPT_ENT_TYPE_ILLUMOS_BOOT;
63199767f8SToomas Soome static const uuid_t gpt_uuid_illumos_ufs = GPT_ENT_TYPE_ILLUMOS_UFS;
64199767f8SToomas Soome static const uuid_t gpt_uuid_illumos_zfs = GPT_ENT_TYPE_ILLUMOS_ZFS;
65199767f8SToomas Soome static const uuid_t gpt_uuid_reserved = GPT_ENT_TYPE_RESERVED;
663423c61dSToomas Soome static const uuid_t gpt_uuid_apple_apfs = GPT_ENT_TYPE_APPLE_APFS;
67199767f8SToomas Soome #endif
68199767f8SToomas Soome 
69199767f8SToomas Soome struct pentry {
70199767f8SToomas Soome 	struct ptable_entry	part;
71199767f8SToomas Soome 	uint64_t		flags;
72199767f8SToomas Soome 	union {
73199767f8SToomas Soome 		uint8_t bsd;
74199767f8SToomas Soome 		uint8_t	mbr;
75199767f8SToomas Soome 		uuid_t	gpt;
76199767f8SToomas Soome 		uint16_t vtoc8;
77199767f8SToomas Soome 		uint16_t vtoc;
78199767f8SToomas Soome 	} type;
79199767f8SToomas Soome 	STAILQ_ENTRY(pentry)	entry;
80199767f8SToomas Soome };
81199767f8SToomas Soome 
82199767f8SToomas Soome struct ptable {
83199767f8SToomas Soome 	enum ptable_type	type;
84199767f8SToomas Soome 	uint16_t		sectorsize;
85199767f8SToomas Soome 	uint64_t		sectors;
86199767f8SToomas Soome 
87199767f8SToomas Soome 	STAILQ_HEAD(, pentry)	entries;
88199767f8SToomas Soome };
89199767f8SToomas Soome 
90199767f8SToomas Soome static struct parttypes {
91199767f8SToomas Soome 	enum partition_type	type;
92199767f8SToomas Soome 	const char		*desc;
93199767f8SToomas Soome } ptypes[] = {
94199767f8SToomas Soome 	{ PART_UNKNOWN,		"Unknown" },
95199767f8SToomas Soome 	{ PART_EFI,		"EFI" },
96199767f8SToomas Soome 	{ PART_FREEBSD,		"FreeBSD" },
97199767f8SToomas Soome 	{ PART_FREEBSD_BOOT,	"FreeBSD boot" },
98199767f8SToomas Soome 	{ PART_FREEBSD_UFS,	"FreeBSD UFS" },
99199767f8SToomas Soome 	{ PART_FREEBSD_ZFS,	"FreeBSD ZFS" },
100199767f8SToomas Soome 	{ PART_FREEBSD_SWAP,	"FreeBSD swap" },
101199767f8SToomas Soome 	{ PART_FREEBSD_VINUM,	"FreeBSD vinum" },
102199767f8SToomas Soome 	{ PART_LINUX,		"Linux" },
103199767f8SToomas Soome 	{ PART_LINUX_SWAP,	"Linux swap" },
104199767f8SToomas Soome 	{ PART_DOS,		"DOS/Windows" },
105922a2a16SToomas Soome 	{ PART_ISO9660,		"ISO9660" },
106199767f8SToomas Soome 	{ PART_SOLARIS2,	"Solaris 2" },
107199767f8SToomas Soome 	{ PART_ILLUMOS_UFS,	"illumos UFS" },
108199767f8SToomas Soome 	{ PART_ILLUMOS_ZFS,	"illumos ZFS" },
109199767f8SToomas Soome 	{ PART_RESERVED,	"Reserved" },
110199767f8SToomas Soome 	{ PART_VTOC_BOOT,	"boot" },
111199767f8SToomas Soome 	{ PART_VTOC_ROOT,	"root" },
112199767f8SToomas Soome 	{ PART_VTOC_SWAP,	"swap" },
113199767f8SToomas Soome 	{ PART_VTOC_USR,	"usr" },
114199767f8SToomas Soome 	{ PART_VTOC_STAND,	"stand" },
115199767f8SToomas Soome 	{ PART_VTOC_VAR,	"var" },
1163423c61dSToomas Soome 	{ PART_VTOC_HOME,	"home" },
1173423c61dSToomas Soome 	{ PART_APFS,		"APFS" }
118199767f8SToomas Soome };
119199767f8SToomas Soome 
120199767f8SToomas Soome const char *
parttype2str(enum partition_type type)121199767f8SToomas Soome parttype2str(enum partition_type type)
122199767f8SToomas Soome {
123199767f8SToomas Soome 	size_t i;
124199767f8SToomas Soome 
1253867c045SToomas Soome 	for (i = 0; i < nitems(ptypes); i++)
126199767f8SToomas Soome 		if (ptypes[i].type == type)
127199767f8SToomas Soome 			return (ptypes[i].desc);
128199767f8SToomas Soome 	return (ptypes[0].desc);
129199767f8SToomas Soome }
130199767f8SToomas Soome 
131199767f8SToomas Soome #ifdef LOADER_GPT_SUPPORT
132199767f8SToomas Soome static void
uuid_letoh(uuid_t * uuid)133199767f8SToomas Soome uuid_letoh(uuid_t *uuid)
134199767f8SToomas Soome {
135199767f8SToomas Soome 
136199767f8SToomas Soome 	uuid->time_low = le32toh(uuid->time_low);
137199767f8SToomas Soome 	uuid->time_mid = le16toh(uuid->time_mid);
138199767f8SToomas Soome 	uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version);
139199767f8SToomas Soome }
140199767f8SToomas Soome 
141199767f8SToomas Soome static enum partition_type
gpt_parttype(uuid_t type)142199767f8SToomas Soome gpt_parttype(uuid_t type)
143199767f8SToomas Soome {
144199767f8SToomas Soome 
145199767f8SToomas Soome 	if (uuid_equal(&type, &gpt_uuid_efi, NULL))
146199767f8SToomas Soome 		return (PART_EFI);
147199767f8SToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL))
148199767f8SToomas Soome 		return (PART_DOS);
149199767f8SToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL))
150199767f8SToomas Soome 		return (PART_FREEBSD_BOOT);
151199767f8SToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL))
152199767f8SToomas Soome 		return (PART_FREEBSD_UFS);
153199767f8SToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL))
154199767f8SToomas Soome 		return (PART_FREEBSD_ZFS);
155199767f8SToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL))
156199767f8SToomas Soome 		return (PART_FREEBSD_SWAP);
157199767f8SToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL))
158199767f8SToomas Soome 		return (PART_FREEBSD_VINUM);
159199767f8SToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL))
160199767f8SToomas Soome 		return (PART_FREEBSD);
161b93266daSToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_illumos_boot, NULL))
162b93266daSToomas Soome 		return (PART_VTOC_BOOT);
163199767f8SToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_illumos_ufs, NULL))
164199767f8SToomas Soome 		return (PART_ILLUMOS_UFS);
165199767f8SToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_illumos_zfs, NULL))
166199767f8SToomas Soome 		return (PART_ILLUMOS_ZFS);
167199767f8SToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_reserved, NULL))
168199767f8SToomas Soome 		return (PART_RESERVED);
1693423c61dSToomas Soome 	else if (uuid_equal(&type, &gpt_uuid_apple_apfs, NULL))
1703423c61dSToomas Soome 		return (PART_APFS);
171199767f8SToomas Soome 	return (PART_UNKNOWN);
172199767f8SToomas Soome }
173199767f8SToomas Soome 
174686ab1b2SToomas Soome static struct gpt_hdr *
gpt_checkhdr(struct gpt_hdr * hdr,uint64_t lba_self,uint64_t lba_last __attribute ((unused)),uint16_t sectorsize)175199767f8SToomas Soome gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self,
176199767f8SToomas Soome     uint64_t lba_last __attribute((unused)), uint16_t sectorsize)
177199767f8SToomas Soome {
178199767f8SToomas Soome 	uint32_t sz, crc;
179199767f8SToomas Soome 
180686ab1b2SToomas Soome 	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof (hdr->hdr_sig)) != 0) {
1817efc4ab5SToomas Soome 		DPRINTF("no GPT signature");
182199767f8SToomas Soome 		return (NULL);
183199767f8SToomas Soome 	}
184199767f8SToomas Soome 	sz = le32toh(hdr->hdr_size);
185199767f8SToomas Soome 	if (sz < 92 || sz > sectorsize) {
1867efc4ab5SToomas Soome 		DPRINTF("invalid GPT header size: %u", sz);
187199767f8SToomas Soome 		return (NULL);
188199767f8SToomas Soome 	}
189199767f8SToomas Soome 	crc = le32toh(hdr->hdr_crc_self);
190388010daSToomas Soome 	hdr->hdr_crc_self = crc32(0, Z_NULL, 0);
191388010daSToomas Soome 	if (crc32(hdr->hdr_crc_self, (const Bytef *)hdr, sz) != crc) {
1927efc4ab5SToomas Soome 		DPRINTF("GPT header's CRC doesn't match");
193199767f8SToomas Soome 		return (NULL);
194199767f8SToomas Soome 	}
195199767f8SToomas Soome 	hdr->hdr_crc_self = crc;
196199767f8SToomas Soome 	hdr->hdr_revision = le32toh(hdr->hdr_revision);
197199767f8SToomas Soome 	if (hdr->hdr_revision < GPT_HDR_REVISION) {
1987efc4ab5SToomas Soome 		DPRINTF("unsupported GPT revision %u", hdr->hdr_revision);
199199767f8SToomas Soome 		return (NULL);
200199767f8SToomas Soome 	}
201199767f8SToomas Soome 	hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
202199767f8SToomas Soome 	if (hdr->hdr_lba_self != lba_self) {
2037efc4ab5SToomas Soome 		DPRINTF("self LBA doesn't match");
204199767f8SToomas Soome 		return (NULL);
205199767f8SToomas Soome 	}
206199767f8SToomas Soome 	hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
207199767f8SToomas Soome 	if (hdr->hdr_lba_alt == hdr->hdr_lba_self) {
2087efc4ab5SToomas Soome 		DPRINTF("invalid alternate LBA");
209199767f8SToomas Soome 		return (NULL);
210199767f8SToomas Soome 	}
211199767f8SToomas Soome 	hdr->hdr_entries = le32toh(hdr->hdr_entries);
212199767f8SToomas Soome 	hdr->hdr_entsz = le32toh(hdr->hdr_entsz);
213199767f8SToomas Soome 	if (hdr->hdr_entries == 0 ||
214686ab1b2SToomas Soome 	    hdr->hdr_entsz < sizeof (struct gpt_ent) ||
215199767f8SToomas Soome 	    sectorsize % hdr->hdr_entsz != 0) {
2167efc4ab5SToomas Soome 		DPRINTF("invalid entry size or number of entries");
217199767f8SToomas Soome 		return (NULL);
218199767f8SToomas Soome 	}
219199767f8SToomas Soome 	hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
220199767f8SToomas Soome 	hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end);
221199767f8SToomas Soome 	hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table);
222199767f8SToomas Soome 	hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table);
223199767f8SToomas Soome 	uuid_letoh(&hdr->hdr_uuid);
224199767f8SToomas Soome 	return (hdr);
225199767f8SToomas Soome }
226199767f8SToomas Soome 
227199767f8SToomas Soome static int
gpt_checktbl(const struct gpt_hdr * hdr,uint8_t * tbl,size_t size,uint64_t lba_last __attribute ((unused)))228686ab1b2SToomas Soome gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, size_t size,
229199767f8SToomas Soome     uint64_t lba_last __attribute((unused)))
230199767f8SToomas Soome {
231199767f8SToomas Soome 	struct gpt_ent *ent;
232199767f8SToomas Soome 	uint32_t i, cnt;
233199767f8SToomas Soome 
234199767f8SToomas Soome 	cnt = size / hdr->hdr_entsz;
235199767f8SToomas Soome 	if (hdr->hdr_entries <= cnt) {
236199767f8SToomas Soome 		cnt = hdr->hdr_entries;
237199767f8SToomas Soome 		/* Check CRC only when buffer size is enough for table. */
238199767f8SToomas Soome 		if (hdr->hdr_crc_table !=
239388010daSToomas Soome 		    crc32(0, tbl, hdr->hdr_entries * hdr->hdr_entsz)) {
2407efc4ab5SToomas Soome 			DPRINTF("GPT table's CRC doesn't match");
241199767f8SToomas Soome 			return (-1);
242199767f8SToomas Soome 		}
243199767f8SToomas Soome 	}
244199767f8SToomas Soome 	for (i = 0; i < cnt; i++) {
245199767f8SToomas Soome 		ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
246199767f8SToomas Soome 		uuid_letoh(&ent->ent_type);
247199767f8SToomas Soome 		if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
248199767f8SToomas Soome 			continue;
249199767f8SToomas Soome 		ent->ent_lba_start = le64toh(ent->ent_lba_start);
250199767f8SToomas Soome 		ent->ent_lba_end = le64toh(ent->ent_lba_end);
251199767f8SToomas Soome 	}
252199767f8SToomas Soome 	return (0);
253199767f8SToomas Soome }
254199767f8SToomas Soome 
255686ab1b2SToomas Soome static struct ptable *
ptable_gptread(struct ptable * table,void * dev,diskread_t dread)256199767f8SToomas Soome ptable_gptread(struct ptable *table, void *dev, diskread_t dread)
257199767f8SToomas Soome {
258199767f8SToomas Soome 	struct pentry *entry;
259199767f8SToomas Soome 	struct gpt_hdr *phdr, hdr;
260199767f8SToomas Soome 	struct gpt_ent *ent;
261686ab1b2SToomas Soome 	uint8_t *buf, *tbl;
262199767f8SToomas Soome 	uint64_t offset;
263199767f8SToomas Soome 	int pri, sec;
264199767f8SToomas Soome 	size_t size, i;
265199767f8SToomas Soome 
266199767f8SToomas Soome 	buf = malloc(table->sectorsize);
267199767f8SToomas Soome 	if (buf == NULL)
268199767f8SToomas Soome 		return (NULL);
269199767f8SToomas Soome 	tbl = malloc(table->sectorsize * MAXTBLSZ);
270199767f8SToomas Soome 	if (tbl == NULL) {
271199767f8SToomas Soome 		free(buf);
272199767f8SToomas Soome 		return (NULL);
273199767f8SToomas Soome 	}
274199767f8SToomas Soome 	/* Read the primary GPT header. */
275199767f8SToomas Soome 	if (dread(dev, buf, 1, 1) != 0) {
276199767f8SToomas Soome 		ptable_close(table);
277199767f8SToomas Soome 		table = NULL;
278199767f8SToomas Soome 		goto out;
279199767f8SToomas Soome 	}
280199767f8SToomas Soome 	pri = sec = 0;
281199767f8SToomas Soome 	/* Check the primary GPT header. */
282199767f8SToomas Soome 	phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1,
283199767f8SToomas Soome 	    table->sectorsize);
284199767f8SToomas Soome 	if (phdr != NULL) {
285199767f8SToomas Soome 		/* Read the primary GPT table. */
286199767f8SToomas Soome 		size = MIN(MAXTBLSZ, (phdr->hdr_entries * phdr->hdr_entsz +
287199767f8SToomas Soome 		    table->sectorsize - 1) / table->sectorsize);
288199767f8SToomas Soome 		if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
289199767f8SToomas Soome 		    gpt_checktbl(phdr, tbl, size * table->sectorsize,
290199767f8SToomas Soome 		    table->sectors - 1) == 0) {
291686ab1b2SToomas Soome 			memcpy(&hdr, phdr, sizeof (hdr));
292199767f8SToomas Soome 			pri = 1;
293199767f8SToomas Soome 		}
294199767f8SToomas Soome 	}
295199767f8SToomas Soome 	offset = pri ? hdr.hdr_lba_alt: table->sectors - 1;
296199767f8SToomas Soome 	/* Read the backup GPT header. */
297199767f8SToomas Soome 	if (dread(dev, buf, 1, offset) != 0)
298199767f8SToomas Soome 		phdr = NULL;
299199767f8SToomas Soome 	else
300199767f8SToomas Soome 		phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset,
301199767f8SToomas Soome 		    table->sectors - 1, table->sectorsize);
302199767f8SToomas Soome 	if (phdr != NULL) {
303199767f8SToomas Soome 		/*
304199767f8SToomas Soome 		 * Compare primary and backup headers.
305199767f8SToomas Soome 		 * If they are equal, then we do not need to read backup
306199767f8SToomas Soome 		 * table. If they are different, then prefer backup header
307199767f8SToomas Soome 		 * and try to read backup table.
308199767f8SToomas Soome 		 */
309199767f8SToomas Soome 		if (pri == 0 ||
310199767f8SToomas Soome 		    uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 ||
311199767f8SToomas Soome 		    hdr.hdr_revision != phdr->hdr_revision ||
312199767f8SToomas Soome 		    hdr.hdr_size != phdr->hdr_size ||
313199767f8SToomas Soome 		    hdr.hdr_lba_start != phdr->hdr_lba_start ||
314199767f8SToomas Soome 		    hdr.hdr_lba_end != phdr->hdr_lba_end ||
315199767f8SToomas Soome 		    hdr.hdr_entries != phdr->hdr_entries ||
316199767f8SToomas Soome 		    hdr.hdr_entsz != phdr->hdr_entsz ||
317199767f8SToomas Soome 		    hdr.hdr_crc_table != phdr->hdr_crc_table) {
318199767f8SToomas Soome 			/* Read the backup GPT table. */
319199767f8SToomas Soome 			size = MIN(MAXTBLSZ, (phdr->hdr_entries *
320199767f8SToomas Soome 			    phdr->hdr_entsz + table->sectorsize - 1) /
321199767f8SToomas Soome 			    table->sectorsize);
322199767f8SToomas Soome 			if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
323199767f8SToomas Soome 			    gpt_checktbl(phdr, tbl, size * table->sectorsize,
324199767f8SToomas Soome 			    table->sectors - 1) == 0) {
325686ab1b2SToomas Soome 				memcpy(&hdr, phdr, sizeof (hdr));
326199767f8SToomas Soome 				sec = 1;
327199767f8SToomas Soome 			}
328199767f8SToomas Soome 		}
329199767f8SToomas Soome 	}
330199767f8SToomas Soome 	if (pri == 0 && sec == 0) {
331199767f8SToomas Soome 		/* Both primary and backup tables are invalid. */
332199767f8SToomas Soome 		table->type = PTABLE_NONE;
333199767f8SToomas Soome 		goto out;
334199767f8SToomas Soome 	}
3357efc4ab5SToomas Soome 	DPRINTF("GPT detected");
336199767f8SToomas Soome 	size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
337199767f8SToomas Soome 	    MAXTBLSZ * table->sectorsize);
338252244c3SToomas Soome 
339252244c3SToomas Soome 	/*
340252244c3SToomas Soome 	 * If the disk's sector count is smaller than the sector count recorded
341252244c3SToomas Soome 	 * in the disk's GPT table header, set the table->sectors to the value
342252244c3SToomas Soome 	 * recorded in GPT tables. This is done to work around buggy firmware
343252244c3SToomas Soome 	 * that returns truncated disk sizes.
344252244c3SToomas Soome 	 *
345252244c3SToomas Soome 	 * Note, this is still not a foolproof way to get disk's size. For
346252244c3SToomas Soome 	 * example, an image file can be truncated when copied to smaller media.
347252244c3SToomas Soome 	 */
348124619e5SToomas Soome 	table->sectors = hdr.hdr_lba_alt + 1;
349252244c3SToomas Soome 
350199767f8SToomas Soome 	for (i = 0; i < size / hdr.hdr_entsz; i++) {
351199767f8SToomas Soome 		ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz);
352199767f8SToomas Soome 		if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
353199767f8SToomas Soome 			continue;
354252244c3SToomas Soome 
355252244c3SToomas Soome 		/* Simple sanity checks. */
356252244c3SToomas Soome 		if (ent->ent_lba_start < hdr.hdr_lba_start ||
357252244c3SToomas Soome 		    ent->ent_lba_end > hdr.hdr_lba_end ||
358252244c3SToomas Soome 		    ent->ent_lba_start > ent->ent_lba_end)
359252244c3SToomas Soome 			continue;
360252244c3SToomas Soome 
361686ab1b2SToomas Soome 		entry = malloc(sizeof (*entry));
362199767f8SToomas Soome 		if (entry == NULL)
363199767f8SToomas Soome 			break;
364199767f8SToomas Soome 		entry->part.start = ent->ent_lba_start;
365199767f8SToomas Soome 		entry->part.end = ent->ent_lba_end;
366199767f8SToomas Soome 		entry->part.index = i + 1;
367199767f8SToomas Soome 		entry->part.type = gpt_parttype(ent->ent_type);
368199767f8SToomas Soome 		entry->flags = le64toh(ent->ent_attr);
369686ab1b2SToomas Soome 		memcpy(&entry->type.gpt, &ent->ent_type, sizeof (uuid_t));
370199767f8SToomas Soome 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
3717efc4ab5SToomas Soome 		DPRINTF("new GPT partition added");
372199767f8SToomas Soome 	}
373199767f8SToomas Soome out:
374199767f8SToomas Soome 	free(buf);
375199767f8SToomas Soome 	free(tbl);
376199767f8SToomas Soome 	return (table);
377199767f8SToomas Soome }
378199767f8SToomas Soome #endif /* LOADER_GPT_SUPPORT */
379199767f8SToomas Soome 
380199767f8SToomas Soome #ifdef LOADER_MBR_SUPPORT
381199767f8SToomas Soome /* We do not need to support too many EBR partitions in the loader */
382199767f8SToomas Soome #define	MAXEBRENTRIES		8
383199767f8SToomas Soome static enum partition_type
mbr_parttype(uint8_t type)384199767f8SToomas Soome mbr_parttype(uint8_t type)
385199767f8SToomas Soome {
386199767f8SToomas Soome 
387199767f8SToomas Soome 	switch (type) {
388199767f8SToomas Soome 	case DOSPTYP_386BSD:
389199767f8SToomas Soome 		return (PART_FREEBSD);
390199767f8SToomas Soome 	case DOSPTYP_LINSWP:
391199767f8SToomas Soome 		return (PART_LINUX_SWAP);
392199767f8SToomas Soome 	case DOSPTYP_LINUX:
393199767f8SToomas Soome 		return (PART_LINUX);
394199767f8SToomas Soome 	case DOSPTYP_SUNIXOS2:
395199767f8SToomas Soome 		return (PART_SOLARIS2);
396199767f8SToomas Soome 	case 0x01:
397199767f8SToomas Soome 	case 0x04:
398199767f8SToomas Soome 	case 0x06:
399199767f8SToomas Soome 	case 0x07:
400199767f8SToomas Soome 	case 0x0b:
401199767f8SToomas Soome 	case 0x0c:
402199767f8SToomas Soome 	case 0x0e:
403199767f8SToomas Soome 		return (PART_DOS);
404199767f8SToomas Soome 	}
405199767f8SToomas Soome 	return (PART_UNKNOWN);
406199767f8SToomas Soome }
407199767f8SToomas Soome 
408686ab1b2SToomas Soome static struct ptable *
ptable_ebrread(struct ptable * table,void * dev,diskread_t dread)409199767f8SToomas Soome ptable_ebrread(struct ptable *table, void *dev, diskread_t dread)
410199767f8SToomas Soome {
411199767f8SToomas Soome 	struct dos_partition *dp;
412199767f8SToomas Soome 	struct pentry *e1, *entry;
413199767f8SToomas Soome 	uint32_t start, end, offset;
414686ab1b2SToomas Soome 	uint8_t *buf;
415199767f8SToomas Soome 	int i, idx;
416199767f8SToomas Soome 
417199767f8SToomas Soome 	STAILQ_FOREACH(e1, &table->entries, entry) {
418199767f8SToomas Soome 		if (e1->type.mbr == DOSPTYP_EXT ||
419199767f8SToomas Soome 		    e1->type.mbr == DOSPTYP_EXTLBA)
420199767f8SToomas Soome 			break;
421199767f8SToomas Soome 	}
422199767f8SToomas Soome 	if (e1 == NULL)
423199767f8SToomas Soome 		return (table);
424199767f8SToomas Soome 	idx = 5;
425199767f8SToomas Soome 	offset = e1->part.start;
426199767f8SToomas Soome 	buf = malloc(table->sectorsize);
427199767f8SToomas Soome 	if (buf == NULL)
428199767f8SToomas Soome 		return (table);
4297efc4ab5SToomas Soome 	DPRINTF("EBR detected");
430199767f8SToomas Soome 	for (i = 0; i < MAXEBRENTRIES; i++) {
431199767f8SToomas Soome #if 0	/* Some BIOSes return an incorrect number of sectors */
432199767f8SToomas Soome 		if (offset >= table->sectors)
433199767f8SToomas Soome 			break;
434199767f8SToomas Soome #endif
435199767f8SToomas Soome 		if (dread(dev, buf, 1, offset) != 0)
436199767f8SToomas Soome 			break;
437199767f8SToomas Soome 		dp = (struct dos_partition *)(buf + DOSPARTOFF);
438199767f8SToomas Soome 		if (dp[0].dp_typ == 0)
439199767f8SToomas Soome 			break;
440199767f8SToomas Soome 		start = le32toh(dp[0].dp_start);
441199767f8SToomas Soome 		if (dp[0].dp_typ == DOSPTYP_EXT &&
442199767f8SToomas Soome 		    dp[1].dp_typ == 0) {
443199767f8SToomas Soome 			offset = e1->part.start + start;
444199767f8SToomas Soome 			continue;
445199767f8SToomas Soome 		}
446199767f8SToomas Soome 		end = le32toh(dp[0].dp_size);
447686ab1b2SToomas Soome 		entry = malloc(sizeof (*entry));
448199767f8SToomas Soome 		if (entry == NULL)
449199767f8SToomas Soome 			break;
450199767f8SToomas Soome 		entry->part.start = offset + start;
451199767f8SToomas Soome 		entry->part.end = entry->part.start + end - 1;
452199767f8SToomas Soome 		entry->part.index = idx++;
453199767f8SToomas Soome 		entry->part.type = mbr_parttype(dp[0].dp_typ);
454199767f8SToomas Soome 		entry->flags = dp[0].dp_flag;
455199767f8SToomas Soome 		entry->type.mbr = dp[0].dp_typ;
456199767f8SToomas Soome 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
4577efc4ab5SToomas Soome 		DPRINTF("new EBR partition added");
458199767f8SToomas Soome 		if (dp[1].dp_typ == 0)
459199767f8SToomas Soome 			break;
460199767f8SToomas Soome 		offset = e1->part.start + le32toh(dp[1].dp_start);
461199767f8SToomas Soome 	}
462199767f8SToomas Soome 	free(buf);
463199767f8SToomas Soome 	return (table);
464199767f8SToomas Soome }
465199767f8SToomas Soome #endif /* LOADER_MBR_SUPPORT */
466199767f8SToomas Soome 
467199767f8SToomas Soome static enum partition_type
bsd_parttype(uint8_t type)468199767f8SToomas Soome bsd_parttype(uint8_t type)
469199767f8SToomas Soome {
470199767f8SToomas Soome 
471199767f8SToomas Soome 	switch (type) {
472199767f8SToomas Soome 	case FS_SWAP:
473199767f8SToomas Soome 		return (PART_FREEBSD_SWAP);
474199767f8SToomas Soome 	case FS_BSDFFS:
475199767f8SToomas Soome 		return (PART_FREEBSD_UFS);
476199767f8SToomas Soome 	case FS_VINUM:
477199767f8SToomas Soome 		return (PART_FREEBSD_VINUM);
478199767f8SToomas Soome 	case FS_ZFS:
479199767f8SToomas Soome 		return (PART_FREEBSD_ZFS);
480199767f8SToomas Soome 	}
481199767f8SToomas Soome 	return (PART_UNKNOWN);
482199767f8SToomas Soome }
483199767f8SToomas Soome 
484686ab1b2SToomas Soome static struct ptable *
ptable_bsdread(struct ptable * table,void * dev,diskread_t dread)485199767f8SToomas Soome ptable_bsdread(struct ptable *table, void *dev, diskread_t dread)
486199767f8SToomas Soome {
487199767f8SToomas Soome 	struct disklabel *dl;
488199767f8SToomas Soome 	struct partition *part;
489199767f8SToomas Soome 	struct pentry *entry;
490686ab1b2SToomas Soome 	uint8_t *buf;
491199767f8SToomas Soome 	uint32_t raw_offset;
492199767f8SToomas Soome 	int i;
493199767f8SToomas Soome 
494686ab1b2SToomas Soome 	if (table->sectorsize < sizeof (struct disklabel)) {
4957efc4ab5SToomas Soome 		DPRINTF("Too small sectorsize");
496199767f8SToomas Soome 		return (table);
497199767f8SToomas Soome 	}
498199767f8SToomas Soome 	buf = malloc(table->sectorsize);
499199767f8SToomas Soome 	if (buf == NULL)
500199767f8SToomas Soome 		return (table);
501199767f8SToomas Soome 	if (dread(dev, buf, 1, 1) != 0) {
5027efc4ab5SToomas Soome 		DPRINTF("read failed");
503199767f8SToomas Soome 		ptable_close(table);
504199767f8SToomas Soome 		table = NULL;
505199767f8SToomas Soome 		goto out;
506199767f8SToomas Soome 	}
507199767f8SToomas Soome 	dl = (struct disklabel *)buf;
508199767f8SToomas Soome 	if (le32toh(dl->d_magic) != DISKMAGIC &&
509199767f8SToomas Soome 	    le32toh(dl->d_magic2) != DISKMAGIC)
510199767f8SToomas Soome 		goto out;
511199767f8SToomas Soome 	if (le32toh(dl->d_secsize) != table->sectorsize) {
5127efc4ab5SToomas Soome 		DPRINTF("unsupported sector size");
513199767f8SToomas Soome 		goto out;
514199767f8SToomas Soome 	}
515199767f8SToomas Soome 	dl->d_npartitions = le16toh(dl->d_npartitions);
516199767f8SToomas Soome 	if (dl->d_npartitions > 20 || dl->d_npartitions < 8) {
5177efc4ab5SToomas Soome 		DPRINTF("invalid number of partitions");
518199767f8SToomas Soome 		goto out;
519199767f8SToomas Soome 	}
5207efc4ab5SToomas Soome 	DPRINTF("BSD detected");
521199767f8SToomas Soome 	part = &dl->d_partitions[0];
522199767f8SToomas Soome 	raw_offset = le32toh(part[RAW_PART].p_offset);
523199767f8SToomas Soome 	for (i = 0; i < dl->d_npartitions; i++, part++) {
524199767f8SToomas Soome 		if (i == RAW_PART)
525199767f8SToomas Soome 			continue;
526199767f8SToomas Soome 		if (part->p_size == 0)
527199767f8SToomas Soome 			continue;
528686ab1b2SToomas Soome 		entry = malloc(sizeof (*entry));
529199767f8SToomas Soome 		if (entry == NULL)
530199767f8SToomas Soome 			break;
531199767f8SToomas Soome 		entry->part.start = le32toh(part->p_offset) - raw_offset;
532199767f8SToomas Soome 		entry->part.end = entry->part.start +
53327b4c18aSToomas Soome 		    le32toh(part->p_size) - 1;
534199767f8SToomas Soome 		entry->part.type = bsd_parttype(part->p_fstype);
535199767f8SToomas Soome 		entry->part.index = i; /* starts from zero */
536199767f8SToomas Soome 		entry->type.bsd = part->p_fstype;
537199767f8SToomas Soome 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
5387efc4ab5SToomas Soome 		DPRINTF("new BSD partition added");
539199767f8SToomas Soome 	}
540199767f8SToomas Soome 	table->type = PTABLE_BSD;
541199767f8SToomas Soome out:
542199767f8SToomas Soome 	free(buf);
543199767f8SToomas Soome 	return (table);
544199767f8SToomas Soome }
545199767f8SToomas Soome 
546199767f8SToomas Soome #ifdef LOADER_VTOC8_SUPPORT
547199767f8SToomas Soome static enum partition_type
vtoc8_parttype(uint16_t type)548199767f8SToomas Soome vtoc8_parttype(uint16_t type)
549199767f8SToomas Soome {
550199767f8SToomas Soome 
551199767f8SToomas Soome 	switch (type) {
552199767f8SToomas Soome 	case VTOC_TAG_FREEBSD_SWAP:
553199767f8SToomas Soome 		return (PART_FREEBSD_SWAP);
554199767f8SToomas Soome 	case VTOC_TAG_FREEBSD_UFS:
555199767f8SToomas Soome 		return (PART_FREEBSD_UFS);
556199767f8SToomas Soome 	case VTOC_TAG_FREEBSD_VINUM:
557199767f8SToomas Soome 		return (PART_FREEBSD_VINUM);
558199767f8SToomas Soome 	case VTOC_TAG_FREEBSD_ZFS:
559199767f8SToomas Soome 		return (PART_FREEBSD_ZFS);
560199767f8SToomas Soome 	};
561199767f8SToomas Soome 	return (PART_UNKNOWN);
562199767f8SToomas Soome }
563199767f8SToomas Soome 
564686ab1b2SToomas Soome static struct ptable *
ptable_vtoc8read(struct ptable * table,void * dev,diskread_t dread)565199767f8SToomas Soome ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread)
566199767f8SToomas Soome {
567199767f8SToomas Soome 	struct pentry *entry;
568199767f8SToomas Soome 	struct vtoc8 *dl;
569686ab1b2SToomas Soome 	uint8_t *buf;
570199767f8SToomas Soome 	uint16_t sum, heads, sectors;
571199767f8SToomas Soome 	int i;
572199767f8SToomas Soome 
573686ab1b2SToomas Soome 	if (table->sectorsize != sizeof (struct vtoc8))
574199767f8SToomas Soome 		return (table);
575199767f8SToomas Soome 	buf = malloc(table->sectorsize);
576199767f8SToomas Soome 	if (buf == NULL)
577199767f8SToomas Soome 		return (table);
578199767f8SToomas Soome 	if (dread(dev, buf, 1, 0) != 0) {
5797efc4ab5SToomas Soome 		DPRINTF("read failed");
580199767f8SToomas Soome 		ptable_close(table);
581199767f8SToomas Soome 		table = NULL;
582199767f8SToomas Soome 		goto out;
583199767f8SToomas Soome 	}
584199767f8SToomas Soome 	dl = (struct vtoc8 *)buf;
585199767f8SToomas Soome 	/* Check the sum */
586686ab1b2SToomas Soome 	for (i = sum = 0; i < sizeof (struct vtoc8); i += sizeof (sum))
587199767f8SToomas Soome 		sum ^= be16dec(buf + i);
588199767f8SToomas Soome 	if (sum != 0) {
5897efc4ab5SToomas Soome 		DPRINTF("incorrect checksum");
590199767f8SToomas Soome 		goto out;
591199767f8SToomas Soome 	}
592199767f8SToomas Soome 	if (be16toh(dl->nparts) != VTOC8_NPARTS) {
5937efc4ab5SToomas Soome 		DPRINTF("invalid number of entries");
594199767f8SToomas Soome 		goto out;
595199767f8SToomas Soome 	}
596199767f8SToomas Soome 	sectors = be16toh(dl->nsecs);
597199767f8SToomas Soome 	heads = be16toh(dl->nheads);
598199767f8SToomas Soome 	if (sectors * heads == 0) {
5997efc4ab5SToomas Soome 		DPRINTF("invalid geometry");
600199767f8SToomas Soome 		goto out;
601199767f8SToomas Soome 	}
6027efc4ab5SToomas Soome 	DPRINTF("VTOC8 detected");
603199767f8SToomas Soome 	for (i = 0; i < VTOC8_NPARTS; i++) {
604199767f8SToomas Soome 		dl->part[i].tag = be16toh(dl->part[i].tag);
605199767f8SToomas Soome 		if (i == VTOC_RAW_PART ||
606199767f8SToomas Soome 		    dl->part[i].tag == VTOC_TAG_UNASSIGNED)
607199767f8SToomas Soome 			continue;
608686ab1b2SToomas Soome 		entry = malloc(sizeof (*entry));
609199767f8SToomas Soome 		if (entry == NULL)
610199767f8SToomas Soome 			break;
611199767f8SToomas Soome 		entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors;
612199767f8SToomas Soome 		entry->part.end = be32toh(dl->map[i].nblks) +
613199767f8SToomas Soome 		    entry->part.start - 1;
614199767f8SToomas Soome 		entry->part.type = vtoc8_parttype(dl->part[i].tag);
615199767f8SToomas Soome 		entry->part.index = i; /* starts from zero */
616199767f8SToomas Soome 		entry->type.vtoc8 = dl->part[i].tag;
617199767f8SToomas Soome 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
6187efc4ab5SToomas Soome 		DPRINTF("new VTOC8 partition added");
619199767f8SToomas Soome 	}
620199767f8SToomas Soome 	table->type = PTABLE_VTOC8;
621199767f8SToomas Soome out:
622199767f8SToomas Soome 	free(buf);
623199767f8SToomas Soome 	return (table);
624199767f8SToomas Soome 
625199767f8SToomas Soome }
626199767f8SToomas Soome #endif /* LOADER_VTOC8_SUPPORT */
627199767f8SToomas Soome 
628199767f8SToomas Soome static enum partition_type
vtoc_parttype(uint16_t type)629199767f8SToomas Soome vtoc_parttype(uint16_t type)
630199767f8SToomas Soome {
631199767f8SToomas Soome 	switch (type) {
632199767f8SToomas Soome 	case VTOC_TAG_BOOT:
633199767f8SToomas Soome 		return (PART_VTOC_BOOT);
634199767f8SToomas Soome 	case VTOC_TAG_ROOT:
635199767f8SToomas Soome 		return (PART_VTOC_ROOT);
636199767f8SToomas Soome 	case VTOC_TAG_SWAP:
637199767f8SToomas Soome 		return (PART_VTOC_SWAP);
638199767f8SToomas Soome 	case VTOC_TAG_USR:
639199767f8SToomas Soome 		return (PART_VTOC_USR);
640199767f8SToomas Soome 	case VTOC_TAG_BACKUP:
641199767f8SToomas Soome 		return (PART_VTOC_BACKUP);
642199767f8SToomas Soome 	case VTOC_TAG_STAND:
643199767f8SToomas Soome 		return (PART_VTOC_STAND);
644199767f8SToomas Soome 	case VTOC_TAG_VAR:
645199767f8SToomas Soome 		return (PART_VTOC_VAR);
646199767f8SToomas Soome 	case VTOC_TAG_HOME:
647199767f8SToomas Soome 		return (PART_VTOC_HOME);
648199767f8SToomas Soome 	};
649199767f8SToomas Soome 	return (PART_UNKNOWN);
650199767f8SToomas Soome }
651199767f8SToomas Soome 
652686ab1b2SToomas Soome static struct ptable *
ptable_dklabelread(struct ptable * table,void * dev,diskread_t dread)653199767f8SToomas Soome ptable_dklabelread(struct ptable *table, void *dev, diskread_t dread)
654199767f8SToomas Soome {
655199767f8SToomas Soome 	struct pentry *entry;
656199767f8SToomas Soome 	struct dk_label *dl;
657199767f8SToomas Soome 	struct dk_vtoc *dv;
658686ab1b2SToomas Soome 	uint8_t *buf;
659199767f8SToomas Soome 	int i;
660199767f8SToomas Soome 
661686ab1b2SToomas Soome 	if (table->sectorsize < sizeof (struct dk_label)) {
6627efc4ab5SToomas Soome 		DPRINTF("Too small sectorsize");
663199767f8SToomas Soome 		return (table);
664199767f8SToomas Soome 	}
665199767f8SToomas Soome 	buf = malloc(table->sectorsize);
666199767f8SToomas Soome 	if (buf == NULL)
667199767f8SToomas Soome 		return (table);
668199767f8SToomas Soome 	if (dread(dev, buf, 1, DK_LABEL_LOC) != 0) {
6697efc4ab5SToomas Soome 		DPRINTF("read failed");
670199767f8SToomas Soome 		ptable_close(table);
671199767f8SToomas Soome 		table = NULL;
672199767f8SToomas Soome 		goto out;
673199767f8SToomas Soome 	}
674199767f8SToomas Soome 	dl = (struct dk_label *)buf;
675199767f8SToomas Soome 	dv = (struct dk_vtoc *)&dl->dkl_vtoc;
676199767f8SToomas Soome 
677199767f8SToomas Soome 	if (dl->dkl_magic != VTOC_MAGIC) {
6787efc4ab5SToomas Soome 		DPRINTF("dk_label magic error");
679199767f8SToomas Soome 		goto out;
680199767f8SToomas Soome 	}
681199767f8SToomas Soome 	if (dv->v_sanity != VTOC_SANITY) {
6827efc4ab5SToomas Soome 		DPRINTF("this vtoc is not sane");
683199767f8SToomas Soome 		goto out;
684199767f8SToomas Soome 	}
685199767f8SToomas Soome 	if (dv->v_nparts != NDKMAP) {
6867efc4ab5SToomas Soome 		DPRINTF("invalid number of entries");
687199767f8SToomas Soome 		goto out;
688199767f8SToomas Soome 	}
6897efc4ab5SToomas Soome 	DPRINTF("VTOC detected");
690199767f8SToomas Soome 	for (i = 0; i < NDKMAP; i++) {
691199767f8SToomas Soome 		if (i == VTOC_RAW_PART ||	/* skip slice 2 and empty */
692199767f8SToomas Soome 		    dv->v_part[i].p_size == 0)
693199767f8SToomas Soome 			continue;
694686ab1b2SToomas Soome 		entry = malloc(sizeof (*entry));
695199767f8SToomas Soome 		if (entry == NULL)
696199767f8SToomas Soome 			break;
697199767f8SToomas Soome 		entry->part.start = dv->v_part[i].p_start;
698199767f8SToomas Soome 		entry->part.end = dv->v_part[i].p_size +
699c5cfe7a3SToomas Soome 		    entry->part.start - 1;
700199767f8SToomas Soome 		entry->part.type = vtoc_parttype(dv->v_part[i].p_tag);
701199767f8SToomas Soome 		entry->part.index = i; /* starts from zero */
702199767f8SToomas Soome 		entry->type.vtoc = dv->v_part[i].p_tag;
703199767f8SToomas Soome 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
7047efc4ab5SToomas Soome 		DPRINTF("new VTOC partition added");
705199767f8SToomas Soome 	}
706199767f8SToomas Soome 	table->type = PTABLE_VTOC;
707199767f8SToomas Soome out:
708199767f8SToomas Soome 	free(buf);
709199767f8SToomas Soome 	return (table);
710199767f8SToomas Soome }
711199767f8SToomas Soome 
712922a2a16SToomas Soome #define	cdb2devb(bno)	((bno) * ISO_DEFAULT_BLOCK_SIZE / table->sectorsize)
713922a2a16SToomas Soome 
714922a2a16SToomas Soome static struct ptable *
ptable_iso9660read(struct ptable * table,void * dev,diskread_t dread)715922a2a16SToomas Soome ptable_iso9660read(struct ptable *table, void *dev, diskread_t dread)
716922a2a16SToomas Soome {
717922a2a16SToomas Soome 	uint8_t *buf;
718922a2a16SToomas Soome 	struct iso_primary_descriptor *vd;
719922a2a16SToomas Soome 	struct pentry *entry;
720922a2a16SToomas Soome 
721922a2a16SToomas Soome 	buf = malloc(table->sectorsize);
722922a2a16SToomas Soome 	if (buf == NULL)
723922a2a16SToomas Soome 		return (table);
724922a2a16SToomas Soome 
725922a2a16SToomas Soome 	if (dread(dev, buf, 1, cdb2devb(16)) != 0) {
7267efc4ab5SToomas Soome 		DPRINTF("read failed");
727922a2a16SToomas Soome 		ptable_close(table);
728922a2a16SToomas Soome 		table = NULL;
729922a2a16SToomas Soome 		goto out;
730922a2a16SToomas Soome 	}
731922a2a16SToomas Soome 	vd = (struct iso_primary_descriptor *)buf;
732922a2a16SToomas Soome 	if (bcmp(vd->id, ISO_STANDARD_ID, sizeof (vd->id)) != 0)
733922a2a16SToomas Soome 		goto out;
734922a2a16SToomas Soome 
735922a2a16SToomas Soome 	entry = malloc(sizeof (*entry));
736922a2a16SToomas Soome 	if (entry == NULL)
737922a2a16SToomas Soome 		goto out;
738922a2a16SToomas Soome 	entry->part.start = 0;
739922a2a16SToomas Soome 	entry->part.end = table->sectors;
740922a2a16SToomas Soome 	entry->part.type = PART_ISO9660;
741922a2a16SToomas Soome 	entry->part.index = 0;
742922a2a16SToomas Soome 	STAILQ_INSERT_TAIL(&table->entries, entry, entry);
743922a2a16SToomas Soome 
744922a2a16SToomas Soome 	table->type = PTABLE_ISO9660;
745922a2a16SToomas Soome 
746922a2a16SToomas Soome out:
747922a2a16SToomas Soome 	free(buf);
748922a2a16SToomas Soome 	return (table);
749922a2a16SToomas Soome }
750922a2a16SToomas Soome 
751686ab1b2SToomas Soome struct ptable *
ptable_open(void * dev,uint64_t sectors,uint16_t sectorsize,diskread_t * dread)75279bea51bSToomas Soome ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize, diskread_t *dread)
753199767f8SToomas Soome {
754199767f8SToomas Soome 	struct dos_partition *dp;
755199767f8SToomas Soome 	struct ptable *table;
756686ab1b2SToomas Soome 	uint8_t *buf;
75728a0ed27SToomas Soome 	int i;
758199767f8SToomas Soome #ifdef LOADER_MBR_SUPPORT
759199767f8SToomas Soome 	struct pentry *entry;
760199767f8SToomas Soome 	uint32_t start, end;
761199767f8SToomas Soome 	int has_ext;
762199767f8SToomas Soome #endif
763199767f8SToomas Soome 	table = NULL;
7643fd7ddd1SToomas Soome 	dp = NULL;
765199767f8SToomas Soome 	buf = malloc(sectorsize);
766199767f8SToomas Soome 	if (buf == NULL)
767199767f8SToomas Soome 		return (NULL);
768199767f8SToomas Soome 	/* First, read the MBR. */
769199767f8SToomas Soome 	if (dread(dev, buf, 1, DOSBBSECTOR) != 0) {
7707efc4ab5SToomas Soome 		DPRINTF("read failed");
771199767f8SToomas Soome 		goto out;
772199767f8SToomas Soome 	}
773199767f8SToomas Soome 
774686ab1b2SToomas Soome 	table = malloc(sizeof (*table));
775199767f8SToomas Soome 	if (table == NULL)
776199767f8SToomas Soome 		goto out;
777199767f8SToomas Soome 	table->sectors = sectors;
778199767f8SToomas Soome 	table->sectorsize = sectorsize;
779199767f8SToomas Soome 	table->type = PTABLE_NONE;
780199767f8SToomas Soome 	STAILQ_INIT(&table->entries);
781199767f8SToomas Soome 
782922a2a16SToomas Soome 	if (ptable_iso9660read(table, dev, dread) == NULL) {
783922a2a16SToomas Soome 		/* Read error. */
784922a2a16SToomas Soome 		table = NULL;
785922a2a16SToomas Soome 		goto out;
786922a2a16SToomas Soome 	} else if (table->type == PTABLE_ISO9660)
787922a2a16SToomas Soome 		goto out;
788922a2a16SToomas Soome 
789199767f8SToomas Soome 	if (ptable_dklabelread(table, dev, dread) == NULL) { /* Read error. */
790199767f8SToomas Soome 		table = NULL;
791199767f8SToomas Soome 		goto out;
792199767f8SToomas Soome 	} else if (table->type == PTABLE_VTOC)
793199767f8SToomas Soome 		goto out;
794199767f8SToomas Soome 
795199767f8SToomas Soome #ifdef LOADER_VTOC8_SUPPORT
796199767f8SToomas Soome 	if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) {
797199767f8SToomas Soome 		if (ptable_vtoc8read(table, dev, dread) == NULL) {
798199767f8SToomas Soome 			/* Read error. */
799199767f8SToomas Soome 			table = NULL;
800199767f8SToomas Soome 			goto out;
801199767f8SToomas Soome 		} else if (table->type == PTABLE_VTOC8)
802199767f8SToomas Soome 			goto out;
803199767f8SToomas Soome 	}
804199767f8SToomas Soome #endif
805199767f8SToomas Soome 	/* Check the BSD label. */
806199767f8SToomas Soome 	if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */
807199767f8SToomas Soome 		table = NULL;
808199767f8SToomas Soome 		goto out;
809199767f8SToomas Soome 	} else if (table->type == PTABLE_BSD)
810199767f8SToomas Soome 		goto out;
811199767f8SToomas Soome 
812199767f8SToomas Soome #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
813199767f8SToomas Soome 	/* Check the MBR magic. */
814199767f8SToomas Soome 	if (buf[DOSMAGICOFFSET] != 0x55 ||
815199767f8SToomas Soome 	    buf[DOSMAGICOFFSET + 1] != 0xaa) {
8167efc4ab5SToomas Soome 		DPRINTF("magic sequence not found");
817199767f8SToomas Soome #if defined(LOADER_GPT_SUPPORT)
818199767f8SToomas Soome 		/* There is no PMBR, check that we have backup GPT */
819199767f8SToomas Soome 		table->type = PTABLE_GPT;
820199767f8SToomas Soome 		table = ptable_gptread(table, dev, dread);
821199767f8SToomas Soome #endif
822199767f8SToomas Soome 		goto out;
823199767f8SToomas Soome 	}
824199767f8SToomas Soome 	/* Check that we have PMBR. Also do some validation. */
8253fd7ddd1SToomas Soome 	dp = malloc(NDOSPART * sizeof (struct dos_partition));
8263fd7ddd1SToomas Soome 	if (dp == NULL)
8273fd7ddd1SToomas Soome 		goto out;
8283fd7ddd1SToomas Soome 	bcopy(buf + DOSPARTOFF, dp, NDOSPART * sizeof (struct dos_partition));
8293fd7ddd1SToomas Soome 
83028a0ed27SToomas Soome 	/*
83128a0ed27SToomas Soome 	 * macOS can create PMBR partition in a hybrid MBR; that is, an MBR
83228a0ed27SToomas Soome 	 * partition which has a DOSTYP_PMBR entry defined to start at sector 1.
83328a0ed27SToomas Soome 	 * After the DOSTYP_PMBR, there may be other paritions. A UEFI
83428a0ed27SToomas Soome 	 * compliant PMBR has no other partitions.
83528a0ed27SToomas Soome 	 */
83628a0ed27SToomas Soome 	for (i = 0; i < NDOSPART; i++) {
837199767f8SToomas Soome 		if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) {
8387efc4ab5SToomas Soome 			DPRINTF("invalid partition flag %x", dp[i].dp_flag);
839199767f8SToomas Soome 			goto out;
840199767f8SToomas Soome 		}
841199767f8SToomas Soome #ifdef LOADER_GPT_SUPPORT
84228a0ed27SToomas Soome 		if (dp[i].dp_typ == DOSPTYP_PMBR && dp[i].dp_start == 1) {
843199767f8SToomas Soome 			table->type = PTABLE_GPT;
8447efc4ab5SToomas Soome 			DPRINTF("PMBR detected");
845199767f8SToomas Soome 		}
846199767f8SToomas Soome #endif
847199767f8SToomas Soome 	}
848199767f8SToomas Soome #ifdef LOADER_GPT_SUPPORT
849199767f8SToomas Soome 	if (table->type == PTABLE_GPT) {
850199767f8SToomas Soome 		table = ptable_gptread(table, dev, dread);
851199767f8SToomas Soome 		goto out;
852199767f8SToomas Soome 	}
853199767f8SToomas Soome #endif
854199767f8SToomas Soome #ifdef LOADER_MBR_SUPPORT
855199767f8SToomas Soome 	/* Read MBR. */
8567efc4ab5SToomas Soome 	DPRINTF("MBR detected");
857199767f8SToomas Soome 	table->type = PTABLE_MBR;
858199767f8SToomas Soome 	for (i = has_ext = 0; i < NDOSPART; i++) {
859199767f8SToomas Soome 		if (dp[i].dp_typ == 0)
860199767f8SToomas Soome 			continue;
861199767f8SToomas Soome 		start = le32dec(&(dp[i].dp_start));
862199767f8SToomas Soome 		end = le32dec(&(dp[i].dp_size));
863199767f8SToomas Soome 		if (start == 0 || end == 0)
864199767f8SToomas Soome 			continue;
865199767f8SToomas Soome #if 0	/* Some BIOSes return an incorrect number of sectors */
866199767f8SToomas Soome 		if (start + end - 1 >= sectors)
867199767f8SToomas Soome 			continue;	/* XXX: ignore */
868199767f8SToomas Soome #endif
869199767f8SToomas Soome 		if (dp[i].dp_typ == DOSPTYP_EXT ||
870199767f8SToomas Soome 		    dp[i].dp_typ == DOSPTYP_EXTLBA)
871199767f8SToomas Soome 			has_ext = 1;
872686ab1b2SToomas Soome 		entry = malloc(sizeof (*entry));
873199767f8SToomas Soome 		if (entry == NULL)
874199767f8SToomas Soome 			break;
875199767f8SToomas Soome 		entry->part.start = start;
876199767f8SToomas Soome 		entry->part.end = start + end - 1;
877199767f8SToomas Soome 		entry->part.index = i + 1;
878199767f8SToomas Soome 		entry->part.type = mbr_parttype(dp[i].dp_typ);
879199767f8SToomas Soome 		entry->flags = dp[i].dp_flag;
880199767f8SToomas Soome 		entry->type.mbr = dp[i].dp_typ;
881199767f8SToomas Soome 		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
8827efc4ab5SToomas Soome 		DPRINTF("new MBR partition added");
883199767f8SToomas Soome 	}
884199767f8SToomas Soome 	if (has_ext) {
885199767f8SToomas Soome 		table = ptable_ebrread(table, dev, dread);
886199767f8SToomas Soome 		/* FALLTHROUGH */
887199767f8SToomas Soome 	}
888199767f8SToomas Soome #endif /* LOADER_MBR_SUPPORT */
889199767f8SToomas Soome #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
890199767f8SToomas Soome out:
8913fd7ddd1SToomas Soome 	free(dp);
892199767f8SToomas Soome 	free(buf);
893199767f8SToomas Soome 	return (table);
894199767f8SToomas Soome }
895199767f8SToomas Soome 
896199767f8SToomas Soome void
ptable_close(struct ptable * table)897199767f8SToomas Soome ptable_close(struct ptable *table)
898199767f8SToomas Soome {
899199767f8SToomas Soome 	struct pentry *entry;
900199767f8SToomas Soome 
9017ac713f3SToomas Soome 	if (table == NULL)
9027ac713f3SToomas Soome 		return;
9037ac713f3SToomas Soome 
904199767f8SToomas Soome 	while (!STAILQ_EMPTY(&table->entries)) {
905199767f8SToomas Soome 		entry = STAILQ_FIRST(&table->entries);
906199767f8SToomas Soome 		STAILQ_REMOVE_HEAD(&table->entries, entry);
907199767f8SToomas Soome 		free(entry);
908199767f8SToomas Soome 	}
909199767f8SToomas Soome 	free(table);
910199767f8SToomas Soome }
911199767f8SToomas Soome 
912199767f8SToomas Soome enum ptable_type
ptable_gettype(const struct ptable * table)913199767f8SToomas Soome ptable_gettype(const struct ptable *table)
914199767f8SToomas Soome {
915199767f8SToomas Soome 
916199767f8SToomas Soome 	return (table->type);
917199767f8SToomas Soome }
918199767f8SToomas Soome 
919252244c3SToomas Soome int
ptable_getsize(const struct ptable * table,uint64_t * sizep)920252244c3SToomas Soome ptable_getsize(const struct ptable *table, uint64_t *sizep)
921252244c3SToomas Soome {
922252244c3SToomas Soome 	uint64_t tmp = table->sectors * table->sectorsize;
923252244c3SToomas Soome 
924252244c3SToomas Soome 	if (tmp < table->sectors)
925252244c3SToomas Soome 		return (EOVERFLOW);
926252244c3SToomas Soome 
927252244c3SToomas Soome 	if (sizep != NULL)
928252244c3SToomas Soome 		*sizep = tmp;
929252244c3SToomas Soome 	return (0);
930252244c3SToomas Soome }
931252244c3SToomas Soome 
932199767f8SToomas Soome int
ptable_getpart(const struct ptable * table,struct ptable_entry * part,int idx)933199767f8SToomas Soome ptable_getpart(const struct ptable *table, struct ptable_entry *part, int idx)
934199767f8SToomas Soome {
935199767f8SToomas Soome 	struct pentry *entry;
936199767f8SToomas Soome 
937199767f8SToomas Soome 	if (part == NULL || table == NULL)
938199767f8SToomas Soome 		return (EINVAL);
939199767f8SToomas Soome 
940199767f8SToomas Soome 	STAILQ_FOREACH(entry, &table->entries, entry) {
941199767f8SToomas Soome 		if (entry->part.index != idx)
942199767f8SToomas Soome 			continue;
943686ab1b2SToomas Soome 		memcpy(part, &entry->part, sizeof (*part));
944199767f8SToomas Soome 		return (0);
945199767f8SToomas Soome 	}
946199767f8SToomas Soome 	return (ENOENT);
947199767f8SToomas Soome }
948199767f8SToomas Soome 
949199767f8SToomas Soome /*
950199767f8SToomas Soome  * Search for a slice with the following preferences:
951199767f8SToomas Soome  *
952199767f8SToomas Soome  * 1: Active illumos slice
953199767f8SToomas Soome  * 2: Non-active illumos slice
954199767f8SToomas Soome  * 3: Active Linux slice
955199767f8SToomas Soome  * 4: non-active Linux slice
956199767f8SToomas Soome  * 5: Active FAT/FAT32 slice
957199767f8SToomas Soome  * 6: non-active FAT/FAT32 slice
958199767f8SToomas Soome  */
959686ab1b2SToomas Soome #define	PREF_RAWDISK	0
960686ab1b2SToomas Soome #define	PREF_ILLUMOS_ACT	1
961686ab1b2SToomas Soome #define	PREF_ILLUMOS	2
962686ab1b2SToomas Soome #define	PREF_LINUX_ACT	3
963686ab1b2SToomas Soome #define	PREF_LINUX	4
964686ab1b2SToomas Soome #define	PREF_DOS_ACT	5
965686ab1b2SToomas Soome #define	PREF_DOS	6
966686ab1b2SToomas Soome #define	PREF_NONE	7
967199767f8SToomas Soome int
ptable_getbestpart(const struct ptable * table,struct ptable_entry * part)968199767f8SToomas Soome ptable_getbestpart(const struct ptable *table, struct ptable_entry *part)
969199767f8SToomas Soome {
970199767f8SToomas Soome 	struct pentry *entry, *best;
971199767f8SToomas Soome 	int pref, preflevel;
972199767f8SToomas Soome 
973199767f8SToomas Soome 	if (part == NULL || table == NULL)
974199767f8SToomas Soome 		return (EINVAL);
975199767f8SToomas Soome 
976199767f8SToomas Soome 	best = NULL;
977199767f8SToomas Soome 	preflevel = pref = PREF_NONE;
978199767f8SToomas Soome 	STAILQ_FOREACH(entry, &table->entries, entry) {
979199767f8SToomas Soome #ifdef LOADER_MBR_SUPPORT
980199767f8SToomas Soome 		if (table->type == PTABLE_MBR) {
981199767f8SToomas Soome 			switch (entry->type.mbr) {
982199767f8SToomas Soome 			case DOSPTYP_SUNIXOS2:
983199767f8SToomas Soome 				pref = entry->flags & 0x80 ? PREF_ILLUMOS_ACT:
984199767f8SToomas Soome 				    PREF_ILLUMOS;
985199767f8SToomas Soome 				break;
986199767f8SToomas Soome 			case DOSPTYP_LINUX:
987199767f8SToomas Soome 				pref = entry->flags & 0x80 ? PREF_LINUX_ACT:
988199767f8SToomas Soome 				    PREF_LINUX;
989199767f8SToomas Soome 				break;
990199767f8SToomas Soome 			case 0x01:		/* DOS/Windows */
991199767f8SToomas Soome 			case 0x04:
992199767f8SToomas Soome 			case 0x06:
993199767f8SToomas Soome 			case 0x0c:
994199767f8SToomas Soome 			case 0x0e:
995199767f8SToomas Soome 			case DOSPTYP_FAT32:
996199767f8SToomas Soome 				pref = entry->flags & 0x80 ? PREF_DOS_ACT:
997199767f8SToomas Soome 				    PREF_DOS;
998199767f8SToomas Soome 				break;
999199767f8SToomas Soome 			default:
1000199767f8SToomas Soome 				pref = PREF_NONE;
1001199767f8SToomas Soome 			}
1002199767f8SToomas Soome 		}
1003199767f8SToomas Soome #endif /* LOADER_MBR_SUPPORT */
1004199767f8SToomas Soome #ifdef LOADER_GPT_SUPPORT
1005199767f8SToomas Soome 		if (table->type == PTABLE_GPT) {
1006199767f8SToomas Soome 			if (entry->part.type == PART_DOS)
1007199767f8SToomas Soome 				pref = PREF_DOS;
1008199767f8SToomas Soome 			else if (entry->part.type == PART_ILLUMOS_ZFS)
1009199767f8SToomas Soome 				pref = PREF_ILLUMOS;
1010199767f8SToomas Soome 			else
1011199767f8SToomas Soome 				pref = PREF_NONE;
1012199767f8SToomas Soome 		}
1013199767f8SToomas Soome #endif /* LOADER_GPT_SUPPORT */
1014199767f8SToomas Soome 		if (pref < preflevel) {
1015199767f8SToomas Soome 			preflevel = pref;
1016199767f8SToomas Soome 			best = entry;
1017199767f8SToomas Soome 		}
1018199767f8SToomas Soome 	}
1019199767f8SToomas Soome 	if (best != NULL) {
1020686ab1b2SToomas Soome 		memcpy(part, &best->part, sizeof (*part));
1021199767f8SToomas Soome 		return (0);
1022199767f8SToomas Soome 	}
1023199767f8SToomas Soome 	return (ENOENT);
1024199767f8SToomas Soome }
1025199767f8SToomas Soome 
1026199767f8SToomas Soome /*
1027199767f8SToomas Soome  * iterate will stop if iterator will return non 0.
1028199767f8SToomas Soome  */
1029199767f8SToomas Soome int
ptable_iterate(const struct ptable * table,void * arg,ptable_iterate_t * iter)1030199767f8SToomas Soome ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter)
1031199767f8SToomas Soome {
1032199767f8SToomas Soome 	struct pentry *entry;
1033199767f8SToomas Soome 	char name[32];
1034199767f8SToomas Soome 	int ret = 0;
1035199767f8SToomas Soome 
1036199767f8SToomas Soome 	name[0] = '\0';
1037199767f8SToomas Soome 	STAILQ_FOREACH(entry, &table->entries, entry) {
1038199767f8SToomas Soome #ifdef LOADER_MBR_SUPPORT
1039199767f8SToomas Soome 		if (table->type == PTABLE_MBR)
1040199767f8SToomas Soome 			sprintf(name, "s%d", entry->part.index);
1041199767f8SToomas Soome 		else
1042199767f8SToomas Soome #endif
1043199767f8SToomas Soome #ifdef LOADER_GPT_SUPPORT
1044199767f8SToomas Soome 		if (table->type == PTABLE_GPT)
1045199767f8SToomas Soome 			sprintf(name, "p%d", entry->part.index);
1046199767f8SToomas Soome 		else
1047199767f8SToomas Soome #endif
1048199767f8SToomas Soome #ifdef LOADER_VTOC8_SUPPORT
1049199767f8SToomas Soome 		if (table->type == PTABLE_VTOC8)
1050686ab1b2SToomas Soome 			sprintf(name, "%c", (uint8_t)'a' +
1051199767f8SToomas Soome 			    entry->part.index);
1052199767f8SToomas Soome 		else
1053199767f8SToomas Soome #endif
1054199767f8SToomas Soome 		if (table->type == PTABLE_VTOC)
1055686ab1b2SToomas Soome 			sprintf(name, "%c", (uint8_t)'a' +
1056199767f8SToomas Soome 			    entry->part.index);
1057199767f8SToomas Soome 		else
1058199767f8SToomas Soome 		if (table->type == PTABLE_BSD)
1059686ab1b2SToomas Soome 			sprintf(name, "%c", (uint8_t)'a' +
1060199767f8SToomas Soome 			    entry->part.index);
1061199767f8SToomas Soome 		ret = iter(arg, name, &entry->part);
1062199767f8SToomas Soome 		if (ret != 0)
1063199767f8SToomas Soome 			return (ret);
1064199767f8SToomas Soome 	}
1065199767f8SToomas Soome 	return (ret);
1066199767f8SToomas Soome }
1067