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