[PATCH 4/7] MIPS: math-emu: Correct the emulation of microMIPS ADDIUPC instruction

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Emulate the microMIPS ADDIUPC instruction directly in `mips_dsemul'.  If 
executed in the emulation frame, this instruction produces an incorrect
result, because the value of the PC there is not the same as where the
instruction originated.

Reshape code so as to handle all microMIPS cases together.

Signed-off-by: Maciej W. Rozycki <macro@xxxxxxxxxx>
---
Ralf,

 Please apply and backport to stable branches.  This is the program I used 
to validate this change:

#include <stdio.h>

struct pc {
	void *pc0;
	void *pc1;
};

static struct pc addiupc(double x, double y, int i, int unaligned)
{
	struct pc pc;

	asm(	
	"	.set	push		\n"
	"	.set	noreorder	\n"
	"	c.eq.d	%2, %3		\n"
	"	.align	2		\n"
	"	.rept	%5		\n"
	"	nop16			\n"
	"	.endr			\n"
	"	bc1t	1f		\n"
	"0:	 .fill	0		\n"
	"	 addiu	%0, $pc, %4	\n"
	"1:	la	%1, 0b		\n"
	"	addu	%1, %1, %z4	\n"
	"	.set	pop		"
		: "=&u" (pc.pc0), "=&r" (pc.pc1)
		: "f" (x), "f" (y), "i" (i), "i" (unaligned));

	return pc;
}

int main(int argc, char **argv)
{
	struct pc pc;

	pc = addiupc(1.0, 2.0, 0, 0);
	printf("al ne 0: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(0.0, 0.0, 0, 0);
	printf("al eq 0: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(1.0, 2.0, 0, 1);
	printf("un ne 0: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(0.0, 0.0, 0, 1);
	printf("un eq 0: %p, %p\n", pc.pc0, pc.pc1);

	pc = addiupc(1.0, 2.0, 16, 0);
	printf("al ne 16: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(0.0, 0.0, 16, 0);
	printf("al eq 16: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(1.0, 2.0, 16, 1);
	printf("un ne 16: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(0.0, 0.0, 16, 1);
	printf("un eq 16: %p, %p\n", pc.pc0, pc.pc1);

	pc = addiupc(1.0, 2.0, -16, 0);
	printf("al ne -16: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(0.0, 0.0, -16, 0);
	printf("al eq -16: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(1.0, 2.0, -16, 1);
	printf("un ne -16: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(0.0, 0.0, -16, 1);
	printf("un eq -16: %p, %p\n", pc.pc0, pc.pc1);

	pc = addiupc(1.0, 2.0, 16777212, 0);
	printf("al ne 16777212: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(0.0, 0.0, 16777212, 0);
	printf("al eq 16777212: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(1.0, 2.0, 16777212, 1);
	printf("un ne 16777212: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(0.0, 0.0, 16777212, 1);
	printf("un eq 16777212: %p, %p\n", pc.pc0, pc.pc1);

	pc = addiupc(1.0, 2.0, -16777216, 0);
	printf("al ne -16777216: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(0.0, 0.0, -16777216, 0);
	printf("al eq -16777216: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(1.0, 2.0, -16777216, 1);
	printf("un ne -16777216: %p, %p\n", pc.pc0, pc.pc1);
	pc = addiupc(0.0, 0.0, -16777216, 1);
	printf("un eq -16777216: %p, %p\n", pc.pc0, pc.pc1);

	return 0;
}

With the current state of our tree, where FPU emulation is involved this 
program, when compiled to microMIPS code, produces the following output 
(on a DSP-enabled processor):

al ne 0: 0x40052c, 0x40052c
Illegal instruction

(because of the issue fixed with 3/7) with 3/7 applied it prints:

al ne 0: 0x40052c, 0x40052c
al eq 0: 0x7fe144f8, 0x400550
un ne 0: 0x400574, 0x400576
un eq 0: 0x7fe144f8, 0x40059a
al ne 16: 0x4005cc, 0x4005cc
al eq 16: 0x7fe14508, 0x4005f0
un ne 16: 0x400614, 0x400616
un eq 16: 0x7fe14508, 0x40063e
al ne -16: 0x400644, 0x400644
al eq -16: 0x7fe144e8, 0x400668
un ne -16: 0x40068c, 0x40068e
un eq -16: 0x7fe144e8, 0x4006b6
al ne 16777212: 0x14006e8, 0x14006e8
al eq 16777212: 0x80e144f4, 0x1400714
un ne 16777212: 0x1400740, 0x1400742
un eq 16777212: 0x80e144f4, 0x1400772
al ne -16777216: 0xff4007a4, 0xff4007a4
al eq -16777216: 0x7ee144f8, 0xff4007cc
un ne -16777216: 0xff4007f4, 0xff4007f6
un eq -16777216: 0x7ee144f8, 0xff400822

(notice the addresses offsetted from the emulation frame in the "eq" case, 
printed on the left) whereas with the change applied it prints:

al ne 0: 0x40052c, 0x40052c
al eq 0: 0x400550, 0x400550
un ne 0: 0x400574, 0x400576
un eq 0: 0x400598, 0x40059a
al ne 16: 0x4005cc, 0x4005cc
al eq 16: 0x4005f0, 0x4005f0
un ne 16: 0x400614, 0x400616
un eq 16: 0x40063c, 0x40063e
al ne -16: 0x400644, 0x400644
al eq -16: 0x400668, 0x400668
un ne -16: 0x40068c, 0x40068e
un eq -16: 0x4006b4, 0x4006b6
al ne 16777212: 0x14006e8, 0x14006e8
al eq 16777212: 0x1400714, 0x1400714
un ne 16777212: 0x1400740, 0x1400742
un eq 16777212: 0x1400770, 0x1400772
al ne -16777216: 0xff4007a4, 0xff4007a4
al eq -16777216: 0xff4007cc, 0xff4007cc
un ne -16777216: 0xff4007f4, 0xff4007f6
un eq -16777216: 0xff400820, 0xff400822

instead, as expected (notice the 4-byte alignment of the results, absent 
from the reference values calculated with the LA/ADDU sequence, printed on 
the right).

 Further changes will be required to correctly handle PC-relative MIPSr6 
instructions.  I do not intend to make such changes anytime soon, people 
are encouraged to make them themselves if interested in MIPSr6 execution 
environments.

 If adding further cases to `mips_dsemul' I suggest factoring out code to 
