Skip to content

Commit

Permalink
Merge patch series "Add support to handle misaligned accesses in S-mode"
Browse files Browse the repository at this point in the history
Clément Léger <cleger@rivosinc.com> says:

Since commit 61cadb9 ("Provide new description of misaligned load/store
behavior compatible with privileged architecture.") in the RISC-V ISA
manual, it is stated that misaligned load/store might not be supported.
However, the RISC-V kernel uABI describes that misaligned accesses are
supported. In order to support that, this series adds support for S-mode
handling of misaligned accesses as well support for prctl(PR_UNALIGN).

Handling misaligned access in kernel allows for a finer grain control
of the misaligned accesses behavior, and thanks to the prctl() call,
can allow disabling misaligned access emulation to generate SIGBUS. User
space can then optimize its software by removing such access based on
SIGBUS generation.

This series is useful when using a SBI implementation that does not
handle misaligned traps as well as detecting misaligned accesses
generated by userspace application using the prctrl(PR_SET_UNALIGN)
feature.

This series can be tested using the spike simulator[1] and a modified
openSBI version[2] which allows to always delegate misaligned load/store to
S-mode. A test[3] that exercise various instructions/registers can be
executed to verify the unaligned access support.

[1] https://github.com/riscv-software-src/riscv-isa-sim
[2] https://github.com/rivosinc/opensbi/tree/dev/cleger/no_misaligned
[3] https://github.com/clementleger/unaligned_test

* b4-shazam-merge:
  riscv: add support for PR_SET_UNALIGN and PR_GET_UNALIGN
  riscv: report misaligned accesses emulation to hwprobe
  riscv: annotate check_unaligned_access_boot_cpu() with __init
  riscv: add support for sysctl unaligned_enabled control
  riscv: add floating point insn support to misaligned access emulation
  riscv: report perf event for misaligned fault
  riscv: add support for misaligned trap handling in S-mode
  riscv: remove unused functions in traps_misaligned.c

[Palmer: Evan pointed out an extra __init on
unaligned_emulation_finish(), I've squashed it in as a merge conflict.]

Link: https://lore.kernel.org/r/20231004151405.521596-1-cleger@rivosinc.com
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
  • Loading branch information
palmer-dabbelt committed Nov 1, 2023
2 parents 0c37376 + 9f23a5d commit a22f86f
Show file tree
Hide file tree
Showing 11 changed files with 523 additions and 58 deletions.
9 changes: 9 additions & 0 deletions arch/riscv/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,15 @@ config THREAD_SIZE_ORDER
Specify the Pages of thread stack size (from 4KB to 64KB), which also
affects irq stack size, which is equal to thread stack size.

config RISCV_MISALIGNED
bool "Support misaligned load/store traps for kernel and userspace"
select SYSCTL_ARCH_UNALIGN_ALLOW
default y
help
Say Y here if you want the kernel to embed support for misaligned
load/store for both kernel and userspace. When disable, misaligned
accesses will generate SIGBUS in userspace and panic in kernel.

endmenu # "Platform type"

menu "Kernel features"
Expand Down
18 changes: 18 additions & 0 deletions arch/riscv/include/asm/cpufeature.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,22 @@ extern struct riscv_isainfo hart_isa[NR_CPUS];
int check_unaligned_access(void *unused);
void riscv_user_isa_enable(void);

#ifdef CONFIG_RISCV_MISALIGNED
bool unaligned_ctl_available(void);
bool check_unaligned_access_emulated(int cpu);
void unaligned_emulation_finish(void);
#else
static inline bool unaligned_ctl_available(void)
{
return false;
}

static inline bool check_unaligned_access_emulated(int cpu)
{
return false;
}

static inline void unaligned_emulation_finish(void) {}
#endif

#endif
14 changes: 14 additions & 0 deletions arch/riscv/include/asm/entry-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,18 @@
void handle_page_fault(struct pt_regs *regs);
void handle_break(struct pt_regs *regs);

#ifdef CONFIG_RISCV_MISALIGNED
int handle_misaligned_load(struct pt_regs *regs);
int handle_misaligned_store(struct pt_regs *regs);
#else
static inline int handle_misaligned_load(struct pt_regs *regs)
{
return -1;
}
static inline int handle_misaligned_store(struct pt_regs *regs)
{
return -1;
}
#endif

#endif /* _ASM_RISCV_ENTRY_COMMON_H */
9 changes: 9 additions & 0 deletions arch/riscv/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <linux/const.h>
#include <linux/cache.h>
#include <linux/prctl.h>

#include <vdso/processor.h>

Expand Down Expand Up @@ -82,6 +83,7 @@ struct thread_struct {
unsigned long bad_cause;
unsigned long vstate_ctrl;
struct __riscv_v_ext_state vstate;
unsigned long align_ctl;
};

/* Whitelist the fstate from the task_struct for hardened usercopy */
Expand All @@ -94,6 +96,7 @@ static inline void arch_thread_struct_whitelist(unsigned long *offset,

#define INIT_THREAD { \
.sp = sizeof(init_stack) + (long)&init_stack, \
.align_ctl = PR_UNALIGN_NOPRINT, \
}

#define task_pt_regs(tsk) \
Expand Down Expand Up @@ -134,6 +137,12 @@ extern long riscv_v_vstate_ctrl_set_current(unsigned long arg);
extern long riscv_v_vstate_ctrl_get_current(void);
#endif /* CONFIG_RISCV_ISA_V */

extern int get_unalign_ctl(struct task_struct *tsk, unsigned long addr);
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))

