RISC-V Patches for the 6.14 Merge Window, Part 1
* The PH1520 pinctrl and dwmac drivers are enabeled in defconfig. * A redundant AQRL barrier has been removed from the futex cmpxchg implementation. * Support for the T-Head vector extensions, which includes exposing these extensions to userspace on systems that implement them. * Some more page table information is now printed on die() and systems that cause PA overflows. -----BEGIN PGP SIGNATURE----- iQJHBAABCAAxFiEEKzw3R0RoQ7JKlDp6LhMZ81+7GIkFAmedHIoTHHBhbG1lckBk YWJiZWx0LmNvbQAKCRAuExnzX7sYievXD/4hdt8h+fMM0I9mmJS096YevRJONdfe Wk7D5q4PBwSHISHahuzfphieBhqPVnYkkEd7Vw6xRrLbUnhA41Fe0uvR52dx5UZd 3LwrDV/kjGTD59x6A2Zo9bSs/qPKJ2WHmHwHM21jY5tvcIB2Lo4dF8HT63OrwVNW DxsujLO0jUw+HEwXPsfmUAZJWOPZuUnatl/9CaLMLwQv5N7yiMuz5oYDzJXTLnNh m3Hv3CCtj1EeQPqDoWzz9nZvmAKOwcblSzz6OAy+xrRk1N0N3QFQPbIaRvkI9OVz +wPHQiyx4KZNeAe0csV0uLQRIiXZV8rkCz5UT65s3Bfy3vukvzz+1VBdNnCqiP8Q RpCTcYw62Cr6BWnvyTh+s9bhHb1ijG043nXd/Ty7ZRPCNLKHY6oL1CZ0pgqbTwPs D2U2ZTZFTc35mPrU6QMfbTiUVWCU2XagFhI27Dgj3xh9mkBOQCHwk2Mrzn7uS4iz xGNnrjRnKtuwBrvD68JzxCkEi8INFn2ifbVr44VZrOdTM7XtODGAYrBohQtV62kU 2L+q8DoHYis+0xFbR1wdrY1mRZoe45boUFgwnOpmoBr9ULe584sL+526y7IkkEHu /9hmLPtLg7nyoR/rO1j1Sfg4Eqdwg5HY1TKNfagJZAdu23EDRwrcW1PD0P6vtDv8 j4og8MmL7dTt3A== =HbAQ -----END PGP SIGNATURE----- Merge tag 'riscv-for-linus-6.14-mw1' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux Pull RISC-V updates from Palmer Dabbelt: - The PH1520 pinctrl and dwmac drivers are enabeled in defconfig - A redundant AQRL barrier has been removed from the futex cmpxchg implementation - Support for the T-Head vector extensions, which includes exposing these extensions to userspace on systems that implement them - Some more page table information is now printed on die() and systems that cause PA overflows * tag 'riscv-for-linus-6.14-mw1' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux: riscv: add a warning when physical memory address overflows riscv/mm/fault: add show_pte() before die() riscv: Add ghostwrite vulnerability selftests: riscv: Support xtheadvector in vector tests selftests: riscv: Fix vector tests riscv: hwprobe: Document thead vendor extensions and xtheadvector extension riscv: hwprobe: Add thead vendor extension probing riscv: vector: Support xtheadvector save/restore riscv: Add xtheadvector instruction definitions riscv: csr: Add CSR encodings for CSR_VXRM/CSR_VXSAT RISC-V: define the elements of the VCSR vector CSR riscv: vector: Use vlenb from DT for thead riscv: Add thead and xtheadvector as a vendor extension riscv: dts: allwinner: Add xtheadvector to the D1/D1s devicetree dt-bindings: cpus: add a thead vlen register length property dt-bindings: riscv: Add xtheadvector ISA extension description RISC-V: Mark riscv_v_init() as __init riscv: defconfig: drop RT_GROUP_SCHED=y riscv/futex: Optimize atomic cmpxchg riscv: defconfig: enable pinctrl and dwmac support for TH1520
This commit is contained in:
commit
1b5f3c51fb
|
@ -293,3 +293,13 @@ The following keys are defined:
|
|||
|
||||
* :c:macro:`RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED`: Misaligned vector accesses are
|
||||
not supported at all and will generate a misaligned address fault.
|
||||
|
||||
* :c:macro:`RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0`: A bitmask containing the
|
||||
thead vendor extensions that are compatible with the
|
||||
:c:macro:`RISCV_HWPROBE_BASE_BEHAVIOR_IMA`: base system behavior.
|
||||
|
||||
* T-HEAD
|
||||
|
||||
* :c:macro:`RISCV_HWPROBE_VENDOR_EXT_XTHEADVECTOR`: The xtheadvector vendor
|
||||
extension is supported in the T-Head ISA extensions spec starting from
|
||||
commit a18c801634 ("Add T-Head VECTOR vendor extension. ").
|
||||
|
|
|
@ -26,6 +26,18 @@ description: |
|
|||
allOf:
|
||||
- $ref: /schemas/cpu.yaml#
|
||||
- $ref: extensions.yaml
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- thead,c906
|
||||
- thead,c910
|
||||
- thead,c920
|
||||
then:
|
||||
properties:
|
||||
thead,vlenb: false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -96,6 +108,13 @@ properties:
|
|||
description:
|
||||
The blocksize in bytes for the Zicboz cache operations.
|
||||
|
||||
thead,vlenb:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
VLEN/8, the vector register length in bytes. This property is required on
|
||||
thead systems where the vector register length is not identical on all harts, or
|
||||
the vlenb CSR is not available.
|
||||
|
||||
# RISC-V has multiple properties for cache op block sizes as the sizes
|
||||
# differ between individual CBO extensions
|
||||
cache-op-block-size: false
|
||||
|
|
|
@ -621,6 +621,10 @@ properties:
|
|||
latency, as ratified in commit 56ed795 ("Update
|
||||
riscv-crypto-spec-vector.adoc") of riscv-crypto.
|
||||
|
||||
# vendor extensions, each extension sorted alphanumerically under the
|
||||
# vendor they belong to. Vendors are sorted alphanumerically as well.
|
||||
|
||||
# Andes
|
||||
- const: xandespmu
|
||||
description:
|
||||
The Andes Technology performance monitor extension for counter overflow
|
||||
|
@ -628,6 +632,12 @@ properties:
|
|||
Registers in the AX45MP datasheet.
|
||||
https://www.andestech.com/wp-content/uploads/AX45MP-1C-Rev.-5.0.0-Datasheet.pdf
|
||||
|
||||
# T-HEAD
|
||||
- const: xtheadvector
|
||||
description:
|
||||
The T-HEAD specific 0.7.1 vector implementation as written in
|
||||
https://github.com/T-head-Semi/thead-extension-spec/blob/95358cb2cca9489361c61d335e03d3134b14133f/xtheadvector.adoc.
|
||||
|
||||
allOf:
|
||||
# Zcb depends on Zca
|
||||
- if:
|
||||
|
|
|
@ -119,4 +119,15 @@ config ERRATA_THEAD_PMU
|
|||
|
||||
If you don't know what to do here, say "Y".
|
||||
|
||||
config ERRATA_THEAD_GHOSTWRITE
|
||||
bool "Apply T-Head Ghostwrite errata"
|
||||
depends on ERRATA_THEAD && RISCV_ISA_XTHEADVECTOR
|
||||
default y
|
||||
help
|
||||
The T-Head C9xx cores have a vulnerability in the xtheadvector
|
||||
instruction set. When this errata is enabled, the CPUs will be probed
|
||||
to determine if they are vulnerable and disable xtheadvector.
|
||||
|
||||
If you don't know what to do here, say "Y".
|
||||
|
||||
endmenu # "CPU errata selection"
|
||||
|
|
|
@ -16,4 +16,30 @@ config RISCV_ISA_VENDOR_EXT_ANDES
|
|||
If you don't know what to do here, say Y.
|
||||
endmenu
|
||||
|
||||
menu "T-Head"
|
||||
config RISCV_ISA_VENDOR_EXT_THEAD
|
||||
bool "T-Head vendor extension support"
|
||||
select RISCV_ISA_VENDOR_EXT
|
||||
default y
|
||||
help
|
||||
Say N here to disable detection of and support for all T-Head vendor
|
||||
extensions. Without this option enabled, T-Head vendor extensions will
|
||||
not be detected at boot and their presence not reported to userspace.
|
||||
|
||||
If you don't know what to do here, say Y.
|
||||
|
||||
config RISCV_ISA_XTHEADVECTOR
|
||||
bool "xtheadvector extension support"
|
||||
depends on RISCV_ISA_VENDOR_EXT_THEAD
|
||||
depends on RISCV_ISA_V
|
||||
depends on FPU
|
||||
default y
|
||||
help
|
||||
Say N here if you want to disable all xtheadvector related procedures
|
||||
in the kernel. This will disable vector for any T-Head board that
|
||||
contains xtheadvector rather than the standard vector.
|
||||
|
||||
If you don't know what to do here, say Y.
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -27,7 +27,8 @@
|
|||
riscv,isa = "rv64imafdc";
|
||||
riscv,isa-base = "rv64i";
|
||||
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "zicntr", "zicsr",
|
||||
"zifencei", "zihpm";
|
||||
"zifencei", "zihpm", "xtheadvector";
|
||||
thead,vlenb = <128>;
|
||||
#cooling-cells = <2>;
|
||||
|
||||
cpu0_intc: interrupt-controller {
|
||||
|
|
|
@ -10,7 +10,6 @@ CONFIG_MEMCG=y
|
|||
CONFIG_BLK_CGROUP=y
|
||||
CONFIG_CGROUP_SCHED=y
|
||||
CONFIG_CFS_BANDWIDTH=y
|
||||
CONFIG_RT_GROUP_SCHED=y
|
||||
CONFIG_CGROUP_PIDS=y
|
||||
CONFIG_CGROUP_FREEZER=y
|
||||
CONFIG_CGROUP_HUGETLB=y
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/bugs.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/dma-noncoherent.h>
|
||||
|
@ -142,6 +143,31 @@ static bool errata_probe_pmu(unsigned int stage,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool errata_probe_ghostwrite(unsigned int stage,
|
||||
unsigned long arch_id, unsigned long impid)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_ERRATA_THEAD_GHOSTWRITE))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* target-c9xx cores report arch_id and impid as 0
|
||||
*
|
||||
* While ghostwrite may not affect all c9xx cores that implement
|
||||
* xtheadvector, there is no futher granularity than c9xx. Assume
|
||||
* vulnerable for this entire class of processors when xtheadvector is
|
||||
* enabled.
|
||||
*/
|
||||
if (arch_id != 0 || impid != 0)
|
||||
return false;
|
||||
|
||||
if (stage != RISCV_ALTERNATIVES_EARLY_BOOT)
|
||||
return false;
|
||||
|
||||
ghostwrite_set_vulnerable();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 thead_errata_probe(unsigned int stage,
|
||||
unsigned long archid, unsigned long impid)
|
||||
{
|
||||
|
@ -155,6 +181,8 @@ static u32 thead_errata_probe(unsigned int stage,
|
|||
if (errata_probe_pmu(stage, archid, impid))
|
||||
cpu_req_errata |= BIT(ERRATA_THEAD_PMU);
|
||||
|
||||
errata_probe_ghostwrite(stage, archid, impid);
|
||||
|
||||
return cpu_req_errata;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Interface for managing mitigations for riscv vulnerabilities.
|
||||
*
|
||||
* Copyright (C) 2024 Rivos Inc.
|
||||
*/
|
||||
|
||||
#ifndef __ASM_BUGS_H
|
||||
#define __ASM_BUGS_H
|
||||
|
||||
/* Watch out, ordering is important here. */
|
||||
enum mitigation_state {
|
||||
UNAFFECTED,
|
||||
MITIGATED,
|
||||
VULNERABLE,
|
||||
};
|
||||
|
||||
void ghostwrite_set_vulnerable(void);
|
||||
bool ghostwrite_enable_mitigation(void);
|
||||
enum mitigation_state ghostwrite_get_state(void);
|
||||
|
||||
#endif /* __ASM_BUGS_H */
|
|
@ -34,6 +34,8 @@ DECLARE_PER_CPU(struct riscv_cpuinfo, riscv_cpuinfo);
|
|||
/* Per-cpu ISA extensions. */
|
||||
extern struct riscv_isainfo hart_isa[NR_CPUS];
|
||||
|
||||
extern u32 thead_vlenb_of;
|
||||
|
||||
void __init riscv_user_isa_enable(void);
|
||||
|
||||
#define _RISCV_ISA_EXT_DATA(_name, _id, _subset_exts, _subset_exts_size, _validate) { \
|
||||
|
|
|
@ -30,6 +30,12 @@
|
|||
#define SR_VS_CLEAN _AC(0x00000400, UL)
|
||||
#define SR_VS_DIRTY _AC(0x00000600, UL)
|
||||
|
||||
#define SR_VS_THEAD _AC(0x01800000, UL) /* xtheadvector Status */
|
||||
#define SR_VS_OFF_THEAD _AC(0x00000000, UL)
|
||||
#define SR_VS_INITIAL_THEAD _AC(0x00800000, UL)
|
||||
#define SR_VS_CLEAN_THEAD _AC(0x01000000, UL)
|
||||
#define SR_VS_DIRTY_THEAD _AC(0x01800000, UL)
|
||||
|
||||
#define SR_XS _AC(0x00018000, UL) /* Extension Status */
|
||||
#define SR_XS_OFF _AC(0x00000000, UL)
|
||||
#define SR_XS_INITIAL _AC(0x00008000, UL)
|
||||
|
@ -315,6 +321,15 @@
|
|||
#define CSR_STIMECMP 0x14D
|
||||
#define CSR_STIMECMPH 0x15D
|
||||
|
||||
/* xtheadvector symbolic CSR names */
|
||||
#define CSR_VXSAT 0x9
|
||||
#define CSR_VXRM 0xa
|
||||
|
||||
/* xtheadvector CSR masks */
|
||||
#define CSR_VXRM_MASK 3
|
||||
#define CSR_VXRM_SHIFT 1
|
||||
#define CSR_VXSAT_MASK 1
|
||||
|
||||
/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
|
||||
#define CSR_SISELECT 0x150
|
||||
#define CSR_SIREG 0x151
|
||||
|
|
|
@ -25,7 +25,8 @@
|
|||
#ifdef CONFIG_ERRATA_THEAD
|
||||
#define ERRATA_THEAD_MAE 0
|
||||
#define ERRATA_THEAD_PMU 1
|
||||
#define ERRATA_THEAD_NUMBER 2
|
||||
#define ERRATA_THEAD_GHOSTWRITE 2
|
||||
#define ERRATA_THEAD_NUMBER 3
|
||||
#endif
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
|
|
|
@ -85,7 +85,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
|
|||
|
||||
__enable_user_access();
|
||||
__asm__ __volatile__ (
|
||||
"1: lr.w.aqrl %[v],%[u] \n"
|
||||
"1: lr.w %[v],%[u] \n"
|
||||
" bne %[v],%z[ov],3f \n"
|
||||
"2: sc.w.aqrl %[t],%z[nv],%[u] \n"
|
||||
" bnez %[t],1b \n"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright 2023 Rivos, Inc
|
||||
* Copyright 2023-2024 Rivos, Inc
|
||||
*/
|
||||
|
||||
#ifndef _ASM_HWPROBE_H
|
||||
|
@ -8,7 +8,7 @@
|
|||
|
||||
#include <uapi/asm/hwprobe.h>
|
||||
|
||||
#define RISCV_HWPROBE_MAX_KEY 10
|
||||
#define RISCV_HWPROBE_MAX_KEY 11
|
||||
|
||||
static inline bool riscv_hwprobe_key_is_valid(__s64 key)
|
||||
{
|
||||
|
@ -21,6 +21,7 @@ static inline bool hwprobe_key_is_bitmask(__s64 key)
|
|||
case RISCV_HWPROBE_KEY_BASE_BEHAVIOR:
|
||||
case RISCV_HWPROBE_KEY_IMA_EXT_0:
|
||||
case RISCV_HWPROBE_KEY_CPUPERF_0:
|
||||
case RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ do { \
|
|||
__set_prev_cpu(__prev->thread); \
|
||||
if (has_fpu()) \
|
||||
__switch_to_fpu(__prev, __next); \
|
||||
if (has_vector()) \
|
||||
if (has_vector() || has_xtheadvector()) \
|
||||
__switch_to_vector(__prev, __next); \
|
||||
if (switch_to_should_flush_icache(__next)) \
|
||||
local_flush_icache_all(); \
|
||||
|
|
|
@ -18,6 +18,27 @@
|
|||
#include <asm/cpufeature.h>
|
||||
#include <asm/csr.h>
|
||||
#include <asm/asm.h>
|
||||
#include <asm/vendorid_list.h>
|
||||
#include <asm/vendor_extensions.h>
|
||||
#include <asm/vendor_extensions/thead.h>
|
||||
|
||||
#define __riscv_v_vstate_or(_val, TYPE) ({ \
|
||||
typeof(_val) _res = _val; \
|
||||
if (has_xtheadvector()) \
|
||||
_res = (_res & ~SR_VS_THEAD) | SR_VS_##TYPE##_THEAD; \
|
||||
else \
|
||||
_res = (_res & ~SR_VS) | SR_VS_##TYPE; \
|
||||
_res; \
|
||||
})
|
||||
|
||||
#define __riscv_v_vstate_check(_val, TYPE) ({ \
|
||||
bool _res; \
|
||||
if (has_xtheadvector()) \
|
||||
_res = ((_val) & SR_VS_THEAD) == SR_VS_##TYPE##_THEAD; \
|
||||
else \
|
||||
_res = ((_val) & SR_VS) == SR_VS_##TYPE; \
|
||||
_res; \
|
||||
})
|
||||
|
||||
extern unsigned long riscv_v_vsize;
|
||||
int riscv_v_setup_vsize(void);
|
||||
|
@ -41,39 +62,62 @@ static __always_inline bool has_vector(void)
|
|||
return riscv_has_extension_unlikely(RISCV_ISA_EXT_ZVE32X);
|
||||
}
|
||||
|
||||
static __always_inline bool has_xtheadvector_no_alternatives(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR))
|
||||
return riscv_isa_vendor_extension_available(THEAD_VENDOR_ID, XTHEADVECTOR);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline bool has_xtheadvector(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR))
|
||||
return riscv_has_vendor_extension_unlikely(THEAD_VENDOR_ID,
|
||||
RISCV_ISA_VENDOR_EXT_XTHEADVECTOR);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void __riscv_v_vstate_clean(struct pt_regs *regs)
|
||||
{
|
||||
regs->status = (regs->status & ~SR_VS) | SR_VS_CLEAN;
|
||||
regs->status = __riscv_v_vstate_or(regs->status, CLEAN);
|
||||
}
|
||||
|
||||
static inline void __riscv_v_vstate_dirty(struct pt_regs *regs)
|
||||
{
|
||||
regs->status = (regs->status & ~SR_VS) | SR_VS_DIRTY;
|
||||
regs->status = __riscv_v_vstate_or(regs->status, DIRTY);
|
||||
}
|
||||
|
||||
static inline void riscv_v_vstate_off(struct pt_regs *regs)
|
||||
{
|
||||
regs->status = (regs->status & ~SR_VS) | SR_VS_OFF;
|
||||
regs->status = __riscv_v_vstate_or(regs->status, OFF);
|
||||
}
|
||||
|
||||
static inline void riscv_v_vstate_on(struct pt_regs *regs)
|
||||
{
|
||||
regs->status = (regs->status & ~SR_VS) | SR_VS_INITIAL;
|
||||
regs->status = __riscv_v_vstate_or(regs->status, INITIAL);
|
||||
}
|
||||
|
||||
static inline bool riscv_v_vstate_query(struct pt_regs *regs)
|
||||
{
|
||||
return (regs->status & SR_VS) != 0;
|
||||
return !__riscv_v_vstate_check(regs->status, OFF);
|
||||
}
|
||||
|
||||
static __always_inline void riscv_v_enable(void)
|
||||
{
|
||||
csr_set(CSR_SSTATUS, SR_VS);
|
||||
if (has_xtheadvector())
|
||||
csr_set(CSR_SSTATUS, SR_VS_THEAD);
|
||||
else
|
||||
csr_set(CSR_SSTATUS, SR_VS);
|
||||
}
|
||||
|
||||
static __always_inline void riscv_v_disable(void)
|
||||
{
|
||||
csr_clear(CSR_SSTATUS, SR_VS);
|
||||
if (has_xtheadvector())
|
||||
csr_clear(CSR_SSTATUS, SR_VS_THEAD);
|
||||
else
|
||||
csr_clear(CSR_SSTATUS, SR_VS);
|
||||
}
|
||||
|
||||
static __always_inline void __vstate_csr_save(struct __riscv_v_ext_state *dest)
|
||||
|
@ -82,10 +126,36 @@ static __always_inline void __vstate_csr_save(struct __riscv_v_ext_state *dest)
|
|||
"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"
|
||||
"csrr %4, " __stringify(CSR_VLENB) "\n\t"
|
||||
: "=r" (dest->vstart), "=r" (dest->vtype), "=r" (dest->vl),
|
||||
"=r" (dest->vcsr), "=r" (dest->vlenb) : :);
|
||||
"=r" (dest->vcsr) : :);
|
||||
|
||||
if (has_xtheadvector()) {
|
||||
unsigned long status;
|
||||
|
||||
/*
|
||||
* CSR_VCSR is defined as
|
||||
* [2:1] - vxrm[1:0]
|
||||
* [0] - vxsat
|
||||
* The earlier vector spec implemented by T-Head uses separate
|
||||
* registers for the same bit-elements, so just combine those
|
||||
* into the existing output field.
|
||||
*
|
||||
* Additionally T-Head cores need FS to be enabled when accessing
|
||||
* the VXRM and VXSAT CSRs, otherwise ending in illegal instructions.
|
||||
* Though the cores do not implement the VXRM and VXSAT fields in the
|
||||
* FCSR CSR that vector-0.7.1 specifies.
|
||||
*/
|
||||
status = csr_read_set(CSR_STATUS, SR_FS_DIRTY);
|
||||
dest->vcsr = csr_read(CSR_VXSAT) | csr_read(CSR_VXRM) << CSR_VXRM_SHIFT;
|
||||
|
||||
dest->vlenb = riscv_v_vsize / 32;
|
||||
|
||||
if ((status & SR_FS) != SR_FS_DIRTY)
|
||||
csr_write(CSR_STATUS, status);
|
||||
} else {
|
||||
dest->vcsr = csr_read(CSR_VCSR);
|
||||
dest->vlenb = csr_read(CSR_VLENB);
|
||||
}
|
||||
}
|
||||
|
||||
static __always_inline void __vstate_csr_restore(struct __riscv_v_ext_state *src)
|
||||
|
@ -96,9 +166,25 @@ static __always_inline void __vstate_csr_restore(struct __riscv_v_ext_state *src
|
|||
"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) :);
|
||||
: : "r" (src->vstart), "r" (src->vtype), "r" (src->vl));
|
||||
|
||||
if (has_xtheadvector()) {
|
||||
unsigned long status = csr_read(CSR_SSTATUS);
|
||||
|
||||
/*
|
||||
* Similar to __vstate_csr_save above, restore values for the
|
||||
* separate VXRM and VXSAT CSRs from the vcsr variable.
|
||||
*/
|
||||
status = csr_read_set(CSR_STATUS, SR_FS_DIRTY);
|
||||
|
||||
csr_write(CSR_VXRM, (src->vcsr >> CSR_VXRM_SHIFT) & CSR_VXRM_MASK);
|
||||
csr_write(CSR_VXSAT, src->vcsr & CSR_VXSAT_MASK);
|
||||
|
||||
if ((status & SR_FS) != SR_FS_DIRTY)
|
||||
csr_write(CSR_STATUS, status);
|
||||
} else {
|
||||
csr_write(CSR_VCSR, src->vcsr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
|
||||
|
@ -108,19 +194,33 @@ static inline void __riscv_v_vstate_save(struct __riscv_v_ext_state *save_to,
|
|||
|
||||
riscv_v_enable();
|
||||
__vstate_csr_save(save_to);
|
||||
asm volatile (
|
||||
".option push\n\t"
|
||||
".option arch, +zve32x\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");
|
||||
if (has_xtheadvector()) {
|
||||
asm volatile (
|
||||
"mv t0, %0\n\t"
|
||||
THEAD_VSETVLI_T4X0E8M8D1
|
||||
THEAD_VSB_V_V0T0
|
||||
"add t0, t0, t4\n\t"
|
||||
THEAD_VSB_V_V0T0
|
||||
"add t0, t0, t4\n\t"
|
||||
THEAD_VSB_V_V0T0
|
||||
"add t0, t0, t4\n\t"
|
||||
THEAD_VSB_V_V0T0
|
||||
: : "r" (datap) : "memory", "t0", "t4");
|
||||
} else {
|
||||
asm volatile (
|
||||
".option push\n\t"
|
||||
".option arch, +zve32x\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();
|
||||
}
|
||||
|
||||
|
@ -130,19 +230,33 @@ static inline void __riscv_v_vstate_restore(struct __riscv_v_ext_state *restore_
|
|||
unsigned long vl;
|
||||
|
||||
riscv_v_enable();
|
||||
asm volatile (
|
||||
".option push\n\t"
|
||||
".option arch, +zve32x\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");
|
||||
if (has_xtheadvector()) {
|
||||
asm volatile (
|
||||
"mv t0, %0\n\t"
|
||||
THEAD_VSETVLI_T4X0E8M8D1
|
||||
THEAD_VLB_V_V0T0
|
||||
"add t0, t0, t4\n\t"
|
||||
THEAD_VLB_V_V0T0
|
||||
"add t0, t0, t4\n\t"
|
||||
THEAD_VLB_V_V0T0
|
||||
"add t0, t0, t4\n\t"
|
||||
THEAD_VLB_V_V0T0
|
||||
: : "r" (datap) : "memory", "t0", "t4");
|
||||
} else {
|
||||
asm volatile (
|
||||
".option push\n\t"
|
||||
".option arch, +zve32x\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();
|
||||
}
|
||||
|
@ -152,33 +266,41 @@ static inline void __riscv_v_vstate_discard(void)
|
|||
unsigned long vl, vtype_inval = 1UL << (BITS_PER_LONG - 1);
|
||||
|
||||
riscv_v_enable();
|
||||
if (has_xtheadvector())
|
||||
asm volatile (THEAD_VSETVLI_T4X0E8M8D1 : : : "t4");
|
||||
else
|
||||
asm volatile (
|
||||
".option push\n\t"
|
||||
".option arch, +zve32x\n\t"
|
||||
"vsetvli %0, x0, e8, m8, ta, ma\n\t"
|
||||
".option pop\n\t": "=&r" (vl));
|
||||
|
||||
asm volatile (
|
||||
".option push\n\t"
|
||||
".option arch, +zve32x\n\t"
|
||||
"vsetvli %0, x0, e8, m8, ta, ma\n\t"
|
||||
"vmv.v.i v0, -1\n\t"
|
||||
"vmv.v.i v8, -1\n\t"
|
||||
"vmv.v.i v16, -1\n\t"
|
||||
"vmv.v.i v24, -1\n\t"
|
||||
"vsetvl %0, x0, %1\n\t"
|
||||
".option pop\n\t"
|
||||
: "=&r" (vl) : "r" (vtype_inval) : "memory");
|
||||
: "=&r" (vl) : "r" (vtype_inval));
|
||||
|
||||
riscv_v_disable();
|
||||
}
|
||||
|
||||
static inline void riscv_v_vstate_discard(struct pt_regs *regs)
|
||||
{
|
||||
if ((regs->status & SR_VS) == SR_VS_OFF)
|
||||
return;
|
||||
|
||||
__riscv_v_vstate_discard();
|
||||
__riscv_v_vstate_dirty(regs);
|
||||
if (riscv_v_vstate_query(regs)) {
|
||||
__riscv_v_vstate_discard();
|
||||
__riscv_v_vstate_dirty(regs);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void riscv_v_vstate_save(struct __riscv_v_ext_state *vstate,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if ((regs->status & SR_VS) == SR_VS_DIRTY) {
|
||||
if (__riscv_v_vstate_check(regs->status, DIRTY)) {
|
||||
__riscv_v_vstate_save(vstate, vstate->datap);
|
||||
__riscv_v_vstate_clean(regs);
|
||||
}
|
||||
|
@ -187,7 +309,7 @@ static inline void riscv_v_vstate_save(struct __riscv_v_ext_state *vstate,
|
|||
static inline void riscv_v_vstate_restore(struct __riscv_v_ext_state *vstate,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if ((regs->status & SR_VS) != SR_VS_OFF) {
|
||||
if (riscv_v_vstate_query(regs)) {
|
||||
__riscv_v_vstate_restore(vstate, vstate->datap);
|
||||
__riscv_v_vstate_clean(regs);
|
||||
}
|
||||
|
@ -196,7 +318,7 @@ static inline void riscv_v_vstate_restore(struct __riscv_v_ext_state *vstate,
|
|||
static inline void riscv_v_vstate_set_restore(struct task_struct *task,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
if ((regs->status & SR_VS) != SR_VS_OFF) {
|
||||
if (riscv_v_vstate_query(regs)) {
|
||||
set_tsk_thread_flag(task, TIF_RISCV_V_DEFER_RESTORE);
|
||||
riscv_v_vstate_on(regs);
|
||||
}
|
||||
|
@ -270,6 +392,8 @@ struct pt_regs;
|
|||
static inline int riscv_v_setup_vsize(void) { return -EOPNOTSUPP; }
|
||||
static __always_inline bool has_vector(void) { return false; }
|
||||
static __always_inline bool insn_is_vector(u32 insn_buf) { return false; }
|
||||
static __always_inline bool has_xtheadvector_no_alternatives(void) { return false; }
|
||||
static __always_inline bool has_xtheadvector(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; }
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H
|
||||
#define _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_H
|
||||
|
||||
#include <asm/vendor_extensions.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Extension keys must be strictly less than RISCV_ISA_VENDOR_EXT_MAX.
|
||||
*/
|
||||
#define RISCV_ISA_VENDOR_EXT_XTHEADVECTOR 0
|
||||
|
||||
extern struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead;
|
||||
|
||||
#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD
|
||||
void disable_xtheadvector(void);
|
||||
#else
|
||||
static inline void disable_xtheadvector(void) { }
|
||||
#endif
|
||||
|
||||
/* Extension specific helpers */
|
||||
|
||||
/*
|
||||
* Vector 0.7.1 as used for example on T-Head Xuantie cores, uses an older
|
||||
* encoding for vsetvli (ta, ma vs. d1), so provide an instruction for
|
||||
* vsetvli t4, x0, e8, m8, d1
|
||||
*/
|
||||
#define THEAD_VSETVLI_T4X0E8M8D1 ".long 0x00307ed7\n\t"
|
||||
|
||||
/*
|
||||
* While in theory, the vector-0.7.1 vsb.v and vlb.v result in the same
|
||||
* encoding as the standard vse8.v and vle8.v, compilers seem to optimize
|
||||
* the call resulting in a different encoding and then using a value for
|
||||
* the "mop" field that is not part of vector-0.7.1
|
||||
* So encode specific variants for vstate_save and _restore.
|
||||
*/
|
||||
#define THEAD_VSB_V_V0T0 ".long 0x02028027\n\t"
|
||||
#define THEAD_VSB_V_V8T0 ".long 0x02028427\n\t"
|
||||
#define THEAD_VSB_V_V16T0 ".long 0x02028827\n\t"
|
||||
#define THEAD_VSB_V_V24T0 ".long 0x02028c27\n\t"
|
||||
#define THEAD_VLB_V_V0T0 ".long 0x012028007\n\t"
|
||||
#define THEAD_VLB_V_V8T0 ".long 0x012028407\n\t"
|
||||
#define THEAD_VLB_V_V16T0 ".long 0x012028807\n\t"
|
||||
#define THEAD_VLB_V_V24T0 ".long 0x012028c07\n\t"
|
||||
|
||||
#endif
|
|
@ -0,0 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_HWPROBE_H
|
||||
#define _ASM_RISCV_VENDOR_EXTENSIONS_THEAD_HWPROBE_H
|
||||
|
||||
#include <linux/cpumask.h>
|
||||
|
||||
#include <uapi/asm/hwprobe.h>
|
||||
|
||||
#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD
|
||||
void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair, const struct cpumask *cpus);
|
||||
#else
|
||||
static inline void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair,
|
||||
const struct cpumask *cpus)
|
||||
{
|
||||
pair->value = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,37 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright 2024 Rivos, Inc
|
||||
*/
|
||||
|
||||
#ifndef _ASM_RISCV_SYS_HWPROBE_H
|
||||
#define _ASM_RISCV_SYS_HWPROBE_H
|
||||
|
||||
#include <asm/cpufeature.h>
|
||||
|
||||
#define VENDOR_EXT_KEY(ext) \
|
||||
do { \
|
||||
if (__riscv_isa_extension_available(isainfo->isa, RISCV_ISA_VENDOR_EXT_##ext)) \
|
||||
pair->value |= RISCV_HWPROBE_VENDOR_EXT_##ext; \
|
||||
else \
|
||||
missing |= RISCV_HWPROBE_VENDOR_EXT_##ext; \
|
||||
} while (false)
|
||||
|
||||
/*
|
||||
* Loop through and record extensions that 1) anyone has, and 2) anyone
|
||||
* doesn't have.
|
||||
*
|
||||
* _extension_checks is an arbitrary C block to set the values of pair->value
|
||||
* and missing. It should be filled with VENDOR_EXT_KEY expressions.
|
||||
*/
|
||||
#define VENDOR_EXTENSION_SUPPORTED(pair, cpus, per_hart_vendor_bitmap, _extension_checks) \
|
||||
do { \
|
||||
int cpu; \
|
||||
u64 missing = 0; \
|
||||
for_each_cpu(cpu, (cpus)) { \
|
||||
struct riscv_isavendorinfo *isainfo = &(per_hart_vendor_bitmap)[cpu]; \
|
||||
_extension_checks \
|
||||
} \
|
||||
(pair)->value &= ~missing; \
|
||||
} while (false) \
|
||||
|
||||
#endif /* _ASM_RISCV_SYS_HWPROBE_H */
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright 2023 Rivos, Inc
|
||||
* Copyright 2023-2024 Rivos, Inc
|
||||
*/
|
||||
|
||||
#ifndef _UAPI_ASM_HWPROBE_H
|
||||
|
@ -94,6 +94,7 @@ struct riscv_hwprobe {
|
|||
#define RISCV_HWPROBE_MISALIGNED_VECTOR_SLOW 2
|
||||
#define RISCV_HWPROBE_MISALIGNED_VECTOR_FAST 3
|
||||
#define RISCV_HWPROBE_MISALIGNED_VECTOR_UNSUPPORTED 4
|
||||
#define RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0 11
|
||||
/* Increase RISCV_HWPROBE_MAX_KEY when adding items. */
|
||||
|
||||
/* Flags */
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
|
||||
#define RISCV_HWPROBE_VENDOR_EXT_XTHEADVECTOR (1 << 0)
|
|
@ -123,3 +123,5 @@ obj-$(CONFIG_COMPAT) += compat_vdso/
|
|||
obj-$(CONFIG_64BIT) += pi/
|
||||
obj-$(CONFIG_ACPI) += acpi.o
|
||||
obj-$(CONFIG_ACPI_NUMA) += acpi_numa.o
|
||||
|
||||
obj-$(CONFIG_GENERIC_CPU_VULNERABILITIES) += bugs.o
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2024 Rivos Inc.
|
||||
*/
|
||||
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sprintf.h>
|
||||
|
||||
#include <asm/bugs.h>
|
||||
#include <asm/vendor_extensions/thead.h>
|
||||
|
||||
static enum mitigation_state ghostwrite_state;
|
||||
|
||||
void ghostwrite_set_vulnerable(void)
|
||||
{
|
||||
ghostwrite_state = VULNERABLE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Vendor extension alternatives will use the value set at the time of boot
|
||||
* alternative patching, thus this must be called before boot alternatives are
|
||||
* patched (and after extension probing) to be effective.
|
||||
*
|
||||
* Returns true if mitgated, false otherwise.
|
||||
*/
|
||||
bool ghostwrite_enable_mitigation(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR) &&
|
||||
ghostwrite_state == VULNERABLE && !cpu_mitigations_off()) {
|
||||
disable_xtheadvector();
|
||||
ghostwrite_state = MITIGATED;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum mitigation_state ghostwrite_get_state(void)
|
||||
{
|
||||
return ghostwrite_state;
|
||||
}
|
||||
|
||||
ssize_t cpu_show_ghostwrite(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR)) {
|
||||
switch (ghostwrite_state) {
|
||||
case UNAFFECTED:
|
||||
return sprintf(buf, "Not affected\n");
|
||||
case MITIGATED:
|
||||
return sprintf(buf, "Mitigation: xtheadvector disabled\n");
|
||||
case VULNERABLE:
|
||||
fallthrough;
|
||||
default:
|
||||
return sprintf(buf, "Vulnerable\n");
|
||||
}
|
||||
} else {
|
||||
return sprintf(buf, "Not affected\n");
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <asm/acpi.h>
|
||||
#include <asm/alternative.h>
|
||||
#include <asm/bugs.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/hwcap.h>
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include <asm/sbi.h>
|
||||
#include <asm/vector.h>
|
||||
#include <asm/vendor_extensions.h>
|
||||
#include <asm/vendor_extensions/thead.h>
|
||||
|
||||
#define NUM_ALPHA_EXTS ('z' - 'a' + 1)
|
||||
|
||||
|
@ -39,6 +41,8 @@ static DECLARE_BITMAP(riscv_isa, RISCV_ISA_EXT_MAX) __read_mostly;
|
|||
/* Per-cpu ISA extensions. */
|
||||
struct riscv_isainfo hart_isa[NR_CPUS];
|
||||
|
||||
u32 thead_vlenb_of;
|
||||
|
||||
/**
|
||||
* riscv_isa_extension_base() - Get base extension word
|
||||
*
|
||||
|
@ -791,9 +795,50 @@ static void __init riscv_fill_vendor_ext_list(int cpu)
|
|||
}
|
||||
}
|
||||
|
||||
static int has_thead_homogeneous_vlenb(void)
|
||||
{
|
||||
int cpu;
|
||||
u32 prev_vlenb = 0;
|
||||
u32 vlenb;
|
||||
|
||||
/* Ignore thead,vlenb property if xtheavector is not enabled in the kernel */
|
||||
if (!IS_ENABLED(CONFIG_RISCV_ISA_XTHEADVECTOR))
|
||||
return 0;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct device_node *cpu_node;
|
||||
|
||||
cpu_node = of_cpu_device_node_get(cpu);
|
||||
if (!cpu_node) {
|
||||
pr_warn("Unable to find cpu node\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(cpu_node, "thead,vlenb", &vlenb)) {
|
||||
of_node_put(cpu_node);
|
||||
|
||||
if (prev_vlenb)
|
||||
return -ENOENT;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev_vlenb && vlenb != prev_vlenb) {
|
||||
of_node_put(cpu_node);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
prev_vlenb = vlenb;
|
||||
of_node_put(cpu_node);
|
||||
}
|
||||
|
||||
thead_vlenb_of = vlenb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap)
|
||||
{
|
||||
unsigned int cpu;
|
||||
bool mitigated;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
unsigned long this_hwcap = 0;
|
||||
|
@ -844,6 +889,17 @@ static int __init riscv_fill_hwcap_from_ext_list(unsigned long *isa2hwcap)
|
|||
riscv_fill_vendor_ext_list(cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute ghostwrite mitigation immediately after detecting extensions
|
||||
* to disable xtheadvector if necessary.
|
||||
*/
|
||||
mitigated = ghostwrite_enable_mitigation();
|
||||
|
||||
if (!mitigated && has_xtheadvector_no_alternatives() && has_thead_homogeneous_vlenb() < 0) {
|
||||
pr_warn("Unsupported heterogeneous vlenb detected, vector extension disabled.\n");
|
||||
disable_xtheadvector();
|
||||
}
|
||||
|
||||
if (bitmap_empty(riscv_isa, RISCV_ISA_EXT_MAX))
|
||||
return -ENOENT;
|
||||
|
||||
|
@ -896,7 +952,8 @@ void __init riscv_fill_hwcap(void)
|
|||
elf_hwcap &= ~COMPAT_HWCAP_ISA_F;
|
||||
}
|
||||
|
||||
if (__riscv_isa_extension_available(NULL, RISCV_ISA_EXT_ZVE32X)) {
|
||||
if (__riscv_isa_extension_available(NULL, RISCV_ISA_EXT_ZVE32X) ||
|
||||
has_xtheadvector_no_alternatives()) {
|
||||
/*
|
||||
* This cannot fail when called on the boot hart
|
||||
*/
|
||||
|
|
|
@ -143,7 +143,7 @@ static int riscv_v_start_kernel_context(bool *is_nested)
|
|||
|
||||
/* Transfer the ownership of V from user to kernel, then save */
|
||||
riscv_v_start(RISCV_PREEMPT_V | RISCV_PREEMPT_V_DIRTY);
|
||||
if ((task_pt_regs(current)->status & SR_VS) == SR_VS_DIRTY) {
|
||||
if (__riscv_v_vstate_check(task_pt_regs(current)->status, DIRTY)) {
|
||||
uvstate = ¤t->thread.vstate;
|
||||
__riscv_v_vstate_save(uvstate, uvstate->datap);
|
||||
}
|
||||
|
@ -160,7 +160,7 @@ asmlinkage void riscv_v_context_nesting_start(struct pt_regs *regs)
|
|||
return;
|
||||
|
||||
depth = riscv_v_ctx_get_depth();
|
||||
if (depth == 0 && (regs->status & SR_VS) == SR_VS_DIRTY)
|
||||
if (depth == 0 && __riscv_v_vstate_check(regs->status, DIRTY))
|
||||
riscv_preempt_v_set_dirty();
|
||||
|
||||
riscv_v_ctx_depth_inc();
|
||||
|
@ -208,7 +208,7 @@ void kernel_vector_begin(void)
|
|||
{
|
||||
bool nested = false;
|
||||
|
||||
if (WARN_ON(!has_vector()))
|
||||
if (WARN_ON(!(has_vector() || has_xtheadvector())))
|
||||
return;
|
||||
|
||||
BUG_ON(!may_use_simd());
|
||||
|
@ -236,7 +236,7 @@ EXPORT_SYMBOL_GPL(kernel_vector_begin);
|
|||
*/
|
||||
void kernel_vector_end(void)
|
||||
{
|
||||
if (WARN_ON(!has_vector()))
|
||||
if (WARN_ON(!(has_vector() || has_xtheadvector())))
|
||||
return;
|
||||
|
||||
riscv_v_disable();
|
||||
|
|
|
@ -190,7 +190,7 @@ void flush_thread(void)
|
|||
void arch_release_task_struct(struct task_struct *tsk)
|
||||
{
|
||||
/* Free the vector context of datap. */
|
||||
if (has_vector())
|
||||
if (has_vector() || has_xtheadvector())
|
||||
riscv_v_thread_free(tsk);
|
||||
}
|
||||
|
||||
|
@ -240,7 +240,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
|
|||
p->thread.s[0] = 0;
|
||||
}
|
||||
p->thread.riscv_v_flags = 0;
|
||||
if (has_vector())
|
||||
if (has_vector() || has_xtheadvector())
|
||||
riscv_v_thread_alloc(p);
|
||||
p->thread.ra = (unsigned long)ret_from_fork;
|
||||
p->thread.sp = (unsigned long)childregs; /* kernel sp */
|
||||
|
|
|
@ -189,7 +189,7 @@ static long restore_sigcontext(struct pt_regs *regs,
|
|||
|
||||
return 0;
|
||||
case RISCV_V_MAGIC:
|
||||
if (!has_vector() || !riscv_v_vstate_query(regs) ||
|
||||
if (!(has_vector() || has_xtheadvector()) || !riscv_v_vstate_query(regs) ||
|
||||
size != riscv_v_sc_size)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -211,7 +211,7 @@ static size_t get_rt_frame_size(bool cal_all)
|
|||
|
||||
frame_size = sizeof(*frame);
|
||||
|
||||
if (has_vector()) {
|
||||
if (has_vector() || has_xtheadvector()) {
|
||||
if (cal_all || riscv_v_vstate_query(task_pt_regs(current)))
|
||||
total_context_size += riscv_v_sc_size;
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ static long setup_sigcontext(struct rt_sigframe __user *frame,
|
|||
if (has_fpu())
|
||||
err |= save_fp_state(regs, &sc->sc_fpregs);
|
||||
/* Save the vector state. */
|
||||
if (has_vector() && riscv_v_vstate_query(regs))
|
||||
if ((has_vector() || has_xtheadvector()) && 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);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/vector.h>
|
||||
#include <asm/vendor_extensions/thead_hwprobe.h>
|
||||
#include <vdso/vsyscall.h>
|
||||
|
||||
|
||||
|
@ -286,6 +287,10 @@ static void hwprobe_one_pair(struct riscv_hwprobe *pair,
|
|||
pair->value = riscv_timebase;
|
||||
break;
|
||||
|
||||
case RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0:
|
||||
hwprobe_isa_vendor_ext_thead_0(pair, cpus);
|
||||
break;
|
||||
|
||||
/*
|
||||
* For forward compatibility, unknown keys don't fail the whole
|
||||
* call, but get their element key set to -1 and value set to 0
|
||||
|
|
|
@ -33,7 +33,17 @@ int riscv_v_setup_vsize(void)
|
|||
{
|
||||
unsigned long this_vsize;
|
||||
|
||||
/* There are 32 vector registers with vlenb length. */
|
||||
/*
|
||||
* There are 32 vector registers with vlenb length.
|
||||
*
|
||||
* If the thead,vlenb property was provided by the firmware, use that
|
||||
* instead of probing the CSRs.
|
||||
*/
|
||||
if (thead_vlenb_of) {
|
||||
riscv_v_vsize = thead_vlenb_of * 32;
|
||||
return 0;
|
||||
}
|
||||
|
||||
riscv_v_enable();
|
||||
this_vsize = csr_read(CSR_VLENB) * 32;
|
||||
riscv_v_disable();
|
||||
|
@ -53,7 +63,7 @@ int riscv_v_setup_vsize(void)
|
|||
|
||||
void __init riscv_v_setup_ctx_cache(void)
|
||||
{
|
||||
if (!has_vector())
|
||||
if (!(has_vector() || has_xtheadvector()))
|
||||
return;
|
||||
|
||||
riscv_v_user_cachep = kmem_cache_create_usercopy("riscv_vector_ctx",
|
||||
|
@ -173,7 +183,7 @@ bool riscv_v_first_use_handler(struct pt_regs *regs)
|
|||
u32 __user *epc = (u32 __user *)regs->epc;
|
||||
u32 insn = (u32)regs->badaddr;
|
||||
|
||||
if (!has_vector())
|
||||
if (!(has_vector() || has_xtheadvector()))
|
||||
return false;
|
||||
|
||||
/* Do not handle if V is not supported, or disabled */
|
||||
|
@ -216,7 +226,7 @@ void riscv_v_vstate_ctrl_init(struct task_struct *tsk)
|
|||
bool inherit;
|
||||
int cur, next;
|
||||
|
||||
if (!has_vector())
|
||||
if (!(has_vector() || has_xtheadvector()))
|
||||
return;
|
||||
|
||||
next = riscv_v_ctrl_get_next(tsk);
|
||||
|
@ -238,7 +248,7 @@ void riscv_v_vstate_ctrl_init(struct task_struct *tsk)
|
|||
|
||||
long riscv_v_vstate_ctrl_get_current(void)
|
||||
{
|
||||
if (!has_vector())
|
||||
if (!(has_vector() || has_xtheadvector()))
|
||||
return -EINVAL;
|
||||
|
||||
return current->thread.vstate_ctrl & PR_RISCV_V_VSTATE_CTRL_MASK;
|
||||
|
@ -249,7 +259,7 @@ long riscv_v_vstate_ctrl_set_current(unsigned long arg)
|
|||
bool inherit;
|
||||
int cur, next;
|
||||
|
||||
if (!has_vector())
|
||||
if (!(has_vector() || has_xtheadvector()))
|
||||
return -EINVAL;
|
||||
|
||||
if (arg & ~PR_RISCV_V_VSTATE_CTRL_MASK)
|
||||
|
@ -299,7 +309,7 @@ static const struct ctl_table riscv_v_default_vstate_table[] = {
|
|||
|
||||
static int __init riscv_v_sysctl_init(void)
|
||||
{
|
||||
if (has_vector())
|
||||
if (has_vector() || has_xtheadvector())
|
||||
if (!register_sysctl("abi", riscv_v_default_vstate_table))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
|
@ -309,7 +319,7 @@ static int __init riscv_v_sysctl_init(void)
|
|||
static int __init riscv_v_sysctl_init(void) { return 0; }
|
||||
#endif /* ! CONFIG_SYSCTL */
|
||||
|
||||
static int riscv_v_init(void)
|
||||
static int __init riscv_v_init(void)
|
||||
{
|
||||
return riscv_v_sysctl_init();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <asm/vendorid_list.h>
|
||||
#include <asm/vendor_extensions.h>
|
||||
#include <asm/vendor_extensions/andes.h>
|
||||
#include <asm/vendor_extensions/thead.h>
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/types.h>
|
||||
|
@ -14,6 +15,9 @@ struct riscv_isa_vendor_ext_data_list *riscv_isa_vendor_ext_list[] = {
|
|||
#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_ANDES
|
||||
&riscv_isa_vendor_ext_list_andes,
|
||||
#endif
|
||||
#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD
|
||||
&riscv_isa_vendor_ext_list_thead,
|
||||
#endif
|
||||
};
|
||||
|
||||
const size_t riscv_isa_vendor_ext_list_size = ARRAY_SIZE(riscv_isa_vendor_ext_list);
|
||||
|
@ -41,6 +45,12 @@ bool __riscv_isa_vendor_extension_available(int cpu, unsigned long vendor, unsig
|
|||
cpu_bmap = riscv_isa_vendor_ext_list_andes.per_hart_isa_bitmap;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_RISCV_ISA_VENDOR_EXT_THEAD
|
||||
case THEAD_VENDOR_ID:
|
||||
bmap = &riscv_isa_vendor_ext_list_thead.all_harts_isa_bitmap;
|
||||
cpu_bmap = riscv_isa_vendor_ext_list_thead.per_hart_isa_bitmap;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_ANDES) += andes.o
|
||||
obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_THEAD) += thead.o
|
||||
obj-$(CONFIG_RISCV_ISA_VENDOR_EXT_THEAD) += thead_hwprobe.o
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/vendor_extensions.h>
|
||||
#include <asm/vendor_extensions/thead.h>
|
||||
|
||||
#include <linux/array_size.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* All T-Head vendor extensions supported in Linux */
|
||||
static const struct riscv_isa_ext_data riscv_isa_vendor_ext_thead[] = {
|
||||
__RISCV_ISA_EXT_DATA(xtheadvector, RISCV_ISA_VENDOR_EXT_XTHEADVECTOR),
|
||||
};
|
||||
|
||||
struct riscv_isa_vendor_ext_data_list riscv_isa_vendor_ext_list_thead = {
|
||||
.ext_data_count = ARRAY_SIZE(riscv_isa_vendor_ext_thead),
|
||||
.ext_data = riscv_isa_vendor_ext_thead,
|
||||
};
|
||||
|
||||
void disable_xtheadvector(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu)
|
||||
clear_bit(RISCV_ISA_VENDOR_EXT_XTHEADVECTOR, riscv_isa_vendor_ext_list_thead.per_hart_isa_bitmap[cpu].isa);
|
||||
|
||||
clear_bit(RISCV_ISA_VENDOR_EXT_XTHEADVECTOR, riscv_isa_vendor_ext_list_thead.all_harts_isa_bitmap.isa);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include <asm/vendor_extensions/thead.h>
|
||||
#include <asm/vendor_extensions/thead_hwprobe.h>
|
||||
#include <asm/vendor_extensions/vendor_hwprobe.h>
|
||||
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <uapi/asm/hwprobe.h>
|
||||
#include <uapi/asm/vendor/thead.h>
|
||||
|
||||
void hwprobe_isa_vendor_ext_thead_0(struct riscv_hwprobe *pair, const struct cpumask *cpus)
|
||||
{
|
||||
VENDOR_EXTENSION_SUPPORTED(pair, cpus,
|
||||
riscv_isa_vendor_ext_list_thead.per_hart_isa_bitmap, {
|
||||
VENDOR_EXT_KEY(XTHEADVECTOR);
|
||||
});
|
||||
}
|
|
@ -22,6 +22,57 @@
|
|||
|
||||
#include "../kernel/head.h"
|
||||
|
||||
static void show_pte(unsigned long addr)
|
||||
{
|
||||
pgd_t *pgdp, pgd;
|
||||
p4d_t *p4dp, p4d;
|
||||
pud_t *pudp, pud;
|
||||
pmd_t *pmdp, pmd;
|
||||
pte_t *ptep, pte;
|
||||
struct mm_struct *mm = current->mm;
|
||||
|
||||
if (!mm)
|
||||
mm = &init_mm;
|
||||
|
||||
pr_alert("Current %s pgtable: %luK pagesize, %d-bit VAs, pgdp=0x%016llx\n",
|
||||
current->comm, PAGE_SIZE / SZ_1K, VA_BITS,
|
||||
mm == &init_mm ? (u64)__pa_symbol(mm->pgd) : virt_to_phys(mm->pgd));
|
||||
|
||||
pgdp = pgd_offset(mm, addr);
|
||||
pgd = pgdp_get(pgdp);
|
||||
pr_alert("[%016lx] pgd=%016lx", addr, pgd_val(pgd));
|
||||
if (pgd_none(pgd) || pgd_bad(pgd) || pgd_leaf(pgd))
|
||||
goto out;
|
||||
|
||||
p4dp = p4d_offset(pgdp, addr);
|
||||
p4d = p4dp_get(p4dp);
|
||||
pr_cont(", p4d=%016lx", p4d_val(p4d));
|
||||
if (p4d_none(p4d) || p4d_bad(p4d) || p4d_leaf(p4d))
|
||||
goto out;
|
||||
|
||||
pudp = pud_offset(p4dp, addr);
|
||||
pud = pudp_get(pudp);
|
||||
pr_cont(", pud=%016lx", pud_val(pud));
|
||||
if (pud_none(pud) || pud_bad(pud) || pud_leaf(pud))
|
||||
goto out;
|
||||
|
||||
pmdp = pmd_offset(pudp, addr);
|
||||
pmd = pmdp_get(pmdp);
|
||||
pr_cont(", pmd=%016lx", pmd_val(pmd));
|
||||
if (pmd_none(pmd) || pmd_bad(pmd) || pmd_leaf(pmd))
|
||||
goto out;
|
||||
|
||||
ptep = pte_offset_map(pmdp, addr);
|
||||
if (!ptep)
|
||||
goto out;
|
||||
|
||||
pte = ptep_get(ptep);
|
||||
pr_cont(", pte=%016lx", pte_val(pte));
|
||||
pte_unmap(ptep);
|
||||
out:
|
||||
pr_cont("\n");
|
||||
}
|
||||
|
||||
static void die_kernel_fault(const char *msg, unsigned long addr,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
|
@ -31,6 +82,7 @@ static void die_kernel_fault(const char *msg, unsigned long addr,
|
|||
addr);
|
||||
|
||||
bust_spinlocks(0);
|
||||
show_pte(addr);
|
||||
die(regs, "Oops");
|
||||
make_task_dead(SIGKILL);
|
||||
}
|
||||
|
|
|
@ -268,8 +268,12 @@ static void __init setup_bootmem(void)
|
|||
*/
|
||||
if (IS_ENABLED(CONFIG_64BIT) && IS_ENABLED(CONFIG_MMU)) {
|
||||
max_mapped_addr = __pa(PAGE_OFFSET) + KERN_VIRT_SIZE;
|
||||
memblock_cap_memory_range(phys_ram_base,
|
||||
max_mapped_addr - phys_ram_base);
|
||||
if (memblock_end_of_DRAM() > max_mapped_addr) {
|
||||
memblock_cap_memory_range(phys_ram_base,
|
||||
max_mapped_addr - phys_ram_base);
|
||||
pr_warn("Physical memory overflows the linear mapping size: region above %pa removed",
|
||||
&max_mapped_addr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -599,6 +599,7 @@ CPU_SHOW_VULN_FALLBACK(retbleed);
|
|||
CPU_SHOW_VULN_FALLBACK(spec_rstack_overflow);
|
||||
CPU_SHOW_VULN_FALLBACK(gds);
|
||||
CPU_SHOW_VULN_FALLBACK(reg_file_data_sampling);
|
||||
CPU_SHOW_VULN_FALLBACK(ghostwrite);
|
||||
|
||||
static DEVICE_ATTR(meltdown, 0444, cpu_show_meltdown, NULL);
|
||||
static DEVICE_ATTR(spectre_v1, 0444, cpu_show_spectre_v1, NULL);
|
||||
|
@ -614,6 +615,7 @@ static DEVICE_ATTR(retbleed, 0444, cpu_show_retbleed, NULL);
|
|||
static DEVICE_ATTR(spec_rstack_overflow, 0444, cpu_show_spec_rstack_overflow, NULL);
|
||||
static DEVICE_ATTR(gather_data_sampling, 0444, cpu_show_gds, NULL);
|
||||
static DEVICE_ATTR(reg_file_data_sampling, 0444, cpu_show_reg_file_data_sampling, NULL);
|
||||
static DEVICE_ATTR(ghostwrite, 0444, cpu_show_ghostwrite, NULL);
|
||||
|
||||
static struct attribute *cpu_root_vulnerabilities_attrs[] = {
|
||||
&dev_attr_meltdown.attr,
|
||||
|
@ -630,6 +632,7 @@ static struct attribute *cpu_root_vulnerabilities_attrs[] = {
|
|||
&dev_attr_spec_rstack_overflow.attr,
|
||||
&dev_attr_gather_data_sampling.attr,
|
||||
&dev_attr_reg_file_data_sampling.attr,
|
||||
&dev_attr_ghostwrite.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
@ -77,6 +77,7 @@ extern ssize_t cpu_show_gds(struct device *dev,
|
|||
struct device_attribute *attr, char *buf);
|
||||
extern ssize_t cpu_show_reg_file_data_sampling(struct device *dev,
|
||||
struct device_attribute *attr, char *buf);
|
||||
extern ssize_t cpu_show_ghostwrite(struct device *dev, struct device_attribute *attr, char *buf);
|
||||
|
||||
extern __printf(4, 5)
|
||||
struct device *cpu_device_create(struct device *parent, void *drvdata,
|
||||
|
|
Binary file not shown.
|
@ -1,3 +1,4 @@
|
|||
vstate_exec_nolibc
|
||||
vstate_prctl
|
||||
v_initval_nolibc
|
||||
v_initval
|
||||
v_exec_initval_nolibc
|
||||
|
|
|
@ -2,18 +2,27 @@
|
|||
# Copyright (C) 2021 ARM Limited
|
||||
# Originally tools/testing/arm64/abi/Makefile
|
||||
|
||||
TEST_GEN_PROGS := vstate_prctl v_initval_nolibc
|
||||
TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc
|
||||
TEST_GEN_PROGS := v_initval vstate_prctl
|
||||
TEST_GEN_PROGS_EXTENDED := vstate_exec_nolibc v_exec_initval_nolibc
|
||||
|
||||
include ../../lib.mk
|
||||
|
||||
$(OUTPUT)/vstate_prctl: vstate_prctl.c ../hwprobe/sys_hwprobe.S
|
||||
$(OUTPUT)/sys_hwprobe.o: ../hwprobe/sys_hwprobe.S
|
||||
$(CC) -static -c -o$@ $(CFLAGS) $^
|
||||
|
||||
$(OUTPUT)/v_helpers.o: v_helpers.c
|
||||
$(CC) -static -c -o$@ $(CFLAGS) $^
|
||||
|
||||
$(OUTPUT)/vstate_prctl: vstate_prctl.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
|
||||
$(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
|
||||
|
||||
$(OUTPUT)/v_initval_nolibc: v_initval_nolibc.c
|
||||
$(OUTPUT)/v_initval: v_initval.c $(OUTPUT)/sys_hwprobe.o $(OUTPUT)/v_helpers.o
|
||||
$(CC) -static -o$@ $(CFLAGS) $(LDFLAGS) $^
|
||||
|
||||
$(OUTPUT)/v_exec_initval_nolibc: v_exec_initval_nolibc.c
|
||||
$(CC) -nostdlib -static -include ../../../../include/nolibc/nolibc.h \
|
||||
-Wall $(CFLAGS) $(LDFLAGS) $^ -o $@ -lgcc
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Get values of vector registers as soon as the program starts to test if
|
||||
* is properly cleaning the values before starting a new program. Vector
|
||||
* registers are caller saved, so no function calls may happen before reading
|
||||
* the values. To further ensure consistency, this file is compiled without
|
||||
* libc and without auto-vectorization.
|
||||
*
|
||||
* To be "clean" all values must be either all ones or all zeroes.
|
||||
*/
|
||||
|
||||
#define __stringify_1(x...) #x
|
||||
#define __stringify(x...) __stringify_1(x)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char prev_value = 0, value;
|
||||
unsigned long vl;
|
||||
int first = 1;
|
||||
|
||||
if (argc > 2 && strcmp(argv[2], "x"))
|
||||
asm volatile (
|
||||
// 0 | zimm[10:0] | rs1 | 1 1 1 | rd |1010111| vsetvli
|
||||
// vsetvli t4, x0, e8, m1, d1
|
||||
".4byte 0b00000000000000000111111011010111\n\t"
|
||||
"mv %[vl], t4\n\t"
|
||||
: [vl] "=r" (vl) : : "t4"
|
||||
);
|
||||
else
|
||||
asm volatile (
|
||||
".option push\n\t"
|
||||
".option arch, +v\n\t"
|
||||
"vsetvli %[vl], x0, e8, m1, ta, ma\n\t"
|
||||
".option pop\n\t"
|
||||
: [vl] "=r" (vl)
|
||||
);
|
||||
|
||||
#define CHECK_VECTOR_REGISTER(register) ({ \
|
||||
for (int i = 0; i < vl; i++) { \
|
||||
asm volatile ( \
|
||||
".option push\n\t" \
|
||||
".option arch, +v\n\t" \
|
||||
"vmv.x.s %0, " __stringify(register) "\n\t" \
|
||||
"vsrl.vi " __stringify(register) ", " __stringify(register) ", 8\n\t" \
|
||||
".option pop\n\t" \
|
||||
: "=r" (value)); \
|
||||
if (first) { \
|
||||
first = 0; \
|
||||
} else if (value != prev_value || !(value == 0x00 || value == 0xff)) { \
|
||||
printf("Register " __stringify(register) \
|
||||
" values not clean! value: %u\n", value); \
|
||||
exit(-1); \
|
||||
} \
|
||||
prev_value = value; \
|
||||
} \
|
||||
})
|
||||
|
||||
CHECK_VECTOR_REGISTER(v0);
|
||||
CHECK_VECTOR_REGISTER(v1);
|
||||
CHECK_VECTOR_REGISTER(v2);
|
||||
CHECK_VECTOR_REGISTER(v3);
|
||||
CHECK_VECTOR_REGISTER(v4);
|
||||
CHECK_VECTOR_REGISTER(v5);
|
||||
CHECK_VECTOR_REGISTER(v6);
|
||||
CHECK_VECTOR_REGISTER(v7);
|
||||
CHECK_VECTOR_REGISTER(v8);
|
||||
CHECK_VECTOR_REGISTER(v9);
|
||||
CHECK_VECTOR_REGISTER(v10);
|
||||
CHECK_VECTOR_REGISTER(v11);
|
||||
CHECK_VECTOR_REGISTER(v12);
|
||||
CHECK_VECTOR_REGISTER(v13);
|
||||
CHECK_VECTOR_REGISTER(v14);
|
||||
CHECK_VECTOR_REGISTER(v15);
|
||||
CHECK_VECTOR_REGISTER(v16);
|
||||
CHECK_VECTOR_REGISTER(v17);
|
||||
CHECK_VECTOR_REGISTER(v18);
|
||||
CHECK_VECTOR_REGISTER(v19);
|
||||
CHECK_VECTOR_REGISTER(v20);
|
||||
CHECK_VECTOR_REGISTER(v21);
|
||||
CHECK_VECTOR_REGISTER(v22);
|
||||
CHECK_VECTOR_REGISTER(v23);
|
||||
CHECK_VECTOR_REGISTER(v24);
|
||||
CHECK_VECTOR_REGISTER(v25);
|
||||
CHECK_VECTOR_REGISTER(v26);
|
||||
CHECK_VECTOR_REGISTER(v27);
|
||||
CHECK_VECTOR_REGISTER(v28);
|
||||
CHECK_VECTOR_REGISTER(v29);
|
||||
CHECK_VECTOR_REGISTER(v30);
|
||||
CHECK_VECTOR_REGISTER(v31);
|
||||
|
||||
#undef CHECK_VECTOR_REGISTER
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include "../hwprobe/hwprobe.h"
|
||||
#include <asm/vendor/thead.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
bool is_xtheadvector_supported(void)
|
||||
{
|
||||
struct riscv_hwprobe pair;
|
||||
|
||||
pair.key = RISCV_HWPROBE_KEY_VENDOR_EXT_THEAD_0;
|
||||
riscv_hwprobe(&pair, 1, 0, NULL, 0);
|
||||
return pair.value & RISCV_HWPROBE_VENDOR_EXT_XTHEADVECTOR;
|
||||
}
|
||||
|
||||
bool is_vector_supported(void)
|
||||
{
|
||||
struct riscv_hwprobe pair;
|
||||
|
||||
pair.key = RISCV_HWPROBE_KEY_IMA_EXT_0;
|
||||
riscv_hwprobe(&pair, 1, 0, NULL, 0);
|
||||
return pair.value & RISCV_HWPROBE_EXT_ZVE32X;
|
||||
}
|
||||
|
||||
int launch_test(char *next_program, int test_inherit, int xtheadvector)
|
||||
{
|
||||
char *exec_argv[4], *exec_envp[1];
|
||||
int rc, pid, status;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
printf("fork failed %d", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!pid) {
|
||||
exec_argv[0] = next_program;
|
||||
exec_argv[1] = test_inherit != 0 ? "x" : NULL;
|
||||
exec_argv[2] = xtheadvector != 0 ? "x" : NULL;
|
||||
exec_argv[3] = NULL;
|
||||
exec_envp[0] = NULL;
|
||||
/* launch the program again to check inherit */
|
||||
rc = execve(next_program, exec_argv, exec_envp);
|
||||
if (rc) {
|
||||
perror("execve");
|
||||
printf("child execve failed %d\n", rc);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
rc = waitpid(-1, &status, 0);
|
||||
if (rc < 0) {
|
||||
printf("waitpid failed\n");
|
||||
return -3;
|
||||
}
|
||||
|
||||
if ((WIFEXITED(status) && WEXITSTATUS(status) == -1) ||
|
||||
WIFSIGNALED(status)) {
|
||||
printf("child exited abnormally\n");
|
||||
return -4;
|
||||
}
|
||||
|
||||
return WEXITSTATUS(status);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#include <stdbool.h>
|
||||
|
||||
bool is_xtheadvector_supported(void);
|
||||
|
||||
bool is_vector_supported(void);
|
||||
|
||||
int launch_test(char *next_program, int test_inherit, int xtheadvector);
|
|
@ -0,0 +1,22 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include "../../kselftest_harness.h"
|
||||
#include "v_helpers.h"
|
||||
|
||||
#define NEXT_PROGRAM "./v_exec_initval_nolibc"
|
||||
|
||||
TEST(v_initval)
|
||||
{
|
||||
int xtheadvector = 0;
|
||||
|
||||
if (!is_vector_supported()) {
|
||||
if (is_xtheadvector_supported())
|
||||
xtheadvector = 1;
|
||||
else
|
||||
SKIP(return, "Vector not supported");
|
||||
}
|
||||
|
||||
ASSERT_EQ(0, launch_test(NEXT_PROGRAM, 0, xtheadvector));
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
|
@ -1,72 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
#include "../../kselftest.h"
|
||||
#define MAX_VSIZE (8192 * 32)
|
||||
|
||||
void dump(char *ptr, int size)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (i != 0) {
|
||||
if (i % 16 == 0)
|
||||
printf("\n");
|
||||
else if (i % 8 == 0)
|
||||
printf(" ");
|
||||
}
|
||||
printf("%02x ", ptr[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
unsigned long vl;
|
||||
char *datap, *tmp;
|
||||
|
||||
ksft_set_plan(1);
|
||||
|
||||
datap = malloc(MAX_VSIZE);
|
||||
if (!datap) {
|
||||
ksft_test_result_fail("fail to allocate memory for size = %d\n", MAX_VSIZE);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
tmp = datap;
|
||||
asm volatile (
|
||||
".option push\n\t"
|
||||
".option arch, +v\n\t"
|
||||
"vsetvli %0, x0, e8, m8, ta, ma\n\t"
|
||||
"vse8.v v0, (%2)\n\t"
|
||||
"add %1, %2, %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" (tmp) : "r" (datap) : "memory");
|
||||
|
||||
ksft_print_msg("vl = %lu\n", vl);
|
||||
|
||||
if (datap[0] != 0x00 && datap[0] != 0xff) {
|
||||
ksft_test_result_fail("v-regesters are not properly initialized\n");
|
||||
dump(datap, vl * 4);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
for (i = 1; i < vl * 4; i++) {
|
||||
if (datap[i] != datap[0]) {
|
||||
ksft_test_result_fail("detect stale values on v-regesters\n");
|
||||
dump(datap, vl * 4);
|
||||
exit(-2);
|
||||
}
|
||||
}
|
||||
|
||||
free(datap);
|
||||
|
||||
ksft_test_result_pass("tests for v_initval_nolibc pass\n");
|
||||
ksft_exit_pass();
|
||||
return 0;
|
||||
}
|
|
@ -6,13 +6,16 @@
|
|||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int rc, pid, status, test_inherit = 0;
|
||||
int rc, pid, status, test_inherit = 0, xtheadvector = 0;
|
||||
long ctrl, ctrl_c;
|
||||
char *exec_argv[2], *exec_envp[2];
|
||||
|
||||
if (argc > 1)
|
||||
if (argc > 1 && strcmp(argv[1], "x"))
|
||||
test_inherit = 1;
|
||||
|
||||
if (argc > 2 && strcmp(argv[2], "x"))
|
||||
xtheadvector = 1;
|
||||
|
||||
ctrl = my_syscall1(__NR_prctl, PR_RISCV_V_GET_CONTROL);
|
||||
if (ctrl < 0) {
|
||||
puts("PR_RISCV_V_GET_CONTROL is not supported\n");
|
||||
|
@ -53,11 +56,14 @@ int main(int argc, char **argv)
|
|||
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"
|
||||
);
|
||||
if (xtheadvector)
|
||||
asm volatile (".4byte 0x00007ed7");
|
||||
else
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,181 +3,244 @@
|
|||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../hwprobe/hwprobe.h"
|
||||
#include "../../kselftest.h"
|
||||
#include "../../kselftest_harness.h"
|
||||
#include "v_helpers.h"
|
||||
|
||||
#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 test_and_compare_child(long provided, long expected, int inherit, int xtheadvector)
|
||||
{
|
||||
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);
|
||||
printf("prctl with provided arg %lx failed with code %d\n",
|
||||
provided, rc);
|
||||
return -1;
|
||||
}
|
||||
rc = launch_test(inherit);
|
||||
rc = launch_test(NEXT_PROGRAM, inherit, xtheadvector);
|
||||
if (rc != expected) {
|
||||
ksft_test_result_fail("Test failed, check %d != %ld\n", rc,
|
||||
expected);
|
||||
printf("Test failed, check %d != %ld\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
|
||||
#define PR_RISCV_V_VSTATE_CTRL_CUR_SHIFT 0
|
||||
#define PR_RISCV_V_VSTATE_CTRL_NEXT_SHIFT 2
|
||||
|
||||
int main(void)
|
||||
TEST(get_control_no_v)
|
||||
{
|
||||
struct riscv_hwprobe pair;
|
||||
long flag, expected;
|
||||
long rc;
|
||||
|
||||
ksft_set_plan(1);
|
||||
if (is_vector_supported() || is_xtheadvector_supported())
|
||||
SKIP(return, "Test expects vector to be not supported");
|
||||
|
||||
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 %ld\n", rc);
|
||||
return -1;
|
||||
}
|
||||
rc = prctl(PR_RISCV_V_GET_CONTROL);
|
||||
EXPECT_EQ(-1, rc)
|
||||
TH_LOG("GET_CONTROL should fail on kernel/hw without ZVE32X");
|
||||
EXPECT_EQ(EINVAL, errno)
|
||||
TH_LOG("GET_CONTROL should fail on kernel/hw without ZVE32X");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
TEST(set_control_no_v)
|
||||
{
|
||||
long rc;
|
||||
|
||||
if (!(pair.value & RISCV_HWPROBE_EXT_ZVE32X)) {
|
||||
rc = prctl(PR_RISCV_V_GET_CONTROL);
|
||||
if (rc != -1 || errno != EINVAL) {
|
||||
ksft_test_result_fail("GET_CONTROL should fail on kernel/hw without ZVE32X\n");
|
||||
return -3;
|
||||
}
|
||||
if (is_vector_supported() || is_xtheadvector_supported())
|
||||
SKIP(return, "Test expects vector to be not supported");
|
||||
|
||||
rc = prctl(PR_RISCV_V_SET_CONTROL, PR_RISCV_V_VSTATE_CTRL_ON);
|
||||
if (rc != -1 || errno != EINVAL) {
|
||||
ksft_test_result_fail("SET_CONTROL should fail on kernel/hw without ZVE32X\n");
|
||||
return -4;
|
||||
}
|
||||
rc = prctl(PR_RISCV_V_SET_CONTROL, PR_RISCV_V_VSTATE_CTRL_ON);
|
||||
EXPECT_EQ(-1, rc)
|
||||
TH_LOG("SET_CONTROL should fail on kernel/hw without ZVE32X");
|
||||
EXPECT_EQ(EINVAL, errno)
|
||||
TH_LOG("SET_CONTROL should fail on kernel/hw without ZVE32X");
|
||||
}
|
||||
|
||||
ksft_test_result_skip("Vector not supported\n");
|
||||
return 0;
|
||||
}
|
||||
TEST(vstate_on_current)
|
||||
{
|
||||
long flag;
|
||||
long rc;
|
||||
|
||||
if (!is_vector_supported() && !is_xtheadvector_supported())
|
||||
SKIP(return, "Vector not supported");
|
||||
|
||||
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;
|
||||
}
|
||||
EXPECT_EQ(0, rc) TH_LOG("Enabling V for current should always succeed");
|
||||
}
|
||||
|
||||
TEST(vstate_off_eperm)
|
||||
{
|
||||
long flag;
|
||||
long rc;
|
||||
|
||||
if (!is_vector_supported() && !is_xtheadvector_supported())
|
||||
SKIP(return, "Vector not supported");
|
||||
|
||||
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;
|
||||
EXPECT_EQ(EPERM, errno)
|
||||
TH_LOG("Disabling V in current thread with V enabled must fail with EPERM(%d)", errno);
|
||||
EXPECT_EQ(-1, rc)
|
||||
TH_LOG("Disabling V in current thread with V enabled must fail with EPERM(%d)", errno);
|
||||
}
|
||||
|
||||
TEST(vstate_on_no_nesting)
|
||||
{
|
||||
long flag;
|
||||
int xtheadvector = 0;
|
||||
|
||||
if (!is_vector_supported()) {
|
||||
if (is_xtheadvector_supported())
|
||||
xtheadvector = 1;
|
||||
else
|
||||
SKIP(return, "Vector not supported");
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
EXPECT_EQ(0, test_and_compare_child(flag, PR_RISCV_V_VSTATE_CTRL_ON, 0, xtheadvector));
|
||||
}
|
||||
|
||||
TEST(vstate_off_nesting)
|
||||
{
|
||||
long flag;
|
||||
int xtheadvector = 0;
|
||||
|
||||
if (!is_vector_supported()) {
|
||||
if (is_xtheadvector_supported())
|
||||
xtheadvector = 1;
|
||||
else
|
||||
SKIP(return, "Vector not supported");
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
EXPECT_EQ(0, test_and_compare_child(flag, PR_RISCV_V_VSTATE_CTRL_OFF, 1, xtheadvector));
|
||||
}
|
||||
|
||||
TEST(vstate_on_inherit_no_nesting)
|
||||
{
|
||||
long flag, expected;
|
||||
int xtheadvector = 0;
|
||||
|
||||
if (!is_vector_supported()) {
|
||||
if (is_xtheadvector_supported())
|
||||
xtheadvector = 1;
|
||||
else
|
||||
SKIP(return, "Vector not supported");
|
||||
}
|
||||
|
||||
/* Turn on next's vector explicitly and test no 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;
|
||||
|
||||
EXPECT_EQ(0, test_and_compare_child(flag, expected, 0, xtheadvector));
|
||||
}
|
||||
|
||||
TEST(vstate_on_inherit)
|
||||
{
|
||||
long flag, expected;
|
||||
int xtheadvector = 0;
|
||||
|
||||
if (!is_vector_supported()) {
|
||||
if (is_xtheadvector_supported())
|
||||
xtheadvector = 1;
|
||||
else
|
||||
SKIP(return, "Vector not supported");
|
||||
}
|
||||
|
||||
/* 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;
|
||||
EXPECT_EQ(0, test_and_compare_child(flag, expected, 1, xtheadvector));
|
||||
}
|
||||
|
||||
TEST(vstate_off_inherit_no_nesting)
|
||||
{
|
||||
long flag, expected;
|
||||
int xtheadvector = 0;
|
||||
|
||||
if (!is_vector_supported()) {
|
||||
if (is_xtheadvector_supported())
|
||||
xtheadvector = 1;
|
||||
else
|
||||
SKIP(return, "Vector not supported");
|
||||
}
|
||||
/* Turn off next's vector explicitly and test no 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;
|
||||
|
||||
EXPECT_EQ(0, test_and_compare_child(flag, expected, 0, xtheadvector));
|
||||
}
|
||||
|
||||
TEST(vstate_off_inherit)
|
||||
{
|
||||
long flag, expected;
|
||||
int xtheadvector = 0;
|
||||
|
||||
if (!is_vector_supported()) {
|
||||
if (is_xtheadvector_supported())
|
||||
xtheadvector = 1;
|
||||
else
|
||||
SKIP(return, "Vector not supported");
|
||||
}
|
||||
|
||||
/* 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;
|
||||
EXPECT_EQ(0, test_and_compare_child(flag, expected, 1, xtheadvector));
|
||||
}
|
||||
|
||||
/* arguments should fail with EINVAL */
|
||||
TEST(inval_set_control_1)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!is_vector_supported() && !is_xtheadvector_supported())
|
||||
SKIP(return, "Vector not supported");
|
||||
|
||||
/* 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;
|
||||
}
|
||||
EXPECT_EQ(-1, rc);
|
||||
EXPECT_EQ(EINVAL, errno);
|
||||
}
|
||||
|
||||
/* arguments should fail with EINVAL */
|
||||
TEST(inval_set_control_2)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!is_vector_supported() && !is_xtheadvector_supported())
|
||||
SKIP(return, "Vector not supported");
|
||||
|
||||
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;
|
||||
EXPECT_EQ(-1, rc);
|
||||
EXPECT_EQ(EINVAL, errno);
|
||||
}
|
||||
|
||||
/* arguments should fail with EINVAL */
|
||||
TEST(inval_set_control_3)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!is_vector_supported() && !is_xtheadvector_supported())
|
||||
SKIP(return, "Vector not supported");
|
||||
|
||||
rc = prctl(PR_RISCV_V_SET_CONTROL, 0xc);
|
||||
EXPECT_EQ(-1, rc);
|
||||
EXPECT_EQ(EINVAL, errno);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
|
|
Loading…
Reference in New Issue