:: limine / common / lib / part.s2.c 18.1 KB raw

1
#include <stddef.h>
2
#include <stdint.h>
3
#include <lib/part.h>
4
#include <drivers/disk.h>
5
#if defined (BIOS)
6
#  include <lib/real.h>
7
#endif
8
#include <lib/libc.h>
9
#include <lib/misc.h>
10
#include <lib/print.h>
11
#include <mm/pmm.h>
12
#include <fs/file.h>
13
14
enum {
15
    CACHE_NOT_READY = 0,
16
    CACHE_READY
17
};
18
19
static bool cache_block(struct volume *volume, uint64_t block) {
20
    if (volume->cache_status == CACHE_READY && block == volume->cached_block)
21
        return true;
22
23
    volume->cache_status = CACHE_NOT_READY;
24
25
    if (volume->cache == NULL)
26
        volume->cache =
27
            ext_mem_alloc(CHECKED_MUL((uint64_t)volume->fastest_xfer_size, (uint64_t)volume->sector_size,
28
                panic(false, "cache_block: block size overflow")));
29
30
    if (volume->first_sect % (volume->sector_size / 512)) {
31
        return false;
32
    }
33
34
    uint64_t first_sect = volume->first_sect / (volume->sector_size / 512);
35
36
    uint64_t xfer_size = volume->fastest_xfer_size;
37
38
    uint64_t block_offset = CHECKED_MUL(block, (uint64_t)volume->fastest_xfer_size, return false);
39
    uint64_t read_sector = CHECKED_ADD(first_sect, block_offset, return false);
40
41
    // Clamp xfer_size to remaining sectors in volume
42
    if (volume->sect_count != (uint64_t)-1) {
43
        uint64_t volume_sectors = volume->sect_count / (volume->sector_size / 512);
44
        uint64_t end_sector;
45
        if (__builtin_add_overflow(first_sect, volume_sectors, &end_sector)) {
46
            end_sector = UINT64_MAX;
47
        }
48
        if (read_sector >= end_sector) {
49
            return false;
50
        }
51
        uint64_t remaining = end_sector - read_sector;
52
        if (xfer_size > remaining) {
53
            xfer_size = remaining;
54
        }
55
    }
56
57
    int ret = disk_read_sectors(volume, volume->cache, read_sector, xfer_size);
58
    if (ret != DISK_SUCCESS) {
59
        return false;
60
    }
61
62
    volume->cache_status = CACHE_READY;
63
    volume->cached_block = block;
64
65
    return true;
66
}
67
68
bool volume_read(struct volume *volume, void *buffer, uint64_t loc, uint64_t count) {
69
    if (volume->pxe) {
70
        panic(false, "Attempted volume_read() on pxe");
71
    }
72
73
    if (volume->sect_count != (uint64_t)-1) {
74
        // sect_count is always in 512-byte sectors for both whole disks and partitions
75
        uint64_t part_size = CHECKED_MUL(volume->sect_count, 512, return false);
76
        if (loc >= part_size || count > part_size - loc) {
77
            return false;
78
        }
79
    }
80
81
    uint64_t block_size = volume->fastest_xfer_size * volume->sector_size;
82
83
    uint64_t progress = 0;
84
    while (progress < count) {
85
        uint64_t block = (loc + progress) / block_size;
86
87
        if (!cache_block(volume, block))
88
            return false;
89
90
        uint64_t chunk = count - progress;
91
        uint64_t offset = (loc + progress) % block_size;
92
        if (chunk > block_size - offset)
93
            chunk = block_size - offset;
94
95
        memcpy(buffer + progress, &volume->cache[offset], chunk);
96
        progress += chunk;
97
    }
98
99
    return true;
100
}
101
102
static bool partition_range_valid(struct volume *volume,
103
                                  uint64_t first_sect, uint64_t sect_count) {
104
    if (sect_count == 0) {
105
        return false;
106
    }
107
108
    uint64_t end_sect = CHECKED_ADD(first_sect, sect_count, return false);
109
110
    if (volume->sect_count != (uint64_t)-1 && end_sect > volume->sect_count) {
111
        return false;
112
    }
113
114
    return true;
115
}
116
117
struct gpt_table_header {
118
    // the head
119
    char     signature[8];
120
    uint32_t revision;
121
    uint32_t header_size;
122
    uint32_t crc32;
123
    uint32_t _reserved0;
124
125
    // the partitioning info
126
    uint64_t my_lba;
127
    uint64_t alternate_lba;
128
    uint64_t first_usable_lba;
129
    uint64_t last_usable_lba;
130
131
    // the guid
132
    struct guid disk_guid;
133
134
    // entries related
135
    uint64_t partition_entry_lba;
136
    uint32_t number_of_partition_entries;
137
    uint32_t size_of_partition_entry;
138
    uint32_t partition_entry_array_crc32;
139
} __attribute__((packed));
140
141
struct gpt_entry {
142
    struct guid partition_type_guid;
143
144
    struct guid unique_partition_guid;
145
146
    uint64_t starting_lba;
147
    uint64_t ending_lba;
148
149
    uint64_t attributes;
150
151
    uint16_t partition_name[36];
152
} __attribute__((packed));
153
154
bool gpt_get_guid(struct guid *guid, struct volume *volume) {
155
    struct gpt_table_header header = {0};
156
157
    int lb_guesses[] = {
158
        512,
159
        4096
160
    };
161
    int lb_size = -1;
162
163
    for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) {
164
        // read header, located after the first block
165
        if (!volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header)))
