linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] MIPS: R6: emulation of PC-relative instructions
@ 2015-08-05 23:53 Leonid Yegoshin
  2015-08-11 14:41 ` Markos Chandras
  0 siblings, 1 reply; 3+ messages in thread
From: Leonid Yegoshin @ 2015-08-05 23:53 UTC (permalink / raw)
  To: linux-mips, paul.burton, david.daney, Steven.Hill, linux-kernel,
	ralf, markos.chandras, macro

MIPS R6 has 6 new PC-relative instructions: LWUPC, LWPC, LDPC, ADDIUPC, ALUIPC
and AUIPC. These instructions can be placed in BD-slot of BC1* branch
instruction and FPU may be not available, which requires emulation of these
instructions.

However, the traditional way to emulate that is via filling some emulation block
in stack or special area and jump to it. This is not suitable for PC-relative
instructions.

So, this patch introduces a universal emulation of that instructions directly by
kernel emulator.

Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
---
 arch/mips/include/uapi/asm/inst.h     |   42 ++++++++++++++-
 arch/mips/kernel/mips-r2-to-r6-emul.c |    3 +
 arch/mips/math-emu/dsemul.c           |   94 +++++++++++++++++++++++++++++++++
 3 files changed, 138 insertions(+), 1 deletion(-)

diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h
index 3dce80e67948..6253197d4908 100644
--- a/arch/mips/include/uapi/asm/inst.h
+++ b/arch/mips/include/uapi/asm/inst.h
@@ -33,7 +33,7 @@ enum major_op {
 	sdl_op, sdr_op, swr_op, cache_op,
 	ll_op, lwc1_op, lwc2_op, bc6_op = lwc2_op, pref_op,
 	lld_op, ldc1_op, ldc2_op, beqzcjic_op = ldc2_op, ld_op,
-	sc_op, swc1_op, swc2_op, balc6_op = swc2_op, major_3b_op,
+	sc_op, swc1_op, swc2_op, balc6_op = swc2_op, pcrel_op,
 	scd_op, sdc1_op, sdc2_op, bnezcjialc_op = sdc2_op, sd_op
 };
 
@@ -238,6 +238,17 @@ enum msa_2b_fmt {
 	msa_fmt_d = 3,
 };
 
+enum relpc_op {
+	addiupc_op  = 0,
+	lwpc_op     = 1,
+	lwupc_op    = 2,
+};
+
+enum relpc_func {
+	auipc_func  = 6,
+	aluipc_func = 7,
+};
+
 /*
  * (microMIPS) Major opcodes.
  */
@@ -648,6 +659,32 @@ struct spec3_format {   /* SPEC3 */
 	;)))))
 };
 
+struct rel_format {        /* PC-relative */
+	__BITFIELD_FIELD(unsigned int opcode : 6,
+	__BITFIELD_FIELD(unsigned int rs : 5,
+	__BITFIELD_FIELD(unsigned int op : 2,
+	__BITFIELD_FIELD(signed int simmediate : 19,
+	;))))
+};
+
+struct rl16_format {        /* PC-relative, 16bit offset */
+	__BITFIELD_FIELD(unsigned int opcode : 6,
+	__BITFIELD_FIELD(unsigned int rs : 5,
+	__BITFIELD_FIELD(unsigned int op : 2,
+	__BITFIELD_FIELD(unsigned int func : 3,
+	__BITFIELD_FIELD(signed int simmediate : 16,
+	;)))))
+};
+
+struct rl18_format {        /* PC-relative, 18bit offset */
+	__BITFIELD_FIELD(unsigned int opcode : 6,
+	__BITFIELD_FIELD(unsigned int rs : 5,
+	__BITFIELD_FIELD(unsigned int op : 2,
+	__BITFIELD_FIELD(unsigned int unused : 1,
+	__BITFIELD_FIELD(signed int simmediate : 18,
+	;)))))
+};
+
 /*
  * microMIPS instruction formats (32-bit length)
  *
@@ -917,6 +954,9 @@ union mips_instruction {
 	struct f_format f_format;
 	struct ma_format ma_format;
 	struct msa_mi10_format msa_mi10_format;
+	struct rel_format rel_format;
+	struct rl16_format rl16_format;
+	struct rl18_format rl18_format;
 	struct b_format b_format;
 	struct ps_format ps_format;
 	struct v_format v_format;
diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c
index 040842a3aec4..c2e132f8e391 100644
--- a/arch/mips/kernel/mips-r2-to-r6-emul.c
+++ b/arch/mips/kernel/mips-r2-to-r6-emul.c
@@ -1033,6 +1033,7 @@ repeat:
 			if (nir) {
 				err = mipsr6_emul(regs, nir);
 				if (err > 0) {
+					regs->cp0_epc = nepc;
 					err = mips_dsemul(regs, nir, cpc, epc, r31);
 					if (err == SIGILL)
 						err = SIGEMT;
@@ -1082,6 +1083,7 @@ repeat:
 			if (nir) {
 				err = mipsr6_emul(regs, nir);
 				if (err > 0) {
+					regs->cp0_epc = nepc;
 					err = mips_dsemul(regs, nir, cpc, epc, r31);
 					if (err == SIGILL)
 						err = SIGEMT;
@@ -1149,6 +1151,7 @@ repeat:
 		if (nir) {
 			err = mipsr6_emul(regs, nir);
 			if (err > 0) {
+				regs->cp0_epc = nepc;
 				err = mips_dsemul(regs, nir, cpc, epc, r31);
 				if (err == SIGILL)
 					err = SIGEMT;
diff --git a/arch/mips/math-emu/dsemul.c b/arch/mips/math-emu/dsemul.c
index eac76a09d822..9b388aaf594f 100644
--- a/arch/mips/math-emu/dsemul.c
+++ b/arch/mips/math-emu/dsemul.c
@@ -8,6 +8,95 @@
 
 #include "ieee754.h"
 
+#ifdef CONFIG_CPU_MIPSR6
+
+static int mipsr6_pc(struct pt_regs *regs, mips_instruction inst, unsigned long cpc,
+		    unsigned long bpc, unsigned long r31)
+{
+	union mips_instruction ir = (union mips_instruction)inst;
+	register unsigned long vaddr;
+	unsigned int val;
+	int err = SIGILL;
+
+	if (ir.rel_format.opcode != pcrel_op)
+		return SIGILL;
+
+	switch (ir.rel_format.op) {
+	case addiupc_op:
+		vaddr = regs->cp0_epc + (ir.rel_format.simmediate << 2);
+		if (config_enabled(CONFIG_64BIT) && !(regs->cp0_status & ST0_UX))
+			__asm__ __volatile__("sll %0, %0, 0":"+&r"(vaddr)::);
+		regs->regs[ir.rel_format.rs] = vaddr;
+		return 0;
+#ifdef CONFIG_CPU_MIPS64
+	case lwupc_op:
+		vaddr = regs->cp0_epc + (ir.rel_format.simmediate << 2);
+		if (config_enabled(CONFIG_64BIT) && !(regs->cp0_status & ST0_UX))
+			__asm__ __volatile__("sll %0, %0, 0":"+&r"(vaddr)::);
+		if (get_user(val, (u32 __user *)vaddr)) {
+			current->thread.cp0_baduaddr = vaddr;
+			err = SIGSEGV;
+			break;
+		}
+		regs->regs[ir.rel_format.rs] = val;
+		return 0;
+#endif
+	case lwpc_op:
+		vaddr = regs->cp0_epc + (ir.rel_format.simmediate << 2);
+		if (config_enabled(CONFIG_64BIT) && !(regs->cp0_status & ST0_UX))
+			__asm__ __volatile__("sll %0, %0, 0":"+&r"(vaddr)::);
+		if (get_user(val, (u32 __user *)vaddr)) {
+			current->thread.cp0_baduaddr = vaddr;
+			err = SIGSEGV;
+			break;
+		}
+		regs->regs[ir.rel_format.rs] = (int)val;
+		return 0;
+	default:
+		switch (ir.rl16_format.func) {
+		case aluipc_func:
+			vaddr = regs->cp0_epc + (ir.rl16_format.simmediate << 16);
+			if (config_enabled(CONFIG_64BIT) &&
+			    !(regs->cp0_status & ST0_UX))
+				__asm__ __volatile__("sll %0, %0, 0":"+&r"(vaddr)::);
+			vaddr &= ~0xffff;
+			regs->regs[ir.rel_format.rs] = vaddr;
+			return 0;
+		case auipc_func:
+			vaddr = regs->cp0_epc + (ir.rl16_format.simmediate << 16);
+			if (config_enabled(CONFIG_64BIT) &&
+			    !(regs->cp0_status & ST0_UX))
+				__asm__ __volatile__("sll %0, %0, 0":"+&r"(vaddr)::);
+			regs->regs[ir.rel_format.rs] = vaddr;
+			return 0;
+		default: {
+#ifdef CONFIG_CPU_MIPS64
+				unsigned long dval;
+
+				if (ir.rl18_format.unused)
+					break;
+				/* LDPC */
+				vaddr = regs->cp0_epc & ~0x7;
+				vaddr += (ir.rl18_format.simmediate << 3);
+				if (config_enabled(CONFIG_64BIT) &&
+				    !(regs->cp0_status & ST0_UX))
+					__asm__ __volatile__("sll %0, %0, 0":"+&r"(vaddr)::);
+				if (get_user(dval, (u64 __user *)vaddr)) {
+					current->thread.cp0_baduaddr = vaddr;
+					err = SIGSEGV;
+					break;
+				}
+				regs->regs[ir.rel_format.rs] = dval;
+				return 0;
+#endif
+			}
+		}
+		break;
+	}
+	return err;
+}
+#endif
+
 /*
  * Emulate the arbritrary instruction ir at xcp->cp0_epc.  Required when
  * we have to emulate the instruction in a COP1 branch delay slot.  Do
@@ -54,6 +143,11 @@ int mips_dsemul(struct pt_regs *regs, mips_instruction ir, unsigned long cpc,
 
 	pr_debug("dsemul %lx %lx\n", regs->cp0_epc, cpc);
 
+#ifdef CONFIG_CPU_MIPSR6
+	err = mipsr6_pc(regs, ir, cpc, bpc, r31);
+	if (err != SIGILL)
+		return err;
+#endif
 	/*
 	 * The strategy is to push the instruction onto the user stack/VDSO page
 	 * and put a trap after it which we can catch and jump to


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] MIPS: R6: emulation of PC-relative instructions
  2015-08-05 23:53 [PATCH] MIPS: R6: emulation of PC-relative instructions Leonid Yegoshin
@ 2015-08-11 14:41 ` Markos Chandras
  2015-08-11 18:24   ` Leonid Yegoshin
  0 siblings, 1 reply; 3+ messages in thread
From: Markos Chandras @ 2015-08-11 14:41 UTC (permalink / raw)
  To: Leonid Yegoshin
  Cc: linux-mips, paul.burton, david.daney, Steven.Hill, linux-kernel,
	ralf, macro

Hi,

On Wed, Aug 05, 2015 at 04:53:43PM -0700, Leonid Yegoshin wrote:
> MIPS R6 has 6 new PC-relative instructions: LWUPC, LWPC, LDPC, ADDIUPC, ALUIPC
> and AUIPC. These instructions can be placed in BD-slot of BC1* branch
> instruction and FPU may be not available, which requires emulation of these
> instructions.
> 
> However, the traditional way to emulate that is via filling some emulation block
> in stack or special area and jump to it. This is not suitable for PC-relative
> instructions.
> 
> So, this patch introduces a universal emulation of that instructions directly by
> kernel emulator.
> 
> Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
> ---
>  arch/mips/include/uapi/asm/inst.h     |   42 ++++++++++++++-
>  arch/mips/kernel/mips-r2-to-r6-emul.c |    3 +
>  arch/mips/math-emu/dsemul.c           |   94 +++++++++++++++++++++++++++++++++
>  3 files changed, 138 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/mips/include/uapi/asm/inst.h b/arch/mips/include/uapi/asm/inst.h
> index 3dce80e67948..6253197d4908 100644
> --- a/arch/mips/include/uapi/asm/inst.h
> +++ b/arch/mips/include/uapi/asm/inst.h
> @@ -33,7 +33,7 @@ enum major_op {
>  	sdl_op, sdr_op, swr_op, cache_op,
>  	ll_op, lwc1_op, lwc2_op, bc6_op = lwc2_op, pref_op,
>  	lld_op, ldc1_op, ldc2_op, beqzcjic_op = ldc2_op, ld_op,
> -	sc_op, swc1_op, swc2_op, balc6_op = swc2_op, major_3b_op,
> +	sc_op, swc1_op, swc2_op, balc6_op = swc2_op, pcrel_op,
>  	scd_op, sdc1_op, sdc2_op, bnezcjialc_op = sdc2_op, sd_op
>  };
>  
>  			if (nir) {
>  				err = mipsr6_emul(regs, nir);
>  				if (err > 0) {
> +					regs->cp0_epc = nepc;

Does this change belog to this patch? If so why? Maybe a comment would help?
It does feel like it fixes a different problem but I haven't read your patch in depth.

>  					err = mips_dsemul(regs, nir, cpc, epc, r31);
>  					if (err == SIGILL)
>  						err = SIGEMT;
> @@ -1082,6 +1083,7 @@ repeat:
>  			if (nir) {
>  				err = mipsr6_emul(regs, nir);
>  				if (err > 0) {
> +					regs->cp0_epc = nepc;
likewise

>  					err = mips_dsemul(regs, nir, cpc, epc, r31);
>  					if (err == SIGILL)
>  						err = SIGEMT;
> @@ -1149,6 +1151,7 @@ repeat:
>  		if (nir) {
>  			err = mipsr6_emul(regs, nir);
>  			if (err > 0) {
> +				regs->cp0_epc = nepc;
likewise

>  				err = mips_dsemul(regs, nir, cpc, epc, r31);
>  				if (err == SIGILL)
>  					err = SIGEMT;
> diff --git a/arch/mips/math-emu/dsemul.c b/arch/mips/math-emu/dsemul.c
> index eac76a09d822..9b388aaf594f 100644
> --- a/arch/mips/math-emu/dsemul.c
> +++ b/arch/mips/math-emu/dsemul.c
> @@ -8,6 +8,95 @@
>  
>  #include "ieee754.h"
>  
> +#ifdef CONFIG_CPU_MIPSR6

Can we simply avoid the if/def for R6 please? Just leave this function as is and
use if(cpu_has_mips_r6) when calling it. If you can't do that, please explain
why.

> +
> +static int mipsr6_pc(struct pt_regs *regs, mips_instruction inst, unsigned long cpc,
> +		    unsigned long bpc, unsigned long r31)
> +{
> +	union mips_instruction ir = (union mips_instruction)inst;
> +	register unsigned long vaddr;
> +	unsigned int val;
> +	int err = SIGILL;
> +
> +	if (ir.rel_format.opcode != pcrel_op)
> +		return SIGILL;
> +
> +	switch (ir.rel_format.op) {
> +	case addiupc_op:
> +		vaddr = regs->cp0_epc + (ir.rel_format.simmediate << 2);
> +		if (config_enabled(CONFIG_64BIT) && !(regs->cp0_status & ST0_UX))
> +			__asm__ __volatile__("sll %0, %0, 0":"+&r"(vaddr)::);
> +		regs->regs[ir.rel_format.rs] = vaddr;
> +		return 0;
> +#ifdef CONFIG_CPU_MIPS64

Could you use cpu_has_mips64 and avoid the if/def and return SIGILL if it is not
true?

Same thing for the rest of this patch.

-- 
markos

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] MIPS: R6: emulation of PC-relative instructions
  2015-08-11 14:41 ` Markos Chandras
