Commit | Line | Data |
---|---|---|
291f3632 MF |
1 | /* ----------------------------------------------------------------------- |
2 | * | |
3 | * Copyright 2011 Intel Corporation; author Matt Fleming | |
4 | * | |
5 | * This file is part of the Linux kernel, and is made available under | |
6 | * the terms of the GNU General Public License version 2. | |
7 | * | |
8 | * ----------------------------------------------------------------------- */ | |
9 | ||
10 | #include <linux/efi.h> | |
dd5fc854 | 11 | #include <linux/pci.h> |
291f3632 MF |
12 | #include <asm/efi.h> |
13 | #include <asm/setup.h> | |
14 | #include <asm/desc.h> | |
15 | ||
0f905a43 MF |
16 | #undef memcpy /* Use memcpy from misc.c */ |
17 | ||
291f3632 MF |
18 | #include "eboot.h" |
19 | ||
20 | static efi_system_table_t *sys_table; | |
21 | ||
291f3632 | 22 | |
7721da4c | 23 | #include "../../../../drivers/firmware/efi/efi-stub-helper.c" |
291f3632 | 24 | |
291f3632 | 25 | |
291f3632 MF |
26 | |
27 | static void find_bits(unsigned long mask, u8 *pos, u8 *size) | |
28 | { | |
29 | u8 first, len; | |
30 | ||
31 | first = 0; | |
32 | len = 0; | |
33 | ||
34 | if (mask) { | |
35 | while (!(mask & 0x1)) { | |
36 | mask = mask >> 1; | |
37 | first++; | |
38 | } | |
39 | ||
40 | while (mask & 0x1) { | |
41 | mask = mask >> 1; | |
42 | len++; | |
43 | } | |
44 | } | |
45 | ||
46 | *pos = first; | |
47 | *size = len; | |
48 | } | |
49 | ||
dd5fc854 MG |
50 | static efi_status_t setup_efi_pci(struct boot_params *params) |
51 | { | |
52 | efi_pci_io_protocol *pci; | |
53 | efi_status_t status; | |
54 | void **pci_handle; | |
55 | efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID; | |
56 | unsigned long nr_pci, size = 0; | |
57 | int i; | |
58 | struct setup_data *data; | |
59 | ||
bc754790 | 60 | data = (struct setup_data *)(unsigned long)params->hdr.setup_data; |
dd5fc854 MG |
61 | |
62 | while (data && data->next) | |
bc754790 | 63 | data = (struct setup_data *)(unsigned long)data->next; |
dd5fc854 MG |
64 | |
65 | status = efi_call_phys5(sys_table->boottime->locate_handle, | |
66 | EFI_LOCATE_BY_PROTOCOL, &pci_proto, | |
67 | NULL, &size, pci_handle); | |
68 | ||
69 | if (status == EFI_BUFFER_TOO_SMALL) { | |
70 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | |
71 | EFI_LOADER_DATA, size, &pci_handle); | |
72 | ||
73 | if (status != EFI_SUCCESS) | |
74 | return status; | |
75 | ||
76 | status = efi_call_phys5(sys_table->boottime->locate_handle, | |
77 | EFI_LOCATE_BY_PROTOCOL, &pci_proto, | |
78 | NULL, &size, pci_handle); | |
79 | } | |
80 | ||
81 | if (status != EFI_SUCCESS) | |
82 | goto free_handle; | |
83 | ||
84 | nr_pci = size / sizeof(void *); | |
85 | for (i = 0; i < nr_pci; i++) { | |
86 | void *h = pci_handle[i]; | |
87 | uint64_t attributes; | |
88 | struct pci_setup_rom *rom; | |
89 | ||
90 | status = efi_call_phys3(sys_table->boottime->handle_protocol, | |
91 | h, &pci_proto, &pci); | |
92 | ||
93 | if (status != EFI_SUCCESS) | |
94 | continue; | |
95 | ||
96 | if (!pci) | |
97 | continue; | |
98 | ||
b607e212 | 99 | #ifdef CONFIG_X86_64 |
dd5fc854 MG |
100 | status = efi_call_phys4(pci->attributes, pci, |
101 | EfiPciIoAttributeOperationGet, 0, | |
102 | &attributes); | |
b607e212 DW |
103 | #else |
104 | status = efi_call_phys5(pci->attributes, pci, | |
105 | EfiPciIoAttributeOperationGet, 0, 0, | |
106 | &attributes); | |
107 | #endif | |
dd5fc854 MG |
108 | if (status != EFI_SUCCESS) |
109 | continue; | |
110 | ||
dd5fc854 MG |
111 | if (!pci->romimage || !pci->romsize) |
112 | continue; | |
113 | ||
114 | size = pci->romsize + sizeof(*rom); | |
115 | ||
116 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | |
117 | EFI_LOADER_DATA, size, &rom); | |
118 | ||
119 | if (status != EFI_SUCCESS) | |
120 | continue; | |
121 | ||
122 | rom->data.type = SETUP_PCI; | |
123 | rom->data.len = size - sizeof(struct setup_data); | |
124 | rom->data.next = 0; | |
125 | rom->pcilen = pci->romsize; | |
126 | ||
127 | status = efi_call_phys5(pci->pci.read, pci, | |
128 | EfiPciIoWidthUint16, PCI_VENDOR_ID, | |
129 | 1, &(rom->vendor)); | |
130 | ||
131 | if (status != EFI_SUCCESS) | |
132 | goto free_struct; | |
133 | ||
134 | status = efi_call_phys5(pci->pci.read, pci, | |
135 | EfiPciIoWidthUint16, PCI_DEVICE_ID, | |
136 | 1, &(rom->devid)); | |
137 | ||
138 | if (status != EFI_SUCCESS) | |
139 | goto free_struct; | |
140 | ||
141 | status = efi_call_phys5(pci->get_location, pci, | |
142 | &(rom->segment), &(rom->bus), | |
143 | &(rom->device), &(rom->function)); | |
144 | ||
145 | if (status != EFI_SUCCESS) | |
146 | goto free_struct; | |
147 | ||
148 | memcpy(rom->romdata, pci->romimage, pci->romsize); | |
149 | ||
150 | if (data) | |
bc754790 | 151 | data->next = (unsigned long)rom; |
dd5fc854 | 152 | else |
bc754790 | 153 | params->hdr.setup_data = (unsigned long)rom; |
dd5fc854 MG |
154 | |
155 | data = (struct setup_data *)rom; | |
156 | ||
157 | continue; | |
158 | free_struct: | |
159 | efi_call_phys1(sys_table->boottime->free_pool, rom); | |
160 | } | |
161 | ||
162 | free_handle: | |
163 | efi_call_phys1(sys_table->boottime->free_pool, pci_handle); | |
164 | return status; | |
165 | } | |
166 | ||
291f3632 MF |
167 | /* |
168 | * See if we have Graphics Output Protocol | |
169 | */ | |
170 | static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, | |
171 | unsigned long size) | |
172 | { | |
173 | struct efi_graphics_output_protocol *gop, *first_gop; | |
174 | struct efi_pixel_bitmask pixel_info; | |
175 | unsigned long nr_gops; | |
176 | efi_status_t status; | |
177 | void **gop_handle; | |
178 | u16 width, height; | |
179 | u32 fb_base, fb_size; | |
180 | u32 pixels_per_scan_line; | |
181 | int pixel_format; | |
182 | int i; | |
183 | ||
184 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | |
185 | EFI_LOADER_DATA, size, &gop_handle); | |
186 | if (status != EFI_SUCCESS) | |
187 | return status; | |
188 | ||
189 | status = efi_call_phys5(sys_table->boottime->locate_handle, | |
190 | EFI_LOCATE_BY_PROTOCOL, proto, | |
191 | NULL, &size, gop_handle); | |
192 | if (status != EFI_SUCCESS) | |
193 | goto free_handle; | |
194 | ||
195 | first_gop = NULL; | |
196 | ||
197 | nr_gops = size / sizeof(void *); | |
198 | for (i = 0; i < nr_gops; i++) { | |
199 | struct efi_graphics_output_mode_info *info; | |
38cb5ef4 MG |
200 | efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; |
201 | bool conout_found = false; | |
202 | void *dummy; | |
291f3632 MF |
203 | void *h = gop_handle[i]; |
204 | ||
205 | status = efi_call_phys3(sys_table->boottime->handle_protocol, | |
206 | h, proto, &gop); | |
207 | if (status != EFI_SUCCESS) | |
208 | continue; | |
209 | ||
38cb5ef4 MG |
210 | status = efi_call_phys3(sys_table->boottime->handle_protocol, |
211 | h, &conout_proto, &dummy); | |
212 | ||
213 | if (status == EFI_SUCCESS) | |
214 | conout_found = true; | |
291f3632 MF |
215 | |
216 | status = efi_call_phys4(gop->query_mode, gop, | |
217 | gop->mode->mode, &size, &info); | |
38cb5ef4 | 218 | if (status == EFI_SUCCESS && (!first_gop || conout_found)) { |
291f3632 | 219 | /* |
38cb5ef4 MG |
220 | * Systems that use the UEFI Console Splitter may |
221 | * provide multiple GOP devices, not all of which are | |
222 | * backed by real hardware. The workaround is to search | |
223 | * for a GOP implementing the ConOut protocol, and if | |
224 | * one isn't found, to just fall back to the first GOP. | |
291f3632 MF |
225 | */ |
226 | width = info->horizontal_resolution; | |
227 | height = info->vertical_resolution; | |
228 | fb_base = gop->mode->frame_buffer_base; | |
229 | fb_size = gop->mode->frame_buffer_size; | |
230 | pixel_format = info->pixel_format; | |
231 | pixel_info = info->pixel_information; | |
232 | pixels_per_scan_line = info->pixels_per_scan_line; | |
233 | ||
234 | /* | |
38cb5ef4 | 235 | * Once we've found a GOP supporting ConOut, |
291f3632 MF |
236 | * don't bother looking any further. |
237 | */ | |
70a479cb | 238 | first_gop = gop; |
38cb5ef4 | 239 | if (conout_found) |
291f3632 | 240 | break; |
291f3632 MF |
241 | } |
242 | } | |
243 | ||
244 | /* Did we find any GOPs? */ | |
245 | if (!first_gop) | |
246 | goto free_handle; | |
247 | ||
248 | /* EFI framebuffer */ | |
249 | si->orig_video_isVGA = VIDEO_TYPE_EFI; | |
250 | ||
251 | si->lfb_width = width; | |
252 | si->lfb_height = height; | |
253 | si->lfb_base = fb_base; | |
291f3632 MF |
254 | si->pages = 1; |
255 | ||
256 | if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) { | |
257 | si->lfb_depth = 32; | |
258 | si->lfb_linelength = pixels_per_scan_line * 4; | |
259 | si->red_size = 8; | |
260 | si->red_pos = 0; | |
261 | si->green_size = 8; | |
262 | si->green_pos = 8; | |
263 | si->blue_size = 8; | |
264 | si->blue_pos = 16; | |
265 | si->rsvd_size = 8; | |
266 | si->rsvd_pos = 24; | |
267 | } else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) { | |
268 | si->lfb_depth = 32; | |
269 | si->lfb_linelength = pixels_per_scan_line * 4; | |
270 | si->red_size = 8; | |
271 | si->red_pos = 16; | |
272 | si->green_size = 8; | |
273 | si->green_pos = 8; | |
274 | si->blue_size = 8; | |
275 | si->blue_pos = 0; | |
276 | si->rsvd_size = 8; | |
277 | si->rsvd_pos = 24; | |
278 | } else if (pixel_format == PIXEL_BIT_MASK) { | |
279 | find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size); | |
280 | find_bits(pixel_info.green_mask, &si->green_pos, | |
281 | &si->green_size); | |
282 | find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size); | |
283 | find_bits(pixel_info.reserved_mask, &si->rsvd_pos, | |
284 | &si->rsvd_size); | |
285 | si->lfb_depth = si->red_size + si->green_size + | |
286 | si->blue_size + si->rsvd_size; | |
287 | si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8; | |
288 | } else { | |
289 | si->lfb_depth = 4; | |
290 | si->lfb_linelength = si->lfb_width / 2; | |
291 | si->red_size = 0; | |
292 | si->red_pos = 0; | |
293 | si->green_size = 0; | |
294 | si->green_pos = 0; | |
295 | si->blue_size = 0; | |
296 | si->blue_pos = 0; | |
297 | si->rsvd_size = 0; | |
298 | si->rsvd_pos = 0; | |
299 | } | |
300 | ||
e9b10953 MG |
301 | si->lfb_size = si->lfb_linelength * si->lfb_height; |
302 | ||
f462ed93 MG |
303 | si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; |
304 | ||
291f3632 MF |
305 | free_handle: |
306 | efi_call_phys1(sys_table->boottime->free_pool, gop_handle); | |
307 | return status; | |
308 | } | |
309 | ||
310 | /* | |
311 | * See if we have Universal Graphics Adapter (UGA) protocol | |
312 | */ | |
313 | static efi_status_t setup_uga(struct screen_info *si, efi_guid_t *uga_proto, | |
314 | unsigned long size) | |
315 | { | |
316 | struct efi_uga_draw_protocol *uga, *first_uga; | |
317 | unsigned long nr_ugas; | |
318 | efi_status_t status; | |
319 | u32 width, height; | |
320 | void **uga_handle = NULL; | |
321 | int i; | |
322 | ||
323 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | |
324 | EFI_LOADER_DATA, size, &uga_handle); | |
325 | if (status != EFI_SUCCESS) | |
326 | return status; | |
327 | ||
328 | status = efi_call_phys5(sys_table->boottime->locate_handle, | |
329 | EFI_LOCATE_BY_PROTOCOL, uga_proto, | |
330 | NULL, &size, uga_handle); | |
331 | if (status != EFI_SUCCESS) | |
332 | goto free_handle; | |
333 | ||
334 | first_uga = NULL; | |
335 | ||
336 | nr_ugas = size / sizeof(void *); | |
337 | for (i = 0; i < nr_ugas; i++) { | |
338 | efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID; | |
339 | void *handle = uga_handle[i]; | |
340 | u32 w, h, depth, refresh; | |
341 | void *pciio; | |
342 | ||
343 | status = efi_call_phys3(sys_table->boottime->handle_protocol, | |
344 | handle, uga_proto, &uga); | |
345 | if (status != EFI_SUCCESS) | |
346 | continue; | |
347 | ||
348 | efi_call_phys3(sys_table->boottime->handle_protocol, | |
349 | handle, &pciio_proto, &pciio); | |
350 | ||
351 | status = efi_call_phys5(uga->get_mode, uga, &w, &h, | |
352 | &depth, &refresh); | |
353 | if (status == EFI_SUCCESS && (!first_uga || pciio)) { | |
354 | width = w; | |
355 | height = h; | |
356 | ||
357 | /* | |
358 | * Once we've found a UGA supporting PCIIO, | |
359 | * don't bother looking any further. | |
360 | */ | |
361 | if (pciio) | |
362 | break; | |
363 | ||
364 | first_uga = uga; | |
365 | } | |
366 | } | |
367 | ||
368 | if (!first_uga) | |
369 | goto free_handle; | |
370 | ||
371 | /* EFI framebuffer */ | |
372 | si->orig_video_isVGA = VIDEO_TYPE_EFI; | |
373 | ||
374 | si->lfb_depth = 32; | |
375 | si->lfb_width = width; | |
376 | si->lfb_height = height; | |
377 | ||
378 | si->red_size = 8; | |
379 | si->red_pos = 16; | |
380 | si->green_size = 8; | |
381 | si->green_pos = 8; | |
382 | si->blue_size = 8; | |
383 | si->blue_pos = 0; | |
384 | si->rsvd_size = 8; | |
385 | si->rsvd_pos = 24; | |
386 | ||
387 | ||
388 | free_handle: | |
389 | efi_call_phys1(sys_table->boottime->free_pool, uga_handle); | |
390 | return status; | |
391 | } | |
392 | ||
393 | void setup_graphics(struct boot_params *boot_params) | |
394 | { | |
395 | efi_guid_t graphics_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; | |
396 | struct screen_info *si; | |
397 | efi_guid_t uga_proto = EFI_UGA_PROTOCOL_GUID; | |
398 | efi_status_t status; | |
399 | unsigned long size; | |
400 | void **gop_handle = NULL; | |
401 | void **uga_handle = NULL; | |
402 | ||
403 | si = &boot_params->screen_info; | |
404 | memset(si, 0, sizeof(*si)); | |
405 | ||
406 | size = 0; | |
407 | status = efi_call_phys5(sys_table->boottime->locate_handle, | |
408 | EFI_LOCATE_BY_PROTOCOL, &graphics_proto, | |
409 | NULL, &size, gop_handle); | |
410 | if (status == EFI_BUFFER_TOO_SMALL) | |
411 | status = setup_gop(si, &graphics_proto, size); | |
412 | ||
413 | if (status != EFI_SUCCESS) { | |
414 | size = 0; | |
415 | status = efi_call_phys5(sys_table->boottime->locate_handle, | |
416 | EFI_LOCATE_BY_PROTOCOL, &uga_proto, | |
417 | NULL, &size, uga_handle); | |
418 | if (status == EFI_BUFFER_TOO_SMALL) | |
419 | setup_uga(si, &uga_proto, size); | |
420 | } | |
421 | } | |
422 | ||
291f3632 MF |
423 | |
424 | /* | |
425 | * Because the x86 boot code expects to be passed a boot_params we | |
426 | * need to create one ourselves (usually the bootloader would create | |
427 | * one for us). | |
428 | */ | |
9ca8f72a | 429 | struct boot_params *make_boot_params(void *handle, efi_system_table_t *_table) |
291f3632 | 430 | { |
9ca8f72a MF |
431 | struct boot_params *boot_params; |
432 | struct sys_desc_table *sdt; | |
433 | struct apm_bios_info *bi; | |
434 | struct setup_header *hdr; | |
435 | struct efi_info *efi; | |
436 | efi_loaded_image_t *image; | |
437 | void *options; | |
438 | u32 load_options_size; | |
439 | efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; | |
291f3632 MF |
440 | int options_size = 0; |
441 | efi_status_t status; | |
291f3632 | 442 | unsigned long cmdline; |
291f3632 MF |
443 | u16 *s2; |
444 | u8 *s1; | |
445 | int i; | |
446 | ||
9ca8f72a MF |
447 | sys_table = _table; |
448 | ||
449 | /* Check if we were booted by the EFI firmware */ | |
450 | if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) | |
451 | return NULL; | |
452 | ||
453 | status = efi_call_phys3(sys_table->boottime->handle_protocol, | |
454 | handle, &proto, (void *)&image); | |
455 | if (status != EFI_SUCCESS) { | |
876dc36a | 456 | efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); |
9ca8f72a MF |
457 | return NULL; |
458 | } | |
459 | ||
40e4530a RF |
460 | status = efi_low_alloc(sys_table, 0x4000, 1, |
461 | (unsigned long *)&boot_params); | |
9ca8f72a | 462 | if (status != EFI_SUCCESS) { |
876dc36a | 463 | efi_printk(sys_table, "Failed to alloc lowmem for boot params\n"); |
9ca8f72a MF |
464 | return NULL; |
465 | } | |
466 | ||
467 | memset(boot_params, 0x0, 0x4000); | |
468 | ||
469 | hdr = &boot_params->hdr; | |
470 | efi = &boot_params->efi_info; | |
471 | bi = &boot_params->apm_bios_info; | |
472 | sdt = &boot_params->sys_desc_table; | |
473 | ||
474 | /* Copy the second sector to boot_params */ | |
475 | memcpy(&hdr->jump, image->image_base + 512, 512); | |
476 | ||
477 | /* | |
478 | * Fill out some of the header fields ourselves because the | |
479 | * EFI firmware loader doesn't load the first sector. | |
480 | */ | |
481 | hdr->root_flags = 1; | |
482 | hdr->vid_mode = 0xffff; | |
483 | hdr->boot_flag = 0xAA55; | |
484 | ||
485 | hdr->code32_start = (__u64)(unsigned long)image->image_base; | |
486 | ||
291f3632 MF |
487 | hdr->type_of_loader = 0x21; |
488 | ||
489 | /* Convert unicode cmdline to ascii */ | |
9ca8f72a MF |
490 | options = image->load_options; |
491 | load_options_size = image->load_options_size / 2; /* ASCII */ | |
291f3632 MF |
492 | cmdline = 0; |
493 | s2 = (u16 *)options; | |
494 | ||
495 | if (s2) { | |
496 | while (*s2 && *s2 != '\n' && options_size < load_options_size) { | |
497 | s2++; | |
498 | options_size++; | |
499 | } | |
500 | ||
501 | if (options_size) { | |
502 | if (options_size > hdr->cmdline_size) | |
503 | options_size = hdr->cmdline_size; | |
504 | ||
505 | options_size++; /* NUL termination */ | |
506 | ||
40e4530a | 507 | status = efi_low_alloc(sys_table, options_size, 1, |
876dc36a | 508 | &cmdline); |
9fa7deda | 509 | if (status != EFI_SUCCESS) { |
876dc36a | 510 | efi_printk(sys_table, "Failed to alloc mem for cmdline\n"); |
291f3632 | 511 | goto fail; |
9fa7deda | 512 | } |
291f3632 MF |
513 | |
514 | s1 = (u8 *)(unsigned long)cmdline; | |
515 | s2 = (u16 *)options; | |
516 | ||
517 | for (i = 0; i < options_size - 1; i++) | |
518 | *s1++ = *s2++; | |
519 | ||
520 | *s1 = '\0'; | |
521 | } | |
522 | } | |
523 | ||
524 | hdr->cmd_line_ptr = cmdline; | |
525 | ||
526 | hdr->ramdisk_image = 0; | |
527 | hdr->ramdisk_size = 0; | |
528 | ||
291f3632 MF |
529 | /* Clear APM BIOS info */ |
530 | memset(bi, 0, sizeof(*bi)); | |
531 | ||
532 | memset(sdt, 0, sizeof(*sdt)); | |
533 | ||
876dc36a | 534 | status = handle_ramdisks(sys_table, image, hdr); |
9ca8f72a MF |
535 | if (status != EFI_SUCCESS) |
536 | goto fail2; | |
537 | ||
538 | return boot_params; | |
539 | fail2: | |
540 | if (options_size) | |
40e4530a | 541 | efi_free(sys_table, options_size, hdr->cmd_line_ptr); |
9ca8f72a | 542 | fail: |
40e4530a | 543 | efi_free(sys_table, 0x4000, (unsigned long)boot_params); |
9ca8f72a MF |
544 | return NULL; |
545 | } | |
546 | ||
547 | static efi_status_t exit_boot(struct boot_params *boot_params, | |
548 | void *handle) | |
549 | { | |
550 | struct efi_info *efi = &boot_params->efi_info; | |
551 | struct e820entry *e820_map = &boot_params->e820_map[0]; | |
552 | struct e820entry *prev = NULL; | |
553 | unsigned long size, key, desc_size, _size; | |
554 | efi_memory_desc_t *mem_map; | |
555 | efi_status_t status; | |
556 | __u32 desc_version; | |
d3768d88 | 557 | bool called_exit = false; |
9ca8f72a MF |
558 | u8 nr_entries; |
559 | int i; | |
291f3632 MF |
560 | |
561 | size = sizeof(*mem_map) * 32; | |
562 | ||
563 | again: | |
d3768d88 | 564 | size += sizeof(*mem_map) * 2; |
291f3632 | 565 | _size = size; |
40e4530a | 566 | status = efi_low_alloc(sys_table, size, 1, (unsigned long *)&mem_map); |
291f3632 | 567 | if (status != EFI_SUCCESS) |
9ca8f72a | 568 | return status; |
291f3632 | 569 | |
d3768d88 | 570 | get_map: |
291f3632 MF |
571 | status = efi_call_phys5(sys_table->boottime->get_memory_map, &size, |
572 | mem_map, &key, &desc_size, &desc_version); | |
573 | if (status == EFI_BUFFER_TOO_SMALL) { | |
40e4530a | 574 | efi_free(sys_table, _size, (unsigned long)mem_map); |
291f3632 MF |
575 | goto again; |
576 | } | |
577 | ||
578 | if (status != EFI_SUCCESS) | |
579 | goto free_mem_map; | |
580 | ||
9ca8f72a | 581 | memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); |
291f3632 MF |
582 | efi->efi_systab = (unsigned long)sys_table; |
583 | efi->efi_memdesc_size = desc_size; | |
584 | efi->efi_memdesc_version = desc_version; | |
585 | efi->efi_memmap = (unsigned long)mem_map; | |
586 | efi->efi_memmap_size = size; | |
587 | ||
588 | #ifdef CONFIG_X86_64 | |
589 | efi->efi_systab_hi = (unsigned long)sys_table >> 32; | |
590 | efi->efi_memmap_hi = (unsigned long)mem_map >> 32; | |
591 | #endif | |
592 | ||
593 | /* Might as well exit boot services now */ | |
594 | status = efi_call_phys2(sys_table->boottime->exit_boot_services, | |
595 | handle, key); | |
d3768d88 ZB |
596 | if (status != EFI_SUCCESS) { |
597 | /* | |
598 | * ExitBootServices() will fail if any of the event | |
599 | * handlers change the memory map. In which case, we | |
600 | * must be prepared to retry, but only once so that | |
601 | * we're guaranteed to exit on repeated failures instead | |
602 | * of spinning forever. | |
603 | */ | |
604 | if (called_exit) | |
605 | goto free_mem_map; | |
606 | ||
607 | called_exit = true; | |
608 | goto get_map; | |
609 | } | |
291f3632 MF |
610 | |
611 | /* Historic? */ | |
612 | boot_params->alt_mem_k = 32 * 1024; | |
613 | ||
614 | /* | |
615 | * Convert the EFI memory map to E820. | |
616 | */ | |
617 | nr_entries = 0; | |
618 | for (i = 0; i < size / desc_size; i++) { | |
619 | efi_memory_desc_t *d; | |
620 | unsigned int e820_type = 0; | |
621 | unsigned long m = (unsigned long)mem_map; | |
622 | ||
623 | d = (efi_memory_desc_t *)(m + (i * desc_size)); | |
624 | switch (d->type) { | |
625 | case EFI_RESERVED_TYPE: | |
626 | case EFI_RUNTIME_SERVICES_CODE: | |
627 | case EFI_RUNTIME_SERVICES_DATA: | |
628 | case EFI_MEMORY_MAPPED_IO: | |
629 | case EFI_MEMORY_MAPPED_IO_PORT_SPACE: | |
630 | case EFI_PAL_CODE: | |
631 | e820_type = E820_RESERVED; | |
632 | break; | |
633 | ||
634 | case EFI_UNUSABLE_MEMORY: | |
635 | e820_type = E820_UNUSABLE; | |
636 | break; | |
637 | ||
638 | case EFI_ACPI_RECLAIM_MEMORY: | |
639 | e820_type = E820_ACPI; | |
640 | break; | |
641 | ||
642 | case EFI_LOADER_CODE: | |
643 | case EFI_LOADER_DATA: | |
644 | case EFI_BOOT_SERVICES_CODE: | |
645 | case EFI_BOOT_SERVICES_DATA: | |
646 | case EFI_CONVENTIONAL_MEMORY: | |
647 | e820_type = E820_RAM; | |
648 | break; | |
649 | ||
650 | case EFI_ACPI_MEMORY_NVS: | |
651 | e820_type = E820_NVS; | |
652 | break; | |
653 | ||
654 | default: | |
655 | continue; | |
656 | } | |
657 | ||
658 | /* Merge adjacent mappings */ | |
659 | if (prev && prev->type == e820_type && | |
660 | (prev->addr + prev->size) == d->phys_addr) | |
661 | prev->size += d->num_pages << 12; | |
662 | else { | |
663 | e820_map->addr = d->phys_addr; | |
664 | e820_map->size = d->num_pages << 12; | |
665 | e820_map->type = e820_type; | |
666 | prev = e820_map++; | |
667 | nr_entries++; | |
668 | } | |
669 | } | |
670 | ||
671 | boot_params->e820_entries = nr_entries; | |
672 | ||
673 | return EFI_SUCCESS; | |
674 | ||
675 | free_mem_map: | |
40e4530a | 676 | efi_free(sys_table, _size, (unsigned long)mem_map); |
291f3632 MF |
677 | return status; |
678 | } | |
679 | ||
9ca8f72a MF |
680 | |
681 | /* | |
682 | * On success we return a pointer to a boot_params structure, and NULL | |
683 | * on failure. | |
684 | */ | |
685 | struct boot_params *efi_main(void *handle, efi_system_table_t *_table, | |
686 | struct boot_params *boot_params) | |
687 | { | |
688 | struct desc_ptr *gdt, *idt; | |
689 | efi_loaded_image_t *image; | |
690 | struct setup_header *hdr = &boot_params->hdr; | |
691 | efi_status_t status; | |
692 | struct desc_struct *desc; | |
693 | ||
694 | sys_table = _table; | |
695 | ||
696 | /* Check if we were booted by the EFI firmware */ | |
697 | if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) | |
698 | goto fail; | |
699 | ||
700 | setup_graphics(boot_params); | |
291f3632 | 701 | |
dd5fc854 MG |
702 | setup_efi_pci(boot_params); |
703 | ||
291f3632 MF |
704 | status = efi_call_phys3(sys_table->boottime->allocate_pool, |
705 | EFI_LOADER_DATA, sizeof(*gdt), | |
706 | (void **)&gdt); | |
9fa7deda | 707 | if (status != EFI_SUCCESS) { |
876dc36a | 708 | efi_printk(sys_table, "Failed to alloc mem for gdt structure\n"); |
291f3632 | 709 | goto fail; |
9fa7deda | 710 | } |
291f3632 MF |
711 | |
712 | gdt->size = 0x800; | |
40e4530a | 713 | status = efi_low_alloc(sys_table, gdt->size, 8, |
876dc36a | 714 | (unsigned long *)&gdt->address); |
9fa7deda | 715 | if (status != EFI_SUCCESS) { |
876dc36a | 716 | efi_printk(sys_table, "Failed to alloc mem for gdt\n"); |
291f3632 | 717 | goto fail; |
9fa7deda | 718 | } |
291f3632 MF |
719 | |
720 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | |
721 | EFI_LOADER_DATA, sizeof(*idt), | |
722 | (void **)&idt); | |
9fa7deda | 723 | if (status != EFI_SUCCESS) { |
876dc36a | 724 | efi_printk(sys_table, "Failed to alloc mem for idt structure\n"); |
291f3632 | 725 | goto fail; |
9fa7deda | 726 | } |
291f3632 MF |
727 | |
728 | idt->size = 0; | |
729 | idt->address = 0; | |
730 | ||
9ca8f72a MF |
731 | /* |
732 | * If the kernel isn't already loaded at the preferred load | |
733 | * address, relocate it. | |
734 | */ | |
735 | if (hdr->pref_address != hdr->code32_start) { | |
736 | status = relocate_kernel(hdr); | |
737 | ||
738 | if (status != EFI_SUCCESS) | |
739 | goto fail; | |
740 | } | |
741 | ||
742 | status = exit_boot(boot_params, handle); | |
291f3632 MF |
743 | if (status != EFI_SUCCESS) |
744 | goto fail; | |
745 | ||
746 | memset((char *)gdt->address, 0x0, gdt->size); | |
747 | desc = (struct desc_struct *)gdt->address; | |
748 | ||
749 | /* The first GDT is a dummy and the second is unused. */ | |
750 | desc += 2; | |
751 | ||
752 | desc->limit0 = 0xffff; | |
753 | desc->base0 = 0x0000; | |
754 | desc->base1 = 0x0000; | |
755 | desc->type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; | |
756 | desc->s = DESC_TYPE_CODE_DATA; | |
757 | desc->dpl = 0; | |
758 | desc->p = 1; | |
759 | desc->limit = 0xf; | |
760 | desc->avl = 0; | |
761 | desc->l = 0; | |
762 | desc->d = SEG_OP_SIZE_32BIT; | |
763 | desc->g = SEG_GRANULARITY_4KB; | |
764 | desc->base2 = 0x00; | |
765 | ||
766 | desc++; | |
767 | desc->limit0 = 0xffff; | |
768 | desc->base0 = 0x0000; | |
769 | desc->base1 = 0x0000; | |
770 | desc->type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; | |
771 | desc->s = DESC_TYPE_CODE_DATA; | |
772 | desc->dpl = 0; | |
773 | desc->p = 1; | |
774 | desc->limit = 0xf; | |
775 | desc->avl = 0; | |
776 | desc->l = 0; | |
777 | desc->d = SEG_OP_SIZE_32BIT; | |
778 | desc->g = SEG_GRANULARITY_4KB; | |
779 | desc->base2 = 0x00; | |
780 | ||
781 | #ifdef CONFIG_X86_64 | |
782 | /* Task segment value */ | |
783 | desc++; | |
784 | desc->limit0 = 0x0000; | |
785 | desc->base0 = 0x0000; | |
786 | desc->base1 = 0x0000; | |
787 | desc->type = SEG_TYPE_TSS; | |
788 | desc->s = 0; | |
789 | desc->dpl = 0; | |
790 | desc->p = 1; | |
791 | desc->limit = 0x0; | |
792 | desc->avl = 0; | |
793 | desc->l = 0; | |
794 | desc->d = 0; | |
795 | desc->g = SEG_GRANULARITY_4KB; | |
796 | desc->base2 = 0x00; | |
797 | #endif /* CONFIG_X86_64 */ | |
798 | ||
799 | asm volatile ("lidt %0" : : "m" (*idt)); | |
800 | asm volatile ("lgdt %0" : : "m" (*gdt)); | |
801 | ||
802 | asm volatile("cli"); | |
803 | ||
804 | return boot_params; | |
805 | fail: | |
806 | return NULL; | |
807 | } |