166
            continue;
167
168
        // check the header
169
        // 'EFI PART'
170
        if (strncmp(header.signature, "EFI PART", 8))
171
            continue;
172
173
        lb_size = lb_guesses[i];
174
        break;
175
    }
176
177
    if (lb_size == -1) {
178
        return false;
179
    }
180
181
    if (header.revision != 0x00010000)
182
        return false;
183
184
    *guid = header.disk_guid;
185
186
    return true;
187
}
188
189
static int gpt_get_part(struct volume *ret, struct volume *volume, int partition) {
190
    struct gpt_table_header header = {0};
191
192
    int lb_guesses[] = {
193
        512,
194
        4096
195
    };
196
    int lb_size = -1;
197
198
    for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) {
199
        // read header, located after the first block
200
        if (!volume_read(volume, &header, lb_guesses[i] * 1, sizeof(header)))
201
            continue;
202
203
        // check the header
204
        // 'EFI PART'
205
        if (strncmp(header.signature, "EFI PART", 8))
206
            continue;
207
208
        lb_size = lb_guesses[i];
209
        break;
210
    }
211
212
    if (lb_size == -1) {
213
        return INVALID_TABLE;
214
    }
215
216
    if (header.revision != 0x00010000)
217
        return INVALID_TABLE;
218
219
    // parse the entries if reached here
220
    if ((uint32_t)partition >= header.number_of_partition_entries)
221
        return END_OF_TABLE;
222
223
    // Validate partition entry size (must be at least as large as our struct)
224
    uint32_t entry_size = header.size_of_partition_entry;
225
    if (entry_size < sizeof(struct gpt_entry)) {
226
        return INVALID_TABLE;
227
    }
228
229
    uint64_t entry_offset = CHECKED_MUL(header.partition_entry_lba, lb_size, return INVALID_TABLE);
230
    // Use actual entry size from header for offset calculation
231
    uint64_t partition_offset = (uint64_t)partition * entry_size;
232
    entry_offset = CHECKED_ADD(entry_offset, partition_offset, return INVALID_TABLE);
233
234
    struct gpt_entry entry = {0};
235
    if (!volume_read(volume, &entry, entry_offset, sizeof(entry))) {
236
        return END_OF_TABLE;
237
    }
238
239
    struct guid empty_guid = {0};
240
    if (!memcmp(&entry.unique_partition_guid, &empty_guid, sizeof(struct guid)))
241
        return NO_PARTITION;
242
243
    // Validate that ending_lba >= starting_lba to prevent underflow
244
    if (entry.ending_lba < entry.starting_lba) {
245
        return NO_PARTITION;  // Invalid partition geometry
246
    }
247
248
    // Calculate sector multiplier for lb_size conversion
249
    uint64_t sect_multiplier = lb_size / 512;
250
251
    uint64_t first_sect_result = CHECKED_MUL(entry.starting_lba, sect_multiplier, return NO_PARTITION);
252
253
    // Check for overflow in sect_count calculation
254
    // First compute partition size in logical blocks
255
    // Check if +1 would overflow (ending_lba == UINT64_MAX)
256
    uint64_t partition_size = entry.ending_lba - entry.starting_lba;
257
    if (partition_size == UINT64_MAX) {
258
        return NO_PARTITION;  // Partition size +1 would overflow
259
    }
260
    uint64_t partition_blocks = partition_size + 1;
261
    uint64_t sect_count_result = CHECKED_MUL(partition_blocks, sect_multiplier, return NO_PARTITION);
