196 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * tools/testing/selftests/kvm/lib/elf.c4 *5 * Copyright (C) 2018, Google LLC.6 */7 8#include "test_util.h"9 10#include <bits/endian.h>11#include <linux/elf.h>12 13#include "kvm_util.h"14 15static void elfhdr_get(const char *filename, Elf64_Ehdr *hdrp)16{17 off_t offset_rv;18 19 /* Open the ELF file. */20 int fd;21 fd = open(filename, O_RDONLY);22 TEST_ASSERT(fd >= 0, "Failed to open ELF file,\n"23 " filename: %s\n"24 " rv: %i errno: %i", filename, fd, errno);25 26 /* Read in and validate ELF Identification Record.27 * The ELF Identification record is the first 16 (EI_NIDENT) bytes28 * of the ELF header, which is at the beginning of the ELF file.29 * For now it is only safe to read the first EI_NIDENT bytes. Once30 * read and validated, the value of e_ehsize can be used to determine31 * the real size of the ELF header.32 */33 unsigned char ident[EI_NIDENT];34 test_read(fd, ident, sizeof(ident));35 TEST_ASSERT((ident[EI_MAG0] == ELFMAG0) && (ident[EI_MAG1] == ELFMAG1)36 && (ident[EI_MAG2] == ELFMAG2) && (ident[EI_MAG3] == ELFMAG3),37 "ELF MAGIC Mismatch,\n"38 " filename: %s\n"39 " ident[EI_MAG0 - EI_MAG3]: %02x %02x %02x %02x\n"40 " Expected: %02x %02x %02x %02x",41 filename,42 ident[EI_MAG0], ident[EI_MAG1], ident[EI_MAG2], ident[EI_MAG3],43 ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3);44 TEST_ASSERT(ident[EI_CLASS] == ELFCLASS64,45 "Current implementation only able to handle ELFCLASS64,\n"46 " filename: %s\n"47 " ident[EI_CLASS]: %02x\n"48 " expected: %02x",49 filename,50 ident[EI_CLASS], ELFCLASS64);51 TEST_ASSERT(((BYTE_ORDER == LITTLE_ENDIAN)52 && (ident[EI_DATA] == ELFDATA2LSB))53 || ((BYTE_ORDER == BIG_ENDIAN)54 && (ident[EI_DATA] == ELFDATA2MSB)), "Current "55 "implementation only able to handle\n"56 "cases where the host and ELF file endianness\n"57 "is the same:\n"58 " host BYTE_ORDER: %u\n"59 " host LITTLE_ENDIAN: %u\n"60 " host BIG_ENDIAN: %u\n"61 " ident[EI_DATA]: %u\n"62 " ELFDATA2LSB: %u\n"63 " ELFDATA2MSB: %u",64 BYTE_ORDER, LITTLE_ENDIAN, BIG_ENDIAN,65 ident[EI_DATA], ELFDATA2LSB, ELFDATA2MSB);66 TEST_ASSERT(ident[EI_VERSION] == EV_CURRENT,67 "Current implementation only able to handle current "68 "ELF version,\n"69 " filename: %s\n"70 " ident[EI_VERSION]: %02x\n"71 " expected: %02x",72 filename, ident[EI_VERSION], EV_CURRENT);73 74 /* Read in the ELF header.75 * With the ELF Identification portion of the ELF header76 * validated, especially that the value at EI_VERSION is77 * as expected, it is now safe to read the entire ELF header.78 */79 offset_rv = lseek(fd, 0, SEEK_SET);80 TEST_ASSERT(offset_rv == 0, "Seek to ELF header failed,\n"81 " rv: %zi expected: %i", offset_rv, 0);82 test_read(fd, hdrp, sizeof(*hdrp));83 TEST_ASSERT(hdrp->e_phentsize == sizeof(Elf64_Phdr),84 "Unexpected physical header size,\n"85 " hdrp->e_phentsize: %x\n"86 " expected: %zx",87 hdrp->e_phentsize, sizeof(Elf64_Phdr));88 TEST_ASSERT(hdrp->e_shentsize == sizeof(Elf64_Shdr),89 "Unexpected section header size,\n"90 " hdrp->e_shentsize: %x\n"91 " expected: %zx",92 hdrp->e_shentsize, sizeof(Elf64_Shdr));93 close(fd);94}95 96/* VM ELF Load97 *98 * Input Args:99 * filename - Path to ELF file100 *101 * Output Args: None102 *103 * Input/Output Args:104 * vm - Pointer to opaque type that describes the VM.105 *106 * Return: None, TEST_ASSERT failures for all error conditions107 *108 * Loads the program image of the ELF file specified by filename,109 * into the virtual address space of the VM pointed to by vm. On entry110 * the VM needs to not be using any of the virtual address space used111 * by the image and it needs to have sufficient available physical pages, to112 * back the virtual pages used to load the image.113 */114void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename)115{116 off_t offset, offset_rv;117 Elf64_Ehdr hdr;118 119 /* Open the ELF file. */120 int fd;121 fd = open(filename, O_RDONLY);122 TEST_ASSERT(fd >= 0, "Failed to open ELF file,\n"123 " filename: %s\n"124 " rv: %i errno: %i", filename, fd, errno);125 126 /* Read in the ELF header. */127 elfhdr_get(filename, &hdr);128 129 /* For each program header.130 * The following ELF header members specify the location131 * and size of the program headers:132 *133 * e_phoff - File offset to start of program headers134 * e_phentsize - Size of each program header135 * e_phnum - Number of program header entries136 */137 for (unsigned int n1 = 0; n1 < hdr.e_phnum; n1++) {138 /* Seek to the beginning of the program header. */139 offset = hdr.e_phoff + (n1 * hdr.e_phentsize);140 offset_rv = lseek(fd, offset, SEEK_SET);141 TEST_ASSERT(offset_rv == offset,142 "Failed to seek to beginning of program header %u,\n"143 " filename: %s\n"144 " rv: %jd errno: %i",145 n1, filename, (intmax_t) offset_rv, errno);146 147 /* Read in the program header. */148 Elf64_Phdr phdr;149 test_read(fd, &phdr, sizeof(phdr));150 151 /* Skip if this header doesn't describe a loadable segment. */152 if (phdr.p_type != PT_LOAD)153 continue;154 155 /* Allocate memory for this segment within the VM. */156 TEST_ASSERT(phdr.p_memsz > 0, "Unexpected loadable segment "157 "memsize of 0,\n"158 " phdr index: %u p_memsz: 0x%" PRIx64,159 n1, (uint64_t) phdr.p_memsz);160 vm_vaddr_t seg_vstart = align_down(phdr.p_vaddr, vm->page_size);161 vm_vaddr_t seg_vend = phdr.p_vaddr + phdr.p_memsz - 1;162 seg_vend |= vm->page_size - 1;163 size_t seg_size = seg_vend - seg_vstart + 1;164 165 vm_vaddr_t vaddr = __vm_vaddr_alloc(vm, seg_size, seg_vstart,166 MEM_REGION_CODE);167 TEST_ASSERT(vaddr == seg_vstart, "Unable to allocate "168 "virtual memory for segment at requested min addr,\n"169 " segment idx: %u\n"170 " seg_vstart: 0x%lx\n"171 " vaddr: 0x%lx",172 n1, seg_vstart, vaddr);173 memset(addr_gva2hva(vm, vaddr), 0, seg_size);174 /* TODO(lhuemill): Set permissions of each memory segment175 * based on the least-significant 3 bits of phdr.p_flags.176 */177 178 /* Load portion of initial state that is contained within179 * the ELF file.180 */181 if (phdr.p_filesz) {182 offset_rv = lseek(fd, phdr.p_offset, SEEK_SET);183 TEST_ASSERT(offset_rv == phdr.p_offset,184 "Seek to program segment offset failed,\n"185 " program header idx: %u errno: %i\n"186 " offset_rv: 0x%jx\n"187 " expected: 0x%jx",188 n1, errno, (intmax_t) offset_rv,189 (intmax_t) phdr.p_offset);190 test_read(fd, addr_gva2hva(vm, phdr.p_vaddr),191 phdr.p_filesz);192 }193 }194 close(fd);195}196