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