RISC-V: User-facing API
authorPalmer Dabbelt <palmer@dabbelt.com>
Tue, 11 Jul 2017 01:07:09 +0000 (18:07 -0700)
committerPalmer Dabbelt <palmer@dabbelt.com>
Tue, 26 Sep 2017 22:26:48 +0000 (15:26 -0700)
This patch contains code that is in some way visible to the user:
including via system calls, the VDSO, module loading and signal
handling.  It also contains some generic code that is ABI visible.

Signed-off-by: Palmer Dabbelt <palmer@dabbelt.com>
27 files changed:
arch/riscv/include/asm/mmu.h [new file with mode: 0644]
arch/riscv/include/asm/ptrace.h [new file with mode: 0644]
arch/riscv/include/asm/syscall.h [new file with mode: 0644]
arch/riscv/include/asm/unistd.h [new file with mode: 0644]
arch/riscv/include/asm/vdso.h [new file with mode: 0644]
arch/riscv/include/uapi/asm/Kbuild [new file with mode: 0644]
arch/riscv/include/uapi/asm/auxvec.h [new file with mode: 0644]
arch/riscv/include/uapi/asm/bitsperlong.h [new file with mode: 0644]
arch/riscv/include/uapi/asm/byteorder.h [new file with mode: 0644]
arch/riscv/include/uapi/asm/elf.h [new file with mode: 0644]
arch/riscv/include/uapi/asm/hwcap.h [new file with mode: 0644]
arch/riscv/include/uapi/asm/ptrace.h [new file with mode: 0644]
arch/riscv/include/uapi/asm/sigcontext.h [new file with mode: 0644]
arch/riscv/include/uapi/asm/siginfo.h [new file with mode: 0644]
arch/riscv/include/uapi/asm/ucontext.h [new file with mode: 0644]
arch/riscv/kernel/cpufeature.c [new file with mode: 0644]
arch/riscv/kernel/module.c [new file with mode: 0644]
arch/riscv/kernel/ptrace.c [new file with mode: 0644]
arch/riscv/kernel/riscv_ksyms.c [new file with mode: 0644]
arch/riscv/kernel/signal.c [new file with mode: 0644]
arch/riscv/kernel/sys_riscv.c [new file with mode: 0644]
arch/riscv/kernel/syscall_table.c [new file with mode: 0644]
arch/riscv/kernel/vdso/.gitignore [new file with mode: 0644]
arch/riscv/kernel/vdso/Makefile [new file with mode: 0644]
arch/riscv/kernel/vdso/rt_sigreturn.S [new file with mode: 0644]
arch/riscv/kernel/vdso/vdso.S [new file with mode: 0644]
arch/riscv/kernel/vdso/vdso.lds.S [new file with mode: 0644]

