Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions core/arch/riscv/include/kernel/thread_arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include <platform_config.h>
#include <riscv.h>
#include <riscv_vector.h>

/*
* Each RISC-V platform must define their own values.
Expand Down Expand Up @@ -167,6 +168,29 @@ struct thread_ctx_regs {

struct user_mode_ctx;

#ifdef CFG_RISCV_WITH_VECTOR
struct thread_user_vector_state {
/* Allocated the first time the TA asks for the vector unit */
struct riscv_vector_state *state;
/* True when @state holds a saved copy of the TA's vector registers */
bool valid;
};

/* Returns false if a context could not be allocated for the TA */
bool thread_user_enable_vector(struct thread_user_vector_state *uvect);
void thread_user_save_vector(void);
void thread_user_clear_vector(struct user_mode_ctx *uctx);
#else /*CFG_RISCV_WITH_VECTOR*/
static inline void thread_user_save_vector(void)
{
}

static inline void thread_user_clear_vector(struct user_mode_ctx *uctx
__unused)
{
}
#endif /*CFG_RISCV_WITH_VECTOR*/

#ifdef CFG_WITH_VFP
uint32_t thread_kernel_enable_vfp(void);
void thread_kernel_disable_vfp(uint32_t state);
Expand Down
35 changes: 35 additions & 0 deletions core/arch/riscv/include/kernel/thread_private_arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef __ASSEMBLER__

#include <kernel/thread.h>
#include <riscv_vector.h>

#define STACK_TMP_OFFS 0

Expand Down Expand Up @@ -58,6 +59,40 @@ struct thread_user_mode_rec {
unsigned long x[13];
};

#ifdef CFG_RISCV_WITH_VECTOR
/*
* Which context the vector registers of this hart currently hold.
*
* OP-TEE owns xstatus.VS while it runs, so one owner per OP-TEE thread is
* enough to describe the hardware: the registers hold either nothing worth
* preserving, the normal world context, or the context of the TA the
* thread is running.
*/
enum riscv_vector_owner {
RISCV_VECTOR_OWNER_NONE = 0,
RISCV_VECTOR_OWNER_NS,
RISCV_VECTOR_OWNER_USER,
};

/*
* struct thread_vector_state - per OP-TEE thread vector bookkeeping
* @ns: saved normal world vector context, allocated at boot
* @uvect: vector context of the TA this thread is running, if it has one
* @owner: context the vector registers currently hold
* @ns_vs: xstatus.VS the normal world had on entry to OP-TEE
* @ns_valid: @ns holds a saved normal world context
* @sec_used: secure code has written the vector registers since entry
*/
struct thread_vector_state {
struct riscv_vector_state *ns;
struct thread_user_vector_state *uvect;
enum riscv_vector_owner owner;
unsigned long ns_vs;
bool ns_valid;
bool sec_used;
};
#endif /*CFG_RISCV_WITH_VECTOR*/

extern long thread_user_kcode_offset;

