1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (c) 2020, Linaro Limited
4 */
5
6 #define LOG_CATEGORY LOGC_EFI
7 #include <common.h>
8 #include <efi_loader.h>
9 #include <efi_load_initrd.h>
10 #include <efi_variable.h>
11 #include <fs.h>
12 #include <malloc.h>
13 #include <mapmem.h>
14
15 static efi_status_t EFIAPI
16 efi_load_file2_initrd(struct efi_load_file_protocol *this,
17 struct efi_device_path *file_path, bool boot_policy,
18 efi_uintn_t *buffer_size, void *buffer);
19
20 static const struct efi_load_file_protocol efi_lf2_protocol = {
21 .load_file = efi_load_file2_initrd,
22 };
23
24 /*
25 * Device path defined by Linux to identify the handle providing the
26 * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
27 */
28 static const struct efi_initrd_dp dp_lf2_handle = {
29 .vendor = {
30 {
31 DEVICE_PATH_TYPE_MEDIA_DEVICE,
32 DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
33 sizeof(dp_lf2_handle.vendor),
34 },
35 EFI_INITRD_MEDIA_GUID,
36 },
37 .end = {
38 DEVICE_PATH_TYPE_END,
39 DEVICE_PATH_SUB_TYPE_END,
40 sizeof(dp_lf2_handle.end),
41 }
42 };
43
44 static efi_handle_t efi_initrd_handle;
45
46 /**
47 * get_initrd_fp() - Get initrd device path from a FilePathList device path
48 *
49 * @initrd_fp: the final initrd filepath
50 *
51 * Return: status code. Caller must free initrd_fp
52 */
get_initrd_fp(struct efi_device_path ** initrd_fp)53 static efi_status_t get_initrd_fp(struct efi_device_path **initrd_fp)
54 {
55 struct efi_device_path *dp = NULL;
56
57 /*
58 * if bootmgr is setup with and initrd, the device path will be
59 * in the FilePathList[] of our load options in Boot####.
60 * The first device path of the multi instance device path will
61 * start with a VenMedia and the initrds will follow.
62 *
63 * If the device path is not found return EFI_INVALID_PARAMETER.
64 * We can then use this specific return value and not install the
65 * protocol, while allowing the boot to continue
66 */
67 dp = efi_get_dp_from_boot(efi_lf2_initrd_guid);
68 if (!dp)
69 return EFI_INVALID_PARAMETER;
70
71 *initrd_fp = dp;
72 return EFI_SUCCESS;
73 }
74
75 /**
76 * efi_load_file2_initrd() - load initial RAM disk
77 *
78 * This function implements the LoadFile service of the EFI_LOAD_FILE2_PROTOCOL
79 * in order to load an initial RAM disk requested by the Linux kernel stub.
80 *
81 * See the UEFI spec for details.
82 *
83 * @this: EFI_LOAD_FILE2_PROTOCOL instance
84 * @file_path: media device path of the file, "" in this case
85 * @boot_policy: must be false
86 * @buffer_size: size of allocated buffer
87 * @buffer: buffer to load the file
88 *
89 * Return: status code
90 */
91 static efi_status_t EFIAPI
efi_load_file2_initrd(struct efi_load_file_protocol * this,struct efi_device_path * file_path,bool boot_policy,efi_uintn_t * buffer_size,void * buffer)92 efi_load_file2_initrd(struct efi_load_file_protocol *this,
93 struct efi_device_path *file_path, bool boot_policy,
94 efi_uintn_t *buffer_size, void *buffer)
95 {
96 struct efi_device_path *initrd_fp = NULL;
97 efi_status_t ret = EFI_NOT_FOUND;
98 struct efi_file_handle *f = NULL;
99 efi_uintn_t bs;
100
101 EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
102 buffer_size, buffer);
103
104 if (!this || this != &efi_lf2_protocol ||
105 !buffer_size) {
106 ret = EFI_INVALID_PARAMETER;
107 goto out;
108 }
109
110 if (file_path->type != dp_lf2_handle.end.type ||
111 file_path->sub_type != dp_lf2_handle.end.sub_type) {
112 ret = EFI_INVALID_PARAMETER;
113 goto out;
114 }
115
116 if (boot_policy) {
117 ret = EFI_UNSUPPORTED;
118 goto out;
119 }
120
121 ret = get_initrd_fp(&initrd_fp);
122 if (ret != EFI_SUCCESS)
123 goto out;
124
125 /* Open file */
126 f = efi_file_from_path(initrd_fp);
127 if (!f) {
128 log_err("Can't find initrd specified in Boot####\n");
129 ret = EFI_NOT_FOUND;
130 goto out;
131 }
132
133 /* Get file size */
134 ret = efi_file_size(f, &bs);
135 if (ret != EFI_SUCCESS)
136 goto out;
137
138 if (!buffer || *buffer_size < bs) {
139 ret = EFI_BUFFER_TOO_SMALL;
140 *buffer_size = bs;
141 } else {
142 ret = EFI_CALL(f->read(f, &bs, (void *)(uintptr_t)buffer));
143 *buffer_size = bs;
144 }
145
146 out:
147 efi_free_pool(initrd_fp);
148 if (f)
149 EFI_CALL(f->close(f));
150 return EFI_EXIT(ret);
151 }
152
153 /**
154 * check_initrd() - Determine if the file defined as an initrd in Boot####
155 * load_options device path is present
156 *
157 * Return: status code
158 */
check_initrd(void)159 static efi_status_t check_initrd(void)
160 {
161 struct efi_device_path *initrd_fp = NULL;
162 struct efi_file_handle *f;
163 efi_status_t ret;
164
165 ret = get_initrd_fp(&initrd_fp);
166 if (ret != EFI_SUCCESS)
167 goto out;
168
169 /*
170 * If the file is not found, but the file path is set, return an error
171 * and trigger the bootmgr fallback
172 */
173 f = efi_file_from_path(initrd_fp);
174 if (!f) {
175 log_err("Can't find initrd specified in Boot####\n");
176 ret = EFI_NOT_FOUND;
177 goto out;
178 }
179
180 EFI_CALL(f->close(f));
181
182 out:
183 efi_free_pool(initrd_fp);
184 return ret;
185 }
186
187 /**
188 * efi_initrd_register() - create handle for loading initial RAM disk
189 *
190 * This function creates a new handle and installs a Linux specific vendor
191 * device path and an EFI_LOAD_FILE2_PROTOCOL. Linux uses the device path
192 * to identify the handle and then calls the LoadFile service of the
193 * EFI_LOAD_FILE2_PROTOCOL to read the initial RAM disk.
194 *
195 * Return: status code
196 */
efi_initrd_register(void)197 efi_status_t efi_initrd_register(void)
198 {
199 efi_status_t ret;
200
201 /*
202 * Allow the user to continue if Boot#### file path is not set for
203 * an initrd
204 */
205 ret = check_initrd();
206 if (ret == EFI_INVALID_PARAMETER)
207 return EFI_SUCCESS;
208 if (ret != EFI_SUCCESS)
209 return ret;
210
211 ret = EFI_CALL(efi_install_multiple_protocol_interfaces
212 (&efi_initrd_handle,
213 /* initramfs */
214 &efi_guid_device_path, &dp_lf2_handle,
215 /* LOAD_FILE2 */
216 &efi_guid_load_file2_protocol,
217 (void *)&efi_lf2_protocol,
218 NULL));
219
220 return ret;
221 }
222
223 /**
224 * efi_initrd_deregister() - delete the handle for loading initial RAM disk
225 *
226 * This will delete the handle containing the Linux specific vendor device
227 * path and EFI_LOAD_FILE2_PROTOCOL for loading an initrd
228 *
229 * Return: status code
230 */
efi_initrd_deregister(void)231 void efi_initrd_deregister(void)
232 {
233 efi_delete_handle(efi_initrd_handle);
234 efi_initrd_handle = NULL;
235 }
236