262
263
    if (!partition_range_valid(volume, first_sect_result, sect_count_result)) {
264
        return NO_PARTITION;
265
    }
266
267
#if defined (UEFI)
268
    ret->efi_handle  = volume->efi_handle;
269
    ret->block_io    = volume->block_io;
270
#elif defined (BIOS)
271
    ret->drive       = volume->drive;
272
#endif
273
    ret->fastest_xfer_size = volume->fastest_xfer_size;
274
    ret->index       = volume->index;
275
    ret->is_optical  = volume->is_optical;
276
    ret->partition   = partition + 1;
277
    ret->sector_size = volume->sector_size;
278
    ret->first_sect  = first_sect_result;
279
    ret->sect_count  = sect_count_result;
280
    ret->backing_dev = volume;
281
282
    struct guid guid;
283
    if (!fs_get_guid(&guid, ret)) {
284
        ret->guid_valid = false;
285
    } else {
286
        ret->guid_valid = true;
287
        ret->guid = guid;
288
    }
289
290
    char *fslabel = fs_get_label(ret);
291
    if (fslabel == NULL) {
292
        ret->fslabel_valid = false;
293
    } else {
294
        ret->fslabel_valid = true;
295
        ret->fslabel = fslabel;
296
    }
297
298
    ret->part_guid_valid = true;
299
    ret->part_guid = entry.unique_partition_guid;
300
301
    return 0;
302
}
303
304
struct mbr_entry {
305
	uint8_t status;
306
	uint8_t chs_first_sect[3];
307
	uint8_t type;
308
	uint8_t chs_last_sect[3];
309
	uint32_t first_sect;
310
	uint32_t sect_count;
311
} __attribute__((packed));
312
313
bool is_valid_mbr(struct volume *volume) {
314
    // Check if actually valid mbr
315
    uint16_t hint = 0;
316
317
    if (!volume_read(volume, &hint, 446, sizeof(uint8_t)))
318
        return false;
319
    if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
320
        return false;
321
    if (!volume_read(volume, &hint, 462, sizeof(uint8_t)))
322
        return false;
323
    if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
324
        return false;
325
    if (!volume_read(volume, &hint, 478, sizeof(uint8_t)))
326
        return false;
327
    if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
328
        return false;
329
    if (!volume_read(volume, &hint, 494, sizeof(uint8_t)))
330
        return false;
331
    if ((uint8_t)hint != 0x00 && (uint8_t)hint != 0x80)
332
        return false;
333
334
    char hintc[64];
335
    if (!volume_read(volume, hintc, 3, 4))
336
        return false;
337
    if (memcmp(hintc, "NTFS", 4) == 0)
338
        return false;
339
    if (!volume_read(volume, hintc, 54, 3))
340
        return false;
341
    if (memcmp(hintc, "FAT", 3) == 0)
342
        return false;
343
    if (!volume_read(volume, hintc, 82, 3))
344
        return false;
345
    if (memcmp(hintc, "FAT", 3) == 0)
346
        return false;
347
    if (!volume_read(volume, hintc, 3, 5))
348
        return false;
349
    if (memcmp(hintc, "FAT32", 5) == 0)
350
        return false;
351
    if (!volume_read(volume, &hint, 1080, sizeof(uint16_t)))
352
        return false;
353
    if (hint == 0xef53)
354
        return false;
355
356
    return true;
357
}
358
359
uint32_t mbr_get_id(struct volume *volume) {
360
    if (!is_valid_mbr(volume)) {
361
        return 0;
362
    }
363
364
    uint32_t ret;
365
    if (!volume_read(volume, &ret, 0x1b8, sizeof(uint32_t))) {
366
        return 0;
367
    }
368
369
    return ret;
370
}
371
372
// Maximum number of logical partitions to prevent infinite loops from circular EBR chains
373
#define MAX_LOGICAL_PARTITIONS 256
374
375
static int mbr_get_logical_part(struct volume *ret, struct volume *extended_part,
376
                                int partition) {
377
    struct mbr_entry entry;
378
379
    // Limit partition index to prevent excessive iteration
380
    if (partition >= MAX_LOGICAL_PARTITIONS) {
381
        return END_OF_TABLE;
382
    }
383
384
    uint64_t ebr_sector = 0;
385
    uint64_t prev_ebr_sector = 0;
386
387
    for (int i = 0; i < partition; i++) {
388
        uint64_t entry_offset = ebr_sector * 512 + 0x1ce;
389
390
        if (!volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry))) {
391
            return END_OF_TABLE;
392
        }