#endif /* __ASSEMBLY__ */

#endif /* _ASM_RISCV_PROCESSOR_H */
2 changes: 1 addition & 1 deletion arch/riscv/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ obj-y += patch.o
obj-y += probes/
obj-$(CONFIG_MMU) += vdso.o vdso/

obj-$(CONFIG_RISCV_M_MODE) += traps_misaligned.o
obj-$(CONFIG_RISCV_MISALIGNED) += traps_misaligned.o
obj-$(CONFIG_FPU) += fpu.o
obj-$(CONFIG_RISCV_ISA_V) += vector.o
obj-$(CONFIG_SMP) += smpboot.o
Expand Down
4 changes: 4 additions & 0 deletions arch/riscv/kernel/cpufeature.c
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,9 @@ int check_unaligned_access(void *unused)
void *src;
long speed = RISCV_HWPROBE_MISALIGNED_SLOW;

if (check_unaligned_access_emulated(cpu))
return 0;

page = alloc_pages(GFP_NOWAIT, get_order(MISALIGNED_BUFFER_SIZE));
if (!page) {
pr_warn("Can't alloc pages to measure memcpy performance");
Expand Down Expand Up @@ -645,6 +648,7 @@ int check_unaligned_access(void *unused)

out:
__free_pages(page, get_order(MISALIGNED_BUFFER_SIZE));
unaligned_emulation_finish();
return 0;
}

Expand Down
121 changes: 121 additions & 0 deletions arch/riscv/kernel/fpu.S
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,124 @@ ENTRY(__fstate_restore)
csrc CSR_STATUS, t1
ret
ENDPROC(__fstate_restore)

#define get_f32(which) fmv.x.s a0, which; j 2f
#define put_f32(which) fmv.s.x which, a1; j 2f
#if __riscv_xlen == 64
# define get_f64(which) fmv.x.d a0, which; j 2f
# define put_f64(which) fmv.d.x which, a1; j 2f
#else
# define get_f64(which) fsd which, 0(a1); j 2f
# define put_f64(which) fld which, 0(a1); j 2f
#endif

.macro fp_access_prologue
/*
* Compute jump offset to store the correct FP register since we don't
* have indirect FP register access
*/
sll t0, a0, 3
la t2, 1f
add t0, t0, t2
li t1, SR_FS
csrs CSR_STATUS, t1
jr t0
1:
.endm

.macro fp_access_epilogue
2:
csrc CSR_STATUS, t1
ret
.endm

#define fp_access_body(__access_func) \
__access_func(f0); \
__access_func(f1); \
__access_func(f2); \
__access_func(f3); \
__access_func(f4); \
__access_func(f5); \
__access_func(f6); \
__access_func(f7); \
__access_func(f8); \
__access_func(f9); \
__access_func(f10); \
__access_func(f11); \
__access_func(f12); \
__access_func(f13); \
__access_func(f14); \
__access_func(f15); \
__access_func(f16); \
__access_func(f17); \
__access_func(f18); \
__access_func(f19); \
__access_func(f20); \
__access_func(f21); \
__access_func(f22); \
__access_func(f23); \
__access_func(f24); \
__access_func(f25); \
__access_func(f26); \
__access_func(f27); \
__access_func(f28); \
__access_func(f29); \
__access_func(f30); \
__access_func(f31)


#ifdef CONFIG_RISCV_MISALIGNED

