Home | History | Annotate | Download | only in uefi
      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);
     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)));
    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 
    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 
    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 
    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