Merge patch series "ISA string parser cleanups"
authorPalmer Dabbelt <palmer@rivosinc.com>
Wed, 21 Jun 2023 14:49:09 +0000 (07:49 -0700)
committerPalmer Dabbelt <palmer@rivosinc.com>
Fri, 23 Jun 2023 17:06:20 +0000 (10:06 -0700)
Conor Dooley <conor@kernel.org> says:

From: Conor Dooley <conor.dooley@microchip.com>

Here are some bits that were discussed with Drew on the "should we
allow caps" threads that I have now created patches for:
- splitting of riscv_of_processor_hartid() into two distinct functions,
  one for use purely during early boot, prior to the establishment of
  the possible-cpus mask & another to fit the other current use-cases
- that then allows us to then completely skip some validation of the
  hartid in the parser
- the biggest diff in the series is a rework of the comments in the
  parser, as I have mostly found the existing (sparse) ones to not be
  all that helpful whenever I have to go back and look at it
- from writing the comments, I found a conditional doing a bit of a
  dance that I found counter-intuitive, so I've had a go at making that
  match what I would expect a little better
- `i` implies 4 other extensions, so add them as extensions and set
  them for the craic. Sure why not like...

* b4-shazam-merge:
  RISC-V: always report presence of extensions formerly part of the base ISA
  dt-bindings: riscv: explicitly mention assumption of Zicntr & Zihpm support
  RISC-V: remove decrement/increment dance in ISA string parser
  RISC-V: rework comments in ISA string parser
  RISC-V: validate riscv,isa at boot, not during ISA string parsing
  RISC-V: split early & late of_node to hartid mapping
  RISC-V: simplify register width check in ISA string parsing

Link: https://lore.kernel.org/r/20230607-audacity-overhaul-82bb867a825f@spud
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
58 files changed:
Documentation/devicetree/bindings/riscv/cpus.yaml
Documentation/riscv/hwprobe.rst
Documentation/riscv/index.rst
Documentation/riscv/vector.rst [new file with mode: 0644]
arch/riscv/Kconfig
arch/riscv/Makefile
arch/riscv/include/asm/asm-extable.h
arch/riscv/include/asm/cpufeature.h
arch/riscv/include/asm/csr.h
arch/riscv/include/asm/elf.h
arch/riscv/include/asm/extable.h
arch/riscv/include/asm/hwcap.h
arch/riscv/include/asm/insn.h
arch/riscv/include/asm/kvm_host.h
arch/riscv/include/asm/kvm_vcpu_vector.h [new file with mode: 0644]
arch/riscv/include/asm/processor.h
arch/riscv/include/asm/switch_to.h
arch/riscv/include/asm/thread_info.h
arch/riscv/include/asm/vector.h [new file with mode: 0644]
arch/riscv/include/uapi/asm/auxvec.h
arch/riscv/include/uapi/asm/hwcap.h
arch/riscv/include/uapi/asm/hwprobe.h
arch/riscv/include/uapi/asm/kvm.h
arch/riscv/include/uapi/asm/ptrace.h
arch/riscv/include/uapi/asm/sigcontext.h
arch/riscv/kernel/Makefile
arch/riscv/kernel/acpi.c
arch/riscv/kernel/cpu.c
arch/riscv/kernel/cpufeature.c
arch/riscv/kernel/entry.S
arch/riscv/kernel/head.S
arch/riscv/kernel/hibernate-asm.S
arch/riscv/kernel/probes/uprobes.c
arch/riscv/kernel/process.c
arch/riscv/kernel/ptrace.c
arch/riscv/kernel/setup.c
arch/riscv/kernel/signal.c
arch/riscv/kernel/smpboot.c
arch/riscv/kernel/sys_riscv.c
arch/riscv/kernel/traps.c
arch/riscv/kernel/vdso/rt_sigreturn.S
arch/riscv/kernel/vector.c [new file with mode: 0644]
arch/riscv/kvm/Makefile
arch/riscv/kvm/vcpu.c
arch/riscv/kvm/vcpu_vector.c [new file with mode: 0644]
arch/riscv/mm/Makefile
arch/riscv/mm/fault.c
arch/riscv/mm/init.c
drivers/perf/riscv_pmu_sbi.c
include/uapi/linux/elf.h
include/uapi/linux/prctl.h
kernel/sys.c
tools/testing/selftests/riscv/Makefile
tools/testing/selftests/riscv/hwprobe/.gitignore [new file with mode: 0644]
tools/testing/selftests/riscv/vector/.gitignore [new file with mode: 0644]
tools/testing/selftests/riscv/vector/Makefile [new file with mode: 0644]
tools/testing/selftests/riscv/vector/vstate_exec_nolibc.c [new file with mode: 0644]
tools/testing/selftests/riscv/vector/vstate_prctl.c [new file with mode: 0644]

index d520888..c2ed979 100644 (file)
@@ -61,7 +61,7 @@ properties:
       hart.  These values originate from the RISC-V Privileged
       Specification document, available from
       https://riscv.org/specifications/
-    $ref: "/schemas/types.yaml#/definitions/string"
+    $ref: /schemas/types.yaml#/definitions/string
     enum:
       - riscv,sv32
       - riscv,sv39
@@ -95,7 +95,7 @@ properties:
       While the isa strings in ISA specification are case
       insensitive, letters in the riscv,isa string must be all
       lowercase.
-    $ref: "/schemas/types.yaml#/definitions/string"
+    $ref: /schemas/types.yaml#/definitions/string
     pattern: ^rv(?:64|32)imaf?d?q?c?b?k?j?p?v?h?(?:[hsxz](?:[a-z])+)?(?:_[hsxz](?:[a-z])+)*$
 
   # RISC-V requires 'timebase-frequency' in /cpus, so disallow it here
@@ -120,7 +120,7 @@ properties:
       - interrupt-controller
 
   cpu-idle-states:
-    $ref: '/schemas/types.yaml#/definitions/phandle-array'
+    $ref: /schemas/types.yaml#/definitions/phandle-array
     items:
       maxItems: 1
     description: |
index 9f0dd62..19165eb 100644 (file)
@@ -64,6 +64,19 @@ The following keys are defined:
   * :c:macro:`RISCV_HWPROBE_IMA_C`: The C extension is supported, as defined
     by version 2.2 of the RISC-V ISA manual.
 
+  * :c:macro:`RISCV_HWPROBE_IMA_V`: The V extension is supported, as defined by
+    version 1.0 of the RISC-V Vector extension manual.
+
+  * :c:macro:`RISCV_HWPROBE_EXT_ZBA`: The Zba address generation extension is
+       supported, as defined in version 1.0 of the Bit-Manipulation ISA
+       extensions.
+
+  * :c:macro:`RISCV_HWPROBE_EXT_ZBB`: The Zbb extension is supported, as defined
+       in version 1.0 of the Bit-Manipulation ISA extensions.
+
+  * :c:macro:`RISCV_HWPROBE_EXT_ZBS`: The Zbs extension is supported, as defined
+       in version 1.0 of the Bit-Manipulation ISA extensions.
+
 * :c:macro:`RISCV_HWPROBE_KEY_CPUPERF_0`: A bitmask that contains performance
   information about the selected set of processors.
 
index 175a91d..95cf9c1 100644 (file)
@@ -10,6 +10,7 @@ RISC-V architecture
     hwprobe
     patch-acceptance
     uabi
+    vector
 
     features
 
diff --git a/Documentation/riscv/vector.rst b/Documentation/riscv/vector.rst
new file mode 100644 (file)
index 0000000..48f189d
--- /dev/null
@@ -0,0 +1,132 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================================
+Vector Extension Support for RISC-V Linux
+=========================================
+
+This document briefly outlines the interface provided to userspace by Linux in
+order to support the use of the RISC-V Vector Extension.
+
+1.  prctl() Interface
+---------------------
+
+Two new prctl() calls are added to allow programs to manage the enablement
+status for the use of Vector in userspace. The intended usage guideline for
+these interfaces is to give init systems a way to modify the availability of V
+for processes running under its domain. Calling thess interfaces is not
+recommended in libraries routines because libraries should not override policies
+configured from the parant process. Also, users must noted that these interfaces
+are not portable to non-Linux, nor non-RISC-V environments, so it is discourage
+to use in a portable code. To get the availability of V in an ELF program,
+please read :c:macro:`COMPAT_HWCAP_ISA_V` bit of :c:macro:`ELF_HWCAP` in the
+auxiliary vector.
+
+* prctl(PR_RISCV_V_SET_CONTROL, unsigned long arg)
+
+    Sets the Vector enablement status of the calling thread, where the control
+    argument consists of two 2-bit enablement statuses and a bit for inheritance
+    mode. Other threads of the calling process are unaffected.
+
+    Enablement status is a tri-state value each occupying 2-bit of space in
+    the control argument:
+
+    * :c:macro:`PR_RISCV_V_VSTATE_CTRL_DEFAULT`: Use the system-wide default
+      enablement status on execve(). The system-wide default setting can be
+      controlled via sysctl interface (see sysctl section below).
+
+    * :c:macro:`PR_RISCV_V_VSTATE_CTRL_ON`: Allow Vector to be run for the
+      thread.
+
+    * :c:macro:`PR_RISCV_V_VSTATE_CTRL_OFF`: Disallow Vector. Executing Vector
+      instructions under such condition will trap and casuse the termination of the thread.
+
+    arg: The control argument is a 5-bit value consisting of 3 parts, and
+    accessed by 3 masks respectively.
+
+    The 3 masks, PR_RISCV_V_VSTATE_CTRL_CUR_MASK,
+    PR_RISCV_V_VSTATE_CTRL_NEXT_MASK, and PR_RISCV_V_VSTATE_CTRL_INHERIT
+    represents bit[1:0], bit[3:2], and bit[4]. bit[1:0] accounts for the
+    enablement status of current thread, and the setting at bit[3:2] takes place
+    at next execve(). bit[4] defines the inheritance mode of the setting in
+    bit[3:2].
+
+        * :c:macro:`PR_RISCV_V_VSTATE_CTRL_CUR_MASK`: bit[1:0]: Account for the
+          Vector enablement status for the calling thread. The calling thread is
+          not able to turn off Vector once it has been enabled. The prctl() call
+          fails with EPERM if the value in this mask is PR_RISCV_V_VSTATE_CTRL_OFF
+          but the current enablement status is not off. Setting
+          PR_RISCV_V_VSTATE_CTRL_DEFAULT here takes no effect but to set back
+          the original enablement status.
+
+        * :c:macro:`PR_RISCV_V_VSTATE_CTRL_NEXT_MASK`: bit[3:2]: Account for the
+          Vector enablement setting for the calling thread at the next execve()
+          system call. If PR_RISCV_V_VSTATE_CTRL_DEFAULT is used in this mask,
+          then the enablement status will be decided by the system-wide
+          enablement status when execve() happen.
+
+        * :c:macro:`PR_RISCV_V_VSTATE_CTRL_INHERIT`: bit[4]: the inheritance
+          mode for the setting at PR_RISCV_V_VSTATE_CTRL_NEXT_MASK. If the bit
+          is set then the following execve() will not clear the setting in both
+          PR_RISCV_V_VSTATE_CTRL_NEXT_MASK and PR_RISCV_V_VSTATE_CTRL_INHERIT.
+          This setting persists across changes in the system-wide default value.
+
+    Return value:
+        * 0 on success;
+        * EINVAL: Vector not supported, invalid enablement status for current or
+          next mask;
+        * EPERM: Turning off Vector in PR_RISCV_V_VSTATE_CTRL_CUR_MASK if Vector
+          was enabled for the calling thread.
+
+    On success:
+        * A valid setting for PR_RISCV_V_VSTATE_CTRL_CUR_MASK takes place
+          immediately. The enablement status specified in
+          PR_RISCV_V_VSTATE_CTRL_NEXT_MASK happens at the next execve() call, or
+          all following execve() calls if PR_RISCV_V_VSTATE_CTRL_INHERIT bit is
+          set.
+        * Every successful call overwrites a previous setting for the calling
+          thread.
+
+* prctl(PR_RISCV_V_GET_CONTROL)
+
+    Gets the same Vector enablement status for the calling thread. Setting for
+    next execve() call and the inheritance bit are all OR-ed together.
+
+    Note that ELF programs are able to get the availability of V for itself by
+    reading :c:macro:`COMPAT_HWCAP_ISA_V` bit of :c:macro:`ELF_HWCAP` in the
+    auxiliary vector.
+
+    Return value:
+        * a nonnegative value on success;
+        * EINVAL: Vector not supported.
+
+2.  System runtime configuration (sysctl)
+-----------------------------------------
+
+To mitigate the ABI impact of expansion of the signal stack, a
+policy mechanism is provided to the administrators, distro maintainers, and
+developers to control the default Vector enablement status for userspace
+processes in form of sysctl knob:
+
+* /proc/sys/abi/riscv_v_default_allow
+
+    Writing the text representation of 0 or 1 to this file sets the default
+    system enablement status for new starting userspace programs. Valid values
+    are:
+
+    * 0: Do not allow Vector code to be executed as the default for new processes.
+    * 1: Allow Vector code to be executed as the default for new processes.
+
+    Reading this file returns the current system default enablement status.
+
+    At every execve() call, a new enablement status of the new process is set to
+    the system default, unless:
+
+      * PR_RISCV_V_VSTATE_CTRL_INHERIT is set for the calling process, and the
+        setting in PR_RISCV_V_VSTATE_CTRL_NEXT_MASK is not
+        PR_RISCV_V_VSTATE_CTRL_DEFAULT. Or,
+
+      * The setting in PR_RISCV_V_VSTATE_CTRL_NEXT_MASK is not
+        PR_RISCV_V_VSTATE_CTRL_DEFAULT.
+
+    Modifying the system default enablement status does not affect the enablement
+    status of any existing process of thread that do not make an execve() call.
index 491ecd7..a9e8b69 100644 (file)
@@ -44,6 +44,7 @@ config RISCV
        select ARCH_SUPPORTS_DEBUG_PAGEALLOC if MMU
        select ARCH_SUPPORTS_HUGETLBFS if MMU
        select ARCH_SUPPORTS_PAGE_TABLE_CHECK if MMU
+       select ARCH_SUPPORTS_PER_VMA_LOCK if MMU
        select ARCH_USE_MEMTEST
        select ARCH_USE_QUEUED_RWLOCKS
        select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU
@@ -264,6 +265,12 @@ config RISCV_DMA_NONCOHERENT
 config AS_HAS_INSN
        def_bool $(as-instr,.insn r 51$(comma) 0$(comma) 0$(comma) t0$(comma) t0$(comma) zero)
 
+config AS_HAS_OPTION_ARCH
+       # https://reviews.llvm.org/D123515
+       def_bool y
+       depends on $(as-instr, .option arch$(comma) +m)
+       depends on !$(as-instr, .option arch$(comma) -i)
+
 source "arch/riscv/Kconfig.socs"
 source "arch/riscv/Kconfig.errata"
 
@@ -462,13 +469,44 @@ config RISCV_ISA_SVPBMT
 
           If you don't know what to do here, say Y.
 
