Skip to content

Commit 7c83232

Browse files
clementlegerpalmer-dabbelt
authored andcommitted
riscv: add support for misaligned trap handling in S-mode
Misalignment trap handling is only supported for M-mode and uses direct accesses to user memory. In S-mode, when handling usermode fault, this requires to use the get_user()/put_user() accessors. Implement load_u8(), store_u8() and get_insn() using these accessors for userspace and direct text access for kernel. Signed-off-by: Clément Léger <[email protected]> Reviewed-by: Björn Töpel <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Palmer Dabbelt <[email protected]>
1 parent f19c3b4 commit 7c83232

File tree

5 files changed

+129
-23
lines changed

5 files changed

+129
-23
lines changed

arch/riscv/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,14 @@ config THREAD_SIZE_ORDER
636636
Specify the Pages of thread stack size (from 4KB to 64KB), which also
637637
affects irq stack size, which is equal to thread stack size.
638638

639+
config RISCV_MISALIGNED
640+
bool "Support misaligned load/store traps for kernel and userspace"
641+
default y
642+
help
643+
Say Y here if you want the kernel to embed support for misaligned
644+
load/store for both kernel and userspace. When disable, misaligned
645+
accesses will generate SIGBUS in userspace and panic in kernel.
646+
639647
endmenu # "Platform type"
640648

641649
menu "Kernel features"

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/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/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,

arch/riscv/kernel/traps_misaligned.c

Lines changed: 106 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <asm/processor.h>
1313
#include <asm/ptrace.h>
1414
#include <asm/csr.h>
15+
#include <asm/entry-common.h>
1516

1617
#define INSN_MATCH_LB 0x3
1718
#define INSN_MASK_LB 0x707f
@@ -151,21 +152,25 @@
151152
#define PRECISION_S 0
152153
#define PRECISION_D 1
153154

154-
static inline u8 load_u8(const u8 *addr)
155+
#ifdef CONFIG_RISCV_M_MODE
156+
static inline int load_u8(struct pt_regs *regs, const u8 *addr, u8 *r_val)
155157
{
156158
u8 val;
157159

158160
asm volatile("lbu %0, %1" : "=&r" (val) : "m" (*addr));
161+
*r_val = val;
159162

160-
return val;
163+
return 0;
161164
}
162165

163-
static inline void store_u8(u8 *addr, u8 val)
166+
static inline int store_u8(struct pt_regs *regs, u8 *addr, u8 val)
164167
{
165168
asm volatile ("sb %0, %1\n" : : "r" (val), "m" (*addr));
169+
170+
return 0;
166171
}
167172

168-
static inline ulong get_insn(ulong mepc)
173+
static inline int get_insn(struct pt_regs *regs, ulong mepc, ulong *r_insn)
169174
{
170175
register ulong __mepc asm ("a2") = mepc;
171176
ulong val, rvc_mask = 3, tmp;
@@ -194,9 +199,87 @@ static inline ulong get_insn(ulong mepc)
194199
: [addr] "r" (__mepc), [rvc_mask] "r" (rvc_mask),
195200
[xlen_minus_16] "i" (XLEN_MINUS_16));
196201

197-
return val;
202+
*r_insn = val;
203+
204+
return 0;
205+
}
206+
#else
207+
static inline int load_u8(struct pt_regs *regs, const u8 *addr, u8 *r_val)
208+
{
209+
if (user_mode(regs)) {
210+
return __get_user(*r_val, addr);
211+
} else {
212+
*r_val = *addr;
213+
return 0;
214+
}
198215
}
199216

