Skip to content
Open
16 changes: 16 additions & 0 deletions Documentation/arch/riscv/uabi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,19 @@ Misaligned accesses
-------------------

Misaligned accesses are supported in userspace, but they may perform poorly.

Pointer masking
---------------

Support for pointer masking in userspace (the Supm extension) is provided via
the ``PR_SET_TAGGED_ADDR_CTRL`` and ``PR_GET_TAGGED_ADDR_CTRL`` ``prctl()``
operations. Pointer masking is disabled by default. To enable it, userspace
must call ``PR_SET_TAGGED_ADDR_CTRL`` with the ``PR_PMLEN`` field set to the
number of mask/tag bits needed by the application. ``PR_PMLEN`` is interpreted
as a lower bound; if the kernel is unable to satisfy the request, the
``PR_SET_TAGGED_ADDR_CTRL`` operation will fail. The actual number of tag bits
is returned in ``PR_PMLEN`` by the ``PR_GET_TAGGED_ADDR_CTRL`` operation.

Additionally, when pointer masking is enabled (``PR_PMLEN`` is greater than 0),
a tagged address ABI is supported, with the same interface and behavior as
documented for AArch64 (Documentation/arch/arm64/tagged-address-abi.rst).
11 changes: 11 additions & 0 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,17 @@ config RISCV_ISA_C

If you don't know what to do here, say Y.

config RISCV_ISA_SUPM
bool "Supm extension for userspace pointer masking"
depends on 64BIT
default y
help
Add support for pointer masking in userspace (Supm) when the
underlying hardware extension (Smnpm or Ssnpm) is detected at boot.

If this option is disabled, userspace will be unable to use
the prctl(PR_{SET,GET}_TAGGED_ADDR_CTRL) API.

config RISCV_ISA_SVNAPOT
bool "Svnapot extension support for supervisor mode NAPOT pages"
depends on 64BIT && MMU
Expand Down
19 changes: 19 additions & 0 deletions arch/riscv/include/asm/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,28 @@

static inline int is_compat_task(void)
{
if (!IS_ENABLED(CONFIG_COMPAT))
return 0;

return test_thread_flag(TIF_32BIT);
}

static inline int is_compat_thread(struct thread_info *thread)
{
if (!IS_ENABLED(CONFIG_COMPAT))
return 0;

return test_ti_thread_flag(thread, TIF_32BIT);
}

static inline void set_compat_task(bool is_compat)
{
if (is_compat)
set_thread_flag(TIF_32BIT);
else
clear_thread_flag(TIF_32BIT);
}

struct compat_user_regs_struct {
compat_ulong_t pc;
compat_ulong_t ra;
Expand Down
16 changes: 16 additions & 0 deletions arch/riscv/include/asm/csr.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@

/* HSTATUS flags */
#ifdef CONFIG_64BIT
#define HSTATUS_HUPMM _AC(0x3000000000000, UL)
#define HSTATUS_HUPMM_PMLEN_0 _AC(0x0000000000000, UL)
#define HSTATUS_HUPMM_PMLEN_7 _AC(0x2000000000000, UL)
#define HSTATUS_HUPMM_PMLEN_16 _AC(0x3000000000000, UL)
#define HSTATUS_VSXL _AC(0x300000000, UL)
#define HSTATUS_VSXL_SHIFT 32
#endif
Expand Down Expand Up @@ -215,6 +219,10 @@
#define ENVCFG_STCE (_AC(1, ULL) << 63)
#define ENVCFG_PBMTE (_AC(1, ULL) << 62)
#define ENVCFG_ADUE (_AC(1, ULL) << 61)
#define ENVCFG_PMM (_AC(0x3, ULL) << 32)
#define ENVCFG_PMM_PMLEN_0 (_AC(0x0, ULL) << 32)
#define ENVCFG_PMM_PMLEN_7 (_AC(0x2, ULL) << 32)
#define ENVCFG_PMM_PMLEN_16 (_AC(0x3, ULL) << 32)
#define ENVCFG_CBZE (_AC(1, UL) << 7)
#define ENVCFG_CBCFE (_AC(1, UL) << 6)
#define ENVCFG_LPE (_AC(1, UL) << 2)
Expand All @@ -238,6 +246,12 @@
#define SMSTATEEN0_SSTATEEN0_SHIFT 63
#define SMSTATEEN0_SSTATEEN0 (_ULL(1) << SMSTATEEN0_SSTATEEN0_SHIFT)

/* mseccfg bits */
#define MSECCFG_PMM ENVCFG_PMM
#define MSECCFG_PMM_PMLEN_0 ENVCFG_PMM_PMLEN_0
#define MSECCFG_PMM_PMLEN_7 ENVCFG_PMM_PMLEN_7
#define MSECCFG_PMM_PMLEN_16 ENVCFG_PMM_PMLEN_16

/* symbolic CSR names: */
#define CSR_CYCLE 0xc00
#define CSR_TIME 0xc01
Expand Down Expand Up @@ -416,6 +430,8 @@
#define CSR_MIP 0x344
#define CSR_PMPCFG0 0x3a0
#define CSR_PMPADDR0 0x3b0
#define CSR_MSECCFG 0x747
#define CSR_MSECCFGH 0x757
#define CSR_MVENDORID 0xf11
#define CSR_MARCHID 0xf12
#define CSR_MIMPID 0xf13
Expand Down
11 changes: 2 additions & 9 deletions arch/riscv/include/asm/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,9 @@ extern bool compat_elf_check_arch(Elf32_Ehdr *hdr);
#define ELF_ET_DYN_BASE ((DEFAULT_MAP_WINDOW / 3) * 2)

#ifdef CONFIG_64BIT
#ifdef CONFIG_COMPAT
#define STACK_RND_MASK (test_thread_flag(TIF_32BIT) ? \
#define STACK_RND_MASK (is_compat_task() ? \
0x7ff >> (PAGE_SHIFT - 12) : \
0x3ffff >> (PAGE_SHIFT - 12))
#else
#define STACK_RND_MASK (0x3ffff >> (PAGE_SHIFT - 12))
#endif
#endif

