]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - sys/boot/common/part.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / sys / boot / 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 <crc32.h>
41 #include <part.h>
42 #include <uuid.h>
43
44 #ifdef PART_DEBUG
45 #define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
46 #else
47 #define DEBUG(fmt, args...)
48 #endif
49
50 #ifdef LOADER_GPT_SUPPORT
51 #define MAXTBLSZ        64
52 static const uuid_t gpt_uuid_unused = GPT_ENT_TYPE_UNUSED;
53 static const uuid_t gpt_uuid_ms_basic_data = GPT_ENT_TYPE_MS_BASIC_DATA;
54 static const uuid_t gpt_uuid_freebsd_ufs = GPT_ENT_TYPE_FREEBSD_UFS;
55 static const uuid_t gpt_uuid_efi = GPT_ENT_TYPE_EFI;
56 static const uuid_t gpt_uuid_freebsd = GPT_ENT_TYPE_FREEBSD;
57 static const uuid_t gpt_uuid_freebsd_boot = GPT_ENT_TYPE_FREEBSD_BOOT;
58 static const uuid_t gpt_uuid_freebsd_nandfs = GPT_ENT_TYPE_FREEBSD_NANDFS;
59 static const uuid_t gpt_uuid_freebsd_swap = GPT_ENT_TYPE_FREEBSD_SWAP;
60 static const uuid_t gpt_uuid_freebsd_zfs = GPT_ENT_TYPE_FREEBSD_ZFS;
61 static const uuid_t gpt_uuid_freebsd_vinum = GPT_ENT_TYPE_FREEBSD_VINUM;
62 #endif
63
64 struct pentry {
65         struct ptable_entry     part;
66         uint64_t                flags;
67         union {
68                 uint8_t bsd;
69                 uint8_t mbr;
70                 uuid_t  gpt;
71                 uint16_t vtoc8;
72         } type;
73         STAILQ_ENTRY(pentry)    entry;
74 };
75
76 struct ptable {
77         enum ptable_type        type;
78         uint16_t                sectorsize;
79         uint64_t                sectors;
80
81         STAILQ_HEAD(, pentry)   entries;
82 };
83
84 static struct parttypes {
85         enum partition_type     type;
86         const char              *desc;
87 } ptypes[] = {
88         { PART_UNKNOWN,         "Unknown" },
89         { PART_EFI,             "EFI" },
90         { PART_FREEBSD,         "FreeBSD" },
91         { PART_FREEBSD_BOOT,    "FreeBSD boot" },
92         { PART_FREEBSD_NANDFS,  "FreeBSD nandfs" },
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 };
101
102 const char *
103 parttype2str(enum partition_type type)
104 {
105         size_t i;
106
107         for (i = 0; i < sizeof(ptypes) / sizeof(ptypes[0]); i++)
108                 if (ptypes[i].type == type)
109                         return (ptypes[i].desc);
110         return (ptypes[0].desc);
111 }
112
113 #ifdef LOADER_GPT_SUPPORT
114 static void
115 uuid_letoh(uuid_t *uuid)
116 {
117
118         uuid->time_low = le32toh(uuid->time_low);
119         uuid->time_mid = le16toh(uuid->time_mid);
120         uuid->time_hi_and_version = le16toh(uuid->time_hi_and_version);
121 }
122
123 static enum partition_type
124 gpt_parttype(uuid_t type)
125 {
126
127         if (uuid_equal(&type, &gpt_uuid_efi, NULL))
128                 return (PART_EFI);
129         else if (uuid_equal(&type, &gpt_uuid_ms_basic_data, NULL))
130                 return (PART_DOS);
131         else if (uuid_equal(&type, &gpt_uuid_freebsd_boot, NULL))
132                 return (PART_FREEBSD_BOOT);
133         else if (uuid_equal(&type, &gpt_uuid_freebsd_ufs, NULL))
134                 return (PART_FREEBSD_UFS);
135         else if (uuid_equal(&type, &gpt_uuid_freebsd_zfs, NULL))
136                 return (PART_FREEBSD_ZFS);
137         else if (uuid_equal(&type, &gpt_uuid_freebsd_swap, NULL))
138                 return (PART_FREEBSD_SWAP);
139         else if (uuid_equal(&type, &gpt_uuid_freebsd_vinum, NULL))
140                 return (PART_FREEBSD_VINUM);
141         else if (uuid_equal(&type, &gpt_uuid_freebsd_nandfs, NULL))
142                 return (PART_FREEBSD_NANDFS);
143         else if (uuid_equal(&type, &gpt_uuid_freebsd, NULL))
144                 return (PART_FREEBSD);
145         return (PART_UNKNOWN);
146 }
147
148 static struct gpt_hdr*
149 gpt_checkhdr(struct gpt_hdr *hdr, uint64_t lba_self, uint64_t lba_last,
150     uint16_t sectorsize)
151 {
152         uint32_t sz, crc;
153
154         if (memcmp(hdr->hdr_sig, GPT_HDR_SIG, sizeof(hdr->hdr_sig)) != 0) {
155                 DEBUG("no GPT signature");
156                 return (NULL);
157         }
158         sz = le32toh(hdr->hdr_size);
159         if (sz < 92 || sz > sectorsize) {
160                 DEBUG("invalid GPT header size: %d", sz);
161                 return (NULL);
162         }
163         crc = le32toh(hdr->hdr_crc_self);
164         hdr->hdr_crc_self = 0;
165         if (crc32(hdr, sz) != crc) {
166                 DEBUG("GPT header's CRC doesn't match");
167                 return (NULL);
168         }
169         hdr->hdr_crc_self = crc;
170         hdr->hdr_revision = le32toh(hdr->hdr_revision);
171         if (hdr->hdr_revision < GPT_HDR_REVISION) {
172                 DEBUG("unsupported GPT revision %d", hdr->hdr_revision);
173                 return (NULL);
174         }
175         hdr->hdr_lba_self = le64toh(hdr->hdr_lba_self);
176         if (hdr->hdr_lba_self != lba_self) {
177                 DEBUG("self LBA doesn't match");
178                 return (NULL);
179         }
180         hdr->hdr_lba_alt = le64toh(hdr->hdr_lba_alt);
181         if (hdr->hdr_lba_alt == hdr->hdr_lba_self) {
182                 DEBUG("invalid alternate LBA");
183                 return (NULL);
184         }
185         hdr->hdr_entries = le32toh(hdr->hdr_entries);
186         hdr->hdr_entsz = le32toh(hdr->hdr_entsz);
187         if (hdr->hdr_entries == 0 ||
188             hdr->hdr_entsz < sizeof(struct gpt_ent) ||
189             sectorsize % hdr->hdr_entsz != 0) {
190                 DEBUG("invalid entry size or number of entries");
191                 return (NULL);
192         }
193         hdr->hdr_lba_start = le64toh(hdr->hdr_lba_start);
194         hdr->hdr_lba_end = le64toh(hdr->hdr_lba_end);
195         hdr->hdr_lba_table = le64toh(hdr->hdr_lba_table);
196         hdr->hdr_crc_table = le32toh(hdr->hdr_crc_table);
197         uuid_letoh(&hdr->hdr_uuid);
198         return (hdr);
199 }
200
201 static int
202 gpt_checktbl(const struct gpt_hdr *hdr, u_char *tbl, size_t size,
203     uint64_t lba_last)
204 {
205         struct gpt_ent *ent;
206         uint32_t i, cnt;
207
208         cnt = size / hdr->hdr_entsz;
209         if (hdr->hdr_entries <= cnt) {
210                 cnt = hdr->hdr_entries;
211                 /* Check CRC only when buffer size is enough for table. */
212                 if (hdr->hdr_crc_table !=
213                     crc32(tbl, hdr->hdr_entries * hdr->hdr_entsz)) {
214                         DEBUG("GPT table's CRC doesn't match");
215                         return (-1);
216                 }
217         }
218         for (i = 0; i < cnt; i++) {
219                 ent = (struct gpt_ent *)(tbl + i * hdr->hdr_entsz);
220                 uuid_letoh(&ent->ent_type);
221                 if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
222                         continue;
223                 ent->ent_lba_start = le64toh(ent->ent_lba_start);
224                 ent->ent_lba_end = le64toh(ent->ent_lba_end);
225         }
226         return (0);
227 }
228
229 static struct ptable*
230 ptable_gptread(struct ptable *table, void *dev, diskread_t dread)
231 {
232         struct pentry *entry;
233         struct gpt_hdr *phdr, hdr;
234         struct gpt_ent *ent;
235         u_char *buf, *tbl;
236         uint64_t offset;
237         int pri, sec;
238         size_t size, i;
239
240         buf = malloc(table->sectorsize);
241         if (buf == NULL)
242                 return (NULL);
243         tbl = malloc(table->sectorsize * MAXTBLSZ);
244         if (tbl == NULL) {
245                 free(buf);
246                 return (NULL);
247         }
248         /* Read the primary GPT header. */
249         if (dread(dev, buf, 1, 1) != 0) {
250                 ptable_close(table);
251                 table = NULL;
252                 goto out;
253         }
254         pri = sec = 0;
255         /* Check the primary GPT header. */
256         phdr = gpt_checkhdr((struct gpt_hdr *)buf, 1, table->sectors - 1,
257             table->sectorsize);
258         if (phdr != NULL) {
259                 /* Read the primary GPT table. */
260                 size = MIN(MAXTBLSZ, (phdr->hdr_entries * phdr->hdr_entsz +
261                     table->sectorsize - 1) / 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, (phdr->hdr_entries *
294                             phdr->hdr_entsz + table->sectorsize - 1) /
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         DEBUG("GPT detected");
310         size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
311             MAXTBLSZ * table->sectorsize);
312         for (i = 0; i < size / hdr.hdr_entsz; i++) {
313                 ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz);
314                 if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
315                         continue;
316                 entry = malloc(sizeof(*entry));
317                 if (entry == NULL)
318                         break;
319                 entry->part.start = ent->ent_lba_start;
320                 entry->part.end = ent->ent_lba_end;
321                 entry->part.index = i + 1;
322                 entry->part.type = gpt_parttype(ent->ent_type);
323                 entry->flags = le64toh(ent->ent_attr);
324                 memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t));
325                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
326                 DEBUG("new GPT partition added");
327         }
328 out:
329         free(buf);
330         free(tbl);
331         return (table);
332 }
333 #endif /* LOADER_GPT_SUPPORT */
334
335 #ifdef LOADER_MBR_SUPPORT
336 /* We do not need to support too many EBR partitions in the loader */
337 #define MAXEBRENTRIES           8
338 static enum partition_type
339 mbr_parttype(uint8_t type)
340 {
341
342         switch (type) {
343         case DOSPTYP_386BSD:
344                 return (PART_FREEBSD);
345         case DOSPTYP_LINSWP:
346                 return (PART_LINUX_SWAP);
347         case DOSPTYP_LINUX:
348                 return (PART_LINUX);
349         case 0x01:
350         case 0x04:
351         case 0x06:
352         case 0x07:
353         case 0x0b:
354         case 0x0c:
355         case 0x0e:
356                 return (PART_DOS);
357         }
358         return (PART_UNKNOWN);
359 }
360
361 static struct ptable*
362 ptable_ebrread(struct ptable *table, void *dev, diskread_t dread)
363 {
364         struct dos_partition *dp;
365         struct pentry *e1, *entry;
366         uint32_t start, end, offset;
367         u_char *buf;
368         int i, index;
369
370         STAILQ_FOREACH(e1, &table->entries, entry) {
371                 if (e1->type.mbr == DOSPTYP_EXT ||
372                     e1->type.mbr == DOSPTYP_EXTLBA)
373                         break;
374         }
375         if (e1 == NULL)
376                 return (table);
377         index = 5;
378         offset = e1->part.start;
379         buf = malloc(table->sectorsize);
380         if (buf == NULL)
381                 return (table);
382         for (i = 0; i < MAXEBRENTRIES; i++) {
383 #if 0   /* Some BIOSes return an incorrect number of sectors */
384                 if (offset >= table->sectors)
385                         break;
386 #endif
387                 if (dread(dev, buf, 1, offset) != 0)
388                         break;
389                 dp = (struct dos_partition *)(buf + DOSPARTOFF);
390                 if (dp[0].dp_typ == 0)
391                         break;
392                 start = le32toh(dp[0].dp_start);
393                 if (dp[0].dp_typ == DOSPTYP_EXT &&
394                     dp[1].dp_typ == 0) {
395                         offset = e1->part.start + start;
396                         continue;
397                 }
398                 end = le32toh(dp[0].dp_size);
399                 entry = malloc(sizeof(*entry));
400                 if (entry == NULL)
401                         break;
402                 entry->part.start = offset + start;
403                 entry->part.end = entry->part.start + end - 1;
404                 entry->part.index = index++;
405                 entry->part.type = mbr_parttype(dp[0].dp_typ);
406                 entry->flags = dp[0].dp_flag;
407                 entry->type.mbr = dp[0].dp_typ;
408                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
409                 DEBUG("new EBR partition added");
410                 if (dp[1].dp_typ == 0)
411                         break;
412                 offset = e1->part.start + le32toh(dp[1].dp_start);
413         }
414         free(buf);
415         return (table);
416 }
417 #endif /* LOADER_MBR_SUPPORT */
418
419 static enum partition_type
420 bsd_parttype(uint8_t type)
421 {
422
423         switch (type) {
424         case FS_NANDFS:
425                 return (PART_FREEBSD_NANDFS);
426         case FS_SWAP:
427                 return (PART_FREEBSD_SWAP);
428         case FS_BSDFFS:
429                 return (PART_FREEBSD_UFS);
430         case FS_VINUM:
431                 return (PART_FREEBSD_VINUM);
432         case FS_ZFS:
433                 return (PART_FREEBSD_ZFS);
434         }
435         return (PART_UNKNOWN);
436 }
437
438 static struct ptable*
439 ptable_bsdread(struct ptable *table, void *dev, diskread_t dread)
440 {
441         struct disklabel *dl;
442         struct partition *part;
443         struct pentry *entry;
444         u_char *buf;
445         uint32_t raw_offset;
446         int i;
447
448         if (table->sectorsize < sizeof(struct disklabel)) {
449                 DEBUG("Too small sectorsize");
450                 return (table);
451         }
452         buf = malloc(table->sectorsize);
453         if (buf == NULL)
454                 return (table);
455         if (dread(dev, buf, 1, 1) != 0) {
456                 DEBUG("read failed");
457                 ptable_close(table);
458                 table = NULL;
459                 goto out;
460         }
461         dl = (struct disklabel *)buf;
462         if (le32toh(dl->d_magic) != DISKMAGIC &&
463             le32toh(dl->d_magic2) != DISKMAGIC)
464                 goto out;
465         if (le32toh(dl->d_secsize) != table->sectorsize) {
466                 DEBUG("unsupported sector size");
467                 goto out;
468         }
469         dl->d_npartitions = le16toh(dl->d_npartitions);
470         if (dl->d_npartitions > 20 || dl->d_npartitions < 8) {
471                 DEBUG("invalid number of partitions");
472                 goto out;
473         }
474         part = &dl->d_partitions[0];
475         raw_offset = le32toh(part[RAW_PART].p_offset);
476         for (i = 0; i < dl->d_npartitions; i++, part++) {
477                 if (i == RAW_PART)
478                         continue;
479                 if (part->p_size == 0)
480                         continue;
481                 entry = malloc(sizeof(*entry));
482                 if (entry == NULL)
483                         break;
484                 entry->part.start = le32toh(part->p_offset) - raw_offset;
485                 entry->part.end = entry->part.start +
486                     le32toh(part->p_size) + 1;
487                 entry->part.type = bsd_parttype(part->p_fstype);
488                 entry->part.index = i; /* starts from zero */
489                 entry->type.bsd = part->p_fstype;
490                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
491                 DEBUG("new BSD partition added");
492         }
493         table->type = PTABLE_BSD;
494 out:
495         free(buf);
496         return (table);
497 }
498
499 #ifdef LOADER_VTOC8_SUPPORT
500 static enum partition_type
501 vtoc8_parttype(uint16_t type)
502 {
503
504         switch (type) {
505         case VTOC_TAG_FREEBSD_NANDFS:
506                 return (PART_FREEBSD_NANDFS);
507         case VTOC_TAG_FREEBSD_SWAP:
508                 return (PART_FREEBSD_SWAP);
509         case VTOC_TAG_FREEBSD_UFS:
510                 return (PART_FREEBSD_UFS);
511         case VTOC_TAG_FREEBSD_VINUM:
512                 return (PART_FREEBSD_VINUM);
513         case VTOC_TAG_FREEBSD_ZFS:
514                 return (PART_FREEBSD_ZFS);
515         };
516         return (PART_UNKNOWN);
517 }
518
519 static struct ptable*
520 ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread)
521 {
522         struct pentry *entry;
523         struct vtoc8 *dl;
524         u_char *buf;
525         uint16_t sum, heads, sectors;
526         int i;
527
528         if (table->sectorsize != sizeof(struct vtoc8))
529                 return (table);
530         buf = malloc(table->sectorsize);
531         if (buf == NULL)
532                 return (table);
533         if (dread(dev, buf, 1, 0) != 0) {
534                 DEBUG("read failed");
535                 ptable_close(table);
536                 table = NULL;
537                 goto out;
538         }
539         dl = (struct vtoc8 *)buf;
540         /* Check the sum */
541         for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum))
542                 sum ^= be16dec(buf + i);
543         if (sum != 0) {
544                 DEBUG("incorrect checksum");
545                 goto out;
546         }
547         if (be16toh(dl->nparts) != VTOC8_NPARTS) {
548                 DEBUG("invalid number of entries");
549                 goto out;
550         }
551         sectors = be16toh(dl->nsecs);
552         heads = be16toh(dl->nheads);
553         if (sectors * heads == 0) {
554                 DEBUG("invalid geometry");
555                 goto out;
556         }
557         for (i = 0; i < VTOC8_NPARTS; i++) {
558                 dl->part[i].tag = be16toh(dl->part[i].tag);
559                 if (i == VTOC_RAW_PART ||
560                     dl->part[i].tag == VTOC_TAG_UNASSIGNED)
561                         continue;
562                 entry = malloc(sizeof(*entry));
563                 if (entry == NULL)
564                         break;
565                 entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors;
566                 entry->part.end = be32toh(dl->map[i].nblks) +
567                     entry->part.start - 1;
568                 entry->part.type = vtoc8_parttype(dl->part[i].tag);
569                 entry->part.index = i; /* starts from zero */
570                 entry->type.vtoc8 = dl->part[i].tag;
571                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
572                 DEBUG("new VTOC8 partition added");
573         }
574         table->type = PTABLE_VTOC8;
575 out:
576         free(buf);
577         return (table);
578
579 }
580 #endif /* LOADER_VTOC8_SUPPORT */
581
582 struct ptable*
583 ptable_open(void *dev, off_t sectors, uint16_t sectorsize,
584     diskread_t *dread)
585 {
586         struct dos_partition *dp;
587         struct ptable *table;
588         u_char *buf;
589         int i, count;
590 #ifdef LOADER_MBR_SUPPORT
591         struct pentry *entry;
592         uint32_t start, end;
593         int has_ext;
594 #endif
595         table = NULL;
596         buf = malloc(sectorsize);
597         if (buf == NULL)
598                 return (NULL);
599         /* First, read the MBR. */
600         if (dread(dev, buf, 1, DOSBBSECTOR) != 0) {
601                 DEBUG("read failed");
602                 goto out;
603         }
604
605         table = malloc(sizeof(*table));
606         if (table == NULL)
607                 goto out;
608         table->sectors = sectors;
609         table->sectorsize = sectorsize;
610         table->type = PTABLE_NONE;
611         STAILQ_INIT(&table->entries);
612
613 #ifdef LOADER_VTOC8_SUPPORT
614         if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) {
615                 if (ptable_vtoc8read(table, dev, dread) == NULL) {
616                         /* Read error. */
617                         table = NULL;
618                         goto out;
619                 } else if (table->type == PTABLE_VTOC8)
620                         goto out;
621         }
622 #endif
623         /* Check the BSD label. */
624         if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */
625                 table = NULL;
626                 goto out;
627         } else if (table->type == PTABLE_BSD)
628                 goto out;
629
630 #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
631         /* Check the MBR magic. */
632         if (buf[DOSMAGICOFFSET] != 0x55 ||
633             buf[DOSMAGICOFFSET + 1] != 0xaa) {
634                 DEBUG("magic sequence not found");
635 #if defined(LOADER_GPT_SUPPORT)
636                 /* There is no PMBR, check that we have backup GPT */
637                 table->type = PTABLE_GPT;
638                 table = ptable_gptread(table, dev, dread);
639 #endif
640                 goto out;
641         }
642         /* Check that we have PMBR. Also do some validation. */
643         dp = (struct dos_partition *)(buf + DOSPARTOFF);
644         for (i = 0, count = 0; i < NDOSPART; i++) {
645                 if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) {
646                         DEBUG("invalid partition flag %x", dp[i].dp_flag);
647                         goto out;
648                 }
649 #ifdef LOADER_GPT_SUPPORT
650                 if (dp[i].dp_typ == DOSPTYP_PMBR) {
651                         table->type = PTABLE_GPT;
652                         DEBUG("PMBR detected");
653                 }
654 #endif
655                 if (dp[i].dp_typ != 0)
656                         count++;
657         }
658         /* Do we have some invalid values? */
659         if (table->type == PTABLE_GPT && count > 1) {
660                 if (dp[1].dp_typ != DOSPTYP_HFS) {
661                         table->type = PTABLE_NONE;
662                         DEBUG("Incorrect PMBR, ignore it");
663                 } else
664                         DEBUG("Bootcamp detected");
665         }
666 #ifdef LOADER_GPT_SUPPORT
667         if (table->type == PTABLE_GPT) {
668                 table = ptable_gptread(table, dev, dread);
669                 goto out;
670         }
671 #endif
672 #ifdef LOADER_MBR_SUPPORT
673         /* Read MBR. */
674         table->type = PTABLE_MBR;
675         for (i = has_ext = 0; i < NDOSPART; i++) {
676                 if (dp[i].dp_typ == 0)
677                         continue;
678                 start = le32dec(&(dp[i].dp_start));
679                 end = le32dec(&(dp[i].dp_size));
680                 if (start == 0 || end == 0)
681                         continue;
682 #if 0   /* Some BIOSes return an incorrect number of sectors */
683                 if (start + end - 1 >= sectors)
684                         continue;       /* XXX: ignore */
685 #endif
686                 if (dp[i].dp_typ == DOSPTYP_EXT ||
687                     dp[i].dp_typ == DOSPTYP_EXTLBA)
688                         has_ext = 1;
689                 entry = malloc(sizeof(*entry));
690                 if (entry == NULL)
691                         break;
692                 entry->part.start = start;
693                 entry->part.end = start + end - 1;
694                 entry->part.index = i + 1;
695                 entry->part.type = mbr_parttype(dp[i].dp_typ);
696                 entry->flags = dp[i].dp_flag;
697                 entry->type.mbr = dp[i].dp_typ;
698                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
699                 DEBUG("new MBR partition added");
700         }
701         if (has_ext) {
702                 table = ptable_ebrread(table, dev, dread);
703                 /* FALLTHROUGH */
704         }
705 #endif /* LOADER_MBR_SUPPORT */
706 #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
707 out:
708         free(buf);
709         return (table);
710 }
711
712 void
713 ptable_close(struct ptable *table)
714 {
715         struct pentry *entry;
716
717         while (!STAILQ_EMPTY(&table->entries)) {
718                 entry = STAILQ_FIRST(&table->entries);
719                 STAILQ_REMOVE_HEAD(&table->entries, entry);
720                 free(entry);
721         }
722         free(table);
723 }
724
725 enum ptable_type
726 ptable_gettype(const struct ptable *table)
727 {
728
729         return (table->type);
730 }
731
732 int
733 ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index)
734 {
735         struct pentry *entry;
736
737         if (part == NULL || table == NULL)
738                 return (EINVAL);
739
740         STAILQ_FOREACH(entry, &table->entries, entry) {
741                 if (entry->part.index != index)
742                         continue;
743                 memcpy(part, &entry->part, sizeof(*part));
744                 return (0);
745         }
746         return (ENOENT);
747 }
748
749 /*
750  * Search for a slice with the following preferences:
751  *
752  * 1: Active FreeBSD slice
753  * 2: Non-active FreeBSD slice
754  * 3: Active Linux slice
755  * 4: non-active Linux slice
756  * 5: Active FAT/FAT32 slice
757  * 6: non-active FAT/FAT32 slice
758  */
759 #define PREF_RAWDISK    0
760 #define PREF_FBSD_ACT   1
761 #define PREF_FBSD       2
762 #define PREF_LINUX_ACT  3
763 #define PREF_LINUX      4
764 #define PREF_DOS_ACT    5
765 #define PREF_DOS        6
766 #define PREF_NONE       7
767 int
768 ptable_getbestpart(const struct ptable *table, struct ptable_entry *part)
769 {
770         struct pentry *entry, *best;
771         int pref, preflevel;
772
773         if (part == NULL || table == NULL)
774                 return (EINVAL);
775
776         best = NULL;
777         preflevel = pref = PREF_NONE;
778         STAILQ_FOREACH(entry, &table->entries, entry) {
779 #ifdef LOADER_MBR_SUPPORT
780                 if (table->type == PTABLE_MBR) {
781                         switch (entry->type.mbr) {
782                         case DOSPTYP_386BSD:
783                                 pref = entry->flags & 0x80 ? PREF_FBSD_ACT:
784                                     PREF_FBSD;
785                                 break;
786                         case DOSPTYP_LINUX:
787                                 pref = entry->flags & 0x80 ? PREF_LINUX_ACT:
788                                     PREF_LINUX;
789                                 break;
790                         case 0x01:              /* DOS/Windows */
791                         case 0x04:
792                         case 0x06:
793                         case 0x0c:
794                         case 0x0e:
795                         case DOSPTYP_FAT32:
796                                 pref = entry->flags & 0x80 ? PREF_DOS_ACT:
797                                     PREF_DOS;
798                                 break;
799                         default:
800                                 pref = PREF_NONE;
801                         }
802                 }
803 #endif /* LOADER_MBR_SUPPORT */
804 #ifdef LOADER_GPT_SUPPORT
805                 if (table->type == PTABLE_GPT) {
806                         if (entry->part.type == PART_DOS)
807                                 pref = PREF_DOS;
808                         else if (entry->part.type == PART_FREEBSD_UFS ||
809                             entry->part.type == PART_FREEBSD_ZFS)
810                                 pref = PREF_FBSD;
811                         else
812                                 pref = PREF_NONE;
813                 }
814 #endif /* LOADER_GPT_SUPPORT */
815                 if (pref < preflevel) {
816                         preflevel = pref;
817                         best = entry;
818                 }
819         }
820         if (best != NULL) {
821                 memcpy(part, &best->part, sizeof(*part));
822                 return (0);
823         }
824         return (ENOENT);
825 }
826
827 void
828 ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter)
829 {
830         struct pentry *entry;
831         char name[32];
832
833         name[0] = '\0';
834         STAILQ_FOREACH(entry, &table->entries, entry) {
835 #ifdef LOADER_MBR_SUPPORT
836                 if (table->type == PTABLE_MBR)
837                         sprintf(name, "s%d", entry->part.index);
838                 else
839 #endif
840 #ifdef LOADER_GPT_SUPPORT
841                 if (table->type == PTABLE_GPT)
842                         sprintf(name, "p%d", entry->part.index);
843                 else
844 #endif
845 #ifdef LOADER_VTOC8_SUPPORT
846                 if (table->type == PTABLE_VTOC8)
847                         sprintf(name, "%c", (u_char) 'a' +
848                             entry->part.index);
849                 else
850 #endif
851                 if (table->type == PTABLE_BSD)
852                         sprintf(name, "%c", (u_char) 'a' +
853                             entry->part.index);
854                 iter(arg, name, &entry->part);
855         }
856 }
857