+config TOOLCHAIN_HAS_V
+       bool
+       default y
+       depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64iv)
+       depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32iv)
+       depends on LLD_VERSION >= 140000 || LD_VERSION >= 23800
+       depends on AS_HAS_OPTION_ARCH
+
+config RISCV_ISA_V
+       bool "VECTOR extension support"
+       depends on TOOLCHAIN_HAS_V
+       depends on FPU
+       select DYNAMIC_SIGFRAME
+       default y
+       help
+         Say N here if you want to disable all vector related procedure
+         in the kernel.
+
+         If you don't know what to do here, say Y.
+
+config RISCV_ISA_V_DEFAULT_ENABLE
+       bool "Enable userspace Vector by default"
+       depends on RISCV_ISA_V
+       default y
+       help
+         Say Y here if you want to enable Vector in userspace by default.
+         Otherwise, userspace has to make explicit prctl() call to enable
+         Vector, or enable it via the sysctl interface.
+
+         If you don't know what to do here, say Y.
+
 config TOOLCHAIN_HAS_ZBB
        bool
        default y
        depends on !64BIT || $(cc-option,-mabi=lp64 -march=rv64ima_zbb)
        depends on !32BIT || $(cc-option,-mabi=ilp32 -march=rv32ima_zbb)
        depends on LLD_VERSION >= 150000 || LD_VERSION >= 23900
-       depends on AS_IS_GNU
+       depends on AS_HAS_OPTION_ARCH
 
 config RISCV_ISA_ZBB
        bool "Zbb extension support for bit manipulation instructions"
index 0fb256b..6ec6d52 100644 (file)
@@ -60,6 +60,7 @@ riscv-march-$(CONFIG_ARCH_RV32I)      := rv32ima
 riscv-march-$(CONFIG_ARCH_RV64I)       := rv64ima
 riscv-march-$(CONFIG_FPU)              := $(riscv-march-y)fd
 riscv-march-$(CONFIG_RISCV_ISA_C)      := $(riscv-march-y)c
+riscv-march-$(CONFIG_RISCV_ISA_V)      := $(riscv-march-y)v
 
 ifdef CONFIG_TOOLCHAIN_NEEDS_OLD_ISA_SPEC
 KBUILD_CFLAGS += -Wa,-misa-spec=2.2
@@ -71,7 +72,10 @@ endif
 # Check if the toolchain supports Zihintpause extension
 riscv-march-$(CONFIG_TOOLCHAIN_HAS_ZIHINTPAUSE) := $(riscv-march-y)_zihintpause
 
-KBUILD_CFLAGS += -march=$(subst fd,,$(riscv-march-y))
+# Remove F,D,V from isa string for all. Keep extensions between "fd" and "v" by
+# matching non-v and non-multi-letter extensions out with the filter ([^v_]*)
+KBUILD_CFLAGS += -march=$(shell echo $(riscv-march-y) | sed -E 's/(rv32ima|rv64ima)fd([^v_]*)v?/\1\2/')
+
 KBUILD_AFLAGS += -march=$(riscv-march-y)
 
 KBUILD_CFLAGS += -mno-save-restore
index 14be067..00a96e7 100644 (file)
@@ -7,6 +7,8 @@
 #define EX_TYPE_BPF                    2
 #define EX_TYPE_UACCESS_ERR_ZERO       3
 
+#ifdef CONFIG_MMU
+
 #ifdef __ASSEMBLY__
 
 #define __ASM_EXTABLE_RAW(insn, fixup, type, data)     \
@@ -62,4 +64,8 @@
 
 #endif /* __ASSEMBLY__ */
 
+#else /* CONFIG_MMU */
+       #define _ASM_EXTABLE_UACCESS_ERR(insn, fixup, err)
+#endif /* CONFIG_MMU */
+
 #endif /* __ASM_ASM_EXTABLE_H */
index 808d540..23fed53 100644 (file)
@@ -6,6 +6,9 @@
 #ifndef _ASM_CPUFEATURE_H
 #define _ASM_CPUFEATURE_H
 
+#include <linux/bitmap.h>
+#include <asm/hwcap.h>
+
 /*
  * These are probed via a device_initcall(), via either the SBI or directly
  * from the corresponding CSRs.
@@ -16,8 +19,15 @@ struct riscv_cpuinfo {
        unsigned long mimpid;
 };
 
+struct riscv_isainfo {
+       DECLARE_BITMAP(isa, RISCV_ISA_EXT_MAX);
+};
+
 DECLARE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo);
 
 DECLARE_PER_CPU(long, misaligned_access_speed);
 
+/* Per-cpu ISA extensions. */
+extern struct riscv_isainfo hart_isa[NR_CPUS];
+
 #endif
index b6acb7e..b98b3b6 100644 (file)
 #define SR_FS_CLEAN    _AC(0x00004000, UL)
 #define SR_FS_DIRTY    _AC(0x00006000, UL)
 
+#define SR_VS          _AC(0x00000600, UL) /* Vector Status */
+#define SR_VS_OFF      _AC(0x00000000, UL)
+#define SR_VS_INITIAL  _AC(0x00000200, UL)
+#define SR_VS_CLEAN    _AC(0x00000400, UL)
+#define SR_VS_DIRTY    _AC(0x00000600, UL)
+
 #define SR_XS          _AC(0x00018000, UL) /* Extension Status */
 #define SR_XS_OFF      _AC(0x00000000, UL)
 #define SR_XS_INITIAL  _AC(0x00008000, UL)
 #define SR_XS_CLEAN    _AC(0x00010000, UL)
 #define SR_XS_DIRTY    _AC(0x00018000, UL)
 
+#define SR_FS_VS       (SR_FS | SR_VS) /* Vector and Floating-Point Unit */
+
 #ifndef CONFIG_64BIT
-#define SR_SD          _AC(0x80000000, UL) /* FS/XS dirty */
+#define SR_SD          _AC(0x80000000, UL) /* FS/VS/XS dirty */
 #else
-#define SR_SD          _AC(0x8000000000000000, UL) /* FS/XS dirty */
+#define SR_SD          _AC(0x8000000000000000, UL) /* FS/VS/XS dirty */
 #endif
 
 #ifdef CONFIG_64BIT
 #define CSR_MVIPH              0x319
 #define CSR_MIPH               0x354
 
+#define CSR_VSTART             0x8
+#define CSR_VCSR               0xf
+#define CSR_VL                 0xc20
+#define CSR_VTYPE              0xc21
+#define CSR_VLENB              0xc22
+
 #ifdef CONFIG_RISCV_M_MODE
 # define CSR_STATUS    CSR_MSTATUS
 # define CSR_IE                CSR_MIE
index 30e7d24..c242807 100644 (file)
@@ -66,7 +66,7 @@ extern bool compat_elf_check_arch(Elf32_Ehdr *hdr);
  * via a bitmap that coorespends to each single-letter ISA extension.  This is
  * essentially defunct, but will remain for compatibility with userspace.
  */
-#define ELF_HWCAP      (elf_hwcap & ((1UL << RISCV_ISA_EXT_BASE) - 1))
+#define ELF_HWCAP      riscv_get_elf_hwcap()
 extern unsigned long elf_hwcap;
 
 /*
@@ -105,6 +105,15 @@ do {                                                               \
                get_cache_size(3, CACHE_TYPE_UNIFIED));         \
        NEW_AUX_ENT(AT_L3_CACHEGEOMETRY,                        \
                get_cache_geometry(3, CACHE_TYPE_UNIFIED));     \
+       /*                                                       \
+        * Should always be nonzero unless there's a kernel bug. \
+        * If we haven't determined a sensible value to give to  \
+        * userspace, omit the entry:                            \
+        */                                                      \
+       if (likely(signal_minsigstksz))                          \
+               NEW_AUX_ENT(AT_MINSIGSTKSZ, signal_minsigstksz); \
+       else                                                     \
+               NEW_AUX_ENT(AT_IGNORE, 0);                       \
 } while (0)
 #define ARCH_HAS_SETUP_ADDITIONAL_PAGES
 struct linux_binprm;
index 512012d..3eb5c1f 100644 (file)
@@ -32,7 +32,11 @@ do {                                                 \
        (b)->data = (tmp).data;                         \
 } while (0)
 
+#ifdef CONFIG_MMU
 bool fixup_exception(struct pt_regs *regs);
+#else
+static inline bool fixup_exception(struct pt_regs *regs) { return false; }
+#endif
 
 #if defined(CONFIG_BPF_JIT) && defined(CONFIG_ARCH_RV64I)
 bool ex_handler_bpf(const struct exception_table_entry *ex, struct pt_regs *regs);
index e0eb9ad..f041bfa 100644 (file)
@@ -22,6 +22,7 @@
 #define RISCV_ISA_EXT_m                ('m' - 'a')
 #define RISCV_ISA_EXT_s                ('s' - 'a')
 #define RISCV_ISA_EXT_u                ('u' - 'a')
+#define RISCV_ISA_EXT_v                ('v' - 'a')
 
 /*
  * These macros represent the logical IDs of each multi-letter RISC-V ISA
 #define RISCV_ISA_EXT_ZICBOZ           34
 #define RISCV_ISA_EXT_SMAIA            35
 #define RISCV_ISA_EXT_SSAIA            36
-#define RISCV_ISA_EXT_ZICNTR           37
-#define RISCV_ISA_EXT_ZICSR            38
-#define RISCV_ISA_EXT_ZIFENCEI         39
-#define RISCV_ISA_EXT_ZIHPM            40
+#define RISCV_ISA_EXT_ZBA              37
+#define RISCV_ISA_EXT_ZBS              38
+#define RISCV_ISA_EXT_ZICNTR           39
+#define RISCV_ISA_EXT_ZICSR            40
+#define RISCV_ISA_EXT_ZIFENCEI         41
+#define RISCV_ISA_EXT_ZIHPM            42
 
 #define RISCV_ISA_EXT_MAX              64
 #define RISCV_ISA_EXT_NAME_LEN_MAX     32
@@ -64,6 +67,8 @@
 
 #include <linux/jump_label.h>
 
+unsigned long riscv_get_elf_hwcap(void);
+
 struct riscv_isa_ext_data {
        /* Name of the extension displayed to userspace via /proc/cpuinfo */
        char uprop[RISCV_ISA_EXT_NAME_LEN_MAX];
index 8d5c84f..4e1505c 100644 (file)
 #define RVG_OPCODE_JALR                0x67
 #define RVG_OPCODE_JAL         0x6f
 #define RVG_OPCODE_SYSTEM      0x73
+#define RVG_SYSTEM_CSR_OFF     20
+#define RVG_SYSTEM_CSR_MASK    GENMASK(12, 0)
+
+/* parts of opcode for RVF, RVD and RVQ */
+#define RVFDQ_FL_FS_WIDTH_OFF  12
+#define RVFDQ_FL_FS_WIDTH_MASK GENMASK(3, 0)
+#define RVFDQ_FL_FS_WIDTH_W    2
+#define RVFDQ_FL_FS_WIDTH_D    3
+#define RVFDQ_LS_FS_WIDTH_Q    4
+#define RVFDQ_OPCODE_FL                0x07
+#define RVFDQ_OPCODE_FS                0x27
+
+/* parts of opcode for RVV */
+#define RVV_OPCODE_VECTOR      0x57
+#define RVV_VL_VS_WIDTH_8      0
+#define RVV_VL_VS_WIDTH_16     5
+#define RVV_VL_VS_WIDTH_32     6
+#define RVV_VL_VS_WIDTH_64     7
+#define RVV_OPCODE_VL          RVFDQ_OPCODE_FL
+#define RVV_OPCODE_VS          RVFDQ_OPCODE_FS
 
 /* parts of opcode for RVC*/
 #define RVC_OPCODE_C0          0x0
@@ -304,6 +324,15 @@ static __always_inline bool riscv_insn_is_branch(u32 code)
        (RVC_X(x_, RVC_B_IMM_7_6_OPOFF, RVC_B_IMM_7_6_MASK) << RVC_B_IMM_7_6_OFF) | \
        (RVC_IMM_SIGN(x_) << RVC_B_IMM_SIGN_OFF); })
 
+#define RVG_EXTRACT_SYSTEM_CSR(x) \
+       ({typeof(x) x_ = (x); RV_X(x_, RVG_SYSTEM_CSR_OFF, RVG_SYSTEM_CSR_MASK); })
+
+#define RVFDQ_EXTRACT_FL_FS_WIDTH(x) \
+       ({typeof(x) x_ = (x); RV_X(x_, RVFDQ_FL_FS_WIDTH_OFF, \
+                                  RVFDQ_FL_FS_WIDTH_MASK); })
+
+#define RVV_EXRACT_VL_VS_WIDTH(x) RVFDQ_EXTRACT_FL_FS_WIDTH(x)
+
 /*
  * Get the immediate from a J-type instruction.
  *
index ee0accc..bd47a1d 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/spinlock.h>
 #include <asm/hwcap.h>
 #include <asm/kvm_aia.h>
+#include <asm/ptrace.h>
 #include <asm/kvm_vcpu_fp.h>
 #include <asm/kvm_vcpu_insn.h>
 #include <asm/kvm_vcpu_sbi.h>
@@ -145,6 +146,7 @@ struct kvm_cpu_context {
        unsigned long sstatus;
        unsigned long hstatus;
        union __riscv_fp_state fp;
+       struct __riscv_v_ext_state vector;
 };
 
 struct kvm_vcpu_csr {
diff --git a/arch/riscv/include/asm/kvm_vcpu_vector.h b/arch/riscv/include/asm/kvm_vcpu_vector.h
new file mode 100644 (file)
index 0000000..ff994fd
--- /dev/null
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2022 SiFive
+ *
+ * Authors:
+ *     Vincent Chen <vincent.chen@sifive.com>
+ *     Greentime Hu <greentime.hu@sifive.com>
+ */
+
+#ifndef __KVM_VCPU_RISCV_VECTOR_H
+#define __KVM_VCPU_RISCV_VECTOR_H
+
+#include <linux/types.h>
+
+#ifdef CONFIG_RISCV_ISA_V
+#include <asm/vector.h>
+#include <asm/kvm_host.h>
+
+static __always_inline void __kvm_riscv_vector_save(struct kvm_cpu_context *context)
+{
+       __riscv_v_vstate_save(&context->vector, context->vector.datap);
+}
+
+static __always_inline void __kvm_riscv_vector_restore(struct kvm_cpu_context *context)
+{
+       __riscv_v_vstate_restore(&context->vector, context->vector.datap);
+}
+
+void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu);
+void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
+                                     unsigned long *isa);
+void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
+                                        unsigned long *isa);
+void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx);
+void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx);
+int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu,
+                                       struct kvm_cpu_context *cntx);
+void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu);
+#else
+
+struct kvm_cpu_context;
+
+static inline void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu)
+{
+}
+
+static inline void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
+                                                   unsigned long *isa)
+{
+}
+
+static inline void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
+                                                      unsigned long *isa)
+{
+}
+
+static inline void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx)
+{
+}
+
+static inline void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
+{
+}
+
+static inline int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu,
+                                                     struct kvm_cpu_context *cntx)
+{
+       return 0;
+}
+
+static inline void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu)
+{
+}
+#endif
+
+int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu,
+                                 const struct kvm_one_reg *reg,
+                                 unsigned long rtype);
+int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu,
+                                 const struct kvm_one_reg *reg,
+                                 unsigned long rtype);
+#endif
index 3479f9f..c950a8d 100644 (file)
@@ -7,6 +7,7 @@
 #define _ASM_RISCV_PROCESSOR_H
 
 #include <linux/const.h>
