• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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