Skip to content

Commit 09d6775

Browse files
SiFiveHollandpalmer-dabbelt
authored andcommitted
riscv: Add support for userspace pointer masking
RISC-V supports pointer masking with a variable number of tag bits (which is called "PMLEN" in the specification) and which is configured at the next higher privilege level. Wire up the PR_SET_TAGGED_ADDR_CTRL and PR_GET_TAGGED_ADDR_CTRL prctls so userspace can request a lower bound on the number of tag bits and determine the actual number of tag bits. As with arm64's PR_TAGGED_ADDR_ENABLE, the pointer masking configuration is thread-scoped, inherited on clone() and fork() and cleared on execve(). Reviewed-by: Charlie Jenkins <[email protected]> Tested-by: Charlie Jenkins <[email protected]> Signed-off-by: Samuel Holland <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent 29eedc7 commit 09d6775

File tree

6 files changed

+137
-1
lines changed

6 files changed

+137
-1
lines changed

Documentation/arch/riscv/uabi.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,15 @@ Misaligned accesses
6868
Misaligned scalar accesses are supported in userspace, but they may perform
6969
poorly. Misaligned vector accesses are only supported if the Zicclsm extension
7070
is supported.
71+
72+
Pointer masking
73+
---------------
74+
75+
Support for pointer masking in userspace (the Supm extension) is provided via
76+
the ``PR_SET_TAGGED_ADDR_CTRL`` and ``PR_GET_TAGGED_ADDR_CTRL`` ``prctl()``
77+
operations. Pointer masking is disabled by default. To enable it, userspace
78+
must call ``PR_SET_TAGGED_ADDR_CTRL`` with the ``PR_PMLEN`` field set to the
79+
number of mask/tag bits needed by the application. ``PR_PMLEN`` is interpreted
80+
as a lower bound; if the kernel is unable to satisfy the request, the
81+
``PR_SET_TAGGED_ADDR_CTRL`` operation will fail. The actual number of tag bits
82+
is returned in ``PR_PMLEN`` by the ``PR_GET_TAGGED_ADDR_CTRL`` operation.

arch/riscv/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,17 @@ config RISCV_ISA_C
531531

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

534+
config RISCV_ISA_SUPM
535+
bool "Supm extension for userspace pointer masking"
536+
depends on 64BIT
537+
default y
538+
help
539+
Add support for pointer masking in userspace (Supm) when the
540+
underlying hardware extension (Smnpm or Ssnpm) is detected at boot.
541+
542+
If this option is disabled, userspace will be unable to use
543+
the prctl(PR_{SET,GET}_TAGGED_ADDR_CTRL) API.
544+
534545
config RISCV_ISA_SVNAPOT
535546
bool "Svnapot extension support for supervisor mode NAPOT pages"
536547
depends on 64BIT && MMU

arch/riscv/include/asm/processor.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ extern int set_unalign_ctl(struct task_struct *tsk, unsigned int val);
178178
#define RISCV_SET_ICACHE_FLUSH_CTX(arg1, arg2) riscv_set_icache_flush_ctx(arg1, arg2)
179179
extern int riscv_set_icache_flush_ctx(unsigned long ctx, unsigned long per_thread);
180180

181+
#ifdef CONFIG_RISCV_ISA_SUPM
182+
/* PR_{SET,GET}_TAGGED_ADDR_CTRL prctl */
183+
long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg);
184+
long get_tagged_addr_ctrl(struct task_struct *task);
185+
#define SET_TAGGED_ADDR_CTRL(arg) set_tagged_addr_ctrl(current, arg)
186+
#define GET_TAGGED_ADDR_CTRL() get_tagged_addr_ctrl(current)
187+
#endif
188+
181189
#endif /* __ASSEMBLY__ */
182190

183191
#endif /* _ASM_RISCV_PROCESSOR_H */

arch/riscv/include/asm/switch_to.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ static __always_inline bool has_fpu(void) { return false; }
7070
#define __switch_to_fpu(__prev, __next) do { } while (0)
7171
#endif
7272

73+
static inline void envcfg_update_bits(struct task_struct *task,
74+
unsigned long mask, unsigned long val)
75+
{
76+
unsigned long envcfg;
77+
78+
envcfg = (task->thread.envcfg & ~mask) | val;
79+
task->thread.envcfg = envcfg;
80+
if (task == current)
81+
csr_write(CSR_ENVCFG, envcfg);
82+
}
83+
7384
static inline void __switch_to_envcfg(struct task_struct *next)
7485
{
7586
asm volatile (ALTERNATIVE("nop", "csrw " __stringify(CSR_ENVCFG) ", %0",

arch/riscv/kernel/process.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* Copyright (C) 2017 SiFive
88
*/
99

10+
#include <linux/bitfield.h>
1011
#include <linux/cpu.h>
1112
#include <linux/kernel.h>
1213
#include <linux/sched.h>
@@ -180,6 +181,10 @@ void flush_thread(void)
180181
memset(&current->thread.vstate, 0, sizeof(struct __riscv_v_ext_state));
181182
clear_tsk_thread_flag(current, TIF_RISCV_V_DEFER_RESTORE);
182183
#endif
184+
#ifdef CONFIG_RISCV_ISA_SUPM
185+
if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM))
186+
envcfg_update_bits(current, ENVCFG_PMM, ENVCFG_PMM_PMLEN_0);
187+
#endif
183188
}
184189

