diff options
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/efi/efi-stub-helper.c | 76 |
1 files changed, 55 insertions, 21 deletions
diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index 333ed9c37751..361e24d8d665 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c | |||
@@ -485,38 +485,72 @@ fail: | |||
485 | 485 | ||
486 | return status; | 486 | return status; |
487 | } | 487 | } |
488 | 488 | /* | |
489 | static efi_status_t relocate_kernel(struct setup_header *hdr) | 489 | * Relocate a kernel image, either compressed or uncompressed. |
490 | * In the ARM64 case, all kernel images are currently | ||
491 | * uncompressed, and as such when we relocate it we need to | ||
492 | * allocate additional space for the BSS segment. Any low | ||
493 | * memory that this function should avoid needs to be | ||
494 | * unavailable in the EFI memory map, as if the preferred | ||
495 | * address is not available the lowest available address will | ||
496 | * be used. | ||
497 | */ | ||
498 | static efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg, | ||
499 | unsigned long *image_addr, | ||
500 | unsigned long image_size, | ||
501 | unsigned long alloc_size, | ||
502 | unsigned long preferred_addr, | ||
503 | unsigned long alignment) | ||
490 | { | 504 | { |
491 | unsigned long start, nr_pages; | 505 | unsigned long cur_image_addr; |
506 | unsigned long new_addr = 0; | ||
492 | efi_status_t status; | 507 | efi_status_t status; |
508 | unsigned long nr_pages; | ||
509 | efi_physical_addr_t efi_addr = preferred_addr; | ||
510 | |||
511 | if (!image_addr || !image_size || !alloc_size) | ||
512 | return EFI_INVALID_PARAMETER; | ||
513 | if (alloc_size < image_size) | ||
514 | return EFI_INVALID_PARAMETER; | ||
515 | |||
516 | cur_image_addr = *image_addr; | ||
493 | 517 | ||
494 | /* | 518 | /* |
495 | * The EFI firmware loader could have placed the kernel image | 519 | * The EFI firmware loader could have placed the kernel image |
496 | * anywhere in memory, but the kernel has various restrictions | 520 | * anywhere in memory, but the kernel has restrictions on the |
497 | * on the max physical address it can run at. Attempt to move | 521 | * max physical address it can run at. Some architectures |
498 | * the kernel to boot_params.pref_address, or as low as | 522 | * also have a prefered address, so first try to relocate |
499 | * possible. | 523 | * to the preferred address. If that fails, allocate as low |
524 | * as possible while respecting the required alignment. | ||
500 | */ | 525 | */ |
501 | start = hdr->pref_address; | 526 | nr_pages = round_up(alloc_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; |
502 | nr_pages = round_up(hdr->init_size, EFI_PAGE_SIZE) / EFI_PAGE_SIZE; | 527 | status = efi_call_phys4(sys_table_arg->boottime->allocate_pages, |
503 | |||
504 | status = efi_call_phys4(sys_table->boottime->allocate_pages, | ||
505 | EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, | 528 | EFI_ALLOCATE_ADDRESS, EFI_LOADER_DATA, |
506 | nr_pages, &start); | 529 | nr_pages, &efi_addr); |
530 | new_addr = efi_addr; | ||
531 | /* | ||
532 | * If preferred address allocation failed allocate as low as | ||
533 | * possible. | ||
534 | */ | ||
507 | if (status != EFI_SUCCESS) { | 535 | if (status != EFI_SUCCESS) { |
508 | status = efi_low_alloc(sys_table, hdr->init_size, | 536 | status = efi_low_alloc(sys_table_arg, alloc_size, alignment, |
509 | hdr->kernel_alignment, &start); | 537 | &new_addr); |
510 | if (status != EFI_SUCCESS) | 538 | } |
511 | efi_printk(sys_table, "Failed to alloc mem for kernel\n"); | 539 | if (status != EFI_SUCCESS) { |
540 | efi_printk(sys_table_arg, "ERROR: Failed to allocate usable memory for kernel.\n"); | ||
541 | return status; | ||
512 | } | 542 | } |
513 | 543 | ||
514 | if (status == EFI_SUCCESS) | 544 | /* |
515 | memcpy((void *)start, (void *)(unsigned long)hdr->code32_start, | 545 | * We know source/dest won't overlap since both memory ranges |
516 | hdr->init_size); | 546 | * have been allocated by UEFI, so we can safely use memcpy. |
547 | */ | ||
548 | memcpy((void *)new_addr, (void *)cur_image_addr, image_size); | ||
549 | /* Zero any extra space we may have allocated for BSS. */ | ||
550 | memset((void *)(new_addr + image_size), alloc_size - image_size, 0); | ||
517 | 551 | ||
518 | hdr->pref_address = hdr->code32_start; | 552 | /* Return the new address of the relocated image. */ |
519 | hdr->code32_start = (__u32)start; | 553 | *image_addr = new_addr; |
520 | 554 | ||
521 | return status; | 555 | return status; |
522 | } | 556 | } |