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