Add system call support and related uaccess.h for kvx.

Co-developed-by: Clement Leger <[email protected]>
Signed-off-by: Clement Leger <[email protected]>
Co-developed-by: Guillaume Thouvenin <[email protected]>
Signed-off-by: Guillaume Thouvenin <[email protected]>
Co-developed-by: Julian Vetter <[email protected]>
Signed-off-by: Julian Vetter <[email protected]>
Co-developed-by: Julien Villette <[email protected]>
Signed-off-by: Julien Villette <[email protected]>
Co-developed-by: Marius Gligor <[email protected]>
Signed-off-by: Marius Gligor <[email protected]>
Co-developed-by: Yann Sionneau <[email protected]>
Signed-off-by: Yann Sionneau <[email protected]>
---

Notes:
    V1 -> V2:
     - typo fixes (clober* -> clobber*)
     - use generic __access_ok

 arch/kvx/include/asm/syscall.h       |   73 ++
 arch/kvx/include/asm/syscalls.h      |   21 +
 arch/kvx/include/asm/uaccess.h       |  317 +++++
 arch/kvx/include/asm/unistd.h        |   11 +
 arch/kvx/include/uapi/asm/cachectl.h |   25 +
 arch/kvx/include/uapi/asm/unistd.h   |   16 +
 arch/kvx/kernel/entry.S              | 1759 ++++++++++++++++++++++++++
 arch/kvx/kernel/sys_kvx.c            |   58 +
 arch/kvx/kernel/syscall_table.c      |   19 +
 9 files changed, 2299 insertions(+)
 create mode 100644 arch/kvx/include/asm/syscall.h
 create mode 100644 arch/kvx/include/asm/syscalls.h
 create mode 100644 arch/kvx/include/asm/uaccess.h
 create mode 100644 arch/kvx/include/asm/unistd.h
 create mode 100644 arch/kvx/include/uapi/asm/cachectl.h
 create mode 100644 arch/kvx/include/uapi/asm/unistd.h
 create mode 100644 arch/kvx/kernel/entry.S
 create mode 100644 arch/kvx/kernel/sys_kvx.c
 create mode 100644 arch/kvx/kernel/syscall_table.c

