2 * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
31 * MBR/GPT partitioned disk device handling.
33 * Ideas and algorithms from:
35 * - NetBSD libi386/biosdisk.c
36 * - FreeBSD biosboot/disk.c
42 #include <sys/diskmbr.h>
43 #include <sys/disklabel.h>
49 #include <bootstrap.h>
54 # define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
56 # define DEBUG(fmt, args...)
60 * Search for a slice with the following preferences:
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
69 #define PREF_RAWDISK 0
70 #define PREF_FBSD_ACT 1
72 #define PREF_LINUX_ACT 3
74 #define PREF_DOS_ACT 5
78 #ifdef LOADER_GPT_SUPPORT
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;
96 #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
98 /* Given a size in 512 byte sectors, convert it to a human-readable number. */
100 display_size(uint64_t size)
107 if (size >= 10485760000LL) {
110 } else if (size >= 10240000) {
113 } else if (size >= 10000) {
117 sprintf(buf, "%.6ld%cB", (long)size, unit);
123 #ifdef LOADER_MBR_SUPPORT
126 disk_checkextended(struct disk_devdesc *dev,
127 struct dos_partition *slicetab, int slicenum, int *nslicesp)
129 uint8_t buf[DISK_SECSIZE];
130 struct dos_partition *dp;
132 int rc, i, start, end;
134 dp = &slicetab[slicenum];
137 if (dp->dp_size == 0)
139 if (dp->dp_typ != DOSPTYP_EXT)
141 rc = dev->d_dev->dv_strategy(dev, F_READ, dp->dp_start, DISK_SECSIZE,
145 if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) {
146 DEBUG("no magic in extended table");
150 dp = (struct dos_partition *) &buf[DOSPARTOFF];
151 for (i = 0; i < NDOSPART; i++, dp++) {
152 if (dp->dp_size == 0)
154 if (*nslicesp == NEXTDOSPART)
156 dp->dp_start += base;
157 bcopy(dp, &slicetab[*nslicesp], sizeof(*dp));
163 * now, recursively check the slices we just added
165 for (i = start; i < end; i++)
166 disk_checkextended(dev, slicetab, i, nslicesp);
172 disk_readslicetab(struct disk_devdesc *dev,
173 struct dos_partition **slicetabp, int *nslicesp)
175 struct dos_partition *slicetab = NULL;
178 uint8_t buf[DISK_SECSIZE];
181 * Find the slice in the DOS slice table.
183 rc = dev->d_dev->dv_strategy(dev, F_READ, 0, DISK_SECSIZE,
186 DEBUG("error reading MBR");
191 * Check the slice table magic.
193 if (buf[0x1fe] != 0x55 || buf[0x1ff] != 0xaa) {
194 DEBUG("no slice table/MBR (no magic)");
199 * copy the partition table, then pick up any extended partitions.
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);
208 *slicetabp = slicetab;
214 * Search for the best MBR slice (typically the first FreeBSD slice).
217 disk_bestslice(struct dos_partition *slicetab, int nslices)
219 struct dos_partition *dp;
224 preflevel = PREF_NONE;
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;
234 pref = dp->dp_flag & 0x80 ? PREF_LINUX_ACT : PREF_LINUX;
237 case 0x01: /* DOS/Windows */
243 pref = dp->dp_flag & 0x80 ? PREF_DOS_ACT : PREF_DOS;
249 if (pref < preflevel) {
258 disk_openmbr(struct disk_devdesc *dev)
260 struct dos_partition *slicetab = NULL, *dptr;
261 int nslices, sector, slice;
263 uint8_t buf[DISK_SECSIZE];
264 struct disklabel *lp;
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.
271 rc = disk_readslicetab(dev, &slicetab, &nslices);
276 * if a slice number was supplied but not found, this is an error.
278 if (dev->d_slice > 0) {
279 slice = dev->d_slice - 1;
280 if (slice >= nslices) {
281 DEBUG("slice %d not found", slice);
288 * Check for the historically bogus MBR found on true dedicated disks
290 if (slicetab[3].dp_typ == DOSPTYP_386BSD &&
291 slicetab[3].dp_start == 0 && slicetab[3].dp_size == 50000) {
297 * Try to auto-detect the best slice; this should always give
300 if (dev->d_slice == 0) {
301 slice = disk_bestslice(slicetab, nslices);
306 dev->d_slice = slice;
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
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);
321 * Now we have the slice offset, look for the partition in the
322 * disklabel if we have a partition to start with.
324 * XXX we might want to check the label checksum.
326 if (dev->d_partition < 0) {
327 /* no partition, must be after the slice */
328 DEBUG("opening raw slice");
329 dev->d_offset = sector;
334 rc = dev->d_dev->dv_strategy(dev, F_READ, sector + LABELSECTOR,
335 DISK_SECSIZE, (char *) buf, NULL);
337 DEBUG("error reading disklabel");
341 lp = (struct disklabel *) buf;
343 if (lp->d_magic != DISKMAGIC) {
344 DEBUG("no disklabel");
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);
357 lp->d_partitions[dev->d_partition].p_offset -
358 lp->d_partitions[RAW_PART].p_offset +
369 * Print out each valid partition in the disklabel of a FreeBSD slice.
370 * For size calculations, we assume a 512 byte sector size.
373 disk_printbsdslice(struct disk_devdesc *dev, daddr_t offset,
374 char *prefix, int verbose)
377 char buf[DISK_SECSIZE];
378 struct disklabel *lp;
382 rc = dev->d_dev->dv_strategy(dev, F_READ, offset + LABELSECTOR,
383 DISK_SECSIZE, (char *) buf, NULL);
386 lp =(struct disklabel *)(&buf[0]);
387 if (lp->d_magic != DISKMAGIC) {
388 sprintf(line, "%s: FFS bad disklabel\n", prefix);
393 /* Print partitions */
394 for (i = 0; i < lp->d_npartitions; i++) {
396 * For each partition, make sure we know what type of fs it
397 * is. If not, then skip it.
399 fstype = lp->d_partitions[i].p_fstype;
400 if (fstype != FS_BSDFFS &&
405 /* Only print out statistics in verbose mode */
407 sprintf(line, " %s%c: %s %s (%d - %d)\n",
409 (fstype == FS_SWAP) ? "swap " :
410 (fstype == FS_VINUM) ? "vinum" :
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));
417 sprintf(line, " %s%c: %s\n", prefix, 'a' + i,
418 (fstype == FS_SWAP) ? "swap" :
419 (fstype == FS_VINUM) ? "vinum" :
426 disk_printslice(struct disk_devdesc *dev, int slice,
427 struct dos_partition *dp, char *prefix, int verbose)
433 sprintf(stats, " %s (%d - %d)", display_size(dp->dp_size),
434 dp->dp_start, dp->dp_start + dp->dp_size);
438 switch (dp->dp_typ) {
440 disk_printbsdslice(dev, (daddr_t)dp->dp_start,
444 sprintf(line, "%s: Linux swap%s\n", prefix, stats);
449 * read the superblock to confirm this is an ext2fs partition?
451 sprintf(line, "%s: ext2fs%s\n", prefix, stats);
453 case 0x00: /* unused partition */
457 sprintf(line, "%s: FAT-12%s\n", prefix, stats);
462 sprintf(line, "%s: FAT-16%s\n", prefix, stats);
465 sprintf(line, "%s: NTFS/HPFS%s\n", prefix, stats);
469 sprintf(line, "%s: FAT-32%s\n", prefix, stats);
472 sprintf(line, "%s: Unknown fs: 0x%x %s\n", prefix, dp->dp_typ,
479 disk_printmbr(struct disk_devdesc *dev, char *prefix, int verbose)
481 struct dos_partition *slicetab;
486 rc = disk_readslicetab(dev, &slicetab, &nslices);
489 for (i = 0; i < nslices; i++) {
490 sprintf(line, "%ss%d", prefix, i + 1);
491 disk_printslice(dev, i, &slicetab[i], line, verbose);
499 #ifdef LOADER_GPT_SUPPORT
502 disk_readgpt(struct disk_devdesc *dev, struct gpt_part **gptp, int *ngptp)
504 struct dos_partition *dp;
507 struct gpt_part *gptab = NULL;
508 int entries_per_sec, rc, i, part;
510 uint8_t gpt[DISK_SECSIZE], tbl[DISK_SECSIZE];
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.
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,
523 DEBUG("error reading MBR");
527 /* Check the slice table magic. */
528 if (tbl[0x1fe] != 0x55 || tbl[0x1ff] != 0xaa)
531 /* Check for GPT slice. */
533 dp = (struct dos_partition *)(tbl + DOSPARTOFF);
534 for (i = 0; i < NDOSPART; i++) {
535 if (dp[i].dp_typ == 0xee)
537 else if ((part != 1) && (dp[i].dp_typ != 0x00))
543 /* Read primary GPT table header. */
544 rc = dev->d_dev->dv_strategy(dev, F_READ, 1, DISK_SECSIZE,
547 DEBUG("error reading GPT header");
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");
559 /* Walk the partition table to count valid partitions. */
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,
567 DEBUG("error reading GPT table");
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)
580 /* Save the important information about all the valid partitions. */
582 gptab = malloc(part * sizeof(struct gpt_part));
584 for (lba = hdr->hdr_lba_table; lba < elba; lba++) {
585 rc = dev->d_dev->dv_strategy(dev, F_READ, lba, DISK_SECSIZE,
588 DEBUG("error reading GPT table");
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)
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;
613 static struct gpt_part *
614 disk_bestgpt(struct gpt_part *gpt, int ngpt)
616 struct gpt_part *gp, *prefpart;
617 int i, pref, preflevel;
620 preflevel = PREF_NONE;
623 for (i = 0; i < ngpt; i++, gp++) {
624 /* Windows. XXX: Also Linux. */
625 if (uuid_equal(&gp->gp_type, &ms_basic_data, NULL))
628 else if (uuid_equal(&gp->gp_type, &freebsd_ufs, NULL) ||
629 uuid_equal(&gp->gp_type, &freebsd_zfs, NULL))
633 if (pref < preflevel) {
642 disk_opengpt(struct disk_devdesc *dev)
644 struct gpt_part *gpt = NULL, *gp;
647 rc = disk_readgpt(dev, &gpt, &ngpt);
651 /* Is this a request for the whole disk? */
652 if (dev->d_slice < 0) {
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.
662 if (dev->d_partition != 0xff) {
667 /* If a slice number was supplied but not found, this is an error. */
669 if (dev->d_slice > 0) {
670 for (i = 0; i < ngpt; i++) {
671 if (gpt[i].gp_index == dev->d_slice) {
677 DEBUG("partition %d not found", dev->d_slice);
683 /* Try to auto-detect the best partition. */
684 if (dev->d_slice == 0) {
685 gp = disk_bestgpt(gpt, ngpt);
690 dev->d_slice = gp->gp_index;
693 dev->d_offset = gp->gp_start;
703 disk_printgptpart(struct disk_devdesc *dev, struct gpt_part *gp,
704 char *prefix, int verbose)
710 sprintf(stats, " %s",
711 display_size(gp->gp_end + 1 - gp->gp_start));
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);
729 "%s: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x%s\n",
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,
746 disk_printgpt(struct disk_devdesc *dev, char *prefix, int verbose)
748 struct gpt_part *gpt = NULL;
752 rc = disk_readgpt(dev, &gpt, &ngpt);
755 for (i = 0; i < ngpt; i++) {
756 sprintf(line, "%sp%d", prefix, i + 1);
757 disk_printgptpart(dev, &gpt[i], line, verbose);
766 disk_open(struct disk_devdesc *dev)
772 * While we are reading disk metadata, make sure we do it relative
773 * to the start of the disk
777 #ifdef LOADER_GPT_SUPPORT
778 rc = disk_opengpt(dev);
782 #ifdef LOADER_MBR_SUPPORT
783 rc = disk_openmbr(dev);
790 disk_print(struct disk_devdesc *dev, char *prefix, int verbose)
793 #ifdef LOADER_GPT_SUPPORT
794 if (disk_printgpt(dev, prefix, verbose) == 0)
797 #ifdef LOADER_MBR_SUPPORT
798 disk_printmbr(dev, prefix, verbose);