217+
static inline int store_u8(struct pt_regs *regs, u8 *addr, u8 val)
218+
{
219+
if (user_mode(regs)) {
220+
return __put_user(val, addr);
221+
} else {
222+
*addr = val;
223+
return 0;
224+
}
225+
}
226+
227+
#define __read_insn(regs, insn, insn_addr) \
228+
({ \
229+
int __ret; \
230+
\
231+
if (user_mode(regs)) { \
232+
__ret = __get_user(insn, insn_addr); \
233+
} else { \
234+
insn = *insn_addr; \
235+
__ret = 0; \
236+
} \
237+
\
238+
__ret; \
239+
})
240+
241+
static inline int get_insn(struct pt_regs *regs, ulong epc, ulong *r_insn)
242+
{
243+
ulong insn = 0;
244+
245+
if (epc & 0x2) {
246+
ulong tmp = 0;
247+
u16 __user *insn_addr = (u16 __user *)epc;
248+
249+
if (__read_insn(regs, insn, insn_addr))
250+
return -EFAULT;
251+
/* __get_user() uses regular "lw" which sign extend the loaded
252+
* value make sure to clear higher order bits in case we "or" it
253+
* below with the upper 16 bits half.
254+
*/
255+
insn &= GENMASK(15, 0);
256+
if ((insn & __INSN_LENGTH_MASK) != __INSN_LENGTH_32) {
257+
*r_insn = insn;
258+
return 0;
259+
}
260+
insn_addr++;
261+
if (__read_insn(regs, tmp, insn_addr))
262+
return -EFAULT;
263+
*r_insn = (tmp << 16) | insn;
264+
265+
return 0;
266+
} else {
267+
u32 __user *insn_addr = (u32 __user *)epc;
268+
269+
if (__read_insn(regs, insn, insn_addr))
270+
return -EFAULT;
271+
if ((insn & __INSN_LENGTH_MASK) == __INSN_LENGTH_32) {
272+
*r_insn = insn;
273+
return 0;
274+
}
275+
insn &= GENMASK(15, 0);
276+
*r_insn = insn;
277+
278+
return 0;
279+
}
280+
}
281+
#endif
282+
200283
union reg_data {
201284
u8 data_bytes[8];
202285
ulong data_ulong;
@@ -207,10 +290,13 @@ int handle_misaligned_load(struct pt_regs *regs)
207290
{
208291
union reg_data val;
209292
unsigned long epc = regs->epc;
210-
unsigned long insn = get_insn(epc);
211-
unsigned long addr = csr_read(mtval);
293+
unsigned long insn;
294+
unsigned long addr = regs->badaddr;
212295
int i, fp = 0, shift = 0, len = 0;
213296

297+
if (get_insn(regs, epc, &insn))
298+
return -1;
299+
214300
regs->epc = 0;
215301

216302
if ((insn & INSN_MASK_LW) == INSN_MATCH_LW) {
@@ -274,8 +360,10 @@ int handle_misaligned_load(struct pt_regs *regs)
274360
}
275361

276362
val.data_u64 = 0;
277-
for (i = 0; i < len; i++)
278-
val.data_bytes[i] = load_u8((void *)(addr + i));
363+
for (i = 0; i < len; i++) {
364+
if (load_u8(regs, (void *)(addr + i), &val.data_bytes[i]))
365+
return -1;
366+
}
279367

280368
if (fp)
281369
return -1;
@@ -290,10 +378,13 @@ int handle_misaligned_store(struct pt_regs *regs)
290378
{
291379
union reg_data val;
292380
unsigned long epc = regs->epc;
293-
unsigned long insn = get_insn(epc);
294-
unsigned long addr = csr_read(mtval);
381+
unsigned long insn;
382+
unsigned long addr = regs->badaddr;
295383
int i, len = 0;
296384

385+
if (get_insn(regs, epc, &insn))
386+
return -1;
387+
297388
regs->epc = 0;
298389

299390
val.data_ulong = GET_RS2(insn, regs);
@@ -327,8 +418,10 @@ int handle_misaligned_store(struct pt_regs *regs)
327418
return -1;
328419
}
329420

330-
for (i = 0; i < len; i++)
331-
store_u8((void *)(addr + i), val.data_bytes[i]);
421+
for (i = 0; i < len; i++) {
422+
if (store_u8(regs, (void *)(addr + i), val.data_bytes[i]))
423+
return -1;
424+
}
332425

333426
regs->epc = epc + INSN_LEN(insn);
334427

0 commit comments

Comments
 (0)