+#include <linux/cache.h>
 
 #include <vdso/processor.h>
 
@@ -39,6 +40,8 @@ struct thread_struct {
        unsigned long s[12];    /* s[0]: frame pointer */
        struct __riscv_d_ext_state fstate;
        unsigned long bad_cause;
+       unsigned long vstate_ctrl;
+       struct __riscv_v_ext_state vstate;
 };
 
 /* Whitelist the fstate from the task_struct for hardened usercopy */
@@ -81,6 +84,16 @@ int riscv_of_parent_hartid(struct device_node *node, unsigned long *hartid);
 extern void riscv_fill_hwcap(void);
 extern int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src);
 
+extern unsigned long signal_minsigstksz __ro_after_init;
+
+#ifdef CONFIG_RISCV_ISA_V
+/* Userspace interface for PR_RISCV_V_{SET,GET}_VS prctl()s: */
+#define RISCV_V_SET_CONTROL(arg)       riscv_v_vstate_ctrl_set_current(arg)
+#define RISCV_V_GET_CONTROL()          riscv_v_vstate_ctrl_get_current()
+extern long riscv_v_vstate_ctrl_set_current(unsigned long arg);
+extern long riscv_v_vstate_ctrl_get_current(void);
+#endif /* CONFIG_RISCV_ISA_V */
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* _ASM_RISCV_PROCESSOR_H */
index 60f8ca0..a727be7 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/jump_label.h>
 #include <linux/sched/task_stack.h>
+#include <asm/vector.h>
 #include <asm/hwcap.h>
 #include <asm/processor.h>
 #include <asm/ptrace.h>
@@ -46,7 +47,7 @@ static inline void fstate_restore(struct task_struct *task,
        }
 }
 
-static inline void __switch_to_aux(struct task_struct *prev,
+static inline void __switch_to_fpu(struct task_struct *prev,
                                   struct task_struct *next)
 {
        struct pt_regs *regs;
@@ -66,7 +67,7 @@ static __always_inline bool has_fpu(void)
 static __always_inline bool has_fpu(void) { return false; }
 #define fstate_save(task, regs) do { } while (0)
 #define fstate_restore(task, regs) do { } while (0)
-#define __switch_to_aux(__prev, __next) do { } while (0)
+#define __switch_to_fpu(__prev, __next) do { } while (0)
 #endif
 
 extern struct task_struct *__switch_to(struct task_struct *,
@@ -77,7 +78,9 @@ do {                                                  \
        struct task_struct *__prev = (prev);            \
        struct task_struct *__next = (next);            \
        if (has_fpu())                                  \
-               __switch_to_aux(__prev, __next);        \
+               __switch_to_fpu(__prev, __next);        \
+       if (has_vector())                                       \
+               __switch_to_vector(__prev, __next);     \
        ((last) = __switch_to(__prev, __next));         \
 } while (0)
 
index e0d2021..97e6f65 100644 (file)
@@ -81,6 +81,9 @@ struct thread_info {
        .preempt_count  = INIT_PREEMPT_COUNT,   \
 }
 
+void arch_release_task_struct(struct task_struct *tsk);
+int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src);
+
 #endif /* !__ASSEMBLY__ */
 
 /*
diff --git a/arch/riscv/include/asm/vector.h b/arch/riscv/include/asm/vector.h
new file mode 100644 (file)
index 0000000..04c0b07
--- /dev/null
@@ -0,0 +1,184 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020 SiFive
+ */
+
+#ifndef __ASM_RISCV_VECTOR_H
+#define __ASM_RISCV_VECTOR_H
+
+#include <linux/types.h>
+#include <uapi/asm-generic/errno.h>
+
+#ifdef CONFIG_RISCV_ISA_V
+
+#include <linux/stringify.h>
+#include <linux/sched.h>
+#include <linux/sched/task_stack.h>
+#include <asm/ptrace.h>
+#include <asm/hwcap.h>
+#include <asm/csr.h>
+#include <asm/asm.h>
+
+extern unsigned long riscv_v_vsize;
+int riscv_v_setup_vsize(void);
+bool riscv_v_first_use_handler(struct pt_regs *regs);
+
+static __always_inline bool has_vector(void)
+{
+       return riscv_has_extension_unlikely(RISCV_ISA_EXT_v);
+}
+
+static inline void __riscv_v_vstate_clean(struct pt_regs *regs)
+{
+       regs->status = (regs->status & ~SR_VS) | SR_VS_CLEAN;
+}
+
+static inline void riscv_v_vstate_off(struct pt_regs *regs)
+{
+       regs->status = (regs->status & ~SR_VS) | SR_VS_OFF;
+}
+
+static inline void riscv_v_vstate_on(struct pt_regs *regs)
+{
+       regs->status = (regs->status & ~SR_VS) | SR_VS_INITIAL;
+}
+
+static inline bool riscv_v_vstate_query(struct pt_regs *regs)
+{
+       return (regs->status & SR_VS) != 0;
+}
+
+static __always_inline void riscv_v_enable(void)
+{
+       csr_set(CSR_SSTATUS, SR_VS);
+}
+
+static __always_inline void riscv_v_disable(void)
+{
+       csr_clear(CSR_SSTATUS, SR_VS);
+}
+
+static __always_inline void __vstate_csr_save(struct __riscv_v_ext_state *dest)
+{
+       asm volatile (
+               "csrr   %0, " __stringify(CSR_VSTART) "\n\t"
+               "csrr   %1, " __stringify(CSR_VTYPE) "\n\t"
+               "csrr   %2, " __stringify(CSR_VL) "\n\t"
+               "csrr   %3, " __stringify(CSR_VCSR) "\n\t"
+               : "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl),
+                 "=r" (dest->vcsr) : :);
+}
+
+static __always_inline void __vstate_csr_restore(struct __riscv_v_ext_state *src)
+{
+       asm volatile (
+               ".option push\n\t"
+               ".option arch, +v\n\t"
+               "vsetvl  x0, %2, %1\n\t"
+               ".option pop\n\t"
+               "csrw   " __stringify(CSR_VSTART) ", %0\n\t"
+               "csrw   " __stringify(CSR_VCSR) ", %3\n\t"
+               : : "r" (src->vstart), "r" (src->vtype), "r" (src->vl),
+                   "r" (src->vcsr) :);
+}
+
+static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
+                                        void *datap)
+{
+       unsigned long vl;
+
+       riscv_v_enable();
+       __vstate_csr_save(save_to);
+       asm volatile (
+               ".option push\n\t"
+               ".option arch, +v\n\t"
+               "vsetvli        %0, x0, e8, m8, ta, ma\n\t"
+               "vse8.v         v0, (%1)\n\t"
+               "add            %1, %1, %0\n\t"
+               "vse8.v         v8, (%1)\n\t"
+               "add            %1, %1, %0\n\t"
+               "vse8.v         v16, (%1)\n\t"
+               "add            %1, %1, %0\n\t"
+               "vse8.v         v24, (%1)\n\t"
+               ".option pop\n\t"
+               : "=&r" (vl) : "r" (datap) : "memory");
+       riscv_v_disable();
+}
+
+static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_from,
+                                           void *datap)
+{
+       unsigned long vl;
+
+       riscv_v_enable();
+       asm volatile (
+               ".option push\n\t"
+               ".option arch, +v\n\t"
+               "vsetvli        %0, x0, e8, m8, ta, ma\n\t"
+               "vle8.v         v0, (%1)\n\t"
+               "add            %1, %1, %0\n\t"
+               "vle8.v         v8, (%1)\n\t"
+               "add            %1, %1, %0\n\t"
+               "vle8.v         v16, (%1)\n\t"
+               "add            %1, %1, %0\n\t"
+               "vle8.v         v24, (%1)\n\t"
+               ".option pop\n\t"
+               : "=&r" (vl) : "r" (datap) : "memory");
+       __vstate_csr_restore(restore_from);
+       riscv_v_disable();
+}
+
+static inline void riscv_v_vstate_save(struct task_struct *task,
+                                      struct pt_regs *regs)
+{
+       if ((regs->status & SR_VS) == SR_VS_DIRTY) {
+               struct __riscv_v_ext_state *vstate = &task->thread.vstate;
+
+               __riscv_v_vstate_save(vstate, vstate->datap);
+               __riscv_v_vstate_clean(regs);
+       }
+}
+
+static inline void riscv_v_vstate_restore(struct task_struct *task,
+                                         struct pt_regs *regs)
+{
+       if ((regs->status & SR_VS) != SR_VS_OFF) {
+               struct __riscv_v_ext_state *vstate = &task->thread.vstate;
+
+               __riscv_v_vstate_restore(vstate, vstate->datap);
+               __riscv_v_vstate_clean(regs);
+       }
+}
+
+static inline void __switch_to_vector(struct task_struct *prev,
+                                     struct task_struct *next)
+{
+       struct pt_regs *regs;
+
+       regs = task_pt_regs(prev);
+       riscv_v_vstate_save(prev, regs);
+       riscv_v_vstate_restore(next, task_pt_regs(next));
+}
+
+void riscv_v_vstate_ctrl_init(struct task_struct *tsk);
+bool riscv_v_vstate_ctrl_user_allowed(void);
+
+#else /* ! CONFIG_RISCV_ISA_V  */
+
+struct pt_regs;
+
+static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; }
+static __always_inline bool has_vector(void) { return false; }
+static inline bool riscv_v_first_use_handler(struct pt_regs *regs) { return false; }
+static inline bool riscv_v_vstate_query(struct pt_regs *regs) { return false; }
+static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
+#define riscv_v_vsize (0)
+#define riscv_v_vstate_save(task, regs)                do {} while (0)
+#define riscv_v_vstate_restore(task, regs)     do {} while (0)
+#define __switch_to_vector(__prev, __next)     do {} while (0)
+#define riscv_v_vstate_off(regs)               do {} while (0)
+#define riscv_v_vstate_on(regs)                        do {} while (0)
+
+#endif /* CONFIG_RISCV_ISA_V */
+
+#endif /* ! __ASM_RISCV_VECTOR_H */
index fb187a3..10aaa83 100644 (file)
@@ -35,5 +35,6 @@
 
 /* entries in ARCH_DLINFO */
 #define AT_VECTOR_SIZE_ARCH    9
+#define AT_MINSIGSTKSZ         51
 
 #endif /* _UAPI_ASM_RISCV_AUXVEC_H */
index 46dc3f5..c52bb7b 100644 (file)
@@ -21,5 +21,6 @@
 #define COMPAT_HWCAP_ISA_F     (1 << ('F' - 'A'))
 #define COMPAT_HWCAP_ISA_D     (1 << ('D' - 'A'))
 #define COMPAT_HWCAP_ISA_C     (1 << ('C' - 'A'))
+#define COMPAT_HWCAP_ISA_V     (1 << ('V' - 'A'))
 
 #endif /* _UAPI_ASM_RISCV_HWCAP_H */
index 8d745a4..006bfb4 100644 (file)
@@ -25,6 +25,10 @@ struct riscv_hwprobe {
 #define RISCV_HWPROBE_KEY_IMA_EXT_0    4
 #define                RISCV_HWPROBE_IMA_FD            (1 << 0)
 #define                RISCV_HWPROBE_IMA_C             (1 << 1)
+#define                RISCV_HWPROBE_IMA_V             (1 << 2)
+#define                RISCV_HWPROBE_EXT_ZBA           (1 << 3)
+#define                RISCV_HWPROBE_EXT_ZBB           (1 << 4)
+#define                RISCV_HWPROBE_EXT_ZBS           (1 << 5)
 #define RISCV_HWPROBE_KEY_CPUPERF_0    5
 #define                RISCV_HWPROBE_MISALIGNED_UNKNOWN        (0 << 0)
 #define                RISCV_HWPROBE_MISALIGNED_EMULATED       (1 << 0)
index f92790c..855c047 100644 (file)
@@ -121,6 +121,7 @@ enum KVM_RISCV_ISA_EXT_ID {
        KVM_RISCV_ISA_EXT_ZICBOZ,
        KVM_RISCV_ISA_EXT_ZBB,
        KVM_RISCV_ISA_EXT_SSAIA,
+       KVM_RISCV_ISA_EXT_V,
        KVM_RISCV_ISA_EXT_MAX,
 };
 
@@ -203,6 +204,13 @@ enum KVM_RISCV_SBI_EXT_ID {
 #define KVM_REG_RISCV_SBI_MULTI_REG_LAST       \
                KVM_REG_RISCV_SBI_MULTI_REG(KVM_RISCV_SBI_EXT_MAX - 1)
 
+/* V extension registers are mapped as type 9 */
+#define KVM_REG_RISCV_VECTOR           (0x09 << KVM_REG_RISCV_TYPE_SHIFT)
+#define KVM_REG_RISCV_VECTOR_CSR_REG(name)     \
+               (offsetof(struct __riscv_v_ext_state, name) / sizeof(unsigned long))
+#define KVM_REG_RISCV_VECTOR_REG(n)    \
+               ((n) + sizeof(struct __riscv_v_ext_state) / sizeof(unsigned long))
+
 #endif
 
 #endif /* __LINUX_KVM_RISCV_H */
index 882547f..e17c550 100644 (file)
@@ -71,12 +71,51 @@ struct __riscv_q_ext_state {
        __u32 reserved[3];
 };
 
+struct __riscv_ctx_hdr {
+       __u32 magic;
+       __u32 size;
+};
+
+struct __riscv_extra_ext_header {
+       __u32 __padding[129] __attribute__((aligned(16)));
+       /*
+        * Reserved for expansion of sigcontext structure.  Currently zeroed
+        * upon signal, and must be zero upon sigreturn.
+        */
+       __u32 reserved;
+       struct __riscv_ctx_hdr hdr;
+};
+
 union __riscv_fp_state {
        struct __riscv_f_ext_state f;
        struct __riscv_d_ext_state d;
        struct __riscv_q_ext_state q;
 };
 
+struct __riscv_v_ext_state {
+       unsigned long vstart;
+       unsigned long vl;
+       unsigned long vtype;
+       unsigned long vcsr;
+       void *datap;
+       /*
+        * In signal handler, datap will be set a correct user stack offset
+        * and vector registers will be copied to the address of datap
+        * pointer.
+        *
+        * In ptrace syscall, datap will be set to zero and the vector
+        * registers will be copied to the address right after this
+        * structure.
+        */
+};
+
+/*
+ * According to spec: The number of bits in a single vector register,
+ * VLEN >= ELEN, which must be a power of 2, and must be no greater than
+ * 2^16 = 65536bits = 8192bytes
+ */
+#define RISCV_MAX_VLENB (8192)
+
 #endif /* __ASSEMBLY__ */
 
 #endif /* _UAPI_ASM_RISCV_PTRACE_H */
index 84f2dfc..8b8a854 100644 (file)
@@ -8,6 +8,17 @@
 
 #include <asm/ptrace.h>
 
+/* The Magic number for signal context frame header. */
+#define RISCV_V_MAGIC  0x53465457
+#define END_MAGIC      0x0
+
+/* The size of END signal context header. */
+#define END_HDR_SIZE   0x0
+
+struct __sc_riscv_v_state {
+       struct __riscv_v_ext_state v_state;
+} __attribute__((aligned(16)));
+
 /*
  * Signal context structure
  *
  */
 struct sigcontext {
        struct user_regs_struct sc_regs;
-       union __riscv_fp_state sc_fpregs;
+       union {
+               union __riscv_fp_state sc_fpregs;
+               struct __riscv_extra_ext_header sc_extdesc;
+       };
 };
 
 #endif /* _UAPI_ASM_RISCV_SIGCONTEXT_H */
index ed5fcd9..a429519 100644 (file)
@@ -56,6 +56,7 @@ obj-$(CONFIG_MMU) += vdso.o vdso/
 
 obj-$(CONFIG_RISCV_M_MODE)     += traps_misaligned.o
 obj-$(CONFIG_FPU)              += fpu.o
+obj-$(CONFIG_RISCV_ISA_V)      += vector.o
 obj-$(CONFIG_SMP)              += smpboot.o
 obj-$(CONFIG_SMP)              += smp.o
 obj-$(CONFIG_SMP)              += cpu_ops.o
index df5a45a..5ee03eb 100644 (file)
@@ -204,7 +204,7 @@ void __init __iomem *__acpi_map_table(unsigned long phys, unsigned long size)
        if (!size)
                return NULL;
 
-       return early_memremap(phys, size);
+       return early_ioremap(phys, size);
 }
 
 void __init __acpi_unmap_table(void __iomem *map, unsigned long size)
@@ -212,7 +212,7 @@ void __init __acpi_unmap_table(void __iomem *map, unsigned long size)
        if (!map || !size)
                return;
 
-       early_memunmap(map, size);
+       early_iounmap(map, size);
 }
 
 void *acpi_os_ioremap(acpi_physical_address phys, acpi_size size)
