1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2020, Linaro Limited
4  */
5 
6 #include <common.h>
7 #include <efi_loader.h>
8 #include <efi_load_initrd.h>
9 #include <fs.h>
10 #include <malloc.h>
11 #include <mapmem.h>
12 
13 static efi_status_t EFIAPI
14 efi_load_file2_initrd(struct efi_load_file_protocol *this,
15 		      struct efi_device_path *file_path, bool boot_policy,
16 		      efi_uintn_t *buffer_size, void *buffer);
17 
18 static const struct efi_load_file_protocol efi_lf2_protocol = {
19 	.load_file = efi_load_file2_initrd,
20 };
21 
22 /*
23  * Device path defined by Linux to identify the handle providing the
24  * EFI_LOAD_FILE2_PROTOCOL used for loading the initial ramdisk.
25  */
26 static const struct efi_initrd_dp dp = {
27 	.vendor = {
28 		{
29 		   DEVICE_PATH_TYPE_MEDIA_DEVICE,
30 		   DEVICE_PATH_SUB_TYPE_VENDOR_PATH,
31 		   sizeof(dp.vendor),
32 		},
33 		EFI_INITRD_MEDIA_GUID,
34 	},
35 	.end = {
36 		DEVICE_PATH_TYPE_END,
37 		DEVICE_PATH_SUB_TYPE_END,
38 		sizeof(dp.end),
39 	}
40 };
41 
42 /**
43  * get_file_size() - retrieve the size of initramfs, set efi status on error
44  *
45  * @dev:			device to read from, e.g. "mmc"
46  * @part:			device partition, e.g. "0:1"
47  * @file:			name of file
48  * @status:			EFI exit code in case of failure
49  *
50  * Return:			size of file
51  */
get_file_size(const char * dev,const char * part,const char * file,efi_status_t * status)52 static loff_t get_file_size(const char *dev, const char *part, const char *file,
53 			    efi_status_t *status)
54 {
55 	loff_t sz = 0;
56 	int ret;
57 
58 	ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
59 	if (ret) {
60 		*status = EFI_NO_MEDIA;
61 		goto out;
62 	}
63 
64 	ret = fs_size(file, &sz);
65 	if (ret) {
66 		sz = 0;
67 		*status = EFI_NOT_FOUND;
68 		goto out;
69 	}
70 
71 out:
72 	return sz;
73 }
74 
75 /**
76  * efi_load_file2initrd() - 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 	char *filespec;
97 	efi_status_t status = EFI_NOT_FOUND;
98 	loff_t file_sz = 0, read_sz = 0;
99 	char *dev, *part, *file;
100 	char *pos;
101 	int ret;
102 
103 	EFI_ENTRY("%p, %p, %d, %p, %p", this, file_path, boot_policy,
104 		  buffer_size, buffer);
105 
106 	filespec = strdup(CONFIG_EFI_INITRD_FILESPEC);
107 	if (!filespec)
108 		goto out;
109 	pos = filespec;
110 
111 	if (!this || this != &efi_lf2_protocol ||
112 	    !buffer_size) {
113 		status = EFI_INVALID_PARAMETER;
114 		goto out;
115 	}
116 
117 	if (file_path->type != dp.end.type ||
118 	    file_path->sub_type != dp.end.sub_type) {
119 		status = EFI_INVALID_PARAMETER;
120 		goto out;
121 	}
122 
123 	if (boot_policy) {
124 		status = EFI_UNSUPPORTED;
125 		goto out;
126 	}
127 
128 	/*
129 	 * expect a string with three space separated parts:
130 	 *
131 	 * * a block device type, e.g. "mmc"
132 	 * * a device and partition identifier, e.g. "0:1"
133 	 * * a file path on the block device, e.g. "/boot/initrd.cpio.gz"
134 	 */
135 	dev = strsep(&pos, " ");
136 	if (!dev)
137 		goto out;
138 	part = strsep(&pos, " ");
139 	if (!part)
140 		goto out;
141 	file = strsep(&pos, " ");
142 	if (!file)
143 		goto out;
144 
145 	file_sz = get_file_size(dev, part, file, &status);
146 	if (!file_sz)
147 		goto out;
148 
149 	if (!buffer || *buffer_size < file_sz) {
150 		status = EFI_BUFFER_TOO_SMALL;
151 		*buffer_size = file_sz;
152 	} else {
153 		ret = fs_set_blk_dev(dev, part, FS_TYPE_ANY);
154 		if (ret) {
155 			status = EFI_NO_MEDIA;
156 			goto out;
157 		}
158 
159 		ret = fs_read(file, map_to_sysmem(buffer), 0, *buffer_size,
160 			      &read_sz);
161 		if (ret || read_sz != file_sz)
162 			goto out;
163 		*buffer_size = read_sz;
164 
165 		status = EFI_SUCCESS;
166 	}
167 
168 out:
169 	free(filespec);
170 	return EFI_EXIT(status);
171 }
172 
173 /**
174  * efi_initrd_register() - create handle for loading initial RAM disk
175  *
176  * This function creates a new handle and installs a Linux specific vendor
177  * device path and an EFI_LOAD_FILE_2_PROTOCOL. Linux uses the device path
178  * to identify the handle and then calls the LoadFile service of the
179  * EFI_LOAD_FILE_2_PROTOCOL to read the initial RAM disk.
180  *
181  * Return:	status code
182  */
efi_initrd_register(void)183 efi_status_t efi_initrd_register(void)
184 {
185 	efi_handle_t efi_initrd_handle = NULL;
186 	efi_status_t ret;
187 
188 	ret = EFI_CALL(efi_install_multiple_protocol_interfaces
189 		       (&efi_initrd_handle,
190 			/* initramfs */
191 			&efi_guid_device_path, &dp,
192 			/* LOAD_FILE2 */
193 			&efi_guid_load_file2_protocol,
194 			(void *)&efi_lf2_protocol,
195 			NULL));
196 
197 	return ret;
198 }
199