@ 2015-08-11 18:24   ` Leonid Yegoshin
  0 siblings, 0 replies; 3+ messages in thread
From: Leonid Yegoshin @ 2015-08-11 18:24 UTC (permalink / raw)
  To: Markos Chandras
  Cc: linux-mips, paul.burton, david.daney, Steven.Hill, linux-kernel,
	ralf, macro

On 08/11/2015 07:41 AM, Markos Chandras wrote:
> Hi,
>
> On Wed, Aug 05, 2015 at 04:53:43PM -0700, Leonid Yegoshin wrote:
>>   			if (nir) {
>>   				err = mipsr6_emul(regs, nir);
>>   				if (err > 0) {
>> +					regs->cp0_epc = nepc;
> Does this change belog to this patch? If so why?

Yes, it is needed for correct address calculation. Until PC-relative 
instruction emulation it was not needed in dsemul().

>   Maybe a comment would help?
> It does feel like it fixes a different problem but I haven't read your patch in depth.
>
>>   
>>
>>   
>>   #include "ieee754.h"
>>   
>> +#ifdef CONFIG_CPU_MIPSR6
> Can we simply avoid the if/def for R6 please? Just leave this function as is and
> use if(cpu_has_mips_r6) when calling it. If you can't do that, please explain
> why.
Yes, we can. But we have a bunch of that in many places and somewhere it 
is difficult to avoid "#ifdef".
I just like to have an uniform standard.

Besides that "#ifdef" assures quickly that it is a build time choice but 
not runtime.

>> +
>> +static int mipsr6_pc(struct pt_regs *regs, mips_instruction inst, unsigned long cpc,
>> +		    unsigned long bpc, unsigned long r31)
>> +{
>> +	union mips_instruction ir = (union mips_instruction)inst;
>> +	register unsigned long vaddr;
>> +	unsigned int val;
>> +	int err = SIGILL;
>> +
>> +	if (ir.rel_format.opcode != pcrel_op)
>> +		return SIGILL;
>> +
>> +	switch (ir.rel_format.op) {
>> +	case addiupc_op:
>> +		vaddr = regs->cp0_epc + (ir.rel_format.simmediate << 2);
>> +		if (config_enabled(CONFIG_64BIT) && !(regs->cp0_status & ST0_UX))
>> +			__asm__ __volatile__("sll %0, %0, 0":"+&r"(vaddr)::);
>> +		regs->regs[ir.rel_format.rs] = vaddr;
>> +		return 0;
>> +#ifdef CONFIG_CPU_MIPS64
> Could you use cpu_has_mips64 and avoid the if/def

No we can't - cpu_has_mips64 is a CPU ISA feature but here is a kernel 
code capability restriction. The difference happens during running 
MIPS32 kernel on MIPS64 processor. We should not emulate MIPS64 
instructions on MIPS32 kernel.

>   and return SIGILL if it is not
> true?

The common return standard for emulation functions in MIPS is:

     0 - OK, emulation done
     SIGILL - doesn't recognize an instruction, it still may be some 
another way to fix a problem or SIGILL.
     other - some trouble or whatever (non-standardized, in MIPS R2 
emulator it has subcodes)

I don't see a reason to change it and have here a special standard.

- Leonid.

>
>


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2015-08-11 18:24 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-05 23:53 [PATCH] MIPS: R6: emulation of PC-relative instructions Leonid Yegoshin
2015-08-11 14:41 ` Markos Chandras
2015-08-11 18:24   ` Leonid Yegoshin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).