185190
void arch_release_task_struct(struct task_struct *tsk)
@@ -242,3 +247,89 @@ void __init arch_task_cache_init(void)
242247
{
243248
riscv_v_setup_ctx_cache();
244249
}
250+
251+
#ifdef CONFIG_RISCV_ISA_SUPM
252+
enum {
253+
PMLEN_0 = 0,
254+
PMLEN_7 = 7,
255+
PMLEN_16 = 16,
256+
};
257+
258+
static bool have_user_pmlen_7;
259+
static bool have_user_pmlen_16;
260+
261+
long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg)
262+
{
263+
unsigned long valid_mask = PR_PMLEN_MASK;
264+
struct thread_info *ti = task_thread_info(task);
265+
unsigned long pmm;
266+
u8 pmlen;
267+
268+
if (is_compat_thread(ti))
269+
return -EINVAL;
270+
271+
if (arg & ~valid_mask)
272+
return -EINVAL;
273+
274+
/*
275+
* Prefer the smallest PMLEN that satisfies the user's request,
276+
* in case choosing a larger PMLEN has a performance impact.
277+
*/
278+
pmlen = FIELD_GET(PR_PMLEN_MASK, arg);
279+
if (pmlen == PMLEN_0)
280+
pmm = ENVCFG_PMM_PMLEN_0;
281+
else if (pmlen <= PMLEN_7 && have_user_pmlen_7)
282+
pmm = ENVCFG_PMM_PMLEN_7;
283+
else if (pmlen <= PMLEN_16 && have_user_pmlen_16)
284+
pmm = ENVCFG_PMM_PMLEN_16;
285+
else
286+
return -EINVAL;
287+
288+
envcfg_update_bits(task, ENVCFG_PMM, pmm);
289+
290+
return 0;
291+
}
292+
293+
long get_tagged_addr_ctrl(struct task_struct *task)
294+
{
295+
struct thread_info *ti = task_thread_info(task);
296+
long ret = 0;
297+
298+
if (is_compat_thread(ti))
299+
return -EINVAL;
300+
301+
switch (task->thread.envcfg & ENVCFG_PMM) {
302+
case ENVCFG_PMM_PMLEN_7:
303+
ret = FIELD_PREP(PR_PMLEN_MASK, PMLEN_7);
304+
break;
305+
case ENVCFG_PMM_PMLEN_16:
306+
ret = FIELD_PREP(PR_PMLEN_MASK, PMLEN_16);
307+
break;
308+
}
309+
310+
return ret;
311+
}
312+
313+
static bool try_to_set_pmm(unsigned long value)
314+
{
315+
csr_set(CSR_ENVCFG, value);
316+
return (csr_read_clear(CSR_ENVCFG, ENVCFG_PMM) & ENVCFG_PMM) == value;
317+
}
318+
319+
static int __init tagged_addr_init(void)
320+
{
321+
if (!riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM))
322+
return 0;
323+
324+
/*
325+
* envcfg.PMM is a WARL field. Detect which values are supported.
326+
* Assume the supported PMLEN values are the same on all harts.
327+
*/
328+
csr_clear(CSR_ENVCFG, ENVCFG_PMM);
329+
have_user_pmlen_7 = try_to_set_pmm(ENVCFG_PMM_PMLEN_7);
330+
have_user_pmlen_16 = try_to_set_pmm(ENVCFG_PMM_PMLEN_16);
331+
332+
return 0;
333+
}
334+
core_initcall(tagged_addr_init);
335+
#endif /* CONFIG_RISCV_ISA_SUPM */

include/uapi/linux/prctl.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ struct prctl_mm_map {
230230
# define PR_PAC_APDBKEY (1UL << 3)
231231
# define PR_PAC_APGAKEY (1UL << 4)
232232

233-
/* Tagged user address controls for arm64 */
233+
/* Tagged user address controls for arm64 and RISC-V */
234234
#define PR_SET_TAGGED_ADDR_CTRL 55
235235
#define PR_GET_TAGGED_ADDR_CTRL 56
236236
# define PR_TAGGED_ADDR_ENABLE (1UL << 0)
@@ -244,6 +244,9 @@ struct prctl_mm_map {
244244
# define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT)
245245
/* Unused; kept only for source compatibility */
246246
# define PR_MTE_TCF_SHIFT 1
247+
/* RISC-V pointer masking tag length */
248+
# define PR_PMLEN_SHIFT 24
249+
# define PR_PMLEN_MASK (0x7fUL << PR_PMLEN_SHIFT)
247250

248251
/* Control reclaim behavior when allocating memory */
249252
#define PR_SET_IO_FLUSHER 57

0 commit comments

Comments
 (0)