Skip to content

Commit 0619ff9

Browse files
Merge patch series "Add support to handle misaligned accesses in S-mode"
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 Link: https://lore.kernel.org/r/20231004151405.521596-1-cleger@rivosinc.com Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
2 parents e1c05b3 + 9f23a5d commit 0619ff9

11 files changed

Lines changed: 524 additions & 59 deletions

File tree

arch/riscv/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,15 @@ config THREAD_SIZE_ORDER
643643
Specify the Pages of thread stack size (from 4KB to 64KB), which also
644644
affects irq stack size, which is equal to thread stack size.
645645

646+
config RISCV_MISALIGNED
647+
bool "Support misaligned load/store traps for kernel and userspace"
648+
select SYSCTL_ARCH_UNALIGN_ALLOW
649+
default y
650+
help
651+
Say Y here if you want the kernel to embed support for misaligned
652+
load/store for both kernel and userspace. When disable, misaligned
653+
accesses will generate SIGBUS in userspace and panic in kernel.
654+
646655
endmenu # "Platform type"
647656

648657
menu "Kernel features"

arch/riscv/include/asm/cpufeature.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,22 @@ extern struct riscv_isainfo hart_isa[NR_CPUS];
3333
void check_unaligned_access(int cpu);
3434
void riscv_user_isa_enable(void);
3535

36+
#ifdef CONFIG_RISCV_MISALIGNED
37+
bool unaligned_ctl_available(void);
38+
bool check_unaligned_access_emulated(int cpu);
39+
void unaligned_emulation_finish(void);
40+
#else
41+
static inline bool unaligned_ctl_available(void)
42+
{
43+
return false;
44+
}
45+
46+
static inline bool check_unaligned_access_emulated(int cpu)
47+
{
48+
return false;
49+
}
50+
51+
static inline void unaligned_emulation_finish(void) {}
52+
#endif
53+
3654
#endif

arch/riscv/include/asm/entry-common.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,18 @@
88
void handle_page_fault(struct pt_regs *regs);
99
void handle_break(struct pt_regs *regs);
1010

11+
#ifdef CONFIG_RISCV_MISALIGNED
12+
int handle_misaligned_load(struct pt_regs *regs);
13+
int handle_misaligned_store(struct pt_regs *regs);
14+
#else
15+
static inline int handle_misaligned_load(struct pt_regs *regs)
16+
{
17+
return -1;
18+
}
19+
static inline int handle_misaligned_store(struct pt_regs *regs)
20+
{
21+
return -1;
22+
}
23+
#endif
24+
1125
#endif /* _ASM_RISCV_ENTRY_COMMON_H */

arch/riscv/include/asm/processor.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <linux/const.h>
1010
#include <linux/cache.h>
11+
#include <linux/prctl.h>
1112

1213
#include <vdso/processor.h>
1314

@@ -82,6 +83,7 @@ struct thread_struct {
8283
unsigned long bad_cause;
8384
unsigned long vstate_ctrl;
8485
struct __riscv_v_ext_state vstate;
86+
unsigned long align_ctl;
8587
};
8688

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

9597
#define INIT_THREAD { \
9698
.sp = sizeof(init_stack) + (long)&init_stack, \
99+
.align_ctl = PR_UNALIGN_NOPRINT, \
97100
}
98101

99102
#define task_pt_regs(tsk) \
@@ -134,6 +137,12 @@ extern long riscv_v_vstate_ctrl_set_current(unsigned long arg);
134137
extern long riscv_v_vstate_ctrl_get_current(void);
135138
#endif /* CONFIG_RISCV_ISA_V */
136139

140+
extern int get_unalign_ctl(struct task_struct *tsk, unsigned long addr);
141+
extern int set_unalign_ctl(struct task_struct *tsk, unsigned int val);
142+
143+
#define GET_UNALIGN_CTL(tsk, addr) get_unalign_ctl((tsk), (addr))
144+
#define SET_UNALIGN_CTL(tsk, val) set_unalign_ctl((tsk), (val))
145+
137146
#endif /* __ASSEMBLY__ */
138147

139148
#endif /* _ASM_RISCV_PROCESSOR_H */

arch/riscv/kernel/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ obj-y += patch.o
5959
obj-y += probes/
6060
obj-$(CONFIG_MMU) += vdso.o vdso/
6161

