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