disk.c revision 223712
1/*-
2 * Copyright (c) 1998 Michael Smith <msmith@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 AUTHOR 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 AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/boot/common/disk.c 223712 2011-07-01 18:31:59Z marius $");
29
30/*
31 * MBR/GPT partitioned disk device handling.
32 *
33 * Ideas and algorithms from:
34 *
35 * - NetBSD libi386/biosdisk.c
36 * - FreeBSD biosboot/disk.c
37 *
38 */
39
40#include <stand.h>
41
42#include <sys/diskmbr.h>
43#include <sys/disklabel.h>
44#include <sys/gpt.h>
45
46#include <stdarg.h>
47#include <uuid.h>
48
49#include <bootstrap.h>
50
51#include "disk.h"
52
53#ifdef DISK_DEBUG
54# define DEBUG(fmt, args...)	printf("%s: " fmt "\n" , __func__ , ## args)
55#else
56# define DEBUG(fmt, args...)
57#endif
58
59/*
60 * Search for a slice with the following preferences:
61 *
62 * 1: Active FreeBSD slice
63 * 2: Non-active FreeBSD slice
64 * 3: Active Linux slice
65 * 4: non-active Linux slice
66 * 5: Active FAT/FAT32 slice
67 * 6: non-active FAT/FAT32 slice
68 */
69#define PREF_RAWDISK	0
70#define PREF_FBSD_ACT	1
71#define PREF_FBSD	2
72#define PREF_LINUX_ACT	3
73#define PREF_LINUX	4
74#define PREF_DOS_ACT	5
75#define PREF_DOS	6
76#define PREF_NONE	7
77
78#ifdef LOADER_GPT_SUPPORT
79
80struct gpt_part {
81	int		gp_index;
82	uuid_t		gp_type;
83	uint64_t	gp_start;
84	uint64_t	gp_end;
85};
86
87static uuid_t efi = GPT_ENT_TYPE_EFI;
88static uuid_t freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
89static uuid_t freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
90static uuid_t freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
91static uuid_t freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
92static uuid_t ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
93
94#endif
95
96#if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
97
98/* Given a size in 512 byte sectors, convert it to a human-readable number. */
99static char *
100display_size(uint64_t size)
101{
102	static char buf[80];
103	char unit;
104
105	size /= 2;
106	unit = 'K';
107	if (size >= 10485760000LL) {
108		size /= 1073741824;
109		unit = 'T';
110	} else if (size >= 10240000) {
111		size /= 1048576;
112		unit = 'G';
113	} else if (size >= 10000) {
114		size /= 1024;
115		unit = 'M';
116	}
117	sprintf(buf, "%.6ld%cB", (long)size, unit);
118	return (buf);
119}
120
121#endif
122
123#ifdef LOADER_MBR_SUPPORT
124
125static void
126disk_checkextended(struct disk_devdesc *dev,
127    struct dos_partition *slicetab, int slicenum, int *nslicesp)
128{
129	uint8_t			buf[DISK_SECSIZE];
130	struct dos_partition	*dp;
131	uint32_t		base;
132	int			rc, i, start, end;
133
134	dp = &slicetab[slicenum];
135	start = *nslicesp;
136
137	if (dp->dp_size == 0)
138		goto done;
139	if (dp->dp_typ != DOSPTYP_EXT)
140		goto done;
141	rc = dev->d_dev->dv_strategy(dev, F_READ, dp->dp_start, DISK_SECSIZE,
142		(char *) buf, NULL);
143	if (rc)
144		goto done;
145	if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) {
146		DEBUG("no magic in extended table");
147		goto done;
148	}
149	base = dp->dp_start;
150	dp = (struct dos_partition *) &buf[DOSPARTOFF];
151	for (i = 0; i < NDOSPART; i++, dp++) {
152		if (dp->dp_size == 0)
153			continue;
154		if (*nslicesp == NEXTDOSPART)
155			goto done;
156		dp->dp_start += base;
157		bcopy(dp, &slicetab[*nslicesp], sizeof(*dp));
158		(*nslicesp)++;
159	}
160	end = *nslicesp;
161
162	/*
163	 * now, recursively check the slices we just added
164	 */
165	for (i = start; i < end; i++)
166		disk_checkextended(dev, slicetab, i, nslicesp);
167done:
168	return;
169}
170
171static int
172disk_readslicetab(struct disk_devdesc *dev,
173    struct dos_partition **slicetabp, int *nslicesp)
174{
175	struct dos_partition	*slicetab = NULL;
176	int			nslices, i;
177	int			rc;
178	uint8_t			buf[DISK_SECSIZE];
179
180	/*
181	 * Find the slice in the DOS slice table.
182	 */
183	rc = dev->d_dev->dv_strategy(dev, F_READ, 0, DISK_SECSIZE,
184		(char *) buf, NULL);
185	if (rc) {
186		DEBUG("error reading MBR");
187		return (rc);
188	}
189
190	/*
191	 * Check the slice table magic.
192	 */
193	if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) {
194		DEBUG("no slice table/MBR (no magic)");
195		return (rc);
196	}
197
198	/*
199	 * copy the partition table, then pick up any extended partitions.
200	 */
201	slicetab = malloc(NEXTDOSPART * sizeof(struct dos_partition));
202	bcopy(buf + DOSPARTOFF, slicetab,
203		sizeof(struct dos_partition) * NDOSPART);
204	nslices = NDOSPART;		/* extended slices start here */
205	for (i = 0; i < NDOSPART; i++)
206		disk_checkextended(dev, slicetab, i, &nslices);
207
208	*slicetabp = slicetab;
209	*nslicesp = nslices;
210	return (0);
211}
212
213/*
214 * Search for the best MBR slice (typically the first FreeBSD slice).
215 */
216static int
217disk_bestslice(struct dos_partition *slicetab, int nslices)
218{
219	struct dos_partition *dp;
220	int pref, preflevel;
221	int i, prefslice;
222
223	prefslice = 0;
224	preflevel = PREF_NONE;
225
226	dp = &slicetab[0];
227	for (i = 0; i < nslices; i++, dp++) {
228		switch (dp->dp_typ) {
229		case DOSPTYP_386BSD:		/* FreeBSD */
230			pref = dp->dp_flag & 0x80 ? PREF_FBSD_ACT : PREF_FBSD;
231			break;
232
233		case DOSPTYP_LINUX:
234			pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX;
235			break;
236
237		case 0x01:		/* DOS/Windows */
238		case 0x04:
239		case 0x06:
240		case 0x0b:
241		case 0x0c:
242		case 0x0e:
243			pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS;
244			break;
245
246		default:
247		        pref = PREF_NONE;
248		}
249		if (pref < preflevel) {
250			preflevel = pref;
251			prefslice = i + 1;
252		}
253	}
254	return (prefslice);
255}
256
257static int
258disk_openmbr(struct disk_devdesc *dev)
259{
260	struct dos_partition	*slicetab = NULL, *dptr;
261	int			nslices, sector, slice;
262	int			rc;
263	uint8_t			buf[DISK_SECSIZE];
264	struct disklabel	*lp;
265
266	/*
267	 * Following calculations attempt to determine the correct value
268	 * for dev->d_offset by looking for the slice and partition specified,
269	 * or searching for reasonable defaults.
270	 */
271	rc = disk_readslicetab(dev, &slicetab, &nslices);
272	if (rc)
273		return (rc);
274
275	/*
276	 * if a slice number was supplied but not found, this is an error.
277	 */
278	if (dev->d_slice > 0) {
279		slice = dev->d_slice - 1;
280		if (slice >= nslices) {
281			DEBUG("slice %d not found", slice);
282			rc = EPART;
283			goto out;
284		}
285	}
286
287	/*
288	 * Check for the historically bogus MBR found on true dedicated disks
289	 */
290	if (slicetab[3].dp_typ == DOSPTYP_386BSD &&
291	    slicetab[3].dp_start == 0 && slicetab[3].dp_size == 50000) {
292		sector = 0;
293		goto unsliced;
294	}
295
296	/*
297	 * Try to auto-detect the best slice; this should always give
298	 * a slice number
299	 */
300	if (dev->d_slice == 0) {
301		slice = disk_bestslice(slicetab, nslices);
302		if (slice == -1) {
303			rc = ENOENT;
304			goto out;
305		}
306		dev->d_slice = slice;
307	}
308
309	/*
310	 * Accept the supplied slice number unequivocally (we may be looking
311	 * at a DOS partition).
312	 * Note: we number 1-4, offsets are 0-3
313	 */
314	dptr = &slicetab[dev->d_slice - 1];
315	sector = dptr->dp_start;
316	DEBUG("slice entry %d at %d, %d sectors",
317		dev->d_slice - 1, sector, dptr->dp_size);
318
319unsliced:
320	/*
321	 * Now we have the slice offset, look for the partition in the
322	 * disklabel if we have a partition to start with.
323	 *
324	 * XXX we might want to check the label checksum.
325	 */
326	if (dev->d_partition < 0) {
327		/* no partition, must be after the slice */
328		DEBUG("opening raw slice");
329		dev->d_offset = sector;
330		rc = 0;
331		goto out;
332	}
333
334	rc = dev->d_dev->dv_strategy(dev, F_READ, sector + LABELSECTOR,
335            DISK_SECSIZE, (char *) buf, NULL);
336	if (rc) {
337		DEBUG("error reading disklabel");
338		goto out;
339	}
340
341	lp = (struct disklabel *) buf;
342
343	if (lp->d_magic != DISKMAGIC) {
344		DEBUG("no disklabel");
345		rc = ENOENT;
346		goto out;
347	}
348	if (dev->d_partition >= lp->d_npartitions) {
349		DEBUG("partition '%c' exceeds partitions in table (a-'%c')",
350		  'a' + dev->d_partition,
351		  'a' + lp->d_npartitions);
352		rc = EPART;
353		goto out;
354	}
355
356	dev->d_offset =
357		lp->d_partitions[dev->d_partition].p_offset -
358		lp->d_partitions[RAW_PART].p_offset +
359		sector;
360	rc = 0;
361
362out:
363	if (slicetab)
364		free(slicetab);
365	return (rc);
366}
367
368/*
369 * Print out each valid partition in the disklabel of a FreeBSD slice.
370 * For size calculations, we assume a 512 byte sector size.
371 */
372static void
373disk_printbsdslice(struct disk_devdesc *dev, daddr_t offset,
374    char *prefix, int verbose)
375{
376	char			line[80];
377	char			buf[DISK_SECSIZE];
378	struct disklabel	*lp;
379	int			i, rc, fstype;
380
381	/* read disklabel */
382	rc = dev->d_dev->dv_strategy(dev, F_READ, offset + LABELSECTOR,
383		DISK_SECSIZE, (char *) buf, NULL);
384	if (rc)
385		return;
386	lp =(struct disklabel *)(&buf[0]);
387	if (lp->d_magic != DISKMAGIC) {
388		sprintf(line, "%s: FFS  bad disklabel\n", prefix);
389		pager_output(line);
390		return;
391	}
392
393	/* Print partitions */
394	for (i = 0; i < lp->d_npartitions; i++) {
395		/*
396		 * For each partition, make sure we know what type of fs it
397		 * is.  If not, then skip it.
398		 */
399		fstype = lp->d_partitions[i].p_fstype;
400		if (fstype != FS_BSDFFS &&
401		    fstype != FS_SWAP &&
402		    fstype != FS_VINUM)
403			continue;
404
405		/* Only print out statistics in verbose mode */
406		if (verbose)
407			sprintf(line, "  %s%c: %s %s (%d - %d)\n",
408				prefix, 'a' + i,
409			    (fstype == FS_SWAP) ? "swap " :
410			    (fstype == FS_VINUM) ? "vinum" :
411			    "FFS  ",
412			    display_size(lp->d_partitions[i].p_size),
413			    lp->d_partitions[i].p_offset,
414			    (lp->d_partitions[i].p_offset
415			     + lp->d_partitions[i].p_size));
416		else
417			sprintf(line, "  %s%c: %s\n", prefix, 'a' + i,
418			    (fstype == FS_SWAP) ? "swap" :
419			    (fstype == FS_VINUM) ? "vinum" :
420			    "FFS");
421		pager_output(line);
422	}
423}
424
425static void
426disk_printslice(struct disk_devdesc *dev, int slice,
427    struct dos_partition *dp, char *prefix, int verbose)
428{
429	char stats[80];
430	char line[80];
431
432	if (verbose)
433		sprintf(stats, " %s (%d - %d)", display_size(dp->dp_size),
434		    dp->dp_start, dp->dp_start + dp->dp_size);
435	else
436		stats[0] = '\0';
437
438	switch (dp->dp_typ) {
439	case DOSPTYP_386BSD:
440		disk_printbsdslice(dev, (daddr_t)dp->dp_start,
441		     prefix, verbose);
442		return;
443	case DOSPTYP_LINSWP:
444		sprintf(line, "%s: Linux swap%s\n", prefix, stats);
445		break;
446	case DOSPTYP_LINUX:
447		/*
448		 * XXX
449		 * read the superblock to confirm this is an ext2fs partition?
450		 */
451		sprintf(line, "%s: ext2fs%s\n", prefix, stats);
452		break;
453	case 0x00:				/* unused partition */
454	case DOSPTYP_EXT:
455		return;
456	case 0x01:
457		sprintf(line, "%s: FAT-12%s\n", prefix, stats);
458		break;
459	case 0x04:
460	case 0x06:
461	case 0x0e:
462		sprintf(line, "%s: FAT-16%s\n", prefix, stats);
463		break;
464	case 0x07:
465		sprintf(line, "%s: NTFS/HPFS%s\n", prefix, stats);
466		break;
467	case 0x0b:
468	case 0x0c:
469		sprintf(line, "%s: FAT-32%s\n", prefix, stats);
470		break;
471	default:
472		sprintf(line, "%s: Unknown fs: 0x%x %s\n", prefix, dp->dp_typ,
473		    stats);
474	}
475	pager_output(line);
476}
477
478static int
479disk_printmbr(struct disk_devdesc *dev, char *prefix, int verbose)
480{
481	struct dos_partition	*slicetab;
482	int			nslices, i;
483	int			rc;
484	char			line[80];
485
486	rc = disk_readslicetab(dev, &slicetab, &nslices);
487	if (rc)
488		return (rc);
489	for (i = 0; i < nslices; i++) {
490		sprintf(line, "%ss%d", prefix, i + 1);
491		disk_printslice(dev, i, &slicetab[i], line, verbose);
492	}
493	free(slicetab);
494	return (0);
495}
496
497#endif
498
499#ifdef LOADER_GPT_SUPPORT
500
501static int
502disk_readgpt(struct disk_devdesc *dev, struct gpt_part **gptp, int *ngptp)
503{
504	struct dos_partition	*dp;
505	struct gpt_hdr		*hdr;
506	struct gpt_ent		*ent;
507	struct gpt_part		*gptab = NULL;
508	int			entries_per_sec, rc, i, part;
509	daddr_t			lba, elba;
510	uint8_t			gpt[DISK_SECSIZE], tbl[DISK_SECSIZE];
511
512	/*
513	 * Following calculations attempt to determine the correct value
514	 * for dev->d_offset by looking for the slice and partition specified,
515	 * or searching for reasonable defaults.
516	 */
517	rc = 0;
518
519	/* First, read the MBR and see if we have a PMBR. */
520	rc = dev->d_dev->dv_strategy(dev, F_READ, 0, DISK_SECSIZE,
521		(char *) tbl, NULL);
522	if (rc) {
523		DEBUG("error reading MBR");
524		return (EIO);
525	}
526
527	/* Check the slice table magic. */
528	if (tbl[0x1fe] != 0x55 || tbl[0x1ff] != 0xaa)
529		return (ENXIO);
530
531	/* Check for GPT slice. */
532	part = 0;
533	dp = (struct dos_partition *)(tbl + DOSPARTOFF);
534	for (i = 0; i < NDOSPART; i++) {
535		if (dp[i].dp_typ == 0xee)
536			part++;
537		else if ((part != 1) && (dp[i].dp_typ != 0x00))
538			return (EINVAL);
539	}
540	if (part != 1)
541		return (EINVAL);
542
543	/* Read primary GPT table header. */
544	rc = dev->d_dev->dv_strategy(dev, F_READ, 1, DISK_SECSIZE,
545		(char *) gpt, NULL);
546	if (rc) {
547		DEBUG("error reading GPT header");
548		return (EIO);
549	}
550	hdr = (struct gpt_hdr *)gpt;
551	if (bcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0 ||
552	    hdr->hdr_lba_self != 1 || hdr->hdr_revision < 0x00010000 ||
553	    hdr->hdr_entsz < sizeof(*ent) ||
554	    DISK_SECSIZE % hdr->hdr_entsz != 0) {
555		DEBUG("Invalid GPT header\n");
556		return (EINVAL);
557	}
558
559	/* Walk the partition table to count valid partitions. */
560	part = 0;
561	entries_per_sec = DISK_SECSIZE / hdr->hdr_entsz;
562	elba = hdr->hdr_lba_table + hdr->hdr_entries / entries_per_sec;
563	for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
564		rc = dev->d_dev->dv_strategy(dev, F_READ, lba, DISK_SECSIZE,
565			(char *) tbl, NULL);
566		if (rc) {
567			DEBUG("error reading GPT table");
568			return (EIO);
569		}
570		for (i = 0; i < entries_per_sec; i++) {
571			ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
572			if (uuid_is_nil(&ent->ent_type, NULL) ||
573			    ent->ent_lba_start == 0 ||
574			    ent->ent_lba_end < ent->ent_lba_start)
575				continue;
576			part++;
577		}
578	}
579
580	/* Save the important information about all the valid partitions. */
581	if (part != 0) {
582		gptab = malloc(part * sizeof(struct gpt_part));
583		part = 0;
584		for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
585			rc = dev->d_dev->dv_strategy(dev, F_READ, lba, DISK_SECSIZE,
586				(char *) tbl, NULL);
587			if (rc) {
588				DEBUG("error reading GPT table");
589				free(gptab);
590				return (EIO);
591			}
592			for (i = 0; i < entries_per_sec; i++) {
593				ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
594				if (uuid_is_nil(&ent->ent_type, NULL) ||
595				    ent->ent_lba_start == 0 ||
596				    ent->ent_lba_end < ent->ent_lba_start)
597					continue;
598				gptab[part].gp_index = (lba - hdr->hdr_lba_table) *
599					entries_per_sec + i + 1;
600				gptab[part].gp_type = ent->ent_type;
601				gptab[part].gp_start = ent->ent_lba_start;
602				gptab[part].gp_end = ent->ent_lba_end;
603				part++;
604			}
605		}
606	}
607
608	*gptp = gptab;
609	*ngptp = part;
610	return (0);
611}
612
613static struct gpt_part *
614disk_bestgpt(struct gpt_part *gpt, int ngpt)
615{
616	struct gpt_part *gp, *prefpart;
617	int i, pref, preflevel;
618
619	prefpart = NULL;
620	preflevel = PREF_NONE;
621
622	gp = gpt;
623	for (i = 0; i < ngpt; i++, gp++) {
624		/* Windows. XXX: Also Linux. */
625		if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
626			pref = PREF_DOS;
627		/* FreeBSD */
628		else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL) ||
629		         uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
630			pref = PREF_FBSD;
631		else
632			pref = PREF_NONE;
633		if (pref < preflevel) {
634			preflevel = pref;
635			prefpart = gp;
636		}
637	}
638	return (prefpart);
639}
640
641static int
642disk_opengpt(struct disk_devdesc *dev)
643{
644	struct gpt_part		*gpt = NULL, *gp;
645	int			rc, ngpt, i;
646
647	rc = disk_readgpt(dev, &gpt, &ngpt);
648	if (rc)
649		return (rc);
650
651	/* Is this a request for the whole disk? */
652	if (dev->d_slice < 0) {
653		dev->d_offset = 0;
654		rc = 0;
655		goto out;
656	}
657
658	/*
659	 * If a partition number was supplied, then the user is trying to use
660	 * an MBR address rather than a GPT address, so fail.
661	 */
662	if (dev->d_partition != 0xff) {
663		rc = ENOENT;
664		goto out;
665	}
666
667	/* If a slice number was supplied but not found, this is an error. */
668	gp = NULL;
669	if (dev->d_slice > 0) {
670		for (i = 0; i < ngpt; i++) {
671			if (gpt[i].gp_index == dev->d_slice) {
672				gp = &gpt[i];
673				break;
674			}
675		}
676		if (gp == NULL) {
677			DEBUG("partition %d not found", dev->d_slice);
678			rc = ENOENT;
679			goto out;
680		}
681	}
682
683	/* Try to auto-detect the best partition. */
684	if (dev->d_slice == 0) {
685		gp = disk_bestgpt(gpt, ngpt);
686		if (gp == NULL) {
687			rc = ENOENT;
688			goto out;
689		}
690		dev->d_slice = gp->gp_index;
691	}
692
693	dev->d_offset = gp->gp_start;
694	rc = 0;
695
696out:
697	if (gpt)
698		free(gpt);
699	return (rc);
700}
701
702static void
703disk_printgptpart(struct disk_devdesc *dev, struct gpt_part *gp,
704    char *prefix, int verbose)
705{
706	char stats[80];
707	char line[96];
708
709	if (verbose)
710		sprintf(stats, " %s",
711			display_size(gp->gp_end + 1 - gp->gp_start));
712	else
713		stats[0] = '\0';
714
715	if (uuid_equal(&gp->gp_type, &efi, NULL))
716		sprintf(line, "%s: EFI         %s\n", prefix, stats);
717	else if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
718		sprintf(line, "%s: FAT/NTFS    %s\n", prefix, stats);
719	else if (uuid_equal(&gp->gp_type, &freebsd_boot, NULL))
720		sprintf(line, "%s: FreeBSD boot%s\n", prefix, stats);
721	else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL))
722		sprintf(line, "%s: FreeBSD UFS %s\n", prefix, stats);
723	else if (uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
724		sprintf(line, "%s: FreeBSD ZFS %s\n", prefix, stats);
725	else if (uuid_equal(&gp->gp_type, &freebsd_swap, NULL))
726		sprintf(line, "%s: FreeBSD swap%s\n", prefix, stats);
727	else
728		sprintf(line,
729		    "%s: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x%s\n",
730		    prefix,
731		    gp->gp_type.time_low, gp->gp_type.time_mid,
732		    gp->gp_type.time_hi_and_version,
733		    gp->gp_type.clock_seq_hi_and_reserved,
734		    gp->gp_type.clock_seq_low,
735		    gp->gp_type.node[0],
736		    gp->gp_type.node[1],
737		    gp->gp_type.node[2],
738		    gp->gp_type.node[3],
739		    gp->gp_type.node[4],
740		    gp->gp_type.node[5],
741		    stats);
742	pager_output(line);
743}
744
745static int
746disk_printgpt(struct disk_devdesc *dev, char *prefix, int verbose)
747{
748	struct gpt_part		*gpt = NULL;
749	int			rc, ngpt, i;
750	char			line[80];
751
752	rc = disk_readgpt(dev, &gpt, &ngpt);
753	if (rc)
754		return (rc);
755	for (i = 0; i < ngpt; i++) {
756		sprintf(line, "%sp%d", prefix, i + 1);
757		disk_printgptpart(dev, &gpt[i], line, verbose);
758	}
759	free(gpt);
760	return (0);
761}
762
763#endif
764
765int
766disk_open(struct disk_devdesc *dev)
767{
768	int rc;
769
770	rc = 0;
771	/*
772	 * While we are reading disk metadata, make sure we do it relative
773	 * to the start of the disk
774	 */
775	dev->d_offset = 0;
776
777#ifdef LOADER_GPT_SUPPORT
778	rc = disk_opengpt(dev);
779	if (rc)
780#endif
781#ifdef LOADER_MBR_SUPPORT
782		rc = disk_openmbr(dev);
783#endif
784
785	return (rc);
786}
787
788void
789disk_print(struct disk_devdesc *dev, char *prefix, int verbose)
790{
791
792#ifdef LOADER_GPT_SUPPORT
793	if (disk_printgpt(dev, prefix, verbose) == 0)
794		return;
795#endif
796#ifdef LOADER_MBR_SUPPORT
797	disk_printmbr(dev, prefix, verbose);
798#endif
799}
800