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