62-
obj-$(CONFIG_RISCV_M_MODE) += traps_misaligned.o
62+
obj-$(CONFIG_RISCV_MISALIGNED) += traps_misaligned.o
6363
obj-$(CONFIG_FPU) += fpu.o
6464
obj-$(CONFIG_RISCV_ISA_V) += vector.o
6565
obj-$(CONFIG_SMP) += smpboot.o

arch/riscv/kernel/cpufeature.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,9 @@ void check_unaligned_access(int cpu)
569569
void *src;
570570
long speed = RISCV_HWPROBE_MISALIGNED_SLOW;
571571

572+
if (check_unaligned_access_emulated(cpu))
573+
return;
574+
572575
page = alloc_pages(GFP_NOWAIT, get_order(MISALIGNED_BUFFER_SIZE));
573576
if (!page) {
574577
pr_warn("Can't alloc pages to measure memcpy performance");
@@ -646,9 +649,10 @@ void check_unaligned_access(int cpu)
646649
__free_pages(page, get_order(MISALIGNED_BUFFER_SIZE));
647650
}
648651

649-
static int check_unaligned_access_boot_cpu(void)
652+
static int __init check_unaligned_access_boot_cpu(void)
650653
{
651654
check_unaligned_access(0);
655+
unaligned_emulation_finish();
652656
return 0;
653657
}
654658

arch/riscv/kernel/fpu.S

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,124 @@ ENTRY(__fstate_restore)
104104
csrc CSR_STATUS, t1
105105
ret
106106
ENDPROC(__fstate_restore)
107+
108+
#define get_f32(which) fmv.x.s a0, which; j 2f
109+
#define put_f32(which) fmv.s.x which, a1; j 2f
110+
#if __riscv_xlen == 64
111+
# define get_f64(which) fmv.x.d a0, which; j 2f
112+
# define put_f64(which) fmv.d.x which, a1; j 2f
113+
#else
114+
# define get_f64(which) fsd which, 0(a1); j 2f
115+
# define put_f64(which) fld which, 0(a1); j 2f
116+
#endif
117+
118+
.macro fp_access_prologue
119+
/*
120+
* Compute jump offset to store the correct FP register since we don't
121+
* have indirect FP register access
122+
*/
123+
sll t0, a0, 3
124+
la t2, 1f
125+
add t0, t0, t2
126+
li t1, SR_FS
127+
csrs CSR_STATUS, t1
128+
jr t0
129+
1:
130+
.endm
131+
132+
.macro fp_access_epilogue
133+
2:
134+
csrc CSR_STATUS, t1
135+
ret
136+
.endm
137+
138+
#define fp_access_body(__access_func) \
139+
__access_func(f0); \
140+
__access_func(f1); \
141+
__access_func(f2); \
142+
__access_func(f3); \
143+
__access_func(f4); \
144+
__access_func(f5); \
145+
__access_func(f6); \
146+
__access_func(f7); \
147+
__access_func(f8); \
148+
__access_func(f9); \
149+
__access_func(f10); \
150+
__access_func(f11); \
151+
__access_func(f12); \
152+
__access_func(f13); \
153+
__access_func(f14); \
154+
__access_func(f15); \
155+
__access_func(f16); \
156+
__access_func(f17); \
157+
__access_func(f18); \
158+
__access_func(f19); \
159+
__access_func(f20); \
160+
__access_func(f21); \
161+
__access_func(f22); \
162+
__access_func(f23); \
163+
__access_func(f24); \
164+
__access_func(f25); \
165+
__access_func(f26); \
166+
__access_func(f27); \
167+
__access_func(f28); \
168+
__access_func(f29); \
169+
__access_func(f30); \
170+
__access_func(f31)
171+
172+
173+
#ifdef CONFIG_RISCV_MISALIGNED
174+
175+
/*
176+
* Disable compressed instructions set to keep a constant offset between FP
177+
* load/store/move instructions
178+
*/
179+
.option norvc
180+
/*
181+
* put_f32_reg - Set a FP register from a register containing the value
182+
* a0 = FP register index to be set
183+
* a1 = value to be loaded in the FP register
184+
*/
185+
SYM_FUNC_START(put_f32_reg)
186+
fp_access_prologue
187+
fp_access_body(put_f32)
188+
fp_access_epilogue
189+
SYM_FUNC_END(put_f32_reg)
190+
191+
/*
192+
* get_f32_reg - Get a FP register value and return it
193+
* a0 = FP register index to be retrieved
194+
*/
195+
SYM_FUNC_START(get_f32_reg)
196+
fp_access_prologue
197+
fp_access_body(get_f32)
198+
fp_access_epilogue
199+
SYM_FUNC_END(get_f32_reg)
200+
201+
/*
202+
* put_f64_reg - Set a 64 bits FP register from a value or a pointer.
203+
* a0 = FP register index to be set
204+
* a1 = value/pointer to be loaded in the FP register (when xlen == 32 bits, we
205+
* load the value to a pointer).
206+
*/
207+
SYM_FUNC_START(put_f64_reg)
208+
fp_access_prologue
209+
fp_access_body(put_f64)
210+
fp_access_epilogue
211+
SYM_FUNC_END(put_f64_reg)
212+
213+
/*
214+
* put_f64_reg - Get a 64 bits FP register value and returned it or store it to
215+
* a pointer.
216+
* a0 = FP register index to be retrieved
217+
* a1 = If xlen == 32, pointer which should be loaded with the FP register value
218+
* or unused if xlen == 64. In which case the FP register value is returned
219+
* through a0
220+
*/
221+
SYM_FUNC_START(get_f64_reg)
222+
fp_access_prologue
223+
fp_access_body(get_f64)
224+
fp_access_epilogue
225+
SYM_FUNC_END(get_f64_reg)
226+
227+
#endif /* CONFIG_RISCV_MISALIGNED */

