Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/common/part.c
34677 views
1
/*-
2
* Copyright (c) 2012 Andrey V. Elsukov <[email protected]>
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 *
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
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
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 *
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
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 *
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
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 *
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
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 *
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 *
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 *
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
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
702
ptable_gettype(const struct ptable *table)
703
{
704
705
return (table->type);
706
}
707
708
int
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
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
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
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
844