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>
39 #include <fs/cd9660/iso.h>
46 #define DEBUG(fmt, args...) printf("%s: " fmt "\n", __func__, ## args)
48 #define DEBUG(fmt, args...)
51 #ifdef LOADER_GPT_SUPPORT
53 static const uuid_t gpt_uuid_unused
= GPT_ENT_TYPE_UNUSED
;
54 static const uuid_t gpt_uuid_ms_basic_data
= GPT_ENT_TYPE_MS_BASIC_DATA
;
55 static const uuid_t gpt_uuid_freebsd_ufs
= GPT_ENT_TYPE_FREEBSD_UFS
;
56 static const uuid_t gpt_uuid_efi
= GPT_ENT_TYPE_EFI
;
57 static const uuid_t gpt_uuid_freebsd
= GPT_ENT_TYPE_FREEBSD
;
58 static const uuid_t gpt_uuid_freebsd_boot
= GPT_ENT_TYPE_FREEBSD_BOOT
;
59 static const uuid_t gpt_uuid_freebsd_nandfs
= GPT_ENT_TYPE_FREEBSD_NANDFS
;
60 static const uuid_t gpt_uuid_freebsd_swap
= GPT_ENT_TYPE_FREEBSD_SWAP
;
61 static const uuid_t gpt_uuid_freebsd_zfs
= GPT_ENT_TYPE_FREEBSD_ZFS
;
62 static const uuid_t gpt_uuid_freebsd_vinum
= GPT_ENT_TYPE_FREEBSD_VINUM
;
63 static const uuid_t gpt_uuid_illumos_boot
= GPT_ENT_TYPE_ILLUMOS_BOOT
;
64 static const uuid_t gpt_uuid_illumos_ufs
= GPT_ENT_TYPE_ILLUMOS_UFS
;
65 static const uuid_t gpt_uuid_illumos_zfs
= GPT_ENT_TYPE_ILLUMOS_ZFS
;
66 static const uuid_t gpt_uuid_reserved
= GPT_ENT_TYPE_RESERVED
;
70 struct ptable_entry part
;
79 STAILQ_ENTRY(pentry
) entry
;
83 enum ptable_type type
;
87 STAILQ_HEAD(, pentry
) entries
;
90 static struct parttypes
{
91 enum partition_type type
;
94 { PART_UNKNOWN
, "Unknown" },
96 { PART_FREEBSD
, "FreeBSD" },
97 { PART_FREEBSD_BOOT
, "FreeBSD boot" },
98 { PART_FREEBSD_NANDFS
, "FreeBSD nandfs" },
99 { PART_FREEBSD_UFS
, "FreeBSD UFS" },
100 { PART_FREEBSD_ZFS
, "FreeBSD ZFS" },
101 { PART_FREEBSD_SWAP
, "FreeBSD swap" },
102 { PART_FREEBSD_VINUM
, "FreeBSD vinum" },
103 { PART_LINUX
, "Linux" },
104 { PART_LINUX_SWAP
, "Linux swap" },
105 { PART_DOS
, "DOS/Windows" },
106 { PART_ISO9660
, "ISO9660" },
107 { PART_SOLARIS2
, "Solaris 2" },
108 { PART_ILLUMOS_UFS
, "illumos UFS" },
109 { PART_ILLUMOS_ZFS
, "illumos ZFS" },
110 { PART_RESERVED
, "Reserved" },
111 { PART_VTOC_BOOT
, "boot" },
112 { PART_VTOC_ROOT
, "root" },
113 { PART_VTOC_SWAP
, "swap" },
114 { PART_VTOC_USR
, "usr" },
115 { PART_VTOC_STAND
, "stand" },
116 { PART_VTOC_VAR
, "var" },
117 { PART_VTOC_HOME
, "home" }
121 parttype2str(enum partition_type type
)
125 for (i
= 0; i
< nitems(ptypes
); i
++)
126 if (ptypes
[i
].type
== type
)
127 return (ptypes
[i
].desc
);
128 return (ptypes
[0].desc
);
131 #ifdef LOADER_GPT_SUPPORT
133 uuid_letoh(uuid_t
*uuid
)
136 uuid
->time_low
= le32toh(uuid
->time_low
);
137 uuid
->time_mid
= le16toh(uuid
->time_mid
);
138 uuid
->time_hi_and_version
= le16toh(uuid
->time_hi_and_version
);
141 static enum partition_type
142 gpt_parttype(uuid_t type
)
145 if (uuid_equal(&type
, &gpt_uuid_efi
, NULL
))
147 else if (uuid_equal(&type
, &gpt_uuid_ms_basic_data
, NULL
))
149 else if (uuid_equal(&type
, &gpt_uuid_freebsd_boot
, NULL
))
150 return (PART_FREEBSD_BOOT
);
151 else if (uuid_equal(&type
, &gpt_uuid_freebsd_ufs
, NULL
))
152 return (PART_FREEBSD_UFS
);
153 else if (uuid_equal(&type
, &gpt_uuid_freebsd_zfs
, NULL
))
154 return (PART_FREEBSD_ZFS
);
155 else if (uuid_equal(&type
, &gpt_uuid_freebsd_swap
, NULL
))
156 return (PART_FREEBSD_SWAP
);
157 else if (uuid_equal(&type
, &gpt_uuid_freebsd_vinum
, NULL
))
158 return (PART_FREEBSD_VINUM
);
159 else if (uuid_equal(&type
, &gpt_uuid_freebsd_nandfs
, NULL
))
160 return (PART_FREEBSD_NANDFS
);
161 else if (uuid_equal(&type
, &gpt_uuid_freebsd
, NULL
))
162 return (PART_FREEBSD
);
163 else if (uuid_equal(&type
, &gpt_uuid_illumos_boot
, NULL
))
164 return (PART_VTOC_BOOT
);
165 else if (uuid_equal(&type
, &gpt_uuid_illumos_ufs
, NULL
))
166 return (PART_ILLUMOS_UFS
);
167 else if (uuid_equal(&type
, &gpt_uuid_illumos_zfs
, NULL
))
168 return (PART_ILLUMOS_ZFS
);
169 else if (uuid_equal(&type
, &gpt_uuid_reserved
, NULL
))
170 return (PART_RESERVED
);
171 return (PART_UNKNOWN
);
174 static struct gpt_hdr
*
175 gpt_checkhdr(struct gpt_hdr
*hdr
, uint64_t lba_self
,
176 uint64_t lba_last
__attribute((unused
)), uint16_t sectorsize
)
180 if (memcmp(hdr
->hdr_sig
, GPT_HDR_SIG
, sizeof (hdr
->hdr_sig
)) != 0) {
181 DEBUG("no GPT signature");
184 sz
= le32toh(hdr
->hdr_size
);
185 if (sz
< 92 || sz
> sectorsize
) {
186 DEBUG("invalid GPT header size: %u", sz
);
189 crc
= le32toh(hdr
->hdr_crc_self
);
190 hdr
->hdr_crc_self
= crc32(0, Z_NULL
, 0);
191 if (crc32(hdr
->hdr_crc_self
, (const Bytef
*)hdr
, sz
) != crc
) {
192 DEBUG("GPT header's CRC doesn't match");
195 hdr
->hdr_crc_self
= crc
;
196 hdr
->hdr_revision
= le32toh(hdr
->hdr_revision
);
197 if (hdr
->hdr_revision
< GPT_HDR_REVISION
) {
198 DEBUG("unsupported GPT revision %u", hdr
->hdr_revision
);
201 hdr
->hdr_lba_self
= le64toh(hdr
->hdr_lba_self
);
202 if (hdr
->hdr_lba_self
!= lba_self
) {
203 DEBUG("self LBA doesn't match");
206 hdr
->hdr_lba_alt
= le64toh(hdr
->hdr_lba_alt
);
207 if (hdr
->hdr_lba_alt
== hdr
->hdr_lba_self
) {
208 DEBUG("invalid alternate LBA");
211 hdr
->hdr_entries
= le32toh(hdr
->hdr_entries
);
212 hdr
->hdr_entsz
= le32toh(hdr
->hdr_entsz
);
213 if (hdr
->hdr_entries
== 0 ||
214 hdr
->hdr_entsz
< sizeof (struct gpt_ent
) ||
215 sectorsize
% hdr
->hdr_entsz
!= 0) {
216 DEBUG("invalid entry size or number of entries");
219 hdr
->hdr_lba_start
= le64toh(hdr
->hdr_lba_start
);
220 hdr
->hdr_lba_end
= le64toh(hdr
->hdr_lba_end
);
221 hdr
->hdr_lba_table
= le64toh(hdr
->hdr_lba_table
);
222 hdr
->hdr_crc_table
= le32toh(hdr
->hdr_crc_table
);
223 uuid_letoh(&hdr
->hdr_uuid
);
228 gpt_checktbl(const struct gpt_hdr
*hdr
, uint8_t *tbl
, size_t size
,
229 uint64_t lba_last
__attribute((unused
)))
234 cnt
= size
/ hdr
->hdr_entsz
;
235 if (hdr
->hdr_entries
<= cnt
) {
236 cnt
= hdr
->hdr_entries
;
237 /* Check CRC only when buffer size is enough for table. */
238 if (hdr
->hdr_crc_table
!=
239 crc32(0, tbl
, hdr
->hdr_entries
* hdr
->hdr_entsz
)) {
240 DEBUG("GPT table's CRC doesn't match");
244 for (i
= 0; i
< cnt
; i
++) {
245 ent
= (struct gpt_ent
*)(tbl
+ i
* hdr
->hdr_entsz
);
246 uuid_letoh(&ent
->ent_type
);
247 if (uuid_equal(&ent
->ent_type
, &gpt_uuid_unused
, NULL
))
249 ent
->ent_lba_start
= le64toh(ent
->ent_lba_start
);
250 ent
->ent_lba_end
= le64toh(ent
->ent_lba_end
);
255 static struct ptable
*
256 ptable_gptread(struct ptable
*table
, void *dev
, diskread_t dread
)
258 struct pentry
*entry
;
259 struct gpt_hdr
*phdr
, hdr
;
266 buf
= malloc(table
->sectorsize
);
269 tbl
= malloc(table
->sectorsize
* MAXTBLSZ
);
274 /* Read the primary GPT header. */
275 if (dread(dev
, buf
, 1, 1) != 0) {
281 /* Check the primary GPT header. */
282 phdr
= gpt_checkhdr((struct gpt_hdr
*)buf
, 1, table
->sectors
- 1,
285 /* Read the primary GPT table. */
286 size
= MIN(MAXTBLSZ
, (phdr
->hdr_entries
* phdr
->hdr_entsz
+
287 table
->sectorsize
- 1) / table
->sectorsize
);
288 if (dread(dev
, tbl
, size
, phdr
->hdr_lba_table
) == 0 &&
289 gpt_checktbl(phdr
, tbl
, size
* table
->sectorsize
,
290 table
->sectors
- 1) == 0) {
291 memcpy(&hdr
, phdr
, sizeof (hdr
));
295 offset
= pri
? hdr
.hdr_lba_alt
: table
->sectors
- 1;
296 /* Read the backup GPT header. */
297 if (dread(dev
, buf
, 1, offset
) != 0)
300 phdr
= gpt_checkhdr((struct gpt_hdr
*)buf
, offset
,
301 table
->sectors
- 1, table
->sectorsize
);
304 * Compare primary and backup headers.
305 * If they are equal, then we do not need to read backup
306 * table. If they are different, then prefer backup header
307 * and try to read backup table.
310 uuid_equal(&hdr
.hdr_uuid
, &phdr
->hdr_uuid
, NULL
) == 0 ||
311 hdr
.hdr_revision
!= phdr
->hdr_revision
||
312 hdr
.hdr_size
!= phdr
->hdr_size
||
313 hdr
.hdr_lba_start
!= phdr
->hdr_lba_start
||
314 hdr
.hdr_lba_end
!= phdr
->hdr_lba_end
||
315 hdr
.hdr_entries
!= phdr
->hdr_entries
||
316 hdr
.hdr_entsz
!= phdr
->hdr_entsz
||
317 hdr
.hdr_crc_table
!= phdr
->hdr_crc_table
) {
318 /* Read the backup GPT table. */
319 size
= MIN(MAXTBLSZ
, (phdr
->hdr_entries
*
320 phdr
->hdr_entsz
+ table
->sectorsize
- 1) /
322 if (dread(dev
, tbl
, size
, phdr
->hdr_lba_table
) == 0 &&
323 gpt_checktbl(phdr
, tbl
, size
* table
->sectorsize
,
324 table
->sectors
- 1) == 0) {
325 memcpy(&hdr
, phdr
, sizeof (hdr
));
330 if (pri
== 0 && sec
== 0) {
331 /* Both primary and backup tables are invalid. */
332 table
->type
= PTABLE_NONE
;
335 DEBUG("GPT detected");
336 size
= MIN(hdr
.hdr_entries
* hdr
.hdr_entsz
,
337 MAXTBLSZ
* table
->sectorsize
);
340 * If the disk's sector count is smaller than the sector count recorded
341 * in the disk's GPT table header, set the table->sectors to the value
342 * recorded in GPT tables. This is done to work around buggy firmware
343 * that returns truncated disk sizes.
345 * Note, this is still not a foolproof way to get disk's size. For
346 * example, an image file can be truncated when copied to smaller media.
348 table
->sectors
= hdr
.hdr_lba_alt
+ 1;
350 for (i
= 0; i
< size
/ hdr
.hdr_entsz
; i
++) {
351 ent
= (struct gpt_ent
*)(tbl
+ i
* hdr
.hdr_entsz
);
352 if (uuid_equal(&ent
->ent_type
, &gpt_uuid_unused
, NULL
))
355 /* Simple sanity checks. */
356 if (ent
->ent_lba_start
< hdr
.hdr_lba_start
||
357 ent
->ent_lba_end
> hdr
.hdr_lba_end
||
358 ent
->ent_lba_start
> ent
->ent_lba_end
)
361 entry
= malloc(sizeof (*entry
));
364 entry
->part
.start
= ent
->ent_lba_start
;
365 entry
->part
.end
= ent
->ent_lba_end
;
366 entry
->part
.index
= i
+ 1;
367 entry
->part
.type
= gpt_parttype(ent
->ent_type
);
368 entry
->flags
= le64toh(ent
->ent_attr
);
369 memcpy(&entry
->type
.gpt
, &ent
->ent_type
, sizeof (uuid_t
));
370 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
371 DEBUG("new GPT partition added");
378 #endif /* LOADER_GPT_SUPPORT */
380 #ifdef LOADER_MBR_SUPPORT
381 /* We do not need to support too many EBR partitions in the loader */
382 #define MAXEBRENTRIES 8
383 static enum partition_type
384 mbr_parttype(uint8_t type
)
389 return (PART_FREEBSD
);
391 return (PART_LINUX_SWAP
);
394 case DOSPTYP_SUNIXOS2
:
395 return (PART_SOLARIS2
);
405 return (PART_UNKNOWN
);
408 static struct ptable
*
409 ptable_ebrread(struct ptable
*table
, void *dev
, diskread_t dread
)
411 struct dos_partition
*dp
;
412 struct pentry
*e1
, *entry
;
413 uint32_t start
, end
, offset
;
417 STAILQ_FOREACH(e1
, &table
->entries
, entry
) {
418 if (e1
->type
.mbr
== DOSPTYP_EXT
||
419 e1
->type
.mbr
== DOSPTYP_EXTLBA
)
425 offset
= e1
->part
.start
;
426 buf
= malloc(table
->sectorsize
);
429 DEBUG("EBR detected");
430 for (i
= 0; i
< MAXEBRENTRIES
; i
++) {
431 #if 0 /* Some BIOSes return an incorrect number of sectors */
432 if (offset
>= table
->sectors
)
435 if (dread(dev
, buf
, 1, offset
) != 0)
437 dp
= (struct dos_partition
*)(buf
+ DOSPARTOFF
);
438 if (dp
[0].dp_typ
== 0)
440 start
= le32toh(dp
[0].dp_start
);
441 if (dp
[0].dp_typ
== DOSPTYP_EXT
&&
443 offset
= e1
->part
.start
+ start
;
446 end
= le32toh(dp
[0].dp_size
);
447 entry
= malloc(sizeof (*entry
));
450 entry
->part
.start
= offset
+ start
;
451 entry
->part
.end
= entry
->part
.start
+ end
- 1;
452 entry
->part
.index
= idx
++;
453 entry
->part
.type
= mbr_parttype(dp
[0].dp_typ
);
454 entry
->flags
= dp
[0].dp_flag
;
455 entry
->type
.mbr
= dp
[0].dp_typ
;
456 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
457 DEBUG("new EBR partition added");
458 if (dp
[1].dp_typ
== 0)
460 offset
= e1
->part
.start
+ le32toh(dp
[1].dp_start
);
465 #endif /* LOADER_MBR_SUPPORT */
467 static enum partition_type
468 bsd_parttype(uint8_t type
)
473 return (PART_FREEBSD_NANDFS
);
475 return (PART_FREEBSD_SWAP
);
477 return (PART_FREEBSD_UFS
);
479 return (PART_FREEBSD_VINUM
);
481 return (PART_FREEBSD_ZFS
);
483 return (PART_UNKNOWN
);
486 static struct ptable
*
487 ptable_bsdread(struct ptable
*table
, void *dev
, diskread_t dread
)
489 struct disklabel
*dl
;
490 struct partition
*part
;
491 struct pentry
*entry
;
496 if (table
->sectorsize
< sizeof (struct disklabel
)) {
497 DEBUG("Too small sectorsize");
500 buf
= malloc(table
->sectorsize
);
503 if (dread(dev
, buf
, 1, 1) != 0) {
504 DEBUG("read failed");
509 dl
= (struct disklabel
*)buf
;
510 if (le32toh(dl
->d_magic
) != DISKMAGIC
&&
511 le32toh(dl
->d_magic2
) != DISKMAGIC
)
513 if (le32toh(dl
->d_secsize
) != table
->sectorsize
) {
514 DEBUG("unsupported sector size");
517 dl
->d_npartitions
= le16toh(dl
->d_npartitions
);
518 if (dl
->d_npartitions
> 20 || dl
->d_npartitions
< 8) {
519 DEBUG("invalid number of partitions");
522 DEBUG("BSD detected");
523 part
= &dl
->d_partitions
[0];
524 raw_offset
= le32toh(part
[RAW_PART
].p_offset
);
525 for (i
= 0; i
< dl
->d_npartitions
; i
++, part
++) {
528 if (part
->p_size
== 0)
530 entry
= malloc(sizeof (*entry
));
533 entry
->part
.start
= le32toh(part
->p_offset
) - raw_offset
;
534 entry
->part
.end
= entry
->part
.start
+
535 le32toh(part
->p_size
) - 1;
536 entry
->part
.type
= bsd_parttype(part
->p_fstype
);
537 entry
->part
.index
= i
; /* starts from zero */
538 entry
->type
.bsd
= part
->p_fstype
;
539 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
540 DEBUG("new BSD partition added");
542 table
->type
= PTABLE_BSD
;
548 #ifdef LOADER_VTOC8_SUPPORT
549 static enum partition_type
550 vtoc8_parttype(uint16_t type
)
554 case VTOC_TAG_FREEBSD_NANDFS
:
555 return (PART_FREEBSD_NANDFS
);
556 case VTOC_TAG_FREEBSD_SWAP
:
557 return (PART_FREEBSD_SWAP
);
558 case VTOC_TAG_FREEBSD_UFS
:
559 return (PART_FREEBSD_UFS
);
560 case VTOC_TAG_FREEBSD_VINUM
:
561 return (PART_FREEBSD_VINUM
);
562 case VTOC_TAG_FREEBSD_ZFS
:
563 return (PART_FREEBSD_ZFS
);
565 return (PART_UNKNOWN
);
568 static struct ptable
*
569 ptable_vtoc8read(struct ptable
*table
, void *dev
, diskread_t dread
)
571 struct pentry
*entry
;
574 uint16_t sum
, heads
, sectors
;
577 if (table
->sectorsize
!= sizeof (struct vtoc8
))
579 buf
= malloc(table
->sectorsize
);
582 if (dread(dev
, buf
, 1, 0) != 0) {
583 DEBUG("read failed");
588 dl
= (struct vtoc8
*)buf
;
590 for (i
= sum
= 0; i
< sizeof (struct vtoc8
); i
+= sizeof (sum
))
591 sum
^= be16dec(buf
+ i
);
593 DEBUG("incorrect checksum");
596 if (be16toh(dl
->nparts
) != VTOC8_NPARTS
) {
597 DEBUG("invalid number of entries");
600 sectors
= be16toh(dl
->nsecs
);
601 heads
= be16toh(dl
->nheads
);
602 if (sectors
* heads
== 0) {
603 DEBUG("invalid geometry");
606 DEBUG("VTOC8 detected");
607 for (i
= 0; i
< VTOC8_NPARTS
; i
++) {
608 dl
->part
[i
].tag
= be16toh(dl
->part
[i
].tag
);
609 if (i
== VTOC_RAW_PART
||
610 dl
->part
[i
].tag
== VTOC_TAG_UNASSIGNED
)
612 entry
= malloc(sizeof (*entry
));
615 entry
->part
.start
= be32toh(dl
->map
[i
].cyl
) * heads
* sectors
;
616 entry
->part
.end
= be32toh(dl
->map
[i
].nblks
) +
617 entry
->part
.start
- 1;
618 entry
->part
.type
= vtoc8_parttype(dl
->part
[i
].tag
);
619 entry
->part
.index
= i
; /* starts from zero */
620 entry
->type
.vtoc8
= dl
->part
[i
].tag
;
621 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
622 DEBUG("new VTOC8 partition added");
624 table
->type
= PTABLE_VTOC8
;
630 #endif /* LOADER_VTOC8_SUPPORT */
632 static enum partition_type
633 vtoc_parttype(uint16_t type
)
637 return (PART_VTOC_BOOT
);
639 return (PART_VTOC_ROOT
);
641 return (PART_VTOC_SWAP
);
643 return (PART_VTOC_USR
);
644 case VTOC_TAG_BACKUP
:
645 return (PART_VTOC_BACKUP
);
647 return (PART_VTOC_STAND
);
649 return (PART_VTOC_VAR
);
651 return (PART_VTOC_HOME
);
653 return (PART_UNKNOWN
);
656 static struct ptable
*
657 ptable_dklabelread(struct ptable
*table
, void *dev
, diskread_t dread
)
659 struct pentry
*entry
;
665 if (table
->sectorsize
< sizeof (struct dk_label
)) {
666 DEBUG("Too small sectorsize");
669 buf
= malloc(table
->sectorsize
);
672 if (dread(dev
, buf
, 1, DK_LABEL_LOC
) != 0) {
673 DEBUG("read failed");
678 dl
= (struct dk_label
*)buf
;
679 dv
= (struct dk_vtoc
*)&dl
->dkl_vtoc
;
681 if (dl
->dkl_magic
!= VTOC_MAGIC
) {
682 DEBUG("dk_label magic error");
685 if (dv
->v_sanity
!= VTOC_SANITY
) {
686 DEBUG("this vtoc is not sane");
689 if (dv
->v_nparts
!= NDKMAP
) {
690 DEBUG("invalid number of entries");
693 DEBUG("VTOC detected");
694 for (i
= 0; i
< NDKMAP
; i
++) {
695 if (i
== VTOC_RAW_PART
|| /* skip slice 2 and empty */
696 dv
->v_part
[i
].p_size
== 0)
698 entry
= malloc(sizeof (*entry
));
701 entry
->part
.start
= dv
->v_part
[i
].p_start
;
702 entry
->part
.end
= dv
->v_part
[i
].p_size
+
703 entry
->part
.start
- 1;
704 entry
->part
.type
= vtoc_parttype(dv
->v_part
[i
].p_tag
);
705 entry
->part
.index
= i
; /* starts from zero */
706 entry
->type
.vtoc
= dv
->v_part
[i
].p_tag
;
707 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
708 DEBUG("new VTOC partition added");
710 table
->type
= PTABLE_VTOC
;
716 #define cdb2devb(bno) ((bno) * ISO_DEFAULT_BLOCK_SIZE / table->sectorsize)
718 static struct ptable
*
719 ptable_iso9660read(struct ptable
*table
, void *dev
, diskread_t dread
)
722 struct iso_primary_descriptor
*vd
;
723 struct pentry
*entry
;
725 buf
= malloc(table
->sectorsize
);
729 if (dread(dev
, buf
, 1, cdb2devb(16)) != 0) {
730 DEBUG("read failed");
735 vd
= (struct iso_primary_descriptor
*)buf
;
736 if (bcmp(vd
->id
, ISO_STANDARD_ID
, sizeof (vd
->id
)) != 0)
739 entry
= malloc(sizeof (*entry
));
742 entry
->part
.start
= 0;
743 entry
->part
.end
= table
->sectors
;
744 entry
->part
.type
= PART_ISO9660
;
745 entry
->part
.index
= 0;
746 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
748 table
->type
= PTABLE_ISO9660
;
756 ptable_open(void *dev
, uint64_t sectors
, uint16_t sectorsize
, diskread_t
*dread
)
758 struct dos_partition
*dp
;
759 struct ptable
*table
;
762 #ifdef LOADER_MBR_SUPPORT
763 struct pentry
*entry
;
768 buf
= malloc(sectorsize
);
771 /* First, read the MBR. */
772 if (dread(dev
, buf
, 1, DOSBBSECTOR
) != 0) {
773 DEBUG("read failed");
777 table
= malloc(sizeof (*table
));
780 table
->sectors
= sectors
;
781 table
->sectorsize
= sectorsize
;
782 table
->type
= PTABLE_NONE
;
783 STAILQ_INIT(&table
->entries
);
785 if (ptable_iso9660read(table
, dev
, dread
) == NULL
) {
789 } else if (table
->type
== PTABLE_ISO9660
)
792 if (ptable_dklabelread(table
, dev
, dread
) == NULL
) { /* Read error. */
795 } else if (table
->type
== PTABLE_VTOC
)
798 #ifdef LOADER_VTOC8_SUPPORT
799 if (be16dec(buf
+ offsetof(struct vtoc8
, magic
)) == VTOC_MAGIC
) {
800 if (ptable_vtoc8read(table
, dev
, dread
) == NULL
) {
804 } else if (table
->type
== PTABLE_VTOC8
)
808 /* Check the BSD label. */
809 if (ptable_bsdread(table
, dev
, dread
) == NULL
) { /* Read error. */
812 } else if (table
->type
== PTABLE_BSD
)
815 #if defined(LOADER_GPT_SUPPORT) || defined(LOADER_MBR_SUPPORT)
816 /* Check the MBR magic. */
817 if (buf
[DOSMAGICOFFSET
] != 0x55 ||
818 buf
[DOSMAGICOFFSET
+ 1] != 0xaa) {
819 DEBUG("magic sequence not found");
820 #if defined(LOADER_GPT_SUPPORT)
821 /* There is no PMBR, check that we have backup GPT */
822 table
->type
= PTABLE_GPT
;
823 table
= ptable_gptread(table
, dev
, dread
);
827 /* Check that we have PMBR. Also do some validation. */
828 dp
= (struct dos_partition
*)(buf
+ DOSPARTOFF
);
829 for (i
= 0, count
= 0; i
< NDOSPART
; i
++) {
830 if (dp
[i
].dp_flag
!= 0 && dp
[i
].dp_flag
!= 0x80) {
831 DEBUG("invalid partition flag %x", dp
[i
].dp_flag
);
834 #ifdef LOADER_GPT_SUPPORT
835 if (dp
[i
].dp_typ
== DOSPTYP_PMBR
) {
836 table
->type
= PTABLE_GPT
;
837 DEBUG("PMBR detected");
840 if (dp
[i
].dp_typ
!= 0)
843 /* Do we have some invalid values? */
844 if (table
->type
== PTABLE_GPT
&& count
> 1) {
845 if (dp
[1].dp_typ
!= DOSPTYP_HFS
) {
846 table
->type
= PTABLE_NONE
;
847 DEBUG("Incorrect PMBR, ignore it");
849 DEBUG("Bootcamp detected");
852 #ifdef LOADER_GPT_SUPPORT
853 if (table
->type
== PTABLE_GPT
) {
854 table
= ptable_gptread(table
, dev
, dread
);
858 #ifdef LOADER_MBR_SUPPORT
860 DEBUG("MBR detected");
861 table
->type
= PTABLE_MBR
;
862 for (i
= has_ext
= 0; i
< NDOSPART
; i
++) {
863 if (dp
[i
].dp_typ
== 0)
865 start
= le32dec(&(dp
[i
].dp_start
));
866 end
= le32dec(&(dp
[i
].dp_size
));
867 if (start
== 0 || end
== 0)
869 #if 0 /* Some BIOSes return an incorrect number of sectors */
870 if (start
+ end
- 1 >= sectors
)
871 continue; /* XXX: ignore */
873 if (dp
[i
].dp_typ
== DOSPTYP_EXT
||
874 dp
[i
].dp_typ
== DOSPTYP_EXTLBA
)
876 entry
= malloc(sizeof (*entry
));
879 entry
->part
.start
= start
;
880 entry
->part
.end
= start
+ end
- 1;
881 entry
->part
.index
= i
+ 1;
882 entry
->part
.type
= mbr_parttype(dp
[i
].dp_typ
);
883 entry
->flags
= dp
[i
].dp_flag
;
884 entry
->type
.mbr
= dp
[i
].dp_typ
;
885 STAILQ_INSERT_TAIL(&table
->entries
, entry
, entry
);
886 DEBUG("new MBR partition added");
889 table
= ptable_ebrread(table
, dev
, dread
);
892 #endif /* LOADER_MBR_SUPPORT */
893 #endif /* LOADER_MBR_SUPPORT || LOADER_GPT_SUPPORT */
900 ptable_close(struct ptable
*table
)
902 struct pentry
*entry
;
907 while (!STAILQ_EMPTY(&table
->entries
)) {
908 entry
= STAILQ_FIRST(&table
->entries
);
909 STAILQ_REMOVE_HEAD(&table
->entries
, entry
);
916 ptable_gettype(const struct ptable
*table
)
919 return (table
->type
);
923 ptable_getsize(const struct ptable
*table
, uint64_t *sizep
)
925 uint64_t tmp
= table
->sectors
* table
->sectorsize
;
927 if (tmp
< table
->sectors
)
936 ptable_getpart(const struct ptable
*table
, struct ptable_entry
*part
, int idx
)
938 struct pentry
*entry
;
940 if (part
== NULL
|| table
== NULL
)
943 STAILQ_FOREACH(entry
, &table
->entries
, entry
) {
944 if (entry
->part
.index
!= idx
)
946 memcpy(part
, &entry
->part
, sizeof (*part
));
953 * Search for a slice with the following preferences:
955 * 1: Active illumos slice
956 * 2: Non-active illumos slice
957 * 3: Active Linux slice
958 * 4: non-active Linux slice
959 * 5: Active FAT/FAT32 slice
960 * 6: non-active FAT/FAT32 slice
962 #define PREF_RAWDISK 0
963 #define PREF_ILLUMOS_ACT 1
964 #define PREF_ILLUMOS 2
965 #define PREF_LINUX_ACT 3
967 #define PREF_DOS_ACT 5
971 ptable_getbestpart(const struct ptable
*table
, struct ptable_entry
*part
)
973 struct pentry
*entry
, *best
;
976 if (part
== NULL
|| table
== NULL
)
980 preflevel
= pref
= PREF_NONE
;
981 STAILQ_FOREACH(entry
, &table
->entries
, entry
) {
982 #ifdef LOADER_MBR_SUPPORT
983 if (table
->type
== PTABLE_MBR
) {
984 switch (entry
->type
.mbr
) {
985 case DOSPTYP_SUNIXOS2
:
986 pref
= entry
->flags
& 0x80 ? PREF_ILLUMOS_ACT
:
990 pref
= entry
->flags
& 0x80 ? PREF_LINUX_ACT
:
993 case 0x01: /* DOS/Windows */
999 pref
= entry
->flags
& 0x80 ? PREF_DOS_ACT
:
1006 #endif /* LOADER_MBR_SUPPORT */
1007 #ifdef LOADER_GPT_SUPPORT
1008 if (table
->type
== PTABLE_GPT
) {
1009 if (entry
->part
.type
== PART_DOS
)
1011 else if (entry
->part
.type
== PART_ILLUMOS_ZFS
)
1012 pref
= PREF_ILLUMOS
;
1016 #endif /* LOADER_GPT_SUPPORT */
1017 if (pref
< preflevel
) {
1023 memcpy(part
, &best
->part
, sizeof (*part
));
1030 * iterate will stop if iterator will return non 0.
1033 ptable_iterate(const struct ptable
*table
, void *arg
, ptable_iterate_t
*iter
)
1035 struct pentry
*entry
;
1040 STAILQ_FOREACH(entry
, &table
->entries
, entry
) {
1041 #ifdef LOADER_MBR_SUPPORT
1042 if (table
->type
== PTABLE_MBR
)
1043 sprintf(name
, "s%d", entry
->part
.index
);
1046 #ifdef LOADER_GPT_SUPPORT
1047 if (table
->type
== PTABLE_GPT
)
1048 sprintf(name
, "p%d", entry
->part
.index
);
1051 #ifdef LOADER_VTOC8_SUPPORT
1052 if (table
->type
== PTABLE_VTOC8
)
1053 sprintf(name
, "%c", (uint8_t)'a' +
1057 if (table
->type
== PTABLE_VTOC
)
1058 sprintf(name
, "%c", (uint8_t)'a' +
1061 if (table
->type
== PTABLE_BSD
)
1062 sprintf(name
, "%c", (uint8_t)'a' +
1064 ret
= iter(arg
, name
, &entry
->part
);