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
39struct 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__
83typedef VOID (*handover_f)(VOID* image,
84                           EFI_SYSTEM_TABLE* table,
85                           struct SetupHeader* setup);
86static 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
96typedef VOID (*handover_f)(VOID* image,
97                           EFI_SYSTEM_TABLE* table,
98                           struct SetupHeader* setup)
99    __attribute__((regparm(0)));
100static 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
109static 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
116UEFIAvbBootKernelResult 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;
291out:
292  return ret;
293}
294
295const 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