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 }