elf.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. #include "elf.h"
  2. #include "dragonstub/linux-efi.h"
  3. #include "dragonstub/linux/align.h"
  4. #include "dragonstub/printk.h"
  5. #include "dragonstub/types.h"
  6. #include "efidef.h"
  7. #include <efi.h>
  8. #include <efiapi.h>
  9. #include <efidevp.h>
  10. #include <efilib.h>
  11. #include <dragonstub/dragonstub.h>
  12. #include <dragonstub/elfloader.h>
  13. /// @brief 校验ELF文件头
  14. /// @param buf 缓冲区
  15. /// @param bufsize 缓冲区大小
  16. /// @return
  17. static bool verify_ident(const void *buf, u64 bufsize)
  18. {
  19. if (bufsize < EI_NIDENT) {
  20. // 太短,不是ELF
  21. return false;
  22. }
  23. // 检查magic number
  24. for (int i = 0; i < EI_CLASS; i++) {
  25. u8 c = *(u8 *)(buf + i);
  26. if (c != ELFMAG[i]) {
  27. // 不是ELF magic number,跳过
  28. efi_err("ELF magic number not match\n");
  29. return false;
  30. }
  31. }
  32. // verify ELF Version
  33. u8 version = *(u8 *)(buf + EI_VERSION);
  34. if (version != EV_CURRENT) {
  35. efi_err("ELF version not match, expected EV_CURRENT(%d), got %d\n",
  36. EV_CURRENT, version);
  37. // 不是当前版本,跳过
  38. return false;
  39. }
  40. // verify ELF Class
  41. u8 class = *(u8 *)(buf + EI_CLASS);
  42. if (class != ELFCLASS64) {
  43. efi_err("ELF class not match, expected ELFCLASS64(%d), got %d\n",
  44. ELFCLASS64, class);
  45. // 不是64位,跳过
  46. return false;
  47. }
  48. return true;
  49. }
  50. bool elf_check(const void *payload_start, u64 payload_size)
  51. {
  52. // 校验ELF文件头
  53. if (!verify_ident(payload_start, payload_size)) {
  54. return false;
  55. }
  56. // 检查架构
  57. Elf64_Ehdr *ehdr = (Elf64_Ehdr *)payload_start;
  58. #ifdef CONFIG_riscv64
  59. if (ehdr->e_machine != EM_RISCV) {
  60. efi_err("ELF machine not match, expected EM_RISCV(%d), got %d\n",
  61. EM_RISCV, ehdr->e_machine);
  62. return false;
  63. }
  64. #else
  65. // 还没有对当前架构进行检查,抛出编译错误
  66. #error "Unimplement ELF arch test for current cross compile arch"
  67. #endif
  68. return true;
  69. }
  70. /// @brief 获取ELF文件头
  71. /// @param payload_start 文件起始地址
  72. /// @param payload_size 文件大小
  73. /// @param ehdr 返回的ELF文件头
  74. /// @return
  75. efi_status_t elf_get_header(const void *payload_start, u64 payload_size,
  76. Elf64_Ehdr **ehdr)
  77. {
  78. if (!verify_ident(payload_start, payload_size)) {
  79. return EFI_INVALID_PARAMETER;
  80. }
  81. *ehdr = (Elf64_Ehdr *)payload_start;
  82. return EFI_SUCCESS;
  83. }
  84. static void print_elf_info(Elf64_Ehdr *ehdr)
  85. {
  86. efi_info("ELF header:\n");
  87. efi_printk(" e_type: %d\n", ehdr->e_type);
  88. efi_printk(" e_machine: %d\n", ehdr->e_machine);
  89. efi_printk(" e_version: %d\n", ehdr->e_version);
  90. efi_printk(" e_entry: %p\n", ehdr->e_entry);
  91. efi_printk(" e_phoff: %p\n", ehdr->e_phoff);
  92. efi_printk(" e_shoff: %p\n", ehdr->e_shoff);
  93. efi_printk(" e_flags: %d\n", ehdr->e_flags);
  94. efi_printk(" e_ehsize: %d\n", ehdr->e_ehsize);
  95. efi_printk(" e_phentsize: %d\n", ehdr->e_phentsize);
  96. efi_printk(" e_phnum: %d\n", ehdr->e_phnum);
  97. efi_printk(" e_shentsize: %d\n", ehdr->e_shentsize);
  98. efi_printk(" e_shnum: %d\n", ehdr->e_shnum);
  99. efi_printk(" e_shstrndx: %d\n", ehdr->e_shstrndx);
  100. }
  101. static efi_status_t parse_phdrs(const void *payload_start, u64 payload_size,
  102. const Elf64_Ehdr *ehdr, u32 *ret_segments_nr,
  103. Elf64_Phdr **ret_phdr)
  104. {
  105. if (ehdr->e_phnum == 0) {
  106. efi_err("No program header\n");
  107. return EFI_INVALID_PARAMETER;
  108. }
  109. if (ehdr->e_phentsize != sizeof(Elf64_Phdr)) {
  110. efi_err("Invalid program header size: %d, expected %d\n",
  111. ehdr->e_phentsize, sizeof(Elf64_Phdr));
  112. return EFI_INVALID_PARAMETER;
  113. }
  114. u16 phnum = ehdr->e_phnum;
  115. if (phnum == PN_XNUM) {
  116. u64 shoff = ehdr->e_shoff;
  117. if (shoff == 0) {
  118. efi_err("No section header\n");
  119. return EFI_INVALID_PARAMETER;
  120. }
  121. if (shoff + sizeof(Elf64_Shdr) > payload_size) {
  122. efi_err("Section header out of range\n");
  123. return EFI_INVALID_PARAMETER;
  124. }
  125. Elf64_Shdr *shdr = (Elf64_Shdr *)(payload_start + shoff);
  126. phnum = shdr[0].sh_info;
  127. if (phnum == 0) {
  128. efi_err("shdr[0].sh_info indicates no program header\n");
  129. return EFI_INVALID_PARAMETER;
  130. }
  131. }
  132. size_t phoff = ehdr->e_phoff;
  133. size_t phsize = ehdr->e_phentsize;
  134. size_t total_size = phnum * phsize;
  135. if (phoff + total_size > payload_size) {
  136. efi_err("Program header out of range\n");
  137. return EFI_INVALID_PARAMETER;
  138. }
  139. Elf64_Phdr *phdr = (Elf64_Phdr *)(payload_start + phoff);
  140. *ret_segments_nr = phnum;
  141. *ret_phdr = phdr;
  142. return EFI_SUCCESS;
  143. }
  144. /*
  145. * Distro versions of GRUB may ignore the BSS allocation entirely (i.e., fail
  146. * to provide space, and fail to zero it). Check for this condition by double
  147. * checking that the first and the last byte of the image are covered by the
  148. * same EFI memory map entry.
  149. */
  150. static bool check_image_region(u64 base, u64 size)
  151. {
  152. struct efi_boot_memmap *map;
  153. efi_status_t status;
  154. bool ret = false;
  155. u64 map_offset;
  156. status = efi_get_memory_map(&map, false);
  157. if (status != EFI_SUCCESS)
  158. return false;
  159. for (map_offset = 0; map_offset < map->map_size;
  160. map_offset += map->desc_size) {
  161. efi_memory_desc_t *md = (void *)map->map + map_offset;
  162. u64 end = md->PhysicalStart + md->NumberOfPages * EFI_PAGE_SIZE;
  163. /*
  164. * Find the region that covers base, and return whether
  165. * it covers base+size bytes.
  166. */
  167. if (base >= md->PhysicalStart && base < end) {
  168. ret = (base + size) <= end;
  169. break;
  170. }
  171. }
  172. efi_bs_call(FreePool, map);
  173. return ret;
  174. }
  175. /**
  176. * efi_remap_image_all_rwx - Remap a loaded image with the appropriate permissions
  177. * for code and data
  178. *
  179. * @image_base: the base of the image in memory
  180. * @alloc_size: the size of the area in memory occupied by the image
  181. *
  182. * efi_remap_image() uses the EFI memory attribute protocol to remap the code
  183. * region of the loaded image read-only/executable, and the remainder
  184. * read-write/non-executable. The code region is assumed to start at the base
  185. * of the image, and will therefore cover the PE/COFF header as well.
  186. */
  187. void efi_remap_image_all_rwx(unsigned long image_base, unsigned alloc_size)
  188. {
  189. efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
  190. efi_memory_attribute_protocol_t *memattr;
  191. efi_status_t status;
  192. u64 attr;
  193. /*
  194. * If the firmware implements the EFI_MEMORY_ATTRIBUTE_PROTOCOL, let's
  195. * invoke it to remap the text/rodata region of the decompressed image
  196. * as read-only and the data/bss region as non-executable.
  197. */
  198. status = efi_bs_call(LocateProtocol, &guid, NULL, (void **)&memattr);
  199. if (status != EFI_SUCCESS)
  200. return;
  201. // Get the current attributes for the entire region
  202. status = memattr->get_memory_attributes(memattr, image_base, alloc_size,
  203. &attr);
  204. if (status != EFI_SUCCESS) {
  205. efi_warn(
  206. "Failed to retrieve memory attributes for image region: 0x%lx\n",
  207. status);
  208. return;
  209. }
  210. efi_debug("Current attributes for image region: 0x%lx\n", attr);
  211. // If the entire region was already mapped as non-exec, clear the
  212. // attribute from the code region. Otherwise, set it on the data
  213. // region.
  214. if (attr & EFI_MEMORY_XP) {
  215. status = memattr->clear_memory_attributes(
  216. memattr, image_base, alloc_size, EFI_MEMORY_XP);
  217. if (status != EFI_SUCCESS)
  218. efi_warn("Failed to remap region executable\n");
  219. }
  220. if (attr & EFI_MEMORY_WP) {
  221. status = memattr->clear_memory_attributes(
  222. memattr, image_base, alloc_size, EFI_MEMORY_WP);
  223. if (status != EFI_SUCCESS)
  224. efi_warn("Failed to remap region writable\n");
  225. }
  226. if (attr & EFI_MEMORY_RP) {
  227. status = memattr->clear_memory_attributes(
  228. memattr, image_base, alloc_size, EFI_MEMORY_RP);
  229. if (status != EFI_SUCCESS)
  230. efi_warn("Failed to remap region readable\n");
  231. }
  232. }
  233. efi_status_t efi_allocate_kernel_memory(const Elf64_Phdr *phdr_start,
  234. u32 phdrs_nr, u64 *ret_paddr,
  235. u64 *ret_size, u64 *ret_min_paddr,
  236. u64 *ret_max_paddr)
  237. {
  238. efi_status_t status = EFI_SUCCESS;
  239. const Elf64_Phdr *phdr = phdr_start;
  240. u64 min_paddr = UINT64_MAX;
  241. u64 max_paddr = 0;
  242. for (u32 i = 0; i < phdrs_nr; ++i, ++phdr) {
  243. if (phdr->p_type != PT_LOAD) {
  244. continue;
  245. }
  246. if (phdr->p_align & !EFI_PAGE_SIZE) {
  247. efi_err("ELF segment alignment should be multiple of EFI_PAGE_SIZE(%d), but got %d\n",
  248. EFI_PAGE_SIZE, phdr->p_align);
  249. return EFI_INVALID_PARAMETER;
  250. }
  251. min_paddr = min(min_paddr, (u64)phdr->p_paddr);
  252. max_paddr =
  253. max(max_paddr, (u64)(phdr->p_paddr + phdr->p_memsz));
  254. }
  255. u64 mem_size = ALIGN_UP(max_paddr - min_paddr, EFI_PAGE_SIZE);
  256. status = efi_allocate_pages_aligned(mem_size, ret_paddr, UINT64_MAX,
  257. EFI_PAGE_SIZE, EfiLoaderData);
  258. // status = efi_allocate_pages_exact(mem_size, paddr);
  259. if (status != EFI_SUCCESS) {
  260. efi_err("Failed to allocate pages for ELF segment: status: %d, page_size=%d, min_paddr=%p, max_paddr=%p, mem_size=%d. Maybe an OOM error or section overlaps.\n",
  261. status, EFI_PAGE_SIZE, ret_paddr, max_paddr, mem_size);
  262. return status;
  263. }
  264. *ret_size = mem_size;
  265. *ret_min_paddr = min_paddr;
  266. *ret_max_paddr = max_paddr;
  267. efi_info("Allocated kernel memory: paddr=%p, mem_size= %d bytes\n",
  268. *ret_paddr, mem_size);
  269. // zeroed the memory
  270. memset((void *)(*ret_paddr), 0, mem_size);
  271. efi_remap_image_all_rwx(*ret_paddr, mem_size);
  272. return EFI_SUCCESS;
  273. }
  274. static efi_status_t load_program(const void *payload_start, u64 payload_size,
  275. const Elf64_Phdr *phdr_start, u32 phdrs_nr,
  276. u64 *ret_program_mem_paddr,
  277. u64 *ret_program_mem_size, u64 *ret_min_paddr)
  278. {
  279. efi_status_t status = EFI_SUCCESS;
  280. u64 allocated_paddr = 0;
  281. u64 allocated_size = 0;
  282. u64 min_paddr = 0;
  283. u64 max_paddr = 0;
  284. status = efi_allocate_kernel_memory(phdr_start, phdrs_nr,
  285. &allocated_paddr, &allocated_size,
  286. &min_paddr, &max_paddr);
  287. if (status != EFI_SUCCESS) {
  288. efi_err("Failed to allocate kernel memory\n");
  289. return status;
  290. }
  291. const Elf64_Phdr *phdr = phdr_start;
  292. for (u32 i = 0; i < phdrs_nr; ++i, ++phdr) {
  293. if (phdr->p_type != PT_LOAD) {
  294. continue;
  295. }
  296. if (phdr->p_align & !EFI_PAGE_SIZE) {
  297. efi_err("ELF segment alignment should be multiple of EFI_PAGE_SIZE(%d), but got %d\n",
  298. EFI_PAGE_SIZE, phdr->p_align);
  299. status = EFI_INVALID_PARAMETER;
  300. goto failed;
  301. }
  302. u64 paddr = phdr->p_paddr;
  303. u64 mem_size = phdr->p_memsz;
  304. u64 file_size = phdr->p_filesz;
  305. u64 file_offset = phdr->p_offset;
  306. // efi_debug(
  307. // "loading segment: paddr=%p, mem_size=%d, file_size=%d\n",
  308. // paddr, mem_size, file_size);
  309. if (file_offset + file_size > payload_size) {
  310. status = EFI_INVALID_PARAMETER;
  311. goto failed;
  312. }
  313. if (mem_size < file_size) {
  314. status = EFI_INVALID_PARAMETER;
  315. goto failed;
  316. }
  317. if (mem_size == 0) {
  318. continue;
  319. }
  320. memcpy((void *)(allocated_paddr + (paddr - min_paddr)),
  321. payload_start + file_offset, file_size);
  322. // efi_debug(
  323. // "segment loaded: paddr=%p, mem_size=%d, file_size=%d\n",
  324. // paddr, mem_size, file_size);
  325. }
  326. *ret_program_mem_paddr = allocated_paddr;
  327. *ret_program_mem_size = allocated_size;
  328. *ret_min_paddr = min_paddr;
  329. return EFI_SUCCESS;
  330. failed:
  331. efi_free(allocated_size, allocated_paddr);
  332. return status;
  333. }
  334. efi_status_t load_elf(struct payload_info *payload_info)
  335. {
  336. const void *payload_start = (void *)payload_info->payload_addr;
  337. u64 payload_size = payload_info->payload_size;
  338. Elf64_Ehdr *ehdr = NULL;
  339. efi_status_t status =
  340. elf_get_header(payload_start, payload_size, &ehdr);
  341. if (status != EFI_SUCCESS) {
  342. efi_err("Failed to get ELF header\n");
  343. return status;
  344. }
  345. ASSERT(ehdr != NULL);
  346. print_elf_info(ehdr);
  347. u32 phdrs_nr = 0;
  348. Elf64_Phdr *phdr_start = NULL;
  349. status = parse_phdrs(payload_start, payload_size, ehdr, &phdrs_nr,
  350. &phdr_start);
  351. if (status != EFI_SUCCESS) {
  352. efi_err("Failed to parse ELF segments\n");
  353. return status;
  354. }
  355. efi_debug("program headers: %d\n", phdrs_nr);
  356. u64 program_paddr = 0;
  357. u64 program_size = 0;
  358. u64 image_link_base_paddr = 0;
  359. load_program(payload_start, payload_size, phdr_start, phdrs_nr,
  360. &program_paddr, &program_size, &image_link_base_paddr);
  361. payload_info->loaded_paddr = program_paddr;
  362. payload_info->loaded_size = program_size;
  363. payload_info->kernel_entry =
  364. ehdr->e_entry - image_link_base_paddr + program_paddr;
  365. // 处理权限问题
  366. efi_remap_image_all_rwx(program_paddr, program_size);
  367. extern void _start(void);
  368. extern void _image_end(void);
  369. u64 image_size = (u64)&_image_end - (u64)&_start;
  370. efi_debug("image_size: %d\n", image_size);
  371. efi_remap_image_all_rwx((u64)&_start, (image_size + 4095) & ~4095);
  372. return EFI_SUCCESS;
  373. }