#include <asm/signal.h>
 #include <asm/signal32.h>
 #include <asm/switch_to.h>
+#include <asm/vector.h>
 #include <asm/csr.h>
 #include <asm/cacheflush.h>
 
 extern u32 __user_rt_sigreturn[2];
+static size_t riscv_v_sc_size __ro_after_init;
 
 #define DEBUG_SIG 0
 
 #define restore_fp_state(task, regs) (0)
 #endif
 
+#ifdef CONFIG_RISCV_ISA_V
+
+static long save_v_state(struct pt_regs *regs, void __user **sc_vec)
+{
+       struct __riscv_ctx_hdr __user *hdr;
+       struct __sc_riscv_v_state __user *state;
+       void __user *datap;
+       long err;
+
+       hdr = *sc_vec;
+       /* Place state to the user's signal context space after the hdr */
+       state = (struct __sc_riscv_v_state __user *)(hdr + 1);
+       /* Point datap right after the end of __sc_riscv_v_state */
+       datap = state + 1;
+
+       /* datap is designed to be 16 byte aligned for better performance */
+       WARN_ON(unlikely(!IS_ALIGNED((unsigned long)datap, 16)));
+
+       riscv_v_vstate_save(current, regs);
+       /* Copy everything of vstate but datap. */
+       err = __copy_to_user(&state->v_state, ¤t->thread.vstate,
+                            offsetof(struct __riscv_v_ext_state, datap));
+       /* Copy the pointer datap itself. */
+       err |= __put_user(datap, &state->v_state.datap);
+       /* Copy the whole vector content to user space datap. */
+       err |= __copy_to_user(datap, current->thread.vstate.datap, riscv_v_vsize);
+       /* Copy magic to the user space after saving  all vector conetext */
+       err |= __put_user(RISCV_V_MAGIC, &hdr->magic);
+       err |= __put_user(riscv_v_sc_size, &hdr->size);
+       if (unlikely(err))
+               return err;
+
+       /* Only progress the sv_vec if everything has done successfully  */
+       *sc_vec += riscv_v_sc_size;
+       return 0;
+}
+
+/*
+ * Restore Vector extension context from the user's signal frame. This function
+ * assumes a valid extension header. So magic and size checking must be done by
+ * the caller.
+ */
+static long __restore_v_state(struct pt_regs *regs, void __user *sc_vec)
+{
+       long err;
+       struct __sc_riscv_v_state __user *state = sc_vec;
+       void __user *datap;
+
+       /* Copy everything of __sc_riscv_v_state except datap. */
+       err = __copy_from_user(¤t->thread.vstate, &state->v_state,
+                              offsetof(struct __riscv_v_ext_state, datap));
+       if (unlikely(err))
+               return err;
+
+       /* Copy the pointer datap itself. */
+       err = __get_user(datap, &state->v_state.datap);
+       if (unlikely(err))
+               return err;
+       /*
+        * Copy the whole vector content from user space datap. Use
+        * copy_from_user to prevent information leak.
+        */
+       err = copy_from_user(current->thread.vstate.datap, datap, riscv_v_vsize);
+       if (unlikely(err))
+               return err;
+
+       riscv_v_vstate_restore(current, regs);
+
+       return err;
+}
+#else
+#define save_v_state(task, regs) (0)
+#define __restore_v_state(task, regs) (0)
+#endif
+
 static long restore_sigcontext(struct pt_regs *regs,
        struct sigcontext __user *sc)
 {
+       void __user *sc_ext_ptr = &sc->sc_extdesc.hdr;
+       __u32 rsvd;
        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;
        }
 
