Skip to contentSkip to conversation

nds32: Unaligned access handler fix

PATCH In review Nickhu 2 participants 5 messages Oct 18, 2018 – Oct 19, 2018

lore
Share & tools
lore

Nickhu

author
#0 lore raw

cover nds32: Unaligned access handler fix

The patches are about unaligned access handler. We fix some bugs in unaligned access handler and add some kernel configs for unaligned access handler. Then we add the kernel unaligned access handled by software in handler.

Nickhu (3):
nds32: Fix instruction simulator bug for unaligned access handler.
nds32: Add 'HAVE_EFFICIENT_UNALIGNED_ACCESS' config
nds32: Add unaligned access in kernel space.

 arch/nds32/Kconfig.cpu    |  3 ++-
 arch/nds32/kernel/traps.c |  4 +++-
 arch/nds32/mm/alignment.c | 43 +++++++++++++++++++++++----------------
 3 files changed, 30 insertions(+), 20 deletions(-)
Signature · 1 line
2.17.0
author
#1 lore raw

PATCH 1/3 nds32: Fix instruction simulator bug for unaligned access handler.

When emulating the 16 bits instructions, the mapping of general
purpose registers is not the same as 32 bits instructions.

	Example:
		'LWI450 r16, [r15]' 16-bit instruction will be decoded as

'1011010110001110', the target register field is decode as index=12.

But the index of target register should be 16. So the mapping of
register in unaligned access handler is wrong.

---
 arch/nds32/mm/alignment.c | 37 +++++++++++++++++++++----------------
 1 file changed, 21 insertions(+), 16 deletions(-)
arch/nds32/mm/alignment.c +21 -16
index e1aed9dc692d..66a556befd05 100644
@@ -152,12 +152,16 @@ extern int va_writable(struct pt_regs *regs, unsigned long addr);
 
 int unalign_access_mode = 0, unalign_access_debug = 0;
 
-static inline unsigned long *idx_to_addr(struct pt_regs *regs, int idx)
+static inline unsigned long *idx_to_addr(struct pt_regs *regs, int idx,
+				int idx_mode)
 {
 	/* this should be consistent with ptrace.h */
-	if (idx >= 0 && idx <= 25)	/* R0-R25 */
-		return &regs->uregs[0] + idx;
-	else if (idx >= 28 && idx <= 30)	/* FP, GP, LP */
+	if (idx >= 0 && idx <= 25) {	/* R0-R25 */
+		if (idx_mode == 4 && idx > 11)
+			return &regs->uregs[0] + idx + 4;
+		else
+			return &regs->uregs[0] + idx;
+	} else if (idx >= 28 && idx <= 30)	/* FP, GP, LP */
 		return &regs->fp + (idx - 28);
 	else if (idx == 31)	/* SP */
 		return &regs->sp;
@@ -270,10 +274,10 @@ static inline int do_16(unsigned long inst, struct pt_regs *regs)
 	}
 
 	if (addr_mode == 3) {
-		unaligned_addr = *idx_to_addr(regs, RA3(inst));
+		unaligned_addr = *idx_to_addr(regs, RA3(inst), addr_mode);
 		source_idx = RA3(inst);
 	} else {
-		unaligned_addr = *idx_to_addr(regs, RA5(inst));
+		unaligned_addr = *idx_to_addr(regs, RA5(inst), addr_mode);
 		source_idx = RA5(inst);
 	}
 
@@ -293,16 +297,17 @@ static inline int do_16(unsigned long inst, struct pt_regs *regs)
 			return -EACCES;
 
 		get_data(unaligned_addr, &target_val, len);
-		*idx_to_addr(regs, target_idx) = target_val;
+		*idx_to_addr(regs, target_idx, idx_mode) = target_val;
 	} else {
 		if (!access_ok(VERIFY_WRITE, (void *)unaligned_addr, len))
 			return -EACCES;
-		target_val = *idx_to_addr(regs, target_idx);
+		target_val = *idx_to_addr(regs, target_idx, idx_mode);
 		set_data((void *)unaligned_addr, target_val, len);
 	}
 
 	if (!regular)
-		*idx_to_addr(regs, source_idx) = unaligned_addr + shift;
+		*idx_to_addr(regs, source_idx, idx_mode) =
+			unaligned_addr + shift;
 	regs->ipc += 2;
 
 	return 0;