index 6aea641..a2fc952 100644 (file)
@@ -213,7 +213,9 @@ static struct riscv_isa_ext_data isa_ext_arr[] = {
        __RISCV_ISA_EXT_DATA(zifencei, RISCV_ISA_EXT_ZIFENCEI),
        __RISCV_ISA_EXT_DATA(zihintpause, RISCV_ISA_EXT_ZIHINTPAUSE),
        __RISCV_ISA_EXT_DATA(zihpm, RISCV_ISA_EXT_ZIHPM),
+       __RISCV_ISA_EXT_DATA(zba, RISCV_ISA_EXT_ZBA),
        __RISCV_ISA_EXT_DATA(zbb, RISCV_ISA_EXT_ZBB),
+       __RISCV_ISA_EXT_DATA(zbs, RISCV_ISA_EXT_ZBS),
        __RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
        __RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),
        __RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
index d21f7e8..bdcf460 100644 (file)
@@ -21,6 +21,7 @@
 #include <asm/hwcap.h>
 #include <asm/patch.h>
 #include <asm/processor.h>
+#include <asm/vector.h>
 
 #define NUM_ALPHA_EXTS ('z' - 'a' + 1)
 
@@ -29,6 +30,9 @@ unsigned long elf_hwcap __read_mostly;
 /* Host ISA bitmap */
 static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX) __read_mostly;
 
+/* Per-cpu ISA extensions. */
+struct riscv_isainfo hart_isa[NR_CPUS];
+
 /* Performance information */
 DEFINE_PER_CPU(long, misaligned_access_speed);
 
@@ -74,10 +78,10 @@ static bool riscv_isa_extension_check(int id)
        switch (id) {
        case RISCV_ISA_EXT_ZICBOM:
                if (!riscv_cbom_block_size) {
-                       pr_err("Zicbom detected in ISA string, but no cbom-block-size found\n");
+                       pr_err("Zicbom detected in ISA string, disabling as no cbom-block-size found\n");
                        return false;
                } else if (!is_power_of_2(riscv_cbom_block_size)) {
-                       pr_err("cbom-block-size present, but is not a power-of-2\n");
+                       pr_err("Zicbom disabled as cbom-block-size present, but is not a power-of-2\n");
                        return false;
                }
                return true;
@@ -112,6 +116,7 @@ void __init riscv_fill_hwcap(void)
        isa2hwcap['f' - 'a'] = COMPAT_HWCAP_ISA_F;
        isa2hwcap['d' - 'a'] = COMPAT_HWCAP_ISA_D;
        isa2hwcap['c' - 'a'] = COMPAT_HWCAP_ISA_C;
+       isa2hwcap['v' - 'a'] = COMPAT_HWCAP_ISA_V;
 
        elf_hwcap = 0;
 
@@ -124,8 +129,8 @@ void __init riscv_fill_hwcap(void)
        }
 
        for_each_possible_cpu(cpu) {
+               struct riscv_isainfo *isainfo = &hart_isa[cpu];
                unsigned long this_hwcap = 0;
-               DECLARE_BITMAP(this_isa, RISCV_ISA_EXT_MAX);
 
                if (acpi_disabled) {
                        node = of_cpu_device_node_get(cpu);
@@ -156,7 +161,6 @@ void __init riscv_fill_hwcap(void)
                 */
                isa += 4;
 
-               bitmap_zero(this_isa, RISCV_ISA_EXT_MAX);
                while (*isa) {
                        const char *ext = isa++;
                        const char *ext_end = isa;
@@ -282,7 +286,7 @@ void __init riscv_fill_hwcap(void)
                                if ((ext_end - ext == sizeof(name) - 1) &&              \
                                     !strncasecmp(ext, name, sizeof(name) - 1) &&       \
                                     riscv_isa_extension_check(bit))                    \
-                                       set_bit(bit, this_isa);                         \
+                                       set_bit(bit, isainfo->isa);                     \
                        } while (false)                                                 \
 
                        if (unlikely(ext_err))
@@ -292,7 +296,7 @@ void __init riscv_fill_hwcap(void)
 
                                if (riscv_isa_extension_check(nr)) {
                                        this_hwcap |= isa2hwcap[nr];
-                                       set_bit(nr, this_isa);
+                                       set_bit(nr, isainfo->isa);
                                }
                        } else {
                                /* sorted alphabetically */
@@ -303,7 +307,9 @@ void __init riscv_fill_hwcap(void)
                                SET_ISA_EXT_MAP("svinval", RISCV_ISA_EXT_SVINVAL);
                                SET_ISA_EXT_MAP("svnapot", RISCV_ISA_EXT_SVNAPOT);
                                SET_ISA_EXT_MAP("svpbmt", RISCV_ISA_EXT_SVPBMT);
+                               SET_ISA_EXT_MAP("zba", RISCV_ISA_EXT_ZBA);
                                SET_ISA_EXT_MAP("zbb", RISCV_ISA_EXT_ZBB);
+                               SET_ISA_EXT_MAP("zbs", RISCV_ISA_EXT_ZBS);
                                SET_ISA_EXT_MAP("zicbom", RISCV_ISA_EXT_ZICBOM);
                                SET_ISA_EXT_MAP("zicboz", RISCV_ISA_EXT_ZICBOZ);
                                SET_ISA_EXT_MAP("zihintpause", RISCV_ISA_EXT_ZIHINTPAUSE);
@@ -315,8 +321,8 @@ void __init riscv_fill_hwcap(void)
                 * Linux requires the following extensions, so we may as well
                 * always set them.
                 */
-               set_bit(RISCV_ISA_EXT_ZICSR, this_isa);
-               set_bit(RISCV_ISA_EXT_ZIFENCEI, this_isa);
+               set_bit(RISCV_ISA_EXT_ZICSR, isainfo->isa);
+               set_bit(RISCV_ISA_EXT_ZIFENCEI, isainfo->isa);
 
                /*
                 * These ones were as they were part of the base ISA when the
@@ -324,8 +330,8 @@ void __init riscv_fill_hwcap(void)
                 * unconditionally where `i` is in riscv,isa on DT systems.
                 */
                if (acpi_disabled) {
-                       set_bit(RISCV_ISA_EXT_ZICNTR, this_isa);
-                       set_bit(RISCV_ISA_EXT_ZIHPM, this_isa);
+                       set_bit(RISCV_ISA_EXT_ZICNTR, isainfo->isa);
+                       set_bit(RISCV_ISA_EXT_ZIHPM, isainfo->isa);
                }
 
                /*
@@ -339,9 +345,9 @@ void __init riscv_fill_hwcap(void)
                        elf_hwcap = this_hwcap;
 
                if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX))
-                       bitmap_copy(riscv_isa, this_isa, RISCV_ISA_EXT_MAX);
+                       bitmap_copy(riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX);
                else
-                       bitmap_and(riscv_isa, riscv_isa, this_isa, RISCV_ISA_EXT_MAX);
+                       bitmap_and(riscv_isa, riscv_isa, isainfo->isa, RISCV_ISA_EXT_MAX);
        }
 
        if (!acpi_disabled && rhct)
@@ -354,6 +360,17 @@ void __init riscv_fill_hwcap(void)
                elf_hwcap &= ~COMPAT_HWCAP_ISA_F;
        }
 
+       if (elf_hwcap & COMPAT_HWCAP_ISA_V) {
+               riscv_v_setup_vsize();
+               /*
+                * ISA string in device tree might have 'v' flag, but
+                * CONFIG_RISCV_ISA_V is disabled in kernel.
+                * Clear V flag in elf_hwcap if CONFIG_RISCV_ISA_V is disabled.
+                */
+               if (!IS_ENABLED(CONFIG_RISCV_ISA_V))
+                       elf_hwcap &= ~COMPAT_HWCAP_ISA_V;
+       }
+
        memset(print_str, 0, sizeof(print_str));
        for (i = 0, j = 0; i < NUM_ALPHA_EXTS; i++)
                if (riscv_isa[0] & BIT_MASK(i))
@@ -367,6 +384,18 @@ void __init riscv_fill_hwcap(void)
        pr_info("riscv: ELF capabilities %s\n", print_str);
 }
 
+unsigned long riscv_get_elf_hwcap(void)
+{
+       unsigned long hwcap;
+
+       hwcap = (elf_hwcap & ((1UL << RISCV_ISA_EXT_BASE) - 1));
+
+       if (!riscv_v_vstate_ctrl_user_allowed())
+               hwcap &= ~COMPAT_HWCAP_ISA_V;
+
+       return hwcap;
+}
+
 #ifdef CONFIG_RISCV_ALTERNATIVE
 /*
  * Alternative patch sites consider 48 bits when determining when to patch
index 3fbb100..143a2bb 100644 (file)
@@ -48,10 +48,10 @@ _save_context:
         * Disable user-mode memory access as it should only be set in the
         * actual user copy routines.
         *
-        * Disable the FPU to detect illegal usage of floating point in kernel
-        * space.
+        * Disable the FPU/Vector to detect illegal usage of floating point
+        * or vector in kernel space.
         */
-       li t0, SR_SUM | SR_FS
+       li t0, SR_SUM | SR_FS_VS
 
        REG_L s0, TASK_TI_USER_SP(tp)
        csrrc s1, CSR_STATUS, t0
@@ -348,6 +348,6 @@ SYM_CODE_END(excp_vect_table)
 #ifndef CONFIG_MMU
 SYM_CODE_START(__user_rt_sigreturn)
        li a7, __NR_rt_sigreturn
-       scall
+       ecall
 SYM_CODE_END(__user_rt_sigreturn)
 #endif
index 4bf6c44..11c3b94 100644 (file)
@@ -140,10 +140,10 @@ secondary_start_sbi:
        .option pop
 
        /*
-        * Disable FPU to detect illegal usage of
-        * floating point in kernel space
+        * Disable FPU & VECTOR to detect illegal usage of
+        * floating point or vector in kernel space
         */
-       li t0, SR_FS
+       li t0, SR_FS_VS
        csrc CSR_STATUS, t0
 
        /* Set trap vector to spin forever to help debug */
@@ -234,10 +234,10 @@ pmp_done:
 .option pop
 
        /*
-        * Disable FPU to detect illegal usage of
-        * floating point in kernel space
+        * Disable FPU & VECTOR to detect illegal usage of
+        * floating point or vector in kernel space
         */
-       li t0, SR_FS
+       li t0, SR_FS_VS
        csrc CSR_STATUS, t0
 
 #ifdef CONFIG_RISCV_BOOT_SPINWAIT
@@ -301,6 +301,7 @@ clear_bss_done:
        la tp, init_task
        la sp, init_thread_union + THREAD_SIZE
        XIP_FIXUP_OFFSET sp
+       addi sp, sp, -PT_SIZE_ON_STACK
 #ifdef CONFIG_BUILTIN_DTB
        la a0, __dtb_start
        XIP_FIXUP_OFFSET a0
@@ -318,6 +319,7 @@ clear_bss_done:
        /* Restore C environment */
        la tp, init_task
        la sp, init_thread_union + THREAD_SIZE
+       addi sp, sp, -PT_SIZE_ON_STACK
 
 #ifdef CONFIG_KASAN
        call kasan_early_init
@@ -392,7 +394,7 @@ ENTRY(reset_regs)
 #ifdef CONFIG_FPU
        csrr    t0, CSR_MISA
        andi    t0, t0, (COMPAT_HWCAP_ISA_F | COMPAT_HWCAP_ISA_D)
-       beqz    t0, .Lreset_regs_done
+       beqz    t0, .Lreset_regs_done_fpu
 
        li      t1, SR_FS
        csrs    CSR_STATUS, t1
@@ -430,8 +432,31 @@ ENTRY(reset_regs)
        fmv.s.x f31, zero
        csrw    fcsr, 0
        /* note that the caller must clear SR_FS */
+.Lreset_regs_done_fpu:
 #endif /* CONFIG_FPU */
-.Lreset_regs_done:
+
+#ifdef CONFIG_RISCV_ISA_V
+       csrr    t0, CSR_MISA
+       li      t1, COMPAT_HWCAP_ISA_V
+       and     t0, t0, t1
+       beqz    t0, .Lreset_regs_done_vector
+
+       /*
+        * Clear vector registers and reset vcsr
+        * VLMAX has a defined value, VLEN is a constant,
+        * and this form of vsetvli is defined to set vl to VLMAX.
+        */
+       li      t1, SR_VS
+       csrs    CSR_STATUS, t1
+       csrs    CSR_VCSR, x0
+       vsetvli t1, x0, e8, m8, ta, ma
+       vmv.v.i v0, 0
+       vmv.v.i v8, 0
+       vmv.v.i v16, 0
+       vmv.v.i v24, 0
+       /* note that the caller must clear SR_VS */
+.Lreset_regs_done_vector:
+#endif /* CONFIG_RISCV_ISA_V */
        ret
 END(reset_regs)
 #endif /* CONFIG_RISCV_M_MODE */
index effaf5c..d698dd7 100644 (file)
@@ -28,7 +28,6 @@ ENTRY(__hibernate_cpu_resume)
 
        REG_L   a0, hibernate_cpu_context
 
-       suspend_restore_csrs
        suspend_restore_regs
 
        /* Return zero value. */
