aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorRoy Franz <roy.franz@linaro.org>2013-09-22 18:45:27 -0400
committerMatt Fleming <matt.fleming@intel.com>2013-09-25 07:34:34 -0400
commit7721da4c1ebf96ff815987011c1a0edef596b50a (patch)
tree66eaab33c5af6a00d65f75200c6b7838fd00302f /drivers/firmware
parented37ddffe201bfad7be3c45bc08bd65b5298adca (diff)
efi: Move common EFI stub code from x86 arch code to common location
No code changes made, just moving functions and #define from x86 arch directory to common location. Code is shared using #include, similar to how decompression code is shared among architectures. Signed-off-by: Roy Franz <roy.franz@linaro.org> Acked-by: Mark Salter <msalter@redhat.com> Reviewed-by: Grant Likely <grant.likely@linaro.org> Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/efi/efi-stub-helper.c463
1 files changed, 463 insertions, 0 deletions
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c
new file mode 100644
index 000000000000..8a83387fef92
--- /dev/null
+++ b/drivers/firmware/efi/efi-stub-helper.c
@@ -0,0 +1,463 @@
1/*
2 * Helper functions used by the EFI stub on multiple
3 * architectures. This should be #included by the EFI stub
4 * implementation files.
5 *
6 * Copyright 2011 Intel Corporation; author Matt Fleming
7 *
8 * This file is part of the Linux kernel, and is made available
9 * under the terms of the GNU General Public License version 2.
10 *
11 */
12#define EFI_READ_CHUNK_SIZE (1024 * 1024)
13
14struct initrd {
15 efi_file_handle_t *handle;
16 u64 size;
17};
18
19
20
21
22static void efi_char16_printk(efi_char16_t *str)
23{
24 struct efi_simple_text_output_protocol *out;
25
26 out = (struct efi_simple_text_output_protocol *)sys_table->con_out;
27 efi_call_phys2(out->output_string, out, str);
28}
29
30static void efi_printk(char *str)
31{
32 char *s8;
33
34 for (s8 = str; *s8; s8++) {
35 efi_char16_t ch[2] = { 0 };
36
37 ch[0] = *s8;
38 if (*s8 == '\n') {
39 efi_char16_t nl[2] = { '\r', 0 };
40 efi_char16_printk(nl);
41 }
42
43 efi_char16_printk(ch);
44 }
45}
46
47
48static efi_status_t __get_map(efi_memory_desc_t **map, unsigned long *map_size,
49 unsigned long *desc_size)
50{
51 efi_memory_desc_t *m = NULL;
52 efi_status_t status;
53 unsigned long key;
54 u32 desc_version;
55
56 *map_size = sizeof(*m) * 32;
57again:
58 /*
59 * Add an additional efi_memory_desc_t because we're doing an
60 * allocation which may be in a new descriptor region.
61 */
62 *map_size += sizeof(*m);
63 status = efi_call_phys3(sys_table->boottime->allocate_pool,
64 EFI_LOADER_DATA, *map_size, (void **)&m);
65 if (status != EFI_SUCCESS)
66 goto fail;
67
68 status = efi_call_phys5(sys_table->boottime->get_memory_map, map_size,
69 m, &key, desc_size, &desc_version);
70 if (status == EFI_BUFFER_TOO_SMALL) {
71 efi_call_phys1(sys_table->boottime->free_pool, m);
72 goto again;
73 }
74
75 if (status != EFI_SUCCESS)
76 efi_call_phys1(sys_table->boottime->free_pool, m);
77
78fail:
79 *map = m;
80 return status;
81}
82
83/*
84 * Allocate at the highest possible address that is not above 'max'.
85 */
86static efi_status_t high_alloc(unsigned long size, unsigned long align,
87 unsigned long *addr, unsigned long max)
88{
89 unsigned long map_size, desc_size;
90 efi_memory_desc_t *map;
91 efi_status_t status;
92 unsigned long nr_pages;
93 u64 max_addr = 0;
94 int i;
95
96 status = __get_map(&map, &map_size, &desc_size);
97 if (status != EFI_SUCCESS)
98 goto fail;
99
100 nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
101again:
102 for (i = 0; i < map_size / desc_size; i++) {
103 efi_memory_desc_t *desc;
104 unsigned long m = (unsigned long)map;
105 u64 start, end;
106
107 desc = (efi_memory_desc_t *)(m + (i * desc_size));
108 if (desc->type != EFI_CONVENTIONAL_MEMORY)
109 continue;
110
111 if (desc->num_pages < nr_pages)
112 continue;
113
114 start = desc->phys_addr;
115 end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
116
117 if ((start + size) > end || (start + size) > max)
118 continue;
119
120 if (end - size > max)
121 end = max;
122
123 if (round_down(end - size, align) < start)
124 continue;
125
126 start = round_down(end - size, align);
127
128 /*
129 * Don't allocate at 0x0. It will confuse code that
130 * checks pointers against NULL.
131 */
132 if (start == 0x0)
133 continue;
134
135 if (start > max_addr)
136 max_addr = start;
137 }
138
139 if (!max_addr)
140 status = EFI_NOT_FOUND;
141 else {
142 status = efi_call_phys4(sys_table->boottime->allocate_pages,
143 EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
144 nr_pages, &max_addr);
145 if (status != EFI_SUCCESS) {
146 max = max_addr;
147 max_addr = 0;
148 goto again;
149 }
150
151 *addr = max_addr;
152 }
153
154free_pool:
155 efi_call_phys1(sys_table->boottime->free_pool, map);
156
157fail:
158 return status;
159}
160
161/*
162 * Allocate at the lowest possible address.
163 */
164static efi_status_t low_alloc(unsigned long size, unsigned long align,
165 unsigned long *addr)
166{
167 unsigned long map_size, desc_size;
168 efi_memory_desc_t *map;
169 efi_status_t status;
170 unsigned long nr_pages;
171 int i;
172
173 status = __get_map(&map, &map_size, &desc_size);
174 if (status != EFI_SUCCESS)
175 goto fail;
176
177 nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
178 for (i = 0; i < map_size / desc_size; i++) {
179 efi_memory_desc_t *desc;
180 unsigned long m = (unsigned long)map;
181 u64 start, end;
182
183 desc = (efi_memory_desc_t *)(m + (i * desc_size));
184
185 if (desc->type != EFI_CONVENTIONAL_MEMORY)
186 continue;
187
188 if (desc->num_pages < nr_pages)
189 continue;
190
191 start = desc->phys_addr;
192 end = start + desc->num_pages * (1UL << EFI_PAGE_SHIFT);
193
194 /*
195 * Don't allocate at 0x0. It will confuse code that
196 * checks pointers against NULL. Skip the first 8
197 * bytes so we start at a nice even number.
198 */
199 if (start == 0x0)
200 start += 8;
201
202 start = round_up(start, align);
203 if ((start + size) > end)
204 continue;
205
206 status = efi_call_phys4(sys_table->boottime->allocate_pages,
207 EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA,
208 nr_pages, &start);
209 if (status == EFI_SUCCESS) {
210 *addr = start;
211 break;
212 }
213 }
214
215 if (i == map_size / desc_size)
216 status = EFI_NOT_FOUND;
217
218free_pool:
219 efi_call_phys1(sys_table->boottime->free_pool, map);
220fail:
221 return status;
222}
223
224static void low_free(unsigned long size, unsigned long addr)
225{
226 unsigned long nr_pages;
227
228 nr_pages = round_up(size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE;
229 efi_call_phys2(sys_table->boottime->free_pages, addr, nr_pages);
230}
231
232
233/*
234 * Check the cmdline for a LILO-style initrd= arguments.
235 *
236 * We only support loading an initrd from the same filesystem as the
237 * kernel image.
238 */
239static efi_status_t handle_ramdisks(efi_loaded_image_t *image,
240 struct setup_header *hdr)
241{
242 struct initrd *initrds;
243 unsigned long initrd_addr;
244 efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
245 u64 initrd_total;
246 efi_file_io_interface_t *io;
247 efi_file_handle_t *fh;
248 efi_status_t status;
249 int nr_initrds;
250 char *str;
251 int i, j, k;
252
253 initrd_addr = 0;
254 initrd_total = 0;
255
256 str = (char *)(unsigned long)hdr->cmd_line_ptr;
257
258 j = 0; /* See close_handles */
259
260 if (!str || !*str)
261 return EFI_SUCCESS;
262
263 for (nr_initrds = 0; *str; nr_initrds++) {
264 str = strstr(str, "initrd=");
265 if (!str)
266 break;
267
268 str += 7;
269
270 /* Skip any leading slashes */
271 while (*str == '/' || *str == '\\')
272 str++;
273
274 while (*str && *str != ' ' && *str != '\n')
275 str++;
276 }
277
278 if (!nr_initrds)
279 return EFI_SUCCESS;
280
281 status = efi_call_phys3(sys_table->boottime->allocate_pool,
282 EFI_LOADER_DATA,
283 nr_initrds * sizeof(*initrds),
284 &initrds);
285 if (status != EFI_SUCCESS) {
286 efi_printk("Failed to alloc mem for initrds\n");
287 goto fail;
288 }
289
290 str = (char *)(unsigned long)hdr->cmd_line_ptr;
291 for (i = 0; i < nr_initrds; i++) {
292 struct initrd *initrd;
293 efi_file_handle_t *h;
294 efi_file_info_t *info;
295 efi_char16_t filename_16[256];
296 unsigned long info_sz;
297 efi_guid_t info_guid = EFI_FILE_INFO_ID;
298 efi_char16_t *p;
299 u64 file_sz;
300
301 str = strstr(str, "initrd=");
302 if (!str)
303 break;
304
305 str += 7;
306
307 initrd = &initrds[i];
308 p = filename_16;
309
310 /* Skip any leading slashes */
311 while (*str == '/' || *str == '\\')
312 str++;
313
314 while (*str && *str != ' ' && *str != '\n') {
315 if ((u8 *)p >= (u8 *)filename_16 + sizeof(filename_16))
316 break;
317
318 if (*str == '/') {
319 *p++ = '\\';
320 *str++;
321 } else {
322 *p++ = *str++;
323 }
324 }
325
326 *p = '\0';
327
328 /* Only open the volume once. */
329 if (!i) {
330 efi_boot_services_t *boottime;
331
332 boottime = sys_table->boottime;
333
334 status = efi_call_phys3(boottime->handle_protocol,
335 image->device_handle, &fs_proto, &io);
336 if (status != EFI_SUCCESS) {
337 efi_printk("Failed to handle fs_proto\n");
338 goto free_initrds;
339 }
340
341 status = efi_call_phys2(io->open_volume, io, &fh);
342 if (status != EFI_SUCCESS) {
343 efi_printk("Failed to open volume\n");
344 goto free_initrds;
345 }
346 }
347
348 status = efi_call_phys5(fh->open, fh, &h, filename_16,
349 EFI_FILE_MODE_READ, (u64)0);
350 if (status != EFI_SUCCESS) {
351 efi_printk("Failed to open initrd file: ");
352 efi_char16_printk(filename_16);
353 efi_printk("\n");
354 goto close_handles;
355 }
356
357 initrd->handle = h;
358
359 info_sz = 0;
360 status = efi_call_phys4(h->get_info, h, &info_guid,
361 &info_sz, NULL);
362 if (status != EFI_BUFFER_TOO_SMALL) {
363 efi_printk("Failed to get initrd info size\n");
364 goto close_handles;
365 }
366
367grow:
368 status = efi_call_phys3(sys_table->boottime->allocate_pool,
369 EFI_LOADER_DATA, info_sz, &info);
370 if (status != EFI_SUCCESS) {
371 efi_printk("Failed to alloc mem for initrd info\n");
372 goto close_handles;
373 }
374
375 status = efi_call_phys4(h->get_info, h, &info_guid,
376 &info_sz, info);
377 if (status == EFI_BUFFER_TOO_SMALL) {
378 efi_call_phys1(sys_table->boottime->free_pool, info);
379 goto grow;
380 }
381
382 file_sz = info->file_size;
383 efi_call_phys1(sys_table->boottime->free_pool, info);
384
385 if (status != EFI_SUCCESS) {
386 efi_printk("Failed to get initrd info\n");
387 goto close_handles;
388 }
389
390 initrd->size = file_sz;
391 initrd_total += file_sz;
392 }
393
394 if (initrd_total) {
395 unsigned long addr;
396
397 /*
398 * Multiple initrd's need to be at consecutive
399 * addresses in memory, so allocate enough memory for
400 * all the initrd's.
401 */
402 status = high_alloc(initrd_total, 0x1000,
403 &initrd_addr, hdr->initrd_addr_max);
404 if (status != EFI_SUCCESS) {
405 efi_printk("Failed to alloc highmem for initrds\n");
406 goto close_handles;
407 }
408
409 /* We've run out of free low memory. */
410 if (initrd_addr > hdr->initrd_addr_max) {
411 efi_printk("We've run out of free low memory\n");
412 status = EFI_INVALID_PARAMETER;
413 goto free_initrd_total;
414 }
415
416 addr = initrd_addr;
417 for (j = 0; j < nr_initrds; j++) {
418 u64 size;
419
420 size = initrds[j].size;
421 while (size) {
422 u64 chunksize;
423 if (size > EFI_READ_CHUNK_SIZE)
424 chunksize = EFI_READ_CHUNK_SIZE;
425 else
426 chunksize = size;
427 status = efi_call_phys3(fh->read,
428 initrds[j].handle,
429 &chunksize, addr);
430 if (status != EFI_SUCCESS) {
431 efi_printk("Failed to read initrd\n");
432 goto free_initrd_total;
433 }
434 addr += chunksize;
435 size -= chunksize;
436 }
437
438 efi_call_phys1(fh->close, initrds[j].handle);
439 }
440
441 }
442
443 efi_call_phys1(sys_table->boottime->free_pool, initrds);
444
445 hdr->ramdisk_image = initrd_addr;
446 hdr->ramdisk_size = initrd_total;
447
448 return status;
449
450free_initrd_total:
451 low_free(initrd_total, initrd_addr);
452
453close_handles:
454 for (k = j; k < i; k++)
455 efi_call_phys1(fh->close, initrds[k].handle);
456free_initrds:
457 efi_call_phys1(sys_table->boottime->free_pool, initrds);
458fail:
459 hdr->ramdisk_image = 0;
460 hdr->ramdisk_size = 0;
461
462 return status;
463}