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
32struct finfo {
33	efi_file_info_t info;
34	efi_char16_t	filename[MAX_FILENAME_SIZE];
35};
36
37static 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		efi_err("Failed to open file: %ls\n", fi->filename);
50		return status;
51	}
52
53	info_sz = sizeof(struct finfo);
54	status = fh->get_info(fh, &info_guid, &info_sz, fi);
55	if (status != EFI_SUCCESS) {
56		efi_err("Failed to get file info\n");
57		fh->close(fh);
58		return status;
59	}
60
61	*handle = fh;
62	*file_size = fi->info.file_size;
63	return EFI_SUCCESS;
64}
65
66static efi_status_t efi_open_volume(efi_loaded_image_t *image,
67				    efi_file_protocol_t **fh)
68{
69	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
70	efi_simple_file_system_protocol_t *io;
71	efi_status_t status;
72
73	status = efi_bs_call(handle_protocol, image->device_handle, &fs_proto,
74			     (void **)&io);
75	if (status != EFI_SUCCESS) {
76		efi_err("Failed to handle fs_proto\n");
77		return status;
78	}
79
80	status = io->open_volume(io, fh);
81	if (status != EFI_SUCCESS)
82		efi_err("Failed to open volume\n");
83
84	return status;
85}
86
87static int find_file_option(const efi_char16_t *cmdline, int cmdline_len,
88			    const efi_char16_t *prefix, int prefix_size,
89			    efi_char16_t *result, int result_len)
90{
91	int prefix_len = prefix_size / 2;
92	bool found = false;
93	int i;
94
95	for (i = prefix_len; i < cmdline_len; i++) {
96		if (!memcmp(&cmdline[i - prefix_len], prefix, prefix_size)) {
97			found = true;
98			break;
99		}
100	}
101
102	if (!found)
103		return 0;
104
105	/* Skip any leading slashes */
106	while (i < cmdline_len && (cmdline[i] == L'/' || cmdline[i] == L'\\'))
107		i++;
108
109	while (--result_len > 0 && i < cmdline_len) {
110		efi_char16_t c = cmdline[i++];
111
112		if (c == L'\0' || c == L'\n' || c == L' ')
113			break;
114		else if (c == L'/')
115			/* Replace UNIX dir separators with EFI standard ones */
116			*result++ = L'\\';
117		else
118			*result++ = c;
119	}
120	*result = L'\0';
121	return i;
122}
123
124/*
125 * Check the cmdline for a LILO-style file= arguments.
126 *
127 * We only support loading a file from the same filesystem as
128 * the kernel image.
129 */
130efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
131				  const efi_char16_t *optstr,
132				  int optstr_size,
133				  unsigned long soft_limit,
134				  unsigned long hard_limit,
135				  unsigned long *load_addr,
136				  unsigned long *load_size)
137{
138	const efi_char16_t *cmdline = image->load_options;
139	int cmdline_len = image->load_options_size;
140	unsigned long efi_chunk_size = ULONG_MAX;
141	efi_file_protocol_t *volume = NULL;
142	efi_file_protocol_t *file;
143	unsigned long alloc_addr;
144	unsigned long alloc_size;
145	efi_status_t status;
146	int offset;
147
148	if (!load_addr || !load_size)
149		return EFI_INVALID_PARAMETER;
150
151	efi_apply_loadoptions_quirk((const void **)&cmdline, &cmdline_len);
152	cmdline_len /= sizeof(*cmdline);
153
154	if (IS_ENABLED(CONFIG_X86) && !efi_nochunk)
155		efi_chunk_size = EFI_READ_CHUNK_SIZE;
156
157	alloc_addr = alloc_size = 0;
158	do {
159		struct finfo fi;
160		unsigned long size;
161		void *addr;
162
163		offset = find_file_option(cmdline, cmdline_len,
164					  optstr, optstr_size,
165					  fi.filename, ARRAY_SIZE(fi.filename));
166
167		if (!offset)
168			break;
169
170		cmdline += offset;
171		cmdline_len -= offset;
172
173		if (!volume) {
174			status = efi_open_volume(image, &volume);
175			if (status != EFI_SUCCESS)
176				return status;
177		}
178
179		status = efi_open_file(volume, &fi, &file, &size);
180		if (status != EFI_SUCCESS)
181			goto err_close_volume;
182
183		/*
184		 * Check whether the existing allocation can contain the next
185		 * file. This condition will also trigger naturally during the
186		 * first (and typically only) iteration of the loop, given that
187		 * alloc_size == 0 in that case.
188		 */
189		if (round_up(alloc_size + size, EFI_ALLOC_ALIGN) >
190		    round_up(alloc_size, EFI_ALLOC_ALIGN)) {
191			unsigned long old_addr = alloc_addr;
192
193			status = EFI_OUT_OF_RESOURCES;
194			if (soft_limit < hard_limit)
195				status = efi_allocate_pages(alloc_size + size,
196							    &alloc_addr,
197							    soft_limit);
198			if (status == EFI_OUT_OF_RESOURCES)
199				status = efi_allocate_pages(alloc_size + size,
200							    &alloc_addr,
201							    hard_limit);
202			if (status != EFI_SUCCESS) {
203				efi_err("Failed to allocate memory for files\n");
204				goto err_close_file;
205			}
206
207			if (old_addr != 0) {
208				/*
209				 * This is not the first time we've gone
210				 * around this loop, and so we are loading
211				 * multiple files that need to be concatenated
212				 * and returned in a single buffer.
213				 */
214				memcpy((void *)alloc_addr, (void *)old_addr, alloc_size);
215				efi_free(alloc_size, old_addr);
216			}
217		}
218
219		addr = (void *)alloc_addr + alloc_size;
220		alloc_size += size;
221
222		while (size) {
223			unsigned long chunksize = min(size, efi_chunk_size);
224
225			status = file->read(file, &chunksize, addr);
226			if (status != EFI_SUCCESS) {
227				efi_err("Failed to read file\n");
228				goto err_close_file;
229			}
230			addr += chunksize;
231			size -= chunksize;
232		}
233		file->close(file);
234	} while (offset > 0);
235
236	*load_addr = alloc_addr;
237	*load_size = alloc_size;
238
239	if (volume)
240		volume->close(volume);
241
242	if (*load_size == 0)
243		return EFI_NOT_READY;
244	return EFI_SUCCESS;
245
246err_close_file:
247	file->close(file);
248
249err_close_volume:
250	volume->close(volume);
251	efi_free(alloc_size, alloc_addr);
252	return status;
253}
254