xref: /openbmc/linux/drivers/firmware/efi/libstub/file.c (revision 5193a33d78ce8b4fdf880a754700bce21b3756b5)
1*5193a33dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0
2*5193a33dSArd Biesheuvel /*
3*5193a33dSArd Biesheuvel  * Helper functions used by the EFI stub on multiple
4*5193a33dSArd Biesheuvel  * architectures. This should be #included by the EFI stub
5*5193a33dSArd Biesheuvel  * implementation files.
6*5193a33dSArd Biesheuvel  *
7*5193a33dSArd Biesheuvel  * Copyright 2011 Intel Corporation; author Matt Fleming
8*5193a33dSArd Biesheuvel  */
9*5193a33dSArd Biesheuvel 
10*5193a33dSArd Biesheuvel #include <linux/efi.h>
11*5193a33dSArd Biesheuvel #include <asm/efi.h>
12*5193a33dSArd Biesheuvel 
13*5193a33dSArd Biesheuvel #include "efistub.h"
14*5193a33dSArd Biesheuvel 
15*5193a33dSArd Biesheuvel /*
16*5193a33dSArd Biesheuvel  * Some firmware implementations have problems reading files in one go.
17*5193a33dSArd Biesheuvel  * A read chunk size of 1MB seems to work for most platforms.
18*5193a33dSArd Biesheuvel  *
19*5193a33dSArd Biesheuvel  * Unfortunately, reading files in chunks triggers *other* bugs on some
20*5193a33dSArd Biesheuvel  * platforms, so we provide a way to disable this workaround, which can
21*5193a33dSArd Biesheuvel  * be done by passing "efi=nochunk" on the EFI boot stub command line.
22*5193a33dSArd Biesheuvel  *
23*5193a33dSArd Biesheuvel  * If you experience issues with initrd images being corrupt it's worth
24*5193a33dSArd Biesheuvel  * trying efi=nochunk, but chunking is enabled by default on x86 because
25*5193a33dSArd Biesheuvel  * there are far more machines that require the workaround than those that
26*5193a33dSArd Biesheuvel  * break with it enabled.
27*5193a33dSArd Biesheuvel  */
28*5193a33dSArd Biesheuvel #define EFI_READ_CHUNK_SIZE	SZ_1M
29*5193a33dSArd Biesheuvel 
30*5193a33dSArd Biesheuvel struct file_info {
31*5193a33dSArd Biesheuvel 	efi_file_protocol_t *handle;
32*5193a33dSArd Biesheuvel 	u64 size;
33*5193a33dSArd Biesheuvel };
34*5193a33dSArd Biesheuvel 
35*5193a33dSArd Biesheuvel static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16,
36*5193a33dSArd Biesheuvel 				  void **handle, u64 *file_sz)
37*5193a33dSArd Biesheuvel {
38*5193a33dSArd Biesheuvel 	efi_file_protocol_t *h, *fh = __fh;
39*5193a33dSArd Biesheuvel 	efi_file_info_t *info;
40*5193a33dSArd Biesheuvel 	efi_status_t status;
41*5193a33dSArd Biesheuvel 	efi_guid_t info_guid = EFI_FILE_INFO_ID;
42*5193a33dSArd Biesheuvel 	unsigned long info_sz;
43*5193a33dSArd Biesheuvel 
44*5193a33dSArd Biesheuvel 	status = fh->open(fh, &h, filename_16, EFI_FILE_MODE_READ, 0);
45*5193a33dSArd Biesheuvel 	if (status != EFI_SUCCESS) {
46*5193a33dSArd Biesheuvel 		efi_printk("Failed to open file: ");
47*5193a33dSArd Biesheuvel 		efi_char16_printk(filename_16);
48*5193a33dSArd Biesheuvel 		efi_printk("\n");
49*5193a33dSArd Biesheuvel 		return status;
50*5193a33dSArd Biesheuvel 	}
51*5193a33dSArd Biesheuvel 
52*5193a33dSArd Biesheuvel 	*handle = h;
53*5193a33dSArd Biesheuvel 
54*5193a33dSArd Biesheuvel 	info_sz = 0;
55*5193a33dSArd Biesheuvel 	status = h->get_info(h, &info_guid, &info_sz, NULL);
56*5193a33dSArd Biesheuvel 	if (status != EFI_BUFFER_TOO_SMALL) {
57*5193a33dSArd Biesheuvel 		efi_printk("Failed to get file info size\n");
58*5193a33dSArd Biesheuvel 		return status;
59*5193a33dSArd Biesheuvel 	}
60*5193a33dSArd Biesheuvel 
61*5193a33dSArd Biesheuvel grow:
62*5193a33dSArd Biesheuvel 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, info_sz,
63*5193a33dSArd Biesheuvel 			     (void **)&info);
64*5193a33dSArd Biesheuvel 	if (status != EFI_SUCCESS) {
65*5193a33dSArd Biesheuvel 		efi_printk("Failed to alloc mem for file info\n");
66*5193a33dSArd Biesheuvel 		return status;
67*5193a33dSArd Biesheuvel 	}
68*5193a33dSArd Biesheuvel 
69*5193a33dSArd Biesheuvel 	status = h->get_info(h, &info_guid, &info_sz, info);
70*5193a33dSArd Biesheuvel 	if (status == EFI_BUFFER_TOO_SMALL) {
71*5193a33dSArd Biesheuvel 		efi_bs_call(free_pool, info);
72*5193a33dSArd Biesheuvel 		goto grow;
73*5193a33dSArd Biesheuvel 	}
74*5193a33dSArd Biesheuvel 
75*5193a33dSArd Biesheuvel 	*file_sz = info->file_size;
76*5193a33dSArd Biesheuvel 	efi_bs_call(free_pool, info);
77*5193a33dSArd Biesheuvel 
78*5193a33dSArd Biesheuvel 	if (status != EFI_SUCCESS)
79*5193a33dSArd Biesheuvel 		efi_printk("Failed to get initrd info\n");
80*5193a33dSArd Biesheuvel 
81*5193a33dSArd Biesheuvel 	return status;
82*5193a33dSArd Biesheuvel }
83*5193a33dSArd Biesheuvel 
84*5193a33dSArd Biesheuvel static efi_status_t efi_file_read(efi_file_protocol_t *handle,
85*5193a33dSArd Biesheuvel 				  unsigned long *size, void *addr)
86*5193a33dSArd Biesheuvel {
87*5193a33dSArd Biesheuvel 	return handle->read(handle, size, addr);
88*5193a33dSArd Biesheuvel }
89*5193a33dSArd Biesheuvel 
90*5193a33dSArd Biesheuvel static efi_status_t efi_file_close(efi_file_protocol_t *handle)
91*5193a33dSArd Biesheuvel {
92*5193a33dSArd Biesheuvel 	return handle->close(handle);
93*5193a33dSArd Biesheuvel }
94*5193a33dSArd Biesheuvel 
95*5193a33dSArd Biesheuvel static efi_status_t efi_open_volume(efi_loaded_image_t *image,
96*5193a33dSArd Biesheuvel 				    efi_file_protocol_t **__fh)
97*5193a33dSArd Biesheuvel {
98*5193a33dSArd Biesheuvel 	efi_simple_file_system_protocol_t *io;
99*5193a33dSArd Biesheuvel 	efi_file_protocol_t *fh;
100*5193a33dSArd Biesheuvel 	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
101*5193a33dSArd Biesheuvel 	efi_status_t status;
102*5193a33dSArd Biesheuvel 	efi_handle_t handle = image->device_handle;
103*5193a33dSArd Biesheuvel 
104*5193a33dSArd Biesheuvel 	status = efi_bs_call(handle_protocol, handle, &fs_proto, (void **)&io);
105*5193a33dSArd Biesheuvel 	if (status != EFI_SUCCESS) {
106*5193a33dSArd Biesheuvel 		efi_printk("Failed to handle fs_proto\n");
107*5193a33dSArd Biesheuvel 		return status;
108*5193a33dSArd Biesheuvel 	}
109*5193a33dSArd Biesheuvel 
110*5193a33dSArd Biesheuvel 	status = io->open_volume(io, &fh);
111*5193a33dSArd Biesheuvel 	if (status != EFI_SUCCESS)
112*5193a33dSArd Biesheuvel 		efi_printk("Failed to open volume\n");
113*5193a33dSArd Biesheuvel 	else
114*5193a33dSArd Biesheuvel 		*__fh = fh;
115*5193a33dSArd Biesheuvel 
116*5193a33dSArd Biesheuvel 	return status;
117*5193a33dSArd Biesheuvel }
118*5193a33dSArd Biesheuvel 
119*5193a33dSArd Biesheuvel /*
120*5193a33dSArd Biesheuvel  * Check the cmdline for a LILO-style file= arguments.
121*5193a33dSArd Biesheuvel  *
122*5193a33dSArd Biesheuvel  * We only support loading a file from the same filesystem as
123*5193a33dSArd Biesheuvel  * the kernel image.
124*5193a33dSArd Biesheuvel  */
125*5193a33dSArd Biesheuvel efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
126*5193a33dSArd Biesheuvel 				  char *cmd_line, char *option_string,
127*5193a33dSArd Biesheuvel 				  unsigned long max_addr,
128*5193a33dSArd Biesheuvel 				  unsigned long *load_addr,
129*5193a33dSArd Biesheuvel 				  unsigned long *load_size)
130*5193a33dSArd Biesheuvel {
131*5193a33dSArd Biesheuvel 	unsigned long efi_chunk_size = ULONG_MAX;
132*5193a33dSArd Biesheuvel 	struct file_info *files;
133*5193a33dSArd Biesheuvel 	unsigned long file_addr;
134*5193a33dSArd Biesheuvel 	u64 file_size_total;
135*5193a33dSArd Biesheuvel 	efi_file_protocol_t *fh = NULL;
136*5193a33dSArd Biesheuvel 	efi_status_t status;
137*5193a33dSArd Biesheuvel 	int nr_files;
138*5193a33dSArd Biesheuvel 	char *str;
139*5193a33dSArd Biesheuvel 	int i, j, k;
140*5193a33dSArd Biesheuvel 
141*5193a33dSArd Biesheuvel 	if (IS_ENABLED(CONFIG_X86) && !nochunk())
142*5193a33dSArd Biesheuvel 		efi_chunk_size = EFI_READ_CHUNK_SIZE;
143*5193a33dSArd Biesheuvel 
144*5193a33dSArd Biesheuvel 	file_addr = 0;
145*5193a33dSArd Biesheuvel 	file_size_total = 0;
146*5193a33dSArd Biesheuvel 
147*5193a33dSArd Biesheuvel 	str = cmd_line;
148*5193a33dSArd Biesheuvel 
149*5193a33dSArd Biesheuvel 	j = 0;			/* See close_handles */
150*5193a33dSArd Biesheuvel 
151*5193a33dSArd Biesheuvel 	if (!load_addr || !load_size)
152*5193a33dSArd Biesheuvel 		return EFI_INVALID_PARAMETER;
153*5193a33dSArd Biesheuvel 
154*5193a33dSArd Biesheuvel 	*load_addr = 0;
155*5193a33dSArd Biesheuvel 	*load_size = 0;
156*5193a33dSArd Biesheuvel 
157*5193a33dSArd Biesheuvel 	if (!str || !*str)
158*5193a33dSArd Biesheuvel 		return EFI_SUCCESS;
159*5193a33dSArd Biesheuvel 
160*5193a33dSArd Biesheuvel 	for (nr_files = 0; *str; nr_files++) {
161*5193a33dSArd Biesheuvel 		str = strstr(str, option_string);
162*5193a33dSArd Biesheuvel 		if (!str)
163*5193a33dSArd Biesheuvel 			break;
164*5193a33dSArd Biesheuvel 
165*5193a33dSArd Biesheuvel 		str += strlen(option_string);
166*5193a33dSArd Biesheuvel 
167*5193a33dSArd Biesheuvel 		/* Skip any leading slashes */
168*5193a33dSArd Biesheuvel 		while (*str == '/' || *str == '\\')
169*5193a33dSArd Biesheuvel 			str++;
170*5193a33dSArd Biesheuvel 
171*5193a33dSArd Biesheuvel 		while (*str && *str != ' ' && *str != '\n')
172*5193a33dSArd Biesheuvel 			str++;
173*5193a33dSArd Biesheuvel 	}
174*5193a33dSArd Biesheuvel 
175*5193a33dSArd Biesheuvel 	if (!nr_files)
176*5193a33dSArd Biesheuvel 		return EFI_SUCCESS;
177*5193a33dSArd Biesheuvel 
178*5193a33dSArd Biesheuvel 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA,
179*5193a33dSArd Biesheuvel 			     nr_files * sizeof(*files), (void **)&files);
180*5193a33dSArd Biesheuvel 	if (status != EFI_SUCCESS) {
181*5193a33dSArd Biesheuvel 		pr_efi_err("Failed to alloc mem for file handle list\n");
182*5193a33dSArd Biesheuvel 		goto fail;
183*5193a33dSArd Biesheuvel 	}
184*5193a33dSArd Biesheuvel 
185*5193a33dSArd Biesheuvel 	str = cmd_line;
186*5193a33dSArd Biesheuvel 	for (i = 0; i < nr_files; i++) {
187*5193a33dSArd Biesheuvel 		struct file_info *file;
188*5193a33dSArd Biesheuvel 		efi_char16_t filename_16[256];
189*5193a33dSArd Biesheuvel 		efi_char16_t *p;
190*5193a33dSArd Biesheuvel 
191*5193a33dSArd Biesheuvel 		str = strstr(str, option_string);
192*5193a33dSArd Biesheuvel 		if (!str)
193*5193a33dSArd Biesheuvel 			break;
194*5193a33dSArd Biesheuvel 
195*5193a33dSArd Biesheuvel 		str += strlen(option_string);
196*5193a33dSArd Biesheuvel 
197*5193a33dSArd Biesheuvel 		file = &files[i];
198*5193a33dSArd Biesheuvel 		p = filename_16;
199*5193a33dSArd Biesheuvel 
200*5193a33dSArd Biesheuvel 		/* Skip any leading slashes */
201*5193a33dSArd Biesheuvel 		while (*str == '/' || *str == '\\')
202*5193a33dSArd Biesheuvel 			str++;
203*5193a33dSArd Biesheuvel 
204*5193a33dSArd Biesheuvel 		while (*str && *str != ' ' && *str != '\n') {
205*5193a33dSArd Biesheuvel 			if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16))
206*5193a33dSArd Biesheuvel 				break;
207*5193a33dSArd Biesheuvel 
208*5193a33dSArd Biesheuvel 			if (*str == '/') {
209*5193a33dSArd Biesheuvel 				*p++ = '\\';
210*5193a33dSArd Biesheuvel 				str++;
211*5193a33dSArd Biesheuvel 			} else {
212*5193a33dSArd Biesheuvel 				*p++ = *str++;
213*5193a33dSArd Biesheuvel 			}
214*5193a33dSArd Biesheuvel 		}
215*5193a33dSArd Biesheuvel 
216*5193a33dSArd Biesheuvel 		*p = '\0';
217*5193a33dSArd Biesheuvel 
218*5193a33dSArd Biesheuvel 		/* Only open the volume once. */
219*5193a33dSArd Biesheuvel 		if (!i) {
220*5193a33dSArd Biesheuvel 			status = efi_open_volume(image, &fh);
221*5193a33dSArd Biesheuvel 			if (status != EFI_SUCCESS)
222*5193a33dSArd Biesheuvel 				goto free_files;
223*5193a33dSArd Biesheuvel 		}
224*5193a33dSArd Biesheuvel 
225*5193a33dSArd Biesheuvel 		status = efi_file_size(fh, filename_16, (void **)&file->handle,
226*5193a33dSArd Biesheuvel 				       &file->size);
227*5193a33dSArd Biesheuvel 		if (status != EFI_SUCCESS)
228*5193a33dSArd Biesheuvel 			goto close_handles;
229*5193a33dSArd Biesheuvel 
230*5193a33dSArd Biesheuvel 		file_size_total += file->size;
231*5193a33dSArd Biesheuvel 	}
232*5193a33dSArd Biesheuvel 
233*5193a33dSArd Biesheuvel 	if (file_size_total) {
234*5193a33dSArd Biesheuvel 		unsigned long addr;
235*5193a33dSArd Biesheuvel 
236*5193a33dSArd Biesheuvel 		/*
237*5193a33dSArd Biesheuvel 		 * Multiple files need to be at consecutive addresses in memory,
238*5193a33dSArd Biesheuvel 		 * so allocate enough memory for all the files.  This is used
239*5193a33dSArd Biesheuvel 		 * for loading multiple files.
240*5193a33dSArd Biesheuvel 		 */
241*5193a33dSArd Biesheuvel 		status = efi_allocate_pages(file_size_total, &file_addr, max_addr);
242*5193a33dSArd Biesheuvel 		if (status != EFI_SUCCESS) {
243*5193a33dSArd Biesheuvel 			pr_efi_err("Failed to alloc highmem for files\n");
244*5193a33dSArd Biesheuvel 			goto close_handles;
245*5193a33dSArd Biesheuvel 		}
246*5193a33dSArd Biesheuvel 
247*5193a33dSArd Biesheuvel 		/* We've run out of free low memory. */
248*5193a33dSArd Biesheuvel 		if (file_addr > max_addr) {
249*5193a33dSArd Biesheuvel 			pr_efi_err("We've run out of free low memory\n");
250*5193a33dSArd Biesheuvel 			status = EFI_INVALID_PARAMETER;
251*5193a33dSArd Biesheuvel 			goto free_file_total;
252*5193a33dSArd Biesheuvel 		}
253*5193a33dSArd Biesheuvel 
254*5193a33dSArd Biesheuvel 		addr = file_addr;
255*5193a33dSArd Biesheuvel 		for (j = 0; j < nr_files; j++) {
256*5193a33dSArd Biesheuvel 			unsigned long size;
257*5193a33dSArd Biesheuvel 
258*5193a33dSArd Biesheuvel 			size = files[j].size;
259*5193a33dSArd Biesheuvel 			while (size) {
260*5193a33dSArd Biesheuvel 				unsigned long chunksize;
261*5193a33dSArd Biesheuvel 
262*5193a33dSArd Biesheuvel 				if (size > efi_chunk_size)
263*5193a33dSArd Biesheuvel 					chunksize = efi_chunk_size;
264*5193a33dSArd Biesheuvel 				else
265*5193a33dSArd Biesheuvel 					chunksize = size;
266*5193a33dSArd Biesheuvel 
267*5193a33dSArd Biesheuvel 				status = efi_file_read(files[j].handle,
268*5193a33dSArd Biesheuvel 						       &chunksize,
269*5193a33dSArd Biesheuvel 						       (void *)addr);
270*5193a33dSArd Biesheuvel 				if (status != EFI_SUCCESS) {
271*5193a33dSArd Biesheuvel 					pr_efi_err("Failed to read file\n");
272*5193a33dSArd Biesheuvel 					goto free_file_total;
273*5193a33dSArd Biesheuvel 				}
274*5193a33dSArd Biesheuvel 				addr += chunksize;
275*5193a33dSArd Biesheuvel 				size -= chunksize;
276*5193a33dSArd Biesheuvel 			}
277*5193a33dSArd Biesheuvel 
278*5193a33dSArd Biesheuvel 			efi_file_close(files[j].handle);
279*5193a33dSArd Biesheuvel 		}
280*5193a33dSArd Biesheuvel 
281*5193a33dSArd Biesheuvel 	}
282*5193a33dSArd Biesheuvel 
283*5193a33dSArd Biesheuvel 	efi_bs_call(free_pool, files);
284*5193a33dSArd Biesheuvel 
285*5193a33dSArd Biesheuvel 	*load_addr = file_addr;
286*5193a33dSArd Biesheuvel 	*load_size = file_size_total;
287*5193a33dSArd Biesheuvel 
288*5193a33dSArd Biesheuvel 	return status;
289*5193a33dSArd Biesheuvel 
290*5193a33dSArd Biesheuvel free_file_total:
291*5193a33dSArd Biesheuvel 	efi_free(file_size_total, file_addr);
292*5193a33dSArd Biesheuvel 
293*5193a33dSArd Biesheuvel close_handles:
294*5193a33dSArd Biesheuvel 	for (k = j; k < i; k++)
295*5193a33dSArd Biesheuvel 		efi_file_close(files[k].handle);
296*5193a33dSArd Biesheuvel free_files:
297*5193a33dSArd Biesheuvel 	efi_bs_call(free_pool, files);
298*5193a33dSArd Biesheuvel fail:
299*5193a33dSArd Biesheuvel 	*load_addr = 0;
300*5193a33dSArd Biesheuvel 	*load_size = 0;
301*5193a33dSArd Biesheuvel 
302*5193a33dSArd Biesheuvel 	return status;
303*5193a33dSArd Biesheuvel }
304