:: limine / host / limine.c 43.8 KB raw

1
#undef IS_WINDOWS
2
#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32)) && !defined(__CYGWIN__)
3
#define IS_WINDOWS 1
4
#endif
5
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <stdint.h>
9
#include <stddef.h>
10
#include <stdbool.h>
11
#include <stdarg.h>
12
#include <string.h>
13
#include <errno.h>
14
#include <inttypes.h>
15
#include <limits.h>
16
#include <time.h>
17
18
#ifndef LIMINE_NO_BIOS
19
#include "limine-bios-hdd.h"
20
#endif
21
22
static char *program_name = NULL;
23
24
static void perror_wrap(const char *fmt, ...) {
25
    int old_errno = errno;
26
27
    fprintf(stderr, "%s: ", program_name);
28
29
    va_list args;
30
    va_start(args, fmt);
31
32
    vfprintf(stderr, fmt, args);
33
34
    va_end(args);
35
36
    fprintf(stderr, ": %s\n", strerror(old_errno));
37
}
38
39
static void remove_arg(int *argc, char *argv[], int index) {
40
    for (int i = index; i < *argc - 1; i++) {
41
        argv[i] = argv[i + 1];
42
    }
43
44
    (*argc)--;
45
46
    argv[*argc] = NULL;
47
}
48
49
static inline bool mul_u64_overflow(uint64_t a, uint64_t b, uint64_t *res) {
50
    *res = a * b;
51
    return a != 0 && b > UINT64_MAX / a;
52
}
53
54
static inline bool add_u64_overflow(uint64_t a, uint64_t b, uint64_t *res) {
55
    *res = a + b;
56
    return a > UINT64_MAX - b;
57
}
58
59
#ifndef LIMINE_NO_BIOS
60
61
static bool quiet = false;
62
63
static int set_pos(FILE *stream, uint64_t pos) {
64
    if (sizeof(long) >= 8) {
65
        return fseek(stream, (long)pos, SEEK_SET);
66
    }
67
68
    long jump_size = (LONG_MAX / 2) + 1;
69
    long last_jump = pos % jump_size;
70
    uint64_t jumps = pos / jump_size;
71
72
    rewind(stream);
73
74
    for (uint64_t i = 0; i < jumps; i++) {
75
        if (fseek(stream, jump_size, SEEK_CUR) != 0) {
76
            return -1;
77
        }
78
    }
79
    if (fseek(stream, last_jump, SEEK_CUR) != 0) {
80
        return -1;
81
    }
82
83
    return 0;
84
}
85
86
#define SIZEOF_ARRAY(array) (sizeof(array) / sizeof(array[0]))
87
#define DIV_ROUNDUP(a, b) (((a) + ((b) - 1)) / (b))
88
89
struct gpt_table_header {
90
    // the head
91
    char     signature[8];
92
    uint32_t revision;
93
    uint32_t header_size;
94
    uint32_t crc32;
95
    uint32_t _reserved0;
96
97
    // the partitioning info
98
    uint64_t my_lba;
99
    uint64_t alternate_lba;
100
    uint64_t first_usable_lba;
101
    uint64_t last_usable_lba;
102
103
    // the guid
104
    uint64_t disk_guid[2];
105
106
    // entries related
107
    uint64_t partition_entry_lba;
108
    uint32_t number_of_partition_entries;
109
    uint32_t size_of_partition_entry;
110
    uint32_t partition_entry_array_crc32;
111
};
112
113
struct gpt_entry {
114
    uint64_t partition_type_guid[2];
115
116
    uint64_t unique_partition_guid[2];
117
118
    uint64_t starting_lba;
119
    uint64_t ending_lba;
120
121
    uint64_t attributes;
122
123
    uint16_t partition_name[36];
124
};
125
126
struct gpt2mbr_type_conv {
127
    uint64_t gpt_type1;
128
    uint64_t gpt_type2;
129
    uint8_t mbr_type;
130
};
131
132
// This table is very incomplete, but it should be enough for covering
133
// all that matters for ISOHYBRIDs.
134
// Of course, though, expansion is welcome.
135
static struct gpt2mbr_type_conv gpt2mbr_type_conv_table[] = {
136
    { 0x11d2f81fc12a7328, 0x3bc93ec9a0004bba, 0xef }, // EFI system partition
137
    { 0x4433b9e5ebd0a0a2, 0xc79926b7b668c087, 0x07 }, // Microsoft basic data
138
    { 0x11aa000048465300, 0xacec4365300011aa, 0xaf }, // HFS/HFS+
139
};
140
141
static int gpt2mbr_type(uint64_t gpt_type1, uint64_t gpt_type2) {
142
    for (size_t i = 0; i < SIZEOF_ARRAY(gpt2mbr_type_conv_table); i++) {
143
        if (gpt2mbr_type_conv_table[i].gpt_type1 == gpt_type1
144
         && gpt2mbr_type_conv_table[i].gpt_type2 == gpt_type2) {
145
            return gpt2mbr_type_conv_table[i].mbr_type;
146
        }
147
    }
148
    return -1;
149
}
150
151
static void lba2chs(uint8_t *chs, uint64_t lba) {
152
    // If LBA is too big to express, use a standard value for CHS.
153
    if (lba > 63 * 255 * 1024) {
154
        goto lba_too_big;
155
    }
156
157
    uint64_t cylinder = lba / (255 * 63);
158
    if (cylinder >= 1024) {
159
lba_too_big:
160
        chs[0] = 0xfe;
161
        chs[1] = 0xff;
162
        chs[2] = 0xff;
163
        return;
164
    }
165
    uint64_t head = (lba / 63) % 255;
166
    uint64_t sector = (lba % 63) + 1;
167
168
    chs[0] = head;
169
    chs[1] = (cylinder >> 2) & 0xc0; // high 2 bits
170
    chs[1] |= sector & 0x3f;
171
    chs[2] = cylinder; // low 8 bits
172
}
173
174
static uint16_t endswap16(uint16_t value) {
175
    uint16_t ret = 0;
176
    ret |= (value >> 8) & 0x00ff;
177
    ret |= (value << 8) & 0xff00;
178
    return ret;
179
}
180
181
static uint32_t endswap32(uint32_t value) {
182
    uint32_t ret = 0;
183
    ret |= (value >> 24) & 0x000000ff;
184
    ret |= (value >> 8)  & 0x0000ff00;
185
    ret |= (value << 8)  & 0x00ff0000;
186
    ret |= (value << 24) & 0xff000000;
187
    return ret;
188
}
189
190
static uint64_t endswap64(uint64_t value) {
191
    uint64_t ret = 0;
192
    ret |= (value >> 56) & 0x00000000000000ff;
193
    ret |= (value >> 40) & 0x000000000000ff00;
194
    ret |= (value >> 24) & 0x0000000000ff0000;
195
    ret |= (value >> 8)  & 0x00000000ff000000;
196
    ret |= (value << 8)  & 0x000000ff00000000;
197
    ret |= (value << 24) & 0x0000ff0000000000;
198
    ret |= (value << 40) & 0x00ff000000000000;
199
    ret |= (value << 56) & 0xff00000000000000;
200
    return ret;
201
}
202
203
#ifdef __BYTE_ORDER__
204
205
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
206
#define bigendian true
207
#else
208
#define bigendian false
209
#endif
210
211
#else /* !__BYTE_ORDER__ */
212
213
static bool bigendian = false;
214
215
#endif /* !__BYTE_ORDER__ */
216
217
#define ENDSWAP(VALUE) (bigendian ? (                    \
218
    sizeof(VALUE) == 1 ? (VALUE)          :              \
219
    sizeof(VALUE) == 2 ? endswap16(VALUE) :              \
220
    sizeof(VALUE) == 4 ? endswap32(VALUE) :              \
221
    sizeof(VALUE) == 8 ? endswap64(VALUE) : (abort(), 1) \
222
) : (VALUE))
223
224
static enum {
225
    CACHE_CLEAN,
226
    CACHE_DIRTY
227
} cache_state;
228
static uint64_t cached_block;
229
static uint8_t *cache  = NULL;
230
static FILE    *device = NULL;
231
static size_t   block_size;
232
233
static bool device_init(void) {
234
    size_t guesses[] = { 512, 2048, 4096 };
235
236
    for (size_t i = 0; i < SIZEOF_ARRAY(guesses); i++) {
237
        void *tmp = realloc(cache, guesses[i]);
238
        if (tmp == NULL) {
239
            perror_wrap("error: device_init(): realloc()");
240
            return false;
241
        }
242
        cache = tmp;
243
244
        rewind(device);
245
246
        size_t ret = fread(cache, guesses[i], 1, device);
247
        if (ret != 1) {
248
            continue;
249
        }
250
251
        block_size = guesses[i];
252
253
        if (!quiet) {
254
            fprintf(stderr, "Physical block size of %zu bytes.\n", block_size);
255
        }
256
257
        cache_state  = CACHE_CLEAN;
258
        cached_block = 0;
259
        return true;
260
    }
261
262
    fprintf(stderr, "error: device_init(): Couldn't determine block size of device.\n");
263
    return false;
264
}
265
266
static bool device_flush_cache(void) {
267
    if (cache_state == CACHE_CLEAN)
268
        return true;
269
270
    if (set_pos(device, cached_block * block_size) != 0) {
271
        perror_wrap("error: device_flush_cache(): set_pos()");
272
        return false;
273
    }
274
275
    size_t ret = fwrite(cache, block_size, 1, device);
276
    if (ret != 1) {
277
        if (ferror(device)) {
278
            perror_wrap("error: device_flush_cache(): fwrite()");
279
        }
280
        return false;
281
    }
282
283
    cache_state = CACHE_CLEAN;
284
    return true;
285
}
286
287
static bool device_cache_block(uint64_t block) {
288
    if (cached_block == block)
289
        return true;
290
291
    if (cache_state == CACHE_DIRTY) {
292
        if (!device_flush_cache())
293
            return false;
294
    }
295
296
    if (set_pos(device, block * block_size) != 0) {
297
        perror_wrap("error: device_cache_block(): set_pos()");
298
        return false;
299
    }
300
301
    size_t ret = fread(cache, block_size, 1, device);
302
    if (ret != 1) {
303
        if (ferror(device)) {
304
            perror_wrap("error: device_cache_block(): fread()");
305
        }
306
        return false;
307
    }
308
309
    cached_block = block;
310
311
    return true;
312
}
313
314
struct uninstall_data {
315
    void *data;
316
    uint64_t loc;
317
    uint64_t count;
318
};
319
320
#define UNINSTALL_DATA_MAX 256
321
322
static bool uninstalling = false;
323
static struct uninstall_data uninstall_data[UNINSTALL_DATA_MAX];
324
static struct uninstall_data uninstall_data_rev[UNINSTALL_DATA_MAX];
325
static uint64_t uninstall_data_i = 0;
326
static const char *uninstall_file = NULL;
327
328
static void reverse_uninstall_data(void) {
329
    for (size_t i = 0, j = uninstall_data_i - 1; i < uninstall_data_i; i++, j--) {
330
        uninstall_data_rev[j] = uninstall_data[i];
331
    }
332
333
    memcpy(uninstall_data, uninstall_data_rev, uninstall_data_i * sizeof(struct uninstall_data));
334
}
335
336
static void free_uninstall_data(void) {
337
    for (size_t i = 0; i < uninstall_data_i; i++) {
338
        free(uninstall_data[i].data);
339
    }
340
}
341
342
static bool store_uninstall_data(const char *filename) {
343
    if (!quiet) {
344
        fprintf(stderr, "Storing uninstall data to file: `%s`...\n", filename);
345
    }
346
347
    FILE *udfile = fopen(filename, "wb");
348
    if (udfile == NULL) {
349
        perror_wrap("error: `%s`", filename);
350
        goto error;
351
    }
352
353
    if (fwrite(&uninstall_data_i, sizeof(uint64_t), 1, udfile) != 1) {
354
        goto fwrite_error;
355
    }
356
357
    for (size_t i = 0; i < uninstall_data_i; i++) {
358
        if (fwrite(&uninstall_data[i].loc, sizeof(uint64_t), 1, udfile) != 1) {
359
            goto fwrite_error;
360
        }
361
        if (fwrite(&uninstall_data[i].count, sizeof(uint64_t), 1, udfile) != 1) {
362
            goto fwrite_error;
363
        }
364
        if (fwrite(uninstall_data[i].data, uninstall_data[i].count, 1, udfile) != 1) {
365
            goto fwrite_error;
366
        }
367
    }
368
369
    fclose(udfile);
370
    return true;
371
372
fwrite_error:
373
    perror_wrap("error: store_uninstall_data(): fwrite()");
374
375
error:
376
    if (udfile != NULL) {
377
        fclose(udfile);
378
    }
379
    return false;
380
}
381
382
static bool load_uninstall_data(const char *filename) {
383
    size_t loaded_count = 0;
384
385
    if (!quiet) {
386
        fprintf(stderr, "Loading uninstall data from file: `%s`...\n", filename);
387
    }
388
389
    FILE *udfile = fopen(filename, "rb");
390
    if (udfile == NULL) {
391
        perror_wrap("error: `%s`", filename);
392
        goto error;
393
    }
394
395
    if (fread(&uninstall_data_i, sizeof(uint64_t), 1, udfile) != 1) {
396
        goto fread_error;
397
    }
398
399
    if (uninstall_data_i > UNINSTALL_DATA_MAX) {
400
        fprintf(stderr, "error: load_uninstall_data(): too many entries (%zu > %d)\n",
401
                (size_t)uninstall_data_i, UNINSTALL_DATA_MAX);
402
        goto error;
403
    }
404
405
    for (size_t i = 0; i < uninstall_data_i; i++) {
406
        if (fread(&uninstall_data[i].loc, sizeof(uint64_t), 1, udfile) != 1) {
407
            goto fread_error;
408
        }
409
        if (fread(&uninstall_data[i].count, sizeof(uint64_t), 1, udfile) != 1) {
410
            goto fread_error;
411
        }
412
        if (uninstall_data[i].count > SIZE_MAX) {
413
            fprintf(stderr, "error: load_uninstall_data(): entry size too large\n");
414
            goto error;
415
        }
416
        uninstall_data[i].data = malloc((size_t)uninstall_data[i].count);
417
        if (uninstall_data[i].data == NULL) {
418
            perror_wrap("error: load_uninstall_data(): malloc()");
419
            goto error;
420
        }
421
        if (fread(uninstall_data[i].data, uninstall_data[i].count, 1, udfile) != 1) {
422
            free(uninstall_data[i].data);
423
            goto fread_error;
424
        }
425
        loaded_count++;
426
    }
427
428
    fclose(udfile);
429
    return true;
430
431
fread_error:
432
    perror_wrap("error: load_uninstall_data(): fread()");
433
434
error:
435
    // Free any previously allocated uninstall data
436
    for (size_t j = 0; j < loaded_count; j++) {
437
        free(uninstall_data[j].data);
438
    }
439
    if (udfile != NULL) {
440
        fclose(udfile);
441
    }
442
    return false;
443
}
444
445
static bool _device_read(void *_buffer, uint64_t loc, size_t count) {
446
    uint8_t *buffer = _buffer;
447
    uint64_t progress = 0;
448
    while (progress < count) {
449
        uint64_t block = (loc + progress) / block_size;
450
451
        if (!device_cache_block(block)) {
452
            return false;
453
        }
454
455
        uint64_t chunk = count - progress;
456
        uint64_t offset = (loc + progress) % block_size;
457
        if (chunk > block_size - offset)
458
            chunk = block_size - offset;
459
460
        memcpy(buffer + progress, &cache[offset], chunk);
461
        progress += chunk;
462
    }
463
464
    return true;
465
}
466
467
static bool _device_write(const void *_buffer, uint64_t loc, size_t count) {
468
    struct uninstall_data *ud = NULL;
469
470
    if (uninstalling) {
471
        goto skip_save;
472
    }
473
474
    if (uninstall_data_i >= UNINSTALL_DATA_MAX) {
475
        fprintf(stderr, "error: Too many uninstall data entries! Please report this bug upstream.\n");
476
        return false;
477
    }
478
479
    ud = &uninstall_data[uninstall_data_i];
480
481
    ud->data = malloc(count);
482
    if (ud->data == NULL) {
483
        perror_wrap("error: _device_write(): malloc()");
484
        return false;
485
    }
486
487
    if (!_device_read(ud->data, loc, count)) {
488
        free(ud->data);
489
        return false;
490
    }
491
492
    ud->loc = loc;
493
    ud->count = count;
494
495
skip_save:;
496
    const uint8_t *buffer = _buffer;
497
    uint64_t progress = 0;
498
    while (progress < count) {
499
        uint64_t block = (loc + progress) / block_size;
500
501
        if (!device_cache_block(block)) {
502
            if (!uninstalling) {
503
                free(ud->data);
504
            }
505
            return false;
506
        }
507
508
        uint64_t chunk = count - progress;
509
        uint64_t offset = (loc + progress) % block_size;
510
        if (chunk > block_size - offset)
511
            chunk = block_size - offset;
512
513
        memcpy(&cache[offset], buffer + progress, chunk);
514
        cache_state = CACHE_DIRTY;
515
        progress += chunk;
516
    }
517
518
    if (!uninstalling) {
519
        uninstall_data_i++;
520
    }
521
    return true;
522
}
523
524
static bool uninstall(bool quiet_arg) {
525
    bool print_cache_flush_fail = false;
526
    bool print_write_fail = false;
527
    bool ret = true;
528
529
    uninstalling = true;
530
531
    cache_state = CACHE_CLEAN;
532
    cached_block = (uint64_t)-1;
533
534
    for (size_t i = 0; i < uninstall_data_i; i++) {
535
        struct uninstall_data *ud = &uninstall_data[i];
536
        bool retry = false;
537
        while (!_device_write(ud->data, ud->loc, ud->count)) {
538
            if (retry) {
539
                fprintf(stderr, "warning: Retry failed.\n");
540
                print_write_fail = true;
541
                break;
542
            }
543
            if (!quiet) {
544
                fprintf(stderr, "warning: Uninstall data index %zu failed to write, retrying...\n", i);
545
            }
546
            if (!device_flush_cache()) {
547
                print_cache_flush_fail = true;
548
            }
549
            cache_state = CACHE_CLEAN;
550
            cached_block = (uint64_t)-1;
551
            retry = true;
552
        }
553
    }
554
555
    if (!device_flush_cache()) {
556
        print_cache_flush_fail = true;
557
    }
558
559
    if (print_write_fail) {
560
        fprintf(stderr, "error: Some data failed to be uninstalled correctly.\n");
561
        ret = false;
562
    }
563
564
    if (print_cache_flush_fail) {
565
        fprintf(stderr, "error: Device cache flush failure. Uninstall may be incomplete.\n");
566
        ret = false;
567
    }
568
569
    if (ret == true && !quiet && !quiet_arg) {
570
        fprintf(stderr, "Uninstall data restored successfully.\n");
571
    }
572
573
    return ret;
574
}
575
576
#define device_read(BUFFER, LOC, COUNT)        \
577
    do {                                       \
578
        if (!_device_read(BUFFER, LOC, COUNT)) \
579
            goto cleanup;                      \
580
    } while (0)
581
582
#define device_write(BUFFER, LOC, COUNT)        \
583
    do {                                        \
584
        if (!_device_write(BUFFER, LOC, COUNT)) \
585
            goto cleanup;                       \
586
    } while (0)
587
588
static void bios_install_usage(void) {
589
    printf("usage: %s bios-install <device> [GPT partition index]\n", program_name);
590
    printf("\n");
591
    printf("    --force         Force installation even if the safety checks fail\n");
592
    printf("                    (DANGEROUS!)\n");
593
    printf("\n");
594
    printf("    --uninstall     Reverse the entire install procedure\n");
595
    printf("\n");
596
    printf("    --uninstall-data-file=<filename>\n");
597
    printf("                    Set the input (for --uninstall) or output file\n");
598
    printf("                    name of the file which contains uninstall data\n");
599
    printf("\n");
600
    printf("    --no-gpt-to-mbr-isohybrid-conversion\n");
601
    printf("                    Do not automatically convert a GUID partition table (GPT)\n");
602
    printf("                    found on an ISOHYBRID image into an MBR partition table\n");
603
    printf("                    (which is done for better hardware compatibility)\n");
604
    printf("\n");
605
    printf("    --quiet         Do not print verbose diagnostic messages\n");
606
    printf("\n");
607
    printf("    --help | -h     Display this help message\n");
608
    printf("\n");
609
}
610
611
static bool validate_or_force(uint64_t offset, bool force, bool *err) {
612
    *err = false;
613
614
    char hintc[64];
615
    device_read(hintc, offset + 3, 4);
616
    if (memcmp(hintc, "NTFS", 4) == 0) {
617
        if (!force) {
618
            return false;
619
        } else {
620
            memset(hintc, 0, 4);
621
            device_write(hintc, offset + 3, 4);
622
        }
623
    }
624
    device_read(hintc, offset + 54, 3);
625
    if (memcmp(hintc, "FAT", 3) == 0) {
626
        if (!force) {
627
            return false;
628
        } else {
629
            memset(hintc, 0, 5);
630
            device_write(hintc, offset + 54, 5);
631
        }
632
    }
633
    device_read(hintc, offset + 82, 3);
634
    if (memcmp(hintc, "FAT", 3) == 0) {
635
        if (!force) {
636
            return false;
637
        } else {
638
            memset(hintc, 0, 5);
639
            device_write(hintc, offset + 82, 5);
640
        }
641
    }
642
    device_read(hintc, offset + 3, 5);
643
    if (memcmp(hintc, "FAT32", 5) == 0) {
644
        if (!force) {
645
            return false;
646
        } else {
647
            memset(hintc, 0, 5);
648
            device_write(hintc, offset + 3, 5);
649
        }
650
    }
651
    uint16_t hint16 = 0;
652
    device_read(&hint16, offset + 1080, sizeof(uint16_t));
653
    hint16 = ENDSWAP(hint16);
654
    if (hint16 == 0xef53) {
655
        if (!force) {
656
            return false;
657
        } else {
658
            hint16 = 0;
659
            hint16 = ENDSWAP(hint16);
660
            device_write(&hint16, offset + 1080, sizeof(uint16_t));
661
        }
662
    }
663
664
    return true;
665
666
cleanup:
667
    *err = true;
668
    return false;
669
}
670
671
static int bios_install(int argc, char *argv[]) {
672
    int ok = EXIT_FAILURE;
673
    bool force = false;
674
    bool gpt2mbr_allowed = true;
675
    bool uninstall_mode = false;
676
    const uint8_t *bootloader_img = binary_limine_hdd_bin_data;
677
    size_t   bootloader_file_size = sizeof(binary_limine_hdd_bin_data);
678
    uint8_t  orig_mbr[70], timestamp[6];
679
    void *empty_lba = NULL;
680
    const char *part_ndx = NULL;
681
682
#ifndef __BYTE_ORDER__
683
    uint32_t endcheck = 0x12345678;
684
    uint8_t endbyte = *((uint8_t *)&endcheck);
685
    bigendian = endbyte == 0x12;
686
#endif
687
688
    if (argc < 2) {
689
        bios_install_usage();
690
#ifdef IS_WINDOWS
691
        system("pause");
692
#endif
693
        return EXIT_FAILURE;
694
    }
695
696
    for (int i = 1; i < argc; i++) {
697
        if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
698
            bios_install_usage();
699
            return EXIT_SUCCESS;
700
        } else if (strcmp(argv[i], "--quiet") == 0) {
701
            quiet = true;
702
        } else if (strcmp(argv[i], "--force") == 0) {
703
            if (force && !quiet) {
704
                fprintf(stderr, "warning: --force already set.\n");
705
            }
706
            force = true;
707
        } else if (strcmp(argv[i], "--no-gpt-to-mbr-isohybrid-conversion") == 0) {
708
            gpt2mbr_allowed = false;
709
        } else if (strcmp(argv[i], "--uninstall") == 0) {
710
            if (uninstall_mode && !quiet) {
711
                fprintf(stderr, "warning: --uninstall already set.\n");
712
            }
713
            uninstall_mode = true;
714
        } else if (memcmp(argv[i], "--uninstall-data-file=", 22) == 0) {
715
            if (uninstall_file != NULL && !quiet) {
716
                fprintf(stderr, "warning: --uninstall-data-file already set. Overriding...\n");
717
            }
718
            uninstall_file = argv[i] + 22;
719
            if (strlen(uninstall_file) == 0) {
720
                fprintf(stderr, "error: Uninstall data file has a zero-length name!\n");
721
                return EXIT_FAILURE;
722
            }
723
        } else {
724
            if (device != NULL) { // [GPT partition index]
725
                part_ndx = argv[i]; // TODO: Make this non-positional?
726
            } else if ((device = fopen(argv[i], "r+b")) == NULL) { // <device>
727
                perror_wrap("error: `%s`", argv[i]);
728
                return EXIT_FAILURE;
729
            }
730
        }
731
    }
732
733
    if (device == NULL) {
734
        fprintf(stderr, "error: No device specified\n");
735
        bios_install_usage();
736
        return EXIT_FAILURE;
737
    }
738
739
    if (!device_init()) {
740
        goto uninstall_mode_cleanup;
741
    }
742
743
    if (uninstall_mode) {
744
        if (uninstall_file == NULL) {
745
            fprintf(stderr, "error: Uninstall mode set but no --uninstall-data-file=... passed.\n");
746
            goto uninstall_mode_cleanup;
747
        }
748
749
        if (!load_uninstall_data(uninstall_file)) {
750
            goto uninstall_mode_cleanup;
751
        }
752
753
        if (uninstall(false) == false) {
754
            ok = EXIT_FAILURE;
755
        } else {
756
            ok = EXIT_SUCCESS;
757
        }
758
        goto uninstall_mode_cleanup;
759
    }
760
761
    // Probe for GPT and logical block size
762
    int gpt = 0;
763
    struct gpt_table_header gpt_header;
764
    uint64_t lb_guesses[] = { 512, 4096 };
765
    uint64_t lb_size = 0;
766
    for (size_t i = 0; i < SIZEOF_ARRAY(lb_guesses); i++) {
767
        device_read(&gpt_header, lb_guesses[i], sizeof(struct gpt_table_header));
768
        if (!strncmp(gpt_header.signature, "EFI PART", 8)) {
769
            lb_size = lb_guesses[i];
770
            gpt = 1;
771
            if (!quiet) {
772
                fprintf(stderr, "Installing to GPT. Logical block size of %" PRIu64 " bytes.\n",
773
                        lb_guesses[i]);
774
            }
775
            break;
776
        }
777
    }
778
779
    struct gpt_table_header secondary_gpt_header;
780
    if (gpt) {
781
        if (!quiet) {
782
            fprintf(stderr, "Secondary header at LBA 0x%" PRIx64 ".\n",
783
                    ENDSWAP(gpt_header.alternate_lba));
784
        }
785
        device_read(&secondary_gpt_header, lb_size * ENDSWAP(gpt_header.alternate_lba),
786
              sizeof(struct gpt_table_header));
787
        if (!strncmp(secondary_gpt_header.signature, "EFI PART", 8)) {
788
            if (!quiet) {
789
                fprintf(stderr, "Secondary header valid.\n");
790
            }
791
        } else {
792
            fprintf(stderr, "error: Secondary header not valid, aborting.\n");
793
            goto cleanup;
794
        }
795
    }
796
797
    // Check if this is an ISO w/ a GPT, in which case try converting it
798
    // to MBR for improved compatibility with a whole range of hardware that
799
    // does not like booting off of GPT in BIOS or CSM mode, and other
800
    // broken hardware.
801
    if (gpt && gpt2mbr_allowed == true) {
802
        char iso_signature[5];
803
        device_read(iso_signature, 32769, 5);
804
805
        if (strncmp(iso_signature, "CD001", 5) != 0) {
806
            goto no_mbr_conv;
807
        }
808
809
        if (!quiet) {
810
            fprintf(stderr, "Detected ISOHYBRID with a GUID partition table (GPT).\n");
811
            fprintf(stderr, "Converting to MBR for improved compatibility...\n");
812
        }
813
814
        // Gather the (up to 4) GPT partition to convert.
815
        struct {
816
            uint64_t lba_start;
817
            uint64_t lba_end;
818
            uint8_t chs_start[3];
819
            uint8_t chs_end[3];
820
            uint8_t type;
821
        } part_to_conv[4];
822
        size_t part_to_conv_i = 0;
823
824
        uint64_t part_entry_base;
825
        if (mul_u64_overflow(ENDSWAP(gpt_header.partition_entry_lba), lb_size, &part_entry_base)) {
826
            goto no_mbr_conv;
827
        }
828
829
        for (int64_t i = 0; i < (int64_t)ENDSWAP(gpt_header.number_of_partition_entries); i++) {
830
            struct gpt_entry gpt_entry;
831
            uint64_t entry_offset = (uint64_t)i * ENDSWAP(gpt_header.size_of_partition_entry);
832
            if (add_u64_overflow(part_entry_base, entry_offset, &entry_offset)) {
833
                goto no_mbr_conv;
834
            }
835
            device_read(&gpt_entry, entry_offset, sizeof(struct gpt_entry));
836
837
            if (gpt_entry.unique_partition_guid[0] == 0 &&
838
                gpt_entry.unique_partition_guid[1] == 0) {
839
                continue;
840
            }
841
842
            if (part_to_conv_i == 4) {
843
                if (!quiet) {
844
                    fprintf(stderr, "GPT contains more than 4 partitions, will not convert.\n");
845
                }
846
                goto no_mbr_conv;
847
            }
848
849
            if (ENDSWAP(gpt_entry.starting_lba) > UINT32_MAX) {
850
                if (!quiet) {
851
                    fprintf(stderr, "Starting LBA of partition %" PRIi64 " is greater than UINT32_MAX, will not convert GPT.\n", i + 1);
852
                }
853
                goto no_mbr_conv;
854
            }
855
            part_to_conv[part_to_conv_i].lba_start = ENDSWAP(gpt_entry.starting_lba);
856
            lba2chs(part_to_conv[part_to_conv_i].chs_start, part_to_conv[part_to_conv_i].lba_start);
857
858
            if (ENDSWAP(gpt_entry.ending_lba) > UINT32_MAX) {
859
                if (!quiet) {
860
                    fprintf(stderr, "Ending LBA of partition %" PRIi64 " is greater than UINT32_MAX, will not convert GPT.\n", i + 1);
861
                }
862
                goto no_mbr_conv;
863
            }
864
            part_to_conv[part_to_conv_i].lba_end = ENDSWAP(gpt_entry.ending_lba);
865
            lba2chs(part_to_conv[part_to_conv_i].chs_end, part_to_conv[part_to_conv_i].lba_end);
866
867
            if (part_to_conv[part_to_conv_i].lba_end - part_to_conv[part_to_conv_i].lba_start + 1 > UINT32_MAX) {
868
                if (!quiet) {
869
                    fprintf(stderr, "Sector count of partition %" PRIi64 " is greater than UINT32_MAX, will not convert GPT.\n", i + 1);
870
                }
871
                goto no_mbr_conv;
872
            }
873
874
            int type = gpt2mbr_type(ENDSWAP(gpt_entry.partition_type_guid[0]),
875
                                    ENDSWAP(gpt_entry.partition_type_guid[1]));
876
            if (type == -1) {
877
                if (!quiet) {
878
                    fprintf(stderr, "Cannot convert partition type for partition %" PRIi64 ", will not convert GPT.\n", i + 1);
879
                }
880
                goto no_mbr_conv;
881
            }
882
883
            part_to_conv[part_to_conv_i].type = type;
884
885
            part_to_conv_i++;
886
        }
887
888
        // Nuke the GPTs.
889
        empty_lba = calloc(1, lb_size);
890
        if (empty_lba == NULL) {
891
            perror_wrap("error: bios_install(): malloc()");
892
            goto cleanup;
893
        }
894
895
        // ... nuke primary GPT + protective MBR.
896
        for (size_t i = 0; i < 34; i++) {
897
            device_write(empty_lba, i * lb_size, lb_size);
898
        }
899
900
        // ... nuke secondary GPT.
901
        uint64_t alt_lba = ENDSWAP(gpt_header.alternate_lba);
902
        if (alt_lba >= 32) {
903
            for (size_t i = 0; i < 33; i++) {
904
                device_write(empty_lba, (alt_lba - 32 + i) * lb_size, lb_size);
905
            }
906
        }
907
908
        // We're no longer GPT.
909
        gpt = 0;
910
911
        // Generate pseudorandom MBR disk ID.
912
        srand(time(NULL));
913
        for (size_t i = 0; i < 4; i++) {
914
            uint8_t r = rand();
915
            device_write(&r, 0x1b8 + i, 1);
916
        }
917
918
        // Write out the partition entries.
919
        for (size_t i = 0; i < part_to_conv_i; i++) {
920
            device_write(&part_to_conv[i].type, 0x1be + i * 16 + 0x04, 1);
921
            uint32_t lba_start = ENDSWAP((uint32_t)part_to_conv[i].lba_start);
922
            device_write(&lba_start, 0x1be + i * 16 + 0x08, 4);
923
            uint32_t sect_count = ENDSWAP((uint32_t)((part_to_conv[i].lba_end - part_to_conv[i].lba_start) + 1));
924
            device_write(&sect_count, 0x1be + i * 16 + 0x0c, 4);
925
926
            device_write(part_to_conv[i].chs_start, 0x1be + i * 16 + 1, 3);
927
            device_write(part_to_conv[i].chs_end, 0x1be + i * 16 + 5, 3);
928
        }
929
930
        if (!quiet) {
931
            fprintf(stderr, "Conversion successful.\n");
932
        }
933
    }
934
935
no_mbr_conv:;
936
937
    int mbr = 0;
938
    if (gpt == 0) {
939
        // Do all sanity checks on MBR
940
        mbr = 1;
941
942
        uint8_t hint8 = 0;
943
        uint32_t hint32 = 0;
944
945
        bool any_active = false;
946
947
        device_read(&hint8, 446, sizeof(uint8_t));
948
        if (hint8 != 0x00 && hint8 != 0x80) {
949
            if (!force) {
950
                mbr = 0;
951
            } else {
952
                hint8 &= 0x80;
953
                device_write(&hint8, 446, sizeof(uint8_t));
954
            }
955
        }
956
        any_active = any_active || (hint8 & 0x80) != 0;
957
        device_read(&hint8, 446 + 4, sizeof(uint8_t));
958
        if (hint8 != 0x00) {
959
            device_read(&hint32, 446 + 8, sizeof(uint32_t));
960
            hint32 = ENDSWAP(hint32);
961
            if (hint32 < 63) {
962
                goto part_too_low;
963
            }
964
        }
965
        device_read(&hint8, 462, sizeof(uint8_t));
966
        if (hint8 != 0x00 && hint8 != 0x80) {
967
            if (!force) {
968
                mbr = 0;
969
            } else {
970
                hint8 &= 0x80;
971
                device_write(&hint8, 462, sizeof(uint8_t));
972
            }
973
        }
974
        any_active = any_active || (hint8 & 0x80) != 0;
975
        device_read(&hint8, 462 + 4, sizeof(uint8_t));
976
        if (hint8 != 0x00) {
977
            device_read(&hint32, 462 + 8, sizeof(uint32_t));
978
            hint32 = ENDSWAP(hint32);
979
            if (hint32 < 63) {
980
                goto part_too_low;
981
            }
982
        }
983
        device_read(&hint8, 478, sizeof(uint8_t));
984
        if (hint8 != 0x00 && hint8 != 0x80) {
985
            if (!force) {
986
                mbr = 0;
987
            } else {
988
                hint8 &= 0x80;
989
                device_write(&hint8, 478, sizeof(uint8_t));
990
            }
991
        }
992
        any_active = any_active || (hint8 & 0x80) != 0;
993
        device_read(&hint8, 478 + 4, sizeof(uint8_t));
994
        if (hint8 != 0x00) {
995
            device_read(&hint32, 478 + 8, sizeof(uint32_t));
996
            hint32 = ENDSWAP(hint32);
997
            if (hint32 < 63) {
998
                goto part_too_low;
999
            }
1000
        }
1001
        device_read(&hint8, 494, sizeof(uint8_t));
1002
        if (hint8 != 0x00 && hint8 != 0x80) {
1003
            if (!force) {
1004
                mbr = 0;
1005
            } else {
1006
                hint8 &= 0x80;
1007
                device_write(&hint8, 494, sizeof(uint8_t));
1008
            }
1009
        }
1010
        any_active = any_active || (hint8 & 0x80) != 0;
1011
        device_read(&hint8, 494 + 4, sizeof(uint8_t));
1012
        if (hint8 != 0x00) {
1013
            device_read(&hint32, 494 + 8, sizeof(uint32_t));
1014
            hint32 = ENDSWAP(hint32);
1015
            if (hint32 < 63) {
1016
                goto part_too_low;
1017
            }
1018
        }
1019
1020
        if (0) {
1021
part_too_low:
1022
            fprintf(stderr, "error: A partition's start sector is less than 63, aborting.\n");
1023
            goto cleanup;
1024
        }
1025
1026
        if (mbr) {
1027
            bool err;
1028
            mbr = validate_or_force(0, force, &err);
1029
            if (err) {
1030
                goto cleanup;
1031
            }
1032
        }
1033
1034
        if (mbr && !any_active) {
1035
            if (!quiet) {
1036
                fprintf(stderr, "No active partition found, some systems may not boot.\n");
1037
                fprintf(stderr, "Setting partition 1 as active to work around the issue...\n");
1038
            }
1039
            hint8 = 0x80;
1040
            device_write(&hint8, 446, sizeof(uint8_t));
1041
        }
1042
    }
1043
1044
    if (gpt == 0 && mbr == 0) {
1045
        fprintf(stderr, "error: Could not determine if the device has a valid partition table.\n");
1046
        fprintf(stderr, "       Please ensure the device has a valid MBR or GPT.\n");
1047
        fprintf(stderr, "       Alternatively, pass `--force` to override these checks.\n");
1048
        fprintf(stderr, "       **ONLY DO THIS AT YOUR OWN RISK, DATA LOSS MAY OCCUR!**\n");
1049
        goto cleanup;
1050
    }
1051
1052
    // Default location of stage2 for MBR (in post MBR gap)
1053
    uint64_t stage2_loc = 512;
1054
1055
    if (gpt) {
1056
        struct gpt_entry gpt_entry;
1057
        uint32_t partition_num;
1058
1059
        uint64_t gpt_part_entry_base;
1060
        if (mul_u64_overflow(ENDSWAP(gpt_header.partition_entry_lba), lb_size, &gpt_part_entry_base)) {
1061
            fprintf(stderr, "error: GPT partition entry LBA overflows.\n");
1062
            goto cleanup;
1063
        }
1064
1065
        if (part_ndx != NULL) {
1066
            if (sscanf(part_ndx, "%" SCNu32, &partition_num) != 1) {
1067
                fprintf(stderr, "error: Invalid partition number format.\n");
1068
                goto cleanup;
1069
            }
1070
            partition_num--;
1071
            if (partition_num >= ENDSWAP(gpt_header.number_of_partition_entries)) {
1072
                fprintf(stderr, "error: Partition number is too large.\n");
1073
                goto cleanup;
1074
            }
1075
1076
            uint64_t entry_off = (uint64_t)partition_num * ENDSWAP(gpt_header.size_of_partition_entry);
1077
            if (add_u64_overflow(gpt_part_entry_base, entry_off, &entry_off)) {
1078
                fprintf(stderr, "error: GPT partition entry offset overflows.\n");
1079
                goto cleanup;
1080
            }
1081
            device_read(&gpt_entry, entry_off, sizeof(struct gpt_entry));
1082
1083
            if (gpt_entry.unique_partition_guid[0] == 0 &&
1084
              gpt_entry.unique_partition_guid[1] == 0) {
1085
                fprintf(stderr, "error: No such partition: %" PRIu32 ".\n", partition_num + 1);
1086
                goto cleanup;
1087
            }
1088
1089
            if (!force && memcmp("Hah!IdontNeedEFI", &gpt_entry.partition_type_guid, 16) != 0) {
1090
                fprintf(stderr, "error: Chosen partition for BIOS boot code is not of BIOS boot partition type.\n");
1091
                fprintf(stderr, "       Pass `--force` to override this check.\n");
1092
                fprintf(stderr, "       **ONLY DO THIS AT YOUR OWN RISK, DATA LOSS MAY OCCUR!**\n");
1093
                goto cleanup;
1094
            }
1095
        } else {
1096
            // Try to autodetect the BIOS boot partition
1097
            for (partition_num = 0; partition_num < ENDSWAP(gpt_header.number_of_partition_entries); partition_num++) {
1098
                uint64_t entry_off = (uint64_t)partition_num * ENDSWAP(gpt_header.size_of_partition_entry);
1099
                if (add_u64_overflow(gpt_part_entry_base, entry_off, &entry_off)) {
1100
                    fprintf(stderr, "error: GPT partition entry offset overflows.\n");
1101
                    goto cleanup;
1102
                }
1103
                device_read(&gpt_entry, entry_off, sizeof(struct gpt_entry));
1104
1105
                if (memcmp("Hah!IdontNeedEFI", &gpt_entry.partition_type_guid, 16) == 0) {
1106
                    if (!quiet) {
1107
                        fprintf(stderr, "Autodetected partition %" PRIu32 " as BIOS boot partition.\n", partition_num + 1);
1108
                    }
1109
                    goto bios_boot_autodetected;
1110
                }
1111
            }
1112
1113
            fprintf(stderr, "error: Installing to a GPT device, but no BIOS boot partition specified or\n");
1114
            fprintf(stderr, "       detected.\n");
1115
            goto cleanup;
1116
        }
1117
1118
bios_boot_autodetected:;
1119
        uint64_t starting_lba = ENDSWAP(gpt_entry.starting_lba);
1120
        uint64_t ending_lba = ENDSWAP(gpt_entry.ending_lba);
1121
1122
        if (ending_lba < starting_lba) {
1123
            fprintf(stderr, "error: Partition %" PRIu32 " has ending LBA less than starting LBA.\n", partition_num + 1);
1124
            goto cleanup;
1125
        }
1126
1127
        uint64_t part_size;
1128
        if (mul_u64_overflow(ending_lba - starting_lba + 1, lb_size, &part_size)) {
1129
            fprintf(stderr, "error: Partition %" PRIu32 " size overflows.\n", partition_num + 1);
1130
            goto cleanup;
1131
        }
1132
1133
        if (part_size < 32768) {
1134
            fprintf(stderr, "error: Partition %" PRIu32 " is smaller than 32KiB.\n", partition_num + 1);
1135
            goto cleanup;
1136
        }
1137
1138
        if (mul_u64_overflow(starting_lba, lb_size, &stage2_loc)) {
1139
            fprintf(stderr, "error: Partition %" PRIu32 " starting LBA overflows.\n", partition_num + 1);
1140
            goto cleanup;
1141
        }
1142
1143
        bool err;
1144
        bool valid = validate_or_force(stage2_loc, force, &err);
1145
        if (err) {
1146
            goto cleanup;
1147
        }
1148
1149
        if (!valid) {
1150
            fprintf(stderr, "error: The partition selected to install the BIOS boot code to contains\n");
1151
            fprintf(stderr, "       a recognised filesystem.\n");
1152
            fprintf(stderr, "       Pass `--force` to override these checks.\n");
1153
            fprintf(stderr, "       **ONLY DO THIS AT YOUR OWN RISK, DATA LOSS MAY OCCUR!**\n");
1154
            goto cleanup;
1155
        }
1156
1157
        if (!quiet) {
1158
            fprintf(stderr, "Installing BIOS boot code to partition %" PRIu32 ".\n", partition_num + 1);
1159
        }
1160
    } else {
1161
        if (!quiet) {
1162
            fprintf(stderr, "Installing to MBR.\n");
1163
        }
1164
    }
1165
1166
    if (!quiet) {
1167
        fprintf(stderr, "Stage 2 to be located at byte offset 0x%" PRIx64 ".\n", stage2_loc);
1168
    }
1169
1170
    // Save original timestamp
1171
    device_read(timestamp, 218, 6);
1172
1173
    // Save the original partition table of the device
1174
    device_read(orig_mbr, 440, 70);
1175
1176
    // Write the bootsector from the bootloader to the device
1177
    device_write(&bootloader_img[0], 0, 512);
1178
1179
    // Write the rest of stage 2 to the device
1180
    device_write(&bootloader_img[512], stage2_loc, bootloader_file_size - 512);
1181
1182
    // Hardcode in the bootsector the location of stage 2
1183
    stage2_loc = ENDSWAP(stage2_loc);
1184
    device_write(&stage2_loc, 0x1a4, sizeof(uint64_t));
1185
1186
    // Write back timestamp
1187
    device_write(timestamp, 218, 6);
1188
1189
    // Write back the saved partition table to the device
1190
    device_write(orig_mbr, 440, 70);
1191
1192
    if (!device_flush_cache())
1193
        goto cleanup;
1194
1195
    if (!quiet) {
1196
        fprintf(stderr, "Reminder: Remember to copy the limine-bios.sys file in either\n"
1197
                        "          the root, /boot, /limine, or /boot/limine directories of\n"
1198
                        "          one of the partitions on the device, or boot will fail!\n");
1199
1200
        fprintf(stderr, "Limine BIOS stages installed successfully.\n");
1201
    }
1202
1203
    ok = EXIT_SUCCESS;
1204
1205
cleanup:
1206
    reverse_uninstall_data();
1207
    if (ok != EXIT_SUCCESS) {
1208
        // If we failed, attempt to reverse install process
1209
        fprintf(stderr, "Install failed, undoing work...\n");
1210
        uninstall(true);
1211
    } else if (uninstall_file != NULL) {
1212
        store_uninstall_data(uninstall_file);
1213
    }
1214
uninstall_mode_cleanup:
1215
    free_uninstall_data();
1216
    if (empty_lba)
1217
        free(empty_lba);
1218
    if (cache)
1219
        free(cache);
1220
    if (device != NULL)
1221
        fclose(device);
1222
1223
    return ok;
1224
}
1225
#endif
1226
1227
#define CONFIG_B2SUM_SIGNATURE "++CONFIG_B2SUM_SIGNATURE++"
1228
1229
static void enroll_config_usage(void) {
1230
    printf("usage: %s enroll-config <Limine executable> <BLAKE2B of config file>\n", program_name);
1231
    printf("\n");
1232
    printf("    --reset      Remove enrolled BLAKE2B, will not check config integrity\n");
1233
    printf("\n");
1234
    printf("    --quiet      Do not print verbose diagnostic messages\n");
1235
    printf("\n");
1236
    printf("    --help | -h  Display this help message\n");
1237
    printf("\n");
1238
}
1239
1240
static int enroll_config(int argc, char *argv[]) {
1241
    int ret = EXIT_FAILURE;
1242
1243
    char *bootloader = NULL;
1244
    FILE *bootloader_file = NULL;
1245
    bool quiet = false;
1246
    bool reset = false;
1247
1248
    for (int i = 1; i < argc; i++) {
1249
        if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
1250
            enroll_config_usage();
1251
            return EXIT_SUCCESS;
1252
        } else if (strcmp(argv[i], "--quiet") == 0) {
1253
            remove_arg(&argc, argv, i--);
1254
            quiet = true;
1255
        } else if (strcmp(argv[i], "--reset") == 0) {
1256
            remove_arg(&argc, argv, i--);
1257
            reset = true;
1258
        }
1259
    }
1260
1261
    if (argc <= (reset ? 1 : 2)) {
1262
        enroll_config_usage();
1263
#ifdef IS_WINDOWS
1264
        system("pause");
1265
#endif
1266
        return EXIT_FAILURE;
1267
    }
1268
1269
    if (!reset && strlen(argv[2]) != 128) {
1270
        fprintf(stderr, "error: BLAKE2B specified is not 128 characters long.\n");
1271
        goto cleanup;
1272
    }
1273
1274
    bootloader_file = fopen(argv[1], "r+b");
