blob: 4f2e7e48c24a25668b2fbd893a46ff1fb641e226 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/user.h>
#include <sys/uio.h>
#include <sys/prctl.h>
#include "asm/unistd.h"
#include <time.h>
#include <sys/mman.h>
#include <stdint.h>
#include "asm/process_vm_exec.h"
#include "linux/process_vm_exec.h"
#include "../kselftest.h"
#include "log.h"
#ifndef __NR_process_vm_exec
#define __NR_process_vm_exec 441
#endif
#define TEST_SYSCALL 123
#define TEST_SYSCALL_RET 456
#define TEST_MARKER 789
#define TEST_TIMEOUT 5
#define TEST_STACK_SIZE 65536
#if defined(__x86_64__)
static inline long __syscall1(long n, long a1)
{
unsigned long ret;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1) : "rcx", "r11", "memory");
return ret;
}
#elif defined(__aarch64__)
#define __asm_syscall(...) do { \
__asm__ __volatile__ ( "svc 0" \
: "=r"(x0) : __VA_ARGS__ : "memory", "cc"); \
return x0; \
} while (0)
static inline long __syscall1(long n, long a)
{
register long x8 __asm__("x8") = n;
register long x0 __asm__("x0") = a;
__asm_syscall("r"(x8), "0"(x0));
}
#endif
int marker;
static void guest(void)
{
while (1)
if (__syscall1(TEST_SYSCALL, marker) != TEST_SYSCALL_RET)
abort();
}
int main(int argc, char **argv)
{
struct process_vm_exec_context ctx = {
.version = PROCESS_VM_EXEC_GOOGLE_V1,
};
struct timespec start, cur;
int status, ret, i;
pid_t pid;
long sysnr;
void *stack;
ksft_set_plan(1);
stack = mmap(NULL, TEST_STACK_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
if (stack == MAP_FAILED)
return pr_perror("mmap");
pid = fork();
if (pid == 0) {
marker = TEST_MARKER;
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
kill(getpid(), SIGSTOP);
/* unreachable */
abort();
return 0;
}
#if defined(__x86_64__)
ctx.sigctx.rip = (long)guest;
ctx.sigctx.rsp = (long)stack + TEST_STACK_SIZE;
ctx.sigctx.cs = 0x33;
#elif defined(__aarch64__)
ctx.sigctx.pc = (long)guest;
ctx.sigctx.pstate = 0x40001000;
ctx.sigctx.sp = (long)stack + TEST_STACK_SIZE;
#endif
sysnr = 0;
clock_gettime(CLOCK_MONOTONIC, &start);
while (1) {
unsigned long long sigmask = 0xffffffff;
siginfo_t siginfo;
clock_gettime(CLOCK_MONOTONIC, &cur);
if (start.tv_sec + TEST_TIMEOUT < cur.tv_sec ||
(start.tv_sec + TEST_TIMEOUT == cur.tv_sec &&
start.tv_nsec < cur.tv_nsec))
break;
ret = syscall(__NR_process_vm_exec, pid, &ctx, 0, &siginfo, &sigmask, 8);
#ifdef __DEBUG
ksft_print_msg("ret %d signo %d sysno %d ip %lx\n",
ret, siginfo.si_signo, siginfo.si_syscall, ctx.rip);
#endif
if (ret != 0)
pr_fail("unexpected return code: ret %d errno %d", ret, errno);
if (siginfo.si_signo != SIGSYS)
pr_fail("unexpected signal: %d", siginfo.si_signo);
if (siginfo.si_syscall != TEST_SYSCALL)
pr_fail("unexpected syscall: %d", siginfo.si_syscall);
#if defined(__x86_64__)
ctx.sigctx.rax = TEST_SYSCALL_RET;
#elif defined(__aarch64__)
ctx.sigctx.regs[0] = TEST_SYSCALL_RET;
#endif
sysnr++;
}
ksft_test_result_pass("%ld ns/syscall\n", 1000000000 / sysnr);
ksft_exit_pass();
return 0;
}