@@ -312,10 +317,10 @@ static inline int do_16(unsigned long inst, struct pt_regs *regs)
 
 static inline int do_32(unsigned long inst, struct pt_regs *regs)
 {
-	int imm, regular, load, len, sign_ext;
+	int imm, regular, load, len, sign_ext, idx_mode = 5;
 	unsigned long unaligned_addr, target_val, shift;
 
-	unaligned_addr = *idx_to_addr(regs, RA(inst));
+	unaligned_addr = *idx_to_addr(regs, RA(inst), idx_mode);
 
 	switch ((inst >> 25) << 1) {
 
@@ -472,7 +477,7 @@ static inline int do_32(unsigned long inst, struct pt_regs *regs)
 	if (imm)
 		shift = GET_IMMSVAL(IMM(inst)) * len;
 	else
-		shift = *idx_to_addr(regs, RB(inst)) << SV(inst);
+		shift = *idx_to_addr(regs, RB(inst), idx_mode) << SV(inst);
 
 	if (regular)
 		unaligned_addr += shift;
@@ -485,21 +490,21 @@ static inline int do_32(unsigned long inst, struct pt_regs *regs)
 		get_data(unaligned_addr, &target_val, len);
 
 		if (sign_ext)
-			*idx_to_addr(regs, RT(inst)) =
+			*idx_to_addr(regs, RT(inst), idx_mode) =
 			    sign_extend(target_val, len);
 		else
-			*idx_to_addr(regs, RT(inst)) = target_val;
+			*idx_to_addr(regs, RT(inst), idx_mode) = target_val;
 	} else {
 
 		if (!access_ok(VERIFY_WRITE, (void *)unaligned_addr, len))
 			return -EACCES;
 
-		target_val = *idx_to_addr(regs, RT(inst));
+		target_val = *idx_to_addr(regs, RT(inst), idx_mode);
 		set_data((void *)unaligned_addr, target_val, len);
 	}
 
 	if (!regular)
-		*idx_to_addr(regs, RA(inst)) = unaligned_addr + shift;
+		*idx_to_addr(regs, RA(inst), idx_mode) = unaligned_addr + shift;
 
 	regs->ipc += 4;
 
Signature · 1 line
2.17.0
author
#2 lore raw

PATCH 2/3 nds32: Add 'HAVE_EFFICIENT_UNALIGNED_ACCESS' config

In reply to Nickhu

According to my understanding, this config will optimize the code generate. When there is an unaligned access happened, the load word instruction still can be used if there is unaligned access support or the load byte instruction is used. So this config need unaligned access support.

'HAVE_EFFICIENT_UNALIGNED_ACCESS' and 'HW_SUPPORT_UNALIGNMENT_ACCESS' are default configs in nds32.

---
 arch/nds32/Kconfig.cpu | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
arch/nds32/Kconfig.cpu +2 -1
index b8c8984d1456..b8eecd0cde6b 100644
@@ -111,8 +111,9 @@ config ALIGNMENT_TRAP
 
 config HW_SUPPORT_UNALIGNMENT_ACCESS
 	bool "Kernel support unaligned access handling by hw"
+	select HAVE_EFFICIENT_UNALIGNED_ACCESS
 	depends on !ALIGNMENT_TRAP
-	default n
+	default y
 	help
 	  Andes processors load/store world/half-word instructions can access
 	  unaligned memory locations without generating the Data Alignment
Signature · 1 line
2.17.0
author
#3 lore raw

PATCH 3/3 nds32: Add unaligned access in kernel space.

In reply to Nickhu

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.

---
 arch/nds32/kernel/traps.c | 4 +++-
 arch/nds32/mm/alignment.c | 6 ++++--
 2 files changed, 7 insertions(+), 3 deletions(-)
arch/nds32/kernel/traps.c +3 -1
index 1496aab48998..dcde7abc5515 100644
@@ -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);
 
arch/nds32/mm/alignment.c +4 -2
index 66a556befd05..2d7a08af6622 100644
@@ -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);
Signature · 1 line
2.17.0
Acked-by on the v1 series
#4 lore raw
In reply to Nickhu

Nickhu <[email protected]> 於 2018年10月18日 週四 下午4:34寫道:

Quoted 11 lines

The patches are about unaligned access handler. We fix some bugs in unaligned access handler and add some kernel configs for unaligned access handler. Then we add the kernel unaligned access handled by software in handler.

Nickhu (3):
nds32: Fix instruction simulator bug for unaligned access handler.
nds32: Add 'HAVE_EFFICIENT_UNALIGNED_ACCESS' config
nds32: Add unaligned access in kernel space.

Quoted 5 lines
 arch/nds32/Kconfig.cpu    |  3 ++-
 arch/nds32/kernel/traps.c |  4 +++-
 arch/nds32/mm/alignment.c | 43 +++++++++++++++++++++++----------------
 3 files changed, 30 insertions(+), 20 deletions(-)

Hi Nick,
Thanks