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 }