@@ -50,7 +49,7 @@ ENTRY(hibernate_restore_image)
        REG_L   s4, restore_pblist
        REG_L   a1, relocated_restore_code
 
-       jalr    a1
+       jr      a1
 END(hibernate_restore_image)
 
 /*
@@ -73,5 +72,5 @@ ENTRY(hibernate_core_restore_code)
        REG_L   s4, HIBERN_PBE_NEXT(s4)
        bnez    s4, .Lcopy
 
-       jalr    s2
+       jr      s2
 END(hibernate_core_restore_code)
index c976a21..194f166 100644 (file)
@@ -67,6 +67,7 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
        struct uprobe_task *utask = current->utask;
 
        WARN_ON_ONCE(current->thread.bad_cause != UPROBE_TRAP_NR);
+       current->thread.bad_cause = utask->autask.saved_cause;
 
        instruction_pointer_set(regs, utask->vaddr + auprobe->insn_size);
 
@@ -102,6 +103,7 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
 {
        struct uprobe_task *utask = current->utask;
 
+       current->thread.bad_cause = utask->autask.saved_cause;
        /*
         * Task has received a fatal signal, so reset back to probbed
         * address.
index e2a0600..e32d737 100644 (file)
@@ -24,6 +24,7 @@
 #include <asm/switch_to.h>
 #include <asm/thread_info.h>
 #include <asm/cpuidle.h>
+#include <asm/vector.h>
 
 register unsigned long gp_in_global __asm__("gp");
 
@@ -146,12 +147,29 @@ void flush_thread(void)
        fstate_off(current, task_pt_regs(current));
        memset(&current->thread.fstate, 0, sizeof(current->thread.fstate));
 #endif
+#ifdef CONFIG_RISCV_ISA_V
+       /* Reset vector state */
+       riscv_v_vstate_ctrl_init(current);
+       riscv_v_vstate_off(task_pt_regs(current));
+       kfree(current->thread.vstate.datap);
+       memset(&current->thread.vstate, 0, sizeof(struct __riscv_v_ext_state));
+#endif
+}
+
+void arch_release_task_struct(struct task_struct *tsk)
+{
+       /* Free the vector context of datap. */
+       if (has_vector())
+               kfree(tsk->thread.vstate.datap);
 }
 
 int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
 {
        fstate_save(src, task_pt_regs(src));
        *dst = *src;
+       /* clear entire V context, including datap for a new task */
+       memset(&dst->thread.vstate, 0, sizeof(struct __riscv_v_ext_state));
+
        return 0;
 }
 