diff --git a/arch/kvx/include/asm/syscall.h b/arch/kvx/include/asm/syscall.h
new file mode 100644
index 000000000000..a3f6cef73e4a
--- /dev/null
+++ b/arch/kvx/include/asm/syscall.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2023 Kalray Inc.
+ * Author(s): Clement Leger
+ */
+
+#ifndef _ASM_KVX_SYSCALL_H
+#define _ASM_KVX_SYSCALL_H
+
+#include <linux/err.h>
+#include <linux/audit.h>
+
+#include <asm/ptrace.h>
+
+/* The array of function pointers for syscalls. */
+extern void *sys_call_table[];
+
+void scall_machine_exit(unsigned char value);
+
+/**
+ * syscall_get_nr - find which system call a task is executing
+ * @task:      task of interest, must be blocked
+ * @regs:      task_pt_regs() of @task
+ *
+ * If @task is executing a system call or is at system call
+ * tracing about to attempt one, returns the system call number.
+ * If @task is not executing a system call, i.e. it's blocked
+ * inside the kernel for a fault or signal, returns -1.
+ *
+ * Note this returns int even on 64-bit machines.  Only 32 bits of
+ * system call number can be meaningful.  If the actual arch value
+ * is 64 bits, this truncates to 32 bits so 0xffffffff means -1.
+ *
+ * It's only valid to call this when @task is known to be blocked.
+ */
+static inline int syscall_get_nr(struct task_struct *task, struct pt_regs 
*regs)
+{
+       if (!in_syscall(regs))
+               return -1;
+
+       return es_sysno(regs);
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+                                    struct pt_regs *regs)
+{
+       /* 0 if syscall succeeded, otherwise -Errorcode */
+       return IS_ERR_VALUE(regs->r0) ? regs->r0 : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+                                           struct pt_regs *regs)
+{
+       return regs->r0;
+}
+
+static inline int syscall_get_arch(struct task_struct *task)
+{
+       return AUDIT_ARCH_KVX;
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+                                        struct pt_regs *regs,
+                                        unsigned long *args)
+{
+       args[0] = regs->orig_r0;
+       args++;
+       memcpy(args, &regs->r1, 5 * sizeof(args[0]));
+}
+
+int __init setup_syscall_sigreturn_page(void *sigpage_addr);
+
+#endif
diff --git a/arch/kvx/include/asm/syscalls.h b/arch/kvx/include/asm/syscalls.h
new file mode 100644
index 000000000000..beec95ebb97a
--- /dev/null
+++ b/arch/kvx/include/asm/syscalls.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2023 Kalray Inc.
+ * Author(s): Clement Leger
+ */
+
+#ifndef _ASM_KVX_SYSCALLS_H
+#define _ASM_KVX_SYSCALLS_H
+
+#include <asm-generic/syscalls.h>
+
+/* We redefine clone in assembly for special slowpath */
+asmlinkage long __sys_clone(unsigned long clone_flags, unsigned long newsp,
+                       int __user *parent_tid, int __user *child_tid, int tls);
+
+#define sys_clone __sys_clone
+
+long sys_cachectl(unsigned long addr, unsigned long len, unsigned long cache,
+                 unsigned long flags);
+
+#endif
diff --git a/arch/kvx/include/asm/uaccess.h b/arch/kvx/include/asm/uaccess.h
new file mode 100644
index 000000000000..24f91d75c1dd
--- /dev/null
+++ b/arch/kvx/include/asm/uaccess.h
@@ -0,0 +1,317 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * derived from arch/riscv/include/asm/uaccess.h
+ *
+ * Copyright (C) 2017-2023 Kalray Inc.
+ * Author(s): Clement Leger
+ *            Guillaume Thouvenin
+ */
+
+#ifndef _ASM_KVX_UACCESS_H
+#define _ASM_KVX_UACCESS_H
+
+#include <linux/sched.h>
+#include <linux/types.h>
+
+/**
+ * access_ok: - Checks if a user space pointer is valid
+ * @addr: User space pointer to start of block to check
+ * @size: Size of block to check
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * Checks if a pointer to a block of memory in user space is valid.
+ *
+ * Returns true (nonzero) if the memory block may be valid, false (zero)
+ * if it is definitely invalid.
+ *
+ * Note that, depending on architecture, this function probably just
+ * checks that the pointer is in the user space range - after calling
+ * this function, memory access functions may still return -EFAULT.
+ */
+#define access_ok(addr, size) ({                                       \
+       __chk_user_ptr(addr);                                           \
+       likely(__access_ok((addr), (size)));                            \
+})
+
+#include <asm-generic/access_ok.h>
+
+/*
+ * The exception table consists of pairs of addresses: the first is the
+ * address of an instruction that is allowed to fault, and the second is
+ * the address at which the program should continue.  No registers are
+ * modified, so it is entirely up to the continuation code to figure out
+ * what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path.  This means when everything is well,
+ * we don't even have to jump over them.  Further, they do not intrude
+ * on our cache or TLB entries.
+ */
+
+struct exception_table_entry {
+       unsigned long insn, fixup;
+};
+
+extern int fixup_exception(struct pt_regs *regs);
+
+/**
+ * Assembly defined function (usercopy.S)
+ */
+extern unsigned long
+raw_copy_from_user(void *to, const void __user *from, unsigned long n);
+
+extern unsigned long
+raw_copy_to_user(void __user *to, const void *from, unsigned long n);
+
+extern unsigned long
+asm_clear_user(void __user *to, unsigned long n);
+
+#define __clear_user asm_clear_user
+
+static inline __must_check unsigned long
+clear_user(void __user *to, unsigned long n)
+{
+       might_fault();
+       if (!access_ok(to, n))
+               return n;
+
+       return asm_clear_user(to, n);
+}
+
+extern __must_check long strnlen_user(const char __user *str, long n);
+extern long strncpy_from_user(char *dest, const char __user *src, long count);
+
+#define __enable_user_access()
+#define __disable_user_access()
+
+/**
+ * get_user: - Get a simple variable from user space.
+ * @x:   Variable to store result.
+ * @ptr: Source address, in user space.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * This macro copies a single simple variable from user space to kernel
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and the result of
+ * dereferencing @ptr must be assignable to @x without a cast.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ * On error, the variable @x is set to zero.
+ */
+#define get_user(x, ptr)                                               \
+({                                                                     \
+       long __e = -EFAULT;                                             \
+       const __typeof__(*(ptr)) __user *__p = (ptr);                   \
+       might_fault();                                                  \
+       if (likely(access_ok(__p, sizeof(*__p)))) {                     \
+               __e = __get_user(x, __p);                               \
+       } else {                                                        \
+               x = 0;                                                  \
+       }                                                               \
+       __e;                                                            \
+})
+
+/**
+ * __get_user: - Get a simple variable from user space, with less checking.
+ * @x:   Variable to store result.
+ * @ptr: Source address, in user space.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * This macro copies a single simple variable from user space to kernel
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and the result of
+ * dereferencing @ptr must be assignable to @x without a cast.
+ *
+ * Caller must check the pointer with access_ok() before calling this
+ * function.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ * On error, the variable @x is set to zero.
+ */
+#define __get_user(x, ptr)                                             \
+({                                                                     \
+       unsigned long __err = 0;                                        \
+       __chk_user_ptr(ptr);                                            \
+                                                                       \
+       __enable_user_access();                                         \
+       __get_user_nocheck(x, ptr, __err);                              \
+       __disable_user_access();                                        \
+                                                                       \
+       __err;                                                          \
+})
+
+#define __get_user_nocheck(x, ptr, err)                                        
\
+do {                                                                   \
+       unsigned long __gu_addr = (unsigned long)(ptr);                 \
+       unsigned long __gu_val;                                         \
+       switch (sizeof(*(ptr))) {                                       \
+       case 1:                                                         \
+               __get_user_asm("lbz", __gu_val, __gu_addr, err);        \
+               break;                                                  \
+       case 2:                                                         \
+               __get_user_asm("lhz", __gu_val, __gu_addr, err);        \
+               break;                                                  \
+       case 4:                                                         \
+               __get_user_asm("lwz", __gu_val, __gu_addr, err);        \
+               break;                                                  \
+       case 8:                                                         \
+               __get_user_asm("ld", __gu_val, __gu_addr, err);         \
+               break;                                                  \
+       default:                                                        \
+               BUILD_BUG();                                            \
+       }                                                               \
+       (x) = (__typeof__(*(ptr)))__gu_val;                             \
+} while (0)
+
+#define __get_user_asm(op, x, addr, err)                               \
+({                                                                     \
+       __asm__ __volatile__(                                           \
+                       "1:     "op" %1 = 0[%2]\n"                      \
+                       "       ;;\n"                                   \
+                       "2:\n"                                          \
+                       ".section .fixup,\"ax\"\n"                      \
+                       "3:     make %0 = 2b\n"                         \
+                       "       make %1 = 0\n"                          \
+                       "       ;;\n"                                   \
+                       "       make %0 = %3\n"                         \
+                       "       igoto %0\n"                             \
+                       "       ;;\n"                                   \
+                       ".previous\n"                                   \
+                       ".section __ex_table,\"a\"\n"                   \
+                       "       .align 8\n"                             \
+                       "       .dword 1b,3b\n"                         \
+                       ".previous"                                     \
+                       : "=r"(err), "=r"(x)                            \
+                       : "r"(addr), "i"(-EFAULT), "0"(err));           \
+})
+
+/**
+ * put_user: - Write a simple value into user space.
+ * @x:   Value to copy to user space.
+ * @ptr: Destination address, in user space.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * This macro copies a single simple value from kernel space to user
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and @x must be assignable
+ * to the result of dereferencing @ptr.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ */
+#define put_user(x, ptr)                                               \
+({                                                                     \
+       long __e = -EFAULT;                                             \
+       __typeof__(*(ptr)) __user *__p = (ptr);                         \
+       might_fault();                                                  \
+       if (likely(access_ok(__p, sizeof(*__p)))) {                     \
+               __e = __put_user(x, __p);                               \
+       }                                                               \
+       __e;                                                            \
+})
+
+/**
+ * __put_user: - Write a simple value into user space, with less checking.
+ * @x:   Value to copy to user space.
+ * @ptr: Destination address, in user space.
+ *
+ * Context: User context only.  This function may sleep.
+ *
+ * This macro copies a single simple value from kernel space to user
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and @x must be assignable
+ * to the result of dereferencing @ptr.
+ *
+ * Caller must check the pointer with access_ok() before calling this
+ * function.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ */
+#define __put_user(x, ptr)                                             \
+({                                                                     \
+       unsigned long __err = 0;                                        \
+       __chk_user_ptr(ptr);                                            \
+                                                                       \
+       __enable_user_access();                                         \
+       __put_user_nocheck(x, ptr, __err);                              \
+       __disable_user_access();                                        \
+                                                                       \
+       __err;                                                          \
+})
+
+#define __put_user_nocheck(x, ptr, err)                                        
\
+do {                                                                   \
+       unsigned long __pu_addr = (unsigned long)(ptr);                 \
+       __typeof__(*(ptr)) __pu_val = (x);                              \
+       switch (sizeof(*(ptr))) {                                       \
+       case 1:                                                         \
+               __put_user_asm("sb", __pu_val, __pu_addr, err);         \
+               break;                                                  \
+       case 2:                                                         \
+               __put_user_asm("sh", __pu_val, __pu_addr, err);         \
+               break;                                                  \
+       case 4:                                                         \
+               __put_user_asm("sw", __pu_val, __pu_addr, err);         \
+               break;                                                  \
+       case 8:                                                         \
+               __put_user_asm("sd", __pu_val, __pu_addr, err);         \
+               break;                                                  \
+       default:                                                        \
+               BUILD_BUG();                                            \
+       }                                                               \
+} while (0)
+
+#define __put_user_asm(op, x, addr, err)                               \
+({                                                                     \
+       __asm__ __volatile__(                                           \
+                       "1:     "op" 0[%2], %1\n"                       \
+                       "       ;;\n"                                   \
+                       "2:\n"                                          \
+                       ".section .fixup,\"ax\"\n"                      \
+                       "3:     make %0 = 2b\n"                         \
+                       "       ;;\n"                                   \
+                       "       make %0 = %3\n"                         \
+                       "       igoto %0\n"                             \
+                       "       ;;\n"                                   \
+                       ".previous\n"                                   \
+                       ".section __ex_table,\"a\"\n"                   \
+                       "       .align 8\n"                             \
+                       "       .dword 1b,3b\n"                         \
+                       ".previous"                                     \
+                       : "=r"(err)                                     \
+                       : "r"(x), "r"(addr), "i"(-EFAULT), "0"(err));   \
+})
+
+#define HAVE_GET_KERNEL_NOFAULT
+
+#define __get_kernel_nofault(dst, src, type, err_label)                        
\
+do {                                                                   \
+       long __kr_err;                                                  \
+                                                                       \
+       __get_user_nocheck(*((type *)(dst)), (type *)(src), __kr_err);  \
+       if (unlikely(__kr_err))                                         \
+               goto err_label;                                         \
+} while (0)
+
+#define __put_kernel_nofault(dst, src, type, err_label)                        
\
+do {                                                                   \
+       long __kr_err;                                                  \
+                                                                       \
+       __put_user_nocheck(*((type *)(src)), (type *)(dst), __kr_err);  \
+       if (unlikely(__kr_err))                                         \
+               goto err_label;                                         \
+} while (0)
+
+
+#endif /* _ASM_KVX_UACCESS_H */
diff --git a/arch/kvx/include/asm/unistd.h b/arch/kvx/include/asm/unistd.h
new file mode 100644
index 000000000000..6cd093dbf2d8
--- /dev/null
+++ b/arch/kvx/include/asm/unistd.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2023 Kalray Inc.
+ * Author(s): Clement Leger
+ */
+
+#define __ARCH_WANT_SYS_CLONE
+
+#include <uapi/asm/unistd.h>
+
+#define NR_syscalls (__NR_syscalls)
diff --git a/arch/kvx/include/uapi/asm/cachectl.h 
b/arch/kvx/include/uapi/asm/cachectl.h
new file mode 100644
index 000000000000..be0a1aa23cf6
--- /dev/null
+++ b/arch/kvx/include/uapi/asm/cachectl.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2017-2023 Kalray Inc.
+ * Author(s): Clement Leger
+ */
+
+#ifndef _UAPI_ASM_KVX_CACHECTL_H
+#define _UAPI_ASM_KVX_CACHECTL_H
+
+/*
+ * Cache type for cachectl system call
+ */
+#define CACHECTL_CACHE_DCACHE          (1 << 0)
+
+/*
+ * Flags for cachectl system call
+ */
+#define CACHECTL_FLAG_OP_INVAL         (1 << 0)
+#define CACHECTL_FLAG_OP_WB            (1 << 1)
+#define CACHECTL_FLAG_OP_MASK          (CACHECTL_FLAG_OP_INVAL | \
+                                        CACHECTL_FLAG_OP_WB)
+
+#define CACHECTL_FLAG_ADDR_PHYS                (1 << 2)
+
+#endif /* _UAPI_ASM_KVX_CACHECTL_H */
diff --git a/arch/kvx/include/uapi/asm/unistd.h 
b/arch/kvx/include/uapi/asm/unistd.h
new file mode 100644
index 000000000000..5f86d81dbb76
--- /dev/null
+++ b/arch/kvx/include/uapi/asm/unistd.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2017-2023 Kalray Inc.
+ * Author(s): Clement Leger
+ */
+
+#define __ARCH_WANT_RENAMEAT
+#define __ARCH_WANT_NEW_STAT
+#define __ARCH_WANT_SET_GET_RLIMIT
+#define __ARCH_WANT_SYS_CLONE3
+
+#include <asm-generic/unistd.h>
+
+/* Additional KVX specific syscalls */
+#define __NR_cachectl (__NR_arch_specific_syscall)
+__SYSCALL(__NR_cachectl, sys_cachectl)
diff --git a/arch/kvx/kernel/entry.S b/arch/kvx/kernel/entry.S
new file mode 100644
index 000000000000..e73dc2221c06
--- /dev/null
+++ b/arch/kvx/kernel/entry.S
@@ -0,0 +1,1759 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2017-2023 Kalray Inc.
+ * Author(s): Clement Leger
+ *            Guillaume Thouvenin
+ *            Marius Gligor
+ *            Yann Sionneau
+ *            Julien Villette
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+#include <asm/cache.h>
+#include <asm/page.h>
+#include <asm/pgtable-bits.h>
+#include <asm/sys_arch.h>
+#include <asm/sfr_defs.h>
+#include <asm/tlb_defs.h>
+#include <asm/mem_map.h>
+#include <asm/traps.h>
+#include <asm/unistd.h>
+
+#define MMU_SET_MASK           ((1 << KVX_SFR_MMC_SS_WIDTH) - 1)
+
+/* Mask to replicate data from 32bits LSB to MSBs */
+#define REPLICATE_32_MASK 0x0804020108040201
+
+#define PS_HWLOOP_ENABLE       (KVX_SFR_PS_HLE_MASK << 32)
+#define PS_HWLOOP_DISABLE      (KVX_SFR_PS_HLE_MASK)
+#define PS_HWLOOP_EN_ET_CLEAR_DAUS_DIS \
+       (PS_HWLOOP_ENABLE | KVX_SFR_PS_ET_MASK | SFR_SET_VAL_WFXL(PS, DAUS, 0))
+#define PS_ET_CLEAR KVX_SFR_PS_ET_MASK
+#define PS_HLE_EN_IT_EN_ET_CLEAR_DAUS_DIS      \
+               (SFR_SET_VAL_WFXL(PS, HLE, 1) | \
+               SFR_SET_VAL_WFXL(PS, IE, 1) | \
+               SFR_SET_VAL_WFXL(PS, ET, 0) | \
+               SFR_SET_VAL_WFXL(PS, DAUS, 0))
+
+#define PS_IT_DIS              KVX_SFR_PS_IE_MASK
+#define PS_IL_CLEAR            KVX_SFR_PS_IL_WFXL_CLEAR
+
+#define MMC_CLEAR_TLB_CLEAR_WAY \
+       (SFR_CLEAR_WFXL(MMC, SB) | SFR_CLEAR_WFXL(MMC, SW))
+
+#define MMC_SEL_TLB_CLEAR_WAY(__tlb) \
+       (SFR_SET_WFXL(MMC, SB, __tlb) | MMC_CLEAR_TLB_CLEAR_WAY)
+#define MMC_SEL_JTLB_CLEAR_WAY MMC_SEL_TLB_CLEAR_WAY(MMC_SB_JTLB)
+#define MMC_SEL_LTLB_CLEAR_WAY MMC_SEL_TLB_CLEAR_WAY(MMC_SB_LTLB)
+
+#define MME_WFXL(_enable)      SFR_SET_VAL_WFXL(PS, MME, _enable)
+
+/* Temporary scract system register for trap handling */
+#define TMP_SCRATCH_SR $sr_pl3
+
+.altmacro
+
+#define TEL_DEFAULT_VALUE  (TLB_ES_A_MODIFIED << KVX_SFR_TEL_ES_SHIFT)
+
+#define TASK_THREAD_SAVE_AREA_QUAD(__q) \
+                               (TASK_THREAD_SAVE_AREA + (__q) * QUAD_REG_SIZE)
+
+#ifdef CONFIG_DEBUG_EXCEPTION_STACK
+.section .rodata
+stack_error_panic_str_label:
+       .string "Stack has been messed up !"
+#endif
+
+#ifdef CONFIG_KVX_DEBUG_TLB_WRITE
+.section .rodata
+mmc_error_panic_str_label:
+       .string "Failed to write entry to the JTLB (in assembly refill)"
+#endif
+
+#ifdef CONFIG_KVX_DEBUG_ASN
+.section .rodata
+asn_error_panic_str_label:
+       .string "ASN mismatch !"
+#endif
+
+/**
+ * call_trace_hardirqs: hardirqs tracing call
+ * state: State of hardirqs to be reported (on/off)
+ */
+.macro call_trace_hardirqs state
+#ifdef CONFIG_TRACE_IRQFLAGS
+       call trace_hardirqs_\state
+       ;;
+#endif
+.endm
+
+/**
+ * disable_interrupts: Disable interrupts
+ * tmp_reg: Temporary register to use for interrupt disabling
+ */
+.macro disable_interrupt tmp_reg
+       make \tmp_reg = KVX_SFR_PS_IE_MASK
+       ;;
+       wfxl $ps, \tmp_reg
+       ;;
+.endm
+
+/**
+ * call_do_work_pending: Call do_work_pending and set stack argument ($r0)
+ * NOTE: Since do_work_pending requires thread_flags in $r1, they must
+ * be provided in $r1 before calling this macro.
+ * Moreover, do_work_pending expects interrupts to be disabled.
+ */
+.macro call_do_work_pending
+       copyd $r0 = $sp
+       call do_work_pending
+       ;;
+.endm
+
+/**
+ * save_quad_regs: Save quad registers in temporary thread area
+ * After call, the quad is saved in task_struct.thread.save_area.
+ * sr_swap_reg is only used as a temporary value and will be
+ * restored after returning from this macro
+ *
+ * quad:       Quad to saved
+ * sr_swap_reg:        Register used for sr swap and quad saving.
+ */
+.macro save_quad_regs quad sr_swap_reg
+       rswap \sr_swap_reg = $sr
+       ;;
+       /* Save registers in save_area */
+       so __PA(TASK_THREAD_SAVE_AREA_QUAD(0))[\sr_swap_reg] = \quad
+       /* Restore register */
+       rswap \sr_swap_reg = $sr
+       ;;
+.endm
+
+/**
+ * Switch task when entering kernel if needed.
+ * sp_reg: register which will be used to store original stack pointer when
+ * entering the kernel
+ * task_reg: is a register containing the current task pointer
+ * save_regs_label: label to jump to save register immediately. This label is
+ * used when we are already in kernel execution context.
+ * NOTE: when returning from this macro, we consider that the assembly 
following
+ * it will be executed only when coming from user space
+ */
+.macro switch_stack sp_reg task_reg save_regs_label
+       get \sp_reg = $sps
+       ;;
+       /* Check if $sps.pl bit is 0 (ie in kernel mode)
+        * if so, then we dont have to switch stack */
+       cb.even \sp_reg? \save_regs_label
+       /* Copy original $sp in a scratch reg */
+       copyd \sp_reg = $sp
+       ;;
+       /* restore sp from kernel stack pointer */
+       ld $sp = __PA(TASK_THREAD_KERNEL_SP)[\task_reg]
+       ;;
+.endm
+
+/**
+ * Disable MMU using a specified register
+ * scratch_reg: Scratch register to be used for $ps modification (clobbered)
+ * ps_val: Additionnal $ps modifications after returning disabling MMU
+ */
+.macro disable_mmu scratch_reg ps_val=0
+       /* Disable MME in $sps */
+       make \scratch_reg = MME_WFXL(0)
+       ;;
+       wfxl $sps = \scratch_reg
+       ;;
+       /* Enable DAUS in $ps */
+       make \scratch_reg = SFR_SET_VAL_WFXL(PS, DAUS, 1) | \ps_val
+       ;;
+       wfxl $ps = \scratch_reg
+       /* We are now accessing data with MMU disabled */
+       ;;
+.endm
+
+/**
+ * Disable MMU when entering exception path
+ * This macro does not require any register since used register will be
+ * restored after use. This can safely be used directly after entering
+ * exceptions.
+ */
+.macro exception_entry_disable_mmu
+       set TMP_SCRATCH_SR = $r0
+       disable_mmu $r0
+       ;;
+       get $r0 = TMP_SCRATCH_SR
+       ;;
+.endm
+
+/* Save callee registers on stack */
+.macro save_callee
+       sq PT_R18R19[$sp] = $r18r19
+       ;;
+       so PT_Q20[$sp] = $r20r21r22r23
+       ;;
+       so PT_Q24[$sp] = $r24r25r26r27
+       ;;
+       so PT_Q28[$sp] = $r28r29r30r31
+       ;;
+.endm
+
+/**
+ * Save registers for entry in kernel space.
+ * sp_reg should point to the original stack pointer when entering kernel space
+ * task_reg is a register containing the current task pointer
+ * both task_reg and sp_reg must be in saved_quad since they have been
+ * clobberred and will be restored when restoring saved_quad.
+ * pt_regs_sp is a single register which will be filled by pt_regs addr.
+ * When "returning" from this macro, hardware loop are enabled and exception
+ * is cleared, allowing to call kernel function without any risk.
+ */
+.macro save_regs_for_exception sp_reg task_reg saved_quad pt_regs_sp
+.if (\saved_quad==$r4r5r6r7 || \saved_quad==$r60r61r62r63)
+.error "saved_quad must not be $r4r5r6r7 or $r60r61r62r63 !"
+.endif
+       /* make some room on stack to save registers */
+       addd $sp = $sp, -(PT_SIZE_ON_STACK)
+       so __PA(PT_Q4-PT_SIZE_ON_STACK)[$sp] = $r4r5r6r7
+       /* Compute physical address of stack to avoid using 64bits immediate */
+       addd $r6 = $sp, VA_TO_PA_OFFSET - (PT_SIZE_ON_STACK)
+       ;;
+       so PT_Q60[$r6] = $r60r61r62r63
+       /* Now that $r60r61r62r63 is saved, we can use it for saving
+        * original stack stored in scratch_reg. Note that we can not
+        * use the r12r13r14r15 quad to do that because it would
+        * modify the current $r12/sp ! This is if course not what we
+        * want and hence we use the freshly saved quad $r60r61r62r63.
+        *
+        * Note that we must use scratch_reg before reloading the saved
+        * quad since the scratch reg is contained in it, so reloading
+        * it before copying it would overwrite it.
+        */
+       copyd $r60 = \sp_reg
+       ;;
+       /* Reload the saved quad registers to save correct values
+        * Since we use the scratch reg before that */
+       lo \saved_quad = __PA(TASK_THREAD_SAVE_AREA_QUAD(0))[\task_reg]
+       ;;
+       so PT_Q8[$r6] = $r8r9r10r11
+       ;;
+       so PT_Q0[$r6] = $r0r1r2r3
+       copyd $r61 = $r13
+       get $r5 = $le
+       ;;
+       sq PT_R16R17[$r6] = $r16r17
+       make $r10 = 0x0
+       copyd $r62 = $r14
+       ;;
+       so PT_Q32[$r6] = $r32r33r34r35
+       /* Since we are going to enable hardware loop, we must be careful
+        * and reset le (loop exit) to avoid any exploit and return to
+        * user with kernel mode */
+       set $le = $r10
+       copyd $r63 = $r15
+       ;;
+       so PT_Q36[$r6] = $r36r37r38r39
+       get $r0 = $cs
+       ;;
+       so PT_Q40[$r6] = $r40r41r42r43
+       get $r1 = $spc
+       ;;
+       so PT_Q44[$r6] = $r44r45r46r47
+       get $r2 = $sps
+       ;;
+       so PT_Q48[$r6] = $r48r49r50r51
+       get $r3 = $es
+       ;;
+       so PT_Q52[$r6] = $r52r53r54r55
+       get $r7 = $ra
+       ;;
+       so PT_Q56[$r6] = $r56r57r58r59
+       get $r4 = $lc
+       ;;
+       so PT_Q12[$r6] = $r60r61r62r63
+       copyd $r63 = $r6
+       get $r6 = $ls
+       ;;
+       so PT_CS_SPC_SPS_ES[$r63] = $r0r1r2r3
+       ;;
+       so PT_LC_LE_LS_RA[$r63] = $r4r5r6r7
+       /* Clear frame pointer */
+       make $fp = 0
+       ;;
+       /* Copy regs stack pointer for macro caller */
+       copyd \pt_regs_sp = $sp
+#ifdef CONFIG_DEBUG_EXCEPTION_STACK
+       addd $sp = $sp, -STACK_REG_SIZE
+       ;;
+       sd VA_TO_PA_OFFSET[$sp] = $sp
+       ;;
+#endif
+       /* Reenable hwloop, MMU and clear exception taken */
+       make $r8 = PS_HWLOOP_EN_ET_CLEAR_DAUS_DIS
+       ;;
+       wfxl $ps, $r8
+       ;;
+.endm
+
+/***********************************************************************
+*                Exception vectors trampolines
+***********************************************************************/
+#define exception_trampoline(__type) \
+.section .exception.## __type, "ax", @progbits ;\
+ENTRY(kvx_##__type ##_handler_trampoline): ;\
+       goto kvx_## __type ##_handler ;\
+       ;; ;\
+ENDPROC(kvx_ ## __type ## _handler_trampoline) ;\
+.section .early_exception.## __type, "ax", @progbits ;\
+ENTRY(kvx_## __type ##_early_handler): ;\
+1:     nop ;\
+       ;; ;\
+       goto 1b ;\
+       ;; ;\
+ENDPROC(kvx_ ## __type ## _early_handler)
+
+exception_trampoline(debug)
+exception_trampoline(trap)
+exception_trampoline(interrupt)
+exception_trampoline(syscall)
+
+#define EXCEPTION_ENTRY(__name) \
+.section .exception.text, "ax", @progbits ;\
+ENTRY(__name)
+
+
+/***********************************************************************
+*                  Common exception return path
+***********************************************************************/
+/**
+ * Restore registers after exception
+ * When entering this macro, $sp must be located right before regs
+ * storage.
+ */
+EXCEPTION_ENTRY(return_from_exception)
+#ifdef CONFIG_DEBUG_EXCEPTION_STACK
+       ld $r1 = 0[$sp]
+       ;;
+       sbfd $r1 = $r1, $sp
+       ;;
+       cb.deqz $r1, _check_ok
+       ;;
+       make $r2 = panic
+       make $r0 = stack_error_panic_str_label
+       ;;
+       icall $r2
+       ;;
+_check_ok:
+       addd $sp = $sp, STACK_REG_SIZE
+       ;;
+#endif
+       get $r11 = $sr
+       /* Load sps value from saved registers */
+       ld $r6 = PT_SPS[$sp]
+       ;;
+       /* Disable interrupt to check task flags atomically */
+       disable_interrupt $r60
+       ;;
+       /* Check PL bit of sps, if set, then it means we are returning
+        * to a lower privilege level (ie to user), if so, we need to
+        * check work pending. If coming from kernel, directly go to
+        * register restoration */
+       cb.even $r6? _restore_regs
+       ld $r1 = TASK_TI_FLAGS[$r11]
+       ;;
+       /* Do we have work pending ? */
+       andd $r5 = $r1, _TIF_WORK_MASK
+       ;;
+       /**
+        * If we do not have work pending (ie $r5 == 0) then we can
+        * directly jump to _restore_regs without calling do_work_pending
+        */
+       cb.deqz $r5? _restore_regs
+       ;;
+       /*
+        * Work pending can potentially call a signal handler and then return
+        * via rt_sigreturn. Return path will be different (restore all regs)
+        * and hence all registers are needed to be saved.
+        */
+       save_callee
+       ;;
+       call_do_work_pending
+       ;;
+#ifdef CONFIG_TRACE_IRQFLAGS
+       /* reload sps value from saved registers */
+       ld $r6 = PT_SPS[$sp]
+       ;;
+#endif
+_restore_regs:
+#ifdef CONFIG_TRACE_IRQFLAGS
+       /* Check if IRQs are going to be reenable in next context */
+       andd $r6 = $r6, KVX_SFR_SPS_IE_MASK
+       ;;
+       cb.deqz $r6? 1f
+       ;;
+       call trace_hardirqs_on
+       ;;
+1:
+#endif
+       disable_mmu $r0, PS_HWLOOP_DISABLE
+       ;;
+       lo $r0r1r2r3 = __PA(PT_CS_SPC_SPS_ES)[$sp]
+       /* Compute physical address of stack to avoid using 64bits immediate */
+       addd $r11 = $sp, VA_TO_PA_OFFSET
+       ;;
+       lo $r4r5r6r7 = PT_LC_LE_LS_RA[$r11]
+       ;;
+       lo $r60r61r62r63 = PT_Q60[$r11]
+       ;;
+       lo $r56r57r58r59 = PT_Q56[$r11]
+       ;;
+       lo $r52r53r54r55 = PT_Q52[$r11]
+       get $r14 = $sps
+       ;;
+       lo $r48r49r50r51 = PT_Q48[$r11]
+       /* Generate a mask of ones at each bit where the current $sps
+        * differs from the $sps to be restored
+        */
+       xord $r14 = $r2, $r14
+       /* prepare wfxl clear mask on LSBs */
+       notd $r15 = $r2
+       /* prepare wfxl set mask on MSBs */
+       slld $r13 = $r2, 32
+       ;;
+       lo $r44r45r46r47 = PT_Q44[$r11]
+       /* Replicate mask of ones on the 32 MSBs */
+       sbmm8 $r14 = $r14, REPLICATE_32_MASK
+       /* Combine the set and clear mask for wfxl */
+       insf  $r13 = $r15, 31, 0
+       ;;
+       lo $r40r41r42r43 = PT_Q40[$r11]
+       set $lc = $r4
+       /* Mask to drop identical bits in order to avoid useless
+        * privilege traps
+        */
+       andd $r13 = $r13, $r14
+       ;;
+       lq $r16r17 = PT_R16R17[$r11]
+       set $le = $r5
+       ;;
+       lo $r32r33r34r35 = PT_Q32[$r11]
+       set $ls = $r6
+       ;;
+       lo $r36r37r38r39 = PT_Q36[$r11]
+       copyd $r14 = $r11
+       set $ra = $r7
+       ;;
+       lo $r8r9r10r11 = PT_Q8[$r14]
+       set $cs = $r0
+       /* MME was disabled by disable_mmu, reenable it before leaving */
+       ord $r13 = $r13, SFR_SET_VAL_WFXL(SPS, MME, 1)
+       ;;
+       lo $r4r5r6r7 = PT_Q4[$r14]
+       set $spc = $r1
+       ;;
+       lo $r0r1r2r3 = PT_Q0[$r14]
+       /* Store $sps wfxl value in scratch system register */
+       set TMP_SCRATCH_SR = $r13
+       ;;
+       lo $r12r13r14r15 = PT_Q12[$r14]
+       rswap $r0 = TMP_SCRATCH_SR
+       ;;
+       wfxl $sps = $r0
+       ;;
+       /* Finally, restore $r0 value */
+       rswap $r0 = TMP_SCRATCH_SR
+       ;;
+       rfe
+       ;;
+ENDPROC(return_from_exception)
+
+/***********************************************************************
+*                      Debug handling
+***********************************************************************/
+EXCEPTION_ENTRY(kvx_debug_handler):
+       exception_entry_disable_mmu
+       ;;
+       save_quad_regs $r0r1r2r3 $r4
+       ;;
+       get $r2 = $sr
+       ;;
+       switch_stack $r1 $r2 debug_save_regs
+       ;;
+debug_save_regs:
+       save_regs_for_exception $r1 $r2 $r0r1r2r3 $r1
+       ;;
+       get $r0 = $ea
+       /* tail-call for return_from_exception */
+       make $r3 = return_from_exception
+       ;;
+       set $ra = $r3
+       ;;
+       goto debug_handler
+       ;;
+ENDPROC(kvx_debug_handler)
+
+/***********************************************************************
+*                      Traps handling
+***********************************************************************/
+/* These labels will be used for instruction patching */
+.global kvx_perf_tlb_refill, kvx_std_tlb_refill
+EXCEPTION_ENTRY(kvx_trap_handler):
+       /* Enable DAUS for physical data access */
+       exception_entry_disable_mmu
+       ;;
+       /* Save r3 in a temporary system register to check if the trap is a
+       * nomapping or not */
+       set TMP_SCRATCH_SR = $r3
+       ;;
+       get $r3 = $es
+       ;;
+       /* Hardware trap cause  */
+       extfz $r3 = $r3, KVX_SFR_END(ES_HTC), KVX_SFR_START(ES_HTC)
+       ;;
+       /* Is this a nomapping trap ? */
+       compd.eq $r3 = $r3, KVX_TRAP_NOMAPPING
+       ;;
+       /* if nomapping trap, try fast_refill */
+       cb.even $r3? trap_slow_path
+       ;;
+       /*
+        * Fast TLB refill routine
+        *
+        * On kvx, we do not have hardware page walking, hence, TLB refill is
+        * done using the core on no-mapping traps.
+        * This routine must be as fast as possible to avoid wasting CPU time.
+        * For that purpose, it is called directly from trap_handle after saving
+        * only 8 registers ($r0 -> $r7) in a dedicated buffer.
+        * To avoid taking nomapping while accessing page tables inside this
+        * refill handler we switch to physical accesses using DAUS.
+        * Once the switch is done, we save up to 8 registers to compute
+        * the refill data.
+        * This allows to avoid computing a complete task switching in order
+        * to greatly reduce the refill time.
+        *
+        * We refill the JTLB which contains 128 sets with 4 way each.
+        * Currently, the way selection is done using a round robin algorithm.
+        *
+        * The refill is using the basic flow:
+        * 1 -  Enable physical access using DAUS.
+        * 2 -  Save necessary registers
+        * 3 -  Walk the page table to find the TLB entry to add (virtual to
+        *      physical)
+        * 4 -  Compute the TLB entry to be written (convert PTE to TLB entry)
+        * 5 -  Compute the target set (0 -> 127) for the new TLB entry
+        *      This is done by extracting the 6 lsb of page number
+        * 6 -  Get the current way to be used which is selected using a
+               simple round robin
+        * 7 -  Mark PTE entry as _PAGE_ACCESSED (and optionally PAGE_DIRTY)
+        * 8 -  Commit the new tlb entry
+        * 9 -  Restore the virtual memory by disabling DAUS.
+        *
+        */
+       /* Get current task */
+       rswap $r63 = $sr
+       ;;
+#ifdef CONFIG_KVX_MMU_STATS
+       get $r3 = $pm0
+       ;;
+       sd __PA(TASK_THREAD_ENTRY_TS)[$r63] = $r3
+       ;;
+#endif
+       /* Restore $r3 from temporary system scratch register */
+       get $r3 = TMP_SCRATCH_SR
+       /* Save registers to save $tel, $teh and $mmc */
+       so __PA(TASK_THREAD_SAVE_AREA_QUAD(2))[$r63] = $r8r9r10r11
+       ;;
+       get $r8 = $tel
+       /* Save register for refill handler */
+       so __PA(TASK_THREAD_SAVE_AREA_QUAD(1))[$r63] = $r4r5r6r7
+       ;;
+       /* Get exception address */
+       get $r0 = $ea
+       /* Save more registers to be comfy */
+       so __PA(TASK_THREAD_SAVE_AREA_QUAD(0))[$r63] = $r0r1r2r3
+       ;;
+       get $r9 = $teh
+       ;;
+       get $r10 = $mmc
+       ;;
+       /* Restore $r63 value */
+       rswap $r63 = $sr
+       /* Check kernel address range */
+       addd $r4 = $r0, -KERNEL_DIRECT_MEMORY_MAP_BASE
+       /* Load task active mm for pgd loading in macro_tlb_refill */
+       ld $r1 = __PA(TASK_ACTIVE_MM)[$r63]
+       ;;
+kvx_perf_tlb_refill:
+       /* Check if the address is in the kernel direct memory mapping */
+       compd.ltu $r3 = $r4, KERNEL_DIRECT_MEMORY_MAP_SIZE
+       /* Clear low bits of virtual address to align on page size */
+       andd $r5 = $r0, ~(REFILL_PERF_PAGE_SIZE - 1)
+       /* Create corresponding physical address */
+       addd $r2 = $r4, PHYS_OFFSET
+       ;;
+       /* If address is not a kernel one, take the standard path */
+       cb.deqz $r3? kvx_std_tlb_refill
+       /* Prepare $teh value with virtual address and kernel value */
+       ord $r7 = $r5, REFILL_PERF_TEH_VAL
+       ;;
+       /* Get $pm0 value as a pseudo random value for LTLB way to use */
+       get $r4 = $pm0
+       /* Clear low bits of physical address to align on page size */
+       andd $r2 = $r2, ~(REFILL_PERF_PAGE_SIZE - 1)
+       /* Prepare value for $mmc wfxl to select LTLB and correct way */
+       make $r5 = MMC_SEL_LTLB_CLEAR_WAY
+       ;;
+       /* Keep low bits of timer value */
+       andw $r4 = $r4, (REFILL_PERF_ENTRIES - 1)
+       /* Get current task pointer for register restoration */
+       get $r11 = $sr
+       ;;
+       /* Add LTLB base way number for kernel refill way */
+       addw $r4 = $r4, LTLB_KERNEL_RESERVED
+       /* Prepare $tel value with physical address and kernel value */
+       ord $r6 = $r2, REFILL_PERF_TEL_VAL
+       set $teh = $r7
+       ;;
+       /* insert way in $mmc wfxl value */
+       insf $r5 = $r4, KVX_SFR_END(MMC_SW) + 32, KVX_SFR_START(MMC_SW) + 32
+       set $tel = $r6
+       ;;
+       wfxl $mmc = $r5
+       ;;
+       goto do_tlb_write
+       ;;
+kvx_std_tlb_refill:
+       /* extract PGD offset */
+       extfz $r3 = $r0, (ASM_PGDIR_SHIFT + ASM_PGDIR_BITS - 1), ASM_PGDIR_SHIFT
+       /* is mm NULL ? if so, use init_mm */
+       cmoved.deqz $r1? $r1 = init_mm
+       ;;
+       get $r7 = $pcr
+       /* Load pgd base address into $r1 */
+       ld $r1 = __PA(MM_PGD)[$r1]
+       ;;
+       /* Add offset for physical address */
+       addd $r1 = $r1, VA_TO_PA_OFFSET
+       /* Extract processor ID to compute cpu_offset*/
+       extfz $r7 = $r7, KVX_SFR_END(PCR_PID), KVX_SFR_START(PCR_PID)
+       ;;
+       /* Load PGD entry offset */
+       ld.xs $r1 = $r3[$r1]
+       /* Load per_cpu_offset */
+#if defined(CONFIG_SMP)
+       make $r5 = __PA(__per_cpu_offset)
+#endif
+       ;;
+       /* extract PMD offset*/
+       extfz $r3 = $r0, (ASM_PMD_SHIFT + ASM_PMD_BITS - 1), ASM_PMD_SHIFT
+       /* If pgd entry is null -> out */
+       cb.deqz $r1? refill_err_out
+#if defined(CONFIG_SMP)
+       /* Load cpu offset */
+       ld.xs $r7 = $r7[$r5]
+#else
+       /* Force cpu offset to 0 */
+       make $r7 = 0
+#endif
+       ;;
+       /* Load PMD entry offset and keep pointer to the entry for huge page */
+       addx8d $r2 = $r3, $r1
+       ld.xs $r1 = $r3[$r1]
+       ;;
+       /* Check if it is a huge page (2Mb or 512Mb in PMD table)*/
+       andd $r6 = $r1, _PAGE_HUGE
+       /* If pmd entry is null -> out */
+       cb.deqz $r1? refill_err_out
+       /* extract PTE offset */
+       extfz $r3 = $r0, (PAGE_SHIFT + 8), PAGE_SHIFT
+       ;;
+       /*
+        * If the page is a huge one we already have set the PTE and the
+        * pointer to the PTE.
+        */
+       cb.dnez $r6? is_huge_page
+       ;;
+       /* Load PTE entry */
+       ld.xs $r1 = $r3[$r1]
+       addx8d $r2 = $r3, $r1
+       ;;
+       /* Check if it is a huge page (64Kb in PTE table) */
+       andd $r6 = $r1, _PAGE_HUGE
+       ;;
+       /* Check if PTE entry is for a huge page */
+       cb.dnez $r6? is_huge_page
+       ;;
+       /* 4K: Extract set value */
+       extfz $r0 = $r1, (PAGE_SHIFT + KVX_SFR_MMC_SS_WIDTH - 1), PAGE_SHIFT
+       /* 4K: Extract virt page from ea */
+       andd $r4 = $r0, PAGE_MASK
+       ;;
+/*
+ * This path expects the following:
+ * - $r0 = set index
+ * - $r1 = pte entry
+ * - $r2 = pte entry address
+ * - $r4 = virtual page address
+ */
+pte_prepared:
+       /* Compute per_cpu_offset + current way of set address */
+       addd $r5 = $r0, $r7
+       /* Get exception cause for access type handling (page dirtying) */
+       get $r7 = $es
+       /* Clear way and select JTLB */
+       make $r6 = MMC_SEL_JTLB_CLEAR_WAY
+       ;;
+       /* Load current way to use for current set */
+       lbz $r0 = __PA(jtlb_current_set_way)[$r5]
+       /* Check if the access was a "write" access */
+       andd $r7 = $r7, (KVX_TRAP_RWX_WRITE << KVX_SFR_ES_RWX_SHIFT)
+       ;;
+       /* If bit PRESENT of pte entry is 0, then entry is not present */
+       cb.even $r1? refill_err_out
+       /*
+        * Set the JTLB way in $mmc value, add 32 bits to be in the set part.
+        * Since we are refilling JTLB, we must make sure we insert only
+        * relevant bits (ie 2 bits for ways) to avoid using nonexistent ways.
+        */
+       insf $r6 = $r0, KVX_SFR_START(MMC_SW) + 32 + (MMU_JTLB_WAYS_SHIFT - 1),\
+                                               KVX_SFR_START(MMC_SW) + 32
+       /* Extract page global bit */
+       extfz $r3 = $r1, _PAGE_GLOBAL_SHIFT, _PAGE_GLOBAL_SHIFT
+       /* Increment way value, note that we do not care about overflow since
+        * we only use the two lower byte */
+       addd $r0 = $r0, 1
+       ;;
+       /* Prepare MMC */
+       wfxl $mmc, $r6
+       ;;
+       /* Insert global bit (if any) to its position into $teh value */
+       insf $r4 = $r3, KVX_SFR_TEH_G_SHIFT, KVX_SFR_TEH_G_SHIFT
+       /* If "write" access ($r7 != 0), then set it as dirty */
+       cmoved.dnez $r7? $r7 = _PAGE_DIRTY
+       /* Clear bits not related to FN in the pte entry for TEL writing */
+       andd $r6 = $r1, KVX_PFN_MASK
+       /* Store new way */
+       sb __PA(jtlb_current_set_way)[$r5] = $r0
+       ;;
+       /* Extract access perms from pte entry (discard PAGE_READ bit +1) */
+       extfz $r3 = $r1, KVX_ACCESS_PERM_STOP_BIT, KVX_ACCESS_PERM_START_BIT + 1
+       /* Move FN bits to their place */
+       srld $r6 = $r6, KVX_PFN_SHIFT - PAGE_SHIFT
+       /* Extract the page size + cache policy */
+       andd $r0 = $r1, (KVX_PAGE_SZ_MASK | KVX_PAGE_CP_MASK)
+       /* Prepare SBMM value */
+       make $r5 = KVX_SBMM_BYTE_SEL
+       ;;
+       /* Add page size + cache policy to $tel value */
+       ord $r6 = $r6, $r0
+       /* Get $mmc to get current ASN */
+       get $r0 = $mmc
+       /* Add _PAGE_ACCESSED bit to PTE entry for writeback */
+       ord $r7 = $r7, _PAGE_ACCESSED
+       ;;
+       /* OR PTE value with accessed/dirty flags */
+       ord $r1 = $r1, $r7
+       /* Generate the byte selection for sbmm */
+       slld $r5 = $r5, $r3
+       /* Compute the mask to extract set and mask exception address */
+       make $r7 = KVX_PAGE_PA_MATRIX
+       ;;
+       ord $r0 = $r6, TEL_DEFAULT_VALUE
+       /* Add ASN from mmc into future $teh value */
+       insf $r4 = $r0, KVX_SFR_END(MMC_ASN), KVX_SFR_START(MMC_ASN)
+       /* Get the page permission value */
+       sbmm8 $r6 = $r7, $r5
+       /* Check PAGE_READ bit in PTE entry */
+       andd $r3 = $r1, _PAGE_READ
+       ;;
+       /* If PAGE_READ bit is not set, set policy as NA_NA */
+       cmoved.deqz $r3? $r6 = TLB_PA_NA_NA
+       ;;
+       /* Shift PA to correct position */
+       slld $r6 = $r6, KVX_SFR_TEL_PA_SHIFT
+       set $teh = $r4
+       ;;
+       /* Store updated pte entry */
+       sd 0[$r2] = $r1
+       /* Prepare tel */
+       ord $r6 = $r0, $r6
+       /* Get current task pointer for register restoration */
+       get $r11 = $sr
+       ;;
+       set $tel = $r6
+       ;;
+do_tlb_write:
+       tlbwrite
+       ;;
+#ifdef CONFIG_KVX_DEBUG_TLB_WRITE
+       goto mmc_error_check
+       ;;
+mmc_error_check_ok:
+#endif
+#ifdef CONFIG_KVX_DEBUG_ASN
+       goto asn_check
+       ;;
+asn_check_ok:
+#endif
+       set $tel = $r8
+       /* Restore registers */
+       lo $r4r5r6r7 = __PA(TASK_THREAD_SAVE_AREA_QUAD(1))[$r11]
+       ;;
+       set $teh = $r9
+       lo $r0r1r2r3 = __PA(TASK_THREAD_SAVE_AREA_QUAD(0))[$r11]
+       ;;
+       set $mmc = $r10
+       ;;
+       lo $r8r9r10r11 = __PA(TASK_THREAD_SAVE_AREA_QUAD(2))[$r11]
+       ;;
+#ifdef CONFIG_KVX_MMU_STATS
+       /*
+        * Fence to simulate a direct data dependency after returning from trap
+        * nomapping handling. This is the worst case that can happen and the
+        * processor will be stalled waiting for previous loads to complete.
+        */
+       fence
+       ;;
+       get $r4 = $pm0
+       ;;
+       get $r0 = $sr
+       ;;
+       /* Get cycles measured on trap entry */
+       ld $r1 = __PA(TASK_THREAD_ENTRY_TS)[$r0]
+       ;;
+       /* Compute refill time */
+       sbfd $r0 = $r1, $r4
+       ;;
+#ifdef CONFIG_SMP
+       get $r1 = $pcr
+       ;;
+       /* Extract processor ID to compute cpu_offset */
+       extfz $r1 = $r1, KVX_SFR_END(PCR_PID), KVX_SFR_START(PCR_PID)
+       make $r2 = __PA(__per_cpu_offset)
+       ;;
+       /* Load cpu offset */
+       ld.xs $r1 = $r1[$r2]
+       ;;
+       addd $r1 = $r1, __PA(mmu_stats)
+       ;;
+#else
+       make $r1 = __PA(mmu_stats)
+       ;;
+#endif
+       /* Load time between refill + last refill cycle */
+       lq $r2r3 = MMU_STATS_CYCLES_BETWEEN_REFILL_OFF[$r1]
+       ;;
+       /* Set last update time to current if 0 */
+       cmoved.deqz $r3? $r3 = $r4
+       /* remove current refill time to current cycle */
+       sbfd $r4 = $r0, $r4
+       ;;
+       /* Compute time between last refill and current refill */
+       sbfd $r5 = $r3, $r4
+       /* Update last cycle time */
+       copyd $r3 = $r4
+       ;;
+       /* Increment total time between refill */
+       addd $r2 = $r2, $r5
+       ;;
+       sq MMU_STATS_CYCLES_BETWEEN_REFILL_OFF[$r1] = $r2r3
+       /* Get exception address */
+       get $r4 = $ea
+       ;;
+       /* $r2 holds refill type (user/kernel/kernel_direct) */
+       make $r2 = MMU_STATS_REFILL_KERNEL_OFF
+       /* Check if address is a kernel direct mapping one */
+       compd.ltu $r3 = $r4, (KERNEL_DIRECT_MEMORY_MAP_BASE + \
+                             KERNEL_DIRECT_MEMORY_MAP_SIZE)
+       ;;
+       cmoved.dnez $r3? $r2 = MMU_STATS_REFILL_KERNEL_DIRECT_OFF
+       /* Check if address is a user (ie below kernel) */
+       compd.ltu $r3 = $r4, KERNEL_DIRECT_MEMORY_MAP_BASE
+       ;;
+       cmoved.dnez $r3? $r2 = MMU_STATS_REFILL_USER_OFF
+       ;;
+       /* Compute refill struct addr into one reg */
+       addd $r1 = $r2, $r1
+       /* Load refill_struct values */
+       lo $r4r5r6r7 = $r2[$r1]
+       ;;
+       /* Increment count */
+       addd $r4 = $r4, 1
+       /* Increment total cycles count */
+       addd $r5 = $r5, $r0
+       ;;
+       /* Set min to ~0 if 0 */
+       cmoved.deqz $r6? $r6 = ~0
+       ;;
+       /* Compare min and max */
+       compd.ltu $r2 = $r0, $r6
+       compd.gtu $r3 = $r0, $r7
+       ;;
+       /* Update min and max*/
+       cmoved.dnez $r2? $r6 = $r0
+       cmoved.dnez $r3? $r7 = $r0
+       ;;
+       /* store back all values */
+       so 0[$r1] = $r4r5r6r7
+       ;;
+       get $r0 = $sr
+       ;;
+       /* Restore clobberred registers */
+       lo $r4r5r6r7 = __PA(TASK_THREAD_SAVE_AREA_QUAD(1))[$r0]
+       ;;
+       lo $r0r1r2r3 = __PA(TASK_THREAD_SAVE_AREA_QUAD(0))[$r0]
+       ;;
+#endif
+       /* Save $r4 for reenabling mmu and data cache in sps */
+       set TMP_SCRATCH_SR = $r4
+       /* Enable MME in $sps */
+       make $r4 = MME_WFXL(1)
+       ;;
+       /* Reenable $mme in $sps */
+       wfxl $sps = $r4
+       ;;
+       get $r4 = TMP_SCRATCH_SR
+       ;;
+       rfe
+       ;;
+
+is_huge_page:
+       /*
+        * When entering this path:
+        * - $r0 = $ea
+        * - $r1 = pte entry
+        * - $r7 = cpu offset for tlb_current_set_way
+        *
+        * From now on, we have the pte value in $r1 so we can extract the page
+        * size. This value is stored as it is expected by the MMU (ie between
+        * 0 and 3).
+        * Note that page size value is located at the same place as in $tel
+        * and this is checked at build time so we can use TEL_PS defines.
+        * In this codepath, we will extract the set and mask exception address
+        * and align virt and phys address with what the hardware expect.
+        * Indeed, MMU expect lsb of the virtual and physycal address to be 0
+        * according to page size.
+        * This means that for 4K pages, the 12 lsb must be 0, for 64K
+        * pages, the 16 lsb must be 0 and so on.
+        */
+       extfz $r5 = $r1, KVX_SFR_END(TEL_PS), KVX_SFR_START(TEL_PS)
+       /* Compute the mask to extract set and mask exception address */
+       make $r4 = KVX_PS_SHIFT_MATRIX
+       make $r6 = KVX_SBMM_BYTE_SEL
+       ;;
+       /* Generate the byte selection for sbmm */
+       slld $r6 = $r6, $r5
+       ;;
+       /* Get the shift value */
+       sbmm8 $r5 = $r4, $r6
+       make $r4 = 0xFFFFFFFFFFFFFFFF
+       ;;
+       /* extract TLB set from ea (6 lsb of virtual page) */
+       srld $r5 = $r0, $r5
+       /* Generate ea masking according to page shift */
+       slld $r4 = $r4, $r5
+       ;;
+       /* Mask to get the set value */
+       andd $r0 = $r5, MMU_SET_MASK
+       /* Extract virt page from ea */
+       andd $r4 = $r0, $r4
+       ;;
+       /* Returned to fast path */
+       goto pte_prepared
+       ;;
+
+#ifdef CONFIG_KVX_DEBUG_TLB_WRITE
+mmc_error_check:
+       get $r1 = $mmc
+       ;;
+       andd $r1 = $r1, KVX_SFR_MMC_E_MASK
+       ;;
+       cb.deqz $r1? mmc_error_check_ok
+       ;;
+       make $r0 = mmc_error_panic_str_label
+       goto asm_panic
+       ;;
+#endif
+#ifdef CONFIG_KVX_DEBUG_ASN
+/*
+ * When entering this path $r11 = $sr.
+ * WARNING: Do not clobber it here if you don't want to mess up with registers
+ * restoration above.
+ */
+asn_check:
+       get $r1 = $ea
+       ;;
+       /* Check if kernel address, if so, there is no ASN */
+       compd.geu $r2 = $r1, PAGE_OFFSET
+       ;;
+       cb.dnez $r2? asn_check_ok
+       ;;
+       get $r2 = $pcr
+       /* Load active mm addr */
+       ld $r3 = __PA(TASK_ACTIVE_MM)[$r11]
+       ;;
+       get $r5 = $mmc
+       /* Extract processor ID to compute cpu_offset*/
+       extfz $r2 = $r2, KVX_SFR_END(PCR_PID), KVX_SFR_START(PCR_PID)
+       addd $r3 = $r3, MM_CTXT_ASN
+       ;;
+       extfz $r4 = $r5, KVX_SFR_END(MMC_ASN), KVX_SFR_START(MMC_ASN)
+       addd $r3 = $r3, VA_TO_PA_OFFSET
+       ;;
+       /* Load current asn from active_mm */
+       ld.xs $r3 = $r2[$r3]
+       ;;
+       /* Error if ASN is not set */
+       cb.deqz $r3? asn_check_err
+       /* Mask $r3 asn cycle part */
+       andd $r5 = $r3, ((1 << KVX_SFR_MMC_ASN_WIDTH) - 1)
+       ;;
+       /* Compare asn in $mmc and asn in current task mm */
+       compd.eq $r3 = $r5, $r4
+       ;;
+       cb.dnez $r3? asn_check_ok
+       ;;
+asn_check_err:
+       /* We are fried, die peacefully */
+       make $r0 = asn_error_panic_str_label
+       goto asm_panic
+       ;;
+#endif
+
+#if defined(CONFIG_KVX_DEBUG_ASN) || defined(CONFIG_KVX_DEBUG_TLB_WRITE)
+
+/**
+ * This routine calls panic from assembly after setting appropriate things
+ * $r0 = panic string
+ */
+asm_panic:
+       /*
+        * Reenable hardware loop and traps (for nomapping) since some functions
+        * might need it. Moreover, disable DAUS to reenable MMU accesses.
+        */
+       make $r32 = PS_HWLOOP_EN_ET_CLEAR_DAUS_DIS
+       make $r33 = 0
+       get $r34 = $sr
+       ;;
+       /* Clear hw loop exit to disable current loop */
+       set $le = $r33
+       ;;
+       wfxl $ps = $r32
+       ;;
+       /* Restore kernel stack */
+       ld $r12 = TASK_THREAD_KERNEL_SP[$r34]
+       ;;
+       call panic
+       ;;
+#endif
+
+/* Error path for TLB refill */
+refill_err_out:
+       get $r2 = $sr
+       ;;
+       /* Restore clobbered registers */
+       lo $r8r9r10r11 = __PA(TASK_THREAD_SAVE_AREA_QUAD(2))[$r2]
+       ;;
+       lo $r4r5r6r7 = __PA(TASK_THREAD_SAVE_AREA_QUAD(1))[$r2]
+       goto trap_switch_stack
+       ;;
+
+/* This path is entered only when the trap is NOT a NOMAPPING */
+trap_slow_path:
+       /* Restore $r3 from temporary scratch system register */
+       get $r3 = TMP_SCRATCH_SR
+       ;;
+       save_quad_regs $r0r1r2r3 $r4
+       ;;
+       get $r2 = $sr
+       ;;
+trap_switch_stack:
+       switch_stack $r1 $r2 trap_save_regs
+       ;;
+trap_save_regs:
+       save_regs_for_exception $r1 $r2 $r0r1r2r3 $r2
+       ;;
+       get $r1 = $ea
+       /* tail-call for return_from_exception */
+       make $r3 = return_from_exception
+       ;;
+       set $ra = $r3
+       ;;
+       /* Handler call */
+       get $r0 = $es
+       ;;
+       goto trap_handler
+       ;;
+ENDPROC(kvx_trap_handler)
+
+/***********************************************************************
+*                      Interrupts handling
+***********************************************************************/
+EXCEPTION_ENTRY(kvx_interrupt_handler):
+       exception_entry_disable_mmu
+       ;;
+       save_quad_regs $r0r1r2r3 $r4
+       ;;
+       get $r0 = $sr
+       ;;
+       switch_stack $r1 $r0 it_save_regs
+       ;;
+#ifdef CONFIG_SECURE_DAME_HANDLING
+       /**
+        * In order to securely Handle Data Asynchronous Memory Error,
+        * we need to have a correct entry point. This means we do not
+        * want to handle a user induced DAME interrupt when entering
+        * kernel.
+        * In order to do that, we need to do a barrier, which will
+        * reflect the DAME status in $ilr (if any).
+        */
+       barrier
+       ;;
+#endif
+it_save_regs:
+       save_regs_for_exception $r1 $r0 $r0r1r2r3 $r1
+       ;;
+       get $r0 = $ilr
+       ;;
+       get $r2 = $ile
+       ;;
+       /**
+        * When an interrupt happens, the processor automatically clears the
+        * corresponding bit in $ilr. However, as we are using $ilr to get the
+        * list of irqs we want to handle, we need to add the automatically
+        * cleared interrupt bit. This is done by getting the interrupt number
+        * from $es.
+        */
+       get $r3 = $es
+       make $r4 = 1
+       ;;
+       /* Extract interrupt number from $es */
+       extfz $r3 = $r3, KVX_SFR_END(ES_ITN), KVX_SFR_START(ES_ITN)
+       /**
+        * Mask requests with enabled line since ILR will also contain disabled
+        * interrupt lines (ie not enabled in $ile) and we want to respect the
+        * current state of interrupt lines.
+        */
+       andd $r0 = $r0, $r2
+       ;;
+       /* Clear $ilr with bits we are going to handle */
+       wfxl $ilr = $r0
+       slld $r4 = $r4, $r3
+       ;;
+       /* OR the irq mask with the current pending irq */
+       ord $r0 = $r0, $r4
+       call do_IRQ
+       ;;
+       /* From now on, lower the interrupt level (IL) to allow IT nesting.
+        * If returning to user, we will call schedule which will reenable
+        * interrupts by itself when ready.
+        * If returning to kernel and with CONFIG_PREEMPT, we will call
+        * preempt_schedule_irq which will do the same.
+        */
+       make $r0 = PS_IL_CLEAR
+       ;;
+       wfxl $ps = $r0
+       ;;
+       goto return_from_exception
+       ;;
+ENDPROC(kvx_interrupt_handler)
+
+/***********************************************************************
+*                      Syscall handling
+***********************************************************************/
+EXCEPTION_ENTRY(kvx_syscall_handler):
+       /**
+        * Syscalls are seen as standard func call from ABI POV
+        * We may use all caller saved register whithout causing havoc
+        * in the userspace. We need to save callee registers because they
+        * will be restored when returning from fork.
+        * Note that r0 -> r11 MUST not be used since they are
+        * containing syscall parameters !
+        * During this function, $r38 is the syscall handler address. Hence,
+        * this register must not be clobberred during function calls (tracing
+        * for instance.
+        */
+       disable_mmu $r43
+       ;;
+       get $r43 = $es
+       copyd $r52 = $sp
+       copyd $r53 = $tp
+       ;;
+       get $r36 = $sr
+       copyd $r54 = $fp
+       copyd $r55 = $r15
+       ;;
+       /* Extract syscall number */
+       extfz $r32 = $r43, KVX_SFR_END(ES_SN), KVX_SFR_START(ES_SN)
+       /* Get regs to save on stack */
+       get $r63 = $ra
+       addd $r36 = $r36, VA_TO_PA_OFFSET
+       ;;
+       ld $r39 = TASK_TI_FLAGS[$r36]
+       get $r41 = $spc
+       make $r42 = __PA(sys_call_table)
+       ;;
+       /* Check for out-of-bound syscall number */
+       sbfd $r50 = $r32, __NR_syscalls
+       /* Compute syscall func addr (ie sys_call_table[$r32])*/
+       ld.xs $r38 = $r32[$r42]
+       get $r42 = $sps
+       ;;
+       /* True if trace syscall enable */
+       andd $r37 = $r39, _TIF_SYSCALL_WORK
+       /* Restore kernel stack pointer */
+       ld $sp = TASK_THREAD_KERNEL_SP[$r36]
+       /* If the syscall number is invalid, set invalid handler */
+       cmoved.dlez $r50? $r38 = sys_ni_syscall
+       ;;
+       /* Prepare space on stack */
+       addd $sp = $sp, -PT_SIZE_ON_STACK
+       get $r40 = $cs
+       /* Save regs r0 -> r3 in pt_regs for restart & trace if needed */
+       so __PA(PT_Q0 - PT_SIZE_ON_STACK)[$sp] = $r0r1r2r3
+       ;;
+       /* Store user stack pointer, frame pointer, thread pointer and r15 */
+       so __PA(PT_Q12)[$sp] = $r52r53r54r55
+       addd $r36 = $sp, VA_TO_PA_OFFSET
+       get $r60 = $lc
+       ;;
+#ifdef CONFIG_SECURE_DAME_HANDLING
+       get $r44 = $ilr
+       make $r45 = SFR_CLEAR_WFXL(ILR, IT16);
+       ;;
+       /* Extract $ilr.dame bit */
+       extfz $r44 = $r44, KVX_SFR_END(ILR_IT16), KVX_SFR_START(ILR_IT16)
+       /* Save $ilr value */
+       sd PT_ILR[$r36] = $r44
+       /* Clear $ilr.dame */
+       wfxl $ilr = $r45
+       ;;
+#endif
+       so PT_CS_SPC_SPS_ES[$r36] = $r40r41r42r43
+       get $r61 = $le
+       make $r43 = 0x0
+       ;;
+       /* Reenable hardware loops, IT, exceptions and disable DAUS */
+       make $r44 = PS_HLE_EN_IT_EN_ET_CLEAR_DAUS_DIS
+       get $r62 = $ls
+       /* Save regs r4 -> r7 in pt_regs for restart & trace if needed */
+       so PT_Q4[$r36] = $r4r5r6r7
+       ;;
+       /* Clear $le on entry */
+       set $le = $r43
+       /* Save hw loop stuff */
+       so PT_LC_LE_LS_RA[$r36] = $r60r61r62r63
+       /* Clear frame pointer for kernel */
+       make $fp = 0
+       ;;
+       /* Enable hwloop and interrupts and MMU
+        * Note that we have to reenable interrupts after saving context
+        * to avoid losing registers content */
+       wfxl $ps, $r44
+       ;;
+       /* Do we have to trace the syscall ? */
+       cb.dnez $r37? trace_syscall_enter
+       /* Stroe original r0 value */
+       sd PT_ORIG_R0[$sp] = $r0
+       ;;
+do_syscall:
+       /* Call the syscall handler */
+       icall $r38
+       ;;
+       /*
+        * rt_sigreturn syscall will not take this exit path (see
+        * sys_rt_sigreturn for more information).
+        */
+       /* Reload task flags since the syscall might have generated a signal*/
+       get $r36 = $sr
+       ;;
+#ifdef CONFIG_SECURE_DAME_HANDLING
+       /* Barrier to force DAME interrupt to be generated if any pending */
+       barrier
+       ;;
+#endif
+       /* Disable interrupt to check task flags atomically */
+       disable_interrupt $r60
+       ;;
+       ld $r38 = TASK_TI_FLAGS[$r36]
+       ;;
+       andd $r37 = $r38, _TIF_SYSCALL_WORK
+       ;;
+       /* Save r0 which was returned from do_scall previously and will be
+        * clobberred by do_work_pending (and potentially do_syscall_trace_exit
+        * if tracing is enabled)
+        * If do_signal is called and that syscall is restarted,
+        * it will be modified by handle_restart to restore original
+        * r0 value
+        */
+       sd PT_Q0[$sp] = $r0
+       /* used to store if trace system */
+       cb.dnez $r37? trace_syscall_exit
+       ;;
+check_work_pending:
+       /* Do we have work pending ? */
+       andd $r1 = $r38, _TIF_WORK_MASK
+       ;;
+       /* If no work pending, directly continue to ret_to_user */
+       cb.dnez $r1? call_work_pending
+       ;;
+ret_to_user:
+       disable_mmu $r60, PS_HWLOOP_DISABLE
+       ;;
+       /* Compute $sp virtual address to avoid using 64 bits offset */
+       addd $r15 = $sp, VA_TO_PA_OFFSET
+       ;;
+       /* Restore registers */
+       lo $r60r61r62r63 = PT_LC_LE_LS_RA[$r15]
+       get $r33 = $sps
+       ;;
+       lo $r40r41r42r43 = PT_CS_SPC_SPS_ES[$r15];
+       set $ra = $r63
+       ;;
+       /* Restore syscall arguments since they might be needed for
+        * syscall restart
+        */
+       lo $r0r1r2r3 = PT_Q0[$r15]
+       set $cs = $r40
+       /* Generate a mask of ones at each bit where the current $sps
+        * differs from the $sps to be restored
+        */
+       xord $r33 = $r42, $r33
+       /* prepare wfxl clear mask on LSBs */
+       notd $r34 = $r42
+       /* prepare wfxl set mask on MSBs */
+       slld $r35 = $r42, 32
+       ;;
+       set $lc = $r60
+       /* Replicate mask of ones on the 32 MSBs */
+       sbmm8 $r33 = $r33, REPLICATE_32_MASK
+       /* Combine the set and clear mask for wfxl */
+       insf  $r35 = $r34, 31, 0
+       ;;
+       lo $r4r5r6r7 = PT_Q4[$r15]
+       set $le = $r61
+       /* Mask to drop identical bits in order to avoid useless
+        * privilege traps
+        */
+       andd $r35 = $r35, $r33
+       ;;
+       /* Restore $ilr */
+       ld $r44 = PT_ILR[$r15]
+       set $ls = $r62
+       /* Reenable MMU in $sps */
+       ord $r35 = $r35, SFR_SET_VAL_WFXL(SPS, MME, 1)
+       ;;
+       /* Restore user pointer */
+       lo $r12r13r14r15 = PT_Q12[$r15]
+       /* Prepare $r44 as a set mask for $ilr wfxl */
+       slld $r44 = $r44,  32
+       ;;
+       /**
+        * wfxl on $ilr to avoid privilege traps when restoring with set
+        * Note that we do that after disabling interrupt since we explicitly
+        * want to serve DAME itnerrupt to the user (ie not in kernel mode).
+        */
+       wfxl $ilr = $r44
+       ;;
+       wfxl $sps = $r35
+       ;;
+       set $spc = $r41
+       ;;
+       /* TODO: we might have to clear some registers to avoid leaking
+        * information to user space ! callee saved regs have been
+        * restored by called function but caller saved regs might
+        * have been used without being cleared */
+       rfe
+       ;;
+
+/* Slow paths handling */
+call_work_pending:
+       /*
+        * Work pending can potentially call a signal handler and then return
+        * via rt_sigreturn. Return path will be different (restore all regs)
+        * and hence all registers are needed to be saved.
+        */
+       save_callee
+       ;;
+       call_do_work_pending
+       ;;
+       /* Since we are returning to user, interrupts will be reenabled */
+       call_trace_hardirqs on
+       ;;
+       goto ret_to_user
+       ;;
+
+trace_syscall_enter:
+       /* Save $r38 (syscall handler) which was computed above */
+       sd PT_R38[$sp] = $r38
+       ;;
+       /* do_syscall_trace_enter expect pt_regs and syscall number
+        * as argument */
+       copyd $r0 = $sp
+       copyd $r1 = $r32
+       ;;
+       call do_syscall_trace_enter
+       ;;
+       /* Restore r38 (syscall handler) which might has been clobbered by
+        * do_syscall_trace_enter */
+       ld $r38 = PT_R38[$sp]
+       ;;
+       /* if the trace system requested to abort syscall, set $r38 to
+        * non implemented syscall */
+       cmoved.dnez $r0? $r38 = sys_ni_syscall
+       ;;
+       /* Restore registers since the do_syscall_trace_enter call might
+        * have clobbered them and we need them for the actual syscall
+        * call */
+       lo $r0r1r2r3 = PT_Q0[$sp]
+       ;;
+       lo $r4r5r6r7 = PT_Q4[$sp]
+       ;;
+       goto do_syscall
+       ;;
+trace_syscall_exit:
+       copyd $r0 = $sp
+       call do_syscall_trace_exit
+       ;;
+       /* ptrace_notify might re-enable interrupts, disable them to be sure
+        * it will not mess up context restoration path */
+       disable_interrupt $r36
+       ;;
+       get $r36 = $sr
+       ;;
+       ld $r38 = TASK_TI_FLAGS[$r36]
+       goto check_work_pending
+       ;;
+ENDPROC(kvx_syscall_handler)
+
+.text
+/**
+ * sys_rt_sigreturn: Special handler for sigreturn
+ * rt_sigreturn is called after invoking a user signal handler (see
+ * user_scall_rt_sigreturn). Since this signal handler can be invoked after
+ * interrupts have been handled, this means we must restore absolutely all user
+ * registers. Normally, this has been done by setup_sigcontext which saved all
+ * user registers on the user stack.
+ * In restore sigcontext, they have been restored onto entry stack (stack to be
+ * restored). However, the standard syscall path do not restore it completely
+ * since only callee-saved registers are restored for fork and al.
+ * Here, we will restore all registers which might have been clobberred.
+ */
+ENTRY(sys_rt_sigreturn)
+       /*
+        * If syscall tracing is enable (stored in $r37 during syscall
+        * fastpath), tail-call to trace_exit. If not, tail-call to
+        * ret_from_kernel.
+        */
+       cmoved.dnez $r37? $r38 = scall_trace_exit
+       cmoved.deqz $r37? $r38 = ret_from_kernel
+       ;;
+       set $ra = $r38
+       ;;
+       goto _sys_rt_sigreturn
+       ;;
+scall_trace_exit:
+       copyd $r0 = $sp
+       call do_syscall_trace_exit
+       ;;
+       goto ret_from_kernel
+       ;;
+ENDPROC(sys_rt_sigreturn)
+
+/**
+ * __sys_clone: slow path handler for clone
+ *
+ * Save callee registers (from $r18 to $31) since they are needed for
+ * child process when restoring it.
+ * Indeed, when forking we want it to have the same registers contents
+ * as its parent. These registers will then be restored in
+ * ret_from_fork.
+ * This slow path saves them out of the fast path to avoid bloating all syscall
+ * just for one special case.
+ */
+ENTRY(__sys_clone)
+       save_callee
+       ;;
+       /*
+        * Use goto since we want sys_clone to "ret" to the previous caller.
+        * This allows to simply go back in the normal syscall fastpath
+        */
+       goto sys_clone
+       ;;
+ENDPROC(__sys_clone)
+/***********************************************************************
+*                      Context switch
+***********************************************************************/
+
+#define SAVE_TCA_REG(__reg_num, __task_reg, __zero_reg1, __zero_reg2) \
+       xso (CTX_SWITCH_TCA_REGS + (__reg_num * TCA_REG_SIZE))[__task_reg] = \
+                                                       $a##__reg_num ;\
+       movetq $a##__reg_num##.lo = __zero_reg1, __zero_reg2 ;\
+       movetq $a##__reg_num##.hi = __zero_reg1, __zero_reg2 ;\
+       ;;
+
+.macro save_tca_regs task_reg zero_reg1 zero_reg2
+       SAVE_TCA_REG(0, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(1, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(2, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(3, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(4, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(5, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(6, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(7, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(8, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(9, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(10, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(11, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(12, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(13, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(14, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(15, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(16, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(17, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(18, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(19, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(20, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(21, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(22, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(23, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(24, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(25, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(26, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(27, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(28, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(29, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(30, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(31, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(32, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(33, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(34, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(35, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(36, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(37, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(38, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(39, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(40, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(41, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(42, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(43, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(44, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(45, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(46, \task_reg, \zero_reg1, \zero_reg2)
+       SAVE_TCA_REG(47, \task_reg, \zero_reg1, \zero_reg2)
+.endm
+
+#define RESTORE_TCA_REG(__reg_num, __task_reg) \
+       xlo.u $a##__reg_num = (CTX_SWITCH_TCA_REGS + (__reg_num * 
TCA_REG_SIZE)) \
+                                                               [__task_reg] ;\
+       ;;
+
+.macro restore_tca_regs task_reg
+       RESTORE_TCA_REG(0, \task_reg)
+       RESTORE_TCA_REG(1, \task_reg)
+       RESTORE_TCA_REG(2, \task_reg)
+       RESTORE_TCA_REG(3, \task_reg)
+       RESTORE_TCA_REG(4, \task_reg)
+       RESTORE_TCA_REG(5, \task_reg)
+       RESTORE_TCA_REG(6, \task_reg)
+       RESTORE_TCA_REG(7, \task_reg)
+       RESTORE_TCA_REG(8, \task_reg)
+       RESTORE_TCA_REG(9, \task_reg)
+       RESTORE_TCA_REG(10, \task_reg)
+       RESTORE_TCA_REG(11, \task_reg)
+       RESTORE_TCA_REG(12, \task_reg)
+       RESTORE_TCA_REG(13, \task_reg)
+       RESTORE_TCA_REG(14, \task_reg)
+       RESTORE_TCA_REG(15, \task_reg)
+       RESTORE_TCA_REG(16, \task_reg)
+       RESTORE_TCA_REG(17, \task_reg)
+       RESTORE_TCA_REG(18, \task_reg)
+       RESTORE_TCA_REG(19, \task_reg)
+       RESTORE_TCA_REG(20, \task_reg)
+       RESTORE_TCA_REG(21, \task_reg)
+       RESTORE_TCA_REG(22, \task_reg)
+       RESTORE_TCA_REG(23, \task_reg)
+       RESTORE_TCA_REG(24, \task_reg)
+       RESTORE_TCA_REG(25, \task_reg)
+       RESTORE_TCA_REG(26, \task_reg)
+       RESTORE_TCA_REG(27, \task_reg)
+       RESTORE_TCA_REG(28, \task_reg)
+       RESTORE_TCA_REG(29, \task_reg)
+       RESTORE_TCA_REG(30, \task_reg)
+       RESTORE_TCA_REG(31, \task_reg)
+       RESTORE_TCA_REG(32, \task_reg)
+       RESTORE_TCA_REG(33, \task_reg)
+       RESTORE_TCA_REG(34, \task_reg)
+       RESTORE_TCA_REG(35, \task_reg)
+       RESTORE_TCA_REG(36, \task_reg)
+       RESTORE_TCA_REG(37, \task_reg)
+       RESTORE_TCA_REG(38, \task_reg)
+       RESTORE_TCA_REG(39, \task_reg)
+       RESTORE_TCA_REG(40, \task_reg)
+       RESTORE_TCA_REG(41, \task_reg)
+       RESTORE_TCA_REG(42, \task_reg)
+       RESTORE_TCA_REG(43, \task_reg)
+       RESTORE_TCA_REG(44, \task_reg)
+       RESTORE_TCA_REG(45, \task_reg)
+       RESTORE_TCA_REG(46, \task_reg)
+       RESTORE_TCA_REG(47, \task_reg)
+.endm
+
+.text
+/*
+ * When entering in ret_from_kernel_thread, r20 and r21 where set by
+ * copy_thread and have been restored in switch_to.
+ * These registers contains the values needed to call a function
+ * specified by the switch_to caller (or where set by copy_thread).
+ */
+ENTRY(ret_from_kernel_thread)
+       call schedule_tail
+       ;;
+       /* Call fn(arg) */
+       copyd $r0 = $r21
+       ;;
+       icall $r20
+       ;;
+       goto ret_from_kernel
+       ;;
+ENDPROC(ret_from_kernel_thread)
+
+/**
+ * Return from fork.
+ * start_thread will set return stack and pc. Then copy_thread will
+ * take care of the copying logic.
+ * $r20 will then contains 0 if tracing disabled (set by copy_thread)
+ * The mechanism is almost the same as for ret_from_kernel_thread.
+ */
+ENTRY(ret_from_fork)
+       call schedule_tail
+       ;;
+       /* $r20 contains 0 if tracing disable */
+       cb.deqz $r20? ret_from_kernel
+       ;;
+       copyd $r0 = $sp
+       call do_syscall_trace_exit
+       ;;
+ret_from_kernel:
+       /*
+        * When returning from a fork, the child will take this path.
+        * Since we did not restore callee in return_from_exception, we
+        * must do it before.
+        */
+       lo $r28r29r30r31 = PT_Q28[$sp]
+       ;;
+       lo $r24r25r26r27 = PT_Q24[$sp]
+       ;;
+       lo $r20r21r22r23 = PT_Q20[$sp]
+       ;;
+       lq $r18r19 = PT_R18R19[$sp]
+       ;;
+#ifdef CONFIG_DEBUG_EXCEPTION_STACK
+       /**
+       * Debug code expect entry stack to be stored at current $sp.
+       * Make some room and store current $sp to avoid triggering false alarm.
+       */
+       addd $sp = $sp, -STACK_REG_SIZE
+       ;;
+       sd 0[$sp] = $sp
+#endif
+       ;;
+       goto return_from_exception
+       ;;
+ENDPROC(ret_from_fork)
+
+/*
+ * The callee-saved registers must be saved and restored.
+ * When entering:
+ * - r0 = previous task struct
+ * - r1 = next task struct
+ * Moreover, the parameters for function call (given by copy_thread)
+ * are stored in:
+ * - r20 = Func to call
+ * - r21 = Argument for function
+ */
+ENTRY(__switch_to)
+       sd CTX_SWITCH_FP[$r0] = $fp
+       ;;
+       /* Save previous task context */
+       so CTX_SWITCH_Q20[$r0] = $r20r21r22r23
+       ;;
+       so CTX_SWITCH_Q24[$r0] = $r24r25r26r27
+       get $r16 = $ra
+       ;;
+       so CTX_SWITCH_Q28[$r0] = $r28r29r30r31
+       copyd $r17 = $sp
+       get $r2 = $cs
+       ;;
+       so CTX_SWITCH_RA_SP_R18_R19[$r0] = $r16r17r18r19
+       /* Extract XMF bit which means coprocessor was used by user */
+       andd $r3 = $r2, KVX_SFR_CS_XMF_MASK
+       ;;
+#ifdef CONFIG_ENABLE_TCA
+       make $r4 = 0
+       make $r5 = 0
+       make $r6 = 1
+       cb.dnez $r3? save_tca_registers
+       /* Check if next task needs TCA registers to be restored */
+       ld $r7 = CTX_SWITCH_TCA_REGS_SAVED[$r1]
+       ;;
+check_restore_tca:
+       cb.dnez $r7? restore_tca_registers
+       ;;
+restore_fast_path:
+#endif
+       /* Restore next task context */
+       lo $r16r17r18r19 = CTX_SWITCH_RA_SP_R18_R19[$r1]
+       ;;
+       lo $r20r21r22r23 = CTX_SWITCH_Q20[$r1]
+       ;;
+       lo $r24r25r26r27 = CTX_SWITCH_Q24[$r1]
+       copyd $sp = $r17
+       set $ra = $r16
+       ;;
+       lo $r28r29r30r31 = CTX_SWITCH_Q28[$r1]
+       set $sr = $r1
+       ;;
+       ld $fp = CTX_SWITCH_FP[$r1]
+       ;;
+       ret
+       ;;
+#ifdef CONFIG_ENABLE_TCA
+save_tca_registers:
+       save_tca_regs $r0 $r4 $r5
+       ;;
+       /* Indicates that we saved the TCA context */
+       sb CTX_SWITCH_TCA_REGS_SAVED[$r0] = $r6
+       goto check_restore_tca
+       ;;
+restore_tca_registers:
+       restore_tca_regs $r1
+       ;;
+       /* Clear TCA registers saved hint */
+       sb CTX_SWITCH_TCA_REGS_SAVED[$r1] = $r5
+       goto restore_fast_path
+       ;;
+#endif
+ENDPROC(__switch_to)
+
+/***********************************************************************
+*                       Misc functions
+***********************************************************************/
+
+/**
+ * Avoid hardcoding trampoline for rt_sigreturn by using this code and
+ * copying it on user trampoline
+ */
+.pushsection .text
+.global user_scall_rt_sigreturn_end
+ENTRY(user_scall_rt_sigreturn)
+       make $r6 = __NR_rt_sigreturn
+       ;;
+       scall $r6
+       ;;
+user_scall_rt_sigreturn_end:
+ENDPROC(user_scall_rt_sigreturn)
+.popsection
diff --git a/arch/kvx/kernel/sys_kvx.c b/arch/kvx/kernel/sys_kvx.c
new file mode 100644
index 000000000000..b32a821d76c9
--- /dev/null
+++ b/arch/kvx/kernel/sys_kvx.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2017-2023 Kalray Inc.
+ * Author(s): Clement Leger
+ *            Guillaume Thouvenin
+ */
+
+#include <linux/syscalls.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cachectl.h>
+
+SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
+       unsigned long, prot, unsigned long, flags,
+       unsigned long, fd, off_t, off)
+{
+       /* offset must be a multiple of the page size */
+       if (unlikely(offset_in_page(off) != 0))
+               return -EINVAL;
+
+       /* Unlike mmap2 where the offset is in PAGE_SIZE-byte units, here it
+        * is in bytes. So we need to use PAGE_SHIFT.
+        */
+       return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
+}
+
+SYSCALL_DEFINE4(cachectl, unsigned long, addr, unsigned long, len,
+               unsigned long, cache, unsigned long, flags)
+{
+       bool wb = !!(flags & CACHECTL_FLAG_OP_WB);
+       bool inval = !!(flags & CACHECTL_FLAG_OP_INVAL);
+
+       if (len == 0)
+               return 0;
+
+       /* Check for overflow */
+       if (addr + len < addr)
+               return -EFAULT;
+
+       if (cache != CACHECTL_CACHE_DCACHE)
+               return -EINVAL;
+
+       if ((flags & CACHECTL_FLAG_OP_MASK) == 0)
+               return -EINVAL;
+
+       if (flags & CACHECTL_FLAG_ADDR_PHYS) {
+               if (!IS_ENABLED(CONFIG_CACHECTL_UNSAFE_PHYS_OPERATIONS))
+                       return -EINVAL;
+
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+
+               dcache_wb_inval_phys_range(addr, len, wb, inval);
+               return 0;
+       }
+
+       return dcache_wb_inval_virt_range(addr, len, wb, inval);
+}
diff --git a/arch/kvx/kernel/syscall_table.c b/arch/kvx/kernel/syscall_table.c
new file mode 100644
index 000000000000..7859d25e728d
--- /dev/null
+++ b/arch/kvx/kernel/syscall_table.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * derived from arch/riscv/kernel/syscall_table.c
+ *
+ * Copyright (C) 2017-2023 Kalray Inc.
+ * Author(s): Clement Leger
+ */
+
+#include <linux/syscalls.h>
+
+#include <asm/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>
+};
-- 
2.37.2





--
Linux-audit mailing list
[email protected]
https://listman.redhat.com/mailman/listinfo/linux-audit

Reply via email to