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