@@ -176,6 +194,8 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
                p->thread.s[1] = (unsigned long)args->fn_arg;
        } else {
                *childregs = *(current_pt_regs());
+               /* Turn off status.VS */
+               riscv_v_vstate_off(childregs);
                if (usp) /* User fork */
                        childregs->sp = usp;
                if (clone_flags & CLONE_SETTLS)
index 23c48b1..1d572cf 100644 (file)
@@ -7,6 +7,7 @@
  * Copied from arch/tile/kernel/ptrace.c
  */
 
+#include <asm/vector.h>
 #include <asm/ptrace.h>
 #include <asm/syscall.h>
 #include <asm/thread_info.h>
@@ -24,6 +25,9 @@ enum riscv_regset {
 #ifdef CONFIG_FPU
        REGSET_F,
 #endif
+#ifdef CONFIG_RISCV_ISA_V
+       REGSET_V,
+#endif
 };
 
 static int riscv_gpr_get(struct task_struct *target,
@@ -80,6 +84,61 @@ static int riscv_fpr_set(struct task_struct *target,
 }
 #endif
 
+#ifdef CONFIG_RISCV_ISA_V
+static int riscv_vr_get(struct task_struct *target,
+                       const struct user_regset *regset,
+                       struct membuf to)
+{
+       struct __riscv_v_ext_state *vstate = &target->thread.vstate;
+
+       if (!riscv_v_vstate_query(task_pt_regs(target)))
+               return -EINVAL;
+
+       /*
+        * Ensure the vector registers have been saved to the memory before
+        * copying them to membuf.
+        */
+       if (target == current)
+               riscv_v_vstate_save(current, task_pt_regs(current));
+
+       /* Copy vector header from vstate. */
+       membuf_write(&to, vstate, offsetof(struct __riscv_v_ext_state, datap));
+       membuf_zero(&to, sizeof(vstate->datap));
+
+       /* Copy all the vector registers from vstate. */
+       return membuf_write(&to, vstate->datap, riscv_v_vsize);
+}
+
+static int riscv_vr_set(struct task_struct *target,
+                       const struct user_regset *regset,
+                       unsigned int pos, unsigned int count,
+                       const void *kbuf, const void __user *ubuf)
+{
+       int ret, size;
+       struct __riscv_v_ext_state *vstate = &target->thread.vstate;
+
+       if (!riscv_v_vstate_query(task_pt_regs(target)))
+               return -EINVAL;
+
+       /* Copy rest of the vstate except datap */
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate, 0,
+                                offsetof(struct __riscv_v_ext_state, datap));
+       if (unlikely(ret))
+               return ret;
+
+       /* Skip copy datap. */
+       size = sizeof(vstate->datap);
+       count -= size;
+       ubuf += size;
+
+       /* Copy all the vector registers. */
+       pos = 0;
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate->datap,
+                                0, riscv_v_vsize);
+       return ret;
+}
+#endif
+
 static const struct user_regset riscv_user_regset[] = {
        [REGSET_X] = {
                .core_note_type = NT_PRSTATUS,
@@ -99,6 +158,17 @@ static const struct user_regset riscv_user_regset[] = {
                .set = riscv_fpr_set,
        },
 #endif
+#ifdef CONFIG_RISCV_ISA_V
+       [REGSET_V] = {
+               .core_note_type = NT_RISCV_VECTOR,
+               .align = 16,
+               .n = ((32 * RISCV_MAX_VLENB) +
+                     sizeof(struct __riscv_v_ext_state)) / sizeof(__u32),
+               .size = sizeof(__u32),
+               .regset_get = riscv_vr_get,
+               .set = riscv_vr_set,
+       },
+#endif
 };
 
 static const struct user_regset_view riscv_user_native_view = {
index 2ab4cda..971fe77 100644 (file)
@@ -264,6 +264,8 @@ static void __init parse_dtb(void)
 #endif
 }
 
+extern void __init init_rt_signal_env(void);
+
 void __init setup_arch(char **cmdline_p)
 {
        parse_dtb();
@@ -304,6 +306,7 @@ void __init setup_arch(char **cmdline_p)
 
        riscv_init_cbo_blocksizes();
        riscv_fill_hwcap();
+       init_rt_signal_env();
        apply_boot_alternatives();
        if (IS_ENABLED(CONFIG_RISCV_ISA_ZICBOM) &&
            riscv_isa_extension_available(NULL, ZICBOM))
index 9aff9d7..180d951 100644 (file)
 #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>
 
+unsigned long signal_minsigstksz __ro_after_init;
+
 extern u32 __user_rt_sigreturn[2];
+static size_t riscv_v_sc_size __ro_after_init;
 
 #define DEBUG_SIG 0
 
@@ -40,26 +44,13 @@ static long restore_fp_state(struct pt_regs *regs,
 {
        long err;
        struct __riscv_d_ext_state __user *state = &sc_fpregs->d;
-       size_t i;
 
        err = __copy_from_user(&current->thread.fstate, state, sizeof(*state));
        if (unlikely(err))
                return err;
 
        fstate_restore(current, regs);
-
-       /* We support no other extension state at this time. */
-       for (i = 0; i < ARRAY_SIZE(sc_fpregs->q.reserved); i++) {
-               u32 value;
-
-               err = __get_user(value, &sc_fpregs->q.reserved[i]);
-               if (unlikely(err))
-                       break;
-               if (value != 0)
-                       return -EINVAL;
-       }
-
-       return err;
+       return 0;
 }
 
 static long save_fp_state(struct pt_regs *regs,
@@ -67,52 +58,186 @@ static long save_fp_state(struct pt_regs *regs,
 {
        long err;
        struct __riscv_d_ext_state __user *state = &sc_fpregs->d;
-       size_t i;
 
        fstate_save(current, regs);
        err = __copy_to_user(state, &current->thread.fstate, sizeof(*state));
+       return err;
+}
+#else
+#define save_fp_state(task, regs) (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, &current->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;
 
-       /* We support no other extension state at this time. */
-       for (i = 0; i < ARRAY_SIZE(sc_fpregs->q.reserved); i++) {
-               err = __put_user(0, &sc_fpregs->q.reserved[i]);
-               if (unlikely(err))
-                       break;
-       }
+       /* 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(&current->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_fp_state(task, regs) (0)
-#define restore_fp_state(task, regs) (0)
+#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;
        /* sc_regs is structured the same as the start of pt_regs */
        err = __copy_from_user(regs, &sc->sc_regs, sizeof(sc->sc_regs));
+       if (unlikely(err))
+               return err;
+
        /* Restore the floating-point state. */
-       if (has_fpu())
-               err |= restore_fp_state(regs, &sc->sc_fpregs);
+       if (has_fpu()) {
+               err = restore_fp_state(regs, &sc->sc_fpregs);
+               if (unlikely(err))
+                       return err;
+       }
+
+       /* 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(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;
+               default:
+                       return -EINVAL;
+               }
+               sc_ext_ptr = (void __user *)head + size;
+       }
        return err;
 }
 
+static size_t get_rt_frame_size(bool cal_all)
+{
+       struct rt_sigframe __user *frame;
+       size_t frame_size;
+       size_t total_context_size = 0;
+
+       frame_size = sizeof(*frame);
+
+       if (has_vector()) {
+               if (cal_all || 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(false);
 
        /* 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)))
@@ -146,12 +271,23 @@ static long setup_sigcontext(struct rt_sigframe __user *frame,
        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;
+
        /* 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);
+       /* 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;
 }
 
@@ -175,6 +311,13 @@ static inline void __user *get_sigframe(struct ksignal *ksig,
        /* 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;
 }
 
@@ -184,9 +327,10 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
        struct rt_sigframe __user *frame;
        long err = 0;
        unsigned long __maybe_unused addr;
+       size_t frame_size = get_rt_frame_size(false);
 
-       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);
@@ -319,3 +463,23 @@ void arch_do_signal_or_restart(struct pt_regs *regs)
         */
        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;
+       /*
+        * Determine the stack space required for guaranteed signal delivery.
+        * The signal_minsigstksz will be populated into the AT_MINSIGSTKSZ entry
+        * in the auxiliary array at process startup.
+        */
+       signal_minsigstksz = get_rt_frame_size(true);
+}
+
+#ifdef CONFIG_DYNAMIC_SIGFRAME
+bool sigaltstack_size_valid(size_t ss_size)
+{
+       return ss_size > get_rt_frame_size(false);
+}
+#endif /* CONFIG_DYNAMIC_SIGFRAME */
index 3f42331..bb0b76e 100644 (file)
@@ -32,6 +32,8 @@
 #include <asm/tlbflush.h>
 #include <asm/sections.h>
 #include <asm/smp.h>
+#include <uapi/asm/hwcap.h>
+#include <asm/vector.h>
 
 #include "head.h"
 
@@ -244,6 +246,11 @@ asmlinkage __visible void smp_callin(void)
        set_cpu_online(curr_cpuid, 1);
        probe_vendor_features(curr_cpuid);
 
+       if (has_vector()) {
+               if (riscv_v_setup_vsize())
+                       elf_hwcap &= ~COMPAT_HWCAP_ISA_V;
+       }
+
        /*
         * Remote TLB flushes are ignored while the CPU is offline, so emit
         * a local TLB flush right now just in case.
index 5db2968..26ef552 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/cpufeature.h>
 #include <asm/hwprobe.h>
 #include <asm/sbi.h>
+#include <asm/vector.h>
 #include <asm/switch_to.h>
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -121,6 +122,49 @@ static void hwprobe_arch_id(struct riscv_hwprobe *pair,
        pair->value = id;
 }
 
+static void hwprobe_isa_ext0(struct riscv_hwprobe *pair,
+                            const struct cpumask *cpus)
+{
+       int cpu;
+       u64 missing = 0;
+
+       pair->value = 0;
+       if (has_fpu())
+               pair->value |= RISCV_HWPROBE_IMA_FD;
+
+       if (riscv_isa_extension_available(NULL, c))
+               pair->value |= RISCV_HWPROBE_IMA_C;
+
+       if (has_vector())
+               pair->value |= RISCV_HWPROBE_IMA_V;
+
+       /*
+        * Loop through and record extensions that 1) anyone has, and 2) anyone
+        * doesn't have.
+        */
+       for_each_cpu(cpu, cpus) {
+               struct riscv_isainfo *isainfo = &hart_isa[cpu];
+
+               if (riscv_isa_extension_available(isainfo->isa, ZBA))
+                       pair->value |= RISCV_HWPROBE_EXT_ZBA;
+               else
+                       missing |= RISCV_HWPROBE_EXT_ZBA;
+
+               if (riscv_isa_extension_available(isainfo->isa, ZBB))
+                       pair->value |= RISCV_HWPROBE_EXT_ZBB;
+               else
+                       missing |= RISCV_HWPROBE_EXT_ZBB;
+
+               if (riscv_isa_extension_available(isainfo->isa, ZBS))
+                       pair->value |= RISCV_HWPROBE_EXT_ZBS;
+               else
+                       missing |= RISCV_HWPROBE_EXT_ZBS;
+       }
+
+       /* Now turn off reporting features if any CPU is missing it. */
+       pair->value &= ~missing;
+}
+
 static u64 hwprobe_misaligned(const struct cpumask *cpus)
 {
        int cpu;
@@ -164,13 +208,7 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair,
                break;
 
        case RISCV_HWPROBE_KEY_IMA_EXT_0:
-               pair->value = 0;
-               if (has_fpu())
-                       pair->value |= RISCV_HWPROBE_IMA_FD;
-
-               if (riscv_isa_extension_available(NULL, c))
-                       pair->value |= RISCV_HWPROBE_IMA_C;
-
+               hwprobe_isa_ext0(pair, cpus);
                break;
 
        case RISCV_HWPROBE_KEY_CPUPERF_0:
index 8c258b7..05ffdcd 100644 (file)
@@ -26,6 +26,7 @@
 #include <asm/ptrace.h>
 #include <asm/syscall.h>
 #include <asm/thread_info.h>
+#include <asm/vector.h>
 
 int show_unhandled_signals = 1;
 
@@ -145,8 +146,29 @@ DO_ERROR_INFO(do_trap_insn_misaligned,
        SIGBUS, BUS_ADRALN, "instruction address misaligned");
 DO_ERROR_INFO(do_trap_insn_fault,
        SIGSEGV, SEGV_ACCERR, "instruction access fault");
-DO_ERROR_INFO(do_trap_insn_illegal,
-       SIGILL, ILL_ILLOPC, "illegal instruction");
+
+asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *regs)
+{
+       if (user_mode(regs)) {
+               irqentry_enter_from_user_mode(regs);
+
+               local_irq_enable();
+
+               if (!riscv_v_first_use_handler(regs))
+                       do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc,
+                                     "Oops - illegal instruction");
+
+               irqentry_exit_to_user_mode(regs);
+       } else {
+               irqentry_state_t state = irqentry_nmi_enter(regs);
+
+               do_trap_error(regs, SIGILL, ILL_ILLOPC, regs->epc,
+                             "Oops - illegal instruction");
+
+               irqentry_nmi_exit(regs, state);
+       }
+}
+
 DO_ERROR_INFO(do_trap_load_fault,
        SIGSEGV, SEGV_ACCERR, "load access fault");
 #ifndef CONFIG_RISCV_M_MODE
index 0573705..10438c7 100644 (file)
@@ -11,6 +11,6 @@ ENTRY(__vdso_rt_sigreturn)
        .cfi_startproc
        .cfi_signal_frame
        li a7, __NR_rt_sigreturn
-       scall
+       ecall
        .cfi_endproc
 ENDPROC(__vdso_rt_sigreturn)
diff --git a/arch/riscv/kernel/vector.c b/arch/riscv/kernel/vector.c
new file mode 100644 (file)
index 0000000..f9c8e19
--- /dev/null
@@ -0,0 +1,276 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2023 SiFive
+ * Author: Andy Chiu <andy.chiu@sifive.com>
+ */
+#include <linux/export.h>
+#include <linux/sched/signal.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/prctl.h>
+
+#include <asm/thread_info.h>
+#include <asm/processor.h>
+#include <asm/insn.h>
+#include <asm/vector.h>
+#include <asm/csr.h>
+#include <asm/elf.h>
+#include <asm/ptrace.h>
+#include <asm/bug.h>
+
+static bool riscv_v_implicit_uacc = IS_ENABLED(CONFIG_RISCV_ISA_V_DEFAULT_ENABLE);
+
+unsigned long riscv_v_vsize __read_mostly;
+EXPORT_SYMBOL_GPL(riscv_v_vsize);
+
+int riscv_v_setup_vsize(void)
+{
+       unsigned long this_vsize;
+
+       /* There are 32 vector registers with vlenb length. */
+       riscv_v_enable();
+       this_vsize = csr_read(CSR_VLENB) * 32;
+       riscv_v_disable();
+
+       if (!riscv_v_vsize) {
+               riscv_v_vsize = this_vsize;
+               return 0;
+       }
+
+       if (riscv_v_vsize != this_vsize) {
+               WARN(1, "RISCV_ISA_V only supports one vlenb on SMP systems");
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static bool insn_is_vector(u32 insn_buf)
+{
+       u32 opcode = insn_buf & __INSN_OPCODE_MASK;
+       u32 width, csr;
+
+       /*
+        * All V-related instructions, including CSR operations are 4-Byte. So,
+        * do not handle if the instruction length is not 4-Byte.
+        */
+       if (unlikely(GET_INSN_LENGTH(insn_buf) != 4))
+               return false;
+
+       switch (opcode) {
+       case RVV_OPCODE_VECTOR:
+               return true;
+       case RVV_OPCODE_VL:
+       case RVV_OPCODE_VS:
+               width = RVV_EXRACT_VL_VS_WIDTH(insn_buf);
+               if (width == RVV_VL_VS_WIDTH_8 || width == RVV_VL_VS_WIDTH_16 ||
+                   width == RVV_VL_VS_WIDTH_32 || width == RVV_VL_VS_WIDTH_64)
+                       return true;
+
+               break;
+       case RVG_OPCODE_SYSTEM:
+               csr = RVG_EXTRACT_SYSTEM_CSR(insn_buf);
+               if ((csr >= CSR_VSTART && csr <= CSR_VCSR) ||
+                   (csr >= CSR_VL && csr <= CSR_VLENB))
+                       return true;
+       }
+
+       return false;
+}
+
+static int riscv_v_thread_zalloc(void)
+{
+       void *datap;
+
+       datap = kzalloc(riscv_v_vsize, GFP_KERNEL);
+       if (!datap)
+               return -ENOMEM;
+
+       current->thread.vstate.datap = datap;
+       memset(&current->thread.vstate, 0, offsetof(struct __riscv_v_ext_state,
+                                                   datap));
+       return 0;
+}
+
+#define VSTATE_CTRL_GET_CUR(x) ((x) & PR_RISCV_V_VSTATE_CTRL_CUR_MASK)
+#define VSTATE_CTRL_GET_NEXT(x) (((x) & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK) >> 2)
+#define VSTATE_CTRL_MAKE_NEXT(x) (((x) << 2) & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK)
+#define VSTATE_CTRL_GET_INHERIT(x) (!!((x) & PR_RISCV_V_VSTATE_CTRL_INHERIT))
+static inline int riscv_v_ctrl_get_cur(struct task_struct *tsk)
+{
+       return VSTATE_CTRL_GET_CUR(tsk->thread.vstate_ctrl);
+}
+
+static inline int riscv_v_ctrl_get_next(struct task_struct *tsk)
+{
+       return VSTATE_CTRL_GET_NEXT(tsk->thread.vstate_ctrl);
+}
+
+static inline bool riscv_v_ctrl_test_inherit(struct task_struct *tsk)
+{
+       return VSTATE_CTRL_GET_INHERIT(tsk->thread.vstate_ctrl);
+}
+
+static inline void riscv_v_ctrl_set(struct task_struct *tsk, int cur, int nxt,
+                                   bool inherit)
+{
+       unsigned long ctrl;
+
+       ctrl = cur & PR_RISCV_V_VSTATE_CTRL_CUR_MASK;
+       ctrl |= VSTATE_CTRL_MAKE_NEXT(nxt);
+       if (inherit)
+               ctrl |= PR_RISCV_V_VSTATE_CTRL_INHERIT;
+       tsk->thread.vstate_ctrl = ctrl;
+}
+
+bool riscv_v_vstate_ctrl_user_allowed(void)
+{
+       return riscv_v_ctrl_get_cur(current) == PR_RISCV_V_VSTATE_CTRL_ON;
+}
+EXPORT_SYMBOL_GPL(riscv_v_vstate_ctrl_user_allowed);
+
+bool riscv_v_first_use_handler(struct pt_regs *regs)
+{
+       u32 __user *epc = (u32 __user *)regs->epc;
+       u32 insn = (u32)regs->badaddr;
+
+       /* Do not handle if V is not supported, or disabled */
+       if (!(ELF_HWCAP & COMPAT_HWCAP_ISA_V))
+               return false;
+
+       /* If V has been enabled then it is not the first-use trap */
+       if (riscv_v_vstate_query(regs))
+               return false;
+
+       /* Get the instruction */
+       if (!insn) {
+               if (__get_user(insn, epc))
+                       return false;
+       }
+
+       /* Filter out non-V instructions */
+       if (!insn_is_vector(insn))
+               return false;
+
+       /* Sanity check. datap should be null by the time of the first-use trap */
+       WARN_ON(current->thread.vstate.datap);
+
+       /*
+        * Now we sure that this is a V instruction. And it executes in the
+        * context where VS has been off. So, try to allocate the user's V
+        * context and resume execution.
+        */
+       if (riscv_v_thread_zalloc()) {
+               force_sig(SIGBUS);
+               return true;
+       }
+       riscv_v_vstate_on(regs);
+       return true;
+}
+
+void riscv_v_vstate_ctrl_init(struct task_struct *tsk)
+{
+       bool inherit;
+       int cur, next;
+
+       if (!has_vector())
+               return;
+
+       next = riscv_v_ctrl_get_next(tsk);
+       if (!next) {
+               if (READ_ONCE(riscv_v_implicit_uacc))
+                       cur = PR_RISCV_V_VSTATE_CTRL_ON;
+               else
+                       cur = PR_RISCV_V_VSTATE_CTRL_OFF;
+       } else {
+               cur = next;
+       }
+       /* Clear next mask if inherit-bit is not set */
+       inherit = riscv_v_ctrl_test_inherit(tsk);
+       if (!inherit)
+               next = PR_RISCV_V_VSTATE_CTRL_DEFAULT;
+
+       riscv_v_ctrl_set(tsk, cur, next, inherit);
+}
+
+long riscv_v_vstate_ctrl_get_current(void)
+{
+       if (!has_vector())
+               return -EINVAL;
+
+       return current->thread.vstate_ctrl & PR_RISCV_V_VSTATE_CTRL_MASK;
+}
+
+long riscv_v_vstate_ctrl_set_current(unsigned long arg)
+{
+       bool inherit;
+       int cur, next;
+
+       if (!has_vector())
+               return -EINVAL;
+
+       if (arg & ~PR_RISCV_V_VSTATE_CTRL_MASK)
+               return -EINVAL;
+
+       cur = VSTATE_CTRL_GET_CUR(arg);
+       switch (cur) {
+       case PR_RISCV_V_VSTATE_CTRL_OFF:
+               /* Do not allow user to turn off V if current is not off */
+               if (riscv_v_ctrl_get_cur(current) != PR_RISCV_V_VSTATE_CTRL_OFF)
+                       return -EPERM;
+
+               break;
+       case PR_RISCV_V_VSTATE_CTRL_ON:
+               break;
+       case PR_RISCV_V_VSTATE_CTRL_DEFAULT:
+               cur = riscv_v_ctrl_get_cur(current);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       next = VSTATE_CTRL_GET_NEXT(arg);
+       inherit = VSTATE_CTRL_GET_INHERIT(arg);
+       switch (next) {
+       case PR_RISCV_V_VSTATE_CTRL_DEFAULT:
+       case PR_RISCV_V_VSTATE_CTRL_OFF:
+       case PR_RISCV_V_VSTATE_CTRL_ON:
+               riscv_v_ctrl_set(current, cur, next, inherit);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+#ifdef CONFIG_SYSCTL
+
+static struct ctl_table riscv_v_default_vstate_table[] = {
+       {
+               .procname       = "riscv_v_default_allow",
+               .data           = &riscv_v_implicit_uacc,
+               .maxlen         = sizeof(riscv_v_implicit_uacc),
+               .mode           = 0644,
+               .proc_handler   = proc_dobool,
+       },
+       { }
+};
+
+static int __init riscv_v_sysctl_init(void)
+{
+       if (has_vector())
+               if (!register_sysctl("abi", riscv_v_default_vstate_table))
+                       return -EINVAL;
+       return 0;
+}
+
+#else /* ! CONFIG_SYSCTL */
+static int __init riscv_v_sysctl_init(void) { return 0; }
+#endif /* ! CONFIG_SYSCTL */
+
+static int riscv_v_init(void)
+{
+       return riscv_v_sysctl_init();
+}
+core_initcall(riscv_v_init);
index 8031b89..7b4c21f 100644 (file)
@@ -17,6 +17,7 @@ kvm-y += mmu.o
 kvm-y += vcpu.o
 kvm-y += vcpu_exit.o
 kvm-y += vcpu_fp.o
+kvm-y += vcpu_vector.o
 kvm-y += vcpu_insn.o
 kvm-y += vcpu_switch.o
 kvm-y += vcpu_sbi.o
index 8bd9f2a..de24127 100644 (file)
@@ -22,6 +22,8 @@
 #include <asm/cacheflush.h>
 #include <asm/hwcap.h>
 #include <asm/sbi.h>
+#include <asm/vector.h>
+#include <asm/kvm_vcpu_vector.h>
 
 const struct _kvm_stats_desc kvm_vcpu_stats_desc[] = {
        KVM_GENERIC_VCPU_STATS(),
@@ -57,6 +59,7 @@ static const unsigned long kvm_isa_ext_arr[] = {
        [KVM_RISCV_ISA_EXT_H] = RISCV_ISA_EXT_h,
        [KVM_RISCV_ISA_EXT_I] = RISCV_ISA_EXT_i,
        [KVM_RISCV_ISA_EXT_M] = RISCV_ISA_EXT_m,
+       [KVM_RISCV_ISA_EXT_V] = RISCV_ISA_EXT_v,
 
        KVM_ISA_EXT_ARR(SSAIA),
        KVM_ISA_EXT_ARR(SSTC),
@@ -85,6 +88,8 @@ static bool kvm_riscv_vcpu_isa_enable_allowed(unsigned long ext)
        switch (ext) {
        case KVM_RISCV_ISA_EXT_H:
                return false;
+       case KVM_RISCV_ISA_EXT_V:
+               return riscv_v_vstate_ctrl_user_allowed();
        default:
                break;
        }
@@ -138,6 +143,8 @@ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu)
 
        kvm_riscv_vcpu_fp_reset(vcpu);
 
+       kvm_riscv_vcpu_vector_reset(vcpu);
+
        kvm_riscv_vcpu_timer_reset(vcpu);
 
        kvm_riscv_vcpu_aia_reset(vcpu);
@@ -198,6 +205,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
        cntx->hstatus |= HSTATUS_SPVP;
        cntx->hstatus |= HSTATUS_SPV;
 
+       if (kvm_riscv_vcpu_alloc_vector_context(vcpu, cntx))
+               return -ENOMEM;
+
        /* By default, make CY, TM, and IR counters accessible in VU mode */
        reset_csr->scounteren = 0x7;
 
@@ -241,6 +251,9 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
 
        /* Free unused pages pre-allocated for G-stage page table mappings */
        kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
+
+       /* Free vector context space for host and guest kernel */
+       kvm_riscv_vcpu_free_vector_context(vcpu);
 }
 
 int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
@@ -679,6 +692,9 @@ static int kvm_riscv_vcpu_set_reg(struct kvm_vcpu *vcpu,
                return kvm_riscv_vcpu_set_reg_isa_ext(vcpu, reg);
        case KVM_REG_RISCV_SBI_EXT:
                return kvm_riscv_vcpu_set_reg_sbi_ext(vcpu, reg);
+       case KVM_REG_RISCV_VECTOR:
+               return kvm_riscv_vcpu_set_reg_vector(vcpu, reg,
+                                                KVM_REG_RISCV_VECTOR);
        default:
                break;
        }
@@ -708,6 +724,9 @@ static int kvm_riscv_vcpu_get_reg(struct kvm_vcpu *vcpu,
                return kvm_riscv_vcpu_get_reg_isa_ext(vcpu, reg);
        case KVM_REG_RISCV_SBI_EXT:
                return kvm_riscv_vcpu_get_reg_sbi_ext(vcpu, reg);
+       case KVM_REG_RISCV_VECTOR:
+               return kvm_riscv_vcpu_get_reg_vector(vcpu, reg,
+                                                KVM_REG_RISCV_VECTOR);
        default:
                break;
        }
@@ -1002,6 +1021,9 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
        kvm_riscv_vcpu_host_fp_save(&vcpu->arch.host_context);
        kvm_riscv_vcpu_guest_fp_restore(&vcpu->arch.guest_context,
                                        vcpu->arch.isa);
+       kvm_riscv_vcpu_host_vector_save(&vcpu->arch.host_context);
+       kvm_riscv_vcpu_guest_vector_restore(&vcpu->arch.guest_context,
+                                           vcpu->arch.isa);
 
        kvm_riscv_vcpu_aia_load(vcpu, cpu);
 
@@ -1021,6 +1043,9 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
        kvm_riscv_vcpu_host_fp_restore(&vcpu->arch.host_context);
 
        kvm_riscv_vcpu_timer_save(vcpu);
+       kvm_riscv_vcpu_guest_vector_save(&vcpu->arch.guest_context,
+                                        vcpu->arch.isa);
+       kvm_riscv_vcpu_host_vector_restore(&vcpu->arch.host_context);
 
        csr->vsstatus = csr_read(CSR_VSSTATUS);
        csr->vsie = csr_read(CSR_VSIE);
diff --git a/arch/riscv/kvm/vcpu_vector.c b/arch/riscv/kvm/vcpu_vector.c
new file mode 100644 (file)
index 0000000..edd2eec
--- /dev/null
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 SiFive
+ *
+ * Authors:
+ *     Vincent Chen <vincent.chen@sifive.com>
+ *     Greentime Hu <greentime.hu@sifive.com>
+ */
+
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/kvm_host.h>
+#include <linux/uaccess.h>
+#include <asm/hwcap.h>
+#include <asm/kvm_vcpu_vector.h>
+#include <asm/vector.h>
+
+#ifdef CONFIG_RISCV_ISA_V
+void kvm_riscv_vcpu_vector_reset(struct kvm_vcpu *vcpu)
+{
+       unsigned long *isa = vcpu->arch.isa;
+       struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
+
+       cntx->sstatus &= ~SR_VS;
+       if (riscv_isa_extension_available(isa, v)) {
+               cntx->sstatus |= SR_VS_INITIAL;
+               WARN_ON(!cntx->vector.datap);
+               memset(cntx->vector.datap, 0, riscv_v_vsize);
+       } else {
+               cntx->sstatus |= SR_VS_OFF;
+       }
+}
+
+static void kvm_riscv_vcpu_vector_clean(struct kvm_cpu_context *cntx)
+{
+       cntx->sstatus &= ~SR_VS;
+       cntx->sstatus |= SR_VS_CLEAN;
+}
+
+void kvm_riscv_vcpu_guest_vector_save(struct kvm_cpu_context *cntx,
+                                     unsigned long *isa)
+{
+       if ((cntx->sstatus & SR_VS) == SR_VS_DIRTY) {
+               if (riscv_isa_extension_available(isa, v))
+                       __kvm_riscv_vector_save(cntx);
+               kvm_riscv_vcpu_vector_clean(cntx);
+       }
+}
+
+void kvm_riscv_vcpu_guest_vector_restore(struct kvm_cpu_context *cntx,
+                                        unsigned long *isa)
+{
+       if ((cntx->sstatus & SR_VS) != SR_VS_OFF) {
+               if (riscv_isa_extension_available(isa, v))
+                       __kvm_riscv_vector_restore(cntx);
+               kvm_riscv_vcpu_vector_clean(cntx);
+       }
+}
+
+void kvm_riscv_vcpu_host_vector_save(struct kvm_cpu_context *cntx)
+{
+       /* No need to check host sstatus as it can be modified outside */
+       if (riscv_isa_extension_available(NULL, v))
+               __kvm_riscv_vector_save(cntx);
+}
+
+void kvm_riscv_vcpu_host_vector_restore(struct kvm_cpu_context *cntx)
+{
+       if (riscv_isa_extension_available(NULL, v))
+               __kvm_riscv_vector_restore(cntx);
+}
+
+int kvm_riscv_vcpu_alloc_vector_context(struct kvm_vcpu *vcpu,
+                                       struct kvm_cpu_context *cntx)
+{
+       cntx->vector.datap = kmalloc(riscv_v_vsize, GFP_KERNEL);
+       if (!cntx->vector.datap)
+               return -ENOMEM;
+
+       vcpu->arch.host_context.vector.datap = kzalloc(riscv_v_vsize, GFP_KERNEL);
+       if (!vcpu->arch.host_context.vector.datap)
+               return -ENOMEM;
+
+       return 0;
+}
+
+void kvm_riscv_vcpu_free_vector_context(struct kvm_vcpu *vcpu)
+{
+       kfree(vcpu->arch.guest_reset_context.vector.datap);
+       kfree(vcpu->arch.host_context.vector.datap);
+}
+#endif
+
+static void *kvm_riscv_vcpu_vreg_addr(struct kvm_vcpu *vcpu,
+                                     unsigned long reg_num,
+                                     size_t reg_size)
+{
+       struct kvm_cpu_context *cntx = &vcpu->arch.guest_context;
+       void *reg_val;
+       size_t vlenb = riscv_v_vsize / 32;
+
+       if (reg_num < KVM_REG_RISCV_VECTOR_REG(0)) {
+               if (reg_size != sizeof(unsigned long))
+                       return NULL;
+               switch (reg_num) {
+               case KVM_REG_RISCV_VECTOR_CSR_REG(vstart):
+                       reg_val = &cntx->vector.vstart;
+                       break;
+               case KVM_REG_RISCV_VECTOR_CSR_REG(vl):
+                       reg_val = &cntx->vector.vl;
+                       break;
+               case KVM_REG_RISCV_VECTOR_CSR_REG(vtype):
+                       reg_val = &cntx->vector.vtype;
+                       break;
+               case KVM_REG_RISCV_VECTOR_CSR_REG(vcsr):
+                       reg_val = &cntx->vector.vcsr;
+                       break;
+               case KVM_REG_RISCV_VECTOR_CSR_REG(datap):
+               default:
+                       return NULL;
+               }
+       } else if (reg_num <= KVM_REG_RISCV_VECTOR_REG(31)) {
+               if (reg_size != vlenb)
+                       return NULL;
+               reg_val = cntx->vector.datap
+                         + (reg_num - KVM_REG_RISCV_VECTOR_REG(0)) * vlenb;
+       } else {
+               return NULL;
+       }
+
+       return reg_val;
+}
+
+int kvm_riscv_vcpu_get_reg_vector(struct kvm_vcpu *vcpu,
+                                 const struct kvm_one_reg *reg,
+                                 unsigned long rtype)
+{
+       unsigned long *isa = vcpu->arch.isa;
+       unsigned long __user *uaddr =
+                       (unsigned long __user *)(unsigned long)reg->addr;
+       unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
+                                           KVM_REG_SIZE_MASK |
+                                           rtype);
+       void *reg_val = NULL;
+       size_t reg_size = KVM_REG_SIZE(reg->id);
+
+       if (rtype == KVM_REG_RISCV_VECTOR &&
+           riscv_isa_extension_available(isa, v)) {
+               reg_val = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size);
+       }
+
+       if (!reg_val)
+               return -EINVAL;
+
+       if (copy_to_user(uaddr, reg_val, reg_size))
+               return -EFAULT;
+
+       return 0;
+}
+
+int kvm_riscv_vcpu_set_reg_vector(struct kvm_vcpu *vcpu,
+                                 const struct kvm_one_reg *reg,
+                                 unsigned long rtype)
+{
+       unsigned long *isa = vcpu->arch.isa;
+       unsigned long __user *uaddr =
+                       (unsigned long __user *)(unsigned long)reg->addr;
+       unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK |
+                                           KVM_REG_SIZE_MASK |
+                                           rtype);
+       void *reg_val = NULL;
+       size_t reg_size = KVM_REG_SIZE(reg->id);
+
+       if (rtype == KVM_REG_RISCV_VECTOR &&
+           riscv_isa_extension_available(isa, v)) {
+               reg_val = kvm_riscv_vcpu_vreg_addr(vcpu, reg_num, reg_size);
+       }
+
+       if (!reg_val)
+               return -EINVAL;
+
+       if (copy_from_user(reg_val, uaddr, reg_size))
+               return -EFAULT;
+
+       return 0;
+}
index b85e9e8..9c454f9 100644 (file)
@@ -13,8 +13,7 @@ endif
 KCOV_INSTRUMENT_init.o := n
 
 obj-y += init.o
-obj-y += extable.o
-obj-$(CONFIG_MMU) += fault.o pageattr.o
+obj-$(CONFIG_MMU) += extable.o fault.o pageattr.o
 obj-y += cacheflush.o
 obj-y += context.o
 obj-y += pgtable.o
index 8685f85..e52ed89 100644 (file)
@@ -238,24 +238,12 @@ void handle_page_fault(struct pt_regs *regs)
         * only copy the information from the master page table,
         * nothing more.
         */
-       if (unlikely((addr >= VMALLOC_START) && (addr < VMALLOC_END))) {
+       if ((!IS_ENABLED(CONFIG_MMU) || !IS_ENABLED(CONFIG_64BIT)) &&
+           unlikely(addr >= VMALLOC_START && addr < VMALLOC_END)) {
                vmalloc_fault(regs, code, addr);
                return;
        }
 
-#ifdef CONFIG_64BIT
-       /*
-        * Modules in 64bit kernels lie in their own virtual region which is not
-        * in the vmalloc region, but dealing with page faults in this region
-        * or the vmalloc region amounts to doing the same thing: checking that
-        * the mapping exists in init_mm.pgd and updating user page table, so
-        * just use vmalloc_fault.
-        */
-       if (unlikely(addr >= MODULES_VADDR && addr < MODULES_END)) {
-               vmalloc_fault(regs, code, addr);
-               return;
-       }
-#endif
        /* Enable interrupts if they were enabled in the parent context. */
        if (!regs_irqs_disabled(regs))
                local_irq_enable();
@@ -286,6 +274,36 @@ void handle_page_fault(struct pt_regs *regs)
                flags |= FAULT_FLAG_WRITE;
        else if (cause == EXC_INST_PAGE_FAULT)
                flags |= FAULT_FLAG_INSTRUCTION;
+#ifdef CONFIG_PER_VMA_LOCK
+       if (!(flags & FAULT_FLAG_USER))
+               goto lock_mmap;
+
+       vma = lock_vma_under_rcu(mm, addr);
+       if (!vma)
+               goto lock_mmap;
+
+       if (unlikely(access_error(cause, vma))) {
+               vma_end_read(vma);
+               goto lock_mmap;
+       }
+
+       fault = handle_mm_fault(vma, addr, flags | FAULT_FLAG_VMA_LOCK, regs);
+       vma_end_read(vma);
+
+       if (!(fault & VM_FAULT_RETRY)) {
+               count_vm_vma_lock_event(VMA_LOCK_SUCCESS);
+               goto done;
+       }
+       count_vm_vma_lock_event(VMA_LOCK_RETRY);
+
+       if (fault_signal_pending(fault, regs)) {
+               if (!user_mode(regs))
+                       no_context(regs, addr);
+               return;
+       }
+lock_mmap:
+#endif /* CONFIG_PER_VMA_LOCK */
+
 retry:
        mmap_read_lock(mm);
        vma = find_vma(mm, addr);
@@ -355,6 +373,9 @@ good_area:
 
        mmap_read_unlock(mm);
 
+#ifdef CONFIG_PER_VMA_LOCK
+done:
+#endif
        if (unlikely(fault & VM_FAULT_ERROR)) {
                tsk->thread.bad_cause = cause;
                mm_fault_error(regs, addr, fault);
index 747e5b1..45ceaff 100644 (file)
@@ -1363,3 +1363,61 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node,
        return vmemmap_populate_basepages(start, end, node, NULL);
 }
 #endif
+
+#if defined(CONFIG_MMU) && defined(CONFIG_64BIT)
+/*
+ * Pre-allocates page-table pages for a specific area in the kernel
+ * page-table. Only the level which needs to be synchronized between
+ * all page-tables is allocated because the synchronization can be
+ * expensive.
+ */
+static void __init preallocate_pgd_pages_range(unsigned long start, unsigned long end,
+                                              const char *area)
+{
+       unsigned long addr;
+       const char *lvl;
+
+       for (addr = start; addr < end && addr >= start; addr = ALIGN(addr + 1, PGDIR_SIZE)) {
+               pgd_t *pgd = pgd_offset_k(addr);
+               p4d_t *p4d;
+               pud_t *pud;
+               pmd_t *pmd;
+
+               lvl = "p4d";
+               p4d = p4d_alloc(&init_mm, pgd, addr);
+               if (!p4d)
+                       goto failed;
+
+               if (pgtable_l5_enabled)
+                       continue;
+
+               lvl = "pud";
+               pud = pud_alloc(&init_mm, p4d, addr);
+               if (!pud)
+                       goto failed;
+
+               if (pgtable_l4_enabled)
+                       continue;
+
+               lvl = "pmd";
+               pmd = pmd_alloc(&init_mm, pud, addr);
+               if (!pmd)
+                       goto failed;
+       }
+       return;
+
+failed:
+       /*
+        * The pages have to be there now or they will be missing in
+        * process page-tables later.
+        */
+       panic("Failed to pre-allocate %s pages for %s area\n", lvl, area);
+}
+
+void __init pgtable_cache_init(void)
+{
+       preallocate_pgd_pages_range(VMALLOC_START, VMALLOC_END, "vmalloc");
+       if (IS_ENABLED(CONFIG_MODULES))
+               preallocate_pgd_pages_range(MODULES_VADDR, MODULES_END, "bpf/modules");
+}
+#endif
index 4f3ac29..4163ff5 100644 (file)
@@ -739,7 +739,6 @@ static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pde
 {
        int ret;
        struct cpu_hw_events __percpu *hw_events = pmu->hw_events;
-       struct device_node *cpu, *child;
        struct irq_domain *domain = NULL;
 
        if (riscv_isa_extension_available(NULL, SSCOFPMF)) {
@@ -756,20 +755,8 @@ static int pmu_sbi_setup_irqs(struct riscv_pmu *pmu, struct platform_device *pde
        if (!riscv_pmu_use_irq)
                return -EOPNOTSUPP;
 
-       for_each_of_cpu_node(cpu) {
-               child = of_get_compatible_child(cpu, "riscv,cpu-intc");
-               if (!child) {
-                       pr_err("Failed to find INTC node\n");
-                       of_node_put(cpu);
-                       return -ENODEV;
-               }
-               domain = irq_find_host(child);
-               of_node_put(child);
-               if (domain) {
-                       of_node_put(cpu);
-                       break;
-               }
-       }
+       domain = irq_find_matching_fwnode(riscv_get_intc_hwnode(),
+                                         DOMAIN_BUS_ANY);
        if (!domain) {
                pr_err("Failed to find INTC IRQ root domain\n");
                return -ENODEV;
@@ -868,6 +855,12 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
                goto out_free;
        }
 
+       /* It is possible to get from SBI more than max number of counters */
+       if (num_counters > RISCV_MAX_COUNTERS) {
+               num_counters = RISCV_MAX_COUNTERS;
+               pr_info("SBI returned more than maximum number of counters. Limiting the number of counters to %d\n", num_counters);
+       }
+
        /* cache all the information about counters now */
        if (pmu_sbi_get_ctrinfo(num_counters, &cmask))
                goto out_free;
index ac3da85..7d8d9ae 100644 (file)
@@ -440,6 +440,7 @@ typedef struct elf64_shdr {
 #define NT_MIPS_DSP    0x800           /* MIPS DSP ASE registers */
 #define NT_MIPS_FP_MODE        0x801           /* MIPS floating-point mode */
 #define NT_MIPS_MSA    0x802           /* MIPS SIMD registers */
+#define NT_RISCV_VECTOR        0x900           /* RISC-V vector registers */
 #define NT_LOONGARCH_CPUCFG    0xa00   /* LoongArch CPU config registers */
 #define NT_LOONGARCH_CSR       0xa01   /* LoongArch control and status registers */
 #define NT_LOONGARCH_LSX       0xa02   /* LoongArch Loongson SIMD Extension registers */
index f23d9a1..3c36aea 100644 (file)
@@ -294,4 +294,15 @@ struct prctl_mm_map {
 
 #define PR_SET_MEMORY_MERGE            67
 #define PR_GET_MEMORY_MERGE            68
+
+#define PR_RISCV_V_SET_CONTROL         69
+#define PR_RISCV_V_GET_CONTROL         70
+# define PR_RISCV_V_VSTATE_CTRL_DEFAULT                0
+# define PR_RISCV_V_VSTATE_CTRL_OFF            1
+# define PR_RISCV_V_VSTATE_CTRL_ON             2
+# define PR_RISCV_V_VSTATE_CTRL_INHERIT                (1 << 4)
+# define PR_RISCV_V_VSTATE_CTRL_CUR_MASK       0x3
+# define PR_RISCV_V_VSTATE_CTRL_NEXT_MASK      0xc
+# define PR_RISCV_V_VSTATE_CTRL_MASK           0x1f
+
 #endif /* _LINUX_PRCTL_H */
index 339fee3..05f8389 100644 (file)
 #ifndef GET_TAGGED_ADDR_CTRL
 # define GET_TAGGED_ADDR_CTRL()                (-EINVAL)
 #endif
+#ifndef RISCV_V_SET_CONTROL
+# define RISCV_V_SET_CONTROL(a)                (-EINVAL)
+#endif
+#ifndef RISCV_V_GET_CONTROL
+# define RISCV_V_GET_CONTROL()         (-EINVAL)
+#endif
 
 /*
  * this is where the system-wide overflow UID and GID are defined, for
@@ -2708,6 +2714,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                error = !!test_bit(MMF_VM_MERGE_ANY, &me->mm->flags);
                break;
 #endif
+       case PR_RISCV_V_SET_CONTROL:
+               error = RISCV_V_SET_CONTROL(arg2);
+               break;
+       case PR_RISCV_V_GET_CONTROL:
+               error = RISCV_V_GET_CONTROL();
+               break;
        default:
                error = -EINVAL;
                break;
index 32a7290..9dd629c 100644 (file)
@@ -5,7 +5,7 @@
 ARCH ?= $(shell uname -m 2>/dev/null || echo not)
 
 ifneq (,$(filter $(ARCH),riscv))
-RISCV_SUBTARGETS ?= hwprobe
+RISCV_SUBTARGETS ?= hwprobe vector
 else
 RISCV_SUBTARGETS :=
 endif
diff --git a/tools/testing/selftests/riscv/hwprobe/.gitignore b/tools/testing/selftests/riscv/hwprobe/.gitignore
new file mode 100644 (file)
index 0000000..8113dc3
--- /dev/null
@@ -0,0 +1 @@
+hwprobe
diff --git a/tools/testing/selftests/riscv/vector/.gitignore b/tools/testing/selftests/riscv/vector/.gitignore
new file mode 100644 (file)
index 0000000..4f2b4e8
--- /dev/null
@@ -0,0 +1,2 @@
+vstate_exec_nolibc
+vstate_prctl
diff --git a/tools/testing/selftests/riscv/vector/Makefile b/tools/testing/selftests/riscv/vector/Makefile
new file mode 100644 (file)
index 0000000..cd6e80b
--- /dev/null
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2021 ARM Limited
+# Originally tools/testing/arm64/abi/Makefile
+
+TEST_GEN_PROGS := vstate_prctl
+TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc
+
+include ../../lib.mk
+
+$(OUTPUT)/vstate_prctl: vstate_prctl.c ../hwprobe/sys_hwprobe.S
+       $(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
+
+$(OUTPUT)/vstate_exec_nolibc: vstate_exec_nolibc.c
+       $(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \
+               -Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc
diff --git a/tools/testing/selftests/riscv/vector/vstate_exec_nolibc.c b/tools/testing/selftests/riscv/vector/vstate_exec_nolibc.c
new file mode 100644 (file)
index 0000000..5cbc392
--- /dev/null
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <sys/prctl.h>
+
+#define THIS_PROGRAM "./vstate_exec_nolibc"
+
+int main(int argc, char **argv)
+{
+       int rc, pid, status, test_inherit = 0;
+       long ctrl, ctrl_c;
+       char *exec_argv[2], *exec_envp[2];
+
+       if (argc > 1)
+               test_inherit = 1;
+
+       ctrl = my_syscall1(__NR_prctl, PR_RISCV_V_GET_CONTROL);
+       if (ctrl < 0) {
+               puts("PR_RISCV_V_GET_CONTROL is not supported\n");
+               return ctrl;
+       }
+
+       if (test_inherit) {
+               pid = fork();
+               if (pid == -1) {
+                       puts("fork failed\n");
+                       exit(-1);
+               }
+
+               /* child  */
+               if (!pid) {
+                       exec_argv[0] = THIS_PROGRAM;
+                       exec_argv[1] = NULL;
+                       exec_envp[0] = NULL;
+                       exec_envp[1] = NULL;
+                       /* launch the program again to check inherit */
+                       rc = execve(THIS_PROGRAM, exec_argv, exec_envp);
+                       if (rc) {
+                               puts("child execve failed\n");
+                               exit(-1);
+                       }
+               }
+
+       } else {
+               pid = fork();
+               if (pid == -1) {
+                       puts("fork failed\n");
+                       exit(-1);
+               }
+
+               if (!pid) {
+                       rc = my_syscall1(__NR_prctl, PR_RISCV_V_GET_CONTROL);
+                       if (rc != ctrl) {
+                               puts("child's vstate_ctrl not equal to parent's\n");
+                               exit(-1);
+                       }
+                       asm volatile (".option push\n\t"
+                                     ".option arch, +v\n\t"
+                                     "vsetvli x0, x0, e32, m8, ta, ma\n\t"
+                                     ".option pop\n\t"
+                                     );
+                       exit(ctrl);
+               }
+       }
+
+       rc = waitpid(-1, &status, 0);
+
+       if (WIFEXITED(status) && WEXITSTATUS(status) == -1) {
+               puts("child exited abnormally\n");
+               exit(-1);
+       }
+
+       if (WIFSIGNALED(status)) {
+               if (WTERMSIG(status) != SIGILL) {
+                       puts("child was terminated by unexpected signal\n");
+                       exit(-1);
+               }
+
+               if ((ctrl & PR_RISCV_V_VSTATE_CTRL_CUR_MASK) != PR_RISCV_V_VSTATE_CTRL_OFF) {
+                       puts("child signaled by illegal V access but vstate_ctrl is not off\n");
+                       exit(-1);
+               }
+
+               /* child terminated, and its vstate_ctrl is off */
+               exit(ctrl);
+       }
+
+       ctrl_c = WEXITSTATUS(status);
+       if (test_inherit) {
+               if (ctrl & PR_RISCV_V_VSTATE_CTRL_INHERIT) {
+                       if (!(ctrl_c & PR_RISCV_V_VSTATE_CTRL_INHERIT)) {
+                               puts("parent has inherit bit, but child has not\n");
+                               exit(-1);
+                       }
+               }
+               rc = (ctrl & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK) >> 2;
+               if (rc != PR_RISCV_V_VSTATE_CTRL_DEFAULT) {
+                       if (rc != (ctrl_c & PR_RISCV_V_VSTATE_CTRL_CUR_MASK)) {
+                               puts("parent's next setting does not equal to child's\n");
+                               exit(-1);
+                       }
+
+                       if (!(ctrl & PR_RISCV_V_VSTATE_CTRL_INHERIT)) {
+                               if ((ctrl_c & PR_RISCV_V_VSTATE_CTRL_NEXT_MASK) !=
+                                   PR_RISCV_V_VSTATE_CTRL_DEFAULT) {
+                                       puts("must clear child's next vstate_ctrl if !inherit\n");
+                                       exit(-1);
+                               }
+                       }
+               }
+       }
+       return ctrl;
+}
diff --git a/tools/testing/selftests/riscv/vector/vstate_prctl.c b/tools/testing/selftests/riscv/vector/vstate_prctl.c
new file mode 100644 (file)
index 0000000..b348b47
--- /dev/null
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <sys/prctl.h>
+#include <unistd.h>
+#include <asm/hwprobe.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+#include "../../kselftest.h"
+
+/*
+ * Rather than relying on having a new enough libc to define this, just do it
+ * ourselves.  This way we don't need to be coupled to a new-enough libc to
+ * contain the call.
+ */
+long riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
+                  size_t cpu_count, unsigned long *cpus, unsigned int flags);
+
+#define NEXT_PROGRAM "./vstate_exec_nolibc"
+static int launch_test(int test_inherit)
+{
+       char *exec_argv[3], *exec_envp[1];
+       int rc, pid, status;
+
+       pid = fork();
+       if (pid < 0) {
+               ksft_test_result_fail("fork failed %d", pid);
+               return -1;
+       }
+
+       if (!pid) {
+               exec_argv[0] = NEXT_PROGRAM;
+               exec_argv[1] = test_inherit != 0 ? "x" : NULL;
+               exec_argv[2] = NULL;
+               exec_envp[0] = NULL;
+               /* launch the program again to check inherit */
+               rc = execve(NEXT_PROGRAM, exec_argv, exec_envp);
+               if (rc) {
+                       perror("execve");
+                       ksft_test_result_fail("child execve failed %d\n", rc);
+                       exit(-1);
+               }
+       }
+
+       rc = waitpid(-1, &status, 0);
+       if (rc < 0) {
+               ksft_test_result_fail("waitpid failed\n");
+               return -3;
+       }
+
+       if ((WIFEXITED(status) && WEXITSTATUS(status) == -1) ||
+           WIFSIGNALED(status)) {
+               ksft_test_result_fail("child exited abnormally\n");
+               return -4;
+       }
+
+       return WEXITSTATUS(status);
+}
+
+int test_and_compare_child(long provided, long expected, int inherit)
+{
+       int rc;
+
+       rc = prctl(PR_RISCV_V_SET_CONTROL, provided);
+       if (rc != 0) {
+               ksft_test_result_fail("prctl with provided arg %lx failed with code %d\n",
+                                     provided, rc);
+               return -1;
+       }
+       rc = launch_test(inherit);
+       if (rc != expected) {
+               ksft_test_result_fail("Test failed, check %d != %d\n", rc,
+                                     expected);
+               return -2;
+       }
+       return 0;
+}
+
+#define PR_RISCV_V_VSTATE_CTRL_CUR_SHIFT       0
+#define PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT      2
+
+int main(void)
+{
+       struct riscv_hwprobe pair;
+       long flag, expected;
+       long rc;
+
+       pair.key = RISCV_HWPROBE_KEY_IMA_EXT_0;
+       rc = riscv_hwprobe(&pair, 1, 0, NULL, 0);
+       if (rc < 0) {
+               ksft_test_result_fail("hwprobe() failed with %d\n", rc);
+               return -1;
+       }
+
+       if (pair.key != RISCV_HWPROBE_KEY_IMA_EXT_0) {
+               ksft_test_result_fail("hwprobe cannot probe RISCV_HWPROBE_KEY_IMA_EXT_0\n");
+               return -2;
+       }
+
+       if (!(pair.value & RISCV_HWPROBE_IMA_V)) {
+               rc = prctl(PR_RISCV_V_GET_CONTROL);
+               if (rc != -1 || errno != EINVAL) {
+                       ksft_test_result_fail("GET_CONTROL should fail on kernel/hw without V\n");
+                       return -3;
+               }
+
+               rc = prctl(PR_RISCV_V_SET_CONTROL, PR_RISCV_V_VSTATE_CTRL_ON);
+               if (rc != -1 || errno != EINVAL) {
+                       ksft_test_result_fail("GET_CONTROL should fail on kernel/hw without V\n");
+                       return -4;
+               }
+
+               ksft_test_result_skip("Vector not supported\n");
+               return 0;
+       }
+
+       flag = PR_RISCV_V_VSTATE_CTRL_ON;
+       rc = prctl(PR_RISCV_V_SET_CONTROL, flag);
+       if (rc != 0) {
+               ksft_test_result_fail("Enabling V for current should always success\n");
+               return -5;
+       }
+
+       flag = PR_RISCV_V_VSTATE_CTRL_OFF;
+       rc = prctl(PR_RISCV_V_SET_CONTROL, flag);
+       if (rc != -1 || errno != EPERM) {
+               ksft_test_result_fail("Disabling current's V alive must fail with EPERM(%d)\n",
+                                     errno);
+               return -5;
+       }
+
+       /* Turn on next's vector explicitly and test */
+       flag = PR_RISCV_V_VSTATE_CTRL_ON << PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT;
+       if (test_and_compare_child(flag, PR_RISCV_V_VSTATE_CTRL_ON, 0))
+               return -6;
+
+       /* Turn off next's vector explicitly and test */
+       flag = PR_RISCV_V_VSTATE_CTRL_OFF << PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT;
+       if (test_and_compare_child(flag, PR_RISCV_V_VSTATE_CTRL_OFF, 0))
+               return -7;
+
+       /* Turn on next's vector explicitly and test inherit */
+       flag = PR_RISCV_V_VSTATE_CTRL_ON << PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT;
+       flag |= PR_RISCV_V_VSTATE_CTRL_INHERIT;
+       expected = flag | PR_RISCV_V_VSTATE_CTRL_ON;
+       if (test_and_compare_child(flag, expected, 0))
+               return -8;
+
+       if (test_and_compare_child(flag, expected, 1))
+               return -9;
+
+       /* Turn off next's vector explicitly and test inherit */
+       flag = PR_RISCV_V_VSTATE_CTRL_OFF << PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT;
+       flag |= PR_RISCV_V_VSTATE_CTRL_INHERIT;
+       expected = flag | PR_RISCV_V_VSTATE_CTRL_OFF;
+       if (test_and_compare_child(flag, expected, 0))
+               return -10;
+
+       if (test_and_compare_child(flag, expected, 1))
+               return -11;
+
+       /* arguments should fail with EINVAL */
+       rc = prctl(PR_RISCV_V_SET_CONTROL, 0xff0);
+       if (rc != -1 || errno != EINVAL) {
+               ksft_test_result_fail("Undefined control argument should return EINVAL\n");
+               return -12;
+       }
+
+       rc = prctl(PR_RISCV_V_SET_CONTROL, 0x3);
+       if (rc != -1 || errno != EINVAL) {
+               ksft_test_result_fail("Undefined control argument should return EINVAL\n");
+               return -12;
+       }
+
+       rc = prctl(PR_RISCV_V_SET_CONTROL, 0xc);
+       if (rc != -1 || errno != EINVAL) {
+               ksft_test_result_fail("Undefined control argument should return EINVAL\n");
+               return -12;
+       }
+
+       rc = prctl(PR_RISCV_V_SET_CONTROL, 0xc);
+       if (rc != -1 || errno != EINVAL) {
+               ksft_test_result_fail("Undefined control argument should return EINVAL\n");
+               return -12;
+       }
+
+       ksft_test_result_pass("tests for riscv_v_vstate_ctrl pass\n");
+       ksft_exit_pass();
+       return 0;
+}