diff options
author | Roy Franz <roy.franz@linaro.org> | 2013-09-22 18:45:27 -0400 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2013-09-25 07:34:34 -0400 |
commit | 7721da4c1ebf96ff815987011c1a0edef596b50a (patch) | |
tree | 66eaab33c5af6a00d65f75200c6b7838fd00302f /drivers/firmware | |
parent | ed37ddffe201bfad7be3c45bc08bd65b5298adca (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.c | 463 |
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 | |||
14 | struct initrd { | ||
15 | efi_file_handle_t *handle; | ||
16 | u64 size; | ||
17 | }; | ||
18 | |||
19 | |||
20 | |||
21 | |||
22 | static 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 | |||
30 | static 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 | |||
48 | static 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; | ||
57 | again: | ||
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 | |||
78 | fail: | ||
79 | *map = m; | ||
80 | return status; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Allocate at the highest possible address that is not above 'max'. | ||
85 | */ | ||
86 | static 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; | ||
101 | again: | ||
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 | |||
154 | free_pool: | ||
155 | efi_call_phys1(sys_table->boottime->free_pool, map); | ||
156 | |||
157 | fail: | ||
158 | return status; | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * Allocate at the lowest possible address. | ||
163 | */ | ||
164 | static 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 | |||
218 | free_pool: | ||
219 | efi_call_phys1(sys_table->boottime->free_pool, map); | ||
220 | fail: | ||
221 | return status; | ||
222 | } | ||
223 | |||
224 | static 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 | */ | ||
239 | static 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 | |||
367 | grow: | ||
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 | |||
450 | free_initrd_total: | ||
451 | low_free(initrd_total, initrd_addr); | ||
452 | |||
453 | close_handles: | ||
454 | for (k = j; k < i; k++) | ||
455 | efi_call_phys1(fh->close, initrds[k].handle); | ||
456 | free_initrds: | ||
457 | efi_call_phys1(sys_table->boottime->free_pool, initrds); | ||
458 | fail: | ||
459 | hdr->ramdisk_image = 0; | ||
460 | hdr->ramdisk_size = 0; | ||
461 | |||
462 | return status; | ||
463 | } | ||