]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/boot/common/part.c
Import bhyve_graphics into CURRENT. Thanks to all who tested
[FreeBSD/FreeBSD.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 < nitems(ptypes); 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,
261                     howmany(phdr->hdr_entries * phdr->hdr_entsz,
262                         table->sectorsize));
263                 if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
264                     gpt_checktbl(phdr, tbl, size * table->sectorsize,
265                     table->sectors - 1) == 0) {
266                         memcpy(&hdr, phdr, sizeof(hdr));
267                         pri = 1;
268                 }
269         }
270         offset = pri ? hdr.hdr_lba_alt: table->sectors - 1;
271         /* Read the backup GPT header. */
272         if (dread(dev, buf, 1, offset) != 0)
273                 phdr = NULL;
274         else
275                 phdr = gpt_checkhdr((struct gpt_hdr *)buf, offset,
276                     table->sectors - 1, table->sectorsize);
277         if (phdr != NULL) {
278                 /*
279                  * Compare primary and backup headers.
280                  * If they are equal, then we do not need to read backup
281                  * table. If they are different, then prefer backup header
282                  * and try to read backup table.
283                  */
284                 if (pri == 0 ||
285                     uuid_equal(&hdr.hdr_uuid, &phdr->hdr_uuid, NULL) == 0 ||
286                     hdr.hdr_revision != phdr->hdr_revision ||
287                     hdr.hdr_size != phdr->hdr_size ||
288                     hdr.hdr_lba_start != phdr->hdr_lba_start ||
289                     hdr.hdr_lba_end != phdr->hdr_lba_end ||
290                     hdr.hdr_entries != phdr->hdr_entries ||
291                     hdr.hdr_entsz != phdr->hdr_entsz ||
292                     hdr.hdr_crc_table != phdr->hdr_crc_table) {
293                         /* Read the backup GPT table. */
294                         size = MIN(MAXTBLSZ,
295                                    howmany(phdr->hdr_entries * phdr->hdr_entsz,
296                                        table->sectorsize));
297                         if (dread(dev, tbl, size, phdr->hdr_lba_table) == 0 &&
298                             gpt_checktbl(phdr, tbl, size * table->sectorsize,
299                             table->sectors - 1) == 0) {
300                                 memcpy(&hdr, phdr, sizeof(hdr));
301                                 sec = 1;
302                         }
303                 }
304         }
305         if (pri == 0 && sec == 0) {
306                 /* Both primary and backup tables are invalid. */
307                 table->type = PTABLE_NONE;
308                 goto out;
309         }
310         DEBUG("GPT detected");
311         size = MIN(hdr.hdr_entries * hdr.hdr_entsz,
312             MAXTBLSZ * table->sectorsize);
313         for (i = 0; i < size / hdr.hdr_entsz; i++) {
314                 ent = (struct gpt_ent *)(tbl + i * hdr.hdr_entsz);
315                 if (uuid_equal(&ent->ent_type, &gpt_uuid_unused, NULL))
316                         continue;
317                 entry = malloc(sizeof(*entry));
318                 if (entry == NULL)
319                         break;
320                 entry->part.start = ent->ent_lba_start;
321                 entry->part.end = ent->ent_lba_end;
322                 entry->part.index = i + 1;
323                 entry->part.type = gpt_parttype(ent->ent_type);
324                 entry->flags = le64toh(ent->ent_attr);
325                 memcpy(&entry->type.gpt, &ent->ent_type, sizeof(uuid_t));
326                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
327                 DEBUG("new GPT partition added");
328         }
329 out:
330         free(buf);
331         free(tbl);
332         return (table);
333 }
334 #endif /* LOADER_GPT_SUPPORT */
335
336 #ifdef LOADER_MBR_SUPPORT
337 /* We do not need to support too many EBR partitions in the loader */
338 #define MAXEBRENTRIES           8
339 static enum partition_type
340 mbr_parttype(uint8_t type)
341 {
342
343         switch (type) {
344         case DOSPTYP_386BSD:
345                 return (PART_FREEBSD);
346         case DOSPTYP_LINSWP:
347                 return (PART_LINUX_SWAP);
348         case DOSPTYP_LINUX:
349                 return (PART_LINUX);
350         case 0x01:
351         case 0x04:
352         case 0x06:
353         case 0x07:
354         case 0x0b:
355         case 0x0c:
356         case 0x0e:
357                 return (PART_DOS);
358         }
359         return (PART_UNKNOWN);
360 }
361
362 static struct ptable*
363 ptable_ebrread(struct ptable *table, void *dev, diskread_t dread)
364 {
365         struct dos_partition *dp;
366         struct pentry *e1, *entry;
367         uint32_t start, end, offset;
368         u_char *buf;
369         int i, index;
370
371         STAILQ_FOREACH(e1, &table->entries, entry) {
372                 if (e1->type.mbr == DOSPTYP_EXT ||
373                     e1->type.mbr == DOSPTYP_EXTLBA)
374                         break;
375         }
376         if (e1 == NULL)
377                 return (table);
378         index = 5;
379         offset = e1->part.start;
380         buf = malloc(table->sectorsize);
381         if (buf == NULL)
382                 return (table);
383         DEBUG("EBR detected");
384         for (i = 0; i < MAXEBRENTRIES; i++) {
385 #if 0   /* Some BIOSes return an incorrect number of sectors */
386                 if (offset >= table->sectors)
387                         break;
388 #endif
389                 if (dread(dev, buf, 1, offset) != 0)
390                         break;
391                 dp = (struct dos_partition *)(buf + DOSPARTOFF);
392                 if (dp[0].dp_typ == 0)
393                         break;
394                 start = le32toh(dp[0].dp_start);
395                 if (dp[0].dp_typ == DOSPTYP_EXT &&
396                     dp[1].dp_typ == 0) {
397                         offset = e1->part.start + start;
398                         continue;
399                 }
400                 end = le32toh(dp[0].dp_size);
401                 entry = malloc(sizeof(*entry));
402                 if (entry == NULL)
403                         break;
404                 entry->part.start = offset + start;
405                 entry->part.end = entry->part.start + end - 1;
406                 entry->part.index = index++;
407                 entry->part.type = mbr_parttype(dp[0].dp_typ);
408                 entry->flags = dp[0].dp_flag;
409                 entry->type.mbr = dp[0].dp_typ;
410                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
411                 DEBUG("new EBR partition added");
412                 if (dp[1].dp_typ == 0)
413                         break;
414                 offset = e1->part.start + le32toh(dp[1].dp_start);
415         }
416         free(buf);
417         return (table);
418 }
419 #endif /* LOADER_MBR_SUPPORT */
420
421 static enum partition_type
422 bsd_parttype(uint8_t type)
423 {
424
425         switch (type) {
426         case FS_NANDFS:
427                 return (PART_FREEBSD_NANDFS);
428         case FS_SWAP:
429                 return (PART_FREEBSD_SWAP);
430         case FS_BSDFFS:
431                 return (PART_FREEBSD_UFS);
432         case FS_VINUM:
433                 return (PART_FREEBSD_VINUM);
434         case FS_ZFS:
435                 return (PART_FREEBSD_ZFS);
436         }
437         return (PART_UNKNOWN);
438 }
439
440 static struct ptable*
441 ptable_bsdread(struct ptable *table, void *dev, diskread_t dread)
442 {
443         struct disklabel *dl;
444         struct partition *part;
445         struct pentry *entry;
446         u_char *buf;
447         uint32_t raw_offset;
448         int i;
449
450         if (table->sectorsize < sizeof(struct disklabel)) {
451                 DEBUG("Too small sectorsize");
452                 return (table);
453         }
454         buf = malloc(table->sectorsize);
455         if (buf == NULL)
456                 return (table);
457         if (dread(dev, buf, 1, 1) != 0) {
458                 DEBUG("read failed");
459                 ptable_close(table);
460                 table = NULL;
461                 goto out;
462         }
463         dl = (struct disklabel *)buf;
464         if (le32toh(dl->d_magic) != DISKMAGIC &&
465             le32toh(dl->d_magic2) != DISKMAGIC)
466                 goto out;
467         if (le32toh(dl->d_secsize) != table->sectorsize) {
468                 DEBUG("unsupported sector size");
469                 goto out;
470         }
471         dl->d_npartitions = le16toh(dl->d_npartitions);
472         if (dl->d_npartitions > 20 || dl->d_npartitions < 8) {
473                 DEBUG("invalid number of partitions");
474                 goto out;
475         }
476         DEBUG("BSD detected");
477         part = &dl->d_partitions[0];
478         raw_offset = le32toh(part[RAW_PART].p_offset);
479         for (i = 0; i < dl->d_npartitions; i++, part++) {
480                 if (i == RAW_PART)
481                         continue;
482                 if (part->p_size == 0)
483                         continue;
484                 entry = malloc(sizeof(*entry));
485                 if (entry == NULL)
486                         break;
487                 entry->part.start = le32toh(part->p_offset) - raw_offset;
488                 entry->part.end = entry->part.start +
489                     le32toh(part->p_size) + 1;
490                 entry->part.type = bsd_parttype(part->p_fstype);
491                 entry->part.index = i; /* starts from zero */
492                 entry->type.bsd = part->p_fstype;
493                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
494                 DEBUG("new BSD partition added");
495         }
496         table->type = PTABLE_BSD;
497 out:
498         free(buf);
499         return (table);
500 }
501
502 #ifdef LOADER_VTOC8_SUPPORT
503 static enum partition_type
504 vtoc8_parttype(uint16_t type)
505 {
506
507         switch (type) {
508         case VTOC_TAG_FREEBSD_NANDFS:
509                 return (PART_FREEBSD_NANDFS);
510         case VTOC_TAG_FREEBSD_SWAP:
511                 return (PART_FREEBSD_SWAP);
512         case VTOC_TAG_FREEBSD_UFS:
513                 return (PART_FREEBSD_UFS);
514         case VTOC_TAG_FREEBSD_VINUM:
515                 return (PART_FREEBSD_VINUM);
516         case VTOC_TAG_FREEBSD_ZFS:
517                 return (PART_FREEBSD_ZFS);
518         }
519         return (PART_UNKNOWN);
520 }
521
522 static struct ptable*
523 ptable_vtoc8read(struct ptable *table, void *dev, diskread_t dread)
524 {
525         struct pentry *entry;
526         struct vtoc8 *dl;
527         u_char *buf;
528         uint16_t sum, heads, sectors;
529         int i;
530
531         if (table->sectorsize != sizeof(struct vtoc8))
532                 return (table);
533         buf = malloc(table->sectorsize);
534         if (buf == NULL)
535                 return (table);
536         if (dread(dev, buf, 1, 0) != 0) {
537                 DEBUG("read failed");
538                 ptable_close(table);
539                 table = NULL;
540                 goto out;
541         }
542         dl = (struct vtoc8 *)buf;
543         /* Check the sum */
544         for (i = sum = 0; i < sizeof(struct vtoc8); i += sizeof(sum))
545                 sum ^= be16dec(buf + i);
546         if (sum != 0) {
547                 DEBUG("incorrect checksum");
548                 goto out;
549         }
550         if (be16toh(dl->nparts) != VTOC8_NPARTS) {
551                 DEBUG("invalid number of entries");
552                 goto out;
553         }
554         sectors = be16toh(dl->nsecs);
555         heads = be16toh(dl->nheads);
556         if (sectors * heads == 0) {
557                 DEBUG("invalid geometry");
558                 goto out;
559         }
560         DEBUG("VTOC8 detected");
561         for (i = 0; i < VTOC8_NPARTS; i++) {
562                 dl->part[i].tag = be16toh(dl->part[i].tag);
563                 if (i == VTOC_RAW_PART ||
564                     dl->part[i].tag == VTOC_TAG_UNASSIGNED)
565                         continue;
566                 entry = malloc(sizeof(*entry));
567                 if (entry == NULL)
568                         break;
569                 entry->part.start = be32toh(dl->map[i].cyl) * heads * sectors;
570                 entry->part.end = be32toh(dl->map[i].nblks) +
571                     entry->part.start - 1;
572                 entry->part.type = vtoc8_parttype(dl->part[i].tag);
573                 entry->part.index = i; /* starts from zero */
574                 entry->type.vtoc8 = dl->part[i].tag;
575                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
576                 DEBUG("new VTOC8 partition added");
577         }
578         table->type = PTABLE_VTOC8;
579 out:
580         free(buf);
581         return (table);
582
583 }
584 #endif /* LOADER_VTOC8_SUPPORT */
585
586 struct ptable*
587 ptable_open(void *dev, off_t sectors, uint16_t sectorsize,
588     diskread_t *dread)
589 {
590         struct dos_partition *dp;
591         struct ptable *table;
592         u_char *buf;
593         int i, count;
594 #ifdef LOADER_MBR_SUPPORT
595         struct pentry *entry;
596         uint32_t start, end;
597         int has_ext;
598 #endif
599         table = NULL;
600         buf = malloc(sectorsize);
601         if (buf == NULL)
602                 return (NULL);
603         /* First, read the MBR. */
604         if (dread(dev, buf, 1, DOSBBSECTOR) != 0) {
605                 DEBUG("read failed");
606                 goto out;
607         }
608
609         table = malloc(sizeof(*table));
610         if (table == NULL)
611                 goto out;
612         table->sectors = sectors;
613         table->sectorsize = sectorsize;
614         table->type = PTABLE_NONE;
615         STAILQ_INIT(&table->entries);
616
617 #ifdef LOADER_VTOC8_SUPPORT
618         if (be16dec(buf + offsetof(struct vtoc8, magic)) == VTOC_MAGIC) {
619                 if (ptable_vtoc8read(table, dev, dread) == NULL) {
620                         /* Read error. */
621                         table = NULL;
622                         goto out;
623                 } else if (table->type == PTABLE_VTOC8)
624                         goto out;
625         }
626 #endif
627         /* Check the BSD label. */
628         if (ptable_bsdread(table, dev, dread) == NULL) { /* Read error. */
629                 table = NULL;
630                 goto out;
631         } else if (table->type == PTABLE_BSD)
632                 goto out;
633
634 #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
635         /* Check the MBR magic. */
636         if (buf[DOSMAGICOFFSET] != 0x55 ||
637             buf[DOSMAGICOFFSET + 1] != 0xaa) {
638                 DEBUG("magic sequence not found");
639 #if defined(LOADER_GPT_SUPPORT)
640                 /* There is no PMBR, check that we have backup GPT */
641                 table->type = PTABLE_GPT;
642                 table = ptable_gptread(table, dev, dread);
643 #endif
644                 goto out;
645         }
646         /* Check that we have PMBR. Also do some validation. */
647         dp = (struct dos_partition *)(buf + DOSPARTOFF);
648         for (i = 0, count = 0; i < NDOSPART; i++) {
649                 if (dp[i].dp_flag != 0 && dp[i].dp_flag != 0x80) {
650                         DEBUG("invalid partition flag %x", dp[i].dp_flag);
651                         goto out;
652                 }
653 #ifdef LOADER_GPT_SUPPORT
654                 if (dp[i].dp_typ == DOSPTYP_PMBR) {
655                         table->type = PTABLE_GPT;
656                         DEBUG("PMBR detected");
657                 }
658 #endif
659                 if (dp[i].dp_typ != 0)
660                         count++;
661         }
662         /* Do we have some invalid values? */
663         if (table->type == PTABLE_GPT && count > 1) {
664                 if (dp[1].dp_typ != DOSPTYP_HFS) {
665                         table->type = PTABLE_NONE;
666                         DEBUG("Incorrect PMBR, ignore it");
667                 } else
668                         DEBUG("Bootcamp detected");
669         }
670 #ifdef LOADER_GPT_SUPPORT
671         if (table->type == PTABLE_GPT) {
672                 table = ptable_gptread(table, dev, dread);
673                 goto out;
674         }
675 #endif
676 #ifdef LOADER_MBR_SUPPORT
677         /* Read MBR. */
678         DEBUG("MBR detected");
679         table->type = PTABLE_MBR;
680         for (i = has_ext = 0; i < NDOSPART; i++) {
681                 if (dp[i].dp_typ == 0)
682                         continue;
683                 start = le32dec(&(dp[i].dp_start));
684                 end = le32dec(&(dp[i].dp_size));
685                 if (start == 0 || end == 0)
686                         continue;
687 #if 0   /* Some BIOSes return an incorrect number of sectors */
688                 if (start + end - 1 >= sectors)
689                         continue;       /* XXX: ignore */
690 #endif
691                 if (dp[i].dp_typ == DOSPTYP_EXT ||
692                     dp[i].dp_typ == DOSPTYP_EXTLBA)
693                         has_ext = 1;
694                 entry = malloc(sizeof(*entry));
695                 if (entry == NULL)
696                         break;
697                 entry->part.start = start;
698                 entry->part.end = start + end - 1;
699                 entry->part.index = i + 1;
700                 entry->part.type = mbr_parttype(dp[i].dp_typ);
701                 entry->flags = dp[i].dp_flag;
702                 entry->type.mbr = dp[i].dp_typ;
703                 STAILQ_INSERT_TAIL(&table->entries, entry, entry);
704                 DEBUG("new MBR partition added");
705         }
706         if (has_ext) {
707                 table = ptable_ebrread(table, dev, dread);
708                 /* FALLTHROUGH */
709         }
710 #endif /* LOADER_MBR_SUPPORT */
711 #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
712 out:
713         free(buf);
714         return (table);
715 }
716
717 void
718 ptable_close(struct ptable *table)
719 {
720         struct pentry *entry;
721
722         while (!STAILQ_EMPTY(&table->entries)) {
723                 entry = STAILQ_FIRST(&table->entries);
724                 STAILQ_REMOVE_HEAD(&table->entries, entry);
725                 free(entry);
726         }
727         free(table);
728 }
729
730 enum ptable_type
731 ptable_gettype(const struct ptable *table)
732 {
733
734         return (table->type);
735 }
736
737 int
738 ptable_getpart(const struct ptable *table, struct ptable_entry *part, int index)
739 {
740         struct pentry *entry;
741
742         if (part == NULL || table == NULL)
743                 return (EINVAL);
744
745         STAILQ_FOREACH(entry, &table->entries, entry) {
746                 if (entry->part.index != index)
747                         continue;
748                 memcpy(part, &entry->part, sizeof(*part));
749                 return (0);
750         }
751         return (ENOENT);
752 }
753
754 /*
755  * Search for a slice with the following preferences:
756  *
757  * 1: Active FreeBSD slice
758  * 2: Non-active FreeBSD slice
759  * 3: Active Linux slice
760  * 4: non-active Linux slice
761  * 5: Active FAT/FAT32 slice
762  * 6: non-active FAT/FAT32 slice
763  */
764 #define PREF_RAWDISK    0
765 #define PREF_FBSD_ACT   1
766 #define PREF_FBSD       2
767 #define PREF_LINUX_ACT  3
768 #define PREF_LINUX      4
769 #define PREF_DOS_ACT    5
770 #define PREF_DOS        6
771 #define PREF_NONE       7
772 int
773 ptable_getbestpart(const struct ptable *table, struct ptable_entry *part)
774 {
775         struct pentry *entry, *best;
776         int pref, preflevel;
777
778         if (part == NULL || table == NULL)
779                 return (EINVAL);
780
781         best = NULL;
782         preflevel = pref = PREF_NONE;
783         STAILQ_FOREACH(entry, &table->entries, entry) {
784 #ifdef LOADER_MBR_SUPPORT
785                 if (table->type == PTABLE_MBR) {
786                         switch (entry->type.mbr) {
787                         case DOSPTYP_386BSD:
788                                 pref = entry->flags & 0x80 ? PREF_FBSD_ACT:
789                                     PREF_FBSD;
790                                 break;
791                         case DOSPTYP_LINUX:
792                                 pref = entry->flags & 0x80 ? PREF_LINUX_ACT:
793                                     PREF_LINUX;
794                                 break;
795                         case 0x01:              /* DOS/Windows */
796                         case 0x04:
797                         case 0x06:
798                         case 0x0c:
799                         case 0x0e:
800                         case DOSPTYP_FAT32:
801                                 pref = entry->flags & 0x80 ? PREF_DOS_ACT:
802                                     PREF_DOS;
803                                 break;
804                         default:
805                                 pref = PREF_NONE;
806                         }
807                 }
808 #endif /* LOADER_MBR_SUPPORT */
809 #ifdef LOADER_GPT_SUPPORT
810                 if (table->type == PTABLE_GPT) {
811                         if (entry->part.type == PART_DOS)
812                                 pref = PREF_DOS;
813                         else if (entry->part.type == PART_FREEBSD_UFS ||
814                             entry->part.type == PART_FREEBSD_ZFS)
815                                 pref = PREF_FBSD;
816                         else
817                                 pref = PREF_NONE;
818                 }
819 #endif /* LOADER_GPT_SUPPORT */
820                 if (pref < preflevel) {
821                         preflevel = pref;
822                         best = entry;
823                 }
824         }
825         if (best != NULL) {
826                 memcpy(part, &best->part, sizeof(*part));
827                 return (0);
828         }
829         return (ENOENT);
830 }
831
832 int
833 ptable_iterate(const struct ptable *table, void *arg, ptable_iterate_t *iter)
834 {
835         struct pentry *entry;
836         char name[32];
837
838         name[0] = '\0';
839         STAILQ_FOREACH(entry, &table->entries, entry) {
840 #ifdef LOADER_MBR_SUPPORT
841                 if (table->type == PTABLE_MBR)
842                         sprintf(name, "s%d", entry->part.index);
843                 else
844 #endif
845 #ifdef LOADER_GPT_SUPPORT
846                 if (table->type == PTABLE_GPT)
847                         sprintf(name, "p%d", entry->part.index);
848                 else
849 #endif
850 #ifdef LOADER_VTOC8_SUPPORT
851                 if (table->type == PTABLE_VTOC8)
852                         sprintf(name, "%c", (u_char) 'a' +
853                             entry->part.index);
854                 else
855 #endif
856                 if (table->type == PTABLE_BSD)
857                         sprintf(name, "%c", (u_char) 'a' +
858                             entry->part.index);
859                 if (iter(arg, name, &entry->part))
860                         return 1;
861         }
862         return 0;
863 }
864