When the instruction code under PC address is read through
_probe_kernel_read in do_alignment,if the pte page corresponding
to the code segment of PC address is reclaimed exactly at this time,
the address mapping cannot be reconstructed because page fault_disable()
is executed in _probe_kernel_read function,and the failure to obtain
the instruction code of PC finally results in the unsuccessful repair
operation.
Thus we can modify the implementation of reading user-mode PC instruction
before local_irq_enable to avoid the above risk.
At the same time, adjust the sequence of code processing and optimize the
process.

Signed-off-by: xiaoqian <xiaoqi...@huawei.com>
Cc: sta...@vger.kernel.org
---
 arch/arm/mm/alignment.c | 81 +++++++++++++++++++++++++++++++++----------------
 1 file changed, 55 insertions(+), 26 deletions(-)

diff --git a/arch/arm/mm/alignment.c b/arch/arm/mm/alignment.c
index e376883ab35b..4124b9ce3c70 100644
--- a/arch/arm/mm/alignment.c
+++ b/arch/arm/mm/alignment.c
@@ -76,6 +76,11 @@
 #define IS_T32(hi16) \
        (((hi16) & 0xe000) == 0xe000 && ((hi16) & 0x1800))
 
+#define INVALID_INSTR_MODE     0
+#define ARM_INSTR_MODE         1
+#define THUMB_INSTR_MODE       2
+#define THUMB2_INSTR_MODE      3
+
 static unsigned long ai_user;
 static unsigned long ai_sys;
 static void *ai_sys_last_pc;
@@ -705,6 +710,48 @@ thumb2arm(u16 tinstr)
        }
 }
 
+static unsigned int
+fetch_usr_pc_instr(struct pt_regs *regs, unsigned long *pc_instrptr)
+{
+       unsigned int fault;
+       unsigned long instrptr;
+       unsigned long instr_mode = INVALID_INSTR_MODE;
+
+       instrptr = instruction_pointer(regs);
+
+       if (thumb_mode(regs)) {
+               u16 tinstr = 0;
+               u16 *ptr = (u16 *)(instrptr & ~1);
+
+               fault = probe_kernel_address(ptr, tinstr);
+               if (!fault) {
+                       tinstr = __mem_to_opcode_thumb16(tinstr);
+                       if (cpu_architecture() >= CPU_ARCH_ARMv7 &&
+                           IS_T32(tinstr)) {
+                               /* Thumb-2 32-bit */
+                               u16 tinstr2 = 0;
+
+                               fault = probe_kernel_address(ptr + 1, tinstr2);
+                               if (!fault) {
+                                       tinstr2 = 
__mem_to_opcode_thumb16(tinstr2);
+                                       *pc_instrptr = 
__opcode_thumb32_compose(tinstr, tinstr2);
+                                       instr_mode = THUMB2_INSTR_MODE;
+                               }
+                       } else {
+                               *pc_instrptr = thumb2arm(tinstr);
+                               instr_mode = THUMB_INSTR_MODE;
+                       }
+               }
+       } else {
+               fault = probe_kernel_address((void *)instrptr, *pc_instrptr);
+               if (!fault) {
+                       *pc_instrptr = __mem_to_opcode_arm(*pc_instrptr);
+                       instr_mode = ARM_INSTR_MODE;
+               }
+       }
+       return instr_mode;
+}
+
 /*
  * Convert Thumb-2 32 bit LDM, STM, LDRD, STRD to equivalent instruction
  * handlable by ARM alignment handler, also find the corresponding handler,
@@ -775,42 +822,24 @@ do_alignment(unsigned long addr, unsigned int fsr, struct 
pt_regs *regs)
        unsigned long instr = 0, instrptr;
        int (*handler)(unsigned long addr, unsigned long instr, struct pt_regs 
*regs);
        unsigned int type;
-       unsigned int fault;
        u16 tinstr = 0;
        int isize = 4;
        int thumb2_32b = 0;
+       unsigned long pc_instr_mode;
+
+       pc_instr_mode = fetch_usr_pc_instr(regs, &instr);
 
        if (interrupts_enabled(regs))
                local_irq_enable();
 
        instrptr = instruction_pointer(regs);
-
-       if (thumb_mode(regs)) {
-               u16 *ptr = (u16 *)(instrptr & ~1);
-               fault = probe_kernel_address(ptr, tinstr);
-               tinstr = __mem_to_opcode_thumb16(tinstr);
-               if (!fault) {
-                       if (cpu_architecture() >= CPU_ARCH_ARMv7 &&
-                           IS_T32(tinstr)) {
-                               /* Thumb-2 32-bit */
-                               u16 tinst2 = 0;
-                               fault = probe_kernel_address(ptr + 1, tinst2);
-                               tinst2 = __mem_to_opcode_thumb16(tinst2);
-                               instr = __opcode_thumb32_compose(tinstr, 
tinst2);
-                               thumb2_32b = 1;
-                       } else {
-                               isize = 2;
-                               instr = thumb2arm(tinstr);
-                       }
-               }
-       } else {
-               fault = probe_kernel_address((void *)instrptr, instr);
-               instr = __mem_to_opcode_arm(instr);
-       }
-
-       if (fault) {
+       if (pc_instr_mode == INVALID_INSTR_MODE) {
                type = TYPE_FAULT;
                goto bad_or_fault;
+       } else if (pc_instr_mode == THUMB_INSTR_MODE) {
+               isize = 2;
+       } else if (pc_instr_mode == THUMB2_INSTR_MODE) {
+               thumb2_32b = 1;
        }
 
        if (user_mode(regs))
-- 
2.12.3

Reply via email to