disk.c revision 223695
1139749Simp/*-
2119815Smarcel * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3119815Smarcel * All rights reserved.
4119815Smarcel *
5119815Smarcel * Redistribution and use in source and binary forms, with or without
6119815Smarcel * modification, are permitted provided that the following conditions
7119815Smarcel * are met:
8119815Smarcel * 1. Redistributions of source code must retain the above copyright
9119815Smarcel *    notice, this list of conditions and the following disclaimer.
10119815Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11119815Smarcel *    notice, this list of conditions and the following disclaimer in the
12119815Smarcel *    documentation and/or other materials provided with the distribution.
13119815Smarcel *
14119815Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15119815Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16119815Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17119815Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18119815Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19119815Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20119815Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21119815Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22119815Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23119815Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24119815Smarcel * SUCH DAMAGE.
25119815Smarcel */
26119815Smarcel
27119815Smarcel#include <sys/cdefs.h>
28119815Smarcel__FBSDID("$FreeBSD: head/sys/boot/common/disk.c 223695 2011-06-30 16:08:56Z dfr $");
29119815Smarcel
30119815Smarcel/*
31119815Smarcel * MBR/GPT partitioned disk device handling.
32119815Smarcel *
33119815Smarcel * Ideas and algorithms from:
34119815Smarcel *
35119815Smarcel * - NetBSD libi386/biosdisk.c
36119815Smarcel * - FreeBSD biosboot/disk.c
37119815Smarcel *
38119815Smarcel */
39119815Smarcel
40137949Smarcel#include <stand.h>
41137949Smarcel
42119815Smarcel#include <sys/diskmbr.h>
43119815Smarcel#include <sys/disklabel.h>
44119815Smarcel#include <sys/gpt.h>
45119815Smarcel
46119815Smarcel#include <stdarg.h>
47119815Smarcel#include <uuid.h>
48119815Smarcel
49119815Smarcel#include <bootstrap.h>
50119815Smarcel
51119815Smarcel#include "disk.h"
52119815Smarcel
53119815Smarcel#ifdef DISK_DEBUG
54119815Smarcel# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
55119815Smarcel#else
56119815Smarcel# define DEBUG(fmt, args...)
57119815Smarcel#endif
58119815Smarcel
59119815Smarcel/*
60119815Smarcel * Search for a slice with the following preferences:
61119815Smarcel *
62119815Smarcel * 1: Active FreeBSD slice
63119815Smarcel * 2: Non-active FreeBSD slice
64119815Smarcel * 3: Active Linux slice
65119815Smarcel * 4: non-active Linux slice
66119815Smarcel * 5: Active FAT/FAT32 slice
67119815Smarcel * 6: non-active FAT/FAT32 slice
68119815Smarcel */
69119815Smarcel#define PREF_RAWDISK	0
70119815Smarcel#define PREF_FBSD_ACT	1
71119815Smarcel#define PREF_FBSD	2
72119815Smarcel#define PREF_LINUX_ACT	3
73119815Smarcel#define PREF_LINUX	4
74119815Smarcel#define PREF_DOS_ACT	5
75119815Smarcel#define PREF_DOS	6
76119815Smarcel#define PREF_NONE	7
77119815Smarcel
78119815Smarcel#ifdef LOADER_GPT_SUPPORT
79119815Smarcel
80119815Smarcelstruct gpt_part {
81119815Smarcel	int		gp_index;
82119815Smarcel	uuid_t		gp_type;
83119815Smarcel	uint64_t	gp_start;
84119815Smarcel	uint64_t	gp_end;
85119815Smarcel};
86119815Smarcel
87119815Smarcelstatic uuid_t efi = GPT_ENT_TYPE_EFI;
88119815Smarcelstatic uuid_t freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
89119815Smarcelstatic uuid_t freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
90119815Smarcelstatic uuid_t freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
91119815Smarcelstatic uuid_t freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
92119815Smarcelstatic uuid_t ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
93119815Smarcel
94119815Smarcel#endif
95119815Smarcel
96119815Smarcel/* Given a size in 512 byte sectors, convert it to a human-readable number. */
97119815Smarcelstatic char *
98119815Smarceldisplay_size(uint64_t size)
99119815Smarcel{
100119815Smarcel	static char buf[80];
101119815Smarcel	char unit;
102119815Smarcel
103119815Smarcel	size /= 2;
104119815Smarcel	unit = 'K';
105119815Smarcel	if (size >= 10485760000LL) {
106119815Smarcel		size /= 1073741824;
107119815Smarcel		unit = 'T';
108119815Smarcel	} else if (size >= 10240000) {
109119815Smarcel		size /= 1048576;
110119815Smarcel		unit = 'G';
111119815Smarcel	} else if (size >= 10000) {
112119815Smarcel		size /= 1024;
113119815Smarcel		unit = 'M';
114119815Smarcel	}
115119815Smarcel	sprintf(buf, "%.6ld%cB", (long)size, unit);
116119815Smarcel	return (buf);
117119815Smarcel}
118119815Smarcel
119119815Smarcelstatic void
120119815Smarceldisk_checkextended(struct disk_devdesc *dev,
121119815Smarcel    struct dos_partition *slicetab, int slicenum, int *nslicesp)
122119815Smarcel{
123119815Smarcel	uint8_t			buf[DISK_SECSIZE];
124119815Smarcel	struct dos_partition	*dp;
125119815Smarcel	uint32_t		base;
126119815Smarcel	int			rc, i, start, end;
127119815Smarcel
128119815Smarcel	dp = &slicetab[slicenum];
129119815Smarcel	start = *nslicesp;
130119815Smarcel
131119815Smarcel	if (dp->dp_size == 0)
132119815Smarcel		goto done;
133119815Smarcel	if (dp->dp_typ != DOSPTYP_EXT)
134119815Smarcel		goto done;
135119815Smarcel	rc = dev->d_dev->dv_strategy(dev, F_READ, dp->dp_start, DISK_SECSIZE,
136119815Smarcel		(char *) buf, NULL);
137119815Smarcel	if (rc)
138119815Smarcel		goto done;
139119815Smarcel	if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) {
140119815Smarcel		DEBUG("no magic in extended table");
141119815Smarcel		goto done;
142119815Smarcel	}
143119815Smarcel	base = dp->dp_start;
144119815Smarcel	dp = (struct dos_partition *) &buf[DOSPARTOFF];
145119815Smarcel	for (i = 0; i < NDOSPART; i++, dp++) {
146119815Smarcel		if (dp->dp_size == 0)
147119815Smarcel			continue;
148119815Smarcel		if (*nslicesp == NEXTDOSPART)
149119815Smarcel			goto done;
150119815Smarcel		dp->dp_start += base;
151119815Smarcel		bcopy(dp, &slicetab[*nslicesp], sizeof(*dp));
152119815Smarcel		(*nslicesp)++;
153119815Smarcel	}
154119815Smarcel	end = *nslicesp;
155119815Smarcel
156119815Smarcel	/*
157119815Smarcel	 * now, recursively check the slices we just added
158119815Smarcel	 */
159119815Smarcel	for (i = start; i < end; i++)
160119815Smarcel		disk_checkextended(dev, slicetab, i, nslicesp);
161119815Smarceldone:
162119815Smarcel	return;
163119815Smarcel}
164119815Smarcel
165119815Smarcelstatic int
166119815Smarceldisk_readslicetab(struct disk_devdesc *dev,
167119815Smarcel    struct dos_partition **slicetabp, int *nslicesp)
168119815Smarcel{
169119815Smarcel	struct dos_partition	*slicetab = NULL;
170119815Smarcel	int			nslices, i;
171119815Smarcel	int			rc;
172119815Smarcel	uint8_t			buf[DISK_SECSIZE];
173119815Smarcel
174119815Smarcel	/*
175119815Smarcel	 * Find the slice in the DOS slice table.
176119815Smarcel	 */
177119815Smarcel	rc = dev->d_dev->dv_strategy(dev, F_READ, 0, DISK_SECSIZE,
178119815Smarcel		(char *) buf, NULL);
179119815Smarcel	if (rc) {
180119815Smarcel		DEBUG("error reading MBR");
181119815Smarcel		return (rc);
182119815Smarcel	}
183119815Smarcel
184119815Smarcel	/*
185119815Smarcel	 * Check the slice table magic.
186119815Smarcel	 */
187119815Smarcel	if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) {
188119815Smarcel		DEBUG("no slice table/MBR (no magic)");
189119815Smarcel		return (rc);
190119815Smarcel	}
191119815Smarcel
192119815Smarcel	/*
193119815Smarcel	 * copy the partition table, then pick up any extended partitions.
194119815Smarcel	 */
195119815Smarcel	slicetab = malloc(NEXTDOSPART * sizeof(struct dos_partition));
196119815Smarcel	bcopy(buf + DOSPARTOFF, slicetab,
197119815Smarcel		sizeof(struct dos_partition) * NDOSPART);
198119815Smarcel	nslices = NDOSPART;		/* extended slices start here */
199119815Smarcel	for (i = 0; i < NDOSPART; i++)
200157989Smarcel		disk_checkextended(dev, slicetab, i, &nslices);
201157989Smarcel
202119815Smarcel	*slicetabp = slicetab;
203119815Smarcel	*nslicesp = nslices;
204119815Smarcel	return (0);
205119815Smarcel}
206119815Smarcel
207119815Smarcel/*
208119815Smarcel * Search for the best MBR slice (typically the first FreeBSD slice).
209119815Smarcel */
210119815Smarcelstatic int
211119815Smarceldisk_bestslice(struct dos_partition *slicetab, int nslices)
212119815Smarcel{
213119815Smarcel	struct dos_partition *dp;
214119815Smarcel	int pref, preflevel;
215119815Smarcel	int i, prefslice;
216119815Smarcel
217119815Smarcel	prefslice = 0;
218119815Smarcel	preflevel = PREF_NONE;
219119815Smarcel
220157380Smarcel	dp = &slicetab[0];
221119815Smarcel	for (i = 0; i < nslices; i++, dp++) {
222119815Smarcel		switch (dp->dp_typ) {
223119815Smarcel		case DOSPTYP_386BSD:		/* FreeBSD */
224119815Smarcel			pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD;
225119815Smarcel			break;
226119815Smarcel
227119815Smarcel		case DOSPTYP_LINUX:
228119815Smarcel			pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX;
229119815Smarcel			break;
230119815Smarcel
231119815Smarcel		case 0x01:		/* DOS/Windows */
232119815Smarcel		case 0x04:
233119815Smarcel		case 0x06:
234119815Smarcel		case 0x0b:
235119815Smarcel		case 0x0c:
236119815Smarcel		case 0x0e:
237119815Smarcel			pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS;
238119815Smarcel			break;
239119815Smarcel
240119815Smarcel		default:
241119815Smarcel		        pref = PREF_NONE;
242119815Smarcel		}
243119815Smarcel		if (pref < preflevel) {
244119815Smarcel			preflevel = pref;
245119815Smarcel			prefslice = i + 1;
246119815Smarcel		}
247119815Smarcel	}
248119815Smarcel	return (prefslice);
249119815Smarcel}
250119815Smarcel
251119815Smarcelstatic int
252119815Smarceldisk_openmbr(struct disk_devdesc *dev)
253119815Smarcel{
254119815Smarcel	struct dos_partition	*slicetab = NULL, *dptr;
255119815Smarcel	int			nslices, sector, slice;
256119815Smarcel	int			rc;
257119815Smarcel	uint8_t			buf[DISK_SECSIZE];
258119815Smarcel	struct disklabel	*lp;
259119815Smarcel
260119815Smarcel	/*
261119815Smarcel	 * Following calculations attempt to determine the correct value
262119815Smarcel	 * for dev->d_offset by looking for the slice and partition specified,
263119815Smarcel	 * or searching for reasonable defaults.
264119815Smarcel	 */
265119815Smarcel	rc = disk_readslicetab(dev, &slicetab, &nslices);
266119815Smarcel	if (rc)
267119815Smarcel		return (rc);
268119815Smarcel
269119815Smarcel	/*
270119815Smarcel	 * if a slice number was supplied but not found, this is an error.
271119815Smarcel	 */
272119815Smarcel	if (dev->d_slice > 0) {
273119815Smarcel		slice = dev->d_slice - 1;
274119815Smarcel		if (slice >= nslices) {
275119815Smarcel			DEBUG("slice %d not found", slice);
276119815Smarcel			rc = EPART;
277119815Smarcel			goto out;
278119815Smarcel		}
279119815Smarcel	}
280119815Smarcel
281119815Smarcel	/*
282119815Smarcel	 * Check for the historically bogus MBR found on true dedicated disks
283119815Smarcel	 */
284119815Smarcel	if (slicetab[3].dp_typ == DOSPTYP_386BSD &&
285119815Smarcel	    slicetab[3].dp_start == 0 && slicetab[3].dp_size == 50000) {
286119815Smarcel		sector = 0;
287119815Smarcel		goto unsliced;
288119815Smarcel	}
289119815Smarcel
290119815Smarcel	/*
291119815Smarcel	 * Try to auto-detect the best slice; this should always give
292119815Smarcel	 * a slice number
293119815Smarcel	 */
294119815Smarcel	if (dev->d_slice == 0) {
295119815Smarcel		slice = disk_bestslice(slicetab, nslices);
296119815Smarcel		if (slice == -1) {
297119815Smarcel			rc = ENOENT;
298119815Smarcel			goto out;
299119815Smarcel		}
300119815Smarcel		dev->d_slice = slice;
301119815Smarcel	}
302119815Smarcel
303119815Smarcel	/*
304119815Smarcel	 * Accept the supplied slice number unequivocally (we may be looking
305119815Smarcel	 * at a DOS partition).
306119815Smarcel	 * Note: we number 1-4, offsets are 0-3
307119815Smarcel	 */
308127742Smarcel	dptr = &slicetab[dev->d_slice - 1];
309119815Smarcel	sector = dptr->dp_start;
310119815Smarcel	DEBUG("slice entry %d at %d, %d sectors",
311119815Smarcel		dev->d_slice - 1, sector, dptr->dp_size);
312119815Smarcel
313119815Smarcelunsliced:
314119815Smarcel	/*
315119815Smarcel	 * Now we have the slice offset, look for the partition in the
316119815Smarcel	 * disklabel if we have a partition to start with.
317119815Smarcel	 *
318119815Smarcel	 * XXX we might want to check the label checksum.
319119815Smarcel	 */
320119815Smarcel	if (dev->d_partition < 0) {
321119815Smarcel		/* no partition, must be after the slice */
322119815Smarcel		DEBUG("opening raw slice");
323119815Smarcel		dev->d_offset = sector;
324157380Smarcel		rc = 0;
325119815Smarcel		goto out;
326157380Smarcel	}
327119815Smarcel
328157380Smarcel	rc = dev->d_dev->dv_strategy(dev, F_READ, sector + LABELSECTOR,
329157380Smarcel            DISK_SECSIZE, (char *) buf, NULL);
330119815Smarcel	if (rc) {
331119815Smarcel		DEBUG("error reading disklabel");
332119815Smarcel		goto out;
333157380Smarcel	}
334157380Smarcel
335119815Smarcel	lp = (struct disklabel *) buf;
336157380Smarcel
337157380Smarcel	if (lp->d_magic != DISKMAGIC) {
338157380Smarcel		DEBUG("no disklabel");
339157380Smarcel		rc = ENOENT;
340157380Smarcel		goto out;
341157380Smarcel	}
342157380Smarcel	if (dev->d_partition >= lp->d_npartitions) {
343157380Smarcel		DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
344119815Smarcel		  'a' + dev->d_partition,
345119815Smarcel		  'a' + lp->d_npartitions);
346119815Smarcel		rc = EPART;
347119815Smarcel		goto out;
348119815Smarcel	}
349119815Smarcel
350119815Smarcel	dev->d_offset =
351119815Smarcel		lp->d_partitions[dev->d_partition].p_offset -
352119815Smarcel		lp->d_partitions[RAW_PART].p_offset +
353119815Smarcel		sector;
354119815Smarcel	rc = 0;
355119815Smarcel
356119815Smarcelout:
357119815Smarcel	if (slicetab)
358119815Smarcel		free(slicetab);
359119815Smarcel	return (rc);
360119815Smarcel}
361119815Smarcel
362119815Smarcel/*
363119815Smarcel * Print out each valid partition in the disklabel of a FreeBSD slice.
364119815Smarcel * For size calculations, we assume a 512 byte sector size.
365119815Smarcel */
366119815Smarcelstatic void
367119815Smarceldisk_printbsdslice(struct disk_devdesc *dev, daddr_t offset,
368119815Smarcel    char *prefix, int verbose)
369119815Smarcel{
370119815Smarcel	char			line[80];
371119815Smarcel	char			buf[DISK_SECSIZE];
372119815Smarcel	struct disklabel	*lp;
373119815Smarcel	int			i, rc, fstype;
374119815Smarcel
375119815Smarcel	/* read disklabel */
376119815Smarcel	rc = dev->d_dev->dv_strategy(dev, F_READ, offset + LABELSECTOR,
377119815Smarcel		DISK_SECSIZE, (char *) buf, NULL);
378119815Smarcel	if (rc)
379119815Smarcel		return;
380119815Smarcel	lp =(struct disklabel *)(&buf[0]);
381119815Smarcel	if (lp->d_magic != DISKMAGIC) {
382119815Smarcel		sprintf(line, "%s: FFS  bad disklabel\n", prefix);
383119815Smarcel		pager_output(line);
384119815Smarcel		return;
385119815Smarcel	}
386119815Smarcel
387119815Smarcel	/* Print partitions */
388119815Smarcel	for (i = 0; i < lp->d_npartitions; i++) {
389119815Smarcel		/*
390119815Smarcel		 * For each partition, make sure we know what type of fs it
391119815Smarcel		 * is.  If not, then skip it.
392119815Smarcel		 */
393119815Smarcel		fstype = lp->d_partitions[i].p_fstype;
394119815Smarcel		if (fstype != FS_BSDFFS &&
395119815Smarcel		    fstype != FS_SWAP &&
396119815Smarcel		    fstype != FS_VINUM)
397119815Smarcel			continue;
398119815Smarcel
399119815Smarcel		/* Only print out statistics in verbose mode */
400119815Smarcel		if (verbose)
401119815Smarcel			sprintf(line, "  %s%c: %s %s (%d - %d)\n",
402119815Smarcel				prefix, 'a' + i,
403119815Smarcel			    (fstype == FS_SWAP) ? "swap " :
404119815Smarcel			    (fstype == FS_VINUM) ? "vinum" :
405119815Smarcel			    "FFS  ",
406119815Smarcel			    display_size(lp->d_partitions[i].p_size),
407119815Smarcel			    lp->d_partitions[i].p_offset,
408119815Smarcel			    (lp->d_partitions[i].p_offset
409119815Smarcel			     + lp->d_partitions[i].p_size));
410119815Smarcel		else
411119815Smarcel			sprintf(line, "  %s%c: %s\n", prefix, 'a' + i,
412119815Smarcel			    (fstype == FS_SWAP) ? "swap" :
413131043Sphk			    (fstype == FS_VINUM) ? "vinum" :
414119815Smarcel			    "FFS");
415131043Sphk		pager_output(line);
416119815Smarcel	}
417119815Smarcel}
418119815Smarcel
419119815Smarcelstatic void
420119815Smarceldisk_printslice(struct disk_devdesc *dev, int slice,
421119815Smarcel    struct dos_partition *dp, char *prefix, int verbose)
422119815Smarcel{
423119815Smarcel	char stats[80];
424119815Smarcel	char line[80];
425119815Smarcel
426119815Smarcel	if (verbose)
427119815Smarcel		sprintf(stats, " %s (%d - %d)", display_size(dp->dp_size),
428119815Smarcel		    dp->dp_start, dp->dp_start + dp->dp_size);
429119815Smarcel	else
430119815Smarcel		stats[0] = '\0';
431119815Smarcel
432119815Smarcel	switch (dp->dp_typ) {
433119815Smarcel	case DOSPTYP_386BSD:
434119815Smarcel		disk_printbsdslice(dev, (daddr_t)dp->dp_start,
435119815Smarcel		     prefix, verbose);
436119815Smarcel		return;
437119815Smarcel	case DOSPTYP_LINSWP:
438119815Smarcel		sprintf(line, "%s: Linux swap%s\n", prefix, stats);
439119815Smarcel		break;
440119815Smarcel	case DOSPTYP_LINUX:
441119815Smarcel		/*
442120143Smarcel		 * XXX
443119815Smarcel		 * read the superblock to confirm this is an ext2fs partition?
444119815Smarcel		 */
445157300Smarcel		sprintf(line, "%s: ext2fs%s\n", prefix, stats);
446157418Smarcel		break;
447119815Smarcel	case 0x00:				/* unused partition */
448119815Smarcel	case DOSPTYP_EXT:
449119815Smarcel		return;
450120143Smarcel	case 0x01:
451120143Smarcel		sprintf(line, "%s: FAT-12%s\n", prefix, stats);
452120143Smarcel		break;
453157300Smarcel	case 0x04:
454120143Smarcel	case 0x06:
455119815Smarcel	case 0x0e:
456119815Smarcel		sprintf(line, "%s: FAT-16%s\n", prefix, stats);
457119815Smarcel		break;
458119815Smarcel	case 0x07:
459119815Smarcel		sprintf(line, "%s: NTFS/HPFS%s\n", prefix, stats);
460119815Smarcel		break;
461119815Smarcel	case 0x0b:
462119815Smarcel	case 0x0c:
463119815Smarcel		sprintf(line, "%s: FAT-32%s\n", prefix, stats);
464119815Smarcel		break;
465119815Smarcel	default:
466157300Smarcel		sprintf(line, "%s: Unknown fs: 0x%x %s\n", prefix, dp->dp_typ,
467119815Smarcel		    stats);
468157300Smarcel	}
469131043Sphk	pager_output(line);
470131043Sphk}
471131043Sphk
472131043Sphkint
473155973Smarceldisk_printmbr(struct disk_devdesc *dev, char *prefix, int verbose)
474119815Smarcel{
475119815Smarcel	struct dos_partition	*slicetab;
476119815Smarcel	int			nslices, i;
477119815Smarcel	int			rc;
478119815Smarcel	char			line[80];
479119815Smarcel
480119815Smarcel	rc = disk_readslicetab(dev, &slicetab, &nslices);
481119815Smarcel	if (rc)
482137709Smarcel		return (rc);
483120022Smarcel	for (i = 0; i < nslices; i++) {
484119815Smarcel		sprintf(line, "%ss%d", prefix, i + 1);
485119815Smarcel		disk_printslice(dev, i, &slicetab[i], line, verbose);
486120143Smarcel	}
487157300Smarcel	free(slicetab);
488119815Smarcel	return (0);
489119815Smarcel}
490119815Smarcel
491119815Smarcel#ifdef LOADER_GPT_SUPPORT
492119815Smarcel
493119815Smarcelstatic int
494119815Smarceldisk_readgpt(struct disk_devdesc *dev, struct gpt_part **gptp, int *ngptp)
495119815Smarcel{
496119815Smarcel	struct dos_partition	*dp;
497119815Smarcel	struct gpt_hdr		*hdr;
498120022Smarcel	struct gpt_ent		*ent;
499120022Smarcel	struct gpt_part		*gptab = NULL;
500120022Smarcel	int			entries_per_sec, rc, i, part;
501120022Smarcel	daddr_t			lba, elba;
502120022Smarcel	uint8_t			gpt[DISK_SECSIZE], tbl[DISK_SECSIZE];
503120022Smarcel
504120022Smarcel	/*
505120022Smarcel	 * Following calculations attempt to determine the correct value
506120022Smarcel	 * for dev->d_offset by looking for the slice and partition specified,
507120022Smarcel	 * or searching for reasonable defaults.
508120022Smarcel	 */
509120022Smarcel	rc = 0;
510120022Smarcel
511120022Smarcel	/* First, read the MBR and see if we have a PMBR. */
512120022Smarcel	rc = dev->d_dev->dv_strategy(dev, F_READ, 0, DISK_SECSIZE,
513120022Smarcel		(char *) tbl, NULL);
514120022Smarcel	if (rc) {
515120022Smarcel		DEBUG("error reading MBR");
516120022Smarcel		return (EIO);
517120022Smarcel	}
518120022Smarcel
519120022Smarcel	/* Check the slice table magic. */
520120022Smarcel	if (tbl[0x1fe] != 0x55 || tbl[0x1ff] != 0xaa)
521120022Smarcel		return (ENXIO);
522120022Smarcel
523120022Smarcel	/* Check for GPT slice. */
524120022Smarcel	part = 0;
525120022Smarcel	dp = (struct dos_partition *)(tbl + DOSPARTOFF);
526120022Smarcel	for (i = 0; i < NDOSPART; i++) {
527120022Smarcel		if (dp[i].dp_typ == 0xee)
528137707Smarcel			part++;
529137707Smarcel		else if ((part != 1) && (dp[i].dp_typ != 0x00))
530137707Smarcel			return (EINVAL);
531137707Smarcel	}
532137707Smarcel	if (part != 1)
533137707Smarcel		return (EINVAL);
534137707Smarcel
535137707Smarcel	/* Read primary GPT table header. */
536137709Smarcel	rc = dev->d_dev->dv_strategy(dev, F_READ, 1, DISK_SECSIZE,
537137709Smarcel		(char *) gpt, NULL);
538137709Smarcel	if (rc) {
539137709Smarcel		DEBUG("error reading GPT header");
540137709Smarcel		return (EIO);
541137707Smarcel	}
542119815Smarcel	hdr = (struct gpt_hdr *)gpt;
543120143Smarcel	if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 ||
544120143Smarcel	    hdr->hdr_lba_self != 1 || hdr->hdr_revision < 0x00010000 ||
545119815Smarcel	    hdr->hdr_entsz < sizeof(*ent) ||
546157300Smarcel	    DISK_SECSIZE % hdr->hdr_entsz != 0) {
547120143Smarcel		DEBUG("Invalid GPT header\n");
548119815Smarcel		return (EINVAL);
549119815Smarcel	}
550119815Smarcel
551119815Smarcel	/* Walk the partition table to count valid partitions. */
552119815Smarcel	part = 0;
553119815Smarcel	entries_per_sec = DISK_SECSIZE / hdr->hdr_entsz;
554119815Smarcel	elba = hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec;
555119815Smarcel	for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
556119815Smarcel		rc = dev->d_dev->dv_strategy(dev, F_READ, lba, DISK_SECSIZE,
557119815Smarcel			(char *) tbl, NULL);
558157300Smarcel		if (rc) {
559119815Smarcel			DEBUG("error reading GPT table");
560120143Smarcel			return (EIO);
561157300Smarcel		}
562119815Smarcel		for (i = 0; i < entries_per_sec; i++) {
563120143Smarcel			ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
564119815Smarcel			if (uuid_is_nil(&ent->ent_type, NULL) ||
565119815Smarcel			    ent->ent_lba_start == 0 ||
566119815Smarcel			    ent->ent_lba_end < ent->ent_lba_start)
567157300Smarcel				continue;
568119815Smarcel			part++;
569155971Smarcel		}
570119815Smarcel	}
571155971Smarcel
572119815Smarcel	/* Save the important information about all the valid partitions. */
573155971Smarcel	if (part != 0) {
574119815Smarcel		gptab = malloc(part * sizeof(struct gpt_part));
575157300Smarcel		part = 0;
576119815Smarcel		for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
577155971Smarcel			rc = dev->d_dev->dv_strategy(dev, F_READ, lba, DISK_SECSIZE,
578119815Smarcel				(char *) tbl, NULL);
579155971Smarcel			if (rc) {
580119815Smarcel				DEBUG("error reading GPT table");
581119815Smarcel				free(gptab);
582119815Smarcel				return (EIO);
583119815Smarcel			}
584119815Smarcel			for (i = 0; i < entries_per_sec; i++) {
585119815Smarcel				ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
586119815Smarcel				if (uuid_is_nil(&ent->ent_type, NULL) ||
587119815Smarcel				    ent->ent_lba_start == 0 ||
588119815Smarcel				    ent->ent_lba_end < ent->ent_lba_start)
589120143Smarcel					continue;
590119815Smarcel				gptab[part].gp_index = (lba - hdr->hdr_lba_table) *
591119815Smarcel					entries_per_sec + i + 1;
592157300Smarcel				gptab[part].gp_type = ent->ent_type;
593120143Smarcel				gptab[part].gp_start = ent->ent_lba_start;
594157300Smarcel				gptab[part].gp_end = ent->ent_lba_end;
595120143Smarcel				part++;
596119815Smarcel			}
597119815Smarcel		}
598119815Smarcel	}
599119815Smarcel
600119815Smarcel	*gptp = gptab;
601119815Smarcel	*ngptp = part;
602119815Smarcel	return (0);
603129757Stmm}
604119815Smarcel
605119815Smarcelstatic struct gpt_part *
606119815Smarceldisk_bestgpt(struct gpt_part *gpt, int ngpt)
607119815Smarcel{
608119815Smarcel	struct gpt_part *gp, *prefpart;
609119815Smarcel	int i, pref, preflevel;
610119815Smarcel
611119815Smarcel	prefpart = NULL;
612119815Smarcel	preflevel = PREF_NONE;
613119815Smarcel
614119815Smarcel	gp = gpt;
615119815Smarcel	for (i = 0; i < ngpt; i++, gp++) {
616119815Smarcel		/* Windows. XXX: Also Linux. */
617119815Smarcel		if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
618119815Smarcel			pref = PREF_DOS;
619119815Smarcel		/* FreeBSD */
620119815Smarcel		else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL) ||
621119815Smarcel		         uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
622119815Smarcel			pref = PREF_FBSD;
623119815Smarcel		else
624119815Smarcel			pref = PREF_NONE;
625119815Smarcel		if (pref < preflevel) {
626129757Stmm			preflevel = pref;
627119815Smarcel			prefpart = gp;
628129757Stmm		}
629119815Smarcel	}
630119815Smarcel	return (prefpart);
631119815Smarcel}
632119815Smarcel
633129757Stmmint
634129757Stmmdisk_opengpt(struct disk_devdesc *dev)
635119815Smarcel{
636119815Smarcel	struct gpt_part		*gpt = NULL, *gp;
637119815Smarcel	int			rc, ngpt, i;
638157418Smarcel
639119815Smarcel	rc = disk_readgpt(dev, &gpt, &ngpt);
640119815Smarcel	if (rc)
641119815Smarcel		return (rc);
642119815Smarcel
643119815Smarcel	/* Is this a request for the whole disk? */
644119815Smarcel	if (dev->d_slice < 0) {
645157418Smarcel		dev->d_offset = 0;
646119815Smarcel		rc = 0;
647119815Smarcel		goto out;
648119815Smarcel	}
649119815Smarcel
650129757Stmm	/*
651119815Smarcel	 * If a partition number was supplied, then the user is trying to use
652119815Smarcel	 * an MBR address rather than a GPT address, so fail.
653119815Smarcel	 */
654119815Smarcel	if (dev->d_partition != 0xff) {
655119815Smarcel		rc = ENOENT;
656119815Smarcel		goto out;
657119815Smarcel	}
658119815Smarcel
659119815Smarcel	/* If a slice number was supplied but not found, this is an error. */
660119815Smarcel	gp = NULL;
661119815Smarcel	if (dev->d_slice > 0) {
662119815Smarcel		for (i = 0; i < ngpt; i++) {
663119815Smarcel			if (gpt[i].gp_index == dev->d_slice) {
664119815Smarcel				gp = &gpt[i];
665119815Smarcel				break;
666119815Smarcel			}
667119815Smarcel		}
668129757Stmm		if (gp == NULL) {
669129757Stmm			DEBUG("partition %d not found", dev->d_slice);
670129757Stmm			rc = ENOENT;
671119815Smarcel			goto out;
672129757Stmm		}
673119815Smarcel	}
674119815Smarcel
675119815Smarcel	/* Try to auto-detect the best partition. */
676119815Smarcel	if (dev->d_slice == 0) {
677119815Smarcel		gp = disk_bestgpt(gpt, ngpt);
678129757Stmm		if (gp == NULL) {
679129757Stmm			rc = ENOENT;
680129757Stmm			goto out;
681129757Stmm		}
682129757Stmm		dev->d_slice = gp->gp_index;
683129757Stmm	}
684129757Stmm
685119815Smarcel	dev->d_offset = gp->gp_start;
686119815Smarcel	rc = 0;
687119815Smarcel
688119815Smarcelout:
689119815Smarcel	if (gpt)
690119815Smarcel		free(gpt);
691119815Smarcel	return (rc);
692119815Smarcel}
693119815Smarcel
694132650Smarcelstatic void
695129757Stmmdisk_printgptpart(struct disk_devdesc *dev, struct gpt_part *gp,
696119815Smarcel    char *prefix, int verbose)
697119815Smarcel{
698119815Smarcel	char stats[80];
699119815Smarcel	char line[96];
700119815Smarcel
701119815Smarcel	if (verbose)
702119815Smarcel		sprintf(stats, " %s",
703129757Stmm			display_size(gp->gp_end + 1 - gp->gp_start));
704119815Smarcel	else
705119815Smarcel		stats[0] = '\0';
706129757Stmm
707119815Smarcel	if (uuid_equal(&gp->gp_type, &efi, NULL))
708119815Smarcel		sprintf(line, "%s: EFI         %s\n", prefix, stats);
709129757Stmm	else if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
710119815Smarcel		sprintf(line, "%s: FAT/NTFS    %s\n", prefix, stats);
711119815Smarcel	else if (uuid_equal(&gp->gp_type, &freebsd_boot, NULL))
712129757Stmm		sprintf(line, "%s: FreeBSD boot%s\n", prefix, stats);
713119815Smarcel	else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL))
714119815Smarcel		sprintf(line, "%s: FreeBSD UFS %s\n", prefix, stats);
715119815Smarcel	else if (uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
716119943Smarcel		sprintf(line, "%s: FreeBSD ZFS %s\n", prefix, stats);
717119815Smarcel	else if (uuid_equal(&gp->gp_type, &freebsd_swap, NULL))
718119815Smarcel		sprintf(line, "%s: FreeBSD swap%s\n", prefix, stats);
719119815Smarcel	else
720119815Smarcel		sprintf(line,
721119815Smarcel		    "%s: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x%s\n",
722119815Smarcel		    prefix,
723119815Smarcel		    gp->gp_type.time_low, gp->gp_type.time_mid,
724119815Smarcel		    gp->gp_type.time_hi_and_version,
725119815Smarcel		    gp->gp_type.clock_seq_hi_and_reserved,
726119815Smarcel		    gp->gp_type.clock_seq_low,
727119815Smarcel		    gp->gp_type.node[0],
728133220Smarcel		    gp->gp_type.node[1],
729133220Smarcel		    gp->gp_type.node[2],
730133220Smarcel		    gp->gp_type.node[3],
731133220Smarcel		    gp->gp_type.node[4],
732133220Smarcel		    gp->gp_type.node[5],
733133220Smarcel		    stats);
734133220Smarcel	pager_output(line);
735120022Smarcel}
736120022Smarcel
737120022Smarcelstatic int
738120022Smarceldisk_printgpt(struct disk_devdesc *dev, char *prefix, int verbose)
739120022Smarcel{
740133220Smarcel	struct gpt_part		*gpt = NULL;
741120022Smarcel	int			rc, ngpt, i;
742119815Smarcel	char			line[80];
743119815Smarcel
744119815Smarcel	rc = disk_readgpt(dev, &gpt, &ngpt);
745119815Smarcel	if (rc)
746119815Smarcel		return (rc);
747119815Smarcel	for (i = 0; i < ngpt; i++) {
748119815Smarcel		sprintf(line, "%sp%d", prefix, i + 1);
749119815Smarcel		disk_printgptpart(dev, &gpt[i], line, verbose);
750119815Smarcel	}
751119815Smarcel	free(gpt);
752119815Smarcel	return (0);
753157300Smarcel}
754120146Smarcel
755120146Smarcel#endif
756120146Smarcel
757120146Smarcelint
758119815Smarceldisk_open(struct disk_devdesc *dev)
759120146Smarcel{
760119815Smarcel	int rc;
761119815Smarcel
762119815Smarcel	/*
763119815Smarcel	 * While we are reading disk metadata, make sure we do it relative
764119815Smarcel	 * to the start of the disk
765119815Smarcel	 */
766120146Smarcel	dev->d_offset = 0;
767119815Smarcel
768120146Smarcel#ifdef LOADER_GPT_SUPPORT
769120146Smarcel	rc = disk_opengpt(dev);
770120146Smarcel	if (rc)
771120146Smarcel#endif
772120146Smarcel		rc = disk_openmbr(dev);
773120146Smarcel
774157300Smarcel	return (rc);
775119815Smarcel}
776119815Smarcel
777119815Smarcelvoid
778119815Smarceldisk_print(struct disk_devdesc *dev, char *prefix, int verbose)
779119815Smarcel{
780119815Smarcel	int			rc;
781119815Smarcel
782119815Smarcel#ifdef LOADER_GPT_SUPPORT
783119815Smarcel	rc = disk_printgpt(dev, prefix, verbose);
784119815Smarcel	if (rc == 0)
785119815Smarcel		return;
786119815Smarcel#endif
787119815Smarcel	disk_printmbr(dev, prefix, verbose);
788119815Smarcel}
789131043Sphk