This commit introduces an entry point of syscall interface for !MMU
mode. It uses an entry function, __kernel_vsyscall, a kernel-wide global
symbol accessible from any locations.

Although it isn't in the scope of this commit, it can be also exposed
via vdso image which is directly accessible from userspace. A standard
library (i.e., libc) can utilize this entry point to implement syscall
wrapper; we can also use this by hooking syscall for unmodified userspace
applications/libraries, which will be implemented in the subsequent
commit.

This only supports 64-bit mode of x86 architecture.

Signed-off-by: Hajime Tazaki <thehaj...@gmail.com>
Signed-off-by: Ricardo Koller <ricar...@google.com>
---
 arch/x86/um/do_syscall_64.c             | 42 ++++++++++++
 arch/x86/um/entry_64.S                  | 88 +++++++++++++++++++++++++
 arch/x86/um/shared/sysdep/syscalls_64.h |  4 ++
 3 files changed, 134 insertions(+)
 create mode 100644 arch/x86/um/do_syscall_64.c
 create mode 100644 arch/x86/um/entry_64.S

diff --git a/arch/x86/um/do_syscall_64.c b/arch/x86/um/do_syscall_64.c
new file mode 100644
index 000000000000..7af6e881ad58
--- /dev/null
+++ b/arch/x86/um/do_syscall_64.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <kern_util.h>
+#include <sysdep/syscalls.h>
+#include <os.h>
+
+#ifndef CONFIG_MMU
+
+__visible void do_syscall_64(struct pt_regs *regs)
+{
+       int syscall;
+
+       syscall = PT_SYSCALL_NR(regs->regs.gp);
+       UPT_SYSCALL_NR(&regs->regs) = syscall;
+
+       pr_debug("syscall(%d) (current=%lx) (fn=%lx)\n",
+                syscall, (unsigned long)current,
+                (unsigned long)sys_call_table[syscall]);
+
+       if (likely(syscall < NR_syscalls)) {
+               PT_REGS_SET_SYSCALL_RETURN(regs,
+                               EXECUTE_SYSCALL(syscall, regs));
+       }
+
+       pr_debug("syscall(%d) --> %lx\n", syscall,
+               regs->regs.gp[HOST_AX]);
+
+       PT_REGS_SYSCALL_RET(regs) = regs->regs.gp[HOST_AX];
+
+       /* force do_signal() --> is_syscall() */
+       set_thread_flag(TIF_SIGPENDING);
+       interrupt_end();
+
+       /* execve succeeded */
+       if (syscall == __NR_execve && regs->regs.gp[HOST_AX] == 0) {
+               userspace(&current->thread.regs.regs,
+                       current_thread_info()->aux_fp_regs);
+       }
+}
+#endif
diff --git a/arch/x86/um/entry_64.S b/arch/x86/um/entry_64.S
new file mode 100644
index 000000000000..12e11ac03543
--- /dev/null
+++ b/arch/x86/um/entry_64.S
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <asm/errno.h>
+
+#include <linux/linkage.h>
+#include <asm/percpu.h>
+#include <asm/desc.h>
+
+#include "../entry/calling.h"
+
+#ifdef CONFIG_SMP
+#error need to stash these variables somewhere else
+#endif
+
+#ifndef CONFIG_MMU
+#define UM_GLOBAL_VAR(x) .data; .align 8; .globl x; x:; .long 0
+
+UM_GLOBAL_VAR(current_top_of_stack)
+UM_GLOBAL_VAR(current_ptregs)
+
+.code64
+.section .entry.text, "ax"
+
+.align 8
+#undef ENTRY
+#define ENTRY(x) .text; .globl x; .type x,%function; x:
+#undef END
+#define END(x)   .size x, . - x
+
+/*
+ * %rcx has the return address (we set it like that in zpoline trampoline).
+ *
+ * Registers on entry:
+ * rax  system call number
+ * rcx  return address
+ * rdi  arg0
+ * rsi  arg1
+ * rdx  arg2
+ * r10  arg3
+ * r8   arg4
+ * r9   arg5
+ *
+ * (note: we are allowed to mess with r11: r11 is callee-clobbered
+ * register in C ABI)
+ */
+ENTRY(__kernel_vsyscall)
+
+       movq    %rsp, %r11
+
+       /* Point rsp to the top of the ptregs array, so we can
+           just fill it with a bunch of push'es. */
+       movq    current_ptregs, %rsp
+
+       /* 8 bytes * 20 registers (plus 8 for the push) */
+       addq    $168, %rsp
+
+       /* Construct struct pt_regs on stack */
+       pushq   $0              /* pt_regs->ss (index 20) */
+       pushq   %r11            /* pt_regs->sp */
+       pushfq                  /* pt_regs->flags */
+       pushq   $0              /* pt_regs->cs */
+       pushq   %rcx            /* pt_regs->ip */
+       pushq   %rax            /* pt_regs->orig_ax */
+
+       PUSH_AND_CLEAR_REGS rax=$-ENOSYS
+
+       mov %rsp, %rdi
+
+       /*
+        * Switch to current top of stack, so "current->" points
+        * to the right task.
+        */
+       movq    current_top_of_stack, %rsp
+
+       call    do_syscall_64
+
+       movq    current_ptregs, %rsp
+
+       POP_REGS
+
+       addq    $8, %rsp        /* skip orig_ax */
+       addq    $8, %rsp        /* skip ip */
+       addq    $8, %rsp        /* skip cs */
+       addq    $8, %rsp        /* skip flags */
+       popq    %rsp
+
+       ret
+
+END(__kernel_vsyscall)
diff --git a/arch/x86/um/shared/sysdep/syscalls_64.h 
b/arch/x86/um/shared/sysdep/syscalls_64.h
index b6b997225841..31aa0694cec0 100644
--- a/arch/x86/um/shared/sysdep/syscalls_64.h
+++ b/arch/x86/um/shared/sysdep/syscalls_64.h
@@ -25,4 +25,8 @@ extern syscall_handler_t *sys_call_table[];
 extern syscall_handler_t sys_modify_ldt;
 extern syscall_handler_t sys_arch_prctl;
 
+__visible void do_syscall_64(struct pt_regs *regs);
+extern long __kernel_vsyscall(int64_t a0, int64_t a1, int64_t a2, int64_t a3,
+                             int64_t a4, int64_t a5, int64_t a6);
+
 #endif
-- 
2.43.0


Reply via email to