]> git.ipfire.org Git - thirdparty/linux.git/blob - drivers/firmware/efi/libstub/file.c
Merge tag 'drm/tegra/for-5.7-fixes' of git://anongit.freedesktop.org/tegra/linux...
[thirdparty/linux.git] / drivers / firmware / efi / libstub / file.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Helper functions used by the EFI stub on multiple
4 * architectures. This should be #included by the EFI stub
5 * implementation files.
6 *
7 * Copyright 2011 Intel Corporation; author Matt Fleming
8 */
9
10 #include <linux/efi.h>
11 #include <asm/efi.h>
12
13 #include "efistub.h"
14
15 #define MAX_FILENAME_SIZE 256
16
17 /*
18 * Some firmware implementations have problems reading files in one go.
19 * A read chunk size of 1MB seems to work for most platforms.
20 *
21 * Unfortunately, reading files in chunks triggers *other* bugs on some
22 * platforms, so we provide a way to disable this workaround, which can
23 * be done by passing "efi=nochunk" on the EFI boot stub command line.
24 *
25 * If you experience issues with initrd images being corrupt it's worth
26 * trying efi=nochunk, but chunking is enabled by default on x86 because
27 * there are far more machines that require the workaround than those that
28 * break with it enabled.
29 */
30 #define EFI_READ_CHUNK_SIZE SZ_1M
31
32 struct finfo {
33 efi_file_info_t info;
34 efi_char16_t filename[MAX_FILENAME_SIZE];
35 };
36
37 static efi_status_t efi_open_file(efi_file_protocol_t *volume,
38 struct finfo *fi,
39 efi_file_protocol_t **handle,
40 unsigned long *file_size)
41 {
42 efi_guid_t info_guid = EFI_FILE_INFO_ID;
43 efi_file_protocol_t *fh;
44 unsigned long info_sz;
45 efi_status_t status;
46
47 status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0);
48 if (status != EFI_SUCCESS) {
49 pr_efi_err("Failed to open file: ");
50 efi_char16_printk(fi->filename);
51 efi_printk("\n");
52 return status;
53 }
54
55 info_sz = sizeof(struct finfo);
56 status = fh->get_info(fh, &info_guid, &info_sz, fi);
57 if (status != EFI_SUCCESS) {
58 pr_efi_err("Failed to get file info\n");
59 fh->close(fh);
60 return status;
61 }
62
63 *handle = fh;
64 *file_size = fi->info.file_size;
65 return EFI_SUCCESS;
66 }
67
68 static efi_status_t efi_open_volume(efi_loaded_image_t *image,
69 efi_file_protocol_t **fh)
70 {
71 efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
72 efi_simple_file_system_protocol_t *io;
73 efi_status_t status;
74
75 status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto,
76 (void **)&io);
77 if (status != EFI_SUCCESS) {
78 pr_efi_err("Failed to handle fs_proto\n");
79 return status;
80 }
81
82 status = io->open_volume(io, fh);
83 if (status != EFI_SUCCESS)
84 pr_efi_err("Failed to open volume\n");
85
86 return status;
87 }
88
89 static int find_file_option(const efi_char16_t *cmdline, int cmdline_len,
90 const efi_char16_t *prefix, int prefix_size,
91 efi_char16_t *result, int result_len)
92 {
93 int prefix_len = prefix_size / 2;
94 bool found = false;
95 int i;
96
97 for (i = prefix_len; i < cmdline_len; i++) {
98 if (!memcmp(&cmdline[i - prefix_len], prefix, prefix_size)) {
99 found = true;
100 break;
101 }
102 }
103
104 if (!found)
105 return 0;
106
107 while (--result_len > 0 && i < cmdline_len) {
108 if (cmdline[i] == L'\0' ||
109 cmdline[i] == L'\n' ||
110 cmdline[i] == L' ')
111 break;
112 *result++ = cmdline[i++];
113 }
114 *result = L'\0';
115 return i;
116 }
117
118 /*
119 * Check the cmdline for a LILO-style file= arguments.
120 *
121 * We only support loading a file from the same filesystem as
122 * the kernel image.
123 */
124 static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
125 const efi_char16_t *optstr,
126 int optstr_size,
127 unsigned long soft_limit,
128 unsigned long hard_limit,
129 unsigned long *load_addr,
130 unsigned long *load_size)
131 {
132 const efi_char16_t *cmdline = image->load_options;
133 int cmdline_len = image->load_options_size / 2;
134 unsigned long efi_chunk_size = ULONG_MAX;
135 efi_file_protocol_t *volume = NULL;
136 efi_file_protocol_t *file;
137 unsigned long alloc_addr;
138 unsigned long alloc_size;
139 efi_status_t status;
140 int offset;
141
142 if (!load_addr || !load_size)
143 return EFI_INVALID_PARAMETER;
144
145 if (IS_ENABLED(CONFIG_X86) && !nochunk())
146 efi_chunk_size = EFI_READ_CHUNK_SIZE;
147
148 alloc_addr = alloc_size = 0;
149 do {
150 struct finfo fi;
151 unsigned long size;
152 void *addr;
153
154 offset = find_file_option(cmdline, cmdline_len,
155 optstr, optstr_size,
156 fi.filename, ARRAY_SIZE(fi.filename));
157
158 if (!offset)
159 break;
160
161 cmdline += offset;
162 cmdline_len -= offset;
163
164 if (!volume) {
165 status = efi_open_volume(image, &volume);
166 if (status != EFI_SUCCESS)
167 return status;
168 }
169
170 status = efi_open_file(volume, &fi, &file, &size);
171 if (status != EFI_SUCCESS)
172 goto err_close_volume;
173
174 /*
175 * Check whether the existing allocation can contain the next
176 * file. This condition will also trigger naturally during the
177 * first (and typically only) iteration of the loop, given that
178 * alloc_size == 0 in that case.
179 */
180 if (round_up(alloc_size + size, EFI_ALLOC_ALIGN) >
181 round_up(alloc_size, EFI_ALLOC_ALIGN)) {
182 unsigned long old_addr = alloc_addr;
183
184 status = EFI_OUT_OF_RESOURCES;
185 if (soft_limit < hard_limit)
186 status = efi_allocate_pages(alloc_size + size,
187 &alloc_addr,
188 soft_limit);
189 if (status == EFI_OUT_OF_RESOURCES)
190 status = efi_allocate_pages(alloc_size + size,
191 &alloc_addr,
192 hard_limit);
193 if (status != EFI_SUCCESS) {
194 pr_efi_err("Failed to allocate memory for files\n");
195 goto err_close_file;
196 }
197
198 if (old_addr != 0) {
199 /*
200 * This is not the first time we've gone
201 * around this loop, and so we are loading
202 * multiple files that need to be concatenated
203 * and returned in a single buffer.
204 */
205 memcpy((void *)alloc_addr, (void *)old_addr, alloc_size);
206 efi_free(alloc_size, old_addr);
207 }
208 }
209
210 addr = (void *)alloc_addr + alloc_size;
211 alloc_size += size;
212
213 while (size) {
214 unsigned long chunksize = min(size, efi_chunk_size);
215
216 status = file->read(file, &chunksize, addr);
217 if (status != EFI_SUCCESS) {
218 pr_efi_err("Failed to read file\n");
219 goto err_close_file;
220 }
221 addr += chunksize;
222 size -= chunksize;
223 }
224 file->close(file);
225 } while (offset > 0);
226
227 *load_addr = alloc_addr;
228 *load_size = alloc_size;
229
230 if (volume)
231 volume->close(volume);
232 return EFI_SUCCESS;
233
234 err_close_file:
235 file->close(file);
236
237 err_close_volume:
238 volume->close(volume);
239 efi_free(alloc_size, alloc_addr);
240 return status;
241 }
242
243 efi_status_t efi_load_dtb(efi_loaded_image_t *image,
244 unsigned long *load_addr,
245 unsigned long *load_size)
246 {
247 return handle_cmdline_files(image, L"dtb=", sizeof(L"dtb=") - 2,
248 ULONG_MAX, ULONG_MAX, load_addr, load_size);
249 }
250
251 efi_status_t efi_load_initrd(efi_loaded_image_t *image,
252 unsigned long *load_addr,
253 unsigned long *load_size,
254 unsigned long soft_limit,
255 unsigned long hard_limit)
256 {
257 return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2,
258 soft_limit, hard_limit, load_addr, load_size);
259 }