]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/common/part.c
zfs: merge openzfs/zfs@a03ebd9be
[FreeBSD/FreeBSD.git] / stand / common / part.c
1 /*-
2  * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 #include <stand.h>
29 #include <sys/param.h>
30 #include <sys/diskmbr.h>
31 #include <sys/disklabel.h>
32 #include <sys/endian.h>
33 #include <sys/gpt.h>
34 #include <sys/stddef.h>
35 #include <sys/queue.h>
36
37 #include <fs/cd9660/iso.h>
38
39 #include <zlib.h>
40 #include <part.h>
41 #include <uuid.h>
42
43 #ifdef PART_DEBUG
44 #define DPRINTF(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
45 #else
46 #define DPRINTF(fmt, args...)   ((void)0)
47 #endif
48
49 #ifdef LOADER_GPT_SUPPORT
50 #define MAXTBLSZ        64
51 static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
52 static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
53 static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
54 static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI;
55 static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
56 static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
57 static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
58 static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
59 static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
60 static const uuid_t gpt_uuid_apple_apfs = GPT_ENT_TYPE_APPLE_APFS;
61 #endif
62
63 struct pentry {
64         struct ptable_entry     part;
65         uint64_t                flags;
66         union {
67                 uint8_t bsd;
68                 uint8_t mbr;
69                 uuid_t  gpt;
70         } type;
71         STAILQ_ENTRY(pentry)    entry;
72 };
73
74 struct ptable {
75         enum ptable_type        type;
76         uint16_t                sectorsize;
77         uint64_t                sectors;
78
79         STAILQ_HEAD(, pentry)   entries;
80 };
81
82 static struct parttypes {
83         enum partition_type     type;
84         const char              *desc;
85 } ptypes[] = {
86         { PART_UNKNOWN,         "Unknown" },
87         { PART_EFI,             "EFI" },
88         { PART_FREEBSD,         "FreeBSD" },
89         { PART_FREEBSD_BOOT,    "FreeBSD boot" },
90         { PART_FREEBSD_UFS,     "FreeBSD UFS" },
91         { PART_FREEBSD_ZFS,     "FreeBSD ZFS" },
92         { PART_FREEBSD_SWAP,    "FreeBSD swap" },
93         { PART_FREEBSD_VINUM,   "FreeBSD vinum" },
94         { PART_LINUX,           "Linux" },
95         { PART_LINUX_SWAP,      "Linux swap" },
96         { PART_DOS,             "DOS/Windows" },
97         { PART_ISO9660,         "ISO9660" },
98         { PART_APFS,            "APFS" },
99 };
100
101 const char *
102 parttype2str(enum partition_type type)
103 {
104         size_t i;
105
106         for (i = 0; i < nitems(ptypes); i++)
107                 if (ptypes[i].type == type)
108                         return (ptypes[i].desc);
109         return (ptypes[0].desc);
110 }
111
112 #ifdef LOADER_GPT_SUPPORT
113 static void
114 uuid_letoh(uuid_t *uuid)
115 {
116
117         uuid->time_low = le32toh(uuid->time_low);
118         uuid->time_mid = le16toh(uuid->time_mid);
119         uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version);
120 }
121
122 static enum partition_type
123 gpt_parttype(uuid_t type)
124 {
125
126         if (uuid_equal(&type, &gpt_uuid_efi, NULL))
127                 return (PART_EFI);
128         else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL))
129                 return (PART_DOS);
130         else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL))
131                 return (PART_FREEBSD_BOOT);
132         else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL))
133                 return (PART_FREEBSD_UFS);
134         else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL))
135                 return (PART_FREEBSD_ZFS);
136         else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL))
137                 return (PART_FREEBSD_SWAP);
138         else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL))
139                 return (PART_FREEBSD_VINUM);
140         else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL))
141                 return (PART_FREEBSD);
142         else if (uuid_equal(&type, &gpt_uuid_apple_apfs, NULL))
143                 return (PART_APFS);
144         return (PART_UNKNOWN);
145 }
146
147 static struct gpt_hdr *
148 gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last,
149     uint16_t sectorsize)
150 {
151         uint32_t sz, crc;
152
153         if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) {
154                 DPRINTF("no GPT signature");
155                 return (NULL);
156         }
157         sz = le32toh(hdr->hdr_size);
158         if (sz < 92 || sz > sectorsize) {
159                 DPRINTF("invalid GPT header size: %d", sz);
160                 return (NULL);
161         }
162         crc = le32toh(hdr->hdr_crc_self);
163         hdr->hdr_crc_self = crc32(0, Z_NULL, 0);
164         if (crc32(hdr->hdr_crc_self, (const Bytef *)hdr, sz) != crc) {
165                 DPRINTF("GPT header's CRC doesn't match");
166                 return (NULL);
167         }
168         hdr->hdr_crc_self = crc;
169         hdr->hdr_revision = le32toh(hdr->hdr_revision);
170         if (hdr->hdr_revision < GPT_HDR_REVISION) {
171                 DPRINTF("unsupported GPT revision %d", hdr->hdr_revision);
172                 return (NULL);
173         }
174         hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
175         if (hdr->hdr_lba_self != lba_self) {
176                 DPRINTF("self LBA doesn't match");
177                 return (NULL);
178         }
179         hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
180         if (hdr->hdr_lba_alt == hdr->hdr_lba_self) {
181                 DPRINTF("invalid alternate LBA");
182                 return (NULL);
183         }
184         hdr->hdr_entries = le32toh(hdr->hdr_entries);
185         hdr->hdr_entsz = le32toh(hdr->hdr_entsz);
186         if (hdr->hdr_entries == 0 ||
187             hdr->hdr_entsz < sizeof(struct gpt_ent) ||
188             sectorsize % hdr->hdr_entsz != 0) {
189                 DPRINTF("invalid entry size or number of entries");
190                 return (NULL);
191         }
192         hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
193         hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end);
194         hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table);
195         hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table);
196         uuid_letoh(&hdr->hdr_uuid);
197         return (hdr);
198 }
199
200 static int
201 gpt_checktbl(const struct gpt_hdr *hdr, uint8_t *tbl, size_t size,
202     uint64_t lba_last)
203 {
204         struct gpt_ent *ent;
205         uint32_t i, cnt;
206
207         cnt = size / hdr->hdr_entsz;
208         if (hdr->hdr_entries <= cnt) {
209                 cnt = hdr->hdr_entries;
210                 /* Check CRC only when buffer size is enough for table. */
211                 if (hdr->hdr_crc_table !=
212                     crc32(0, tbl, hdr->hdr_entries * hdr->hdr_entsz)) {
213                         DPRINTF("GPT table's CRC doesn't match");
214                         return (-1);
215                 }
216         }
217         for (i = 0; i < cnt; i++) {
218                 ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
219                 uuid_letoh(&ent->ent_type);
220                 if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
221                         continue;
222                 ent->ent_lba_start = le64toh(ent->ent_lba_start);
223                 ent->ent_lba_end = le64toh(ent->ent_lba_end);
224         }
225         return (0);
226 }
227
228 static struct ptable *
229 ptable_gptread(struct ptable *table, void *dev, diskread_t dread)
230 {
231         struct pentry *entry;
232         struct gpt_hdr *phdr, hdr;
233         struct gpt_ent *ent;
234         uint8_t *buf, *tbl;
235         uint64_t offset;
236         int pri, sec;
237         size_t size, i;
238
239         buf = malloc(table->sectorsize);
240         if (buf == NULL)
241                 return (NULL);
242         tbl = malloc(table->sectorsize * MAXTBLSZ);
243         if (tbl == NULL) {
244                 free(buf);
245                 return (NULL);
246         }
247         /* Read the primary GPT header. */
248         if (dread(dev, buf, 1, 1) != 0) {
249                 ptable_close(table);
250                 table = NULL;
251                 goto out;
252         }
253         pri = sec = 0;
254         /* Check the primary GPT header. */
255         phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1,
256             table->sectorsize);
257         if (phdr != NULL) {
258                 /* Read the primary GPT table. */
259                 size = MIN(MAXTBLSZ,
260                     howmany(phdr->hdr_entries * phdr->hdr_entsz,
261                         table->sectorsize));
262                 if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
263                     gpt_checktbl(phdr, tbl, size * table->sectorsize,
264                     table->sectors - 1) == 0) {
265                         memcpy(&hdr, phdr, sizeof(hdr));
266                         pri = 1;
267                 }
268         }
269         offset = pri ? hdr.hdr_lba_alt: table->sectors - 1;
270         /* Read the backup GPT header. */
271         if (dread(dev, buf, 1, offset) != 0)
272                 phdr = NULL;
273         else
274                 phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset,
275                     table->sectors - 1, table->sectorsize);
276         if (phdr != NULL) {
277                 /*
278                  * Compare primary and backup headers.
279                  * If they are equal, then we do not need to read backup
280                  * table. If they are different, then prefer backup header
281                  * and try to read backup table.
282                  */
283                 if (pri == 0 ||
284                     uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 ||
285                     hdr.hdr_revision != phdr->hdr_revision ||
286                     hdr.hdr_size != phdr->hdr_size ||
287                     hdr.hdr_lba_start != phdr->hdr_lba_start ||
288                     hdr.hdr_lba_end != phdr->hdr_lba_end ||
289                     hdr.hdr_entries != phdr->hdr_entries ||
290                     hdr.hdr_entsz != phdr->hdr_entsz ||
291                     hdr.hdr_crc_table != phdr->hdr_crc_table) {
292                         /* Read the backup GPT table. */
293                         size = MIN(MAXTBLSZ,
294                                    howmany(phdr->hdr_entries * phdr->hdr_entsz,
295                                        table->sectorsize));
296                         if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
297                             gpt_checktbl(phdr, tbl, size * table->sectorsize,
298                             table->sectors - 1) == 0) {
299                                 memcpy(&hdr, phdr, sizeof(hdr));
300                                 sec = 1;
301                         }
302                 }
303         }
304         if (pri == 0 && sec == 0) {
305                 /* Both primary and backup tables are invalid. */
306                 table->type = PTABLE_NONE;
307                 goto out;
308         }
309         DPRINTF("GPT detected");
310         size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
311             MAXTBLSZ * table->sectorsize);
312
313         /*
314          * If the disk's sector count is smaller than the sector count recorded
315          * in the disk's GPT table header, set the table->sectors to the value
316          * recorded in GPT tables. This is done to work around buggy firmware
317          * that returns truncated disk sizes.
318          *
319          * Note, this is still not a foolproof way to get disk's size. For
320          * example, an image file can be truncated when copied to smaller media.
321          */
322         table->sectors = hdr.hdr_lba_alt + 1;
323
324         for (i = 0; i < size / hdr.hdr_entsz; i++) {
325                 ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz);
326                 if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
327                         continue;
328
329                 /* Simple sanity checks. */
330                 if (ent->ent_lba_start < hdr.hdr_lba_start ||
331                     ent->ent_lba_end > hdr.hdr_lba_end ||
332                     ent->ent_lba_start > ent->ent_lba_end)
333                         continue;
334
335                 entry = malloc(sizeof(*entry));
336                 if (entry == NULL)
337                         break;
338                 entry->part.start = ent->ent_lba_start;
339                 entry->part.end = ent->ent_lba_end;
340                 entry->part.index = i + 1;
341                 entry->part.type = gpt_parttype(ent->ent_type);
342                 entry->flags = le64toh(ent->ent_attr);
343                 memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t));
344                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
345                 DPRINTF("new GPT partition added");
346         }
347 out:
348         free(buf);
349         free(tbl);
350         return (table);
351 }
352 #endif /* LOADER_GPT_SUPPORT */
353
354 #ifdef LOADER_MBR_SUPPORT
355 /* We do not need to support too many EBR partitions in the loader */
356 #define MAXEBRENTRIES           8
357 static enum partition_type
358 mbr_parttype(uint8_t type)
359 {
360
361         switch (type) {
362         case DOSPTYP_386BSD:
363                 return (PART_FREEBSD);
364         case DOSPTYP_LINSWP:
365                 return (PART_LINUX_SWAP);
366         case DOSPTYP_LINUX:
367                 return (PART_LINUX);
368         case 0x01:
369         case 0x04:
370         case 0x06:
371         case 0x07:
372         case 0x0b:
373         case 0x0c:
374         case 0x0e:
375                 return (PART_DOS);
376         }
377         return (PART_UNKNOWN);
378 }
379
380 static struct ptable *
381 ptable_ebrread(struct ptable *table, void *dev, diskread_t dread)
382 {
383         struct dos_partition *dp;
384         struct pentry *e1, *entry;
385         uint32_t start, end, offset;
386         u_char *buf;
387         int i, index;
388
389         STAILQ_FOREACH(e1, &table->entries, entry) {
390                 if (e1->type.mbr == DOSPTYP_EXT ||
391                     e1->type.mbr == DOSPTYP_EXTLBA)
392                         break;
393         }
394         if (e1 == NULL)
395                 return (table);
396         index = 5;
397         offset = e1->part.start;
398         buf = malloc(table->sectorsize);
399         if (buf == NULL)
400                 return (table);
401         DPRINTF("EBR detected");
402         for (i = 0; i < MAXEBRENTRIES; i++) {
403 #if 0   /* Some BIOSes return an incorrect number of sectors */
404                 if (offset >= table->sectors)
405                         break;
406 #endif
407                 if (dread(dev, buf, 1, offset) != 0)
408                         break;
409                 dp = (struct dos_partition *)(buf + DOSPARTOFF);
410                 if (dp[0].dp_typ == 0)
411                         break;
412                 start = le32toh(dp[0].dp_start);
413                 if (dp[0].dp_typ == DOSPTYP_EXT &&
414                     dp[1].dp_typ == 0) {
415                         offset = e1->part.start + start;
416                         continue;
417                 }
418                 end = le32toh(dp[0].dp_size);
419                 entry = malloc(sizeof(*entry));
420                 if (entry == NULL)
421                         break;
422                 entry->part.start = offset + start;
423                 entry->part.end = entry->part.start + end - 1;
424                 entry->part.index = index++;
425                 entry->part.type = mbr_parttype(dp[0].dp_typ);
426                 entry->flags = dp[0].dp_flag;
427                 entry->type.mbr = dp[0].dp_typ;
428                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
429                 DPRINTF("new EBR partition added");
430                 if (dp[1].dp_typ == 0)
431                         break;
432                 offset = e1->part.start + le32toh(dp[1].dp_start);
433         }
434         free(buf);
435         return (table);
436 }
437 #endif /* LOADER_MBR_SUPPORT */
438
439 static enum partition_type
440 bsd_parttype(uint8_t type)
441 {
442
443         switch (type) {
444         case FS_SWAP:
445                 return (PART_FREEBSD_SWAP);
446         case FS_BSDFFS:
447                 return (PART_FREEBSD_UFS);
448         case FS_VINUM:
449                 return (PART_FREEBSD_VINUM);
450         case FS_ZFS:
451                 return (PART_FREEBSD_ZFS);
452         }
453         return (PART_UNKNOWN);
454 }
455
456 static struct ptable *
457 ptable_bsdread(struct ptable *table, void *dev, diskread_t dread)
458 {
459         struct disklabel *dl;
460         struct partition *part;
461         struct pentry *entry;
462         uint8_t *buf;
463         uint32_t raw_offset;
464         int i;
465
466         if (table->sectorsize < sizeof(struct disklabel)) {
467                 DPRINTF("Too small sectorsize");
468                 return (table);
469         }
470         buf = malloc(table->sectorsize);
471         if (buf == NULL)
472                 return (table);
473         if (dread(dev, buf, 1, 1) != 0) {
474                 DPRINTF("read failed");
475                 ptable_close(table);
476                 table = NULL;
477                 goto out;
478         }
479         dl = (struct disklabel *)buf;
480         if (le32toh(dl->d_magic) != DISKMAGIC &&
481             le32toh(dl->d_magic2) != DISKMAGIC)
482                 goto out;
483         if (le32toh(dl->d_secsize) != table->sectorsize) {
484                 DPRINTF("unsupported sector size");
485                 goto out;
486         }
487         dl->d_npartitions = le16toh(dl->d_npartitions);
488         if (dl->d_npartitions > 20 || dl->d_npartitions < 8) {
489                 DPRINTF("invalid number of partitions");
490                 goto out;
491         }
492         DPRINTF("BSD detected");
493         part = &dl->d_partitions[0];
494         raw_offset = le32toh(part[RAW_PART].p_offset);
495         for (i = 0; i < dl->d_npartitions; i++, part++) {
496                 if (i == RAW_PART)
497                         continue;
498                 if (part->p_size == 0)
499                         continue;
500                 entry = malloc(sizeof(*entry));
501                 if (entry == NULL)
502                         break;
503                 entry->part.start = le32toh(part->p_offset) - raw_offset;
504                 entry->part.end = entry->part.start +
505                     le32toh(part->p_size) - 1;
506                 entry->part.type = bsd_parttype(part->p_fstype);
507                 entry->part.index = i; /* starts from zero */
508                 entry->type.bsd = part->p_fstype;
509                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
510                 DPRINTF("new BSD partition added");
511         }
512         table->type = PTABLE_BSD;
513 out:
514         free(buf);
515         return (table);
516 }
517
518 #define cdb2devb(bno)   ((bno) * ISO_DEFAULT_BLOCK_SIZE / table->sectorsize)
519
520 static struct ptable *
521 ptable_iso9660read(struct ptable *table, void *dev, diskread_t dread)
522 {
523         uint8_t *buf;
524         struct iso_primary_descriptor *vd;
525         struct pentry *entry;
526
527         buf = malloc(table->sectorsize);
528         if (buf == NULL)
529                 return (table);
530                 
531         if (dread(dev, buf, 1, cdb2devb(16)) != 0) {
532                 DPRINTF("read failed");
533                 ptable_close(table);
534                 table = NULL;
535                 goto out;
536         }
537         vd = (struct iso_primary_descriptor *)buf;
538         if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
539                 goto out;
540
541         entry = malloc(sizeof(*entry));
542         if (entry == NULL)
543                 goto out;
544         entry->part.start = 0;
545         entry->part.end = table->sectors;
546         entry->part.type = PART_ISO9660;
547         entry->part.index = 0;
548         STAILQ_INSERT_TAIL(&table->entries, entry, entry);
549
550         table->type = PTABLE_ISO9660;
551
552 out:
553         free(buf);
554         return (table);
555 }
556
557 struct ptable *
558 ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize,
559     diskread_t *dread)
560 {
561         struct dos_partition *dp;
562         struct ptable *table;
563         uint8_t *buf;
564 #ifdef LOADER_MBR_SUPPORT
565         struct pentry *entry;
566         uint32_t start, end;
567         int has_ext;
568 #endif
569         table = NULL;
570         dp = NULL;
571         buf = malloc(sectorsize);
572         if (buf == NULL)
573                 return (NULL);
574         /* First, read the MBR. */
575         if (dread(dev, buf, 1, DOSBBSECTOR) != 0) {
576                 DPRINTF("read failed");
577                 goto out;
578         }
579
580         table = malloc(sizeof(*table));
581         if (table == NULL)
582                 goto out;
583         table->sectors = sectors;
584         table->sectorsize = sectorsize;
585         table->type = PTABLE_NONE;
586         STAILQ_INIT(&table->entries);
587
588         if (ptable_iso9660read(table, dev, dread) == NULL) {
589                 /* Read error. */
590                 table = NULL;
591                 goto out;
592         } else if (table->type == PTABLE_ISO9660)
593                 goto out;
594
595         /* Check the BSD label. */
596         if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */
597                 table = NULL;
598                 goto out;
599         } else if (table->type == PTABLE_BSD)
600                 goto out;
601
602 #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
603         /* Check the MBR magic. */
604         if (buf[DOSMAGICOFFSET] != 0x55 ||
605             buf[DOSMAGICOFFSET + 1] != 0xaa) {
606                 DPRINTF("magic sequence not found");
607 #if defined(LOADER_GPT_SUPPORT)
608                 /* There is no PMBR, check that we have backup GPT */
609                 table->type = PTABLE_GPT;
610                 table = ptable_gptread(table, dev, dread);
611 #endif
612                 goto out;
613         }
614         /* Check that we have PMBR. Also do some validation. */
615         dp = malloc(NDOSPART * sizeof(struct dos_partition));
616         if (dp == NULL)
617                 goto out;
618         bcopy(buf + DOSPARTOFF, dp, NDOSPART * sizeof(struct dos_partition));
619
620         /*
621          * In mac we can have PMBR partition in hybrid MBR;
622          * that is, MBR partition which has DOSPTYP_PMBR entry defined as
623          * start sector 1. After DOSPTYP_PMBR, there may be other partitions.
624          * UEFI compliant PMBR has no other partitions.
625          */
626         for (int i = 0; i < NDOSPART; i++) {
627                 if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) {
628                         DPRINTF("invalid partition flag %x", dp[i].dp_flag);
629                         goto out;
630                 }
631 #ifdef LOADER_GPT_SUPPORT
632                 if (dp[i].dp_typ == DOSPTYP_PMBR && dp[i].dp_start == 1) {
633                         table->type = PTABLE_GPT;
634                         DPRINTF("PMBR detected");
635                 }
636 #endif
637         }
638 #ifdef LOADER_GPT_SUPPORT
639         if (table->type == PTABLE_GPT) {
640                 table = ptable_gptread(table, dev, dread);
641                 goto out;
642         }
643 #endif
644 #ifdef LOADER_MBR_SUPPORT
645         /* Read MBR. */
646         DPRINTF("MBR detected");
647         table->type = PTABLE_MBR;
648         for (int i = has_ext = 0; i < NDOSPART; i++) {
649                 if (dp[i].dp_typ == 0)
650                         continue;
651                 start = le32dec(&(dp[i].dp_start));
652                 end = le32dec(&(dp[i].dp_size));
653                 if (start == 0 || end == 0)
654                         continue;
655 #if 0   /* Some BIOSes return an incorrect number of sectors */
656                 if (start + end - 1 >= sectors)
657                         continue;       /* XXX: ignore */
658 #endif
659                 if (dp[i].dp_typ == DOSPTYP_EXT ||
660                     dp[i].dp_typ == DOSPTYP_EXTLBA)
661                         has_ext = 1;
662                 entry = malloc(sizeof(*entry));
663                 if (entry == NULL)
664                         break;
665                 entry->part.start = start;
666                 entry->part.end = start + end - 1;
667                 entry->part.index = i + 1;
668                 entry->part.type = mbr_parttype(dp[i].dp_typ);
669                 entry->flags = dp[i].dp_flag;
670                 entry->type.mbr = dp[i].dp_typ;
671                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
672                 DPRINTF("new MBR partition added");
673         }
674         if (has_ext) {
675                 table = ptable_ebrread(table, dev, dread);
676                 /* FALLTHROUGH */
677         }
678 #endif /* LOADER_MBR_SUPPORT */
679 #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
680 out:
681         free(dp);
682         free(buf);
683         return (table);
684 }
685
686 void
687 ptable_close(struct ptable *table)
688 {
689         struct pentry *entry;
690
691         if (table == NULL)
692                 return;
693
694         while (!STAILQ_EMPTY(&table->entries)) {
695                 entry = STAILQ_FIRST(&table->entries);
696                 STAILQ_REMOVE_HEAD(&table->entries, entry);
697                 free(entry);
698         }
699         free(table);
700 }
701
702 enum ptable_type
703 ptable_gettype(const struct ptable *table)
704 {
705
706         return (table->type);
707 }
708
709 int
710 ptable_getsize(const struct ptable *table, uint64_t *sizep)
711 {
712         uint64_t tmp = table->sectors * table->sectorsize;
713
714         if (tmp < table->sectors)
715                 return (EOVERFLOW);
716
717         if (sizep != NULL)
718                 *sizep = tmp;
719         return (0);
720 }
721
722 int
723 ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index)
724 {
725         struct pentry *entry;
726
727         if (part == NULL || table == NULL)
728                 return (EINVAL);
729
730         STAILQ_FOREACH(entry, &table->entries, entry) {
731                 if (entry->part.index != index)
732                         continue;
733                 memcpy(part, &entry->part, sizeof(*part));
734                 return (0);
735         }
736         return (ENOENT);
737 }
738
739 /*
740  * Search for a slice with the following preferences:
741  *
742  * 1: Active FreeBSD slice
743  * 2: Non-active FreeBSD slice
744  * 3: Active Linux slice
745  * 4: non-active Linux slice
746  * 5: Active FAT/FAT32 slice
747  * 6: non-active FAT/FAT32 slice
748  */
749 #define PREF_RAWDISK    0
750 #define PREF_FBSD_ACT   1
751 #define PREF_FBSD       2
752 #define PREF_LINUX_ACT  3
753 #define PREF_LINUX      4
754 #define PREF_DOS_ACT    5
755 #define PREF_DOS        6
756 #define PREF_NONE       7
757 int
758 ptable_getbestpart(const struct ptable *table, struct ptable_entry *part)
759 {
760         struct pentry *entry, *best;
761         int pref, preflevel;
762
763         if (part == NULL || table == NULL)
764                 return (EINVAL);
765
766         best = NULL;
767         preflevel = pref = PREF_NONE;
768         STAILQ_FOREACH(entry, &table->entries, entry) {
769 #ifdef LOADER_MBR_SUPPORT
770                 if (table->type == PTABLE_MBR) {
771                         switch (entry->type.mbr) {
772                         case DOSPTYP_386BSD:
773                                 pref = entry->flags & 0x80 ? PREF_FBSD_ACT:
774                                     PREF_FBSD;
775                                 break;
776                         case DOSPTYP_LINUX:
777                                 pref = entry->flags & 0x80 ? PREF_LINUX_ACT:
778                                     PREF_LINUX;
779                                 break;
780                         case 0x01:              /* DOS/Windows */
781                         case 0x04:
782                         case 0x06:
783                         case 0x0c:
784                         case 0x0e:
785                         case DOSPTYP_FAT32:
786                                 pref = entry->flags & 0x80 ? PREF_DOS_ACT:
787                                     PREF_DOS;
788                                 break;
789                         default:
790                                 pref = PREF_NONE;
791                         }
792                 }
793 #endif /* LOADER_MBR_SUPPORT */
794 #ifdef LOADER_GPT_SUPPORT
795                 if (table->type == PTABLE_GPT) {
796                         if (entry->part.type == PART_DOS)
797                                 pref = PREF_DOS;
798                         else if (entry->part.type == PART_FREEBSD_UFS ||
799                             entry->part.type == PART_FREEBSD_ZFS)
800                                 pref = PREF_FBSD;
801                         else
802                                 pref = PREF_NONE;
803                 }
804 #endif /* LOADER_GPT_SUPPORT */
805                 if (pref < preflevel) {
806                         preflevel = pref;
807                         best = entry;
808                 }
809         }
810         if (best != NULL) {
811                 memcpy(part, &best->part, sizeof(*part));
812                 return (0);
813         }
814         return (ENOENT);
815 }
816
817 int
818 ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter)
819 {
820         struct pentry *entry;
821         char name[32];
822         int ret = 0;
823
824         name[0] = '\0';
825         STAILQ_FOREACH(entry, &table->entries, entry) {
826 #ifdef LOADER_MBR_SUPPORT
827                 if (table->type == PTABLE_MBR)
828                         sprintf(name, "s%d", entry->part.index);
829                 else
830 #endif
831 #ifdef LOADER_GPT_SUPPORT
832                 if (table->type == PTABLE_GPT)
833                         sprintf(name, "p%d", entry->part.index);
834                 else
835 #endif
836                 if (table->type == PTABLE_BSD)
837                         sprintf(name, "%c", (uint8_t) 'a' +
838                             entry->part.index);
839                 if ((ret = iter(arg, name, &entry->part)) != 0)
840                         return (ret);
841         }
842         return (ret);
843 }