/*
* Disable compressed instructions set to keep a constant offset between FP
* load/store/move instructions
*/
.option norvc
/*
* put_f32_reg - Set a FP register from a register containing the value
* a0 = FP register index to be set
* a1 = value to be loaded in the FP register
*/
SYM_FUNC_START(put_f32_reg)
fp_access_prologue
fp_access_body(put_f32)
fp_access_epilogue
SYM_FUNC_END(put_f32_reg)

/*
* get_f32_reg - Get a FP register value and return it
* a0 = FP register index to be retrieved
*/
SYM_FUNC_START(get_f32_reg)
fp_access_prologue
fp_access_body(get_f32)
fp_access_epilogue
SYM_FUNC_END(get_f32_reg)

/*
* put_f64_reg - Set a 64 bits FP register from a value or a pointer.
* a0 = FP register index to be set
* a1 = value/pointer to be loaded in the FP register (when xlen == 32 bits, we
* load the value to a pointer).
*/
SYM_FUNC_START(put_f64_reg)
fp_access_prologue
fp_access_body(put_f64)
fp_access_epilogue
SYM_FUNC_END(put_f64_reg)

/*
* put_f64_reg - Get a 64 bits FP register value and returned it or store it to
* a pointer.
* a0 = FP register index to be retrieved
* a1 = If xlen == 32, pointer which should be loaded with the FP register value
* or unused if xlen == 64. In which case the FP register value is returned
* through a0
*/
SYM_FUNC_START(get_f64_reg)
fp_access_prologue
fp_access_body(get_f64)
fp_access_epilogue
SYM_FUNC_END(get_f64_reg)

#endif /* CONFIG_RISCV_MISALIGNED */
18 changes: 18 additions & 0 deletions arch/riscv/kernel/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <asm/thread_info.h>
#include <asm/cpuidle.h>
#include <asm/vector.h>
#include <asm/cpufeature.h>

register unsigned long gp_in_global __asm__("gp");

Expand All @@ -41,6 +42,23 @@ void arch_cpu_idle(void)
cpu_do_idle();
}

int set_unalign_ctl(struct task_struct *tsk, unsigned int val)
{
if (!unaligned_ctl_available())
return -EINVAL;

tsk->thread.align_ctl = val;
return 0;
}

int get_unalign_ctl(struct task_struct *tsk, unsigned long adr)
{
if (!unaligned_ctl_available())
return -EINVAL;

return put_user(tsk->thread.align_ctl, (unsigned long __user *)adr);
}

void __show_regs(struct pt_regs *regs)
{
show_regs_print_info(KERN_DEFAULT);
Expand Down
2 changes: 1 addition & 1 deletion arch/riscv/kernel/smpboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,6 @@ asmlinkage __visible void smp_callin(void)
riscv_ipi_enable();

numa_add_cpu(curr_cpuid);
set_cpu_online(curr_cpuid, 1);

/*
* Boot-time misaligned access speed measurements are done in parallel
Expand All @@ -258,6 +257,7 @@ asmlinkage __visible void smp_callin(void)
(per_cpu(misaligned_access_speed, curr_cpuid) == RISCV_HWPROBE_MISALIGNED_UNKNOWN)) {
check_unaligned_access(NULL);
}
set_cpu_online(curr_cpuid, 1);

if (has_vector()) {
if (riscv_v_setup_vsize())
Expand Down
9 changes: 0 additions & 9 deletions arch/riscv/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,6 @@ asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *re

DO_ERROR_INFO(do_trap_load_fault,
SIGSEGV, SEGV_ACCERR, "load access fault");
#ifndef CONFIG_RISCV_M_MODE
DO_ERROR_INFO(do_trap_load_misaligned,
SIGBUS, BUS_ADRALN, "Oops - load address misaligned");
DO_ERROR_INFO(do_trap_store_misaligned,
SIGBUS, BUS_ADRALN, "Oops - store (or AMO) address misaligned");
#else
int handle_misaligned_load(struct pt_regs *regs);
int handle_misaligned_store(struct pt_regs *regs);

asmlinkage __visible __trap_section void do_trap_load_misaligned(struct pt_regs *regs)
{
Expand Down Expand Up @@ -229,7 +221,6 @@ asmlinkage __visible __trap_section void do_trap_store_misaligned(struct pt_regs
irqentry_nmi_exit(regs, state);
}
}
#endif
DO_ERROR_INFO(do_trap_store_fault,
SIGSEGV, SEGV_ACCERR, "store (or AMO) access fault");
DO_ERROR_INFO(do_trap_ecall_s,
Expand Down

0 comments on commit a22f86f

Please sign in to comment.