1
2
3
4
5
6
7
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
19
20
21
22
23
24
25
26
27
28
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 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
68static 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
89static 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
120
121
122
123
124static 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
176
177
178
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
201
202
203
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
234err_close_file:
235 file->close(file);
236
237err_close_volume:
238 volume->close(volume);
239 efi_free(alloc_size, alloc_addr);
240 return status;
241}
242
243efi_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
251efi_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}
260