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; | |
9ca8f72a | 438 | efi_guid_t proto = LOADED_IMAGE_PROTOCOL_GUID; |
291f3632 MF |
439 | int options_size = 0; |
440 | efi_status_t status; | |
5fef3870 | 441 | char *cmdline_ptr; |
291f3632 MF |
442 | u16 *s2; |
443 | u8 *s1; | |
444 | int i; | |
46f4582e RF |
445 | unsigned long ramdisk_addr; |
446 | unsigned long ramdisk_size; | |
291f3632 | 447 | |
9ca8f72a MF |
448 | sys_table = _table; |
449 | ||
450 | /* Check if we were booted by the EFI firmware */ | |
451 | if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) | |
452 | return NULL; | |
453 | ||
454 | status = efi_call_phys3(sys_table->boottime->handle_protocol, | |
455 | handle, &proto, (void *)&image); | |
456 | if (status != EFI_SUCCESS) { | |
876dc36a | 457 | efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n"); |
9ca8f72a MF |
458 | return NULL; |
459 | } | |
460 | ||
40e4530a RF |
461 | status = efi_low_alloc(sys_table, 0x4000, 1, |
462 | (unsigned long *)&boot_params); | |
9ca8f72a | 463 | if (status != EFI_SUCCESS) { |
876dc36a | 464 | efi_printk(sys_table, "Failed to alloc lowmem for boot params\n"); |
9ca8f72a MF |
465 | return NULL; |
466 | } | |
467 | ||
468 | memset(boot_params, 0x0, 0x4000); | |
469 | ||
470 | hdr = &boot_params->hdr; | |
471 | efi = &boot_params->efi_info; | |
472 | bi = &boot_params->apm_bios_info; | |
473 | sdt = &boot_params->sys_desc_table; | |
474 | ||
475 | /* Copy the second sector to boot_params */ | |
476 | memcpy(&hdr->jump, image->image_base + 512, 512); | |
477 | ||
478 | /* | |
479 | * Fill out some of the header fields ourselves because the | |
480 | * EFI firmware loader doesn't load the first sector. | |
481 | */ | |
482 | hdr->root_flags = 1; | |
483 | hdr->vid_mode = 0xffff; | |
484 | hdr->boot_flag = 0xAA55; | |
485 | ||
486 | hdr->code32_start = (__u64)(unsigned long)image->image_base; | |
487 | ||
291f3632 MF |
488 | hdr->type_of_loader = 0x21; |
489 | ||
490 | /* Convert unicode cmdline to ascii */ | |
5fef3870 RF |
491 | cmdline_ptr = efi_convert_cmdline_to_ascii(sys_table, image, |
492 | &options_size); | |
493 | if (!cmdline_ptr) | |
494 | goto fail; | |
495 | hdr->cmd_line_ptr = (unsigned long)cmdline_ptr; | |
291f3632 MF |
496 | |
497 | hdr->ramdisk_image = 0; | |
498 | hdr->ramdisk_size = 0; | |
499 | ||
291f3632 MF |
500 | /* Clear APM BIOS info */ |
501 | memset(bi, 0, sizeof(*bi)); | |
502 | ||
503 | memset(sdt, 0, sizeof(*sdt)); | |
504 | ||
46f4582e RF |
505 | status = handle_cmdline_files(sys_table, image, |
506 | (char *)(unsigned long)hdr->cmd_line_ptr, | |
507 | "initrd=", hdr->initrd_addr_max, | |
508 | &ramdisk_addr, &ramdisk_size); | |
9ca8f72a MF |
509 | if (status != EFI_SUCCESS) |
510 | goto fail2; | |
46f4582e RF |
511 | hdr->ramdisk_image = ramdisk_addr; |
512 | hdr->ramdisk_size = ramdisk_size; | |
9ca8f72a MF |
513 | |
514 | return boot_params; | |
515 | fail2: | |
0e1cadb0 | 516 | efi_free(sys_table, options_size, hdr->cmd_line_ptr); |
9ca8f72a | 517 | fail: |
40e4530a | 518 | efi_free(sys_table, 0x4000, (unsigned long)boot_params); |
9ca8f72a MF |
519 | return NULL; |
520 | } | |
521 | ||
d2078d5a LC |
522 | static void add_e820ext(struct boot_params *params, |
523 | struct setup_data *e820ext, u32 nr_entries) | |
9ca8f72a | 524 | { |
d2078d5a | 525 | struct setup_data *data; |
9ca8f72a | 526 | efi_status_t status; |
d2078d5a | 527 | unsigned long size; |
291f3632 | 528 | |
d2078d5a LC |
529 | e820ext->type = SETUP_E820_EXT; |
530 | e820ext->len = nr_entries * sizeof(struct e820entry); | |
531 | e820ext->next = 0; | |
291f3632 | 532 | |
d2078d5a | 533 | data = (struct setup_data *)(unsigned long)params->hdr.setup_data; |
291f3632 | 534 | |
d2078d5a LC |
535 | while (data && data->next) |
536 | data = (struct setup_data *)(unsigned long)data->next; | |
d3768d88 | 537 | |
d2078d5a LC |
538 | if (data) |
539 | data->next = (unsigned long)e820ext; | |
540 | else | |
541 | params->hdr.setup_data = (unsigned long)e820ext; | |
542 | } | |
291f3632 | 543 | |
d2078d5a LC |
544 | static efi_status_t setup_e820(struct boot_params *params, |
545 | struct setup_data *e820ext, u32 e820ext_size) | |
546 | { | |
547 | struct e820entry *e820_map = ¶ms->e820_map[0]; | |
548 | struct efi_info *efi = ¶ms->efi_info; | |
549 | struct e820entry *prev = NULL; | |
550 | u32 nr_entries; | |
551 | u32 nr_desc; | |
552 | int i; | |
291f3632 | 553 | |
291f3632 | 554 | nr_entries = 0; |
d2078d5a LC |
555 | nr_desc = efi->efi_memmap_size / efi->efi_memdesc_size; |
556 | ||
557 | for (i = 0; i < nr_desc; i++) { | |
291f3632 MF |
558 | efi_memory_desc_t *d; |
559 | unsigned int e820_type = 0; | |
d2078d5a | 560 | unsigned long m = efi->efi_memmap; |
291f3632 | 561 | |
d2078d5a | 562 | d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size)); |
291f3632 MF |
563 | switch (d->type) { |
564 | case EFI_RESERVED_TYPE: | |
565 | case EFI_RUNTIME_SERVICES_CODE: | |
566 | case EFI_RUNTIME_SERVICES_DATA: | |
567 | case EFI_MEMORY_MAPPED_IO: | |
568 | case EFI_MEMORY_MAPPED_IO_PORT_SPACE: | |
569 | case EFI_PAL_CODE: | |
570 | e820_type = E820_RESERVED; | |
571 | break; | |
572 | ||
573 | case EFI_UNUSABLE_MEMORY: | |
574 | e820_type = E820_UNUSABLE; | |
575 | break; | |
576 | ||
577 | case EFI_ACPI_RECLAIM_MEMORY: | |
578 | e820_type = E820_ACPI; | |
579 | break; | |
580 | ||
581 | case EFI_LOADER_CODE: | |
582 | case EFI_LOADER_DATA: | |
583 | case EFI_BOOT_SERVICES_CODE: | |
584 | case EFI_BOOT_SERVICES_DATA: | |
585 | case EFI_CONVENTIONAL_MEMORY: | |
586 | e820_type = E820_RAM; | |
587 | break; | |
588 | ||
589 | case EFI_ACPI_MEMORY_NVS: | |
590 | e820_type = E820_NVS; | |
591 | break; | |
592 | ||
593 | default: | |
594 | continue; | |
595 | } | |
596 | ||
597 | /* Merge adjacent mappings */ | |
598 | if (prev && prev->type == e820_type && | |
d2078d5a | 599 | (prev->addr + prev->size) == d->phys_addr) { |
291f3632 | 600 | prev->size += d->num_pages << 12; |
d2078d5a | 601 | continue; |
291f3632 | 602 | } |
d2078d5a LC |
603 | |
604 | if (nr_entries == ARRAY_SIZE(params->e820_map)) { | |
605 | u32 need = (nr_desc - i) * sizeof(struct e820entry) + | |
606 | sizeof(struct setup_data); | |
607 | ||
608 | if (!e820ext || e820ext_size < need) | |
609 | return EFI_BUFFER_TOO_SMALL; | |
610 | ||
611 | /* boot_params map full, switch to e820 extended */ | |
612 | e820_map = (struct e820entry *)e820ext->data; | |
613 | } | |
614 | ||
615 | e820_map->addr = d->phys_addr; | |
616 | e820_map->size = d->num_pages << PAGE_SHIFT; | |
617 | e820_map->type = e820_type; | |
618 | prev = e820_map++; | |
619 | nr_entries++; | |
291f3632 MF |
620 | } |
621 | ||
d2078d5a LC |
622 | if (nr_entries > ARRAY_SIZE(params->e820_map)) { |
623 | u32 nr_e820ext = nr_entries - ARRAY_SIZE(params->e820_map); | |
624 | ||
625 | add_e820ext(params, e820ext, nr_e820ext); | |
626 | nr_entries -= nr_e820ext; | |
627 | } | |
628 | ||
629 | params->e820_entries = (u8)nr_entries; | |
630 | ||
631 | return EFI_SUCCESS; | |
632 | } | |
633 | ||
634 | static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext, | |
635 | u32 *e820ext_size) | |
636 | { | |
637 | efi_status_t status; | |
638 | unsigned long size; | |
639 | ||
640 | size = sizeof(struct setup_data) + | |
641 | sizeof(struct e820entry) * nr_desc; | |
642 | ||
643 | if (*e820ext) { | |
644 | efi_call_phys1(sys_table->boottime->free_pool, *e820ext); | |
645 | *e820ext = NULL; | |
646 | *e820ext_size = 0; | |
647 | } | |
648 | ||
649 | status = efi_call_phys3(sys_table->boottime->allocate_pool, | |
650 | EFI_LOADER_DATA, size, e820ext); | |
651 | ||
652 | if (status == EFI_SUCCESS) | |
653 | *e820ext_size = size; | |
654 | ||
655 | return status; | |
656 | } | |
657 | ||
658 | static efi_status_t exit_boot(struct boot_params *boot_params, | |
659 | void *handle) | |
660 | { | |
661 | struct efi_info *efi = &boot_params->efi_info; | |
662 | unsigned long map_sz, key, desc_size; | |
663 | efi_memory_desc_t *mem_map; | |
664 | struct setup_data *e820ext; | |
665 | __u32 e820ext_size; | |
666 | __u32 nr_desc, prev_nr_desc; | |
667 | efi_status_t status; | |
668 | __u32 desc_version; | |
669 | bool called_exit = false; | |
670 | u8 nr_entries; | |
671 | int i; | |
672 | ||
673 | nr_desc = 0; | |
674 | e820ext = NULL; | |
675 | e820ext_size = 0; | |
676 | ||
677 | get_map: | |
678 | status = efi_get_memory_map(sys_table, &mem_map, &map_sz, &desc_size, | |
679 | &desc_version, &key); | |
680 | ||
681 | if (status != EFI_SUCCESS) | |
682 | return status; | |
683 | ||
684 | prev_nr_desc = nr_desc; | |
685 | nr_desc = map_sz / desc_size; | |
686 | if (nr_desc > prev_nr_desc && | |
687 | nr_desc > ARRAY_SIZE(boot_params->e820_map)) { | |
688 | u32 nr_e820ext = nr_desc - ARRAY_SIZE(boot_params->e820_map); | |
689 | ||
690 | status = alloc_e820ext(nr_e820ext, &e820ext, &e820ext_size); | |
691 | if (status != EFI_SUCCESS) | |
692 | goto free_mem_map; | |
693 | ||
694 | efi_call_phys1(sys_table->boottime->free_pool, mem_map); | |
695 | goto get_map; /* Allocated memory, get map again */ | |
696 | } | |
697 | ||
698 | memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32)); | |
699 | efi->efi_systab = (unsigned long)sys_table; | |
700 | efi->efi_memdesc_size = desc_size; | |
701 | efi->efi_memdesc_version = desc_version; | |
702 | efi->efi_memmap = (unsigned long)mem_map; | |
703 | efi->efi_memmap_size = map_sz; | |
704 | ||
705 | #ifdef CONFIG_X86_64 | |
706 | efi->efi_systab_hi = (unsigned long)sys_table >> 32; | |
707 | efi->efi_memmap_hi = (unsigned long)mem_map >> 32; | |
708 | #endif | |
709 | ||
710 | /* Might as well exit boot services now */ | |
711 | status = efi_call_phys2(sys_table->boottime->exit_boot_services, | |
712 | handle, key); | |
713 | if (status != EFI_SUCCESS) { | |
714 | /* | |
715 | * ExitBootServices() will fail if any of the event | |
716 | * handlers change the memory map. In which case, we | |
717 | * must be prepared to retry, but only once so that | |
718 | * we're guaranteed to exit on repeated failures instead | |
719 | * of spinning forever. | |
720 | */ | |
721 | if (called_exit) | |
722 | goto free_mem_map; | |
723 | ||
724 | called_exit = true; | |
725 | efi_call_phys1(sys_table->boottime->free_pool, mem_map); | |
726 | goto get_map; | |
727 | } | |
728 | ||
729 | /* Historic? */ | |
730 | boot_params->alt_mem_k = 32 * 1024; | |
731 | ||
732 | status = setup_e820(boot_params, e820ext, e820ext_size); | |
733 | if (status != EFI_SUCCESS) | |
734 | return status; | |
291f3632 MF |
735 | |
736 | return EFI_SUCCESS; | |
737 | ||
738 | free_mem_map: | |
ae8e9060 | 739 | efi_call_phys1(sys_table->boottime->free_pool, mem_map); |
291f3632 MF |
740 | return status; |
741 | } | |
742 | ||
9ca8f72a MF |
743 | |
744 | /* | |
745 | * On success we return a pointer to a boot_params structure, and NULL | |
746 | * on failure. | |
747 | */ | |
748 | struct boot_params *efi_main(void *handle, efi_system_table_t *_table, | |
749 | struct boot_params *boot_params) | |
750 | { | |
0ce6cda2 | 751 | struct desc_ptr *gdt; |
9ca8f72a MF |
752 | efi_loaded_image_t *image; |
753 | struct setup_header *hdr = &boot_params->hdr; | |
754 | efi_status_t status; | |
755 | struct desc_struct *desc; | |
756 | ||
757 | sys_table = _table; | |
758 | ||
759 | /* Check if we were booted by the EFI firmware */ | |
760 | if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) | |
761 | goto fail; | |
762 | ||
763 | setup_graphics(boot_params); | |
291f3632 | 764 | |
dd5fc854 MG |
765 | setup_efi_pci(boot_params); |
766 | ||
291f3632 MF |
767 | status = efi_call_phys3(sys_table->boottime->allocate_pool, |
768 | EFI_LOADER_DATA, sizeof(*gdt), | |
769 | (void **)&gdt); | |
9fa7deda | 770 | if (status != EFI_SUCCESS) { |
876dc36a | 771 | efi_printk(sys_table, "Failed to alloc mem for gdt structure\n"); |
291f3632 | 772 | goto fail; |
9fa7deda | 773 | } |
291f3632 MF |
774 | |
775 | gdt->size = 0x800; | |
40e4530a | 776 | status = efi_low_alloc(sys_table, gdt->size, 8, |
876dc36a | 777 | (unsigned long *)&gdt->address); |
9fa7deda | 778 | if (status != EFI_SUCCESS) { |
876dc36a | 779 | efi_printk(sys_table, "Failed to alloc mem for gdt\n"); |
291f3632 | 780 | goto fail; |
9fa7deda | 781 | } |
291f3632 | 782 | |
9ca8f72a MF |
783 | /* |
784 | * If the kernel isn't already loaded at the preferred load | |
785 | * address, relocate it. | |
786 | */ | |
787 | if (hdr->pref_address != hdr->code32_start) { | |
4a9f3a7c RF |
788 | unsigned long bzimage_addr = hdr->code32_start; |
789 | status = efi_relocate_kernel(sys_table, &bzimage_addr, | |
790 | hdr->init_size, hdr->init_size, | |
791 | hdr->pref_address, | |
792 | hdr->kernel_alignment); | |
9ca8f72a MF |
793 | if (status != EFI_SUCCESS) |
794 | goto fail; | |
4a9f3a7c RF |
795 | |
796 | hdr->pref_address = hdr->code32_start; | |
797 | hdr->code32_start = bzimage_addr; | |
9ca8f72a MF |
798 | } |
799 | ||
800 | status = exit_boot(boot_params, handle); | |
291f3632 MF |
801 | if (status != EFI_SUCCESS) |
802 | goto fail; | |
803 | ||
804 | memset((char *)gdt->address, 0x0, gdt->size); | |
805 | desc = (struct desc_struct *)gdt->address; | |
806 | ||
807 | /* The first GDT is a dummy and the second is unused. */ | |
808 | desc += 2; | |
809 | ||
810 | desc->limit0 = 0xffff; | |
811 | desc->base0 = 0x0000; | |
812 | desc->base1 = 0x0000; | |
813 | desc->type = SEG_TYPE_CODE | SEG_TYPE_EXEC_READ; | |
814 | desc->s = DESC_TYPE_CODE_DATA; | |
815 | desc->dpl = 0; | |
816 | desc->p = 1; | |
817 | desc->limit = 0xf; | |
818 | desc->avl = 0; | |
819 | desc->l = 0; | |
820 | desc->d = SEG_OP_SIZE_32BIT; | |
821 | desc->g = SEG_GRANULARITY_4KB; | |
822 | desc->base2 = 0x00; | |
823 | ||
824 | desc++; | |
825 | desc->limit0 = 0xffff; | |
826 | desc->base0 = 0x0000; | |
827 | desc->base1 = 0x0000; | |
828 | desc->type = SEG_TYPE_DATA | SEG_TYPE_READ_WRITE; | |
829 | desc->s = DESC_TYPE_CODE_DATA; | |
830 | desc->dpl = 0; | |
831 | desc->p = 1; | |
832 | desc->limit = 0xf; | |
833 | desc->avl = 0; | |
834 | desc->l = 0; | |
835 | desc->d = SEG_OP_SIZE_32BIT; | |
836 | desc->g = SEG_GRANULARITY_4KB; | |
837 | desc->base2 = 0x00; | |
838 | ||
839 | #ifdef CONFIG_X86_64 | |
840 | /* Task segment value */ | |
841 | desc++; | |
842 | desc->limit0 = 0x0000; | |
843 | desc->base0 = 0x0000; | |
844 | desc->base1 = 0x0000; | |
845 | desc->type = SEG_TYPE_TSS; | |
846 | desc->s = 0; | |
847 | desc->dpl = 0; | |
848 | desc->p = 1; | |
849 | desc->limit = 0x0; | |
850 | desc->avl = 0; | |
851 | desc->l = 0; | |
852 | desc->d = 0; | |
853 | desc->g = SEG_GRANULARITY_4KB; | |
854 | desc->base2 = 0x00; | |
855 | #endif /* CONFIG_X86_64 */ | |
856 | ||
291f3632 | 857 | asm volatile("cli"); |
0ce6cda2 | 858 | asm volatile ("lgdt %0" : : "m" (*gdt)); |
291f3632 MF |
859 | |
860 | return boot_params; | |
861 | fail: | |
862 | return NULL; | |
863 | } |