/*
Expand Down Expand Up @@ -139,10 +135,7 @@ do { \
#ifdef CONFIG_COMPAT

#define SET_PERSONALITY(ex) \
do { if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \
set_thread_flag(TIF_32BIT); \
else \
clear_thread_flag(TIF_32BIT); \
do { set_compat_task((ex).e_ident[EI_CLASS] == ELFCLASS32); \
if (personality(current->personality) != PER_LINUX32) \
set_personality(PER_LINUX | \
(current->personality & (~PER_MASK))); \
Expand Down
7 changes: 7 additions & 0 deletions arch/riscv/include/asm/mmu.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,16 @@ typedef struct {
#ifdef CONFIG_BINFMT_ELF_FDPIC
unsigned long exec_fdpic_loadmap;
unsigned long interp_fdpic_loadmap;
#endif
unsigned long flags;
#ifdef CONFIG_RISCV_ISA_SUPM
u8 pmlen;
#endif
} mm_context_t;

/* Lock the pointer masking mode because this mm is multithreaded */
#define MM_CONTEXT_LOCK_PMLEN 0

void __meminit create_pgd_mapping(pgd_t *pgdp, uintptr_t va, phys_addr_t pa, phys_addr_t sz,
pgprot_t prot);
#endif /* __ASSEMBLY__ */
Expand Down
5 changes: 5 additions & 0 deletions arch/riscv/include/asm/mmu_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ void switch_mm(struct mm_struct *prev, struct mm_struct *next,
static inline void activate_mm(struct mm_struct *prev,
struct mm_struct *next)
{
#ifdef CONFIG_RISCV_ISA_SUPM
next->context.pmlen = 0;
#endif
switch_mm(prev, next, NULL);
}

Expand All @@ -30,6 +33,8 @@ static inline int init_new_context(struct task_struct *tsk,
#ifdef CONFIG_MMU
atomic_long_set(&mm->context.id, 0);
#endif
if (IS_ENABLED(CONFIG_RISCV_ISA_SUPM))
clear_bit(MM_CONTEXT_LOCK_PMLEN, &mm->context.flags);
return 0;
}

Expand Down
8 changes: 1 addition & 7 deletions arch/riscv/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,16 +141,10 @@
#define VA_USER_SV48 (UL(1) << (VA_BITS_SV48 - 1))
#define VA_USER_SV57 (UL(1) << (VA_BITS_SV57 - 1))

#ifdef CONFIG_COMPAT
#define MMAP_VA_BITS_64 ((VA_BITS >= VA_BITS_SV48) ? VA_BITS_SV48 : VA_BITS)
#define MMAP_MIN_VA_BITS_64 (VA_BITS_SV39)
#define MMAP_VA_BITS (is_compat_task() ? VA_BITS_SV32 : MMAP_VA_BITS_64)
#define MMAP_MIN_VA_BITS (is_compat_task() ? VA_BITS_SV32 : MMAP_MIN_VA_BITS_64)
#else
#define MMAP_VA_BITS ((VA_BITS >= VA_BITS_SV48) ? VA_BITS_SV48 : VA_BITS)
#define MMAP_MIN_VA_BITS (VA_BITS_SV39)
#endif /* CONFIG_COMPAT */

#else
#include <asm/pgtable-32.h>
#endif /* CONFIG_64BIT */
Expand Down Expand Up @@ -1016,7 +1010,7 @@ static inline pte_t pte_swp_clear_exclusive(pte_t pte)

#ifdef CONFIG_COMPAT
#define TASK_SIZE_32 (_AC(0x80000000, UL))
#define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \
#define TASK_SIZE (is_compat_task() ? \
TASK_SIZE_32 : TASK_SIZE_64)
#else
#define TASK_SIZE TASK_SIZE_64
Expand Down
8 changes: 8 additions & 0 deletions arch/riscv/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,14 @@ extern int set_unalign_ctl(struct task_struct *tsk, unsigned int val);
#define GET_UNALIGN_CTL(tsk, addr) get_unalign_ctl((tsk), (addr))
#define SET_UNALIGN_CTL(tsk, val) set_unalign_ctl((tsk), (val))

