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