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