Module Name:    src
Committed By:   ryo
Date:           Sun Jul 26 07:25:38 UTC 2020

Modified Files:
        src/sys/arch/aarch64/aarch64: trap.c

Log Message:
- add support conditionally execution for A32 instruction emulation
- separated the processing of ARM and THUMB emul clearly. do not confuse the 
Thumb-32bit instruction with the ARM instruction.
- use far_el1 instead of tf_pc to return correct fault address when instruction 
emulation


To generate a diff of this commit:
cvs rdiff -u -r1.31 -r1.32 src/sys/arch/aarch64/aarch64/trap.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/aarch64/aarch64/trap.c
diff -u src/sys/arch/aarch64/aarch64/trap.c:1.31 src/sys/arch/aarch64/aarch64/trap.c:1.32
--- src/sys/arch/aarch64/aarch64/trap.c:1.31	Wed Jul  8 03:45:13 2020
+++ src/sys/arch/aarch64/aarch64/trap.c	Sun Jul 26 07:25:38 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: trap.c,v 1.31 2020/07/08 03:45:13 ryo Exp $ */
+/* $NetBSD: trap.c,v 1.32 2020/07/26 07:25:38 ryo Exp $ */
 
 /*-
  * Copyright (c) 2014 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.31 2020/07/08 03:45:13 ryo Exp $");
+__KERNEL_RCSID(1, "$NetBSD: trap.c,v 1.32 2020/07/26 07:25:38 ryo Exp $");
 
 #include "opt_arm_intr_impl.h"
 #include "opt_compat_netbsd32.h"
@@ -374,8 +374,10 @@ emul_aarch64_insn(struct trapframe *tf)
 {
 	uint32_t insn;
 
-	if (ufetch_32((uint32_t *)tf->tf_pc, &insn))
+	if (ufetch_32((uint32_t *)tf->tf_pc, &insn)) {
+		tf->tf_far = reg_far_el1_read();
 		return EMUL_ARM_FAULT;
+	}
 
 	if ((insn & 0xffffffe0) == 0xd53b0020) {
 		/* mrs x?,ctr_el0 */
@@ -457,7 +459,7 @@ trap_el0_sync(struct trapframe *tf)
 			goto unknown;
 		case EMUL_ARM_FAULT:
 			do_trapsignal(l, SIGSEGV, SEGV_MAPERR,
-			    (void *)tf->tf_pc, esr);
+			    (void *)tf->tf_far, esr);
 			break;
 		}
 		userret(l);
@@ -550,74 +552,126 @@ fetch_arm_insn(uint64_t pc, uint64_t sps
 	return 4;
 }
 
