xref: /aosp_15_r20/external/avb/examples/uefi/uefi_avb_boot.c (revision d289c2ba6de359471b23d594623b906876bc48a0)
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_error("Unexpected partition name '", boot->partition_name, "'.\n");
146     ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
147     goto out;
148   }
149 
150   header = (const boot_img_hdr*)boot->data;
151 
152   /* Check boot image header magic field. */
153   if (avb_memcmp(BOOT_MAGIC, header->magic, BOOT_MAGIC_SIZE)) {
154     avb_error("Wrong boot image header magic.\n");
155     ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
156     goto out;
157   }
158 
159   /* Sanity check header. */
160   total_size = header->kernel_size;
161   if (!avb_safe_add_to(&total_size, header->ramdisk_size) ||
162       !avb_safe_add_to(&total_size, header->second_size)) {
163     avb_error("Overflow while adding sizes of kernel and initramfs.\n");
164     ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
165     goto out;
166   }
167   if (total_size > boot->data_size) {
168     avb_error("Invalid kernel/initramfs sizes.\n");
169     ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT;
170     goto out;
171   }
172 
173   /* The kernel has to be in its own specific memory pool. */
174   err = uefi_call_wrapper(BS->AllocatePool,
175                           NUM_ARGS_ALLOCATE_POOL,
176                           EfiLoaderCode,
177                           header->kernel_size,
178                           &kernel_buf);
179   if (EFI_ERROR(err)) {
180     avb_error("Could not allocate kernel buffer.\n");
181     ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
182     goto out;
183   }
184   avb_memcpy(kernel_buf, boot->data + header->page_size, header->kernel_size);
185 
186   /* Ditto for the initrd. */
187   initramfs_buf = NULL;
188   initramfs_size = header->ramdisk_size + header->second_size;
189   if (initramfs_size > 0) {
190     err = uefi_call_wrapper(BS->AllocatePool,
191                             NUM_ARGS_ALLOCATE_POOL,
192                             EfiLoaderCode,
193                             initramfs_size,
194                             &initramfs_buf);
195     if (EFI_ERROR(err)) {
196       avb_error("Could not allocate initrd buffer.\n");
197       ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
198       goto out;
199     }
200     /* Concatente the first and second initramfs. */
201     offset = header->page_size;
202     offset += round_up(header->kernel_size, header->page_size);
203     avb_memcpy(initramfs_buf, boot->data + offset, header->ramdisk_size);
204     offset += round_up(header->ramdisk_size, header->page_size);
205     avb_memcpy(initramfs_buf, boot->data + offset, header->second_size);
206   }
207 
208   /* Prepare the command-line. */
209   cmdline_first_len = avb_strlen((const char*)header->cmdline);
210   cmdline_second_len = avb_strlen(slot_data->cmdline);
211   cmdline_extra_len = cmdline_extra != NULL ? avb_strlen(cmdline_extra) : 0;
212   if (cmdline_extra_len > 0) {
213     cmdline_extra_len += 1;
214   }
215   cmdline_utf8_len =
216       cmdline_first_len + 1 + cmdline_second_len + 1 + cmdline_extra_len;
217   err = uefi_call_wrapper(BS->AllocatePool,
218                           NUM_ARGS_ALLOCATE_POOL,
219                           EfiLoaderCode,
220                           cmdline_utf8_len,
221                           &cmdline_utf8);
222   if (EFI_ERROR(err)) {
223     avb_error("Could not allocate kernel cmdline.\n");
224     ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
225     goto out;
226   }
227   offset = 0;
228   avb_memcpy(cmdline_utf8, header->cmdline, cmdline_first_len);
229   offset += cmdline_first_len;
230   cmdline_utf8[offset] = ' ';
231   offset += 1;
232   avb_memcpy(cmdline_utf8 + offset, slot_data->cmdline, cmdline_second_len);
233   offset += cmdline_second_len;
234   if (cmdline_extra_len > 0) {
235     cmdline_utf8[offset] = ' ';
236     avb_memcpy(cmdline_utf8 + offset + 1, cmdline_extra, cmdline_extra_len - 1);
237     offset += cmdline_extra_len;
238   }
239   cmdline_utf8[offset] = '\0';
240   offset += 1;
241   avb_assert(offset == cmdline_utf8_len);
242 
243   /* Now set up the EFI handover. */
244   image_setup = (struct SetupHeader*)kernel_buf;
245   if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC) {
246     avb_error("Wrong kernel header magic.\n");
247     ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
248     goto out;
249   }
250 
251   if (image_setup->version < 0x20b) {
252     avb_error("Wrong version.\n");
253     ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
254     goto out;
255   }
256 
257   if (!image_setup->relocatable_kernel) {
258     avb_error("Kernel is not relocatable.\n");
259     ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT;
260     goto out;
261   }
262 
263   addr = 0x3fffffff;
264   err = uefi_call_wrapper(BS->AllocatePages,
265                           4,
266                           AllocateMaxAddress,
267                           EfiLoaderData,
268                           EFI_SIZE_TO_PAGES(0x4000),
269                           &addr);
270   if (EFI_ERROR(err)) {
271     avb_error("Could not allocate setup buffer.\n");
272     ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM;
273     goto out;
274   }
275   setup = (struct SetupHeader*)(UINTN)addr;
276   avb_memset(setup, '\0', 0x4000);
277   avb_memcpy(setup, image_setup, sizeof(struct SetupHeader));
278   setup->loader_id = 0xff;
279   setup->code32_start =
280       ((uintptr_t)kernel_buf) + (image_setup->setup_secs + 1) * 512;
281   setup->cmd_line_ptr = (uintptr_t)cmdline_utf8;
282 
283   setup->ramdisk_start = (uintptr_t)initramfs_buf;
284   setup->ramdisk_len = (uintptr_t)initramfs_size;
285 
286   /* Jump to the kernel. */
287   linux_efi_handover(efi_image_handle, setup);
288 
289   ret = UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_START_KERNEL;
290 out:
291   return ret;
292 }
293 
uefi_avb_boot_kernel_result_to_string(UEFIAvbBootKernelResult result)294 const char* uefi_avb_boot_kernel_result_to_string(
295     UEFIAvbBootKernelResult result) {
296   const char* ret = NULL;
297 
298   switch (result) {
299     case UEFI_AVB_BOOT_KERNEL_RESULT_OK:
300       ret = "OK";
301       break;
302     case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_OOM:
303       ret = "ERROR_OEM";
304       break;
305     case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_IO:
306       ret = "ERROR_IO";
307       break;
308     case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_PARTITION_INVALID_FORMAT:
309       ret = "ERROR_PARTITION_INVALID_FORMAT";
310       break;
311     case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_KERNEL_INVALID_FORMAT:
312       ret = "ERROR_KERNEL_INVALID_FORMAT";
313       break;
314     case UEFI_AVB_BOOT_KERNEL_RESULT_ERROR_START_KERNEL:
315       ret = "ERROR_START_KERNEL";
316       break;
317       /* Do not add a 'default:' case here because of -Wswitch. */
318   }
319 
320   if (ret == NULL) {
321     avb_error("Unknown UEFIAvbBootKernelResult value.\n");
322     ret = "(unknown)";
323   }
324 
325   return ret;
326 }
327