393
394
        if (entry.type != 0x0f && entry.type != 0x05) {
395
            return END_OF_TABLE;
396
        }
397
398
        prev_ebr_sector = ebr_sector;
399
        ebr_sector = entry.first_sect;
400
401
        // Detect circular chain: if new sector points to 0 or backwards, it's invalid
402
        // (EBR sectors should always increase within the extended partition)
403
        if (ebr_sector == 0 || (i > 0 && ebr_sector <= prev_ebr_sector)) {
404
            return END_OF_TABLE;  // Circular or corrupted EBR chain
405
        }
406
407
        // Also check that ebr_sector is within the extended partition bounds
408
        if (ebr_sector >= extended_part->sect_count) {
409
            return END_OF_TABLE;  // EBR points outside extended partition
410
        }
411
    }
412
413
    uint64_t entry_offset = ebr_sector * 512 + 0x1be;
414
415
    if (!volume_read(extended_part, &entry, entry_offset, sizeof(struct mbr_entry))) {
416
        return END_OF_TABLE;
417
    }
418
419
    if (entry.type == 0)
420
        return NO_PARTITION;
421
422
    // Validate sect_count is non-zero
423
    if (entry.sect_count == 0) {
424
        return NO_PARTITION;
425
    }
426
427
    uint64_t logical_rel_first = CHECKED_ADD(ebr_sector, entry.first_sect, return NO_PARTITION);
428
    if (!partition_range_valid(extended_part, logical_rel_first, entry.sect_count)) {
429
        return NO_PARTITION;
430
    }
431
432
    uint64_t first_sect_64 = CHECKED_ADD(extended_part->first_sect, logical_rel_first, return NO_PARTITION);
433
    if (!partition_range_valid(extended_part->backing_dev, first_sect_64, entry.sect_count)) {
434
        return NO_PARTITION;
435
    }
436
437
#if defined (UEFI)
438
    ret->efi_handle  = extended_part->efi_handle;
439
    ret->block_io    = extended_part->block_io;
440
#elif defined (BIOS)
441
    ret->drive       = extended_part->drive;
442
#endif
443
    ret->fastest_xfer_size = extended_part->fastest_xfer_size;
444
    ret->index       = extended_part->index;
445
    ret->is_optical  = extended_part->is_optical;
446
    ret->partition   = partition + 4 + 1;
447
    ret->sector_size = extended_part->sector_size;
448
    ret->first_sect  = first_sect_64;
449
    ret->sect_count  = entry.sect_count;
450
    ret->backing_dev = extended_part->backing_dev;
451
452
    struct guid guid;
453
    if (!fs_get_guid(&guid, ret)) {
454
        ret->guid_valid = false;
455
    } else {
456
        ret->guid_valid = true;
457
        ret->guid = guid;
458
    }
459
460
    char *fslabel = fs_get_label(ret);
461
    if (fslabel == NULL) {
462
        ret->fslabel_valid = false;
463
    } else {
464
        ret->fslabel_valid = true;
465
        ret->fslabel = fslabel;
466
    }
467
468
    ret->part_guid_valid = false;
469
470
    return 0;
471
}
472
473
static int mbr_get_part(struct volume *ret, struct volume *volume, int partition) {
474
    if (!is_valid_mbr(volume)) {
475
        return INVALID_TABLE;
476
    }
477
478
    struct mbr_entry entry;
479
480
    if (partition > 3) {
481
        for (int i = 0; i < 4; i++) {
482
            uint64_t entry_offset = 0x1be + sizeof(struct mbr_entry) * i;
483
484
            if (!volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry))) {
485
                continue;
486
            }
487
488
            if (entry.type != 0x0f && entry.type != 0x05)
489
                continue;
490
491
            // Validate extended partition has non-zero size
492
            if (entry.sect_count == 0) {
493
                continue;
494
            }
495
496
            if (!partition_range_valid(volume, entry.first_sect, entry.sect_count)) {
497
                continue;
498
            }
499
500
            struct volume extended_part = {0};
501
502
#if defined (UEFI)
503
            extended_part.efi_handle  = volume->efi_handle;
504
            extended_part.block_io    = volume->block_io;
505
#elif defined (BIOS)
506
            extended_part.drive       = volume->drive;
507
#endif
508
            extended_part.fastest_xfer_size = volume->fastest_xfer_size;
509
            extended_part.index       = volume->index;
510
            extended_part.is_optical  = volume->is_optical;
511
            extended_part.partition   = i + 1;
512
            extended_part.sector_size = volume->sector_size;
513
            extended_part.first_sect  = entry.first_sect;
514
            extended_part.sect_count  = entry.sect_count;
515
            extended_part.backing_dev = volume;
516
517
            return mbr_get_logical_part(ret, &extended_part, partition - 4);
518
        }
