part.c revision 346475
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: stable/11/stand/common/part.c 346475 2019-04-21 03:30:47Z kevans $");
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 <fs/cd9660/iso.h>
41
42#include <crc32.h>
43#include <part.h>
44#include <uuid.h>
45
46#ifdef PART_DEBUG
47#define	DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
48#else
49#define	DEBUG(fmt, args...)
50#endif
51
52#ifdef LOADER_GPT_SUPPORT
53#define	MAXTBLSZ	64
54static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
55static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
56static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
57static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI;
58static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
59static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
60static const uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS;
61static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
62static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
63static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
64#endif
65
66struct pentry {
67	struct ptable_entry	part;
68	uint64_t		flags;
69	union {
70		uint8_t bsd;
71		uint8_t	mbr;
72		uuid_t	gpt;
73		uint16_t vtoc8;
74	} type;
75	STAILQ_ENTRY(pentry)	entry;
76};
77
78struct ptable {
79	enum ptable_type	type;
80	uint16_t		sectorsize;
81	uint64_t		sectors;
82
83	STAILQ_HEAD(, pentry)	entries;
84};
85
86static struct parttypes {
87	enum partition_type	type;
88	const char		*desc;
89} ptypes[] = {
90	{ PART_UNKNOWN,		"Unknown" },
91	{ PART_EFI,		"EFI" },
92	{ PART_FREEBSD,		"FreeBSD" },
93	{ PART_FREEBSD_BOOT,	"FreeBSD boot" },
94	{ PART_FREEBSD_NANDFS,	"FreeBSD nandfs" },
95	{ PART_FREEBSD_UFS,	"FreeBSD UFS" },
96	{ PART_FREEBSD_ZFS,	"FreeBSD ZFS" },
97	{ PART_FREEBSD_SWAP,	"FreeBSD swap" },
98	{ PART_FREEBSD_VINUM,	"FreeBSD vinum" },
99	{ PART_LINUX,		"Linux" },
100	{ PART_LINUX_SWAP,	"Linux swap" },
101	{ PART_DOS,		"DOS/Windows" },
102	{ PART_ISO9660,		"ISO9660" },
103};
104
105const char *
106parttype2str(enum partition_type type)
107{
108	size_t i;
109
110	for (i = 0; i < nitems(ptypes); i++)
111		if (ptypes[i].type == type)
112			return (ptypes[i].desc);
113	return (ptypes[0].desc);
114}
115
116#ifdef LOADER_GPT_SUPPORT
117static void
118uuid_letoh(uuid_t *uuid)
119{
120
121	uuid->time_low = le32toh(uuid->time_low);
122	uuid->time_mid = le16toh(uuid->time_mid);
123	uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version);
124}
125
126static enum partition_type
127gpt_parttype(uuid_t type)
128{
129
130	if (uuid_equal(&type, &gpt_uuid_efi, NULL))
131		return (PART_EFI);
132	else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL))
133		return (PART_DOS);
134	else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL))
135		return (PART_FREEBSD_BOOT);
136	else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL))
137		return (PART_FREEBSD_UFS);
138	else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL))
139		return (PART_FREEBSD_ZFS);
140	else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL))
141		return (PART_FREEBSD_SWAP);
142	else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL))
143		return (PART_FREEBSD_VINUM);
144	else if (uuid_equal(&type, &gpt_uuid_freebsd_nandfs, NULL))
145		return (PART_FREEBSD_NANDFS);
146	else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL))
147		return (PART_FREEBSD);
148	return (PART_UNKNOWN);
149}
150
151static struct gpt_hdr *
152gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last,
153    uint16_t sectorsize)
154{
155	uint32_t sz, crc;
156
157	if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) {
158		DEBUG("no GPT signature");
159		return (NULL);
160	}
161	sz = le32toh(hdr->hdr_size);
162	if (sz < 92 || sz > sectorsize) {
163		DEBUG("invalid GPT header size: %d", sz);
164		return (NULL);
165	}
166	crc = le32toh(hdr->hdr_crc_self);
167	hdr->hdr_crc_self = 0;
168	if (crc32(hdr, sz) != crc) {
169		DEBUG("GPT header's CRC doesn't match");
170		return (NULL);
171	}
172	hdr->hdr_crc_self = crc;
173	hdr->hdr_revision = le32toh(hdr->hdr_revision);
174	if (hdr->hdr_revision < GPT_HDR_REVISION) {
175		DEBUG("unsupported GPT revision %d", hdr->hdr_revision);
176		return (NULL);
177	}
178	hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
179	if (hdr->hdr_lba_self != lba_self) {
180		DEBUG("self LBA doesn't match");
181		return (NULL);
182	}
183	hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
184	if (hdr->hdr_lba_alt == hdr->hdr_lba_self) {
185		DEBUG("invalid alternate LBA");
186		return (NULL);
187	}
188	hdr->hdr_entries = le32toh(hdr->hdr_entries);
189	hdr->hdr_entsz = le32toh(hdr->hdr_entsz);
190	if (hdr->hdr_entries == 0 ||
191	    hdr->hdr_entsz < sizeof(struct gpt_ent) ||
192	    sectorsize % hdr->hdr_entsz != 0) {
193		DEBUG("invalid entry size or number of entries");
194		return (NULL);
195	}
196	hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
197	hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end);
198	hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table);
199	hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table);
200	uuid_letoh(&hdr->hdr_uuid);
201	return (hdr);
202}
203
204static int
205gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, size_t size,
206    uint64_t lba_last)
207{
208	struct gpt_ent *ent;
209	uint32_t i, cnt;
210
211	cnt = size / hdr->hdr_entsz;
212	if (hdr->hdr_entries <= cnt) {
213		cnt = hdr->hdr_entries;
214		/* Check CRC only when buffer size is enough for table. */
215		if (hdr->hdr_crc_table !=
216		    crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) {
217			DEBUG("GPT table's CRC doesn't match");
218			return (-1);
219		}
220	}
221	for (i = 0; i < cnt; i++) {
222		ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
223		uuid_letoh(&ent->ent_type);
224		if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
225			continue;
226		ent->ent_lba_start = le64toh(ent->ent_lba_start);
227		ent->ent_lba_end = le64toh(ent->ent_lba_end);
228	}
229	return (0);
230}
231
232static struct ptable *
233ptable_gptread(struct ptable *table, void *dev, diskread_t dread)
234{
235	struct pentry *entry;
236	struct gpt_hdr *phdr, hdr;
237	struct gpt_ent *ent;
238	uint8_t *buf, *tbl;
239	uint64_t offset;
240	int pri, sec;
241	size_t size, i;
242
243	buf = malloc(table->sectorsize);
244	if (buf == NULL)
245		return (NULL);
246	tbl = malloc(table->sectorsize * MAXTBLSZ);
247	if (tbl == NULL) {
248		free(buf);
249		return (NULL);
250	}
251	/* Read the primary GPT header. */
252	if (dread(dev, buf, 1, 1) != 0) {
253		ptable_close(table);
254		table = NULL;
255		goto out;
256	}
257	pri = sec = 0;
258	/* Check the primary GPT header. */
259	phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1,
260	    table->sectorsize);
261	if (phdr != NULL) {
262		/* Read the primary GPT table. */
263		size = MIN(MAXTBLSZ,
264		    howmany(phdr->hdr_entries * phdr->hdr_entsz,
265		        table->sectorsize));
266		if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
267		    gpt_checktbl(phdr, tbl, size * table->sectorsize,
268		    table->sectors - 1) == 0) {
269			memcpy(&hdr, phdr, sizeof(hdr));
270			pri = 1;
271		}
272	}
273	offset = pri ? hdr.hdr_lba_alt: table->sectors - 1;
274	/* Read the backup GPT header. */
275	if (dread(dev, buf, 1, offset) != 0)
276		phdr = NULL;
277	else
278		phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset,
279		    table->sectors - 1, table->sectorsize);
280	if (phdr != NULL) {
281		/*
282		 * Compare primary and backup headers.
283		 * If they are equal, then we do not need to read backup
284		 * table. If they are different, then prefer backup header
285		 * and try to read backup table.
286		 */
287		if (pri == 0 ||
288		    uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 ||
289		    hdr.hdr_revision != phdr->hdr_revision ||
290		    hdr.hdr_size != phdr->hdr_size ||
291		    hdr.hdr_lba_start != phdr->hdr_lba_start ||
292		    hdr.hdr_lba_end != phdr->hdr_lba_end ||
293		    hdr.hdr_entries != phdr->hdr_entries ||
294		    hdr.hdr_entsz != phdr->hdr_entsz ||
295		    hdr.hdr_crc_table != phdr->hdr_crc_table) {
296			/* Read the backup GPT table. */
297			size = MIN(MAXTBLSZ,
298				   howmany(phdr->hdr_entries * phdr->hdr_entsz,
299				       table->sectorsize));
300			if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
301			    gpt_checktbl(phdr, tbl, size * table->sectorsize,
302			    table->sectors - 1) == 0) {
303				memcpy(&hdr, phdr, sizeof(hdr));
304				sec = 1;
305			}
306		}
307	}
308	if (pri == 0 && sec == 0) {
309		/* Both primary and backup tables are invalid. */
310		table->type = PTABLE_NONE;
311		goto out;
312	}
313	DEBUG("GPT detected");
314	size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
315	    MAXTBLSZ * table->sectorsize);
316
317	/*
318	 * If the disk's sector count is smaller than the sector count recorded
319	 * in the disk's GPT table header, set the table->sectors to the value
320	 * recorded in GPT tables. This is done to work around buggy firmware
321	 * that returns truncated disk sizes.
322	 *
323	 * Note, this is still not a foolproof way to get disk's size. For
324	 * example, an image file can be truncated when copied to smaller media.
325	 */
326	table->sectors = hdr.hdr_lba_alt + 1;
327
328	for (i = 0; i < size / hdr.hdr_entsz; i++) {
329		ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz);
330		if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
331			continue;
332
333		/* Simple sanity checks. */
334		if (ent->ent_lba_start < hdr.hdr_lba_start ||
335		    ent->ent_lba_end > hdr.hdr_lba_end ||
336		    ent->ent_lba_start > ent->ent_lba_end)
337			continue;
338
339		entry = malloc(sizeof(*entry));
340		if (entry == NULL)
341			break;
342		entry->part.start = ent->ent_lba_start;
343		entry->part.end = ent->ent_lba_end;
344		entry->part.index = i + 1;
345		entry->part.type = gpt_parttype(ent->ent_type);
346		entry->flags = le64toh(ent->ent_attr);
347		memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t));
348		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
349		DEBUG("new GPT partition added");
350	}
351out:
352	free(buf);
353	free(tbl);
354	return (table);
355}
356#endif /* LOADER_GPT_SUPPORT */
357
358#ifdef LOADER_MBR_SUPPORT
359/* We do not need to support too many EBR partitions in the loader */
360#define	MAXEBRENTRIES		8
361static enum partition_type
362mbr_parttype(uint8_t type)
363{
364
365	switch (type) {
366	case DOSPTYP_386BSD:
367		return (PART_FREEBSD);
368	case DOSPTYP_LINSWP:
369		return (PART_LINUX_SWAP);
370	case DOSPTYP_LINUX:
371		return (PART_LINUX);
372	case 0x01:
373	case 0x04:
374	case 0x06:
375	case 0x07:
376	case 0x0b:
377	case 0x0c:
378	case 0x0e:
379		return (PART_DOS);
380	}
381	return (PART_UNKNOWN);
382}
383
384static struct ptable *
385ptable_ebrread(struct ptable *table, void *dev, diskread_t dread)
386{
387	struct dos_partition *dp;
388	struct pentry *e1, *entry;
389	uint32_t start, end, offset;
390	u_char *buf;
391	int i, index;
392
393	STAILQ_FOREACH(e1, &table->entries, entry) {
394		if (e1->type.mbr == DOSPTYP_EXT ||
395		    e1->type.mbr == DOSPTYP_EXTLBA)
396			break;
397	}
398	if (e1 == NULL)
399		return (table);
400	index = 5;
401	offset = e1->part.start;
402	buf = malloc(table->sectorsize);
403	if (buf == NULL)
404		return (table);
405	DEBUG("EBR detected");
406	for (i = 0; i < MAXEBRENTRIES; i++) {
407#if 0	/* Some BIOSes return an incorrect number of sectors */
408		if (offset >= table->sectors)
409			break;
410#endif
411		if (dread(dev, buf, 1, offset) != 0)
412			break;
413		dp = (struct dos_partition *)(buf + DOSPARTOFF);
414		if (dp[0].dp_typ == 0)
415			break;
416		start = le32toh(dp[0].dp_start);
417		if (dp[0].dp_typ == DOSPTYP_EXT &&
418		    dp[1].dp_typ == 0) {
419			offset = e1->part.start + start;
420			continue;
421		}
422		end = le32toh(dp[0].dp_size);
423		entry = malloc(sizeof(*entry));
424		if (entry == NULL)
425			break;
426		entry->part.start = offset + start;
427		entry->part.end = entry->part.start + end - 1;
428		entry->part.index = index++;
429		entry->part.type = mbr_parttype(dp[0].dp_typ);
430		entry->flags = dp[0].dp_flag;
431		entry->type.mbr = dp[0].dp_typ;
432		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
433		DEBUG("new EBR partition added");
434		if (dp[1].dp_typ == 0)
435			break;
436		offset = e1->part.start + le32toh(dp[1].dp_start);
437	}
438	free(buf);
439	return (table);
440}
441#endif /* LOADER_MBR_SUPPORT */
442
443static enum partition_type
444bsd_parttype(uint8_t type)
445{
446
447	switch (type) {
448	case FS_NANDFS:
449		return (PART_FREEBSD_NANDFS);
450	case FS_SWAP:
451		return (PART_FREEBSD_SWAP);
452	case FS_BSDFFS:
453		return (PART_FREEBSD_UFS);
454	case FS_VINUM:
455		return (PART_FREEBSD_VINUM);
456	case FS_ZFS:
457		return (PART_FREEBSD_ZFS);
458	}
459	return (PART_UNKNOWN);
460}
461
462static struct ptable *
463ptable_bsdread(struct ptable *table, void *dev, diskread_t dread)
464{
465	struct disklabel *dl;
466	struct partition *part;
467	struct pentry *entry;
468	uint8_t *buf;
469	uint32_t raw_offset;
470	int i;
471
472	if (table->sectorsize < sizeof(struct disklabel)) {
473		DEBUG("Too small sectorsize");
474		return (table);
475	}
476	buf = malloc(table->sectorsize);
477	if (buf == NULL)
478		return (table);
479	if (dread(dev, buf, 1, 1) != 0) {
480		DEBUG("read failed");
481		ptable_close(table);
482		table = NULL;
483		goto out;
484	}
485	dl = (struct disklabel *)buf;
486	if (le32toh(dl->d_magic) != DISKMAGIC &&
487	    le32toh(dl->d_magic2) != DISKMAGIC)
488		goto out;
489	if (le32toh(dl->d_secsize) != table->sectorsize) {
490		DEBUG("unsupported sector size");
491		goto out;
492	}
493	dl->d_npartitions = le16toh(dl->d_npartitions);
494	if (dl->d_npartitions > 20 || dl->d_npartitions < 8) {
495		DEBUG("invalid number of partitions");
496		goto out;
497	}
498	DEBUG("BSD detected");
499	part = &dl->d_partitions[0];
500	raw_offset = le32toh(part[RAW_PART].p_offset);
501	for (i = 0; i < dl->d_npartitions; i++, part++) {
502		if (i == RAW_PART)
503			continue;
504		if (part->p_size == 0)
505			continue;
506		entry = malloc(sizeof(*entry));
507		if (entry == NULL)
508			break;
509		entry->part.start = le32toh(part->p_offset) - raw_offset;
510		entry->part.end = entry->part.start +
511		    le32toh(part->p_size) - 1;
512		entry->part.type = bsd_parttype(part->p_fstype);
513		entry->part.index = i; /* starts from zero */
514		entry->type.bsd = part->p_fstype;
515		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
516		DEBUG("new BSD partition added");
517	}
518	table->type = PTABLE_BSD;
519out:
520	free(buf);
521	return (table);
522}
523
524#ifdef LOADER_VTOC8_SUPPORT
525static enum partition_type
526vtoc8_parttype(uint16_t type)
527{
528
529	switch (type) {
530	case VTOC_TAG_FREEBSD_NANDFS:
531		return (PART_FREEBSD_NANDFS);
532	case VTOC_TAG_FREEBSD_SWAP:
533		return (PART_FREEBSD_SWAP);
534	case VTOC_TAG_FREEBSD_UFS:
535		return (PART_FREEBSD_UFS);
536	case VTOC_TAG_FREEBSD_VINUM:
537		return (PART_FREEBSD_VINUM);
538	case VTOC_TAG_FREEBSD_ZFS:
539		return (PART_FREEBSD_ZFS);
540	}
541	return (PART_UNKNOWN);
542}
543
544static struct ptable *
545ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread)
546{
547	struct pentry *entry;
548	struct vtoc8 *dl;
549	uint8_t *buf;
550	uint16_t sum, heads, sectors;
551	int i;
552
553	if (table->sectorsize != sizeof(struct vtoc8))
554		return (table);
555	buf = malloc(table->sectorsize);
556	if (buf == NULL)
557		return (table);
558	if (dread(dev, buf, 1, 0) != 0) {
559		DEBUG("read failed");
560		ptable_close(table);
561		table = NULL;
562		goto out;
563	}
564	dl = (struct vtoc8 *)buf;
565	/* Check the sum */
566	for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum))
567		sum ^= be16dec(buf + i);
568	if (sum != 0) {
569		DEBUG("incorrect checksum");
570		goto out;
571	}
572	if (be16toh(dl->nparts) != VTOC8_NPARTS) {
573		DEBUG("invalid number of entries");
574		goto out;
575	}
576	sectors = be16toh(dl->nsecs);
577	heads = be16toh(dl->nheads);
578	if (sectors * heads == 0) {
579		DEBUG("invalid geometry");
580		goto out;
581	}
582	DEBUG("VTOC8 detected");
583	for (i = 0; i < VTOC8_NPARTS; i++) {
584		dl->part[i].tag = be16toh(dl->part[i].tag);
585		if (i == VTOC_RAW_PART ||
586		    dl->part[i].tag == VTOC_TAG_UNASSIGNED)
587			continue;
588		entry = malloc(sizeof(*entry));
589		if (entry == NULL)
590			break;
591		entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors;
592		entry->part.end = be32toh(dl->map[i].nblks) +
593		    entry->part.start - 1;
594		entry->part.type = vtoc8_parttype(dl->part[i].tag);
595		entry->part.index = i; /* starts from zero */
596		entry->type.vtoc8 = dl->part[i].tag;
597		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
598		DEBUG("new VTOC8 partition added");
599	}
600	table->type = PTABLE_VTOC8;
601out:
602	free(buf);
603	return (table);
604
605}
606#endif /* LOADER_VTOC8_SUPPORT */
607
608#define cdb2devb(bno)   ((bno) * ISO_DEFAULT_BLOCK_SIZE / table->sectorsize)
609
610static struct ptable *
611ptable_iso9660read(struct ptable *table, void *dev, diskread_t dread)
612{
613	uint8_t *buf;
614	struct iso_primary_descriptor *vd;
615	struct pentry *entry;
616
617	buf = malloc(table->sectorsize);
618	if (buf == NULL)
619		return (table);
620
621	if (dread(dev, buf, 1, cdb2devb(16)) != 0) {
622		DEBUG("read failed");
623		ptable_close(table);
624		table = NULL;
625		goto out;
626	}
627	vd = (struct iso_primary_descriptor *)buf;
628	if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
629		goto out;
630
631	entry = malloc(sizeof(*entry));
632	if (entry == NULL)
633		goto out;
634	entry->part.start = 0;
635	entry->part.end = table->sectors;
636	entry->part.type = PART_ISO9660;
637	entry->part.index = 0;
638	STAILQ_INSERT_TAIL(&table->entries, entry, entry);
639
640	table->type = PTABLE_ISO9660;
641
642out:
643	free(buf);
644	return (table);
645}
646
647struct ptable *
648ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize,
649    diskread_t *dread)
650{
651	struct dos_partition *dp;
652	struct ptable *table;
653	uint8_t *buf;
654	int i, count;
655#ifdef LOADER_MBR_SUPPORT
656	struct pentry *entry;
657	uint32_t start, end;
658	int has_ext;
659#endif
660	table = NULL;
661	buf = malloc(sectorsize);
662	if (buf == NULL)
663		return (NULL);
664	/* First, read the MBR. */
665	if (dread(dev, buf, 1, DOSBBSECTOR) != 0) {
666		DEBUG("read failed");
667		goto out;
668	}
669
670	table = malloc(sizeof(*table));
671	if (table == NULL)
672		goto out;
673	table->sectors = sectors;
674	table->sectorsize = sectorsize;
675	table->type = PTABLE_NONE;
676	STAILQ_INIT(&table->entries);
677
678	if (ptable_iso9660read(table, dev, dread) != NULL) {
679		if (table->type == PTABLE_ISO9660)
680			goto out;
681	}
682
683#ifdef LOADER_VTOC8_SUPPORT
684	if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) {
685		if (ptable_vtoc8read(table, dev, dread) == NULL) {
686			/* Read error. */
687			table = NULL;
688			goto out;
689		} else if (table->type == PTABLE_VTOC8)
690			goto out;
691	}
692#endif
693	/* Check the BSD label. */
694	if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */
695		table = NULL;
696		goto out;
697	} else if (table->type == PTABLE_BSD)
698		goto out;
699
700#if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
701	/* Check the MBR magic. */
702	if (buf[DOSMAGICOFFSET] != 0x55 ||
703	    buf[DOSMAGICOFFSET + 1] != 0xaa) {
704		DEBUG("magic sequence not found");
705#if defined(LOADER_GPT_SUPPORT)
706		/* There is no PMBR, check that we have backup GPT */
707		table->type = PTABLE_GPT;
708		table = ptable_gptread(table, dev, dread);
709#endif
710		goto out;
711	}
712	/* Check that we have PMBR. Also do some validation. */
713	dp = (struct dos_partition *)(buf + DOSPARTOFF);
714	for (i = 0, count = 0; i < NDOSPART; i++) {
715		if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) {
716			DEBUG("invalid partition flag %x", dp[i].dp_flag);
717			goto out;
718		}
719#ifdef LOADER_GPT_SUPPORT
720		if (dp[i].dp_typ == DOSPTYP_PMBR) {
721			table->type = PTABLE_GPT;
722			DEBUG("PMBR detected");
723		}
724#endif
725		if (dp[i].dp_typ != 0)
726			count++;
727	}
728	/* Do we have some invalid values? */
729	if (table->type == PTABLE_GPT && count > 1) {
730		if (dp[1].dp_typ != DOSPTYP_HFS) {
731			table->type = PTABLE_NONE;
732			DEBUG("Incorrect PMBR, ignore it");
733		} else {
734			DEBUG("Bootcamp detected");
735		}
736	}
737#ifdef LOADER_GPT_SUPPORT
738	if (table->type == PTABLE_GPT) {
739		table = ptable_gptread(table, dev, dread);
740		goto out;
741	}
742#endif
743#ifdef LOADER_MBR_SUPPORT
744	/* Read MBR. */
745	DEBUG("MBR detected");
746	table->type = PTABLE_MBR;
747	for (i = has_ext = 0; i < NDOSPART; i++) {
748		if (dp[i].dp_typ == 0)
749			continue;
750		start = le32dec(&(dp[i].dp_start));
751		end = le32dec(&(dp[i].dp_size));
752		if (start == 0 || end == 0)
753			continue;
754#if 0	/* Some BIOSes return an incorrect number of sectors */
755		if (start + end - 1 >= sectors)
756			continue;	/* XXX: ignore */
757#endif
758		if (dp[i].dp_typ == DOSPTYP_EXT ||
759		    dp[i].dp_typ == DOSPTYP_EXTLBA)
760			has_ext = 1;
761		entry = malloc(sizeof(*entry));
762		if (entry == NULL)
763			break;
764		entry->part.start = start;
765		entry->part.end = start + end - 1;
766		entry->part.index = i + 1;
767		entry->part.type = mbr_parttype(dp[i].dp_typ);
768		entry->flags = dp[i].dp_flag;
769		entry->type.mbr = dp[i].dp_typ;
770		STAILQ_INSERT_TAIL(&table->entries, entry, entry);
771		DEBUG("new MBR partition added");
772	}
773	if (has_ext) {
774		table = ptable_ebrread(table, dev, dread);
775		/* FALLTHROUGH */
776	}
777#endif /* LOADER_MBR_SUPPORT */
778#endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
779out:
780	free(buf);
781	return (table);
782}
783
784void
785ptable_close(struct ptable *table)
786{
787	struct pentry *entry;
788
789	while (!STAILQ_EMPTY(&table->entries)) {
790		entry = STAILQ_FIRST(&table->entries);
791		STAILQ_REMOVE_HEAD(&table->entries, entry);
792		free(entry);
793	}
794	free(table);
795}
796
797enum ptable_type
798ptable_gettype(const struct ptable *table)
799{
800
801	return (table->type);
802}
803
804int
805ptable_getsize(const struct ptable *table, uint64_t *sizep)
806{
807	uint64_t tmp = table->sectors * table->sectorsize;
808
809	if (tmp < table->sectors)
810		return (EOVERFLOW);
811
812	if (sizep != NULL)
813		*sizep = tmp;
814	return (0);
815}
816
817int
818ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index)
819{
820	struct pentry *entry;
821
822	if (part == NULL || table == NULL)
823		return (EINVAL);
824
825	STAILQ_FOREACH(entry, &table->entries, entry) {
826		if (entry->part.index != index)
827			continue;
828		memcpy(part, &entry->part, sizeof(*part));
829		return (0);
830	}
831	return (ENOENT);
832}
833
834/*
835 * Search for a slice with the following preferences:
836 *
837 * 1: Active FreeBSD slice
838 * 2: Non-active FreeBSD slice
839 * 3: Active Linux slice
840 * 4: non-active Linux slice
841 * 5: Active FAT/FAT32 slice
842 * 6: non-active FAT/FAT32 slice
843 */
844#define	PREF_RAWDISK	0
845#define	PREF_FBSD_ACT	1
846#define	PREF_FBSD	2
847#define	PREF_LINUX_ACT	3
848#define	PREF_LINUX	4
849#define	PREF_DOS_ACT	5
850#define	PREF_DOS	6
851#define	PREF_NONE	7
852int
853ptable_getbestpart(const struct ptable *table, struct ptable_entry *part)
854{
855	struct pentry *entry, *best;
856	int pref, preflevel;
857
858	if (part == NULL || table == NULL)
859		return (EINVAL);
860
861	best = NULL;
862	preflevel = pref = PREF_NONE;
863	STAILQ_FOREACH(entry, &table->entries, entry) {
864#ifdef LOADER_MBR_SUPPORT
865		if (table->type == PTABLE_MBR) {
866			switch (entry->type.mbr) {
867			case DOSPTYP_386BSD:
868				pref = entry->flags & 0x80 ? PREF_FBSD_ACT:
869				    PREF_FBSD;
870				break;
871			case DOSPTYP_LINUX:
872				pref = entry->flags & 0x80 ? PREF_LINUX_ACT:
873				    PREF_LINUX;
874				break;
875			case 0x01:		/* DOS/Windows */
876			case 0x04:
877			case 0x06:
878			case 0x0c:
879			case 0x0e:
880			case DOSPTYP_FAT32:
881				pref = entry->flags & 0x80 ? PREF_DOS_ACT:
882				    PREF_DOS;
883				break;
884			default:
885				pref = PREF_NONE;
886			}
887		}
888#endif /* LOADER_MBR_SUPPORT */
889#ifdef LOADER_GPT_SUPPORT
890		if (table->type == PTABLE_GPT) {
891			if (entry->part.type == PART_DOS)
892				pref = PREF_DOS;
893			else if (entry->part.type == PART_FREEBSD_UFS ||
894			    entry->part.type == PART_FREEBSD_ZFS)
895				pref = PREF_FBSD;
896			else
897				pref = PREF_NONE;
898		}
899#endif /* LOADER_GPT_SUPPORT */
900#ifdef LOADER_PC98_SUPPORT
901		if (table->type == PTABLE_PC98) {
902			switch(entry->part.type & PC98_MID_MASK) {
903			case PC98_MID_386BSD:		/* FreeBSD */
904				if ((entry->part.type & PC98_MID_BOOTABLE) &&
905				    (preflevel > PREF_FBSD_ACT)) {
906					pref = i;
907					preflevel = PREF_FBSD_ACT;
908				} else if (preflevel > PREF_FBSD) {
909					pref = i;
910					preflevel = PREF_FBSD;
911				}
912				break;
913
914			case 0x11:			/* DOS/Windows */
915			case 0x20:
916			case 0x21:
917			case 0x22:
918			case 0x23:
919			case 0x63:
920				if ((entry->part.type & PC98_MID_BOOTABLE) &&
921				    (preflevel > PREF_DOS_ACT)) {
922					pref = i;
923					preflevel = PREF_DOS_ACT;
924				} else if (preflevel > PREF_DOS) {
925					pref = i;
926					preflevel = PREF_DOS;
927				}
928				break;
929			}
930		}
931#endif /* LOADER_PC98_SUPPORT */
932		if (pref < preflevel) {
933			preflevel = pref;
934			best = entry;
935		}
936	}
937	if (best != NULL) {
938		memcpy(part, &best->part, sizeof(*part));
939		return (0);
940	}
941	return (ENOENT);
942}
943
944int
945ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter)
946{
947	struct pentry *entry;
948	char name[32];
949	int ret = 0;
950
951	name[0] = '\0';
952	STAILQ_FOREACH(entry, &table->entries, entry) {
953#ifdef LOADER_MBR_SUPPORT
954		if (table->type == PTABLE_MBR)
955			sprintf(name, "s%d", entry->part.index);
956		else
957#endif
958#ifdef LOADER_GPT_SUPPORT
959		if (table->type == PTABLE_GPT)
960			sprintf(name, "p%d", entry->part.index);
961		else
962#endif
963#ifdef LOADER_VTOC8_SUPPORT
964		if (table->type == PTABLE_VTOC8)
965			sprintf(name, "%c", (uint8_t) 'a' +
966			    entry->part.index);
967		else
968#endif
969		if (table->type == PTABLE_BSD)
970			sprintf(name, "%c", (uint8_t) 'a' +
971			    entry->part.index);
972		if ((ret = iter(arg, name, &entry->part)) != 0)
973			return (ret);
974	}
975	return (ret);
976}
977#ifdef LOADER_PC98_SUPPORT
978static int
979bd_open_pc98(struct open_disk *od, struct i386_devdesc *dev)
980{
981    struct pc98_partition	*dptr;
982    struct disklabel		*lp;
983    int				sector, slice, i;
984    char			buf[BUFSIZE];
985
986    /*
987     * Following calculations attempt to determine the correct value
988     * for d->od_boff by looking for the slice and partition specified,
989     * or searching for reasonable defaults.
990     */
991
992    /*
993     * Find the slice in the DOS slice table.
994     */
995    od->od_nslices = 0;
996    if (od->od_flags & BD_FLOPPY) {
997	sector = 0;
998	goto unsliced;
999    }
1000    if (bd_read(od, 0, 1, buf)) {
1001	DEBUG("error reading MBR");
1002	return (EIO);
1003    }
1004
1005    /*
1006     * Check the slice table magic.
1007     */
1008    if (((u_char)buf[0x1fe] != 0x55) || ((u_char)buf[0x1ff] != 0xaa)) {
1009	/* If a slice number was explicitly supplied, this is an error */
1010	if (dev->d_kind.biosdisk.slice > 0) {
1011	    DEBUG("no slice table/MBR (no magic)");
1012	    return (ENOENT);
1013	}
1014	sector = 0;
1015	goto unsliced;		/* may be a floppy */
1016    }
1017    if (bd_read(od, 1, 1, buf)) {
1018	DEBUG("error reading MBR");
1019	return (EIO);
1020    }
1021
1022    /*
1023     * copy the partition table, then pick up any extended partitions.
1024     */
1025    bcopy(buf + PC98_PARTOFF, &od->od_slicetab,
1026      sizeof(struct pc98_partition) * PC98_NPARTS);
1027    od->od_nslices = PC98_NPARTS;	/* extended slices start here */
1028    od->od_flags |= BD_PARTTABOK;
1029    dptr = &od->od_slicetab[0];
1030
1031    /* Is this a request for the whole disk? */
1032    if (dev->d_kind.biosdisk.slice == -1) {
1033	sector = 0;
1034	goto unsliced;
1035    }
1036
1037    /*
1038     * if a slice number was supplied but not found, this is an error.
1039     */
1040    if (dev->d_kind.biosdisk.slice > 0) {
1041        slice = dev->d_kind.biosdisk.slice - 1;
1042        if (slice >= od->od_nslices) {
1043            DEBUG("slice %d not found", slice);
1044	    return (ENOENT);
1045        }
1046    }
1047
1048    /* Try to auto-detect the best slice; this should always give a slice number */
1049    if (dev->d_kind.biosdisk.slice == 0) {
1050	slice = bd_bestslice(od);
1051        if (slice == -1) {
1052	    return (ENOENT);
1053        }
1054        dev->d_kind.biosdisk.slice = slice;
1055    }
1056
1057    dptr = &od->od_slicetab[0];
1058    /*
1059     * Accept the supplied slice number unequivocally (we may be looking
1060     * at a DOS partition).
1061     */
1062    dptr += (dev->d_kind.biosdisk.slice - 1);	/* we number 1-4, offsets are 0-3 */
1063    sector = dptr->dp_scyl * od->od_hds * od->od_sec +
1064	dptr->dp_shd * od->od_sec + dptr->dp_ssect;
1065    {
1066	int end = dptr->dp_ecyl * od->od_hds * od->od_sec +
1067	    dptr->dp_ehd * od->od_sec + dptr->dp_esect;
1068	DEBUG("slice entry %d at %d, %d sectors",
1069	      dev->d_kind.biosdisk.slice - 1, sector, end-sector);
1070    }
1071
1072    /*
1073     * If we are looking at a BSD slice, and the partition is < 0, assume the 'a' partition
1074     */
1075    if ((dptr->dp_mid == DOSMID_386BSD) && (dev->d_kind.biosdisk.partition < 0))
1076	dev->d_kind.biosdisk.partition = 0;
1077
1078 unsliced:
1079    /*
1080     * Now we have the slice offset, look for the partition in the disklabel if we have
1081     * a partition to start with.
1082     *
1083     * XXX we might want to check the label checksum.
1084     */
1085    if (dev->d_kind.biosdisk.partition < 0) {
1086	od->od_boff = sector;		/* no partition, must be after the slice */
1087	DEBUG("opening raw slice");
1088    } else {
1089
1090	if (bd_read(od, sector + LABELSECTOR, 1, buf)) {
1091	    DEBUG("error reading disklabel");
1092	    return (EIO);
1093	}
1094	DEBUG("copy %d bytes of label from %p to %p", sizeof(struct disklabel), buf + LABELOFFSET, &od->od_disklabel);
1095	bcopy(buf + LABELOFFSET, &od->od_disklabel, sizeof(struct disklabel));
1096	lp = &od->od_disklabel;
1097	od->od_flags |= BD_LABELOK;
1098
1099	if (lp->d_magic != DISKMAGIC) {
1100	    DEBUG("no disklabel");
1101	    return (ENOENT);
1102	}
1103	if (dev->d_kind.biosdisk.partition >= lp->d_npartitions) {
1104	    DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
1105		  'a' + dev->d_kind.biosdisk.partition, 'a' + lp->d_npartitions);
1106	    return (EPART);
1107	}
1108
1109#ifdef DISK_DEBUG
1110	/* Complain if the partition is unused unless this is a floppy. */
1111	if ((lp->d_partitions[dev->d_kind.biosdisk.partition].p_fstype == FS_UNUSED) &&
1112	    !(od->od_flags & BD_FLOPPY))
1113	    DEBUG("warning, partition marked as unused");
1114#endif
1115
1116	od->od_boff =
1117		lp->d_partitions[dev->d_kind.biosdisk.partition].p_offset -
1118		lp->d_partitions[RAW_PART].p_offset +
1119		sector;
1120    }
1121    return (0);
1122}
1123static void
1124bd_closedisk(struct open_disk *od)
1125{
1126    DEBUG("open_disk %p", od);
1127#if 0
1128    /* XXX is this required? (especially if disk already open...) */
1129    if (od->od_flags & BD_FLOPPY)
1130	delay(3000000);
1131#endif
1132    free(od);
1133}
1134
1135#endif /* LOADER_PC98_SUPPORT */
1136