| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright(c) 2016-20 Intel Corporation. */ |
| |
| #include <elf.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include "defines.h" |
| #include "main.h" |
| #include "../kselftest.h" |
| |
| static const uint64_t MAGIC = 0x1122334455667788ULL; |
| vdso_sgx_enter_enclave_t eenter; |
| |
| struct vdso_symtab { |
| Elf64_Sym *elf_symtab; |
| const char *elf_symstrtab; |
| Elf64_Word *elf_hashtab; |
| }; |
| |
| static void *vdso_get_base_addr(char *envp[]) |
| { |
| Elf64_auxv_t *auxv; |
| int i; |
| |
| for (i = 0; envp[i]; i++) |
| ; |
| |
| auxv = (Elf64_auxv_t *)&envp[i + 1]; |
| |
| for (i = 0; auxv[i].a_type != AT_NULL; i++) { |
| if (auxv[i].a_type == AT_SYSINFO_EHDR) |
| return (void *)auxv[i].a_un.a_val; |
| } |
| |
| return NULL; |
| } |
| |
| static Elf64_Dyn *vdso_get_dyntab(void *addr) |
| { |
| Elf64_Ehdr *ehdr = addr; |
| Elf64_Phdr *phdrtab = addr + ehdr->e_phoff; |
| int i; |
| |
| for (i = 0; i < ehdr->e_phnum; i++) |
| if (phdrtab[i].p_type == PT_DYNAMIC) |
| return addr + phdrtab[i].p_offset; |
| |
| return NULL; |
| } |
| |
| static void *vdso_get_dyn(void *addr, Elf64_Dyn *dyntab, Elf64_Sxword tag) |
| { |
| int i; |
| |
| for (i = 0; dyntab[i].d_tag != DT_NULL; i++) |
| if (dyntab[i].d_tag == tag) |
| return addr + dyntab[i].d_un.d_ptr; |
| |
| return NULL; |
| } |
| |
| static bool vdso_get_symtab(void *addr, struct vdso_symtab *symtab) |
| { |
| Elf64_Dyn *dyntab = vdso_get_dyntab(addr); |
| |
| symtab->elf_symtab = vdso_get_dyn(addr, dyntab, DT_SYMTAB); |
| if (!symtab->elf_symtab) |
| return false; |
| |
| symtab->elf_symstrtab = vdso_get_dyn(addr, dyntab, DT_STRTAB); |
| if (!symtab->elf_symstrtab) |
| return false; |
| |
| symtab->elf_hashtab = vdso_get_dyn(addr, dyntab, DT_HASH); |
| if (!symtab->elf_hashtab) |
| return false; |
| |
| return true; |
| } |
| |
| static unsigned long elf_sym_hash(const char *name) |
| { |
| unsigned long h = 0, high; |
| |
| while (*name) { |
| h = (h << 4) + *name++; |
| high = h & 0xf0000000; |
| |
| if (high) |
| h ^= high >> 24; |
| |
| h &= ~high; |
| } |
| |
| return h; |
| } |
| |
| static Elf64_Sym *vdso_symtab_get(struct vdso_symtab *symtab, const char *name) |
| { |
| Elf64_Word bucketnum = symtab->elf_hashtab[0]; |
| Elf64_Word *buckettab = &symtab->elf_hashtab[2]; |
| Elf64_Word *chaintab = &symtab->elf_hashtab[2 + bucketnum]; |
| Elf64_Sym *sym; |
| Elf64_Word i; |
| |
| for (i = buckettab[elf_sym_hash(name) % bucketnum]; i != STN_UNDEF; |
| i = chaintab[i]) { |
| sym = &symtab->elf_symtab[i]; |
| if (!strcmp(name, &symtab->elf_symstrtab[sym->st_name])) |
| return sym; |
| } |
| |
| return NULL; |
| } |
| |
| bool report_results(struct sgx_enclave_run *run, int ret, uint64_t result, |
| const char *test) |
| { |
| bool valid = true; |
| |
| if (ret) { |
| printf("FAIL: %s() returned: %d\n", test, ret); |
| valid = false; |
| } |
| |
| if (run->function != EEXIT) { |
| printf("FAIL: %s() function, expected: %u, got: %u\n", test, EEXIT, |
| run->function); |
| valid = false; |
| } |
| |
| if (result != MAGIC) { |
| printf("FAIL: %s(), expected: 0x%lx, got: 0x%lx\n", test, MAGIC, |
| result); |
| valid = false; |
| } |
| |
| if (run->user_data) { |
| printf("FAIL: %s() user data, expected: 0x0, got: 0x%llx\n", |
| test, run->user_data); |
| valid = false; |
| } |
| |
| return valid; |
| } |
| |
| static int user_handler(long rdi, long rsi, long rdx, long ursp, long r8, long r9, |
| struct sgx_enclave_run *run) |
| { |
| run->user_data = 0; |
| return 0; |
| } |
| |
| int main(int argc, char *argv[], char *envp[]) |
| { |
| struct sgx_enclave_run run; |
| struct vdso_symtab symtab; |
| Elf64_Sym *eenter_sym; |
| uint64_t result = 0; |
| struct encl encl; |
| unsigned int i; |
| void *addr; |
| int ret; |
| |
| memset(&run, 0, sizeof(run)); |
| |
| if (!encl_load("test_encl.elf", &encl)) { |
| encl_delete(&encl); |
| ksft_exit_skip("cannot load enclaves\n"); |
| } |
| |
| if (!encl_measure(&encl)) |
| goto err; |
| |
| if (!encl_build(&encl)) |
| goto err; |
| |
| /* |
| * An enclave consumer only must do this. |
| */ |
| for (i = 0; i < encl.nr_segments; i++) { |
| struct encl_segment *seg = &encl.segment_tbl[i]; |
| |
| addr = mmap((void *)encl.encl_base + seg->offset, seg->size, |
| seg->prot, MAP_SHARED | MAP_FIXED, encl.fd, 0); |
| if (addr == MAP_FAILED) { |
| fprintf(stderr, "mmap() failed, errno=%d.\n", errno); |
| exit(KSFT_FAIL); |
| } |
| } |
| |
| memset(&run, 0, sizeof(run)); |
| run.tcs = encl.encl_base; |
| |
| addr = vdso_get_base_addr(envp); |
| if (!addr) |
| goto err; |
| |
| if (!vdso_get_symtab(addr, &symtab)) |
| goto err; |
| |
| eenter_sym = vdso_symtab_get(&symtab, "__vdso_sgx_enter_enclave"); |
| if (!eenter_sym) |
| goto err; |
| |
| eenter = addr + eenter_sym->st_value; |
| |
| ret = sgx_call_vdso((void *)&MAGIC, &result, 0, EENTER, NULL, NULL, &run); |
| if (!report_results(&run, ret, result, "sgx_call_vdso")) |
| goto err; |
| |
| |
| /* Invoke the vDSO directly. */ |
| result = 0; |
| ret = eenter((unsigned long)&MAGIC, (unsigned long)&result, 0, EENTER, |
| 0, 0, &run); |
| if (!report_results(&run, ret, result, "eenter")) |
| goto err; |
| |
| /* And with an exit handler. */ |
| run.user_handler = (__u64)user_handler; |
| run.user_data = 0xdeadbeef; |
| ret = eenter((unsigned long)&MAGIC, (unsigned long)&result, 0, EENTER, |
| 0, 0, &run); |
| if (!report_results(&run, ret, result, "user_handler")) |
| goto err; |
| |
| printf("SUCCESS\n"); |
| encl_delete(&encl); |
| exit(KSFT_PASS); |
| |
| err: |
| encl_delete(&encl); |
| exit(KSFT_FAIL); |
| } |