diff --git a/arch/riscv/include/asm/mmu.h b/arch/riscv/include/asm/mmu.h
new file mode 100644 (file)
index 0000000..66805cb
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+
+#ifndef _ASM_RISCV_MMU_H
+#define _ASM_RISCV_MMU_H
+
+#ifndef __ASSEMBLY__
+
+typedef struct {
+       void *vdso;
+} mm_context_t;
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_RISCV_MMU_H */
diff --git a/arch/riscv/include/asm/ptrace.h b/arch/riscv/include/asm/ptrace.h
new file mode 100644 (file)
index 0000000..93b8956
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#ifndef _ASM_RISCV_PTRACE_H
+#define _ASM_RISCV_PTRACE_H
+
+#include <uapi/asm/ptrace.h>
+#include <asm/csr.h>
+
+#ifndef __ASSEMBLY__
+
+struct pt_regs {
+       unsigned long sepc;
+       unsigned long ra;
+       unsigned long sp;
+       unsigned long gp;
+       unsigned long tp;
+       unsigned long t0;
+       unsigned long t1;
+       unsigned long t2;
+       unsigned long s0;
+       unsigned long s1;
+       unsigned long a0;
+       unsigned long a1;
+       unsigned long a2;
+       unsigned long a3;
+       unsigned long a4;
+       unsigned long a5;
+       unsigned long a6;
+       unsigned long a7;
+       unsigned long s2;
+       unsigned long s3;
+       unsigned long s4;
+       unsigned long s5;
+       unsigned long s6;
+       unsigned long s7;
+       unsigned long s8;
+       unsigned long s9;
+       unsigned long s10;
+       unsigned long s11;
+       unsigned long t3;
+       unsigned long t4;
+       unsigned long t5;
+       unsigned long t6;
+       /* Supervisor CSRs */
+       unsigned long sstatus;
+       unsigned long sbadaddr;
+       unsigned long scause;
+        /* a0 value before the syscall */
+        unsigned long orig_a0;
+};
+
+#ifdef CONFIG_64BIT
+#define REG_FMT "%016lx"
+#else
+#define REG_FMT "%08lx"
+#endif
+
+#define user_mode(regs) (((regs)->sstatus & SR_PS) == 0)
+
+
+/* Helpers for working with the instruction pointer */
+#define GET_IP(regs) ((regs)->sepc)
+#define SET_IP(regs, val) (GET_IP(regs) = (val))
+
+static inline unsigned long instruction_pointer(struct pt_regs *regs)
+{
+       return GET_IP(regs);
+}
+static inline void instruction_pointer_set(struct pt_regs *regs,
+                                          unsigned long val)
+{
+       SET_IP(regs, val);
+}
+
+#define profile_pc(regs) instruction_pointer(regs)
+
+/* Helpers for working with the user stack pointer */
+#define GET_USP(regs) ((regs)->sp)
+#define SET_USP(regs, val) (GET_USP(regs) = (val))
+
+static inline unsigned long user_stack_pointer(struct pt_regs *regs)
+{
+       return GET_USP(regs);
+}
+static inline void user_stack_pointer_set(struct pt_regs *regs,
+                                         unsigned long val)
+{
+       SET_USP(regs, val);
+}
+
+/* Helpers for working with the frame pointer */
+#define GET_FP(regs) ((regs)->s0)
+#define SET_FP(regs, val) (GET_FP(regs) = (val))
+
+static inline unsigned long frame_pointer(struct pt_regs *regs)
+{
+       return GET_FP(regs);
+}
+static inline void frame_pointer_set(struct pt_regs *regs,
+                                    unsigned long val)
+{
+       SET_FP(regs, val);
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_RISCV_PTRACE_H */
diff --git a/arch/riscv/include/asm/syscall.h b/arch/riscv/include/asm/syscall.h
new file mode 100644 (file)
index 0000000..8d25f89
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2008-2009 Red Hat, Inc.  All rights reserved.
+ * Copyright 2010 Tilera Corporation. All Rights Reserved.
+ * Copyright 2015 Regents of the University of California, Berkeley
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ * See asm-generic/syscall.h for descriptions of what we must do here.
+ */
+
+#ifndef _ASM_RISCV_SYSCALL_H
+#define _ASM_RISCV_SYSCALL_H
+
+#include <linux/sched.h>
+#include <linux/err.h>
+
+/* The array of function pointers for syscalls. */
+extern void *sys_call_table[];
+
+/*
+ * Only the low 32 bits of orig_r0 are meaningful, so we return int.
+ * This importantly ignores the high bits on 64-bit, so comparisons
+ * sign-extend the low 32 bits.
+ */
+static inline int syscall_get_nr(struct task_struct *task,
+                                struct pt_regs *regs)
+{
+       return regs->a7;
+}
+
+static inline void syscall_set_nr(struct task_struct *task,
+                                 struct pt_regs *regs,
+                                 int sysno)
+{
+       regs->a7 = sysno;
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+                                   struct pt_regs *regs)
+{
+        regs->a0 = regs->orig_a0;
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+                                    struct pt_regs *regs)
+{
+       unsigned long error = regs->a0;
+
+       return IS_ERR_VALUE(error) ? error : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+                                           struct pt_regs *regs)
+{
+       return regs->a0;
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+                                           struct pt_regs *regs,
+                                           int error, long val)
+{
+       regs->a0 = (long) error ?: val;
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+                                        struct pt_regs *regs,
+                                        unsigned int i, unsigned int n,
+                                        unsigned long *args)
+{
+       BUG_ON(i + n > 6);
+       if (i == 0) {
+               args[0] = regs->orig_a0;
+               args++;
+               i++;
+               n--;
+       }
+       memcpy(args, &regs->a1 + i * sizeof(regs->a1), n * sizeof(args[0]));
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+                                        struct pt_regs *regs,
+                                        unsigned int i, unsigned int n,
+                                        const unsigned long *args)
+{
+       BUG_ON(i + n > 6);
+        if (i == 0) {
+                regs->orig_a0 = args[0];
+                args++;
+                i++;
+                n--;
+        }
+       memcpy(&regs->a1 + i * sizeof(regs->a1), args, n * sizeof(regs->a0));
+}
+
+#endif /* _ASM_RISCV_SYSCALL_H */
diff --git a/arch/riscv/include/asm/unistd.h b/arch/riscv/include/asm/unistd.h
new file mode 100644 (file)
index 0000000..9f250ed
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#define __ARCH_HAVE_MMU
+#define __ARCH_WANT_SYS_CLONE
+#include <uapi/asm/unistd.h>
diff --git a/arch/riscv/include/asm/vdso.h b/arch/riscv/include/asm/vdso.h
new file mode 100644 (file)
index 0000000..602f612
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 ARM Limited
+ * Copyright (C) 2014 Regents of the University of California
+ * Copyright (C) 2017 SiFive
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _ASM_RISCV_VDSO_H
+#define _ASM_RISCV_VDSO_H
+
+#include <linux/types.h>
+
+struct vdso_data {
+};
+
+/*
+ * The VDSO symbols are mapped into Linux so we can just use regular symbol
+ * addressing to get their offsets in userspace.  The symbols are mapped at an
+ * offset of 0, but since the linker must support setting weak undefined
+ * symbols to the absolute address 0 it also happens to support other low
+ * addresses even when the code model suggests those low addresses would not
+ * otherwise be availiable.
+ */
+#define VDSO_SYMBOL(base, name)                                                        \
+({                                                                             \
+       extern const char __vdso_##name[];                                      \
+       (void __user *)((unsigned long)(base) + __vdso_##name);                 \
+})
+
+#endif /* _ASM_RISCV_VDSO_H */
diff --git a/arch/riscv/include/uapi/asm/Kbuild b/arch/riscv/include/uapi/asm/Kbuild
new file mode 100644 (file)
index 0000000..5ded96b
--- /dev/null
@@ -0,0 +1,27 @@
+# UAPI Header export list
+include include/uapi/asm-generic/Kbuild.asm
+
+generic-y += setup.h
+generic-y += unistd.h
+generic-y += errno.h
+generic-y += fcntl.h
+generic-y += ioctl.h
+generic-y += ioctls.h
+generic-y += ipcbuf.h
+generic-y += mman.h
+generic-y += msgbuf.h
+generic-y += param.h
+generic-y += poll.h
+generic-y += posix_types.h
+generic-y += resource.h
+generic-y += sembuf.h
+generic-y += shmbuf.h
+generic-y += signal.h
+generic-y += socket.h
+generic-y += sockios.h
+generic-y += stat.h
+generic-y += statfs.h
+generic-y += swab.h
+generic-y += termbits.h
+generic-y += termios.h
+generic-y += types.h
diff --git a/arch/riscv/include/uapi/asm/auxvec.h b/arch/riscv/include/uapi/asm/auxvec.h
new file mode 100644 (file)
index 0000000..1376515
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Copyright (C) 2015 Regents of the University of California
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _UAPI_ASM_RISCV_AUXVEC_H
+#define _UAPI_ASM_RISCV_AUXVEC_H
+
+/* vDSO location */
+#define AT_SYSINFO_EHDR 33
+
+#endif /* _UAPI_ASM_RISCV_AUXVEC_H */
diff --git a/arch/riscv/include/uapi/asm/bitsperlong.h b/arch/riscv/include/uapi/asm/bitsperlong.h
new file mode 100644 (file)
index 0000000..0b3cb52
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Copyright (C) 2015 Regents of the University of California
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _UAPI_ASM_RISCV_BITSPERLONG_H
+#define _UAPI_ASM_RISCV_BITSPERLONG_H
+
+#define __BITS_PER_LONG (__SIZEOF_POINTER__ * 8)
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* _UAPI_ASM_RISCV_BITSPERLONG_H */
diff --git a/arch/riscv/include/uapi/asm/byteorder.h b/arch/riscv/include/uapi/asm/byteorder.h
new file mode 100644 (file)
index 0000000..4ca38af
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Copyright (C) 2015 Regents of the University of California
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _UAPI_ASM_RISCV_BYTEORDER_H
+#define _UAPI_ASM_RISCV_BYTEORDER_H
+
+#include <linux/byteorder/little_endian.h>
+
+#endif /* _UAPI_ASM_RISCV_BYTEORDER_H */
diff --git a/arch/riscv/include/uapi/asm/elf.h b/arch/riscv/include/uapi/asm/elf.h
new file mode 100644 (file)
index 0000000..a510edf
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2003 Matjaz Breskvar <phoenix@bsemi.com>
+ * Copyright (C) 2010-2011 Jonas Bonn <jonas@southpole.se>
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _UAPI_ASM_ELF_H
+#define _UAPI_ASM_ELF_H
+
+#include <asm/ptrace.h>
+
+/* ELF register definitions */
+typedef unsigned long elf_greg_t;
+typedef struct user_regs_struct elf_gregset_t;
+#define ELF_NGREG (sizeof(elf_gregset_t) / sizeof(elf_greg_t))
+
+typedef union __riscv_fp_state elf_fpregset_t;
+
+#define ELF_RISCV_R_SYM(r_info) ((r_info) >> 32)
+#define ELF_RISCV_R_TYPE(r_info) ((r_info) & 0xffffffff)
+
+/*
+ * RISC-V relocation types
+ */
+
+/* Relocation types used by the dynamic linker */
+#define R_RISCV_NONE           0
+#define R_RISCV_32             1
+#define R_RISCV_64             2
+#define R_RISCV_RELATIVE       3
+#define R_RISCV_COPY           4
+#define R_RISCV_JUMP_SLOT      5
+#define R_RISCV_TLS_DTPMOD32   6
+#define R_RISCV_TLS_DTPMOD64   7
+#define R_RISCV_TLS_DTPREL32   8
+#define R_RISCV_TLS_DTPREL64   9
+#define R_RISCV_TLS_TPREL32    10
+#define R_RISCV_TLS_TPREL64    11
+
+/* Relocation types not used by the dynamic linker */
+#define R_RISCV_BRANCH         16
+#define R_RISCV_JAL            17
+#define R_RISCV_CALL           18
+#define R_RISCV_CALL_PLT       19
+#define R_RISCV_GOT_HI20       20
+#define R_RISCV_TLS_GOT_HI20   21
+#define R_RISCV_TLS_GD_HI20    22
+#define R_RISCV_PCREL_HI20     23
+#define R_RISCV_PCREL_LO12_I   24
+#define R_RISCV_PCREL_LO12_S   25
+#define R_RISCV_HI20           26
+#define R_RISCV_LO12_I         27
+#define R_RISCV_LO12_S         28
+#define R_RISCV_TPREL_HI20     29
+#define R_RISCV_TPREL_LO12_I   30
+#define R_RISCV_TPREL_LO12_S   31
+#define R_RISCV_TPREL_ADD      32
+#define R_RISCV_ADD8           33
+#define R_RISCV_ADD16          34
+#define R_RISCV_ADD32          35
+#define R_RISCV_ADD64          36
+#define R_RISCV_SUB8           37
+#define R_RISCV_SUB16          38
+#define R_RISCV_SUB32          39
+#define R_RISCV_SUB64          40
+#define R_RISCV_GNU_VTINHERIT  41
+#define R_RISCV_GNU_VTENTRY    42
+#define R_RISCV_ALIGN          43
+#define R_RISCV_RVC_BRANCH     44
+#define R_RISCV_RVC_JUMP       45
+#define R_RISCV_LUI            46
+#define R_RISCV_GPREL_I                47
+#define R_RISCV_GPREL_S                48
+#define R_RISCV_TPREL_I                49
+#define R_RISCV_TPREL_S                50
+#define R_RISCV_RELAX          51
+
+#endif /* _UAPI_ASM_ELF_H */
diff --git a/arch/riscv/include/uapi/asm/hwcap.h b/arch/riscv/include/uapi/asm/hwcap.h
new file mode 100644 (file)
index 0000000..f333221
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copied from arch/arm64/include/asm/hwcap.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Copyright (C) 2017 SiFive
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __UAPI_ASM_HWCAP_H
+#define __UAPI_ASM_HWCAP_H
+
+/*
+ * Linux saves the floating-point registers according to the ISA Linux is
+ * executing on, as opposed to the ISA the user program is compiled for.  This
+ * is necessary for a handful of esoteric use cases: for example, userpsace
+ * threading libraries must be able to examine the actual machine state in
+ * order to fully reconstruct the state of a thread.
+ */
+#define COMPAT_HWCAP_ISA_I     (1 << ('I' - 'A'))
+#define COMPAT_HWCAP_ISA_M     (1 << ('M' - 'A'))
+#define COMPAT_HWCAP_ISA_A     (1 << ('A' - 'A'))
+#define COMPAT_HWCAP_ISA_F     (1 << ('F' - 'A'))
+#define COMPAT_HWCAP_ISA_D     (1 << ('D' - 'A'))
+#define COMPAT_HWCAP_ISA_C     (1 << ('C' - 'A'))
+
+#endif
diff --git a/arch/riscv/include/uapi/asm/ptrace.h b/arch/riscv/include/uapi/asm/ptrace.h
new file mode 100644 (file)
index 0000000..1a9e4cd
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_ASM_RISCV_PTRACE_H
+#define _UAPI_ASM_RISCV_PTRACE_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+
+/*
+ * User-mode register state for core dumps, ptrace, sigcontext
+ *
+ * This decouples struct pt_regs from the userspace ABI.
+ * struct user_regs_struct must form a prefix of struct pt_regs.
+ */
+struct user_regs_struct {
+       unsigned long pc;
+       unsigned long ra;
+       unsigned long sp;
+       unsigned long gp;
+       unsigned long tp;
+       unsigned long t0;
+       unsigned long t1;
+       unsigned long t2;
+       unsigned long s0;
+       unsigned long s1;
+       unsigned long a0;
+       unsigned long a1;
+       unsigned long a2;
+       unsigned long a3;
+       unsigned long a4;
+       unsigned long a5;
+       unsigned long a6;
+       unsigned long a7;
+       unsigned long s2;
+       unsigned long s3;
+       unsigned long s4;
+       unsigned long s5;
+       unsigned long s6;
+       unsigned long s7;
+       unsigned long s8;
+       unsigned long s9;
+       unsigned long s10;
+       unsigned long s11;
+       unsigned long t3;
+       unsigned long t4;
+       unsigned long t5;
+       unsigned long t6;
+};
+
+struct __riscv_f_ext_state {
+       __u32 f[32];
+       __u32 fcsr;
+};
+
+struct __riscv_d_ext_state {
+       __u64 f[32];
+       __u32 fcsr;
+};
+
+struct __riscv_q_ext_state {
+       __u64 f[64] __attribute__((aligned(16)));
+       __u32 fcsr;
+       /*
+        * Reserved for expansion of sigcontext structure.  Currently zeroed
+        * upon signal, and must be zero upon sigreturn.
+        */
+       __u32 reserved[3];
+};
+
+union __riscv_fp_state {
+       struct __riscv_f_ext_state f;
+       struct __riscv_d_ext_state d;
+       struct __riscv_q_ext_state q;
+};
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _UAPI_ASM_RISCV_PTRACE_H */
diff --git a/arch/riscv/include/uapi/asm/sigcontext.h b/arch/riscv/include/uapi/asm/sigcontext.h
new file mode 100644 (file)
index 0000000..ed7372b
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_ASM_RISCV_SIGCONTEXT_H
+#define _UAPI_ASM_RISCV_SIGCONTEXT_H
+
+#include <asm/ptrace.h>
+
+/*
+ * Signal context structure
+ *
+ * This contains the context saved before a signal handler is invoked;
+ * it is restored by sys_sigreturn / sys_rt_sigreturn.
+ */
+struct sigcontext {
+       struct user_regs_struct sc_regs;
+       union __riscv_fp_state sc_fpregs;
+};
+
+#endif /* _UAPI_ASM_RISCV_SIGCONTEXT_H */
diff --git a/arch/riscv/include/uapi/asm/siginfo.h b/arch/riscv/include/uapi/asm/siginfo.h
new file mode 100644 (file)
index 0000000..f96849a
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Copyright (C) 2016 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SIGINFO_H
+#define __ASM_SIGINFO_H
+
+#define __ARCH_SI_PREAMBLE_SIZE        (__SIZEOF_POINTER__ == 4 ? 12 : 16)
+
+#include <asm-generic/siginfo.h>
+
+#endif
diff --git a/arch/riscv/include/uapi/asm/ucontext.h b/arch/riscv/include/uapi/asm/ucontext.h
new file mode 100644 (file)
index 0000000..1fae8b1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ * Copyright (C) 2017 SiFive, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file was copied from arch/arm64/include/uapi/asm/ucontext.h
+ */
+#ifndef _UAPI__ASM_UCONTEXT_H
+#define _UAPI__ASM_UCONTEXT_H
+
+#include <linux/types.h>
+
+struct ucontext {
+       unsigned long     uc_flags;
+       struct ucontext  *uc_link;
+       stack_t           uc_stack;
+       sigset_t          uc_sigmask;
+       /* There's some padding here to allow sigset_t to be expanded in the
+        * future.  Though this is unlikely, other architectures put uc_sigmask
+        * at the end of this structure and explicitly state it can be
+        * expanded, so we didn't want to box ourselves in here. */
+       __u8              __unused[1024 / 8 - sizeof(sigset_t)];
+       /* We can't put uc_sigmask at the end of this structure because we need
+        * to be able to expand sigcontext in the future.  For example, the
+        * vector ISA extension will almost certainly add ISA state.  We want
+        * to ensure all user-visible ISA state can be saved and restored via a
+        * ucontext, so we're putting this at the end in order to allow for
+        * infinite extensibility.  Since we know this will be extended and we
+        * assume sigset_t won't be extended an extreme amount, we're
+        * prioritizing this. */
+       struct sigcontext uc_mcontext;
+};
+
+#endif /* _UAPI__ASM_UCONTEXT_H */
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
new file mode 100644 (file)
index 0000000..17011a8
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copied from arch/arm64/kernel/cpufeature.c
+ *
+ * Copyright (C) 2015 ARM Ltd.
+ * Copyright (C) 2017 SiFive
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/of.h>
+#include <asm/processor.h>
+#include <asm/hwcap.h>
+
+unsigned long elf_hwcap __read_mostly;
+
+void riscv_fill_hwcap(void)
+{
+       struct device_node *node;
+       const char *isa;
+       size_t i;
+       static unsigned long isa2hwcap[256] = {0};
+
+       isa2hwcap['i'] = isa2hwcap['I'] = COMPAT_HWCAP_ISA_I;
+       isa2hwcap['m'] = isa2hwcap['M'] = COMPAT_HWCAP_ISA_M;
+       isa2hwcap['a'] = isa2hwcap['A'] = COMPAT_HWCAP_ISA_A;
+       isa2hwcap['f'] = isa2hwcap['F'] = COMPAT_HWCAP_ISA_F;
+       isa2hwcap['d'] = isa2hwcap['D'] = COMPAT_HWCAP_ISA_D;
+       isa2hwcap['c'] = isa2hwcap['C'] = COMPAT_HWCAP_ISA_C;
+
+       elf_hwcap = 0;
+
+       /*
+        * We don't support running Linux on hertergenous ISA systems.  For
+        * now, we just check the ISA of the first processor.
+        */
+       node = of_find_node_by_type(NULL, "cpu");
+       if (!node) {
+               pr_warning("Unable to find \"cpu\" devicetree entry");
+               return;
+       }
+
+       if (of_property_read_string(node, "riscv,isa", &isa)) {
+               pr_warning("Unable to find \"riscv,isa\" devicetree entry");
+               return;
+       }
+
+       for (i = 0; i < strlen(isa); ++i)
+               elf_hwcap |= isa2hwcap[(unsigned char)(isa[i])];
+
+       pr_info("elf_hwcap is 0x%lx", elf_hwcap);
+}
diff --git a/arch/riscv/kernel/module.c b/arch/riscv/kernel/module.c
new file mode 100644 (file)
index 0000000..e0f0503
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  Copyright (C) 2017 Zihao Yu
+ */
+
+#include <linux/elf.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/moduleloader.h>
+
+static int apply_r_riscv_64_rela(struct module *me, u32 *location, Elf_Addr v)
+{
+       *(u64 *)location = v;
+       return 0;
+}
+
+static int apply_r_riscv_branch_rela(struct module *me, u32 *location,
+                                    Elf_Addr v)
+{
+       s64 offset = (void *)v - (void *)location;
+       u32 imm12 = (offset & 0x1000) << (31 - 12);
+       u32 imm11 = (offset & 0x800) >> (11 - 7);
+       u32 imm10_5 = (offset & 0x7e0) << (30 - 10);
+       u32 imm4_1 = (offset & 0x1e) << (11 - 4);
+
+       *location = (*location & 0x1fff07f) | imm12 | imm11 | imm10_5 | imm4_1;
+       return 0;
+}
+
+static int apply_r_riscv_jal_rela(struct module *me, u32 *location,
+                                 Elf_Addr v)
+{
+       s64 offset = (void *)v - (void *)location;
+       u32 imm20 = (offset & 0x100000) << (31 - 20);
+       u32 imm19_12 = (offset & 0xff000);
+       u32 imm11 = (offset & 0x800) << (20 - 11);
+       u32 imm10_1 = (offset & 0x7fe) << (30 - 10);
+
+       *location = (*location & 0xfff) | imm20 | imm19_12 | imm11 | imm10_1;
+       return 0;
+}
+
+static int apply_r_riscv_pcrel_hi20_rela(struct module *me, u32 *location,
+                                        Elf_Addr v)
+{
+       s64 offset = (void *)v - (void *)location;
+       s32 hi20;
+
+       if (offset != (s32)offset) {
+               pr_err(
+                 "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
+                 me->name, v, location);
+               return -EINVAL;
+       }
+
+       hi20 = (offset + 0x800) & 0xfffff000;
+       *location = (*location & 0xfff) | hi20;
+       return 0;
+}
+
+static int apply_r_riscv_pcrel_lo12_i_rela(struct module *me, u32 *location,
+                                          Elf_Addr v)
+{
+       /*
+        * v is the lo12 value to fill. It is calculated before calling this
+        * handler.
+        */
+       *location = (*location & 0xfffff) | ((v & 0xfff) << 20);
+       return 0;
+}
+
+static int apply_r_riscv_pcrel_lo12_s_rela(struct module *me, u32 *location,
+                                          Elf_Addr v)
+{
+       /*
+        * v is the lo12 value to fill. It is calculated before calling this
+        * handler.
+        */
+       u32 imm11_5 = (v & 0xfe0) << (31 - 11);
+       u32 imm4_0 = (v & 0x1f) << (11 - 4);
+
+       *location = (*location & 0x1fff07f) | imm11_5 | imm4_0;
+       return 0;
+}
+
+static int apply_r_riscv_call_plt_rela(struct module *me, u32 *location,
+                                      Elf_Addr v)
+{
+       s64 offset = (void *)v - (void *)location;
+       s32 fill_v = offset;
+       u32 hi20, lo12;
+
+       if (offset != fill_v) {
+               pr_err(
+                 "%s: target %016llx can not be addressed by the 32-bit offset from PC = %p\n",
+                 me->name, v, location);
+               return -EINVAL;
+       }
+
+       hi20 = (offset + 0x800) & 0xfffff000;
+       lo12 = (offset - hi20) & 0xfff;
+       *location = (*location & 0xfff) | hi20;
+       *(location + 1) = (*(location + 1) & 0xfffff) | (lo12 << 20);
+       return 0;
+}
+
+static int apply_r_riscv_relax_rela(struct module *me, u32 *location,
+                                   Elf_Addr v)
+{
+       return 0;
+}
+
+static int (*reloc_handlers_rela[]) (struct module *me, u32 *location,
+                               Elf_Addr v) = {
+       [R_RISCV_64]                    = apply_r_riscv_64_rela,
+       [R_RISCV_BRANCH]                = apply_r_riscv_branch_rela,
+       [R_RISCV_JAL]                   = apply_r_riscv_jal_rela,
+       [R_RISCV_PCREL_HI20]            = apply_r_riscv_pcrel_hi20_rela,
+       [R_RISCV_PCREL_LO12_I]          = apply_r_riscv_pcrel_lo12_i_rela,
+       [R_RISCV_PCREL_LO12_S]          = apply_r_riscv_pcrel_lo12_s_rela,
+       [R_RISCV_CALL_PLT]              = apply_r_riscv_call_plt_rela,
+       [R_RISCV_RELAX]                 = apply_r_riscv_relax_rela,
+};
+
+int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
+                      unsigned int symindex, unsigned int relsec,
+                      struct module *me)
+{
+       Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr;
+       int (*handler)(struct module *me, u32 *location, Elf_Addr v);
+       Elf_Sym *sym;
+       u32 *location;
+       unsigned int i, type;
+       Elf_Addr v;
+       int res;
+
+       pr_debug("Applying relocate section %u to %u\n", relsec,
+              sechdrs[relsec].sh_info);
+
+       for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+               /* This is where to make the change */
+               location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+                       + rel[i].r_offset;
+               /* This is the symbol it is referring to */
+               sym = (Elf_Sym *)sechdrs[symindex].sh_addr
+                       + ELF_RISCV_R_SYM(rel[i].r_info);
+               if (IS_ERR_VALUE(sym->st_value)) {
+                       /* Ignore unresolved weak symbol */
+                       if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
+                               continue;
+                       pr_warning("%s: Unknown symbol %s\n",
+                                  me->name, strtab + sym->st_name);
+                       return -ENOENT;
+               }
+
+               type = ELF_RISCV_R_TYPE(rel[i].r_info);
+
+               if (type < ARRAY_SIZE(reloc_handlers_rela))
+                       handler = reloc_handlers_rela[type];
+               else
+                       handler = NULL;
+
+               if (!handler) {
+                       pr_err("%s: Unknown relocation type %u\n",
+                              me->name, type);
+                       return -EINVAL;
+               }
+
+               v = sym->st_value + rel[i].r_addend;
+
+               if (type == R_RISCV_PCREL_LO12_I || type == R_RISCV_PCREL_LO12_S) {
+                       unsigned int j;
+
+                       for (j = 0; j < sechdrs[relsec].sh_size / sizeof(*rel); j++) {
+                               u64 hi20_loc =
+                                       sechdrs[sechdrs[relsec].sh_info].sh_addr
+                                       + rel[j].r_offset;
+                               /* Find the corresponding HI20 PC-relative relocation entry */
+                               if (hi20_loc == sym->st_value) {
+                                       Elf_Sym *hi20_sym =
+                                               (Elf_Sym *)sechdrs[symindex].sh_addr
+                                               + ELF_RISCV_R_SYM(rel[j].r_info);
+                                       u64 hi20_sym_val =
+                                               hi20_sym->st_value
+                                               + rel[j].r_addend;
+                                       /* Calculate lo12 */
+                                       s64 offset = hi20_sym_val - hi20_loc;
+                                       s32 hi20 = (offset + 0x800) & 0xfffff000;
+                                       s32 lo12 = offset - hi20;
+                                       v = lo12;
+                                       break;
+                               }
+                       }
+                       if (j == sechdrs[relsec].sh_size / sizeof(*rel)) {
+                               pr_err(
+                                 "%s: Can not find HI20 PC-relative relocation information\n",
+                                 me->name);
+                               return -EINVAL;
+                       }
+               }
+
+               res = handler(me, location, v);
+               if (res)
+                       return res;
+       }
+
+       return 0;
+}
diff --git a/arch/riscv/kernel/ptrace.c b/arch/riscv/kernel/ptrace.c
new file mode 100644 (file)
index 0000000..ba3e807
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2010 Tilera Corporation. All Rights Reserved.
+ * Copyright 2015 Regents of the University of California
+ * Copyright 2017 SiFive
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ * Copied from arch/tile/kernel/ptrace.c
+ */
+
+#include <asm/ptrace.h>
+#include <asm/syscall.h>
+#include <asm/thread_info.h>
+#include <linux/ptrace.h>
+#include <linux/elf.h>
+#include <linux/regset.h>
+#include <linux/sched.h>
+#include <linux/sched/task_stack.h>
+#include <linux/tracehook.h>
+#include <trace/events/syscalls.h>
+
+enum riscv_regset {
+       REGSET_X,
+};
+
+static int riscv_gpr_get(struct task_struct *target,
+                        const struct user_regset *regset,
+                        unsigned int pos, unsigned int count,
+                        void *kbuf, void __user *ubuf)
+{
+       struct pt_regs *regs;
+
+       regs = task_pt_regs(target);
+       return user_regset_copyout(&pos, &count, &kbuf, &ubuf, regs, 0, -1);
+}
+
+static int riscv_gpr_set(struct task_struct *target,
+                        const struct user_regset *regset,
+                        unsigned int pos, unsigned int count,
+                        const void *kbuf, const void __user *ubuf)
+{
+       int ret;
+       struct pt_regs *regs;
+
+       regs = task_pt_regs(target);
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &regs, 0, -1);
+       return ret;
+}
+
+
+static const struct user_regset riscv_user_regset[] = {
+       [REGSET_X] = {
+               .core_note_type = NT_PRSTATUS,
+               .n = ELF_NGREG,
+               .size = sizeof(elf_greg_t),
+               .align = sizeof(elf_greg_t),
+               .get = &riscv_gpr_get,
+               .set = &riscv_gpr_set,
+       },
+};
+
+static const struct user_regset_view riscv_user_native_view = {
+       .name = "riscv",
+       .e_machine = EM_RISCV,
+       .regsets = riscv_user_regset,
+       .n = ARRAY_SIZE(riscv_user_regset),
+};
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+       return &riscv_user_native_view;
+}
+
+void ptrace_disable(struct task_struct *child)
+{
+       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+}
+
+long arch_ptrace(struct task_struct *child, long request,
+                unsigned long addr, unsigned long data)
+{
+       long ret = -EIO;
+
+       switch (request) {
+       default:
+               ret = ptrace_request(child, request, addr, data);
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * Allows PTRACE_SYSCALL to work.  These are called from entry.S in
+ * {handle,ret_from}_syscall.
+ */
+void do_syscall_trace_enter(struct pt_regs *regs)
+{
+       if (test_thread_flag(TIF_SYSCALL_TRACE))
+               if (tracehook_report_syscall_entry(regs))
+                       syscall_set_nr(current, regs, -1);
+
+#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
+       if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
+               trace_sys_enter(regs, syscall_get_nr(current, regs));
+#endif
+}
+
+void do_syscall_trace_exit(struct pt_regs *regs)
+{
+       if (test_thread_flag(TIF_SYSCALL_TRACE))
+               tracehook_report_syscall_exit(regs, 0);
+
+#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
+       if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
+               trace_sys_exit(regs, regs->regs[0]);
+#endif
+}
diff --git a/arch/riscv/kernel/riscv_ksyms.c b/arch/riscv/kernel/riscv_ksyms.c
new file mode 100644 (file)
index 0000000..23cc81e
--- /dev/null
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2017 Zihao Yu
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/export.h>
+#include <linux/uaccess.h>
+
+/*
+ * Assembly functions that may be used (directly or indirectly) by modules
+ */
+EXPORT_SYMBOL(__copy_user);
diff --git a/arch/riscv/kernel/signal.c b/arch/riscv/kernel/signal.c
new file mode 100644 (file)
index 0000000..718d0c9
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2009 Sunplus Core Technology Co., Ltd.
+ *  Chen Liqin <liqin.chen@sunplusct.com>
+ *  Lennox Wu <lennox.wu@sunplusct.com>
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ */
+
+#include <linux/signal.h>
+#include <linux/uaccess.h>
+#include <linux/syscalls.h>
+#include <linux/tracehook.h>
+#include <linux/linkage.h>
+
+#include <asm/ucontext.h>
+#include <asm/vdso.h>
+#include <asm/switch_to.h>
+#include <asm/csr.h>
+
+#define DEBUG_SIG 0
+
+struct rt_sigframe {
+       struct siginfo info;
+       struct ucontext uc;
+};
+
+static long restore_d_state(struct pt_regs *regs,
+       struct __riscv_d_ext_state __user *state)
+{
+       long err;
+       err = __copy_from_user(&current->thread.fstate, state, sizeof(*state));
+       if (likely(!err))
+               fstate_restore(current, regs);
+       return err;
+}
+
+static long save_d_state(struct pt_regs *regs,
+       struct __riscv_d_ext_state __user *state)
+{
+       fstate_save(current, regs);
+       return __copy_to_user(state, &current->thread.fstate, sizeof(*state));
+}
+
+static long restore_sigcontext(struct pt_regs *regs,
+       struct sigcontext __user *sc)
+{
+       long err;
+       size_t i;
+       /* sc_regs is structured the same as the start of pt_regs */
+       err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs));
+       if (unlikely(err))
+               return err;
+       /* Restore the floating-point state. */
+       err = restore_d_state(regs, &sc->sc_fpregs.d);
+       if (unlikely(err))
+               return err;
+       /* We support no other extension state at this time. */
+       for (i = 0; i < ARRAY_SIZE(sc->sc_fpregs.q.reserved); i++) {
+               u32 value;
+               err = __get_user(value, &sc->sc_fpregs.q.reserved[i]);
+               if (unlikely(err))
+                       break;
+               if (value != 0)
+                       return -EINVAL;
+       }
+       return err;
+}
+
+SYSCALL_DEFINE0(rt_sigreturn)
+{
+       struct pt_regs *regs = current_pt_regs();
+       struct rt_sigframe __user *frame;
+       struct task_struct *task;
+       sigset_t set;
+
+       /* Always make any pending restarted system calls return -EINTR */
+       current->restart_block.fn = do_no_restart_syscall;
+
+       frame = (struct rt_sigframe __user *)regs->sp;
+
+       if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+               goto badframe;
+
+       if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+               goto badframe;
+
+       set_current_blocked(&set);
+
+       if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+               goto badframe;
+
+       if (restore_altstack(&frame->uc.uc_stack))
+               goto badframe;
+
+       return regs->a0;
+
+badframe:
+       task = current;
+       if (show_unhandled_signals) {
+               pr_info_ratelimited(
+                       "%s[%d]: bad frame in %s: frame=%p pc=%p sp=%p\n",
+                       task->comm, task_pid_nr(task), __func__,
+                       frame, (void *)regs->sepc, (void *)regs->sp);
+       }
+       force_sig(SIGSEGV, task);
+       return 0;
+}
+
+static long setup_sigcontext(struct rt_sigframe __user *frame,
+       struct pt_regs *regs)
+{
+       struct sigcontext __user *sc = &frame->uc.uc_mcontext;
+       long err;
+       size_t i;
+       /* sc_regs is structured the same as the start of pt_regs */
+       err = __copy_to_user(&sc->sc_regs, regs, sizeof(sc->sc_regs));
+       /* Save the floating-point state. */
+       err |= save_d_state(regs, &sc->sc_fpregs.d);
+       /* We support no other extension state at this time. */
+       for (i = 0; i < ARRAY_SIZE(sc->sc_fpregs.q.reserved); i++)
+               err |= __put_user(0, &sc->sc_fpregs.q.reserved[i]);
+       return err;
+}
+
+static inline void __user *get_sigframe(struct ksignal *ksig,
+       struct pt_regs *regs, size_t framesize)
+{
+       unsigned long sp;
+       /* Default to using normal stack */
+       sp = regs->sp;
+
+       /*
+        * If we are on the alternate signal stack and would overflow it, don't.
+        * Return an always-bogus address instead so we will die with SIGSEGV.
+        */
+       if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize)))
+               return (void __user __force *)(-1UL);
+
+       /* This is the X/Open sanctioned signal stack switching. */
+       sp = sigsp(sp, ksig) - framesize;
+
+       /* Align the stack frame. */
+       sp &= ~0xfUL;
+
+       return (void __user *)sp;
+}
+
+
+static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
+       struct pt_regs *regs)
+{
+       struct rt_sigframe __user *frame;
+       long err = 0;
+
+       frame = get_sigframe(ksig, regs, sizeof(*frame));
+       if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+               return -EFAULT;
+
+       err |= copy_siginfo_to_user(&frame->info, &ksig->info);
+
+       /* Create the ucontext. */
+       err |= __put_user(0, &frame->uc.uc_flags);
+       err |= __put_user(NULL, &frame->uc.uc_link);
+       err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
+       err |= setup_sigcontext(frame, regs);
+       err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+       if (err)
+               return -EFAULT;
+
+       /* Set up to return from userspace. */
+       regs->ra = (unsigned long)VDSO_SYMBOL(
+               current->mm->context.vdso, rt_sigreturn);
+
+       /*
+        * Set up registers for signal handler.
+        * Registers that we don't modify keep the value they had from
+        * user-space at the time we took the signal.
+        * We always pass siginfo and mcontext, regardless of SA_SIGINFO,
+        * since some things rely on this (e.g. glibc's debug/segfault.c).
+        */
+       regs->sepc = (unsigned long)ksig->ka.sa.sa_handler;
+       regs->sp = (unsigned long)frame;
+       regs->a0 = ksig->sig;                     /* a0: signal number */
+       regs->a1 = (unsigned long)(&frame->info); /* a1: siginfo pointer */
+       regs->a2 = (unsigned long)(&frame->uc);   /* a2: ucontext pointer */
+
+#if DEBUG_SIG
+       pr_info("SIG deliver (%s:%d): sig=%d pc=%p ra=%p sp=%p\n",
+               current->comm, task_pid_nr(current), ksig->sig,
+               (void *)regs->sepc, (void *)regs->ra, frame);
+#endif
+
+       return 0;
+}
+
+static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
+{
+       sigset_t *oldset = sigmask_to_save();
+       int ret;
+
+       /* Are we from a system call? */
+       if (regs->scause == EXC_SYSCALL) {
+               /* If so, check system call restarting.. */
+               switch (regs->a0) {
+               case -ERESTART_RESTARTBLOCK:
+               case -ERESTARTNOHAND:
+                       regs->a0 = -EINTR;
+                       break;
+
+               case -ERESTARTSYS:
+                       if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
+                               regs->a0 = -EINTR;
+                               break;
+                       }
+                       /* fallthrough */
+               case -ERESTARTNOINTR:
+                        regs->a0 = regs->orig_a0;
+                       regs->sepc -= 0x4;
+                       break;
+               }
+       }
+
+       /* Set up the stack frame */
+       ret = setup_rt_frame(ksig, oldset, regs);
+
+       signal_setup_done(ret, ksig, 0);
+}
+
+static void do_signal(struct pt_regs *regs)
+{
+       struct ksignal ksig;
+
+       if (get_signal(&ksig)) {
+               /* Actually deliver the signal */
+               handle_signal(&ksig, regs);
+               return;
+       }
+
+       /* Did we come from a system call? */
+       if (regs->scause == EXC_SYSCALL) {
+               /* Restart the system call - no handlers present */
+               switch (regs->a0) {
+               case -ERESTARTNOHAND:
+               case -ERESTARTSYS:
+               case -ERESTARTNOINTR:
+                        regs->a0 = regs->orig_a0;
+                       regs->sepc -= 0x4;
+                       break;
+               case -ERESTART_RESTARTBLOCK:
+                        regs->a0 = regs->orig_a0;
+                       regs->a7 = __NR_restart_syscall;
+                       regs->sepc -= 0x4;
+                       break;
+               }
+       }
+
+       /*
+        * If there is no signal to deliver, we just put the saved
+        * sigmask back.
+        */
+       restore_saved_sigmask();
+}
+
+/*
+ * notification of userspace execution resumption
+ * - triggered by the _TIF_WORK_MASK flags
+ */
+asmlinkage void do_notify_resume(struct pt_regs *regs,
+       unsigned long thread_info_flags)
+{
+       /* Handle pending signal delivery */
+       if (thread_info_flags & _TIF_SIGPENDING)
+               do_signal(regs);
+
+       if (thread_info_flags & _TIF_NOTIFY_RESUME) {
+               clear_thread_flag(TIF_NOTIFY_RESUME);
+               tracehook_notify_resume(regs);
+       }
+}
diff --git a/arch/riscv/kernel/sys_riscv.c b/arch/riscv/kernel/sys_riscv.c
new file mode 100644 (file)
index 0000000..4351be7
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ * Copyright (C) 2014 Darius Rad <darius@bluespec.com>
+ * Copyright (C) 2017 SiFive
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include <linux/syscalls.h>
+#include <asm/cmpxchg.h>
+#include <asm/unistd.h>
+
+static long riscv_sys_mmap(unsigned long addr, unsigned long len,
+                          unsigned long prot, unsigned long flags,
+                          unsigned long fd, off_t offset,
+                          unsigned long page_shift_offset)
+{
+       if (unlikely(offset & (~PAGE_MASK >> page_shift_offset)))
+               return -EINVAL;
+       return sys_mmap_pgoff(addr, len, prot, flags, fd,
+                             offset >> (PAGE_SHIFT - page_shift_offset));
+}
+
+#ifdef CONFIG_64BIT
+SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
+       unsigned long, prot, unsigned long, flags,
+       unsigned long, fd, off_t, offset)
+{
+       return riscv_sys_mmap(addr, len, prot, flags, fd, offset, 0);
+}
+#else
+SYSCALL_DEFINE6(mmap2, unsigned long, addr, unsigned long, len,
+       unsigned long, prot, unsigned long, flags,
+       unsigned long, fd, off_t, offset)
+{
+       /*
+        * Note that the shift for mmap2 is constant (12),
+        * regardless of PAGE_SIZE
+        */
+       return riscv_sys_mmap(addr, len, prot, flags, fd, offset, 12);
+}
+#endif /* !CONFIG_64BIT */
diff --git a/arch/riscv/kernel/syscall_table.c b/arch/riscv/kernel/syscall_table.c
new file mode 100644 (file)
index 0000000..4e30dc5
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2009 Arnd Bergmann <arnd@arndb.de>
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include <linux/linkage.h>
+#include <linux/syscalls.h>
+#include <asm-generic/syscalls.h>
+
+#undef __SYSCALL
+#define __SYSCALL(nr, call)    [nr] = (call),
+
+void *sys_call_table[__NR_syscalls] = {
+       [0 ... __NR_syscalls - 1] = sys_ni_syscall,
+#include <asm/unistd.h>
+};
diff --git a/arch/riscv/kernel/vdso/.gitignore b/arch/riscv/kernel/vdso/.gitignore
new file mode 100644 (file)
index 0000000..97c2d69
--- /dev/null
@@ -0,0 +1,2 @@
+vdso.lds
+*.tmp
diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile
new file mode 100644 (file)
index 0000000..523d0a8
--- /dev/null
@@ -0,0 +1,63 @@
+# Copied from arch/tile/kernel/vdso/Makefile
+
+# Symbols present in the vdso
+vdso-syms = rt_sigreturn
+
+# Files to link into the vdso
+obj-vdso = $(patsubst %, %.o, $(vdso-syms))
+
+# Build rules
+targets := $(obj-vdso) vdso.so vdso.so.dbg vdso.lds vdso-dummy.o
+obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
+
+obj-y += vdso.o vdso-syms.o
+CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
+
+# Disable gcov profiling for VDSO code
+GCOV_PROFILE := n
+
+# Force dependency
+$(obj)/vdso.o: $(obj)/vdso.so
+
+# link rule for the .so file, .lds has to be first
+SYSCFLAGS_vdso.so.dbg = $(c_flags)
+$(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso) FORCE
+       $(call if_changed,vdsold)
+
+# We also create a special relocatable object that should mirror the symbol
+# table and layout of the linked DSO.  With ld -R we can then refer to
+# these symbols in the kernel code rather than hand-coded addresses.
+
+SYSCFLAGS_vdso.so.dbg = -shared -s -Wl,-soname=linux-vdso.so.1 \
+                            $(call cc-ldoption, -Wl$(comma)--hash-style=both)
+$(obj)/vdso-dummy.o: $(src)/vdso.lds $(obj)/rt_sigreturn.o FORCE
+       $(call if_changed,vdsold)
+
+LDFLAGS_vdso-syms.o := -r -R
+$(obj)/vdso-syms.o: $(obj)/vdso-dummy.o FORCE
+       $(call if_changed,ld)
+
+# strip rule for the .so file
+$(obj)/%.so: OBJCOPYFLAGS := -S
+$(obj)/%.so: $(obj)/%.so.dbg FORCE
+       $(call if_changed,objcopy)
+
+# actual build commands
+# The DSO images are built using a special linker script
+# Add -lgcc so rv32 gets static muldi3 and lshrdi3 definitions.
+# Make sure only to export the intended __vdso_xxx symbol offsets.
+quiet_cmd_vdsold = VDSOLD  $@
+      cmd_vdsold = $(CC) $(KCFLAGS) -nostdlib $(SYSCFLAGS_$(@F)) \
+                           -Wl,-T,$(filter-out FORCE,$^) -o $@.tmp -lgcc && \
+                   $(CROSS_COMPILE)objcopy \
+                           $(patsubst %, -G __vdso_%, $(vdso-syms)) $@.tmp $@
+
+# install commands for the unstripped file
+quiet_cmd_vdso_install = INSTALL $@
+      cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
+
+vdso.so: $(obj)/vdso.so.dbg
+       @mkdir -p $(MODLIB)/vdso
+       $(call cmd,vdso_install)
+
+vdso_install: vdso.so
diff --git a/arch/riscv/kernel/vdso/rt_sigreturn.S b/arch/riscv/kernel/vdso/rt_sigreturn.S
new file mode 100644 (file)
index 0000000..f5aa3d7
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 Regents of the University of California
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include <linux/linkage.h>
+#include <asm/unistd.h>
+
+       .text
+ENTRY(__vdso_rt_sigreturn)
+       .cfi_startproc
+       .cfi_signal_frame
+       li a7, __NR_rt_sigreturn
+       scall
+       .cfi_endproc
+ENDPROC(__vdso_rt_sigreturn)
diff --git a/arch/riscv/kernel/vdso/vdso.S b/arch/riscv/kernel/vdso/vdso.S
new file mode 100644 (file)
index 0000000..7055de5
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 Regents of the University of California
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <asm/page.h>
+
+       __PAGE_ALIGNED_DATA
+
+       .globl vdso_start, vdso_end
+       .balign PAGE_SIZE
+vdso_start:
+       .incbin "arch/riscv/kernel/vdso/vdso.so"
+       .balign PAGE_SIZE
+vdso_end:
+
+       .previous
diff --git a/arch/riscv/kernel/vdso/vdso.lds.S b/arch/riscv/kernel/vdso/vdso.lds.S
new file mode 100644 (file)
index 0000000..8c9dce9
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 Regents of the University of California
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public License
+ *   as published by the Free Software Foundation, version 2.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ */
+
+OUTPUT_ARCH(riscv)
+
+SECTIONS
+{
+       . = SIZEOF_HEADERS;
+
+       .hash           : { *(.hash) }                  :text
+       .gnu.hash       : { *(.gnu.hash) }
+       .dynsym         : { *(.dynsym) }
+       .dynstr         : { *(.dynstr) }
+       .gnu.version    : { *(.gnu.version) }
+       .gnu.version_d  : { *(.gnu.version_d) }
+       .gnu.version_r  : { *(.gnu.version_r) }
+
+       .note           : { *(.note.*) }                :text   :note
+       .dynamic        : { *(.dynamic) }               :text   :dynamic
+
+       .eh_frame_hdr   : { *(.eh_frame_hdr) }          :text   :eh_frame_hdr
+       .eh_frame       : { KEEP (*(.eh_frame)) }       :text
+
+       .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+
+       /*
+        * This linker script is used both with -r and with -shared.
+        * For the layouts to match, we need to skip more than enough
+        * space for the dynamic symbol table, etc. If this amount is
+        * insufficient, ld -shared will error; simply increase it here.
+        */
+       . = 0x800;
+       .text           : { *(.text .text.*) }          :text
+
+       .data           : {
+               *(.got.plt) *(.got)
+               *(.data .data.* .gnu.linkonce.d.*)
+               *(.dynbss)
+               *(.bss .bss.* .gnu.linkonce.b.*)
+       }
+}
+
+/*
+ * We must supply the ELF program headers explicitly to get just one
+ * PT_LOAD segment, and set the flags explicitly to make segments read-only.
+ */
+PHDRS
+{
+       text            PT_LOAD         FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
+       dynamic         PT_DYNAMIC      FLAGS(4);               /* PF_R */
+       note            PT_NOTE         FLAGS(4);               /* PF_R */
+       eh_frame_hdr    PT_GNU_EH_FRAME;
+}
+
+/*
+ * This controls what symbols we export from the DSO.
+ */
+VERSION
+{
+       LINUX_4.15 {
+       global:
+               __vdso_rt_sigreturn;
+               __vdso_cmpxchg32;
+               __vdso_cmpxchg64;
+       local: *;
+       };
+}