-       /* We support no other extension state at this time. */
-       for (i = 0; i < ARRAY_SIZE(sc->sc_fpregs.q.reserved); i++) {
-               u32 value;
+       /* Check the reserved word before extensions parsing */
+       err = __get_user(rsvd, &sc->sc_extdesc.reserved);
+       if (unlikely(err))
+               return err;
+       if (unlikely(rsvd))
+               return -EINVAL;
+
+       while (!err) {
+               __u32 magic, size;
+               struct __riscv_ctx_hdr __user *head = sc_ext_ptr;
 
-               err = __get_user(value, &sc->sc_fpregs.q.reserved[i]);
+               err |= __get_user(magic, &head->magic);
+               err |= __get_user(size, &head->size);
                if (unlikely(err))
+                       return err;
+
+               sc_ext_ptr += sizeof(*head);
+               switch (magic) {
+               case END_MAGIC:
+                       if (size != END_HDR_SIZE)
+                               return -EINVAL;
+
+                       return 0;
+               case RISCV_V_MAGIC:
+                       if (!has_vector() || !riscv_v_vstate_query(regs) ||
+                           size != riscv_v_sc_size)
+                               return -EINVAL;
+
+                       err = __restore_v_state(regs, sc_ext_ptr);
                        break;
-               if (value != 0)
+               default:
                        return -EINVAL;
+               }
+               sc_ext_ptr = (void __user *)head + size;
        }
        return err;
 }
 
+static size_t get_rt_frame_size(void)
+{
+       struct rt_sigframe __user *frame;
+       size_t frame_size;
+       size_t total_context_size = 0;
+
+       frame_size = sizeof(*frame);
+
+       if (has_vector() && riscv_v_vstate_query(task_pt_regs(current)))
+               total_context_size += riscv_v_sc_size;
+       /*
+        * Preserved a __riscv_ctx_hdr for END signal context header if an
+        * extension uses __riscv_extra_ext_header
+        */
+       if (total_context_size)
+               total_context_size += sizeof(struct __riscv_ctx_hdr);
+
+       frame_size += total_context_size;
+
+       frame_size = round_up(frame_size, 16);
+       return frame_size;
+}
+
 SYSCALL_DEFINE0(rt_sigreturn)
 {
        struct pt_regs *regs = current_pt_regs();
        struct rt_sigframe __user *frame;
        struct task_struct *task;
        sigset_t set;
+       size_t frame_size = get_rt_frame_size();
 
        /* 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(frame, sizeof(*frame)))
+       if (!access_ok(frame, frame_size))
                goto badframe;
 
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
        struct pt_regs *regs)
 {
        struct sigcontext __user *sc = &frame->uc.uc_mcontext;
+       struct __riscv_ctx_hdr __user *sc_ext_ptr = &sc->sc_extdesc.hdr;
        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. */
        if (has_fpu())
                err |= save_fp_state(regs, &sc->sc_fpregs);
-       /* 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]);
+       /* Save the vector state. */
+       if (has_vector() && riscv_v_vstate_query(regs))
+               err |= save_v_state(regs, (void __user **)&sc_ext_ptr);
+       /* Write zero to fp-reserved space and check it on restore_sigcontext */
+       err |= __put_user(0, &sc->sc_extdesc.reserved);
+       /* And put END __riscv_ctx_hdr at the end. */
+       err |= __put_user(END_MAGIC, &sc_ext_ptr->magic);
+       err |= __put_user(END_HDR_SIZE, &sc_ext_ptr->size);
 
        return err;
 }
        /* Align the stack frame. */
        sp &= ~0xfUL;
 
+       /*
+        * Fail if the size of the altstack is not large enough for the
+        * sigframe construction.
+        */
+       if (current->sas_ss_size && sp < current->sas_ss_sp)
+               return (void __user __force *)-1UL;
+
        return (void __user *)sp;
 }
 
        struct rt_sigframe __user *frame;
        long err = 0;
        unsigned long __maybe_unused addr;
+       size_t frame_size = get_rt_frame_size();
 
-       frame = get_sigframe(ksig, regs, sizeof(*frame));
-       if (!access_ok(frame, sizeof(*frame)))
+       frame = get_sigframe(ksig, regs, frame_size);
+       if (!access_ok(frame, frame_size))
                return -EFAULT;
 
        err |= copy_siginfo_to_user(&frame->info, &ksig->info);
         */
        restore_saved_sigmask();
 }
+
+void init_rt_signal_env(void);
+void __init init_rt_signal_env(void)
+{
+       riscv_v_sc_size = sizeof(struct __riscv_ctx_hdr) +
+                         sizeof(struct __sc_riscv_v_state) + riscv_v_vsize;
+}