crt_x86_64.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*
  2. * crt_x86_64.c
  3. *
  4. * Copyright (C) 2021 bzt (bztsrc@gitlab)
  5. *
  6. * Permission is hereby granted, free of charge, to any person
  7. * obtaining a copy of this software and associated documentation
  8. * files (the "Software"), to deal in the Software without
  9. * restriction, including without limitation the rights to use, copy,
  10. * modify, merge, publish, distribute, sublicense, and/or sell copies
  11. * of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be
  15. * included in all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  20. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  21. * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  22. * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  24. * DEALINGS IN THE SOFTWARE.
  25. *
  26. * This file is part of the POSIX-UEFI package.
  27. * @brief C runtime, bootstraps an EFI application to call standard main()
  28. *
  29. */
  30. #include <uefi.h>
  31. /* this is implemented by the application */
  32. extern int main(int argc, char_t **argv);
  33. /* definitions for elf relocations */
  34. #ifndef __clang__
  35. typedef uint64_t Elf64_Xword;
  36. typedef int64_t Elf64_Sxword;
  37. typedef uint64_t Elf64_Addr;
  38. typedef struct
  39. {
  40. Elf64_Sxword d_tag; /* Dynamic entry type */
  41. union
  42. {
  43. Elf64_Xword d_val; /* Integer value */
  44. Elf64_Addr d_ptr; /* Address value */
  45. } d_un;
  46. } Elf64_Dyn;
  47. #define DT_NULL 0 /* Marks end of dynamic section */
  48. #define DT_RELA 7 /* Address of Rela relocs */
  49. #define DT_RELASZ 8 /* Total size of Rela relocs */
  50. #define DT_RELAENT 9 /* Size of one Rela reloc */
  51. typedef struct
  52. {
  53. Elf64_Addr r_offset; /* Address */
  54. Elf64_Xword r_info; /* Relocation type and symbol index */
  55. } Elf64_Rel;
  56. #define ELF64_R_TYPE(i) ((i) & 0xffffffff)
  57. #define R_X86_64_RELATIVE 8 /* Adjust by program base */
  58. #endif
  59. /* globals to store system table pointers */
  60. efi_handle_t IM = NULL;
  61. efi_system_table_t *ST = NULL;
  62. efi_boot_services_t *BS = NULL;
  63. efi_runtime_services_t *RT = NULL;
  64. efi_loaded_image_protocol_t *LIP = NULL;
  65. #ifndef UEFI_NO_UTF8
  66. char *__argvutf8 = NULL;
  67. #endif
  68. /* we only need one .o file, so use inline Assembly here */
  69. void bootstrap(void)
  70. {
  71. __asm__ __volatile__ (
  72. /* call init in C */
  73. " .align 4\n"
  74. #ifndef __clang__
  75. " .globl _start\n"
  76. "_start:\n"
  77. " lea ImageBase(%rip), %rdi\n"
  78. " lea _DYNAMIC(%rip), %rsi\n"
  79. " call uefi_init\n"
  80. " ret\n"
  81. /* fake a relocation record, so that EFI won't complain */
  82. " .data\n"
  83. "dummy: .long 0\n"
  84. " .section .reloc, \"a\"\n"
  85. "label1:\n"
  86. " .long dummy-label1\n"
  87. " .long 10\n"
  88. " .word 0\n"
  89. ".text\n"
  90. #else
  91. " .globl __chkstk\n"
  92. "__chkstk:\n"
  93. " ret\n"
  94. #endif
  95. );
  96. /* setjmp and longjmp */
  97. __asm__ __volatile__ (
  98. " .globl setjmp\n"
  99. "setjmp:\n"
  100. " pop %rsi\n"
  101. " movq %rbx,0x00(%rdi)\n"
  102. " movq %rsp,0x08(%rdi)\n"
  103. " push %rsi\n"
  104. " movq %rbp,0x10(%rdi)\n"
  105. " movq %r12,0x18(%rdi)\n"
  106. " movq %r13,0x20(%rdi)\n"
  107. " movq %r14,0x28(%rdi)\n"
  108. " movq %r15,0x30(%rdi)\n"
  109. " movq %rsi,0x38(%rdi)\n"
  110. " xor %rax,%rax\n"
  111. " ret\n"
  112. );
  113. __asm__ __volatile__ (
  114. " .globl longjmp\n"
  115. "longjmp:\n"
  116. " movl %esi, %eax\n"
  117. " movq 0x00(%rdi), %rbx\n"
  118. " movq 0x08(%rdi), %rsp\n"
  119. " movq 0x10(%rdi), %rbp\n"
  120. " movq 0x18(%rdi), %r12\n"
  121. " movq 0x20(%rdi), %r13\n"
  122. " movq 0x28(%rdi), %r14\n"
  123. " movq 0x30(%rdi), %r15\n"
  124. " xor %rdx,%rdx\n"
  125. " mov $1,%rcx\n"
  126. " cmp %rax,%rdx\n"
  127. " cmove %rcx,%rax\n"
  128. " jmp *0x38(%rdi)\n"
  129. );
  130. }
  131. /**
  132. * Initialize POSIX-UEFI and call the application's main() function
  133. */
  134. efi_status_t uefi_init (
  135. #ifndef __clang__
  136. uintptr_t ldbase, Elf64_Dyn *dyn, efi_system_table_t *systab, efi_handle_t image
  137. #else
  138. efi_handle_t image, efi_system_table_t *systab
  139. #endif
  140. ) {
  141. efi_guid_t shpGuid = EFI_SHELL_PARAMETERS_PROTOCOL_GUID;
  142. efi_shell_parameters_protocol_t *shp = NULL;
  143. efi_guid_t shiGuid = SHELL_INTERFACE_PROTOCOL_GUID;
  144. efi_shell_interface_protocol_t *shi = NULL;
  145. efi_guid_t lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
  146. efi_status_t status;
  147. int argc = 0, i, ret;
  148. wchar_t **argv = NULL;
  149. #ifndef UEFI_NO_UTF8
  150. int j;
  151. char *s;
  152. #endif
  153. #ifndef __clang__
  154. long relsz = 0, relent = 0;
  155. Elf64_Rel *rel = 0;
  156. uintptr_t *addr;
  157. /* handle relocations */
  158. for (i = 0; dyn[i].d_tag != DT_NULL; ++i) {
  159. switch (dyn[i].d_tag) {
  160. case DT_RELA: rel = (Elf64_Rel*)((unsigned long)dyn[i].d_un.d_ptr + ldbase); break;
  161. case DT_RELASZ: relsz = dyn[i].d_un.d_val; break;
  162. case DT_RELAENT: relent = dyn[i].d_un.d_val; break;
  163. default: break;
  164. }
  165. }
  166. if (rel && relent) {
  167. while (relsz > 0) {
  168. if(ELF64_R_TYPE (rel->r_info) == R_X86_64_RELATIVE)
  169. { addr = (unsigned long *)(ldbase + rel->r_offset); *addr += ldbase; }
  170. rel = (Elf64_Rel*) ((char *) rel + relent);
  171. relsz -= relent;
  172. }
  173. }
  174. #else
  175. (void)i;
  176. #endif
  177. /* make sure SSE is enabled, because some say there are buggy firmware in the wild not doing that */
  178. __asm__ __volatile__ (
  179. " movq %cr0, %rax\n"
  180. " andb $0xF1, %al\n"
  181. " movq %rax, %cr0\n"
  182. " movq %cr4, %rax\n"
  183. " orw $3 << 9, %ax\n"
  184. " mov %rax, %cr4\n"
  185. );
  186. /* failsafes, should never happen */
  187. if(!image || !systab || !systab->BootServices || !systab->BootServices->HandleProtocol ||
  188. !systab->BootServices->OpenProtocol || !systab->BootServices->AllocatePool || !systab->BootServices->FreePool)
  189. return EFI_UNSUPPORTED;
  190. /* save EFI pointers and loaded image into globals */
  191. IM = image;
  192. ST = systab;
  193. BS = systab->BootServices;
  194. RT = systab->RuntimeServices;
  195. BS->HandleProtocol(image, &lipGuid, (void **)&LIP);
  196. /* get command line arguments */
  197. status = BS->OpenProtocol(image, &shpGuid, (void **)&shp, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
  198. if(!EFI_ERROR(status) && shp) { argc = (int)shp->Argc; argv = shp->Argv; }
  199. else {
  200. /* if shell 2.0 failed, fallback to shell 1.0 interface */
  201. status = BS->OpenProtocol(image, &shiGuid, (void **)&shi, image, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
  202. if(!EFI_ERROR(status) && shi) { argc = (int)shi->Argc; argv = shi->Argv; }
  203. }
  204. /* call main */
  205. #ifndef UEFI_NO_UTF8
  206. if(argc && argv) {
  207. ret = (argc + 1) * ((int)sizeof(uintptr_t) + 1);
  208. for(i = 0; i < argc; i++)
  209. for(j = 0; argv[i] && argv[i][j]; j++)
  210. ret += argv[i][j] < 0x80 ? 1 : (argv[i][j] < 0x800 ? 2 : 3);
  211. status = BS->AllocatePool(LIP ? LIP->ImageDataType : EfiLoaderData, (uintn_t)ret, (void **)&__argvutf8);
  212. if(EFI_ERROR(status) || !__argvutf8) { argc = 0; __argvutf8 = NULL; }
  213. else {
  214. s = __argvutf8 + argc * (int)sizeof(uintptr_t);
  215. *((uintptr_t*)s) = (uintptr_t)0; s += sizeof(uintptr_t);
  216. for(i = 0; i < argc; i++) {
  217. *((uintptr_t*)(__argvutf8 + i * (int)sizeof(uintptr_t))) = (uintptr_t)s;
  218. for(j = 0; argv[i] && argv[i][j]; j++) {
  219. if(argv[i][j]<0x80) { *s++ = argv[i][j]; } else
  220. if(argv[i][j]<0x800) { *s++ = ((argv[i][j]>>6)&0x1F)|0xC0; *s++ = (argv[i][j]&0x3F)|0x80; } else
  221. { *s++ = ((argv[i][j]>>12)&0x0F)|0xE0; *s++ = ((argv[i][j]>>6)&0x3F)|0x80; *s++ = (argv[i][j]&0x3F)|0x80; }
  222. }
  223. *s++ = 0;
  224. }
  225. }
  226. }
  227. ret = main(argc, (char**)__argvutf8);
  228. if(__argvutf8) BS->FreePool(__argvutf8);
  229. #else
  230. ret = main(argc, argv);
  231. #endif
  232. return ret ? EFIERR(ret) : EFI_SUCCESS;
  233. }