+static bool
+arm_cond_match(uint32_t insn, uint64_t spsr)
+{
+	bool invert = (insn >> 28) & 1;
+	bool match;
+
+	switch (insn >> 29) {
+	case 0:	/* EQ or NE */
+		match = spsr & SPSR_Z;
+		break;
+	case 1:	/* CS/HI or CC/LO */
+		match = spsr & SPSR_C;
+		break;
+	case 2:	/* MI or PL */
+		match = spsr & SPSR_N;
+		break;
+	case 3:	/* VS or VC */
+		match = spsr & SPSR_V;
+		break;
+	case 4:	/* HI or LS */
+		match = ((spsr & (SPSR_C | SPSR_Z)) == SPSR_C);
+		break;
+	case 5:	/* GE or LT */
+		match = (!(spsr & SPSR_N) == !(spsr & SPSR_V));
+		break;
+	case 6:	/* GT or LE */
+		match = !(spsr & SPSR_Z) &&
+		    (!(spsr & SPSR_N) == !(spsr & SPSR_V));
+		break;
+	case 7:	/* AL */
+		match = true;
+		break;
+	}
+	return (!match != !invert);
+}
+
+static enum emul_arm_result
+emul_thumb_insn(struct trapframe *tf, uint32_t insn, int insn_size)
+{
+	/* T32-16bit or 32bit instructions */
+	switch (insn_size) {
+	case 2:
+		/* Breakpoint used by GDB */
+		if (insn == 0xdefe) {
+			do_trapsignal(curlwp, SIGTRAP, TRAP_BRKPT,
+			    (void *)tf->tf_pc, 0);
+			return EMUL_ARM_SUCCESS;
+		}
+		/* XXX: some T32 IT instruction deprecated should be emulated */
+		break;
+	case 4:
+		break;
+	default:
+		return EMUL_ARM_FAULT;
+	}
+	return EMUL_ARM_UNKNOWN;
+}
+
 static enum emul_arm_result
 emul_arm_insn(struct trapframe *tf)
 {
-	struct lwp * const l = curlwp;
 	uint32_t insn;
 	int insn_size;
 
 	insn_size = fetch_arm_insn(tf->tf_pc, tf->tf_spsr, &insn);
+	tf->tf_far = reg_far_el1_read();
 
-	switch (insn_size) {
-	case 2:
-		/* T32-16bit instruction */
+	if (tf->tf_spsr & SPSR_A32_T)
+		return emul_thumb_insn(tf, insn, insn_size);
+	if (insn_size != 4)
+		return EMUL_ARM_FAULT;
 
-		/*
-		 * Breakpoint used by GDB.
-		 */
-		if (insn == 0xdefe)
-			goto trap;
+	/* Breakpoint used by GDB */
+	if (insn == 0xe6000011 || insn == 0xe7ffdefe) {
+		do_trapsignal(curlwp, SIGTRAP, TRAP_BRKPT,
+		    (void *)tf->tf_pc, 0);
+		return EMUL_ARM_SUCCESS;
+	}
 
-		/* XXX: some T32 IT instruction deprecated should be emulated */
-		break;
-	case 4:
-		/* T32-32bit instruction, or A32 instruction */
+	/* Unconditional instruction extension space? */
+	if ((insn & 0xf0000000) == 0xf0000000)
+		goto unknown_insn;
 
-		/*
-		 * Breakpoint used by GDB.
-		 */
-		if (insn == 0xe6000011 || insn == 0xe7ffdefe) {
- trap:
-			do_trapsignal(l, SIGTRAP, TRAP_BRKPT,
-			    (void *)tf->tf_pc, 0);
-			return 0;
-		}
-
-		/*
-		 * Emulate ARMv6 instructions with cache operations
-		 * register (c7), that can be used in user mode.
-		 */
-		switch (insn & 0x0fff0fff) {
-		case 0x0e070f95:
+	/*
+	 * Emulate ARMv6 instructions with cache operations
+	 * register (c7), that can be used in user mode.
+	 */
+	switch (insn & 0x0fff0fff) {
+	case 0x0e070f95:
+		if (arm_cond_match(insn, tf->tf_spsr)) {
 			/*
 			 * mcr p15, 0, <Rd>, c7, c5, 4
 			 * (flush prefetch buffer)
 			 */
 			__asm __volatile("isb sy" ::: "memory");
-			goto emulated;
-		case 0x0e070f9a:
+		}
+		goto emulated;
+	case 0x0e070f9a:
+		if (arm_cond_match(insn, tf->tf_spsr)) {
 			/*
 			 * mcr p15, 0, <Rd>, c7, c10, 4
 			 * (data synchronization barrier)
 			 */
 			__asm __volatile("dsb sy" ::: "memory");
-			goto emulated;
-		case 0x0e070fba:
+		}
+		goto emulated;
+	case 0x0e070fba:
+		if (arm_cond_match(insn, tf->tf_spsr)) {
 			/*
 			 * mcr p15, 0, <Rd>, c7, c10, 5
 			 * (data memory barrier)
 			 */
 			__asm __volatile("dmb sy" ::: "memory");
-			goto emulated;
-		default:
-			break;
 		}
-		break;
+		goto emulated;
 	default:
-		return EMUL_ARM_FAULT;
+		break;
 	}
 
+ unknown_insn:
 	/* unknown, or unsupported instruction */
 	return EMUL_ARM_UNKNOWN;
 
@@ -685,7 +739,7 @@ trap_el0_32sync(struct trapframe *tf)
 			goto unknown;
 		case EMUL_ARM_FAULT:
 			do_trapsignal(l, SIGSEGV, SEGV_MAPERR,
-			    (void *)tf->tf_pc, esr);
+			    (void *)tf->tf_far, esr);
 			break;
 		}
 		userret(l);

Reply via email to