:: limine / common / protos / chainload.c 11.4 KB raw

1
#include <stddef.h>
2
#include <stdint.h>
3
#include <stdnoreturn.h>
4
#include <protos/chainload.h>
5
#include <lib/part.h>
6
#include <lib/config.h>
7
#include <lib/misc.h>
8
#include <drivers/disk.h>
9
#include <lib/term.h>
10
#include <lib/fb.h>
11
#include <lib/uri.h>
12
#include <lib/print.h>
13
#include <lib/libc.h>
14
#include <sys/idt.h>
15
#include <lib/bli.h>
16
#include <drivers/vga_textmode.h>
17
#include <mm/pmm.h>
18
#if defined (UEFI)
19
#  include <efi.h>
20
#endif
21
22
#if defined (BIOS)
23
24
__attribute__((noinline, section(".realmode")))
25
noreturn static void spinup(uint8_t drive, void *buf) {
26
    struct idtr real_mode_idt;
27
    real_mode_idt.limit = 0x3ff;
28
    real_mode_idt.ptr   = 0;
29
30
    asm volatile (
31
        "cli\n\t"
32
        "cld\n\t"
33
34
        // Safe stack location
35
        "mov $0x7c00, %%esp\n\t"
36
37
        // move buffer to final location
38
        "mov $0x7c00, %%edi\n\t"
39
        "mov $512, %%ecx\n\t"
40
        "rep movsb\n\t"
41
42
        "lidt (%%eax)\n\t"
43
44
        "pushl $0x08\n\t"
45
        "pushl $1f\n\t"
46
        "lret\n\t"
47
        "1: .code16\n\t"
48
        "movw $0x10, %%ax\n\t"
49
        "movw %%ax, %%ds\n\t"
50
        "movw %%ax, %%es\n\t"
51
        "movw %%ax, %%fs\n\t"
52
        "movw %%ax, %%gs\n\t"
53
        "movw %%ax, %%ss\n\t"
54
        "movl %%cr0, %%eax\n\t"
55
        "andb $0xfe, %%al\n\t"
56
        "movl %%eax, %%cr0\n\t"
57
        "movl $1f, %%eax\n\t"
58
        "pushw $0\n\t"
59
        "pushw %%ax\n\t"
60
        "lret\n\t"
61
        "1:\n\t"
62
        "xorw %%ax, %%ax\n\t"
63
        "movw %%ax, %%ds\n\t"
64
        "movw %%ax, %%es\n\t"
65
        "movw %%ax, %%fs\n\t"
66
        "movw %%ax, %%gs\n\t"
67
        "movw %%ax, %%ss\n\t"
68
69
        "sti\n\t"
70
71
        "pushw $0\n\t"
72
        "pushw $0x7c00\n\t"
73
        "lret\n\t"
74
75
        ".code32\n\t"
76
        :
77
        : "a" (&real_mode_idt), "d" (drive), "S"(buf)
78
        : "memory"
79
    );
80
81
    __builtin_unreachable();
82
}
83
84
noreturn void chainload(char *config, char *cmdline) {
85
    (void)cmdline;
86
87
    uint64_t val;
88
89
    int part; {
90
        char *part_config = config_get_value(config, 0, "PARTITION");
91
        if (part_config == NULL) {
92
            part = 0;
93
        } else {
94
            val = strtoui(part_config, NULL, 10);
95
            if (val > 256) {
96
                panic(true, "bios: BIOS partition number outside range 0-256");
97
            }
98
            part = val;
99
        }
100
    }
101
    int drive; {
102
        char *drive_config = config_get_value(config, 0, "DRIVE");
103
        if (drive_config == NULL) {
104
            drive = boot_volume->index;
105
        } else {
106
            val = strtoui(drive_config, NULL, 10);
107
            if (val < 1 || val > 256) {
108
                panic(true, "bios: BIOS drive number outside range 1-256");
109
            }
110
            drive = val;
111
        }
112
    }
113
114
    struct volume *p = volume_get_by_coord(false, drive, part);
115
    if (p == NULL && config_get_value(config, 0, "GPT_GUID") == NULL
116
                  && config_get_value(config, 0, "GPT_UUID") == NULL
117
                  && config_get_value(config, 0, "MBR_ID") == NULL) {
118
        panic(true, "bios: Specified drive/partition not found");
119
    }
120
121
    char *gpt_guid_s = config_get_value(config, 0, "GPT_GUID");
122
    if (gpt_guid_s == NULL) {
123
        gpt_guid_s = config_get_value(config, 0, "GPT_UUID");
124
    }
125
    if (gpt_guid_s != NULL) {
126
        struct guid guid;
127
        if (!string_to_guid_be(&guid, gpt_guid_s)) {
128
            panic(true, "bios: Malformed GUID");
129
        }
130
131
        p = volume_get_by_guid(&guid);
132
        if (p == NULL) {
133
            if (!string_to_guid_mixed(&guid, gpt_guid_s)) {
134
                panic(true, "bios: Malformed GUID");
135
            }
136
137
            p = volume_get_by_guid(&guid);
138
        }
139
140
        if (p == NULL) {
141
            panic(true, "bios: No matching GPT drive for GPT_GUID found");
142
        }
143
144
        if (p->partition != 0) {
145
            panic(true, "bios: GPT_GUID is that of a partition, not a drive");
146
        }
147
148
        p = volume_get_by_coord(false, p->index, part);
149
150
        if (p == NULL) {
151
            panic(true, "bios: Partition specified is not valid");
152
        }
153
154
        goto load;
155
    }
156
157
    char *mbr_id_s = config_get_value(config, 0, "MBR_ID");
158
    if (mbr_id_s != NULL) {
159
        uint32_t mbr_id = strtoui(mbr_id_s, NULL, 16);
160
161
        for (size_t i = 0; i < volume_index_i; i++) {
162
            p = volume_index[i];
163
164
            if (!is_valid_mbr(p)) {
165
                continue;
166
            }
167
168
            uint32_t mbr_id_1;
169
            if (!volume_read(p, &mbr_id_1, 0x1b8, sizeof(uint32_t))) {
170
                continue;
171
            }
172
173
            if (mbr_id_1 == mbr_id) {
174
                p = volume_get_by_coord(false, p->index, part);
175
176
                if (p == NULL) {
177
                    panic(true, "bios: Partition specified is not valid");
178
                }
179
180
                goto load;
181
            }
182
        }
183
184
        panic(true, "bios: No matching MBR ID found");
185
    }
186
187
load:
188
    vga_textmode_init(false);
189
190
    void *buf = ext_mem_alloc(512);
191
192
    if (!volume_read(p, buf, 0, 512)) {
193
        panic(true, "bios: Failed to read boot sector");
194
    }
195
196
    uint16_t *boot_sig = (uint16_t *)(buf + 0x1fe);
197
198
    if (*boot_sig != 0xaa55) {
199
        panic(true, "bios: Volume is not bootable");
200
    }
201
202
    spinup(p->drive, buf);
203
}
204
205
#elif defined (UEFI)
206
207
static EFI_DEVICE_PATH_PROTOCOL *build_relative_efi_file_path(struct file_handle *image) {
208
    // The file path stored in EFI_LOADED_IMAGE_PROTOCOL::FilePath is
209
    // expected to be relative to the EFI_LOADED_IMAGE_PROTOCOL::DeviceHandle.
210
    // For this reason the EFI_DEVICE_PATH_PROTOCOL of the efi_part_handle
211
    // is not used as a prefix. This likely also means that the returned
212
    // path cannot be given to gBS->LoadImage() directly.
213
214
    size_t original_path_chars = strlen(image->path);
215
216
    size_t efi_file_path_alloc_len = (original_path_chars + 1) * sizeof(CHAR16);
217
    CHAR16 *efi_file_path = ext_mem_alloc(efi_file_path_alloc_len);
218
219
    bool leading_slash = true;
220
    size_t j = 0;
221
    for (size_t i = 0; i < original_path_chars; i++) {
222
        if (image->path[i] == '/' && leading_slash) {
223
            continue;
224
        }
225
        leading_slash = false;
226
        efi_file_path[j++] = image->path[i] == '/' ? '\\' : image->path[i];
227
    }
228
    efi_file_path[j] = 0;
229
230
231
    size_t efi_file_path_len = ((j + 1) * sizeof(CHAR16));
232
    size_t path_item_len     = sizeof(EFI_DEVICE_PATH_PROTOCOL) + efi_file_path_len;
233
    size_t end_item_len      = sizeof(EFI_DEVICE_PATH_PROTOCOL);
234
    size_t alloc_len         = path_item_len + end_item_len;
235
236
    EFI_DEVICE_PATH_PROTOCOL *device_path;
237
    EFI_STATUS status = gBS->AllocatePool(EfiLoaderData, alloc_len, (void **)&device_path);
238
    if (status) {
239
        panic(true, "efi: AllocatePool() failure (%x)", status);
240
    }
241
242
    FILEPATH_DEVICE_PATH *path_item = (FILEPATH_DEVICE_PATH *)device_path;
243
    path_item->Header.Type      = MEDIA_DEVICE_PATH;
244
    path_item->Header.SubType   = MEDIA_FILEPATH_DP;
245
    path_item->Header.Length[0] = path_item_len;
246
    path_item->Header.Length[1] = path_item_len >> 8;
247
    memcpy(&path_item->PathName, efi_file_path, efi_file_path_len);
248
249
    EFI_DEVICE_PATH_PROTOCOL *end_item = (void *)device_path + path_item_len;
250
    end_item->Type      = END_DEVICE_PATH_TYPE;
251
    end_item->SubType   = END_ENTIRE_DEVICE_PATH_SUBTYPE;
252
    end_item->Length[0] = end_item_len;
253
    end_item->Length[1] = end_item_len >> 8;
254
255
    pmm_free(efi_file_path, efi_file_path_alloc_len);
256
    return device_path;
257
}
258
259
noreturn void chainload(char *config, char *cmdline) {
260
    char *image_path = config_get_value(config, 0, "PATH");
261
    if (image_path == NULL) {
262
        image_path = config_get_value(config, 0, "IMAGE_PATH");
263
    }
264
    if (image_path == NULL) {
265
        panic(true, "efi: Image path not specified");
266
    }
267
268
    // The firmware's LoadImage will verify the Secure Boot signature of the
269
    // chainloaded EFI application, so Limine does not need to enforce its
270
    // own hash check here.
271
    bool saved_secure_boot_active = secure_boot_active;
272
    secure_boot_active = false;
273
274
    struct file_handle *image;
275
    if ((image = uri_open(image_path, MEMMAP_RESERVED, false
276
#if defined (__i386__)
277
        , NULL, NULL
278
#endif
279
    )) == NULL)
280
        panic(true, "efi: Failed to open image with path `%s`. Is the path correct?", image_path);
281
282
    secure_boot_active = saved_secure_boot_active;
283
284
    EFI_STATUS status;
285
286
    EFI_HANDLE efi_part_handle = image->efi_part_handle;
287
288
    void *ptr = image->fd;
289
    size_t image_size = image->size;
290
291
    memmap_alloc_range_in(untouched_memmap, &untouched_memmap_entries,
292
                          (uintptr_t)ptr, ALIGN_UP(image_size, 4096, panic(true, "chainload: Alignment overflow")),
293
                          MEMMAP_RESERVED, MEMMAP_USABLE, true, false, true);
294
295
    EFI_DEVICE_PATH_PROTOCOL *efi_file_path = build_relative_efi_file_path(image);
296
297
    fclose(image);
298
    term_notready();
299
300
    size_t req_width = 0, req_height = 0, req_bpp = 0;
301
302
    char *resolution = config_get_value(config, 0, "RESOLUTION");
303
    if (resolution != NULL)
304
        parse_resolution(&req_width, &req_height, &req_bpp, resolution);
305
306
    struct fb_info *fbinfo;
307
    size_t fb_count;
308
    fb_init(&fbinfo, &fb_count, req_width, req_height, req_bpp, false, false);
309
310
    size_t cmdline_len = strlen(cmdline);
311
    CHAR16 *new_cmdline;
312
    status = gBS->AllocatePool(EfiLoaderData, CHECKED_MUL(cmdline_len + 1, sizeof(CHAR16), panic(true, "efi: Allocation size overflow")), (void **)&new_cmdline);
313
    if (status) {
314
        panic(true, "efi: Allocation failure");
315
    }
316
    for (size_t i = 0; i < cmdline_len + 1; i++) {
317
        new_cmdline[i] = cmdline[i];
318
    }
319
320
    pmm_release_uefi_mem();
321
322
    MEMMAP_DEVICE_PATH memdev_path[2];
323
324
    memdev_path[0].Header.Type      = HARDWARE_DEVICE_PATH;
325
    memdev_path[0].Header.SubType   = HW_MEMMAP_DP;
326
    memdev_path[0].Header.Length[0] = sizeof(MEMMAP_DEVICE_PATH);
327
    memdev_path[0].Header.Length[1] = sizeof(MEMMAP_DEVICE_PATH) >> 8;
328
329
    memdev_path[0].MemoryType       = EfiLoaderCode;
330
    memdev_path[0].StartingAddress  = (uintptr_t)ptr;
331
    memdev_path[0].EndingAddress    = (uintptr_t)ptr + image_size;
332
333
    memdev_path[1].Header.Type      = END_DEVICE_PATH_TYPE;
334
    memdev_path[1].Header.SubType   = END_ENTIRE_DEVICE_PATH_SUBTYPE;
335
    memdev_path[1].Header.Length[0] = sizeof(EFI_DEVICE_PATH);
336
    memdev_path[1].Header.Length[1] = sizeof(EFI_DEVICE_PATH) >> 8;
337
338
    EFI_HANDLE new_handle = 0;
339
340
    status = gBS->LoadImage(0, efi_image_handle,
341
                            (EFI_DEVICE_PATH *)memdev_path,
342
                            ptr, image_size, &new_handle);
343
    if (status) {
344
        panic(false, "efi: LoadImage failure (%X)", (uint64_t)status);
345
    }
346
347
    EFI_GUID loaded_img_prot_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
348
349
    EFI_LOADED_IMAGE_PROTOCOL *new_handle_loaded_image = NULL;
350
    status = gBS->HandleProtocol(new_handle, &loaded_img_prot_guid,
351
                                 (void **)&new_handle_loaded_image);
352
    if (status) {
353
        panic(false, "efi: HandleProtocol failure (%X)", (uint64_t)status);
354
    }
355
356
    if (efi_part_handle != 0) {
357
        new_handle_loaded_image->DeviceHandle = efi_part_handle;
358
    }
359
360
    new_handle_loaded_image->FilePath = efi_file_path;
361
362
    new_handle_loaded_image->LoadOptionsSize = (cmdline_len + 1) * sizeof(CHAR16);
363
    new_handle_loaded_image->LoadOptions = new_cmdline;
364
365
    bli_on_boot();
366
367
    UINTN exit_data_size = 0;
368
    CHAR16 *exit_data = NULL;
369
    EFI_STATUS exit_status = gBS->StartImage(new_handle, &exit_data_size, &exit_data);
370
371
    status = gBS->Exit(efi_image_handle, exit_status, exit_data_size, exit_data);
372
    if (status) {
373
        panic(false, "efi: Exit failure (%X)", (uint64_t)status);
374
    }
375
376
    __builtin_unreachable();
377
}
378
379
#endif
tab: 248 wrap: offon