519
520
        return END_OF_TABLE;
521
    }
522
523
    uint64_t entry_offset = 0x1be + sizeof(struct mbr_entry) * partition;
524
525
    if (!volume_read(volume, &entry, entry_offset, sizeof(struct mbr_entry))) {
526
        return END_OF_TABLE;
527
    }
528
529
    if (entry.type == 0)
530
        return NO_PARTITION;
531
532
    // Validate sect_count is non-zero
533
    if (entry.sect_count == 0) {
534
        return NO_PARTITION;
535
    }
536
537
    if (!partition_range_valid(volume, entry.first_sect, entry.sect_count)) {
538
        return NO_PARTITION;
539
    }
540
541
#if defined (UEFI)
542
    ret->efi_handle  = volume->efi_handle;
543
    ret->block_io    = volume->block_io;
544
#elif defined (BIOS)
545
    ret->drive       = volume->drive;
546
#endif
547
    ret->fastest_xfer_size = volume->fastest_xfer_size;
548
    ret->index       = volume->index;
549
    ret->is_optical  = volume->is_optical;
550
    ret->partition   = partition + 1;
551
    ret->sector_size = volume->sector_size;
552
    ret->first_sect  = entry.first_sect;
553
    ret->sect_count  = entry.sect_count;
554
    ret->backing_dev = volume;
555
556
    struct guid guid;
557
    if (!fs_get_guid(&guid, ret)) {
558
        ret->guid_valid = false;
559
    } else {
560
        ret->guid_valid = true;
561
        ret->guid = guid;
562
    }
563
564
    char *fslabel = fs_get_label(ret);
565
    if (fslabel == NULL) {
566
        ret->fslabel_valid = false;
567
    } else {
568
        ret->fslabel_valid = true;
569
        ret->fslabel = fslabel;
570
    }
571
572
    ret->part_guid_valid = false;
573
574
    return 0;
575
}
576
577
int part_get(struct volume *part, struct volume *volume, int partition) {
578
    int ret;
579
580
    // Validate partition index is non-negative
581
    if (partition < 0) {
582
        return NO_PARTITION;
583
    }
584
585
    ret = gpt_get_part(part, volume, partition);
586
    if (ret != INVALID_TABLE)
587
        return ret;
588
589
    ret = mbr_get_part(part, volume, partition);
590
    if (ret != INVALID_TABLE)
591
        return ret;
592
593
    return INVALID_TABLE;
594
}
595
596
struct volume **volume_index = NULL;
597
size_t volume_index_i = 0;
598
599
struct volume *volume_get_by_guid(struct guid *guid) {
600
    for (size_t i = 0; i < volume_index_i; i++) {
601
        if (volume_index[i]->guid_valid
602
         && memcmp(&volume_index[i]->guid, guid, 16) == 0) {
603
            return volume_index[i];
604
        }
605
        if (volume_index[i]->part_guid_valid
606
         && memcmp(&volume_index[i]->part_guid, guid, 16) == 0) {
607
            return volume_index[i];
608
        }
609
    }
610
611
    return NULL;
612
}
613
614
struct volume *volume_get_by_fslabel(char *fslabel) {
615
    for (size_t i = 0; i < volume_index_i; i++) {
616
        if (volume_index[i]->fslabel_valid
617
         && strcmp(volume_index[i]->fslabel, fslabel) == 0) {
618
            return volume_index[i];
619
        }
620
    }
621
622
    return NULL;
623
}
624
625
struct volume *volume_get_by_coord(bool optical, int drive, int partition) {
626
    for (size_t i = 0; i < volume_index_i; i++) {
627
        if (volume_index[i]->index == drive
628
         && volume_index[i]->is_optical == optical
629
         && volume_index[i]->partition == partition) {
630
            return volume_index[i];
631
        }
632
    }
633
634
    return NULL;
635
}
636
637
#if defined (BIOS)
638
struct volume *volume_get_by_bios_drive(int drive) {
639
    for (size_t i = 0; i < volume_index_i; i++) {
640
        if (volume_index[i]->drive == drive) {
641
            return volume_index[i];
642
        }
643
    }
644
645
    return NULL;
646
}
647
#endif
tab: 248 wrap: offon