]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/common/part.c
Merge compiler-rt trunk r366426, resolve conflicts, and add
[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 __FBSDID("$FreeBSD$");
29
30 #include <stand.h>
31 #include <sys/param.h>
32 #include <sys/diskmbr.h>
33 #include <sys/disklabel.h>
34 #include <sys/endian.h>
35 #include <sys/gpt.h>
36 #include <sys/stddef.h>
37 #include <sys/queue.h>
38 #include <sys/vtoc.h>
39
40 #include <fs/cd9660/iso.h>
41
42 #include <crc32.h>
43 #include <part.h>
44 #include <uuid.h>
45
46 #ifdef PART_DEBUG
47 #define DPRINTF(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
48 #else
49 #define DPRINTF(fmt, args...)   ((void)0)
50 #endif
51
52 #ifdef LOADER_GPT_SUPPORT
53 #define MAXTBLSZ        64
54 static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
55 static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
56 static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
57 static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI;
58 static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
59 static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
60 static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
61 static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
62 static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
63 #endif
64
65 struct pentry {
66         struct ptable_entry     part;
67         uint64_t                flags;
68         union {
69                 uint8_t bsd;
70                 uint8_t mbr;
71                 uuid_t  gpt;
72                 uint16_t vtoc8;
73         } type;
74         STAILQ_ENTRY(pentry)    entry;
75 };
76
77 struct ptable {
78         enum ptable_type        type;
79         uint16_t                sectorsize;
80         uint64_t                sectors;
81
82         STAILQ_HEAD(, pentry)   entries;
83 };
84
85 static struct parttypes {
86         enum partition_type     type;
87         const char              *desc;
88 } ptypes[] = {
89         { PART_UNKNOWN,         "Unknown" },
90         { PART_EFI,             "EFI" },
91         { PART_FREEBSD,         "FreeBSD" },
92         { PART_FREEBSD_BOOT,    "FreeBSD boot" },
93         { PART_FREEBSD_UFS,     "FreeBSD UFS" },
94         { PART_FREEBSD_ZFS,     "FreeBSD ZFS" },
95         { PART_FREEBSD_SWAP,    "FreeBSD swap" },
96         { PART_FREEBSD_VINUM,   "FreeBSD vinum" },
97         { PART_LINUX,           "Linux" },
98         { PART_LINUX_SWAP,      "Linux swap" },
99         { PART_DOS,             "DOS/Windows" },
100         { PART_ISO9660,         "ISO9660" },
101 };
102
103 const char *
104 parttype2str(enum partition_type type)
105 {
106         size_t i;
107
108         for (i = 0; i < nitems(ptypes); i++)
109                 if (ptypes[i].type == type)
110                         return (ptypes[i].desc);
111         return (ptypes[0].desc);
112 }
113
114 #ifdef LOADER_GPT_SUPPORT
115 static void
116 uuid_letoh(uuid_t *uuid)
117 {
118
119         uuid->time_low = le32toh(uuid->time_low);
120         uuid->time_mid = le16toh(uuid->time_mid);
121         uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version);
122 }
123
124 static enum partition_type
125 gpt_parttype(uuid_t type)
126 {
127
128         if (uuid_equal(&type, &gpt_uuid_efi, NULL))
129                 return (PART_EFI);
130         else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL))
131                 return (PART_DOS);
132         else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL))
133                 return (PART_FREEBSD_BOOT);
134         else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL))
135                 return (PART_FREEBSD_UFS);
136         else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL))
137                 return (PART_FREEBSD_ZFS);
138         else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL))
139                 return (PART_FREEBSD_SWAP);
140         else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL))
141                 return (PART_FREEBSD_VINUM);
142         else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL))
143                 return (PART_FREEBSD);
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 = 0;
164         if (crc32(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(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 #ifdef LOADER_VTOC8_SUPPORT
519 static enum partition_type
520 vtoc8_parttype(uint16_t type)
521 {
522
523         switch (type) {
524         case VTOC_TAG_FREEBSD_SWAP:
525                 return (PART_FREEBSD_SWAP);
526         case VTOC_TAG_FREEBSD_UFS:
527                 return (PART_FREEBSD_UFS);
528         case VTOC_TAG_FREEBSD_VINUM:
529                 return (PART_FREEBSD_VINUM);
530         case VTOC_TAG_FREEBSD_ZFS:
531                 return (PART_FREEBSD_ZFS);
532         }
533         return (PART_UNKNOWN);
534 }
535
536 static struct ptable *
537 ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread)
538 {
539         struct pentry *entry;
540         struct vtoc8 *dl;
541         uint8_t *buf;
542         uint16_t sum, heads, sectors;
543         int i;
544
545         if (table->sectorsize != sizeof(struct vtoc8))
546                 return (table);
547         buf = malloc(table->sectorsize);
548         if (buf == NULL)
549                 return (table);
550         if (dread(dev, buf, 1, 0) != 0) {
551                 DPRINTF("read failed");
552                 ptable_close(table);
553                 table = NULL;
554                 goto out;
555         }
556         dl = (struct vtoc8 *)buf;
557         /* Check the sum */
558         for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum))
559                 sum ^= be16dec(buf + i);
560         if (sum != 0) {
561                 DPRINTF("incorrect checksum");
562                 goto out;
563         }
564         if (be16toh(dl->nparts) != VTOC8_NPARTS) {
565                 DPRINTF("invalid number of entries");
566                 goto out;
567         }
568         sectors = be16toh(dl->nsecs);
569         heads = be16toh(dl->nheads);
570         if (sectors * heads == 0) {
571                 DPRINTF("invalid geometry");
572                 goto out;
573         }
574         DPRINTF("VTOC8 detected");
575         for (i = 0; i < VTOC8_NPARTS; i++) {
576                 dl->part[i].tag = be16toh(dl->part[i].tag);
577                 if (i == VTOC_RAW_PART ||
578                     dl->part[i].tag == VTOC_TAG_UNASSIGNED)
579                         continue;
580                 entry = malloc(sizeof(*entry));
581                 if (entry == NULL)
582                         break;
583                 entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors;
584                 entry->part.end = be32toh(dl->map[i].nblks) +
585                     entry->part.start - 1;
586                 entry->part.type = vtoc8_parttype(dl->part[i].tag);
587                 entry->part.index = i; /* starts from zero */
588                 entry->type.vtoc8 = dl->part[i].tag;
589                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
590                 DPRINTF("new VTOC8 partition added");
591         }
592         table->type = PTABLE_VTOC8;
593 out:
594         free(buf);
595         return (table);
596
597 }
598 #endif /* LOADER_VTOC8_SUPPORT */
599
600 #define cdb2devb(bno)   ((bno) * ISO_DEFAULT_BLOCK_SIZE / table->sectorsize)
601
602 static struct ptable *
603 ptable_iso9660read(struct ptable *table, void *dev, diskread_t dread)
604 {
605         uint8_t *buf;
606         struct iso_primary_descriptor *vd;
607         struct pentry *entry;
608
609         buf = malloc(table->sectorsize);
610         if (buf == NULL)
611                 return (table);
612                 
613         if (dread(dev, buf, 1, cdb2devb(16)) != 0) {
614                 DPRINTF("read failed");
615                 ptable_close(table);
616                 table = NULL;
617                 goto out;
618         }
619         vd = (struct iso_primary_descriptor *)buf;
620         if (bcmp(vd->id, ISO_STANDARD_ID, sizeof vd->id) != 0)
621                 goto out;
622
623         entry = malloc(sizeof(*entry));
624         if (entry == NULL)
625                 goto out;
626         entry->part.start = 0;
627         entry->part.end = table->sectors;
628         entry->part.type = PART_ISO9660;
629         entry->part.index = 0;
630         STAILQ_INSERT_TAIL(&table->entries, entry, entry);
631
632         table->type = PTABLE_ISO9660;
633
634 out:
635         free(buf);
636         return (table);
637 }
638
639 struct ptable *
640 ptable_open(void *dev, uint64_t sectors, uint16_t sectorsize,
641     diskread_t *dread)
642 {
643         struct dos_partition *dp;
644         struct ptable *table;
645         uint8_t *buf;
646         int i, count;
647 #ifdef LOADER_MBR_SUPPORT
648         struct pentry *entry;
649         uint32_t start, end;
650         int has_ext;
651 #endif
652         table = NULL;
653         buf = malloc(sectorsize);
654         if (buf == NULL)
655                 return (NULL);
656         /* First, read the MBR. */
657         if (dread(dev, buf, 1, DOSBBSECTOR) != 0) {
658                 DPRINTF("read failed");
659                 goto out;
660         }
661
662         table = malloc(sizeof(*table));
663         if (table == NULL)
664                 goto out;
665         table->sectors = sectors;
666         table->sectorsize = sectorsize;
667         table->type = PTABLE_NONE;
668         STAILQ_INIT(&table->entries);
669
670         if (ptable_iso9660read(table, dev, dread) == NULL) {
671                 /* Read error. */
672                 table = NULL;
673                 goto out;
674         } else if (table->type == PTABLE_ISO9660)
675                 goto out;
676
677 #ifdef LOADER_VTOC8_SUPPORT
678         if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) {
679                 if (ptable_vtoc8read(table, dev, dread) == NULL) {
680                         /* Read error. */
681                         table = NULL;
682                         goto out;
683                 } else if (table->type == PTABLE_VTOC8)
684                         goto out;
685         }
686 #endif
687         /* Check the BSD label. */
688         if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */
689                 table = NULL;
690                 goto out;
691         } else if (table->type == PTABLE_BSD)
692                 goto out;
693
694 #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
695         /* Check the MBR magic. */
696         if (buf[DOSMAGICOFFSET] != 0x55 ||
697             buf[DOSMAGICOFFSET + 1] != 0xaa) {
698                 DPRINTF("magic sequence not found");
699 #if defined(LOADER_GPT_SUPPORT)
700                 /* There is no PMBR, check that we have backup GPT */
701                 table->type = PTABLE_GPT;
702                 table = ptable_gptread(table, dev, dread);
703 #endif
704                 goto out;
705         }
706         /* Check that we have PMBR. Also do some validation. */
707         dp = (struct dos_partition *)(buf + DOSPARTOFF);
708         for (i = 0, count = 0; i < NDOSPART; i++) {
709                 if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) {
710                         DPRINTF("invalid partition flag %x", dp[i].dp_flag);
711                         goto out;
712                 }
713 #ifdef LOADER_GPT_SUPPORT
714                 if (dp[i].dp_typ == DOSPTYP_PMBR) {
715                         table->type = PTABLE_GPT;
716                         DPRINTF("PMBR detected");
717                 }
718 #endif
719                 if (dp[i].dp_typ != 0)
720                         count++;
721         }
722         /* Do we have some invalid values? */
723         if (table->type == PTABLE_GPT && count > 1) {
724                 if (dp[1].dp_typ != DOSPTYP_HFS) {
725                         table->type = PTABLE_NONE;
726                         DPRINTF("Incorrect PMBR, ignore it");
727                 } else {
728                         DPRINTF("Bootcamp detected");
729                 }
730         }
731 #ifdef LOADER_GPT_SUPPORT
732         if (table->type == PTABLE_GPT) {
733                 table = ptable_gptread(table, dev, dread);
734                 goto out;
735         }
736 #endif
737 #ifdef LOADER_MBR_SUPPORT
738         /* Read MBR. */
739         DPRINTF("MBR detected");
740         table->type = PTABLE_MBR;
741         for (i = has_ext = 0; i < NDOSPART; i++) {
742                 if (dp[i].dp_typ == 0)
743                         continue;
744                 start = le32dec(&(dp[i].dp_start));
745                 end = le32dec(&(dp[i].dp_size));
746                 if (start == 0 || end == 0)
747                         continue;
748 #if 0   /* Some BIOSes return an incorrect number of sectors */
749                 if (start + end - 1 >= sectors)
750                         continue;       /* XXX: ignore */
751 #endif
752                 if (dp[i].dp_typ == DOSPTYP_EXT ||
753                     dp[i].dp_typ == DOSPTYP_EXTLBA)
754                         has_ext = 1;
755                 entry = malloc(sizeof(*entry));
756                 if (entry == NULL)
757                         break;
758                 entry->part.start = start;
759                 entry->part.end = start + end - 1;
760                 entry->part.index = i + 1;
761                 entry->part.type = mbr_parttype(dp[i].dp_typ);
762                 entry->flags = dp[i].dp_flag;
763                 entry->type.mbr = dp[i].dp_typ;
764                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
765                 DPRINTF("new MBR partition added");
766         }
767         if (has_ext) {
768                 table = ptable_ebrread(table, dev, dread);
769                 /* FALLTHROUGH */
770         }
771 #endif /* LOADER_MBR_SUPPORT */
772 #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
773 out:
774         free(buf);
775         return (table);
776 }
777
778 void
779 ptable_close(struct ptable *table)
780 {
781         struct pentry *entry;
782
783         if (table == NULL)
784                 return;
785
786         while (!STAILQ_EMPTY(&table->entries)) {
787                 entry = STAILQ_FIRST(&table->entries);
788                 STAILQ_REMOVE_HEAD(&table->entries, entry);
789                 free(entry);
790         }
791         free(table);
792 }
793
794 enum ptable_type
795 ptable_gettype(const struct ptable *table)
796 {
797
798         return (table->type);
799 }
800
801 int
802 ptable_getsize(const struct ptable *table, uint64_t *sizep)
803 {
804         uint64_t tmp = table->sectors * table->sectorsize;
805
806         if (tmp < table->sectors)
807                 return (EOVERFLOW);
808
809         if (sizep != NULL)
810                 *sizep = tmp;
811         return (0);
812 }
813
814 int
815 ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index)
816 {
817         struct pentry *entry;
818
819         if (part == NULL || table == NULL)
820                 return (EINVAL);
821
822         STAILQ_FOREACH(entry, &table->entries, entry) {
823                 if (entry->part.index != index)
824                         continue;
825                 memcpy(part, &entry->part, sizeof(*part));
826                 return (0);
827         }
828         return (ENOENT);
829 }
830
831 /*
832  * Search for a slice with the following preferences:
833  *
834  * 1: Active FreeBSD slice
835  * 2: Non-active FreeBSD slice
836  * 3: Active Linux slice
837  * 4: non-active Linux slice
838  * 5: Active FAT/FAT32 slice
839  * 6: non-active FAT/FAT32 slice
840  */
841 #define PREF_RAWDISK    0
842 #define PREF_FBSD_ACT   1
843 #define PREF_FBSD       2
844 #define PREF_LINUX_ACT  3
845 #define PREF_LINUX      4
846 #define PREF_DOS_ACT    5
847 #define PREF_DOS        6
848 #define PREF_NONE       7
849 int
850 ptable_getbestpart(const struct ptable *table, struct ptable_entry *part)
851 {
852         struct pentry *entry, *best;
853         int pref, preflevel;
854
855         if (part == NULL || table == NULL)
856                 return (EINVAL);
857
858         best = NULL;
859         preflevel = pref = PREF_NONE;
860         STAILQ_FOREACH(entry, &table->entries, entry) {
861 #ifdef LOADER_MBR_SUPPORT
862                 if (table->type == PTABLE_MBR) {
863                         switch (entry->type.mbr) {
864                         case DOSPTYP_386BSD:
865                                 pref = entry->flags & 0x80 ? PREF_FBSD_ACT:
866                                     PREF_FBSD;
867                                 break;
868                         case DOSPTYP_LINUX:
869                                 pref = entry->flags & 0x80 ? PREF_LINUX_ACT:
870                                     PREF_LINUX;
871                                 break;
872                         case 0x01:              /* DOS/Windows */
873                         case 0x04:
874                         case 0x06:
875                         case 0x0c:
876                         case 0x0e:
877                         case DOSPTYP_FAT32:
878                                 pref = entry->flags & 0x80 ? PREF_DOS_ACT:
879                                     PREF_DOS;
880                                 break;
881                         default:
882                                 pref = PREF_NONE;
883                         }
884                 }
885 #endif /* LOADER_MBR_SUPPORT */
886 #ifdef LOADER_GPT_SUPPORT
887                 if (table->type == PTABLE_GPT) {
888                         if (entry->part.type == PART_DOS)
889                                 pref = PREF_DOS;
890                         else if (entry->part.type == PART_FREEBSD_UFS ||
891                             entry->part.type == PART_FREEBSD_ZFS)
892                                 pref = PREF_FBSD;
893                         else
894                                 pref = PREF_NONE;
895                 }
896 #endif /* LOADER_GPT_SUPPORT */
897                 if (pref < preflevel) {
898                         preflevel = pref;
899                         best = entry;
900                 }
901         }
902         if (best != NULL) {
903                 memcpy(part, &best->part, sizeof(*part));
904                 return (0);
905         }
906         return (ENOENT);
907 }
908
909 int
910 ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter)
911 {
912         struct pentry *entry;
913         char name[32];
914         int ret = 0;
915
916         name[0] = '\0';
917         STAILQ_FOREACH(entry, &table->entries, entry) {
918 #ifdef LOADER_MBR_SUPPORT
919                 if (table->type == PTABLE_MBR)
920                         sprintf(name, "s%d", entry->part.index);
921                 else
922 #endif
923 #ifdef LOADER_GPT_SUPPORT
924                 if (table->type == PTABLE_GPT)
925                         sprintf(name, "p%d", entry->part.index);
926                 else
927 #endif
928 #ifdef LOADER_VTOC8_SUPPORT
929                 if (table->type == PTABLE_VTOC8)
930                         sprintf(name, "%c", (uint8_t) 'a' +
931                             entry->part.index);
932                 else
933 #endif
934                 if (table->type == PTABLE_BSD)
935                         sprintf(name, "%c", (uint8_t) 'a' +
936                             entry->part.index);
937                 if ((ret = iter(arg, name, &entry->part)) != 0)
938                         return (ret);
939         }
940         return (ret);
941 }