arch/riscv/kernel/process.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <asm/thread_info.h>
2626
#include <asm/cpuidle.h>
2727
#include <asm/vector.h>
28+
#include <asm/cpufeature.h>
2829

2930
register unsigned long gp_in_global __asm__("gp");
3031

@@ -41,6 +42,23 @@ void arch_cpu_idle(void)
4142
cpu_do_idle();
4243
}
4344

45+
int set_unalign_ctl(struct task_struct *tsk, unsigned int val)
46+
{
47+
if (!unaligned_ctl_available())
48+
return -EINVAL;
49+
50+
tsk->thread.align_ctl = val;
51+
return 0;
52+
}
53+
54+
int get_unalign_ctl(struct task_struct *tsk, unsigned long adr)
55+
{
56+
if (!unaligned_ctl_available())
57+
return -EINVAL;
58+
59+
return put_user(tsk->thread.align_ctl, (unsigned long __user *)adr);
60+
}
61+
4462
void __show_regs(struct pt_regs *regs)
4563
{
4664
show_regs_print_info(KERN_DEFAULT);

arch/riscv/kernel/smpboot.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@ asmlinkage __visible void smp_callin(void)
247247
riscv_ipi_enable();
248248

249249
numa_add_cpu(curr_cpuid);
250-
set_cpu_online(curr_cpuid, 1);
251250
check_unaligned_access(curr_cpuid);
251+
set_cpu_online(curr_cpuid, 1);
252252

253253
if (has_vector()) {
254254
if (riscv_v_setup_vsize())

arch/riscv/kernel/traps.c

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,6 @@ asmlinkage __visible __trap_section void do_trap_insn_illegal(struct pt_regs *re
179179

180180
DO_ERROR_INFO(do_trap_load_fault,
181181
SIGSEGV, SEGV_ACCERR, "load access fault");
182-
#ifndef CONFIG_RISCV_M_MODE
183-
DO_ERROR_INFO(do_trap_load_misaligned,
184-
SIGBUS, BUS_ADRALN, "Oops - load address misaligned");
185-
DO_ERROR_INFO(do_trap_store_misaligned,
186-
SIGBUS, BUS_ADRALN, "Oops - store (or AMO) address misaligned");
187-
#else
188-
int handle_misaligned_load(struct pt_regs *regs);
189-
int handle_misaligned_store(struct pt_regs *regs);
190182

191183
asmlinkage __visible __trap_section void do_trap_load_misaligned(struct pt_regs *regs)
192184
{
@@ -229,7 +221,6 @@ asmlinkage __visible __trap_section void do_trap_store_misaligned(struct pt_regs
229221
irqentry_nmi_exit(regs, state);
230222
}
231223
}
232-
#endif
233224
DO_ERROR_INFO(do_trap_store_fault,
234225
SIGSEGV, SEGV_ACCERR, "store (or AMO) access fault");
235226
DO_ERROR_INFO(do_trap_ecall_s,

0 commit comments

Comments
 (0)