chiark / gitweb /
809c69310e32bca9275a52bbcd610202ba96826e
[elogind.git] / src / boot / efi / linux.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /*
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 2.1 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * Copyright (C) 2015 Kay Sievers <kay@vrfy.org>
15  */
16
17 #include <efi.h>
18 #include <efilib.h>
19
20 #include "util.h"
21 #include "linux.h"
22
23 #define SETUP_MAGIC             0x53726448      /* "HdrS" */
24 struct SetupHeader {
25         UINT8 boot_sector[0x01f1];
26         UINT8 setup_secs;
27         UINT16 root_flags;
28         UINT32 sys_size;
29         UINT16 ram_size;
30         UINT16 video_mode;
31         UINT16 root_dev;
32         UINT16 signature;
33         UINT16 jump;
34         UINT32 header;
35         UINT16 version;
36         UINT16 su_switch;
37         UINT16 setup_seg;
38         UINT16 start_sys;
39         UINT16 kernel_ver;
40         UINT8 loader_id;
41         UINT8 load_flags;
42         UINT16 movesize;
43         UINT32 code32_start;
44         UINT32 ramdisk_start;
45         UINT32 ramdisk_len;
46         UINT32 bootsect_kludge;
47         UINT16 heap_end;
48         UINT8 ext_loader_ver;
49         UINT8 ext_loader_type;
50         UINT32 cmd_line_ptr;
51         UINT32 ramdisk_max;
52         UINT32 kernel_alignment;
53         UINT8 relocatable_kernel;
54         UINT8 min_alignment;
55         UINT16 xloadflags;
56         UINT32 cmdline_size;
57         UINT32 hardware_subarch;
58         UINT64 hardware_subarch_data;
59         UINT32 payload_offset;
60         UINT32 payload_length;
61         UINT64 setup_data;
62         UINT64 pref_address;
63         UINT32 init_size;
64         UINT32 handover_offset;
65 } __attribute__((packed));
66
67 #ifdef __x86_64__
68 typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup);
69 static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) {
70         handover_f handover;
71
72         asm volatile ("cli");
73         handover = (handover_f)((UINTN)setup->code32_start + 512 + setup->handover_offset);
74         handover(image, ST, setup);
75 }
76 #else
77 typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup) __attribute__((regparm(0)));
78 static inline VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) {
79         handover_f handover;
80
81         handover = (handover_f)((UINTN)setup->code32_start + setup->handover_offset);
82         handover(image, ST, setup);
83 }
84 #endif
85
86 EFI_STATUS linux_exec(EFI_HANDLE *image,
87                       CHAR8 *cmdline, UINTN cmdline_len,
88                       UINTN linux_addr,
89                       UINTN initrd_addr, UINTN initrd_size) {
90         struct SetupHeader *image_setup;
91         struct SetupHeader *boot_setup;
92         EFI_PHYSICAL_ADDRESS addr;
93         EFI_STATUS err;
94
95         image_setup = (struct SetupHeader *)(linux_addr);
96         if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC)
97                 return EFI_LOAD_ERROR;
98
99         if (image_setup->version < 0x20b || !image_setup->relocatable_kernel)
100                 return EFI_LOAD_ERROR;
101
102         addr = 0x3fffffff;
103         err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
104                                 EFI_SIZE_TO_PAGES(0x4000), &addr);
105         if (EFI_ERROR(err))
106                 return err;
107         boot_setup = (struct SetupHeader *)(UINTN)addr;
108         ZeroMem(boot_setup, 0x4000);
109         CopyMem(boot_setup, image_setup, sizeof(struct SetupHeader));
110         boot_setup->loader_id = 0xff;
111
112         boot_setup->code32_start = (UINT32)linux_addr + (image_setup->setup_secs+1) * 512;
113
114         if (cmdline) {
115                 addr = 0xA0000;
116                 err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData,
117                                         EFI_SIZE_TO_PAGES(cmdline_len + 1), &addr);
118                 if (EFI_ERROR(err))
119                         return err;
120                 CopyMem((VOID *)(UINTN)addr, cmdline, cmdline_len);
121                 ((CHAR8 *)addr)[cmdline_len] = 0;
122                 boot_setup->cmd_line_ptr = (UINT32)addr;
123         }
124
125         boot_setup->ramdisk_start = (UINT32)initrd_addr;
126         boot_setup->ramdisk_len = (UINT32)initrd_size;
127
128         linux_efi_handover(image, boot_setup);
129         return EFI_LOAD_ERROR;
130 }