]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - sys/boot/common/disk.c
MFC r226549,r226550,r226551,r226552,r226553,r226554,r226568,r226569,r226611,
[FreeBSD/releng/9.0.git] / sys / boot / common / disk.c
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$");
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
80 struct gpt_part {
81         int             gp_index;
82         uuid_t          gp_type;
83         uint64_t        gp_start;
84         uint64_t        gp_end;
85 };
86
87 static uuid_t efi = GPT_ENT_TYPE_EFI;
88 static uuid_t freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
89 static uuid_t freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
90 static uuid_t freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
91 static uuid_t freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
92 static 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. */
99 static char *
100 display_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
125 static void
126 disk_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);
167 done:
168         return;
169 }
170
171 static int
172 disk_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  */
216 static int
217 disk_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
257 static int
258 disk_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
319 unsliced:
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
362 out:
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  */
372 static void
373 disk_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
425 static void
426 disk_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
478 static int
479 disk_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
501 static int
502 disk_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
613 static struct gpt_part *
614 disk_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
641 static int
642 disk_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
696 out:
697         if (gpt)
698                 free(gpt);
699         return (rc);
700 }
701
702 static void
703 disk_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
745 static int
746 disk_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
765 int
766 disk_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 == 0)
780                 return (0);
781 #endif
782 #ifdef LOADER_MBR_SUPPORT
783         rc = disk_openmbr(dev);
784 #endif
785
786         return (rc);
787 }
788
789 void
790 disk_print(struct disk_devdesc *dev, char *prefix, int verbose)
791 {
792
793 #ifdef LOADER_GPT_SUPPORT
794         if (disk_printgpt(dev, prefix, verbose) == 0)
795                 return;
796 #endif
797 #ifdef LOADER_MBR_SUPPORT
798         disk_printmbr(dev, prefix, verbose);
799 #endif
800 }