#ifdef CONFIG_RISCV_ISA_SUPM
/* PR_{SET,GET}_TAGGED_ADDR_CTRL prctl */
long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg);
long get_tagged_addr_ctrl(struct task_struct *task);
#define SET_TAGGED_ADDR_CTRL(arg) set_tagged_addr_ctrl(current, arg)
#define GET_TAGGED_ADDR_CTRL() get_tagged_addr_ctrl(current)
#endif

#endif /* __ASSEMBLY__ */

#endif /* _ASM_RISCV_PROCESSOR_H */
11 changes: 11 additions & 0 deletions arch/riscv/include/asm/switch_to.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ static __always_inline bool has_fpu(void) { return false; }
#define __switch_to_fpu(__prev, __next) do { } while (0)
#endif

static inline void envcfg_update_bits(struct task_struct *task,
unsigned long mask, unsigned long val)
{
unsigned long envcfg;

envcfg = (task->thread.envcfg & ~mask) | val;
task->thread.envcfg = envcfg;
if (task == current)
csr_write(CSR_ENVCFG, envcfg);
}

static inline void __switch_to_envcfg(struct task_struct *next)
{
asm volatile (ALTERNATIVE("nop", "csrw " __stringify(CSR_ENVCFG) ", %0",
Expand Down
43 changes: 38 additions & 5 deletions arch/riscv/include/asm/uaccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,41 @@
#define _ASM_RISCV_UACCESS_H

#include <asm/asm-extable.h>
#include <asm/cpufeature.h>
#include <asm/pgtable.h> /* for TASK_SIZE */

#ifdef CONFIG_RISCV_ISA_SUPM
static inline unsigned long __untagged_addr_remote(struct mm_struct *mm, unsigned long addr)
{
if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) {
u8 pmlen = mm->context.pmlen;

/* Virtual addresses are sign-extended; physical addresses are zero-extended. */
if (IS_ENABLED(CONFIG_MMU))
return (long)(addr << pmlen) >> pmlen;
else
return (addr << pmlen) >> pmlen;
}

return addr;
}

#define untagged_addr(addr) ({ \
unsigned long __addr = (__force unsigned long)(addr); \
(__force __typeof__(addr))__untagged_addr_remote(current->mm, __addr); \
})

#define untagged_addr_remote(mm, addr) ({ \
unsigned long __addr = (__force unsigned long)(addr); \
mmap_assert_locked(mm); \
(__force __typeof__(addr))__untagged_addr_remote(mm, __addr); \
})

#define access_ok(addr, size) likely(__access_ok(untagged_addr(addr), size))
#else
#define untagged_addr(addr) (addr)
#endif

/*
* User space memory access functions
*/
Expand Down Expand Up @@ -202,7 +235,7 @@ __gu_failed: \
*/
#define __get_user(x, ptr) \
({ \
const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \
const __typeof__(*(ptr)) __user *__gu_ptr = untagged_addr(ptr); \
long __gu_err = 0; \
__typeof__(x) __gu_val; \
\
Expand Down Expand Up @@ -333,7 +366,7 @@ err_label: \
*/
#define __put_user(x, ptr) \
({ \
__typeof__(*(ptr)) __user *__gu_ptr = (ptr); \
__typeof__(*(ptr)) __user *__gu_ptr = untagged_addr(ptr); \
__typeof__(*__gu_ptr) __val = (x); \
long __pu_err = 0; \
\
Expand Down Expand Up @@ -380,13 +413,13 @@ unsigned long __must_check __asm_copy_from_user(void *to,
static inline unsigned long
raw_copy_from_user(void *to, const void __user *from, unsigned long n)
{
return __asm_copy_from_user(to, from, n);
return __asm_copy_from_user(to, untagged_addr(from), n);
}

static inline unsigned long
raw_copy_to_user(void __user *to, const void *from, unsigned long n)
{
return __asm_copy_to_user(to, from, n);
return __asm_copy_to_user(untagged_addr(to), from, n);
}

extern long strncpy_from_user(char *dest, const char __user *src, long count);
Expand All @@ -401,7 +434,7 @@ unsigned long __must_check clear_user(void __user *to, unsigned long n)
{
might_fault();
return access_ok(to, n) ?
__clear_user(to, n) : n;
__clear_user(untagged_addr(to), n) : n;
}

#define __get_kernel_nofault(dst, src, type, err_label) \
Expand Down
Loading
Loading