1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 #include <efi.h>
26 #include <efilib.h>
27
28 #include "bootimg.h"
29
30 #include "uefi_avb_boot.h"
31 #include "uefi_avb_util.h"
32
33 /* See Documentation/x86/boot.txt for this struct and more information
34 * about the boot/handover protocol.
35 */
36
37 #define SETUP_MAGIC 0x53726448 /* "HdrS" */
38
39 struct SetupHeader {
40 UINT8 boot_sector[0x01f1];
41 UINT8 setup_secs;
42 UINT16 root_flags;
43 UINT32 sys_size;
44 UINT16 ram_size;
45 UINT16 video_mode;
46 UINT16 root_dev;
47 UINT16 signature;
48 UINT16 jump;
49 UINT32 header;
50 UINT16 version;
51 UINT16 su_switch;
52 UINT16 setup_seg;
53 UINT16 start_sys;
54 UINT16 kernel_ver;
55 UINT8 loader_id;
56 UINT8 load_flags;
57 UINT16 movesize;
58 UINT32 code32_start;
59 UINT32 ramdisk_start;
60 UINT32 ramdisk_len;
61 UINT32 bootsect_kludge;
62 UINT16 heap_end;
63 UINT8 ext_loader_ver;
64 UINT8 ext_loader_type;
65 UINT32 cmd_line_ptr;
66 UINT32 ramdisk_max;
67 UINT32 kernel_alignment;
68 UINT8 relocatable_kernel;
69 UINT8 min_alignment;
70 UINT16 xloadflags;
71 UINT32 cmdline_size;
72 UINT32 hardware_subarch;
73 UINT64 hardware_subarch_data;
74 UINT32 payload_offset;
75 UINT32 payload_length;
76 UINT64 setup_data;
77 UINT64 pref_address;
78 UINT32 init_size;
79 UINT32 handover_offset;
80 } __attribute__((packed));
81
82 #ifdef __x86_64__
83 typedef VOID (*handover_f)(VOID* image,
84 EFI_SYSTEM_TABLE* table,
85 struct SetupHeader* setup);
linux_efi_handover(EFI_HANDLE image,struct SetupHeader * setup)86 static inline VOID linux_efi_handover(EFI_HANDLE image,
87 struct SetupHeader* setup) {
88 handover_f handover;
89
90 asm volatile("cli");
91 handover =
92 (handover_f)((UINTN)setup->code32_start + 512 + setup->handover_offset);
93 handover(image, ST, setup);
94 }
95 #else
96 typedef VOID (*handover_f)(VOID* image,
97 EFI_SYSTEM_TABLE* table,
98 struct SetupHeader* setup)
99 __attribute__((regparm(0)));
linux_efi_handover(EFI_HANDLE image,struct SetupHeader * setup)100 static inline VOID linux_efi_handover(EFI_HANDLE image,
101 struct SetupHeader* setup) {
102 handover_f handover;
103
104 handover = (handover_f)((UINTN)setup->code32_start + setup->handover_offset);
105 handover(image, ST, setup);
106 }
107 #endif
108
round_up(size_t value,size_t size)109 static size_t round_up(size_t value, size_t size) {
110 size_t ret = value + size - 1;
111 ret /= size;
112 ret *= size;
113 return ret;
114 }
115
uefi_avb_boot_kernel(EFI_HANDLE efi_image_handle,AvbSlotVerifyData * slot_data,const char * cmdline_extra)116 UEFIAvbBootKernelResult uefi_avb_boot_kernel(EFI_HANDLE efi_image_handle,
117 AvbSlotVerifyData* slot_data,
118 const char* cmdline_extra) {
119 UEFIAvbBootKernelResult ret;
120 const boot_img_hdr* header;
121 EFI_STATUS err;
122 UINT8* kernel_buf = NULL;
123 UINT8* initramfs_buf = NULL;
124 UINT8* cmdline_utf8 = NULL;
125 AvbPartitionData* boot;
126 size_t offset;
127 uint64_t total_size;
128 size_t initramfs_size;
129 size_t cmdline_first_len;
130 size_t cmdline_second_len;
131 size_t cmdline_extra_len;
132 size_t cmdline_utf8_len;
133 struct SetupHeader* image_setup;
134 struct SetupHeader* setup;
135 EFI_PHYSICAL_ADDRESS addr;
136
137 if (slot_data->num_loaded_partitions != 1) {
138 avb_error("No boot partition.\n");
139 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
140 goto out;
141 }
142
143 boot = &slot_data->loaded_partitions[0];
144 if (avb_strcmp(boot->partition_name, "boot") != 0) {
145 avb_errorv(
146 "Unexpected partition name '", boot->partition_name, "'.\n", NULL);
147 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
148 goto out;
149 }
150
151 header = (const boot_img_hdr*)boot->data;
152
153 /* Check boot image header magic field. */
154 if (avb_memcmp(BOOT_MAGIC, header->magic, BOOT_MAGIC_SIZE)) {
155 avb_error("Wrong boot image header magic.\n");
156 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
157 goto out;
158 }
159
160 /* Sanity check header. */
161 total_size = header->kernel_size;
162 if (!avb_safe_add_to(&total_size, header->ramdisk_size) ||
163 !avb_safe_add_to(&total_size, header->second_size)) {
164 avb_error("Overflow while adding sizes of kernel and initramfs.\n");
165 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
166 goto out;
167 }
168 if (total_size > boot->data_size) {
169 avb_error("Invalid kernel/initramfs sizes.\n");
170 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
171 goto out;
172 }
173
174 /* The kernel has to be in its own specific memory pool. */
175 err = uefi_call_wrapper(BS->AllocatePool,
176 NUM_ARGS_ALLOCATE_POOL,
177 EfiLoaderCode,
178 header->kernel_size,
179 &kernel_buf);
180 if (EFI_ERROR(err)) {
181 avb_error("Could not allocate kernel buffer.\n");
182 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
183 goto out;
184 }
185 avb_memcpy(kernel_buf, boot->data + header->page_size, header->kernel_size);
186
187 /* Ditto for the initrd. */
188 initramfs_buf = NULL;
189 initramfs_size = header->ramdisk_size + header->second_size;
190 if (initramfs_size > 0) {
191 err = uefi_call_wrapper(BS->AllocatePool,
192 NUM_ARGS_ALLOCATE_POOL,
193 EfiLoaderCode,
194 initramfs_size,
195 &initramfs_buf);
196 if (EFI_ERROR(err)) {
197 avb_error("Could not allocate initrd buffer.\n");
198 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
199 goto out;
200 }
201 /* Concatente the first and second initramfs. */
202 offset = header->page_size;
203 offset += round_up(header->kernel_size, header->page_size);
204 avb_memcpy(initramfs_buf, boot->data + offset, header->ramdisk_size);
205 offset += round_up(header->ramdisk_size, header->page_size);
206 avb_memcpy(initramfs_buf, boot->data + offset, header->second_size);
207 }
208
209 /* Prepare the command-line. */
210 cmdline_first_len = avb_strlen((const char*)header->cmdline);
211 cmdline_second_len = avb_strlen(slot_data->cmdline);
212 cmdline_extra_len = cmdline_extra != NULL ? avb_strlen(cmdline_extra) : 0;
213 if (cmdline_extra_len > 0) {
214 cmdline_extra_len += 1;
215 }
216 cmdline_utf8_len =
217 cmdline_first_len + 1 + cmdline_second_len + 1 + cmdline_extra_len;
218 err = uefi_call_wrapper(BS->AllocatePool,
219 NUM_ARGS_ALLOCATE_POOL,
220 EfiLoaderCode,
221 cmdline_utf8_len,
222 &cmdline_utf8);
223 if (EFI_ERROR(err)) {
224 avb_error("Could not allocate kernel cmdline.\n");
225 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
226 goto out;
227 }
228 offset = 0;
229 avb_memcpy(cmdline_utf8, header->cmdline, cmdline_first_len);
230 offset += cmdline_first_len;
231 cmdline_utf8[offset] = ' ';
232 offset += 1;
233 avb_memcpy(cmdline_utf8 + offset, slot_data->cmdline, cmdline_second_len);
234 offset += cmdline_second_len;
235 if (cmdline_extra_len > 0) {
236 cmdline_utf8[offset] = ' ';
237 avb_memcpy(cmdline_utf8 + offset + 1, cmdline_extra, cmdline_extra_len - 1);
238 offset += cmdline_extra_len;
239 }
240 cmdline_utf8[offset] = '\0';
241 offset += 1;
242 avb_assert(offset == cmdline_utf8_len);
243
244 /* Now set up the EFI handover. */
245 image_setup = (struct SetupHeader*)kernel_buf;
246 if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC) {
247 avb_error("Wrong kernel header magic.\n");
248 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
249 goto out;
250 }
251
252 if (image_setup->version < 0x20b) {
253 avb_error("Wrong version.\n");
254 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
255 goto out;
256 }
257
258 if (!image_setup->relocatable_kernel) {
259 avb_error("Kernel is not relocatable.\n");
260 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
261 goto out;
262 }
263
264 addr = 0x3fffffff;
265 err = uefi_call_wrapper(BS->AllocatePages,
266 4,
267 AllocateMaxAddress,
268 EfiLoaderData,
269 EFI_SIZE_TO_PAGES(0x4000),
270 &addr);
271 if (EFI_ERROR(err)) {
272 avb_error("Could not allocate setup buffer.\n");
273 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
274 goto out;
275 }
276 setup = (struct SetupHeader*)(UINTN)addr;
277 avb_memset(setup, '\0', 0x4000);
278 avb_memcpy(setup, image_setup, sizeof(struct SetupHeader));
279 setup->loader_id = 0xff;
280 setup->code32_start =
281 ((uintptr_t)kernel_buf) + (image_setup->setup_secs + 1) * 512;
282 setup->cmd_line_ptr = (uintptr_t)cmdline_utf8;
283
284 setup->ramdisk_start = (uintptr_t)initramfs_buf;
285 setup->ramdisk_len = (uintptr_t)initramfs_size;
286
287 /* Jump to the kernel. */
288 linux_efi_handover(efi_image_handle, setup);
289
290 ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_START_KERNEL;
291 out:
292 return ret;
293 }
294
uefi_avb_boot_kernel_result_to_string(UEFIAvbBootKernelResult result)295 const char* uefi_avb_boot_kernel_result_to_string(
296 UEFIAvbBootKernelResult result) {
297 const char* ret = NULL;
298
299 switch (result) {
300 case UEFI_AVB_BOOT_KERNEL_RESULT_OK:
301 ret = "OK";
302 break;
303 case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM:
304 ret = "ERROR_OEM";
305 break;
306 case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_IO:
307 ret = "ERROR_IO";
308 break;
309 case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT:
310 ret = "ERROR_PARTITION_INVALID_FORMAT";
311 break;
312 case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT:
313 ret = "ERROR_KERNEL_INVALID_FORMAT";
314 break;
315 case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_START_KERNEL:
316 ret = "ERROR_START_KERNEL";
317 break;
318 /* Do not add a 'default:' case here because of -Wswitch. */
319 }
320
321 if (ret == NULL) {
322 avb_error("Unknown UEFIAvbBootKernelResult value.\n");
323 ret = "(unknown)";
324 }
325
326 return ret;
327 }
328