void thread_native_interrupt_handler(struct thread_ctx_regs *regs,
Expand Down
14 changes: 14 additions & 0 deletions core/arch/riscv/include/riscv.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright 2022-2023 NXP
* Copyright (c) 2026, RISCStar Solutions Limited
*/

#ifndef __RISCV_H
Expand Down Expand Up @@ -75,6 +76,19 @@
#define CSR_XSTATUS_SUM BIT(18)
#define CSR_XSTATUS_MXR BIT(19)

/*
* xstatus.VS holds the state of the vector unit. The field is two bits wide
* and always resides in the low 32 bits of xstatus, on RV32 as well as on
* RV64.
*/
#define CSR_XSTATUS_VS_SHIFT 9
#define CSR_XSTATUS_VS_MASK SHIFT_U32(3, CSR_XSTATUS_VS_SHIFT)

#define CSR_XSTATUS_VS_OFF 0
#define CSR_XSTATUS_VS_INITIAL 1
#define CSR_XSTATUS_VS_CLEAN 2
#define CSR_XSTATUS_VS_DIRTY 3

#define CSR_XCAUSE_INTR_FLAG BIT64(__riscv_xlen - 1)

#ifndef __ASSEMBLER__
Expand Down
43 changes: 43 additions & 0 deletions core/arch/riscv/include/riscv_macros.S
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
/*
* Copyright 2022-2023 NXP
* Copyright (c) 2015, Linaro Limited
* Copyright (c) 2026, RISCStar Solutions Limited
*/

.altmacro
Expand Down Expand Up @@ -49,6 +50,48 @@
\from_regnum, \to_regnum
.endm

/*
* This helper macro transfers the whole vector register file, eight
* registers at a time.
*
* The whole-register forms vs8r.v and vl8r.v are used because they
* move a fixed amount of state regardless of the vtype and vl in
* effect, so the context can be moved without first having to
* reconstruct the configuration it was taken under.
*
* base_reg is advanced by 8 * vlenb between chunks and is left
* pointing past the end. tmp_reg is clobbered. The vector ISA is
* enabled for this block alone, so the file assembles for a core
* that is otherwise built without it.
*/
.macro _do_vregs instr, base_reg, vlenb_reg, tmp_reg
.option push
.option arch, +v
slli \tmp_reg, \vlenb_reg, 3
\instr v0, (\base_reg)
add \base_reg, \base_reg, \tmp_reg
\instr v8, (\base_reg)
add \base_reg, \base_reg, \tmp_reg
\instr v16, (\base_reg)
add \base_reg, \base_reg, \tmp_reg
\instr v24, (\base_reg)
.option pop
.endm

/*
* Stores registers v0..v31 at [base_reg], vlenb bytes each
*/
.macro store_vregs base_reg, vlenb_reg, tmp_reg
_do_vregs vs8r.v, \base_reg, \vlenb_reg, \tmp_reg
.endm

/*
* Loads registers v0..v31 from [base_reg], vlenb bytes each
*/
.macro load_vregs base_reg, vlenb_reg, tmp_reg
_do_vregs vl8r.v, \base_reg, \vlenb_reg, \tmp_reg
.endm

/*
* Multiplication macro for RISC-V harts without M extension.
*/
Expand Down
132 changes: 132 additions & 0 deletions core/arch/riscv/include/riscv_vector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2026, RISCStar Solutions Limited
*/

#ifndef __RISCV_VECTOR_H
#define __RISCV_VECTOR_H

#ifndef __ASSEMBLER__

#include <compiler.h>
#include <riscv.h>
#include <types_ext.h>

/*
* The width of a vector register is discovered at run time from vlenb and
* can be anything from 8 bytes upwards, so a context cannot be sized from
* the ISA the core was built for. The register file is a flexible array and
* a context is allocated for the hart it will run on.
*/
#define RISCV_VECTOR_NUM_REGS 32

/*
* struct riscv_vector_state - vector register context
* @vstart: index the next vector instruction would resume from
* @vtype: current vector type, read only, put back through vsetvl
* @vl: current vector length, likewise
* @vcsr: vector rounding mode and saturation flag
* @vregs: v0..v31, each vlenb bytes wide and laid out back to back
*
* The layout is shared with riscv_vector.S, see the RISCV_VECTOR_*_OFF
* defines.
*/
struct riscv_vector_state {
unsigned long vstart;
unsigned long vtype;
unsigned long vl;
unsigned long vcsr;
uint8_t vregs[];
};

/*
* Bytes one context occupies on this hart, the header plus 32 * vlenb.
* Reads vlenb, so it enables the vector unit for the read the same way the
* save and restore routines do.
*/
size_t riscv_vector_state_size(void);

void riscv_vector_save_state(struct riscv_vector_state *state);
void riscv_vector_restore_state(struct riscv_vector_state *state);

/*
* xstatus.VS holds the state of the vector unit and takes the same four
* values as the FS field does for floating point:
*
* Off the unit is disabled, a vector instruction or an access to a
* vector CSR traps as an illegal instruction
* Initial enabled, the registers hold their initial value
* Clean enabled, the registers match the copy held in memory
* Dirty enabled, the registers have been written since they were last
* saved
*
* The helpers come in two flavours: those taking an xstatus value operate
* on a saved context, since xstatus is part of the register frame restored
* on the way back to a trapped context, while riscv_vector_write_vs() and
* friends act on the live CSR of this hart.
*/

static inline unsigned long riscv_vector_get_vs(unsigned long xstatus)
{
#ifdef RV32
return get_field_u32(xstatus, CSR_XSTATUS_VS_MASK);
#else
return get_field_u64(xstatus, CSR_XSTATUS_VS_MASK);
#endif
}

static inline unsigned long riscv_vector_set_vs(unsigned long xstatus,
unsigned long vs)
{
#ifdef RV32
return set_field_u32(xstatus, CSR_XSTATUS_VS_MASK, vs);
#else
return set_field_u64(xstatus, CSR_XSTATUS_VS_MASK, vs);
#endif
}

/* Returns true if @xstatus describes a context with the vector unit on */
static inline bool riscv_vector_state_is_enabled(unsigned long xstatus)
{
return riscv_vector_get_vs(xstatus) != CSR_XSTATUS_VS_OFF;
}

static inline void riscv_vector_write_vs(unsigned long vs)
{
write_csr(CSR_XSTATUS,
riscv_vector_set_vs(read_csr(CSR_XSTATUS), vs));
}

static inline unsigned long riscv_vector_read_vs(void)
{
return riscv_vector_get_vs(read_csr(CSR_XSTATUS));
}

static inline void riscv_vector_disable(void)
{
clear_csr(CSR_XSTATUS, CSR_XSTATUS_VS_MASK);
}

static inline bool riscv_vector_is_enabled(void)
{
return riscv_vector_read_vs() != CSR_XSTATUS_VS_OFF;
}

/*
* vlenb is only readable with the unit enabled, so this leaves xstatus.VS
* as it found it the same way the save and restore routines do.
*/
static inline unsigned long riscv_vector_vlenb(void)
{
unsigned long xstatus = read_csr(CSR_XSTATUS);
unsigned long vlenb = 0;

riscv_vector_write_vs(CSR_XSTATUS_VS_INITIAL);
vlenb = read_csr(CSR_VLENB);
write_csr(CSR_XSTATUS, xstatus);

return vlenb;
}

#endif /* !__ASSEMBLER__ */
#endif /* __RISCV_VECTOR_H */
Loading
Loading