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>
30 #include <sys/param.h>
31 #include <sys/diskmbr.h>
32 #include <sys/disklabel.h>
33 #include <sys/endian.h>
35 #include <sys/stddef.h>
36 #include <sys/queue.h>
44 #define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
46 #define DEBUG(fmt, args...)
49 #ifdef LOADER_GPT_SUPPORT
51 static const uuid_t gpt_uuid_unused
= GPT_ENT_TYPE_UNUSED
;
52 static const uuid_t gpt_uuid_ms_basic_data
= GPT_ENT_TYPE_MS_BASIC_DATA
;
53 static const uuid_t gpt_uuid_freebsd_ufs
= GPT_ENT_TYPE_FREEBSD_UFS
;
54 static const uuid_t gpt_uuid_efi
= GPT_ENT_TYPE_EFI
;
55 static const uuid_t gpt_uuid_freebsd
= GPT_ENT_TYPE_FREEBSD
;
56 static const uuid_t gpt_uuid_freebsd_boot
= GPT_ENT_TYPE_FREEBSD_BOOT
;
57 static const uuid_t gpt_uuid_freebsd_nandfs
= GPT_ENT_TYPE_FREEBSD_NANDFS
;
58 static const uuid_t gpt_uuid_freebsd_swap
= GPT_ENT_TYPE_FREEBSD_SWAP
;
59 static const uuid_t gpt_uuid_freebsd_zfs
= GPT_ENT_TYPE_FREEBSD_ZFS
;
60 static const uuid_t gpt_uuid_freebsd_vinum
= GPT_ENT_TYPE_FREEBSD_VINUM
;
61 static const uuid_t gpt_uuid_illumos_boot
= GPT_ENT_TYPE_ILLUMOS_BOOT
;
62 static const uuid_t gpt_uuid_illumos_ufs
= GPT_ENT_TYPE_ILLUMOS_UFS
;
63 static const uuid_t gpt_uuid_illumos_zfs
= GPT_ENT_TYPE_ILLUMOS_ZFS
;
64 static const uuid_t gpt_uuid_reserved
= GPT_ENT_TYPE_RESERVED
;
68 struct ptable_entry part
;
77 STAILQ_ENTRY(pentry
) entry
;
81 enum ptable_type type
;
85 STAILQ_HEAD(, pentry
) entries
;
88 static struct parttypes
{
89 enum partition_type type
;
92 { PART_UNKNOWN
, "Unknown" },
94 { PART_FREEBSD
, "FreeBSD" },
95 { PART_FREEBSD_BOOT
, "FreeBSD boot" },
96 { PART_FREEBSD_NANDFS
, "FreeBSD nandfs" },
97 { PART_FREEBSD_UFS
, "FreeBSD UFS" },
98 { PART_FREEBSD_ZFS
, "FreeBSD ZFS" },
99 { PART_FREEBSD_SWAP
, "FreeBSD swap" },
100 { PART_FREEBSD_VINUM
, "FreeBSD vinum" },
101 { PART_LINUX
, "Linux" },
102 { PART_LINUX_SWAP
, "Linux swap" },
103 { PART_DOS
, "DOS/Windows" },
104 { PART_SOLARIS2
, "Solaris 2" },
105 { PART_ILLUMOS_UFS
, "illumos UFS" },
106 { PART_ILLUMOS_ZFS
, "illumos ZFS" },
107 { PART_RESERVED
, "Reserved" },
108 { PART_VTOC_BOOT
, "boot" },
109 { PART_VTOC_ROOT
, "root" },
110 { PART_VTOC_SWAP
, "swap" },
111 { PART_VTOC_USR
, "usr" },
112 { PART_VTOC_STAND
, "stand" },
113 { PART_VTOC_VAR
, "var" },
114 { PART_VTOC_HOME
, "home" }
118 parttype2str(enum partition_type type
)
122 for (i
= 0; i
< nitems(ptypes
); i
++)
123 if (ptypes
[i
].type
== type
)
124 return (ptypes
[i
].desc
);
125 return (ptypes
[0].desc
);
128 #ifdef LOADER_GPT_SUPPORT
130 uuid_letoh(uuid_t
*uuid
)
133 uuid
->time_low
= le32toh(uuid
->time_low
);
134 uuid
->time_mid
= le16toh(uuid
->time_mid
);
135 uuid
->time_hi_and_version
= le16toh(uuid
->time_hi_and_version
);
138 static enum partition_type
139 gpt_parttype(uuid_t type
)
142 if (uuid_equal(&type
, &gpt_uuid_efi
, NULL
))
144 else if (uuid_equal(&type
, &gpt_uuid_ms_basic_data
, NULL
))
146 else if (uuid_equal(&type
, &gpt_uuid_freebsd_boot
, NULL
))
147 return (PART_FREEBSD_BOOT
);
148 else if (uuid_equal(&type
, &gpt_uuid_freebsd_ufs
, NULL
))
149 return (PART_FREEBSD_UFS
);
150 else if (uuid_equal(&type
, &gpt_uuid_freebsd_zfs
, NULL
))
151 return (PART_FREEBSD_ZFS
);
152 else if (uuid_equal(&type
, &gpt_uuid_freebsd_swap
, NULL
))
153 return (PART_FREEBSD_SWAP
);
154 else if (uuid_equal(&type
, &gpt_uuid_freebsd_vinum
, NULL
))
155 return (PART_FREEBSD_VINUM
);
156 else if (uuid_equal(&type
, &gpt_uuid_freebsd_nandfs
, NULL
))
157 return (PART_FREEBSD_NANDFS
);
158 else if (uuid_equal(&type
, &gpt_uuid_freebsd
, NULL
))
159 return (PART_FREEBSD
);
160 else if (uuid_equal(&type
, &gpt_uuid_illumos_boot
, NULL
))
161 return (PART_VTOC_BOOT
);
162 else if (uuid_equal(&type
, &gpt_uuid_illumos_ufs
, NULL
))
163 return (PART_ILLUMOS_UFS
);
164 else if (uuid_equal(&type
, &gpt_uuid_illumos_zfs
, NULL
))
165 return (PART_ILLUMOS_ZFS
);
166 else if (uuid_equal(&type
, &gpt_uuid_reserved
, NULL
))
167 return (PART_RESERVED
);
168 return (PART_UNKNOWN
);
171 static struct gpt_hdr
*
172 gpt_checkhdr(struct gpt_hdr
*hdr
, uint64_t lba_self
,
173 uint64_t lba_last
__attribute((unused
)), uint16_t sectorsize
)
177 if (memcmp(hdr
->hdr_sig
, GPT_HDR_SIG
, sizeof (hdr
->hdr_sig
)) != 0) {
178 DEBUG("no GPT signature");
181 sz
= le32toh(hdr
->hdr_size
);
182 if (sz
< 92 || sz
> sectorsize
) {
183 DEBUG("invalid GPT header size: %d", sz
);
186 crc
= le32toh(hdr
->hdr_crc_self
);
187 hdr
->hdr_crc_self
= crc32(0, Z_NULL
, 0);
188 if (crc32(hdr
->hdr_crc_self
, (const Bytef
*)hdr
, sz
) != crc
) {
189 DEBUG("GPT header's CRC doesn't match");
192 hdr
->hdr_crc_self
= crc
;
193 hdr
->hdr_revision
= le32toh(hdr
->hdr_revision
);
194 if (hdr
->hdr_revision
< GPT_HDR_REVISION
) {
195 DEBUG("unsupported GPT revision %d", hdr
->hdr_revision
);
198 hdr
->hdr_lba_self
= le64toh(hdr
->hdr_lba_self
);
199 if (hdr
->hdr_lba_self
!= lba_self
) {
200 DEBUG("self LBA doesn't match");
203 hdr
->hdr_lba_alt
= le64toh(hdr
->hdr_lba_alt
);
204 if (hdr
->hdr_lba_alt
== hdr
->hdr_lba_self
) {
205 DEBUG("invalid alternate LBA");
208 hdr
->hdr_entries
= le32toh(hdr
->hdr_entries
);
209 hdr
->hdr_entsz
= le32toh(hdr
->hdr_entsz
);
210 if (hdr
->hdr_entries
== 0 ||
211 hdr
->hdr_entsz
< sizeof (struct gpt_ent
) ||
212 sectorsize
% hdr
->hdr_entsz
!= 0) {
213 DEBUG("invalid entry size or number of entries");
216 hdr
->hdr_lba_start
= le64toh(hdr
->hdr_lba_start
);
217 hdr
->hdr_lba_end
= le64toh(hdr
->hdr_lba_end
);
218 hdr
->hdr_lba_table
= le64toh(hdr
->hdr_lba_table
);
219 hdr
->hdr_crc_table
= le32toh(hdr
->hdr_crc_table
);
220 uuid_letoh(&hdr
->hdr_uuid
);
225 gpt_checktbl(const struct gpt_hdr
*hdr
, uint8_t *tbl
, size_t size
,
226 uint64_t lba_last
__attribute((unused
)))
231 cnt
= size
/ hdr
->hdr_entsz
;
232 if (hdr
->hdr_entries
<= cnt
) {
233 cnt
= hdr
->hdr_entries
;
234 /* Check CRC only when buffer size is enough for table. */
235 if (hdr
->hdr_crc_table
!=
236 crc32(0, tbl
, hdr
->hdr_entries
* hdr
->hdr_entsz
)) {
237 DEBUG("GPT table's CRC doesn't match");
241 for (i
= 0; i
< cnt
; i
++) {
242 ent
= (struct gpt_ent
*)(tbl
+ i
* hdr
->hdr_entsz
);
243 uuid_letoh(&ent
->ent_type
);
244 if (uuid_equal(&ent
->ent_type
, &gpt_uuid_unused
, NULL
))
246 ent
->ent_lba_start
= le64toh(ent
->ent_lba_start
);
247 ent
->ent_lba_end
= le64toh(ent
->ent_lba_end
);
252 static struct ptable
*
253 ptable_gptread(struct ptable
*table
, void *dev
, diskread_t dread
)
255 struct pentry
*entry
;
256 struct gpt_hdr
*phdr
, hdr
;
263 buf
= malloc(table
->sectorsize
);
266 tbl
= malloc(table
->sectorsize
* MAXTBLSZ
);
271 /* Read the primary GPT header. */
272 if (dread(dev
, buf
, 1, 1) != 0) {
278 /* Check the primary GPT header. */
279 phdr
= gpt_checkhdr((struct gpt_hdr
*)buf
, 1, table
->sectors
- 1,
282 /* Read the primary GPT table. */
283 size
= MIN(MAXTBLSZ
, (phdr
->hdr_entries
* phdr
->hdr_entsz
+
284 table
->sectorsize
- 1) / table
->sectorsize
);
285 if (dread(dev
, tbl
, size
, phdr
->hdr_lba_table
) == 0 &&
286 gpt_checktbl(phdr
, tbl
, size
* table
->sectorsize
,
287 table
->sectors
- 1) == 0) {
288 memcpy(&hdr
, phdr
, sizeof (hdr
));
292 offset
= pri
? hdr
.hdr_lba_alt
: table
->sectors
- 1;
293 /* Read the backup GPT header. */
294 if (dread(dev
, buf
, 1, offset
) != 0)
297 phdr
= gpt_checkhdr((struct gpt_hdr
*)buf
, offset
,
298 table
->sectors
- 1, table
->sectorsize
);
301 * Compare primary and backup headers.
302 * If they are equal, then we do not need to read backup
303 * table. If they are different, then prefer backup header
304 * and try to read backup table.
307 uuid_equal(&hdr
.hdr_uuid
, &phdr
->hdr_uuid
, NULL
) == 0 ||
308 hdr
.hdr_revision
!= phdr
->hdr_revision
||
309 hdr
.hdr_size
!= phdr
->hdr_size
||
310 hdr
.hdr_lba_start
!= phdr
->hdr_lba_start
||
311 hdr
.hdr_lba_end
!= phdr
->hdr_lba_end
||
312 hdr
.hdr_entries
!= phdr
->hdr_entries
||
313 hdr
.hdr_entsz
!= phdr
->hdr_entsz
||
314 hdr
.hdr_crc_table
!= phdr
->hdr_crc_table
) {
315 /* Read the backup GPT table. */
316 size
= MIN(MAXTBLSZ
, (phdr
->hdr_entries
*
317 phdr
->hdr_entsz
+ table
->sectorsize
- 1) /
319 if (dread(dev
, tbl
, size
, phdr
->hdr_lba_table
) == 0 &&
320 gpt_checktbl(phdr
, tbl
, size
* table
->sectorsize
,
321 table
->sectors
- 1) == 0) {
322 memcpy(&hdr
, phdr
, sizeof (hdr
));
327 if (pri
== 0 && sec
== 0) {
328 /* Both primary and backup tables are invalid. */
329 table
->type
= PTABLE_NONE
;
332 DEBUG("GPT detected");
333 size
= MIN(hdr
.hdr_entries
* hdr
.hdr_entsz
,
334 MAXTBLSZ
* table
->sectorsize
);
337 * If the disk's sector count is smaller than the sector count recorded
338 * in the disk's GPT table header, set the table->sectors to the value
339 * recorded in GPT tables. This is done to work around buggy firmware
340 * that returns truncated disk sizes.
342 * Note, this is still not a foolproof way to get disk's size. For
343 * example, an image file can be truncated when copied to smaller media.
345 table
->sectors
= hdr
.hdr_lba_alt
+ 1;
347 for (i
= 0; i
< size
/ hdr
.hdr_entsz
; i
++) {
348 ent
= (struct gpt_ent
*)(tbl
+ i
* hdr
.hdr_entsz
);
349 if (uuid_equal(&ent
->ent_type
, &gpt_uuid_unused
, NULL
))
352 /* Simple sanity checks. */
353 if (ent
->ent_lba_start
< hdr
.hdr_lba_start
||
354 ent
->ent_lba_end
> hdr
.hdr_lba_end
||
355 ent
->ent_lba_start
> ent
->ent_lba_end
)
358 entry
= malloc(sizeof (*entry
));
361 entry
->part
.start
= ent
->ent_lba_start
;
362 entry
->part
.end
= ent
->ent_lba_end
;
363 entry
->part
.index
= i
+ 1;
364 entry
->part
.type
= gpt_parttype(ent
->ent_type
);
365 entry
->flags
= le64toh(ent
->ent_attr
);
366 memcpy(&entry
->type
.gpt
, &ent
->ent_type
, sizeof (uuid_t
));
367 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
368 DEBUG("new GPT partition added");
375 #endif /* LOADER_GPT_SUPPORT */
377 #ifdef LOADER_MBR_SUPPORT
378 /* We do not need to support too many EBR partitions in the loader */
379 #define MAXEBRENTRIES 8
380 static enum partition_type
381 mbr_parttype(uint8_t type
)
386 return (PART_FREEBSD
);
388 return (PART_LINUX_SWAP
);
391 case DOSPTYP_SUNIXOS2
:
392 return (PART_SOLARIS2
);
402 return (PART_UNKNOWN
);
405 static struct ptable
*
406 ptable_ebrread(struct ptable
*table
, void *dev
, diskread_t dread
)
408 struct dos_partition
*dp
;
409 struct pentry
*e1
, *entry
;
410 uint32_t start
, end
, offset
;
414 STAILQ_FOREACH(e1
, &table
->entries
, entry
) {
415 if (e1
->type
.mbr
== DOSPTYP_EXT
||
416 e1
->type
.mbr
== DOSPTYP_EXTLBA
)
422 offset
= e1
->part
.start
;
423 buf
= malloc(table
->sectorsize
);
426 DEBUG("EBR detected");
427 for (i
= 0; i
< MAXEBRENTRIES
; i
++) {
428 #if 0 /* Some BIOSes return an incorrect number of sectors */
429 if (offset
>= table
->sectors
)
432 if (dread(dev
, buf
, 1, offset
) != 0)
434 dp
= (struct dos_partition
*)(buf
+ DOSPARTOFF
);
435 if (dp
[0].dp_typ
== 0)
437 start
= le32toh(dp
[0].dp_start
);
438 if (dp
[0].dp_typ
== DOSPTYP_EXT
&&
440 offset
= e1
->part
.start
+ start
;
443 end
= le32toh(dp
[0].dp_size
);
444 entry
= malloc(sizeof (*entry
));
447 entry
->part
.start
= offset
+ start
;
448 entry
->part
.end
= entry
->part
.start
+ end
- 1;
449 entry
->part
.index
= idx
++;
450 entry
->part
.type
= mbr_parttype(dp
[0].dp_typ
);
451 entry
->flags
= dp
[0].dp_flag
;
452 entry
->type
.mbr
= dp
[0].dp_typ
;
453 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
454 DEBUG("new EBR partition added");
455 if (dp
[1].dp_typ
== 0)
457 offset
= e1
->part
.start
+ le32toh(dp
[1].dp_start
);
462 #endif /* LOADER_MBR_SUPPORT */
464 static enum partition_type
465 bsd_parttype(uint8_t type
)
470 return (PART_FREEBSD_NANDFS
);
472 return (PART_FREEBSD_SWAP
);
474 return (PART_FREEBSD_UFS
);
476 return (PART_FREEBSD_VINUM
);
478 return (PART_FREEBSD_ZFS
);
480 return (PART_UNKNOWN
);
483 static struct ptable
*
484 ptable_bsdread(struct ptable
*table
, void *dev
, diskread_t dread
)
486 struct disklabel
*dl
;
487 struct partition
*part
;
488 struct pentry
*entry
;
493 if (table
->sectorsize
< sizeof (struct disklabel
)) {
494 DEBUG("Too small sectorsize");
497 buf
= malloc(table
->sectorsize
);
500 if (dread(dev
, buf
, 1, 1) != 0) {
501 DEBUG("read failed");
506 dl
= (struct disklabel
*)buf
;
507 if (le32toh(dl
->d_magic
) != DISKMAGIC
&&
508 le32toh(dl
->d_magic2
) != DISKMAGIC
)
510 if (le32toh(dl
->d_secsize
) != table
->sectorsize
) {
511 DEBUG("unsupported sector size");
514 dl
->d_npartitions
= le16toh(dl
->d_npartitions
);
515 if (dl
->d_npartitions
> 20 || dl
->d_npartitions
< 8) {
516 DEBUG("invalid number of partitions");
519 DEBUG("BSD detected");
520 part
= &dl
->d_partitions
[0];
521 raw_offset
= le32toh(part
[RAW_PART
].p_offset
);
522 for (i
= 0; i
< dl
->d_npartitions
; i
++, part
++) {
525 if (part
->p_size
== 0)
527 entry
= malloc(sizeof (*entry
));
530 entry
->part
.start
= le32toh(part
->p_offset
) - raw_offset
;
531 entry
->part
.end
= entry
->part
.start
+
532 le32toh(part
->p_size
) - 1;
533 entry
->part
.type
= bsd_parttype(part
->p_fstype
);
534 entry
->part
.index
= i
; /* starts from zero */
535 entry
->type
.bsd
= part
->p_fstype
;
536 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
537 DEBUG("new BSD partition added");
539 table
->type
= PTABLE_BSD
;
545 #ifdef LOADER_VTOC8_SUPPORT
546 static enum partition_type
547 vtoc8_parttype(uint16_t type
)
551 case VTOC_TAG_FREEBSD_NANDFS
:
552 return (PART_FREEBSD_NANDFS
);
553 case VTOC_TAG_FREEBSD_SWAP
:
554 return (PART_FREEBSD_SWAP
);
555 case VTOC_TAG_FREEBSD_UFS
:
556 return (PART_FREEBSD_UFS
);
557 case VTOC_TAG_FREEBSD_VINUM
:
558 return (PART_FREEBSD_VINUM
);
559 case VTOC_TAG_FREEBSD_ZFS
:
560 return (PART_FREEBSD_ZFS
);
562 return (PART_UNKNOWN
);
565 static struct ptable
*
566 ptable_vtoc8read(struct ptable
*table
, void *dev
, diskread_t dread
)
568 struct pentry
*entry
;
571 uint16_t sum
, heads
, sectors
;
574 if (table
->sectorsize
!= sizeof (struct vtoc8
))
576 buf
= malloc(table
->sectorsize
);
579 if (dread(dev
, buf
, 1, 0) != 0) {
580 DEBUG("read failed");
585 dl
= (struct vtoc8
*)buf
;
587 for (i
= sum
= 0; i
< sizeof (struct vtoc8
); i
+= sizeof (sum
))
588 sum
^= be16dec(buf
+ i
);
590 DEBUG("incorrect checksum");
593 if (be16toh(dl
->nparts
) != VTOC8_NPARTS
) {
594 DEBUG("invalid number of entries");
597 sectors
= be16toh(dl
->nsecs
);
598 heads
= be16toh(dl
->nheads
);
599 if (sectors
* heads
== 0) {
600 DEBUG("invalid geometry");
603 DEBUG("VTOC8 detected");
604 for (i
= 0; i
< VTOC8_NPARTS
; i
++) {
605 dl
->part
[i
].tag
= be16toh(dl
->part
[i
].tag
);
606 if (i
== VTOC_RAW_PART
||
607 dl
->part
[i
].tag
== VTOC_TAG_UNASSIGNED
)
609 entry
= malloc(sizeof (*entry
));
612 entry
->part
.start
= be32toh(dl
->map
[i
].cyl
) * heads
* sectors
;
613 entry
->part
.end
= be32toh(dl
->map
[i
].nblks
) +
614 entry
->part
.start
- 1;
615 entry
->part
.type
= vtoc8_parttype(dl
->part
[i
].tag
);
616 entry
->part
.index
= i
; /* starts from zero */
617 entry
->type
.vtoc8
= dl
->part
[i
].tag
;
618 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
619 DEBUG("new VTOC8 partition added");
621 table
->type
= PTABLE_VTOC8
;
627 #endif /* LOADER_VTOC8_SUPPORT */
629 static enum partition_type
630 vtoc_parttype(uint16_t type
)
634 return (PART_VTOC_BOOT
);
636 return (PART_VTOC_ROOT
);
638 return (PART_VTOC_SWAP
);
640 return (PART_VTOC_USR
);
641 case VTOC_TAG_BACKUP
:
642 return (PART_VTOC_BACKUP
);
644 return (PART_VTOC_STAND
);
646 return (PART_VTOC_VAR
);
648 return (PART_VTOC_HOME
);
650 return (PART_UNKNOWN
);
653 static struct ptable
*
654 ptable_dklabelread(struct ptable
*table
, void *dev
, diskread_t dread
)
656 struct pentry
*entry
;
662 if (table
->sectorsize
< sizeof (struct dk_label
)) {
663 DEBUG("Too small sectorsize");
666 buf
= malloc(table
->sectorsize
);
669 if (dread(dev
, buf
, 1, DK_LABEL_LOC
) != 0) {
670 DEBUG("read failed");
675 dl
= (struct dk_label
*)buf
;
676 dv
= (struct dk_vtoc
*)&dl
->dkl_vtoc
;
678 if (dl
->dkl_magic
!= VTOC_MAGIC
) {
679 DEBUG("dk_label magic error");
682 if (dv
->v_sanity
!= VTOC_SANITY
) {
683 DEBUG("this vtoc is not sane");
686 if (dv
->v_nparts
!= NDKMAP
) {
687 DEBUG("invalid number of entries");
690 DEBUG("VTOC detected");
691 for (i
= 0; i
< NDKMAP
; i
++) {
692 if (i
== VTOC_RAW_PART
|| /* skip slice 2 and empty */
693 dv
->v_part
[i
].p_size
== 0)
695 entry
= malloc(sizeof (*entry
));
698 entry
->part
.start
= dv
->v_part
[i
].p_start
;
699 entry
->part
.end
= dv
->v_part
[i
].p_size
+
700 entry
->part
.start
- 1;
701 entry
->part
.type
= vtoc_parttype(dv
->v_part
[i
].p_tag
);
702 entry
->part
.index
= i
; /* starts from zero */
703 entry
->type
.vtoc
= dv
->v_part
[i
].p_tag
;
704 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
705 DEBUG("new VTOC partition added");
707 table
->type
= PTABLE_VTOC
;
714 ptable_open(void *dev
, uint64_t sectors
, uint16_t sectorsize
, diskread_t
*dread
)
716 struct dos_partition
*dp
;
717 struct ptable
*table
;
720 #ifdef LOADER_MBR_SUPPORT
721 struct pentry
*entry
;
726 buf
= malloc(sectorsize
);
729 /* First, read the MBR. */
730 if (dread(dev
, buf
, 1, DOSBBSECTOR
) != 0) {
731 DEBUG("read failed");
735 table
= malloc(sizeof (*table
));
738 table
->sectors
= sectors
;
739 table
->sectorsize
= sectorsize
;
740 table
->type
= PTABLE_NONE
;
741 STAILQ_INIT(&table
->entries
);
743 if (ptable_dklabelread(table
, dev
, dread
) == NULL
) { /* Read error. */
746 } else if (table
->type
== PTABLE_VTOC
)
749 #ifdef LOADER_VTOC8_SUPPORT
750 if (be16dec(buf
+ offsetof(struct vtoc8
, magic
)) == VTOC_MAGIC
) {
751 if (ptable_vtoc8read(table
, dev
, dread
) == NULL
) {
755 } else if (table
->type
== PTABLE_VTOC8
)
759 /* Check the BSD label. */
760 if (ptable_bsdread(table
, dev
, dread
) == NULL
) { /* Read error. */
763 } else if (table
->type
== PTABLE_BSD
)
766 #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
767 /* Check the MBR magic. */
768 if (buf
[DOSMAGICOFFSET
] != 0x55 ||
769 buf
[DOSMAGICOFFSET
+ 1] != 0xaa) {
770 DEBUG("magic sequence not found");
771 #if defined(LOADER_GPT_SUPPORT)
772 /* There is no PMBR, check that we have backup GPT */
773 table
->type
= PTABLE_GPT
;
774 table
= ptable_gptread(table
, dev
, dread
);
778 /* Check that we have PMBR. Also do some validation. */
779 dp
= (struct dos_partition
*)(buf
+ DOSPARTOFF
);
780 for (i
= 0, count
= 0; i
< NDOSPART
; i
++) {
781 if (dp
[i
].dp_flag
!= 0 && dp
[i
].dp_flag
!= 0x80) {
782 DEBUG("invalid partition flag %x", dp
[i
].dp_flag
);
785 #ifdef LOADER_GPT_SUPPORT
786 if (dp
[i
].dp_typ
== DOSPTYP_PMBR
) {
787 table
->type
= PTABLE_GPT
;
788 DEBUG("PMBR detected");
791 if (dp
[i
].dp_typ
!= 0)
794 /* Do we have some invalid values? */
795 if (table
->type
== PTABLE_GPT
&& count
> 1) {
796 if (dp
[1].dp_typ
!= DOSPTYP_HFS
) {
797 table
->type
= PTABLE_NONE
;
798 DEBUG("Incorrect PMBR, ignore it");
800 DEBUG("Bootcamp detected");
803 #ifdef LOADER_GPT_SUPPORT
804 if (table
->type
== PTABLE_GPT
) {
805 table
= ptable_gptread(table
, dev
, dread
);
809 #ifdef LOADER_MBR_SUPPORT
811 DEBUG("MBR detected");
812 table
->type
= PTABLE_MBR
;
813 for (i
= has_ext
= 0; i
< NDOSPART
; i
++) {
814 if (dp
[i
].dp_typ
== 0)
816 start
= le32dec(&(dp
[i
].dp_start
));
817 end
= le32dec(&(dp
[i
].dp_size
));
818 if (start
== 0 || end
== 0)
820 #if 0 /* Some BIOSes return an incorrect number of sectors */
821 if (start
+ end
- 1 >= sectors
)
822 continue; /* XXX: ignore */
824 if (dp
[i
].dp_typ
== DOSPTYP_EXT
||
825 dp
[i
].dp_typ
== DOSPTYP_EXTLBA
)
827 entry
= malloc(sizeof (*entry
));
830 entry
->part
.start
= start
;
831 entry
->part
.end
= start
+ end
- 1;
832 entry
->part
.index
= i
+ 1;
833 entry
->part
.type
= mbr_parttype(dp
[i
].dp_typ
);
834 entry
->flags
= dp
[i
].dp_flag
;
835 entry
->type
.mbr
= dp
[i
].dp_typ
;
836 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
837 DEBUG("new MBR partition added");
840 table
= ptable_ebrread(table
, dev
, dread
);
843 #endif /* LOADER_MBR_SUPPORT */
844 #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
851 ptable_close(struct ptable
*table
)
853 struct pentry
*entry
;
858 while (!STAILQ_EMPTY(&table
->entries
)) {
859 entry
= STAILQ_FIRST(&table
->entries
);
860 STAILQ_REMOVE_HEAD(&table
->entries
, entry
);
867 ptable_gettype(const struct ptable
*table
)
870 return (table
->type
);
874 ptable_getsize(const struct ptable
*table
, uint64_t *sizep
)
876 uint64_t tmp
= table
->sectors
* table
->sectorsize
;
878 if (tmp
< table
->sectors
)
887 ptable_getpart(const struct ptable
*table
, struct ptable_entry
*part
, int idx
)
889 struct pentry
*entry
;
891 if (part
== NULL
|| table
== NULL
)
894 STAILQ_FOREACH(entry
, &table
->entries
, entry
) {
895 if (entry
->part
.index
!= idx
)
897 memcpy(part
, &entry
->part
, sizeof (*part
));
904 * Search for a slice with the following preferences:
906 * 1: Active illumos slice
907 * 2: Non-active illumos slice
908 * 3: Active Linux slice
909 * 4: non-active Linux slice
910 * 5: Active FAT/FAT32 slice
911 * 6: non-active FAT/FAT32 slice
913 #define PREF_RAWDISK 0
914 #define PREF_ILLUMOS_ACT 1
915 #define PREF_ILLUMOS 2
916 #define PREF_LINUX_ACT 3
918 #define PREF_DOS_ACT 5
922 ptable_getbestpart(const struct ptable
*table
, struct ptable_entry
*part
)
924 struct pentry
*entry
, *best
;
927 if (part
== NULL
|| table
== NULL
)
931 preflevel
= pref
= PREF_NONE
;
932 STAILQ_FOREACH(entry
, &table
->entries
, entry
) {
933 #ifdef LOADER_MBR_SUPPORT
934 if (table
->type
== PTABLE_MBR
) {
935 switch (entry
->type
.mbr
) {
936 case DOSPTYP_SUNIXOS2
:
937 pref
= entry
->flags
& 0x80 ? PREF_ILLUMOS_ACT
:
941 pref
= entry
->flags
& 0x80 ? PREF_LINUX_ACT
:
944 case 0x01: /* DOS/Windows */
950 pref
= entry
->flags
& 0x80 ? PREF_DOS_ACT
:
957 #endif /* LOADER_MBR_SUPPORT */
958 #ifdef LOADER_GPT_SUPPORT
959 if (table
->type
== PTABLE_GPT
) {
960 if (entry
->part
.type
== PART_DOS
)
962 else if (entry
->part
.type
== PART_ILLUMOS_ZFS
)
967 #endif /* LOADER_GPT_SUPPORT */
968 if (pref
< preflevel
) {
974 memcpy(part
, &best
->part
, sizeof (*part
));
981 * iterate will stop if iterator will return non 0.
984 ptable_iterate(const struct ptable
*table
, void *arg
, ptable_iterate_t
*iter
)
986 struct pentry
*entry
;
991 STAILQ_FOREACH(entry
, &table
->entries
, entry
) {
992 #ifdef LOADER_MBR_SUPPORT
993 if (table
->type
== PTABLE_MBR
)
994 sprintf(name
, "s%d", entry
->part
.index
);
997 #ifdef LOADER_GPT_SUPPORT
998 if (table
->type
== PTABLE_GPT
)
999 sprintf(name
, "p%d", entry
->part
.index
);
1002 #ifdef LOADER_VTOC8_SUPPORT
1003 if (table
->type
== PTABLE_VTOC8
)
1004 sprintf(name
, "%c", (uint8_t)'a' +
1008 if (table
->type
== PTABLE_VTOC
)
1009 sprintf(name
, "%c", (uint8_t)'a' +
1012 if (table
->type
== PTABLE_BSD
)
1013 sprintf(name
, "%c", (uint8_t)'a' +
1015 ret
= iter(arg
, name
, &entry
->part
);