[PATCH 3/3] nds32: Add unaligned access in kernel space.
From: Nickhu
Date: Thu Oct 18 2018 - 04:56:18 EST
As my colleague has encountered kernel panic when unaligned access
in kernel space. Here is the situation, the structure 'TP_STRUCT__entry':
TP_STRUCT__entry(
__field( u32, tb_id )
__field( int, err )
__field( int, oif )
__field( int, iif )
__field( __u8, tos )
__field( __u8, scope )
__field( __u8, flags )
__field( u8, proto )
__array( __u8, src, 4 )
__array( __u8, dst, 4 )
__array( __u8, gw, 4 )
__array( __u8, saddr, 4 )
__field( u16, sport )
__field( u16, dport )
__dynamic_array(char, name, IFNAMSIZ )
)
When he try to access the element in the structure, the kernel panic
happen. Although he has rearrange the order of the structure to fix
the problem, but we cannot ignore the fact that there still need
unaligned access in kernel space. It can help us to avoid kernel panic
when reasonable unaligned address access happen. The users need to have
the knowledge that some unreasonable unaligned address may cause the bug
in kernel.
The config 'HAVE_EFFICIENT_UNALIGNED_ACCESS' must be with the hw
unaligned access config 'HW_SUPPORT_UNALIGNMENT_ACCESS'. In sw
unalinged access handler, the code 'get_inst()' in arch/nds32/mm/
alignment.c:522 would be generate as load word instruction if
'HAVE_EFFICIENT_UNALIGNED_ACCESS' is set. This would cause the kernel
hang in loop if the address of the load word instruction is unaligned.
For example:
0xbc39e: lwi450 $r0, [$r1], if the $r1 cause unaligned access.
|
| unaligned access handler
v
arch/nds32/mm/alignment.c:522: get_ints():0xb0874b7e lwi450 $r2, [$3],
$r3 is the address '0xbc39e', it would cause kernel unaligned access.
|
| unaligned access handler
v
arch/nds32/mm/alignment.c:522: get_ints():0xb0874b7e lwi450 $r2, [$3],
$r3 is the address '0xb0874b7e', it would cause kernel unaligned access.
The kernel is hang in the loop.
Signed-off-by: Nickhu <nickhu@xxxxxxxxxxxxx>
---
arch/nds32/kernel/traps.c | 4 +++-
arch/nds32/mm/alignment.c | 6 ++++--
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/arch/nds32/kernel/traps.c b/arch/nds32/kernel/traps.c
index 1496aab48998..dcde7abc5515 100644
--- a/arch/nds32/kernel/traps.c
+++ b/arch/nds32/kernel/traps.c
@@ -331,6 +331,7 @@ void do_revinsn(struct pt_regs *regs)
#ifdef CONFIG_ALIGNMENT_TRAP
extern int unalign_access_mode;
extern int do_unaligned_access(unsigned long addr, struct pt_regs *regs);
+extern int va_kernel_present(unsigned long addr);
#endif
void do_dispatch_general(unsigned long entry, unsigned long addr,
unsigned long itype, struct pt_regs *regs,
@@ -341,7 +342,8 @@ void do_dispatch_general(unsigned long entry, unsigned long addr,
if (type == ETYPE_ALIGNMENT_CHECK) {
#ifdef CONFIG_ALIGNMENT_TRAP
/* Alignment check */
- if (user_mode(regs) && unalign_access_mode) {
+ if ((user_mode(regs) && unalign_access_mode) ||
+ va_kernel_present(addr)) {
int ret;
ret = do_unaligned_access(addr, regs);
diff --git a/arch/nds32/mm/alignment.c b/arch/nds32/mm/alignment.c
index 66a556befd05..2d7a08af6622 100644
--- a/arch/nds32/mm/alignment.c
+++ b/arch/nds32/mm/alignment.c
@@ -524,8 +524,10 @@ int do_unaligned_access(unsigned long addr, struct pt_regs *regs)
DEBUG((unalign_access_debug > 0), 1,
"Faulting addr: 0x%08lx, pc: 0x%08lx [inst: 0x%08lx ]\n", addr,
regs->ipc, inst);
-
- set_fs(USER_DS);
+ if ((user_mode(regs) && unalign_access_mode))
+ set_fs(USER_DS);
+ else if (va_kernel_present(addr))
+ set_fs(KERNEL_DS);
if (inst & NDS32_16BIT_INSTRUCTION)
ret = do_16((inst >> 16) & 0xffff, regs);
--
2.17.0