2 * Copyright (c) 2012 Andrey V. Elsukov <ae@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE 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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #include <sys/diskmbr.h>
33 #include <sys/disklabel.h>
34 #include <sys/endian.h>
36 #include <sys/stddef.h>
37 #include <sys/queue.h>
45 #define DEBUG(fmt, args...) printf("%s: " fmt "\n" , __func__ , ## args)
47 #define DEBUG(fmt, args...)
50 #ifdef LOADER_GPT_SUPPORT
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 static const uuid_t gpt_uuid_illumos_boot
= GPT_ENT_TYPE_ILLUMOS_BOOT
;
63 static const uuid_t gpt_uuid_illumos_ufs
= GPT_ENT_TYPE_ILLUMOS_UFS
;
64 static const uuid_t gpt_uuid_illumos_zfs
= GPT_ENT_TYPE_ILLUMOS_ZFS
;
65 static const uuid_t gpt_uuid_reserved
= GPT_ENT_TYPE_RESERVED
;
69 struct ptable_entry part
;
78 STAILQ_ENTRY(pentry
) entry
;
82 enum ptable_type type
;
86 STAILQ_HEAD(, pentry
) entries
;
89 static struct parttypes
{
90 enum partition_type type
;
93 { PART_UNKNOWN
, "Unknown" },
95 { PART_FREEBSD
, "FreeBSD" },
96 { PART_FREEBSD_BOOT
, "FreeBSD boot" },
97 { PART_FREEBSD_NANDFS
, "FreeBSD nandfs" },
98 { PART_FREEBSD_UFS
, "FreeBSD UFS" },
99 { PART_FREEBSD_ZFS
, "FreeBSD ZFS" },
100 { PART_FREEBSD_SWAP
, "FreeBSD swap" },
101 { PART_FREEBSD_VINUM
, "FreeBSD vinum" },
102 { PART_LINUX
, "Linux" },
103 { PART_LINUX_SWAP
, "Linux swap" },
104 { PART_DOS
, "DOS/Windows" },
105 { PART_SOLARIS2
, "Solaris 2" },
106 { PART_ILLUMOS_UFS
, "illumos UFS" },
107 { PART_ILLUMOS_ZFS
, "illumos ZFS" },
108 { PART_RESERVED
, "Reserved" },
109 { PART_VTOC_BOOT
, "boot" },
110 { PART_VTOC_ROOT
, "root" },
111 { PART_VTOC_SWAP
, "swap" },
112 { PART_VTOC_USR
, "usr" },
113 { PART_VTOC_STAND
, "stand" },
114 { PART_VTOC_VAR
, "var" },
115 { PART_VTOC_HOME
, "home" }
119 parttype2str(enum partition_type type
)
123 for (i
= 0; i
< nitems(ptypes
); i
++)
124 if (ptypes
[i
].type
== type
)
125 return (ptypes
[i
].desc
);
126 return (ptypes
[0].desc
);
129 #ifdef LOADER_GPT_SUPPORT
131 uuid_letoh(uuid_t
*uuid
)
134 uuid
->time_low
= le32toh(uuid
->time_low
);
135 uuid
->time_mid
= le16toh(uuid
->time_mid
);
136 uuid
->time_hi_and_version
= le16toh(uuid
->time_hi_and_version
);
139 static enum partition_type
140 gpt_parttype(uuid_t type
)
143 if (uuid_equal(&type
, &gpt_uuid_efi
, NULL
))
145 else if (uuid_equal(&type
, &gpt_uuid_ms_basic_data
, NULL
))
147 else if (uuid_equal(&type
, &gpt_uuid_freebsd_boot
, NULL
))
148 return (PART_FREEBSD_BOOT
);
149 else if (uuid_equal(&type
, &gpt_uuid_freebsd_ufs
, NULL
))
150 return (PART_FREEBSD_UFS
);
151 else if (uuid_equal(&type
, &gpt_uuid_freebsd_zfs
, NULL
))
152 return (PART_FREEBSD_ZFS
);
153 else if (uuid_equal(&type
, &gpt_uuid_freebsd_swap
, NULL
))
154 return (PART_FREEBSD_SWAP
);
155 else if (uuid_equal(&type
, &gpt_uuid_freebsd_vinum
, NULL
))
156 return (PART_FREEBSD_VINUM
);
157 else if (uuid_equal(&type
, &gpt_uuid_freebsd_nandfs
, NULL
))
158 return (PART_FREEBSD_NANDFS
);
159 else if (uuid_equal(&type
, &gpt_uuid_freebsd
, NULL
))
160 return (PART_FREEBSD
);
161 else if (uuid_equal(&type
, &gpt_uuid_illumos_boot
, NULL
))
162 return (PART_VTOC_BOOT
);
163 else if (uuid_equal(&type
, &gpt_uuid_illumos_ufs
, NULL
))
164 return (PART_ILLUMOS_UFS
);
165 else if (uuid_equal(&type
, &gpt_uuid_illumos_zfs
, NULL
))
166 return (PART_ILLUMOS_ZFS
);
167 else if (uuid_equal(&type
, &gpt_uuid_reserved
, NULL
))
168 return (PART_RESERVED
);
169 return (PART_UNKNOWN
);
172 static struct gpt_hdr
*
173 gpt_checkhdr(struct gpt_hdr
*hdr
, uint64_t lba_self
,
174 uint64_t lba_last
__attribute((unused
)), uint16_t sectorsize
)
178 if (memcmp(hdr
->hdr_sig
, GPT_HDR_SIG
, sizeof(hdr
->hdr_sig
)) != 0) {
179 DEBUG("no GPT signature");
182 sz
= le32toh(hdr
->hdr_size
);
183 if (sz
< 92 || sz
> sectorsize
) {
184 DEBUG("invalid GPT header size: %d", sz
);
187 crc
= le32toh(hdr
->hdr_crc_self
);
188 hdr
->hdr_crc_self
= 0;
189 if (crc32(hdr
, sz
) != crc
) {
190 DEBUG("GPT header's CRC doesn't match");
193 hdr
->hdr_crc_self
= crc
;
194 hdr
->hdr_revision
= le32toh(hdr
->hdr_revision
);
195 if (hdr
->hdr_revision
< GPT_HDR_REVISION
) {
196 DEBUG("unsupported GPT revision %d", hdr
->hdr_revision
);
199 hdr
->hdr_lba_self
= le64toh(hdr
->hdr_lba_self
);
200 if (hdr
->hdr_lba_self
!= lba_self
) {
201 DEBUG("self LBA doesn't match");
204 hdr
->hdr_lba_alt
= le64toh(hdr
->hdr_lba_alt
);
205 if (hdr
->hdr_lba_alt
== hdr
->hdr_lba_self
) {
206 DEBUG("invalid alternate LBA");
209 hdr
->hdr_entries
= le32toh(hdr
->hdr_entries
);
210 hdr
->hdr_entsz
= le32toh(hdr
->hdr_entsz
);
211 if (hdr
->hdr_entries
== 0 ||
212 hdr
->hdr_entsz
< sizeof(struct gpt_ent
) ||
213 sectorsize
% hdr
->hdr_entsz
!= 0) {
214 DEBUG("invalid entry size or number of entries");
217 hdr
->hdr_lba_start
= le64toh(hdr
->hdr_lba_start
);
218 hdr
->hdr_lba_end
= le64toh(hdr
->hdr_lba_end
);
219 hdr
->hdr_lba_table
= le64toh(hdr
->hdr_lba_table
);
220 hdr
->hdr_crc_table
= le32toh(hdr
->hdr_crc_table
);
221 uuid_letoh(&hdr
->hdr_uuid
);
226 gpt_checktbl(const struct gpt_hdr
*hdr
, u_char
*tbl
, size_t size
,
227 uint64_t lba_last
__attribute((unused
)))
232 cnt
= size
/ hdr
->hdr_entsz
;
233 if (hdr
->hdr_entries
<= cnt
) {
234 cnt
= hdr
->hdr_entries
;
235 /* Check CRC only when buffer size is enough for table. */
236 if (hdr
->hdr_crc_table
!=
237 crc32(tbl
, hdr
->hdr_entries
* hdr
->hdr_entsz
)) {
238 DEBUG("GPT table's CRC doesn't match");
242 for (i
= 0; i
< cnt
; i
++) {
243 ent
= (struct gpt_ent
*)(tbl
+ i
* hdr
->hdr_entsz
);
244 uuid_letoh(&ent
->ent_type
);
245 if (uuid_equal(&ent
->ent_type
, &gpt_uuid_unused
, NULL
))
247 ent
->ent_lba_start
= le64toh(ent
->ent_lba_start
);
248 ent
->ent_lba_end
= le64toh(ent
->ent_lba_end
);
253 static struct ptable
*
254 ptable_gptread(struct ptable
*table
, void *dev
, diskread_t dread
)
256 struct pentry
*entry
;
257 struct gpt_hdr
*phdr
, hdr
;
264 buf
= malloc(table
->sectorsize
);
267 tbl
= malloc(table
->sectorsize
* MAXTBLSZ
);
272 /* Read the primary GPT header. */
273 if (dread(dev
, buf
, 1, 1) != 0) {
279 /* Check the primary GPT header. */
280 phdr
= gpt_checkhdr((struct gpt_hdr
*)buf
, 1, table
->sectors
- 1,
283 /* Read the primary GPT table. */
284 size
= MIN(MAXTBLSZ
, (phdr
->hdr_entries
* phdr
->hdr_entsz
+
285 table
->sectorsize
- 1) / table
->sectorsize
);
286 if (dread(dev
, tbl
, size
, phdr
->hdr_lba_table
) == 0 &&
287 gpt_checktbl(phdr
, tbl
, size
* table
->sectorsize
,
288 table
->sectors
- 1) == 0) {
289 memcpy(&hdr
, phdr
, sizeof(hdr
));
293 offset
= pri
? hdr
.hdr_lba_alt
: table
->sectors
- 1;
294 /* Read the backup GPT header. */
295 if (dread(dev
, buf
, 1, offset
) != 0)
298 phdr
= gpt_checkhdr((struct gpt_hdr
*)buf
, offset
,
299 table
->sectors
- 1, table
->sectorsize
);
302 * Compare primary and backup headers.
303 * If they are equal, then we do not need to read backup
304 * table. If they are different, then prefer backup header
305 * and try to read backup table.
308 uuid_equal(&hdr
.hdr_uuid
, &phdr
->hdr_uuid
, NULL
) == 0 ||
309 hdr
.hdr_revision
!= phdr
->hdr_revision
||
310 hdr
.hdr_size
!= phdr
->hdr_size
||
311 hdr
.hdr_lba_start
!= phdr
->hdr_lba_start
||
312 hdr
.hdr_lba_end
!= phdr
->hdr_lba_end
||
313 hdr
.hdr_entries
!= phdr
->hdr_entries
||
314 hdr
.hdr_entsz
!= phdr
->hdr_entsz
||
315 hdr
.hdr_crc_table
!= phdr
->hdr_crc_table
) {
316 /* Read the backup GPT table. */
317 size
= MIN(MAXTBLSZ
, (phdr
->hdr_entries
*
318 phdr
->hdr_entsz
+ table
->sectorsize
- 1) /
320 if (dread(dev
, tbl
, size
, phdr
->hdr_lba_table
) == 0 &&
321 gpt_checktbl(phdr
, tbl
, size
* table
->sectorsize
,
322 table
->sectors
- 1) == 0) {
323 memcpy(&hdr
, phdr
, sizeof(hdr
));
328 if (pri
== 0 && sec
== 0) {
329 /* Both primary and backup tables are invalid. */
330 table
->type
= PTABLE_NONE
;
333 DEBUG("GPT detected");
334 size
= MIN(hdr
.hdr_entries
* hdr
.hdr_entsz
,
335 MAXTBLSZ
* table
->sectorsize
);
338 * If the disk's sector count is smaller than the sector count recorded
339 * in the disk's GPT table header, set the table->sectors to the value
340 * recorded in GPT tables. This is done to work around buggy firmware
341 * that returns truncated disk sizes.
343 * Note, this is still not a foolproof way to get disk's size. For
344 * example, an image file can be truncated when copied to smaller media.
346 if (hdr
.hdr_lba_alt
+ 1 > table
->sectors
)
347 table
->sectors
= hdr
.hdr_lba_alt
+ 1;
349 for (i
= 0; i
< size
/ hdr
.hdr_entsz
; i
++) {
350 ent
= (struct gpt_ent
*)(tbl
+ i
* hdr
.hdr_entsz
);
351 if (uuid_equal(&ent
->ent_type
, &gpt_uuid_unused
, NULL
))
354 /* Simple sanity checks. */
355 if (ent
->ent_lba_start
< hdr
.hdr_lba_start
||
356 ent
->ent_lba_end
> hdr
.hdr_lba_end
||
357 ent
->ent_lba_start
> ent
->ent_lba_end
)
360 entry
= malloc(sizeof(*entry
));
363 entry
->part
.start
= ent
->ent_lba_start
;
364 entry
->part
.end
= ent
->ent_lba_end
;
365 entry
->part
.index
= i
+ 1;
366 entry
->part
.type
= gpt_parttype(ent
->ent_type
);
367 entry
->flags
= le64toh(ent
->ent_attr
);
368 memcpy(&entry
->type
.gpt
, &ent
->ent_type
, sizeof(uuid_t
));
369 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
370 DEBUG("new GPT partition added");
377 #endif /* LOADER_GPT_SUPPORT */
379 #ifdef LOADER_MBR_SUPPORT
380 /* We do not need to support too many EBR partitions in the loader */
381 #define MAXEBRENTRIES 8
382 static enum partition_type
383 mbr_parttype(uint8_t type
)
388 return (PART_FREEBSD
);
390 return (PART_LINUX_SWAP
);
393 case DOSPTYP_SUNIXOS2
:
394 return (PART_SOLARIS2
);
404 return (PART_UNKNOWN
);
407 static struct ptable
*
408 ptable_ebrread(struct ptable
*table
, void *dev
, diskread_t dread
)
410 struct dos_partition
*dp
;
411 struct pentry
*e1
, *entry
;
412 uint32_t start
, end
, offset
;
416 STAILQ_FOREACH(e1
, &table
->entries
, entry
) {
417 if (e1
->type
.mbr
== DOSPTYP_EXT
||
418 e1
->type
.mbr
== DOSPTYP_EXTLBA
)
424 offset
= e1
->part
.start
;
425 buf
= malloc(table
->sectorsize
);
428 DEBUG("EBR detected");
429 for (i
= 0; i
< MAXEBRENTRIES
; i
++) {
430 #if 0 /* Some BIOSes return an incorrect number of sectors */
431 if (offset
>= table
->sectors
)
434 if (dread(dev
, buf
, 1, offset
) != 0)
436 dp
= (struct dos_partition
*)(buf
+ DOSPARTOFF
);
437 if (dp
[0].dp_typ
== 0)
439 start
= le32toh(dp
[0].dp_start
);
440 if (dp
[0].dp_typ
== DOSPTYP_EXT
&&
442 offset
= e1
->part
.start
+ start
;
445 end
= le32toh(dp
[0].dp_size
);
446 entry
= malloc(sizeof(*entry
));
449 entry
->part
.start
= offset
+ start
;
450 entry
->part
.end
= entry
->part
.start
+ end
- 1;
451 entry
->part
.index
= idx
++;
452 entry
->part
.type
= mbr_parttype(dp
[0].dp_typ
);
453 entry
->flags
= dp
[0].dp_flag
;
454 entry
->type
.mbr
= dp
[0].dp_typ
;
455 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
456 DEBUG("new EBR partition added");
457 if (dp
[1].dp_typ
== 0)
459 offset
= e1
->part
.start
+ le32toh(dp
[1].dp_start
);
464 #endif /* LOADER_MBR_SUPPORT */
466 static enum partition_type
467 bsd_parttype(uint8_t type
)
472 return (PART_FREEBSD_NANDFS
);
474 return (PART_FREEBSD_SWAP
);
476 return (PART_FREEBSD_UFS
);
478 return (PART_FREEBSD_VINUM
);
480 return (PART_FREEBSD_ZFS
);
482 return (PART_UNKNOWN
);
485 static struct ptable
*
486 ptable_bsdread(struct ptable
*table
, void *dev
, diskread_t dread
)
488 struct disklabel
*dl
;
489 struct partition
*part
;
490 struct pentry
*entry
;
495 if (table
->sectorsize
< sizeof(struct disklabel
)) {
496 DEBUG("Too small sectorsize");
499 buf
= malloc(table
->sectorsize
);
502 if (dread(dev
, buf
, 1, 1) != 0) {
503 DEBUG("read failed");
508 dl
= (struct disklabel
*)buf
;
509 if (le32toh(dl
->d_magic
) != DISKMAGIC
&&
510 le32toh(dl
->d_magic2
) != DISKMAGIC
)
512 if (le32toh(dl
->d_secsize
) != table
->sectorsize
) {
513 DEBUG("unsupported sector size");
516 dl
->d_npartitions
= le16toh(dl
->d_npartitions
);
517 if (dl
->d_npartitions
> 20 || dl
->d_npartitions
< 8) {
518 DEBUG("invalid number of partitions");
521 DEBUG("BSD detected");
522 part
= &dl
->d_partitions
[0];
523 raw_offset
= le32toh(part
[RAW_PART
].p_offset
);
524 for (i
= 0; i
< dl
->d_npartitions
; i
++, part
++) {
527 if (part
->p_size
== 0)
529 entry
= malloc(sizeof(*entry
));
532 entry
->part
.start
= le32toh(part
->p_offset
) - raw_offset
;
533 entry
->part
.end
= entry
->part
.start
+
534 le32toh(part
->p_size
) + 1;
535 entry
->part
.type
= bsd_parttype(part
->p_fstype
);
536 entry
->part
.index
= i
; /* starts from zero */
537 entry
->type
.bsd
= part
->p_fstype
;
538 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
539 DEBUG("new BSD partition added");
541 table
->type
= PTABLE_BSD
;
547 #ifdef LOADER_VTOC8_SUPPORT
548 static enum partition_type
549 vtoc8_parttype(uint16_t type
)
553 case VTOC_TAG_FREEBSD_NANDFS
:
554 return (PART_FREEBSD_NANDFS
);
555 case VTOC_TAG_FREEBSD_SWAP
:
556 return (PART_FREEBSD_SWAP
);
557 case VTOC_TAG_FREEBSD_UFS
:
558 return (PART_FREEBSD_UFS
);
559 case VTOC_TAG_FREEBSD_VINUM
:
560 return (PART_FREEBSD_VINUM
);
561 case VTOC_TAG_FREEBSD_ZFS
:
562 return (PART_FREEBSD_ZFS
);
564 return (PART_UNKNOWN
);
567 static struct ptable
*
568 ptable_vtoc8read(struct ptable
*table
, void *dev
, diskread_t dread
)
570 struct pentry
*entry
;
573 uint16_t sum
, heads
, sectors
;
576 if (table
->sectorsize
!= sizeof(struct vtoc8
))
578 buf
= malloc(table
->sectorsize
);
581 if (dread(dev
, buf
, 1, 0) != 0) {
582 DEBUG("read failed");
587 dl
= (struct vtoc8
*)buf
;
589 for (i
= sum
= 0; i
< sizeof(struct vtoc8
); i
+= sizeof(sum
))
590 sum
^= be16dec(buf
+ i
);
592 DEBUG("incorrect checksum");
595 if (be16toh(dl
->nparts
) != VTOC8_NPARTS
) {
596 DEBUG("invalid number of entries");
599 sectors
= be16toh(dl
->nsecs
);
600 heads
= be16toh(dl
->nheads
);
601 if (sectors
* heads
== 0) {
602 DEBUG("invalid geometry");
605 DEBUG("VTOC8 detected");
606 for (i
= 0; i
< VTOC8_NPARTS
; i
++) {
607 dl
->part
[i
].tag
= be16toh(dl
->part
[i
].tag
);
608 if (i
== VTOC_RAW_PART
||
609 dl
->part
[i
].tag
== VTOC_TAG_UNASSIGNED
)
611 entry
= malloc(sizeof(*entry
));
614 entry
->part
.start
= be32toh(dl
->map
[i
].cyl
) * heads
* sectors
;
615 entry
->part
.end
= be32toh(dl
->map
[i
].nblks
) +
616 entry
->part
.start
- 1;
617 entry
->part
.type
= vtoc8_parttype(dl
->part
[i
].tag
);
618 entry
->part
.index
= i
; /* starts from zero */
619 entry
->type
.vtoc8
= dl
->part
[i
].tag
;
620 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
621 DEBUG("new VTOC8 partition added");
623 table
->type
= PTABLE_VTOC8
;
629 #endif /* LOADER_VTOC8_SUPPORT */
631 static enum partition_type
632 vtoc_parttype(uint16_t type
)
636 return (PART_VTOC_BOOT
);
638 return (PART_VTOC_ROOT
);
640 return (PART_VTOC_SWAP
);
642 return (PART_VTOC_USR
);
643 case VTOC_TAG_BACKUP
:
644 return (PART_VTOC_BACKUP
);
646 return (PART_VTOC_STAND
);
648 return (PART_VTOC_VAR
);
650 return (PART_VTOC_HOME
);
652 return (PART_UNKNOWN
);
655 static struct ptable
*
656 ptable_dklabelread(struct ptable
*table
, void *dev
, diskread_t dread
)
658 struct pentry
*entry
;
664 if (table
->sectorsize
< sizeof(struct dk_label
)) {
665 DEBUG("Too small sectorsize");
668 buf
= malloc(table
->sectorsize
);
671 if (dread(dev
, buf
, 1, DK_LABEL_LOC
) != 0) {
672 DEBUG("read failed");
677 dl
= (struct dk_label
*)buf
;
678 dv
= (struct dk_vtoc
*)&dl
->dkl_vtoc
;
680 if (dl
->dkl_magic
!= VTOC_MAGIC
) {
681 DEBUG("dk_label magic error");
684 if (dv
->v_sanity
!= VTOC_SANITY
) {
685 DEBUG("this vtoc is not sane");
688 if (dv
->v_nparts
!= NDKMAP
) {
689 DEBUG("invalid number of entries");
692 DEBUG("VTOC detected");
693 for (i
= 0; i
< NDKMAP
; i
++) {
694 if (i
== VTOC_RAW_PART
|| /* skip slice 2 and empty */
695 dv
->v_part
[i
].p_size
== 0)
697 entry
= malloc(sizeof(*entry
));
700 entry
->part
.start
= dv
->v_part
[i
].p_start
;
701 entry
->part
.end
= dv
->v_part
[i
].p_size
+
702 entry
->part
.start
- 1;
703 entry
->part
.type
= vtoc_parttype(dv
->v_part
[i
].p_tag
);
704 entry
->part
.index
= i
; /* starts from zero */
705 entry
->type
.vtoc
= dv
->v_part
[i
].p_tag
;
706 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
707 DEBUG("new VTOC partition added");
709 table
->type
= PTABLE_VTOC
;
716 ptable_open(void *dev
, uint64_t sectors
, uint16_t sectorsize
, diskread_t
*dread
)
718 struct dos_partition
*dp
;
719 struct ptable
*table
;
722 #ifdef LOADER_MBR_SUPPORT
723 struct pentry
*entry
;
728 buf
= malloc(sectorsize
);
731 /* First, read the MBR. */
732 if (dread(dev
, buf
, 1, DOSBBSECTOR
) != 0) {
733 DEBUG("read failed");
737 table
= malloc(sizeof(*table
));
740 table
->sectors
= sectors
;
741 table
->sectorsize
= sectorsize
;
742 table
->type
= PTABLE_NONE
;
743 STAILQ_INIT(&table
->entries
);
745 if (ptable_dklabelread(table
, dev
, dread
) == NULL
) { /* Read error. */
748 } else if (table
->type
== PTABLE_VTOC
)
751 #ifdef LOADER_VTOC8_SUPPORT
752 if (be16dec(buf
+ offsetof(struct vtoc8
, magic
)) == VTOC_MAGIC
) {
753 if (ptable_vtoc8read(table
, dev
, dread
) == NULL
) {
757 } else if (table
->type
== PTABLE_VTOC8
)
761 /* Check the BSD label. */
762 if (ptable_bsdread(table
, dev
, dread
) == NULL
) { /* Read error. */
765 } else if (table
->type
== PTABLE_BSD
)
768 #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
769 /* Check the MBR magic. */
770 if (buf
[DOSMAGICOFFSET
] != 0x55 ||
771 buf
[DOSMAGICOFFSET
+ 1] != 0xaa) {
772 DEBUG("magic sequence not found");
773 #if defined(LOADER_GPT_SUPPORT)
774 /* There is no PMBR, check that we have backup GPT */
775 table
->type
= PTABLE_GPT
;
776 table
= ptable_gptread(table
, dev
, dread
);
780 /* Check that we have PMBR. Also do some validation. */
781 dp
= (struct dos_partition
*)(buf
+ DOSPARTOFF
);
782 for (i
= 0, count
= 0; i
< NDOSPART
; i
++) {
783 if (dp
[i
].dp_flag
!= 0 && dp
[i
].dp_flag
!= 0x80) {
784 DEBUG("invalid partition flag %x", dp
[i
].dp_flag
);
787 #ifdef LOADER_GPT_SUPPORT
788 if (dp
[i
].dp_typ
== DOSPTYP_PMBR
) {
789 table
->type
= PTABLE_GPT
;
790 DEBUG("PMBR detected");
793 if (dp
[i
].dp_typ
!= 0)
796 /* Do we have some invalid values? */
797 if (table
->type
== PTABLE_GPT
&& count
> 1) {
798 if (dp
[1].dp_typ
!= DOSPTYP_HFS
) {
799 table
->type
= PTABLE_NONE
;
800 DEBUG("Incorrect PMBR, ignore it");
802 DEBUG("Bootcamp detected");
804 #ifdef LOADER_GPT_SUPPORT
805 if (table
->type
== PTABLE_GPT
) {
806 table
= ptable_gptread(table
, dev
, dread
);
810 #ifdef LOADER_MBR_SUPPORT
812 DEBUG("MBR detected");
813 table
->type
= PTABLE_MBR
;
814 for (i
= has_ext
= 0; i
< NDOSPART
; i
++) {
815 if (dp
[i
].dp_typ
== 0)
817 start
= le32dec(&(dp
[i
].dp_start
));
818 end
= le32dec(&(dp
[i
].dp_size
));
819 if (start
== 0 || end
== 0)
821 #if 0 /* Some BIOSes return an incorrect number of sectors */
822 if (start
+ end
- 1 >= sectors
)
823 continue; /* XXX: ignore */
825 if (dp
[i
].dp_typ
== DOSPTYP_EXT
||
826 dp
[i
].dp_typ
== DOSPTYP_EXTLBA
)
828 entry
= malloc(sizeof(*entry
));
831 entry
->part
.start
= start
;
832 entry
->part
.end
= start
+ end
- 1;
833 entry
->part
.index
= i
+ 1;
834 entry
->part
.type
= mbr_parttype(dp
[i
].dp_typ
);
835 entry
->flags
= dp
[i
].dp_flag
;
836 entry
->type
.mbr
= dp
[i
].dp_typ
;
837 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
838 DEBUG("new MBR partition added");
841 table
= ptable_ebrread(table
, dev
, dread
);
844 #endif /* LOADER_MBR_SUPPORT */
845 #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
852 ptable_close(struct ptable
*table
)
854 struct pentry
*entry
;
856 while (!STAILQ_EMPTY(&table
->entries
)) {
857 entry
= STAILQ_FIRST(&table
->entries
);
858 STAILQ_REMOVE_HEAD(&table
->entries
, entry
);
865 ptable_gettype(const struct ptable
*table
)
868 return (table
->type
);
872 ptable_getsize(const struct ptable
*table
, uint64_t *sizep
)
874 uint64_t tmp
= table
->sectors
* table
->sectorsize
;
876 if (tmp
< table
->sectors
)
885 ptable_getpart(const struct ptable
*table
, struct ptable_entry
*part
, int idx
)
887 struct pentry
*entry
;
889 if (part
== NULL
|| table
== NULL
)
892 STAILQ_FOREACH(entry
, &table
->entries
, entry
) {
893 if (entry
->part
.index
!= idx
)
895 memcpy(part
, &entry
->part
, sizeof(*part
));
902 * Search for a slice with the following preferences:
904 * 1: Active illumos slice
905 * 2: Non-active illumos slice
906 * 3: Active Linux slice
907 * 4: non-active Linux slice
908 * 5: Active FAT/FAT32 slice
909 * 6: non-active FAT/FAT32 slice
911 #define PREF_RAWDISK 0
912 #define PREF_ILLUMOS_ACT 1
913 #define PREF_ILLUMOS 2
914 #define PREF_LINUX_ACT 3
916 #define PREF_DOS_ACT 5
920 ptable_getbestpart(const struct ptable
*table
, struct ptable_entry
*part
)
922 struct pentry
*entry
, *best
;
925 if (part
== NULL
|| table
== NULL
)
929 preflevel
= pref
= PREF_NONE
;
930 STAILQ_FOREACH(entry
, &table
->entries
, entry
) {
931 #ifdef LOADER_MBR_SUPPORT
932 if (table
->type
== PTABLE_MBR
) {
933 switch (entry
->type
.mbr
) {
934 case DOSPTYP_SUNIXOS2
:
935 pref
= entry
->flags
& 0x80 ? PREF_ILLUMOS_ACT
:
939 pref
= entry
->flags
& 0x80 ? PREF_LINUX_ACT
:
942 case 0x01: /* DOS/Windows */
948 pref
= entry
->flags
& 0x80 ? PREF_DOS_ACT
:
955 #endif /* LOADER_MBR_SUPPORT */
956 #ifdef LOADER_GPT_SUPPORT
957 if (table
->type
== PTABLE_GPT
) {
958 if (entry
->part
.type
== PART_DOS
)
960 else if (entry
->part
.type
== PART_ILLUMOS_ZFS
)
965 #endif /* LOADER_GPT_SUPPORT */
966 if (pref
< preflevel
) {
972 memcpy(part
, &best
->part
, sizeof(*part
));
979 * iterate will stop if iterator will return non 0.
982 ptable_iterate(const struct ptable
*table
, void *arg
, ptable_iterate_t
*iter
)
984 struct pentry
*entry
;
989 STAILQ_FOREACH(entry
, &table
->entries
, entry
) {
990 #ifdef LOADER_MBR_SUPPORT
991 if (table
->type
== PTABLE_MBR
)
992 sprintf(name
, "s%d", entry
->part
.index
);
995 #ifdef LOADER_GPT_SUPPORT
996 if (table
->type
== PTABLE_GPT
)
997 sprintf(name
, "p%d", entry
->part
.index
);
1000 #ifdef LOADER_VTOC8_SUPPORT
1001 if (table
->type
== PTABLE_VTOC8
)
1002 sprintf(name
, "%c", (u_char
) 'a' +
1006 if (table
->type
== PTABLE_VTOC
)
1007 sprintf(name
, "%c", (u_char
) 'a' +
1010 if (table
->type
== PTABLE_BSD
)
1011 sprintf(name
, "%c", (u_char
) 'a' +
1013 ret
= iter(arg
, name
, &entry
->part
);