2018-10-18 08:56:47

by Nick Hu

[permalink] [raw]
Subject: [PATCH 0/3] 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(-)

--
2.17.0



2018-10-18 08:56:48

by Nick Hu

[permalink] [raw]
Subject: [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.

Signed-off-by: Nickhu <[email protected]>
---
arch/nds32/mm/alignment.c | 37 +++++++++++++++++++++----------------
1 file changed, 21 insertions(+), 16 deletions(-)

diff --git a/arch/nds32/mm/alignment.c b/arch/nds32/mm/alignment.c
index e1aed9dc692d..66a556befd05 100644
--- a/arch/nds32/mm/alignment.c
+++ b/arch/nds32/mm/alignment.c
@@ -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;

--
2.17.0


2018-10-18 08:57:01

by Nick Hu

[permalink] [raw]
Subject: [PATCH 2/3] nds32: Add 'HAVE_EFFICIENT_UNALIGNED_ACCESS' config

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.

Signed-off-by: Nickhu <[email protected]>
---
arch/nds32/Kconfig.cpu | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/arch/nds32/Kconfig.cpu b/arch/nds32/Kconfig.cpu
index b8c8984d1456..b8eecd0cde6b 100644
--- a/arch/nds32/Kconfig.cpu
+++ b/arch/nds32/Kconfig.cpu
@@ -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
--
2.17.0


2018-10-18 08:59:06

by Nick Hu

[permalink] [raw]
Subject: [PATCH 3/3] nds32: Add unaligned access in kernel space.

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 <[email protected]>
---
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


2018-10-19 09:26:58

by Greentime Hu

[permalink] [raw]
Subject: Re: [PATCH 0/3] nds32: Unaligned access handler fix

Nickhu <[email protected]> 於 2018年10月18日 週四 下午4:34寫道:
>
> 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(-)
>

Hi Nick,
Thanks
Acked-by: Greentime Hu <[email protected]>