handle direct emulation (-1 return code).

  Maciej

linux-umips-dsemul-addiupc.diff
Index: linux-sfr-sead/arch/mips/include/uapi/asm/inst.h
===================================================================
--- linux-sfr-sead.orig/arch/mips/include/uapi/asm/inst.h	2016-01-22 01:07:02.925179000 +0000
+++ linux-sfr-sead/arch/mips/include/uapi/asm/inst.h	2016-01-22 01:10:10.472810000 +0000
@@ -799,6 +799,13 @@ struct mm_x_format {		/* Scaled indexed 
 	;)))))
 };
 
+struct mm_a_format {		/* ADDIUPC format (microMIPS) */
+	__BITFIELD_FIELD(unsigned int opcode : 6,
+	__BITFIELD_FIELD(unsigned int rs : 3,
+	__BITFIELD_FIELD(signed int simmediate : 23,
+	;)))
+};
+
 /*
  * microMIPS instruction formats (16-bit length)
  */
@@ -940,6 +947,7 @@ union mips_instruction {
 	struct mm_i_format mm_i_format;
 	struct mm_m_format mm_m_format;
 	struct mm_x_format mm_x_format;
+	struct mm_a_format mm_a_format;
 	struct mm_b0_format mm_b0_format;
 	struct mm_b1_format mm_b1_format;
 	struct mm16_m_format mm16_m_format ;
Index: linux-sfr-sead/arch/mips/math-emu/dsemul.c
===================================================================
--- linux-sfr-sead.orig/arch/mips/math-emu/dsemul.c	2016-01-22 01:09:07.914682000 +0000
+++ linux-sfr-sead/arch/mips/math-emu/dsemul.c	2016-01-22 01:10:10.476810000 +0000
@@ -43,10 +43,30 @@ int mips_dsemul(struct pt_regs *regs, mi
 	int err;
 
 	/* NOP is easy */
-	if ((get_isa16_mode(regs->cp0_epc) && ((ir >> 16) == MM_NOP16)) ||
-	    (ir == 0))
+	if (ir == 0)
 		return -1;
 
+	/* microMIPS instructions */
+	if (get_isa16_mode(regs->cp0_epc)) {
+		union mips_instruction insn = { .word = ir };
+
+		/* NOP16 aka MOVE16 $0, $0 */
+		if ((ir >> 16) == MM_NOP16)
+			return -1;
+
+		/* ADDIUPC */
+		if (insn.mm_a_format.opcode == mm_addiupc_op) {
+			unsigned int rs;
+			s32 v;
+
+			rs = (((insn.mm_a_format.rs + 0x1e) & 0xf) + 2);
+			v = regs->cp0_epc & ~3;
+			v += insn.mm_a_format.simmediate << 2;
+			regs->regs[rs] = (long)v;
+			return -1;
+		}
+	}
+
 	pr_debug("dsemul %lx %lx\n", regs->cp0_epc, cpc);
 
 	/*




[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux