:: limine / common / protos / multiboot1.c 19.8 KB raw

1
#if defined (__x86_64__) || defined (__i386__)
2
3
#include <stdint.h>
4
#include <stddef.h>
5
#include <stdnoreturn.h>
6
#include <protos/multiboot1.h>
7
#include <protos/multiboot.h>
8
#include <config.h>
9
#include <lib/libc.h>
10
#include <lib/elf.h>
11
#include <lib/misc.h>
12
#include <lib/config.h>
13
#include <lib/print.h>
14
#include <lib/uri.h>
15
#include <lib/tpm.h>
16
#include <lib/fb.h>
17
#include <lib/term.h>
18
#include <lib/elsewhere.h>
19
#include <sys/pic.h>
20
#include <sys/cpu.h>
21
#include <sys/idt.h>
22
#include <sys/iommu.h>
23
#include <sys/lapic.h>
24
#include <fs/file.h>
25
#include <mm/vmm.h>
26
#include <mm/pmm.h>
27
#include <drivers/vga_textmode.h>
28
29
#define LIMINE_BRAND "Limine " LIMINE_VERSION
30
31
#define MEMMAP_MAX 256
32
33
// Returns the size required to store the multiboot info.
34
static size_t get_multiboot1_info_size(
35
    char *cmdline,
36
    size_t modules_count, size_t modules_cmdlines_size,
37
    uint32_t section_entry_size, uint32_t section_num
38
) {
39
#define OVERFLOW panic(true, "multiboot1: info size overflow")
40
    return ALIGN_UP(sizeof(struct multiboot1_info), 16, OVERFLOW) +
41
           ALIGN_UP(strlen(cmdline) + 1, 16, OVERFLOW) +
42
           ALIGN_UP(sizeof(LIMINE_BRAND), 16, OVERFLOW) +
43
           ALIGN_UP(CHECKED_MUL(section_entry_size, section_num, OVERFLOW), 16, OVERFLOW) +
44
           ALIGN_UP(CHECKED_MUL(sizeof(struct multiboot1_module), modules_count, OVERFLOW), 16, OVERFLOW) +
45
           ALIGN_UP(modules_cmdlines_size, 16, OVERFLOW) +
46
           ALIGN_UP(sizeof(struct multiboot1_mmap_entry) * MEMMAP_MAX, 16, OVERFLOW);
47
#undef OVERFLOW
48
}
49
50
static void *mb1_info_alloc(void **mb1_info_raw, size_t size) {
51
    void *ret = *mb1_info_raw;
52
    *mb1_info_raw += ALIGN_UP(size, 16, panic(true, "multiboot: info alloc overflow"));
53
    return ret;
54
}
55
56
noreturn void multiboot1_load(char *config, char *cmdline) {
57
    struct file_handle *kernel_file;
58
59
#if defined (UEFI)
60
    if (cmdline != NULL) {
61
        tpm_measure(TPM_PCR_BOOT_AUTH, TPM_EV_IPL,
62
                    cmdline, strlen(cmdline), "cmdline: ", cmdline);
63
    }
64
#endif
65
66
    char *kernel_path = config_get_value(config, 0, "PATH");
67
    if (kernel_path == NULL) {
68
        kernel_path = config_get_value(config, 0, "KERNEL_PATH");
69
    }
70
    if (kernel_path == NULL) {
71
        panic(true, "multiboot1: Executable path not specified");
72
    }
73
74
    print("multiboot1: Loading executable `%#`...\n", kernel_path);
75
76
    if ((kernel_file = uri_open(kernel_path, MEMMAP_KERNEL_AND_MODULES, false
77
#if defined (__i386__)
78
        , NULL, NULL
79
#endif
80
    )) == NULL)
81
        panic(true, "multiboot1: Failed to open executable with path `%#`. Is the path correct?", kernel_path);
82
83
    uint8_t *kernel = kernel_file->fd;
84
85
    size_t kernel_file_size = kernel_file->size;
86
87
#if defined (UEFI)
88
    tpm_measure_path(TPM_PCR_BOOT_AUTH, TPM_EV_IPL, "path: ", kernel_path);
89
    tpm_measure(TPM_PCR_LOADED_IMAGES, TPM_EV_IPL,
90
                kernel, kernel_file_size, "path: ", kernel_path);
91
#endif
92
93
    fclose(kernel_file);
94
95
    struct multiboot1_header header = {0};
96
    size_t header_offset = 0;
97
98
    // Per Multiboot spec, header must be within first 8192 bytes and 4-byte aligned.
99
    // Ensure we don't read past end of file when checking magic or copying header.
100
    size_t search_limit = 8192;
101
    if (kernel_file_size < sizeof(struct multiboot1_header)) {
102
        panic(true, "multiboot1: Kernel file too small to contain header");
103
    }
104
    if (search_limit > kernel_file_size - sizeof(struct multiboot1_header)) {
105
        search_limit = kernel_file_size - sizeof(struct multiboot1_header);
106
    }
107
108
    for (header_offset = 0; header_offset <= search_limit; header_offset += 4) {
109
        uint32_t v = *(uint32_t *)(kernel + header_offset);
110
111
        if (v == MULTIBOOT1_HEADER_MAGIC) {
112
            memcpy(&header, kernel + header_offset, sizeof(header));
113
            break;
114
        }
115
    }
116
117
    if (header.magic != MULTIBOOT1_HEADER_MAGIC) {
118
        panic(true, "multiboot1: Invalid magic");
119
    }
120
121
    if (header.magic + header.flags + header.checksum)
122
        panic(true, "multiboot1: Header checksum is invalid");
123
124
    bool section_hdr_info_valid = false;
125
    struct elf_section_hdr_info section_hdr_info = {0};
126
127
    uint64_t entry_point;
128
    struct elsewhere_range *ranges;
129
    uint64_t ranges_count = 1;
130
131
    if (header.flags & (1 << 16)) {
132
        if (header.load_addr > header.header_addr)
133
            panic(true, "multiboot1: Illegal load address");
134
135
        size_t addr_diff = header.header_addr - header.load_addr;
136
        if (addr_diff > header_offset)
137
            panic(true, "multiboot1: Address tag offset underflow");
138
139
        size_t load_src = header_offset - addr_diff;
140
141
        size_t load_size;
142
        if (header.load_end_addr) {
143
            if (header.load_end_addr < header.load_addr)
144
                panic(true, "multiboot1: Load end address less than load address");
145
            load_size = header.load_end_addr - header.load_addr;
146
        } else {
147
            if (load_src > kernel_file_size)
148
                panic(true, "multiboot1: Load source exceeds kernel file size");
149
            load_size = kernel_file_size - load_src;
150
        }
151
152
        uint32_t bss_size = 0;
153
        if (header.bss_end_addr) {
154
            uintptr_t bss_addr = CHECKED_ADD((uintptr_t)header.load_addr, load_size,
155
                panic(true, "multiboot1: load_addr + load_size overflow"));
156
            if (header.bss_end_addr < bss_addr)
157
                panic(true, "multiboot1: Illegal bss end address");
158
159
            bss_size = header.bss_end_addr - bss_addr;
160
        }
161
162
        if (load_src > kernel_file_size || load_size > kernel_file_size - load_src) {
163
            panic(true, "multiboot1: load_src + load_size exceeds kernel file size");
164
        }
165
166
        size_t full_size = CHECKED_ADD(load_size, bss_size,
167
            panic(true, "multiboot1: load_size + bss_size overflow"));
168
169
        void *elsewhere = ext_mem_alloc(full_size);
170
171
        memcpy(elsewhere, kernel + load_src, load_size);
172
173
        entry_point = header.entry_addr;
174
175
        ranges = ext_mem_alloc(sizeof(struct elsewhere_range));
176
177
        ranges->elsewhere = (uintptr_t)elsewhere;
178
        ranges->target = header.load_addr;
179
        ranges->length = full_size;
180
    } else {
181
        int bits = elf_bits(kernel, kernel_file_size);
182
183
        switch (bits) {
184
            case 32:
185
                if (!elf32_load_elsewhere(kernel, kernel_file_size, &entry_point, &ranges))
186
                    panic(true, "multiboot1: ELF32 load failure");
187
188
                section_hdr_info = elf32_section_hdr_info(kernel, kernel_file_size);
189
                section_hdr_info_valid = true;
190
                break;
191
            case 64: {
192
                if (!elf64_load_elsewhere(kernel, kernel_file_size, &entry_point, &ranges))
193
                    panic(true, "multiboot1: ELF64 load failure");
194
195
                section_hdr_info = elf64_section_hdr_info(kernel, kernel_file_size);
196
                section_hdr_info_valid = true;
197
                break;
198
            }
199
            default:
200
                panic(true, "multiboot1: Invalid ELF file bitness");
201
        }
202
    }
203
204
    size_t n_modules;
205
    size_t modules_cmdlines_size = 0;
206
207
    for (n_modules = 0;; n_modules++) {
208
        struct conf_tuple conf_tuple = config_get_tuple(config, n_modules, "MODULE_PATH", "MODULE_STRING");
209
        if (!conf_tuple.value1) break;
210
211
        char *module_cmdline = conf_tuple.value2;
212
        if (!module_cmdline) module_cmdline = "";
213
        modules_cmdlines_size += ALIGN_UP(strlen(module_cmdline) + 1, 16, panic(true, "multiboot: info size overflow"));
214
    }
215
216
    size_t mb1_info_size = get_multiboot1_info_size(
217
        cmdline,
218
        n_modules,
219
        modules_cmdlines_size,
220
        section_hdr_info_valid ? section_hdr_info.section_entry_size : 0,
221
        section_hdr_info_valid ? section_hdr_info.num : 0
222
    );
223
224
    // Realloc elsewhere ranges to include mb1 info, modules, and elf sections
225
    uint64_t ranges_max = ranges_count
226
       + 1 /* mb1 info range */
227
       + n_modules
228
       + (section_hdr_info_valid ? section_hdr_info.num : 0);
229
    struct elsewhere_range *new_ranges = ext_mem_alloc_counted(ranges_max, sizeof(struct elsewhere_range));
230
231
    memcpy(new_ranges, ranges, sizeof(struct elsewhere_range) * ranges_count);
232
    pmm_free(ranges, sizeof(struct elsewhere_range) * ranges_count);
233
    ranges = new_ranges;
234
235
    // GRUB allocates boot info at 0x10000, *except* if the kernel happens
236
    // to overlap this region, then it gets moved to right after the
237
    // kernel, or whichever PHDR happens to sit at 0x10000.
238
    // Allocate it wherever, then move it to where GRUB puts it
239
    // afterwards.
240
241
    // Elsewhere append mb1 info *after* kernel but *before* modules.
242
    void *mb1_info_raw = ext_mem_alloc(mb1_info_size);
243
    uint64_t mb1_info_final_loc = 0x10000;
244
245
    if (!elsewhere_append(true /* flexible target */,
246
            ranges, &ranges_count, ranges_max,
247
            mb1_info_raw, &mb1_info_final_loc, mb1_info_size)) {
248
        panic(true, "multiboot1: Cannot allocate mb1 info");
249
    }
250
251
    size_t mb1_info_slide = (size_t)mb1_info_raw - mb1_info_final_loc;
252
253
    struct multiboot1_info *multiboot1_info =
254
        mb1_info_alloc(&mb1_info_raw, sizeof(struct multiboot1_info));
255
256
    if (section_hdr_info_valid == true) {
257
        size_t section_table_size = CHECKED_MUL(section_hdr_info.section_entry_size, section_hdr_info.num,
258
            panic(true, "multiboot1: ELF section table size overflow"));
259
        if (section_hdr_info.section_offset > kernel_file_size ||
260
            section_table_size > kernel_file_size - section_hdr_info.section_offset) {
261
            panic(true, "multiboot1: ELF section headers out of bounds");
262
        }
263
264
        multiboot1_info->elf_sect.num = section_hdr_info.num;
265
        multiboot1_info->elf_sect.size = section_hdr_info.section_entry_size;
266
        multiboot1_info->elf_sect.shndx = section_hdr_info.str_section_idx;
267
268
        void *sections = mb1_info_alloc(&mb1_info_raw, section_table_size);
269
270
        multiboot1_info->elf_sect.addr = (uintptr_t)sections - mb1_info_slide;
271
272
        memcpy(sections, kernel + section_hdr_info.section_offset, section_table_size);
273
274
        int bits = elf_bits(kernel, kernel_file_size);
275
276
        if ((bits == 64 && section_hdr_info.section_entry_size < sizeof(struct elf64_shdr)) ||
277
            (bits == 32 && section_hdr_info.section_entry_size < sizeof(struct elf32_shdr))) {
278
            panic(true, "multiboot1: ELF section entry size too small");
279
        }
280
281
        for (size_t i = 0; i < section_hdr_info.num; i++) {
282
            if (bits == 64) {
283
                struct elf64_shdr *shdr = (void *)sections + i * section_hdr_info.section_entry_size;
284
285
                if (shdr->sh_addr != 0 || shdr->sh_size == 0) {
286
                    continue;
287
                }
288
289
                if (shdr->sh_offset > kernel_file_size ||
290
                    shdr->sh_size > kernel_file_size - shdr->sh_offset) {
291
                    continue;
292
                }
293
294
                uint64_t section = (uint64_t)-1; /* no target preference, use top */
295
296
                if (!elsewhere_append(true /* flexible target */,
297
                        ranges, &ranges_count, ranges_max,
298
                        kernel + shdr->sh_offset, &section, shdr->sh_size)) {
299
                    panic(true, "multiboot1: Cannot allocate elf sections");
300
                }
301
302
                shdr->sh_addr = section;
303
            } else {
304
                struct elf32_shdr *shdr = (void *)sections + i * section_hdr_info.section_entry_size;
305
306
                if (shdr->sh_addr != 0 || shdr->sh_size == 0) {
307
                    continue;
308
                }
309
310
                if (shdr->sh_offset > kernel_file_size ||
311
                    shdr->sh_size > kernel_file_size - shdr->sh_offset) {
312
                    continue;
313
                }
314
315
                uint64_t section = (uint64_t)-1; /* no target preference, use top */
316
317
                if (!elsewhere_append(true /* flexible target */,
318
                        ranges, &ranges_count, ranges_max,
319
                        kernel + shdr->sh_offset, &section, shdr->sh_size)) {
320
                    panic(true, "multiboot1: Cannot allocate elf sections");
321
                }
322
323
                shdr->sh_addr = section;
324
            }
325
        }
326
327
        multiboot1_info->flags |= (1 << 5);
328
    }
329
330
    if (n_modules) {
331
        struct multiboot1_module *mods =
332
            mb1_info_alloc(&mb1_info_raw, sizeof(struct multiboot1_module) * n_modules);
333
334
        multiboot1_info->mods_count = n_modules;
335
        multiboot1_info->mods_addr = (size_t)mods - mb1_info_slide;
336
337
        for (size_t i = 0; i < n_modules; i++) {
338
            struct multiboot1_module *m = mods + i;
339
340
            struct conf_tuple conf_tuple = config_get_tuple(config, i, "MODULE_PATH", "MODULE_STRING");
341
            char *module_path = conf_tuple.value1;
342
            if (module_path == NULL)
343
                panic(true, "multiboot1: Module disappeared unexpectedly");
344
345
            print("multiboot1: Loading module `%#`...\n", module_path);
346
347
            struct file_handle *f;
348
            if ((f = uri_open(module_path, MEMMAP_BOOTLOADER_RECLAIMABLE, false
349
#if defined (__i386__)
350
                , NULL, NULL
351
#endif
352
            )) == NULL)
353
                panic(true, "multiboot1: Failed to open module with path `%#`. Is the path correct?", module_path);
354
355
            char *module_cmdline = conf_tuple.value2;
356
            if (module_cmdline == NULL) {
357
                module_cmdline = "";
358
            }
359
            char *lowmem_modstr = mb1_info_alloc(&mb1_info_raw, strlen(module_cmdline) + 1);
360
            strcpy(lowmem_modstr, module_cmdline);
361
362
            void *module_addr = f->fd;
363
            uint64_t module_target = (uint64_t)-1; /* no target preference, use top */
364
365
#if defined (UEFI)
366
            tpm_measure_path(TPM_PCR_BOOT_AUTH, TPM_EV_IPL, "module_path: ", module_path);
367
            tpm_measure(TPM_PCR_LOADED_IMAGES, TPM_EV_IPL,
368
                        module_addr, f->size, "module_path: ", module_path);
369
#endif
370
371
            if (!elsewhere_append(true /* flexible target */,
372
                    ranges, &ranges_count, ranges_max,
373
                    module_addr, &module_target, f->size)) {
374
                panic(true, "multiboot1: Cannot allocate module");
375
            }
376
377
            m->begin   = module_target;
378
            m->end     = m->begin + f->size;
379
            m->cmdline = (uint32_t)(size_t)lowmem_modstr - mb1_info_slide;
380
            m->pad     = 0;
381
382
            fclose(f);
383
384
            if (verbose) {
385
                print("multiboot1: Requested module %u:\n", (uint32_t)i);
386
                print("            Path:   %s\n", module_path);
387
                print("            String: \"%s\"\n", module_cmdline ?: "");
388
                print("            Begin:  %x\n", m->begin);
389
                print("            End:    %x\n", m->end);
390
            }
391
        }
392
393
        multiboot1_info->flags |= (1 << 3);
394
    }
395
396
    char *lowmem_cmdline = mb1_info_alloc(&mb1_info_raw, strlen(cmdline) + 1);
397
    strcpy(lowmem_cmdline, cmdline);
398
    multiboot1_info->cmdline = (uint32_t)(size_t)lowmem_cmdline - mb1_info_slide;
399
    multiboot1_info->flags |= (1 << 2);
400
401
    char *bootload_name = LIMINE_BRAND;
402
    char *lowmem_bootname = mb1_info_alloc(&mb1_info_raw, strlen(bootload_name) + 1);
403
    strcpy(lowmem_bootname, bootload_name);
404
405
    multiboot1_info->bootloader_name = (uint32_t)(size_t)lowmem_bootname - mb1_info_slide;
406
    multiboot1_info->flags |= (1 << 9);
407
408
    term_notready();
409
410
    size_t req_width = 0;
411
    size_t req_height = 0;
412
    size_t req_bpp = 0;
413
#if defined (BIOS)
414
    {
415
        char *textmode_str = config_get_value(config, 0, "TEXTMODE");
416
        bool textmode = textmode_str != NULL && strcmp(textmode_str, "yes") == 0;
417
        if (textmode) {
418
            goto textmode;
419
        }
420
    }
421
#endif
422
423
424
    if (header.flags & (1 << 2)) {
425
        req_width = header.fb_width;
426
        req_height = header.fb_height;
427
        req_bpp = header.fb_bpp;
428
429
        if (header.fb_mode == 0) {
430
#if defined (UEFI)
431
modeset:;
432
#endif
433
            char *resolution = config_get_value(config, 0, "RESOLUTION");
434
            if (resolution != NULL)
435
                parse_resolution(&req_width, &req_height, &req_bpp, resolution);
436
437
            struct fb_info *fbs;
438
            size_t fbs_count;
439
            fb_init(&fbs, &fbs_count, req_width, req_height, req_bpp, false, false);
440
            if (fbs_count == 0) {
441
#if defined (UEFI)
442
                goto skip_modeset;
443
#elif defined (BIOS)
444
textmode:
445
                vga_textmode_init(false);
446
447
                multiboot1_info->fb_addr    = 0xb8000;
448
                multiboot1_info->fb_width   = 80;
449
                multiboot1_info->fb_height  = 25;
450
                multiboot1_info->fb_bpp     = 16;
451
                multiboot1_info->fb_pitch   = 2 * 80;
452
                multiboot1_info->fb_type    = 2;
453
#endif
454
            } else {
455
                multiboot1_info->fb_addr    = (uint64_t)fbs[0].framebuffer_addr;
456
                multiboot1_info->fb_width   = fbs[0].framebuffer_width;
457
                multiboot1_info->fb_height  = fbs[0].framebuffer_height;
458
                multiboot1_info->fb_bpp     = fbs[0].framebuffer_bpp;
459
                multiboot1_info->fb_pitch   = fbs[0].framebuffer_pitch;
460
                multiboot1_info->fb_type    = 1;
461
                multiboot1_info->fb_red_mask_size    = fbs[0].red_mask_size;
462
                multiboot1_info->fb_red_mask_shift   = fbs[0].red_mask_shift;
463
                multiboot1_info->fb_green_mask_size  = fbs[0].green_mask_size;
464
                multiboot1_info->fb_green_mask_shift = fbs[0].green_mask_shift;
465
                multiboot1_info->fb_blue_mask_size   = fbs[0].blue_mask_size;
466
                multiboot1_info->fb_blue_mask_shift  = fbs[0].blue_mask_shift;
467
            }
468
        } else {
469
#if defined (UEFI)
470
            print("multiboot1: Warning: Cannot use text mode with UEFI\n");
471
            goto modeset;
472
#elif defined (BIOS)
473
            goto textmode;
474
#endif
475
        }
476
477
        multiboot1_info->flags |= (1 << 12);
478
479
#if defined (UEFI)
480
skip_modeset:;
481
#endif
482
    } else {
483
#if defined (UEFI)
484
        panic(true, "multiboot1: Cannot use text mode with UEFI.");
485
#elif defined (BIOS)
486
        vga_textmode_init(false);
487
#endif
488
    }
489
490
    // Load relocation stub where it won't get overwritten (hopefully)
491
    size_t reloc_stub_size = (size_t)multiboot_reloc_stub_end - (size_t)multiboot_reloc_stub;
492
    void *reloc_stub = ext_mem_alloc(reloc_stub_size);
493
    memcpy(reloc_stub, multiboot_reloc_stub, reloc_stub_size);
494
495
#if defined (UEFI)
496
    efi_exit_boot_services();
497
#endif
498
499
    size_t mb_mmap_count;
500
    struct memmap_entry *raw_memmap = get_raw_memmap(&mb_mmap_count);
501
502
    if (mb_mmap_count > MEMMAP_MAX) {
503
        panic(false, "multiboot1: Too many memory map entries.");
504
    }
505
506
    size_t mb_mmap_len = mb_mmap_count * sizeof(struct multiboot1_mmap_entry);
507
    struct multiboot1_mmap_entry *mmap = mb1_info_alloc(&mb1_info_raw, mb_mmap_len);
508
509
    // Multiboot is bad and passes raw memmap. We do the same to support it.
510
    for (size_t i = 0; i < mb_mmap_count; i++) {
511
        mmap[i].size = sizeof(struct multiboot1_mmap_entry) - 4;
512
        mmap[i].addr = raw_memmap[i].base;
513
        mmap[i].len  = raw_memmap[i].length;
514
        mmap[i].type = raw_memmap[i].type;
515
    }
516
517
    struct meminfo memory_info = mmap_get_info(mb_mmap_count, raw_memmap);
518
519
    // Convert the uppermem and lowermem fields from bytes to
520
    // KiB.
521
    multiboot1_info->mem_lower = memory_info.lowermem / 1024;
522
    multiboot1_info->mem_upper = memory_info.uppermem / 1024;
523
524
    multiboot1_info->mmap_length = mb_mmap_len;
525
    multiboot1_info->mmap_addr = (uint32_t)(size_t)mmap - mb1_info_slide;
526
    multiboot1_info->flags |= (1 << 0) | (1 << 6);
527
528
    if (rdmsr(0x1b) & (1 << 10)) {
529
        if (x2apic_disable()) {
530
            printv("multiboot1: Firmware had x2APIC enabled, reverted to xAPIC mode\n");
531
        } else {
532
            printv("multiboot1: Firmware has x2APIC enabled and it could not be disabled\n");
533
        }
534
    }
535
536
    iommu_disable_all();
537
538
    irq_flush_type = IRQ_PIC_ONLY_FLUSH;
539
540
    common_spinup(multiboot_spinup_32, 6,
541
                  (uint32_t)(uintptr_t)reloc_stub, (uint32_t)0x2badb002,
542
                  (uint32_t)mb1_info_final_loc, (uint32_t)entry_point,
543
                  (uint32_t)(uintptr_t)ranges, (uint32_t)ranges_count);
544
}
545
546
#endif
tab: 248 wrap: offon