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