efi-stub.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. /*
  2. * Copyright (C) 2013, 2014 Linaro Ltd; <roy.franz@linaro.org>
  3. *
  4. * This file implements the EFI boot stub for the arm64 kernel.
  5. * Adapted from ARM version by Mark Salter <msalter@redhat.com>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License version 2 as
  9. * published by the Free Software Foundation.
  10. *
  11. */
  12. #include <linux/efi.h>
  13. #include <asm/efi.h>
  14. #include <asm/sections.h>
  15. efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg,
  16. unsigned long *image_addr,
  17. unsigned long *image_size,
  18. unsigned long *reserve_addr,
  19. unsigned long *reserve_size,
  20. unsigned long dram_base,
  21. efi_loaded_image_t *image)
  22. {
  23. efi_status_t status;
  24. unsigned long kernel_size, kernel_memsize = 0;
  25. unsigned long nr_pages;
  26. void *old_image_addr = (void *)*image_addr;
  27. unsigned long preferred_offset;
  28. /*
  29. * The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond
  30. * a 2 MB aligned base, which itself may be lower than dram_base, as
  31. * long as the resulting offset equals or exceeds it.
  32. */
  33. preferred_offset = round_down(dram_base, SZ_2M) + TEXT_OFFSET;
  34. if (preferred_offset < dram_base)
  35. preferred_offset += SZ_2M;
  36. /* Relocate the image, if required. */
  37. kernel_size = _edata - _text;
  38. if (*image_addr != preferred_offset) {
  39. kernel_memsize = kernel_size + (_end - _edata);
  40. /*
  41. * First, try a straight allocation at the preferred offset.
  42. * This will work around the issue where, if dram_base == 0x0,
  43. * efi_low_alloc() refuses to allocate at 0x0 (to prevent the
  44. * address of the allocation to be mistaken for a FAIL return
  45. * value or a NULL pointer). It will also ensure that, on
  46. * platforms where the [dram_base, dram_base + TEXT_OFFSET)
  47. * interval is partially occupied by the firmware (like on APM
  48. * Mustang), we can still place the kernel at the address
  49. * 'dram_base + TEXT_OFFSET'.
  50. */
  51. *image_addr = *reserve_addr = preferred_offset;
  52. nr_pages = round_up(kernel_memsize, EFI_ALLOC_ALIGN) /
  53. EFI_PAGE_SIZE;
  54. status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS,
  55. EFI_LOADER_DATA, nr_pages,
  56. (efi_physical_addr_t *)reserve_addr);
  57. if (status != EFI_SUCCESS) {
  58. kernel_memsize += TEXT_OFFSET;
  59. status = efi_low_alloc(sys_table_arg, kernel_memsize,
  60. SZ_2M, reserve_addr);
  61. if (status != EFI_SUCCESS) {
  62. pr_efi_err(sys_table_arg, "Failed to relocate kernel\n");
  63. return status;
  64. }
  65. *image_addr = *reserve_addr + TEXT_OFFSET;
  66. }
  67. memcpy((void *)*image_addr, old_image_addr, kernel_size);
  68. *reserve_size = kernel_memsize;
  69. }
  70. return EFI_SUCCESS;
  71. }