1275
    if (bootloader_file == NULL) {
1276
        perror_wrap("error: `%s`", argv[1]);
1277
        goto cleanup;
1278
    }
1279
1280
    if (fseek(bootloader_file, 0, SEEK_END) != 0) {
1281
        perror_wrap("error: enroll_config(): fseek()");
1282
        goto cleanup;
1283
    }
1284
    long ftell_result = ftell(bootloader_file);
1285
    if (ftell_result < 0) {
1286
        perror_wrap("error: enroll_config(): ftell()");
1287
        goto cleanup;
1288
    }
1289
    size_t bootloader_size = (size_t)ftell_result;
1290
    rewind(bootloader_file);
1291
1292
    size_t min_size = (sizeof(CONFIG_B2SUM_SIGNATURE) - 1) + 128;
1293
    if (bootloader_size < min_size) {
1294
        fprintf(stderr, "error: Bootloader file too small (need at least %zu bytes)\n", min_size);
1295
        goto cleanup;
1296
    }
1297
1298
    bootloader = malloc(bootloader_size);
1299
    if (bootloader == NULL) {
1300
        perror_wrap("error: enroll_config(): malloc()");
1301
        goto cleanup;
1302
    }
1303
1304
    if (fread(bootloader, bootloader_size, 1, bootloader_file) != 1) {
1305
        perror_wrap("error: enroll_config(): fread()");
1306
        goto cleanup;
1307
    }
1308
1309
    char *checksum_loc = NULL;
1310
    size_t checked_count = 0;
1311
    const char *config_b2sum_sign = CONFIG_B2SUM_SIGNATURE;
1312
    for (size_t i = 0; i < bootloader_size - min_size + 1; i++) {
1313
        if (bootloader[i] != config_b2sum_sign[checked_count]) {
1314
            if (checked_count > 0) {
1315
                i -= checked_count; // restart after first byte of failed match
1316
                checked_count = 0;
1317
            }
1318
            continue;
1319
        }
1320
1321
        checked_count++;
1322
1323
        if (checked_count == sizeof(CONFIG_B2SUM_SIGNATURE) - 1) {
1324
            checksum_loc = &bootloader[i + 1];
1325
            break;
1326
        }
1327
    }
1328
1329
    if (checksum_loc == NULL) {
1330
        fprintf(stderr, "error: Checksum location not found in provided executable.\n");
1331
        goto cleanup;
1332
    }
1333
1334
    if (!reset) {
1335
        memcpy(checksum_loc, argv[2], 128);
1336
    } else {
1337
        memset(checksum_loc, '0', 128);
1338
    }
1339
1340
    if (fseek(bootloader_file, 0, SEEK_SET) != 0) {
1341
        perror_wrap("error: enroll_config(): fseek()");
1342
        goto cleanup;
1343
    }
1344
    if (fwrite(bootloader, bootloader_size, 1, bootloader_file) != 1) {
1345
        perror_wrap("error: enroll_config(): fwrite()");
1346
        goto cleanup;
1347
    }
1348
1349
    if (!quiet) {
1350
        fprintf(stderr, "Config file BLAKE2B successfully %s.\n", reset ? "reset" : "enrolled");
1351
    }
1352
    ret = EXIT_SUCCESS;
1353
1354
cleanup:
1355
    if (bootloader != NULL) {
1356
        free(bootloader);
1357
    }
1358
    if (bootloader_file != NULL) {
1359
        fclose(bootloader_file);
1360
    }
1361
    return ret;
1362
}
1363
1364
#define LIMINE_VERSION "%VERSION%"
1365
#define LIMINE_COPYRIGHT "%COPYRIGHT%"
1366
1367
static void version_usage(void) {
1368
    printf("usage: %s version [options...]\n", program_name);
1369
    printf("\n");
1370
    printf("    --version-only  Only print the version number without licensing info\n");
1371
    printf("                    and other distractions\n");
1372
    printf("\n");
1373
    printf("    --help | -h     Display this help message\n");
1374
    printf("\n");
1375
}
1376
1377
static int version(int argc, char *argv[]) {
1378
    if (argc >= 2) {
1379
        if (strcmp(argv[1], "--help") == 0) {
1380
            version_usage();
1381
            return EXIT_SUCCESS;
1382
        } else if (strcmp(argv[1], "--version-only") == 0) {
1383
            puts(LIMINE_VERSION);
1384
            return EXIT_SUCCESS;
1385
        }
1386
    }
1387
1388
    puts("Limine " LIMINE_VERSION);
1389
    puts(LIMINE_COPYRIGHT);
1390
    puts("Limine is distributed under the terms of the BSD-2-Clause license.");
1391
    puts("There is ABSOLUTELY NO WARRANTY, to the extent permitted by law.");
1392
    return EXIT_SUCCESS;
1393
}
1394
1395
static void general_usage(void) {
1396
    printf("usage: %s <command> <args...>\n", program_name);
1397
    printf("\n");
1398
    printf("    --print-datadir   Print the directory containing the bootloader files\n");
1399
    printf("\n");
1400
    printf("    --version         Print the Limine version (like the `version` command)\n");
1401
    printf("\n");
1402
    printf("    --help | -h       Display this help message\n");
1403
    printf("\n");
1404
    printf("Commands: `help`, `version`, `bios-install`, `enroll-config`\n");
1405
    printf("Use `--help` after specifying the command for command-specific help.\n");
1406
}
1407
1408
static int print_datadir(void) {
1409
#ifdef LIMINE_DATADIR
1410
    puts(LIMINE_DATADIR);
1411
    return EXIT_SUCCESS;
1412
#else
1413
    fprintf(stderr, "error: Cannot print datadir for `limine` built standalone.\n");
1414
    return EXIT_FAILURE;
1415
#endif
1416
}
1417
1418
int main(int argc, char *argv[]) {
1419
    program_name = argv[0];
1420
1421
    if (argc <= 1) {
1422
        general_usage();
1423
        return EXIT_FAILURE;
1424
    }
1425
1426
    if (strcmp(argv[1], "help") == 0
1427
     || strcmp(argv[1], "--help") == 0
1428
     || strcmp(argv[1], "-h") == 0) {
1429
        general_usage();
1430
        return EXIT_SUCCESS;
1431
    } else if (strcmp(argv[1], "bios-install") == 0) {
1432
#ifndef LIMINE_NO_BIOS
1433
        return bios_install(argc - 1, &argv[1]);
1434
#else
1435
        fprintf(stderr, "error: Limine has been compiled without BIOS support.\n");
1436
        return EXIT_FAILURE;
1437
#endif
1438
    } else if (strcmp(argv[1], "enroll-config") == 0) {
1439
        return enroll_config(argc - 1, &argv[1]);
1440
    } else if (strcmp(argv[1], "--print-datadir") == 0) {
1441
        return print_datadir();
1442
    } else if (strcmp(argv[1], "version") == 0
1443
            || strcmp(argv[1], "--version") == 0) {
1444
        return version(argc - 1, &argv[1]);
1445
    }
1446
1447
    general_usage();
1448
    return EXIT_FAILURE;
1449
}
tab: 248 wrap: offon