diff mbox series

[002/120] MIPS: R5900: Trap the RDHWR instruction as an SQ address exception

Message ID 4f856a5ea2c039c6639df875d11b5bff1bf7ecd2.1567326213.git.noring@nocrew.org (mailing list archive)
State RFC
Headers show
Series Linux for the PlayStation 2 | expand

Commit Message

Fredrik Noring Sept. 1, 2019, 3:36 p.m. UTC
On the R5900, the RDHWR instruction is interpreted as the R5900 specific
SQ instruction[1] that traps into a zero page address exception. Hence
RDHWR can be emulated by emulate_load_store_insn().

CONFIG_DEFAULT_MMAP_MIN_ADDR must not be less than PAGE_SIZE to reliably
trap and emulate RDHWR, so this is made a BUILD_BUG_ON for the R5900.

References:

[1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,
    Toshiba Corporation, p. B-162, https://wiki.qemu.org/File:C790.pdf

Signed-off-by: Fredrik Noring <noring@nocrew.org>
---
 arch/mips/include/asm/traps.h |  2 ++
 arch/mips/kernel/traps.c      |  2 +-
 arch/mips/kernel/unaligned.c  | 36 ++++++++++++++++++++++++++++++++++-
 3 files changed, 38 insertions(+), 2 deletions(-)

Comments

Maciej W. Rozycki Sept. 1, 2019, 10 p.m. UTC | #1
On Sun, 1 Sep 2019, Fredrik Noring wrote:

> CONFIG_DEFAULT_MMAP_MIN_ADDR must not be less than PAGE_SIZE to reliably
> trap and emulate RDHWR, so this is made a BUILD_BUG_ON for the R5900.

 I think a more complex solution is required as the value can be changed 
at run time, via /proc/sys/vm/mmap_min_addr, defeating this protection.  
E.g. by introducing an ARCH_MIN_MMAP_MIN_ADDR minimum value, by default 0 
unless overridden by the architecture selected, and then using it for both 
the default DEFAULT_MMAP_MIN_ADDR value and the minimum accepted via 
/proc/sys/vm/mmap_min_addr.

> diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
> index 92bd2b0f0548..f490944d73cf 100644
> --- a/arch/mips/kernel/unaligned.c
> +++ b/arch/mips/kernel/unaligned.c
> @@ -1342,6 +1375,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
>  		cu2_notifier_call_chain(CU2_SDC2_OP, regs);
>  		break;
>  #endif
> +
>  	default:
>  		/*
>  		 * Pheeee...  We encountered an yet unknown instruction or

 Extraneous change.

  Maciej
Philippe Mathieu-Daudé Nov. 19, 2020, 7:15 a.m. UTC | #2
Hi Fredrik,

Cc'ing glibc folks in case...

On 9/1/19 5:36 PM, Fredrik Noring wrote:
> On the R5900, the RDHWR instruction is interpreted as the R5900 specific
> SQ instruction[1] that traps into a zero page address exception. Hence
> RDHWR can be emulated by emulate_load_store_insn().

Please bare with me because this is not my area, but this does
not look the correct way to fix this problem to me.

The R5900 is a MIPS-III, so I don't understand why we have to care
about RDHWR.

IIRC MIPS TLS support has been added in glibc with MIPS32 ISA in
mind in 2005:
https://sourceware.org/git/?p=glibc.git;a=commit;h=f850220be6

MIPS16 is handled differently:
https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=ports/sysdeps/mips/tls-macros.h;h=3e87e42ea;hp=8fe2e4a150d;hb=43301bd3c2;hpb=85bd816a60

Shouldn't we have a similar set of macros for the R5900?

The problem I have is this break running the binaries built
by the Sony Linux Toolkit for Playstation 2 which use the
LQ/SQ instruction:

 1b00d5c:       0c6c00d4        jal     0x1b00350
 1b00d60:       7fa90020        sq      t1,32(sp)
 1b00d64:       a2710000        sb      s1,0(s3)
 1b00d68:       26730001        addiu   s3,s3,1
 1b00d6c:       10000004        b       0x1b00d80
 1b00d70:       7ba90020        lq      t1,32(sp)
 1b00d74:       00000000        nop
 1b00d78:       2617ffff        addiu   s7,s0,-1
 1b00d7c:       25360001        addiu   s6,t1,1
 1b00d80:       91220000        lbu     v0,0(t1)
 1b00d84:       02e0802d        move    s0,s7
 1b00d88:       02c0482d        move    t1,s6
 1b00d8c:       a2620000        sb      v0,0(s3)
 1b00d90:       1600fff9        bnez    s0,0x1b00d78
 1b00d94:       26730001        addiu   s3,s3,1
 1b00d98:       10000003        b       0x1b00da8
 1b00d9c:       01342026        xor     a0,t1,s4
 1b00da0:       253e0003        addiu   s8,t1,3
 1b00da4:       01342026        xor     a0,t1,s4
 1b00da8:       7fa90020        sq      t1,32(sp)
 1b00dac:       2c840001        sltiu   a0,a0,1
 1b00db0:       0c6c00d4        jal     0x1b00350

What do you think?

Regards,

Phil.

> CONFIG_DEFAULT_MMAP_MIN_ADDR must not be less than PAGE_SIZE to reliably
> trap and emulate RDHWR, so this is made a BUILD_BUG_ON for the R5900.
> 
> References:
> 
> [1] "TX System RISC TX79 Core Architecture" manual, revision 2.0,
>     Toshiba Corporation, p. B-162, https://wiki.qemu.org/File:C790.pdf
> 
> Signed-off-by: Fredrik Noring <noring@nocrew.org>
> ---
>  arch/mips/include/asm/traps.h |  2 ++
>  arch/mips/kernel/traps.c      |  2 +-
>  arch/mips/kernel/unaligned.c  | 36 ++++++++++++++++++++++++++++++++++-
>  3 files changed, 38 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/mips/include/asm/traps.h b/arch/mips/include/asm/traps.h
> index 6a0864bb604d..ed6449f2967b 100644
> --- a/arch/mips/include/asm/traps.h
> +++ b/arch/mips/include/asm/traps.h
> @@ -35,4 +35,6 @@ extern int register_nmi_notifier(struct notifier_block *nb);
>  	register_nmi_notifier(&fn##_nb);				\
>  })
>  
> +int simulate_rdhwr(struct pt_regs *regs, int rd, int rt);
> +
>  #endif /* _ASM_TRAPS_H */
> diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
> index 342e41de9d64..9423b9a2eb67 100644
> --- a/arch/mips/kernel/traps.c
> +++ b/arch/mips/kernel/traps.c
> @@ -625,7 +625,7 @@ static int simulate_llsc(struct pt_regs *regs, unsigned int opcode)
>   * Simulate trapping 'rdhwr' instructions to provide user accessible
>   * registers not implemented in hardware.
>   */
> -static int simulate_rdhwr(struct pt_regs *regs, int rd, int rt)
> +int simulate_rdhwr(struct pt_regs *regs, int rd, int rt)
>  {
>  	struct thread_info *ti = task_thread_info(current);
>  
> diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
> index 92bd2b0f0548..f490944d73cf 100644
> --- a/arch/mips/kernel/unaligned.c
> +++ b/arch/mips/kernel/unaligned.c
> @@ -90,6 +90,7 @@
>  #include <asm/fpu_emulator.h>
>  #include <asm/inst.h>
>  #include <asm/mmu_context.h>
> +#include <asm/traps.h>
>  #include <linux/uaccess.h>
>  
>  #define STR(x)	__STR(x)
> @@ -934,7 +935,39 @@ static void emulate_load_store_insn(struct pt_regs *regs,
>  		 * interest.
>  		 */
>  	case spec3_op:
> -		if (insn.dsp_format.func == lx_op) {
> +		if (IS_ENABLED(CONFIG_CPU_R5900)) {
> +			/*
> +			 * On the R5900, the RDHWR instruction
> +			 *
> +			 *     +--------+-------+----+----+-------+--------+
> +			 *     | 011111 | 00000 | rt | rd | 00000 | 111011 |
> +			 *     +--------+-------+----+----+-------+--------+
> +			 *          6       5      5    5     5        6
> +			 *
> +			 * is interpreted as the R5900 specific SQ instruction
> +			 *
> +			 *     +--------+-------+----+---------------------+
> +			 *     | 011111 |  base | rt |        offset       |
> +			 *     +--------+-------+----+---------------------+
> +			 *          6       5      5            16
> +			 *
> +			 * that asserts a zero page address exception. Hence
> +			 * RDHWR can be trapped and emulated here, provided
> +			 * DEFAULT_MMAP_MIN_ADDR isn't zero.
> +			 */
> +			BUILD_BUG_ON(IS_ENABLED(CONFIG_CPU_R5900) &&
> +				CONFIG_DEFAULT_MMAP_MIN_ADDR < PAGE_SIZE);
> +			if (insn.r_format.func == rdhwr_op &&
> +			    insn.r_format.rs == 0 &&
> +			    insn.r_format.re == 0) {
> +				if (compute_return_epc(regs) < 0 ||
> +				    simulate_rdhwr(regs, insn.r_format.rd,
> +						   insn.r_format.rt) < 0)
> +					goto sigill;
> +				return;
> +			}
> +			goto sigbus;
> +		} else if (insn.dsp_format.func == lx_op) {
>  			switch (insn.dsp_format.op) {
>  			case lwx_op:
>  				if (!access_ok(addr, 4))
> @@ -1342,6 +1375,7 @@ static void emulate_load_store_insn(struct pt_regs *regs,
>  		cu2_notifier_call_chain(CU2_SDC2_OP, regs);
>  		break;
>  #endif
> +
>  	default:
>  		/*
>  		 * Pheeee...  We encountered an yet unknown instruction or
>
Maciej W. Rozycki Nov. 19, 2020, 1:28 p.m. UTC | #3
On Thu, 19 Nov 2020, Philippe Mathieu-Daudé wrote:

> > On the R5900, the RDHWR instruction is interpreted as the R5900 specific
> > SQ instruction[1] that traps into a zero page address exception. Hence
> > RDHWR can be emulated by emulate_load_store_insn().
> 
> Please bare with me because this is not my area, but this does
> not look the correct way to fix this problem to me.
> 
> The R5900 is a MIPS-III, so I don't understand why we have to care
> about RDHWR.

 The use of RDHWR, actual or emulated, is a part of the MIPS TLS psABI, 
see: <https://www.linux-mips.org/wiki/NPTL>, in particular starting from: 
<https://www.linux-mips.org/wiki/NPTL#Design_Choices> and throughout (the 
expired certificate of the web site is a known issue, but there is 
currently no way to get it fixed as nobody knows where Ralf Baechle has 
gone).

> IIRC MIPS TLS support has been added in glibc with MIPS32 ISA in
> mind in 2005:
> https://sourceware.org/git/?p=glibc.git;a=commit;h=f850220be6
> 
> MIPS16 is handled differently:
> https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=ports/sysdeps/mips/tls-macros.h;h=3e87e42ea;hp=8fe2e4a150d;hb=43301bd3c2;hpb=85bd816a60
> 
> Shouldn't we have a similar set of macros for the R5900?

 The macros are there to support different instructions used to access 
data within TLS.  However the TLS pointer still has to be fetched with 
RDHWR, and READ_THREAD_POINTER does exactly that, by calling the 
`__builtin_thread_pointer' intrinsic in the MIPS16 case.  How it works is 
explained in GCC sources, in gcc/config/mips/mips.md:

;; In MIPS16 mode, the TLS base pointer is accessed by a
;; libgcc helper function __mips16_rdhwr(), as 'rdhwr' is not
;; accessible in MIPS16.
;;
;; This is not represented as a call insn, to avoid the
;; unnecesarry clobbering of caller-save registers by a
;; function consisting only of: "rdhwr $3,$29; j $31; nop;"
;;
;; A $25 clobber is added to cater for a $25 load stub added by the
;; linker to __mips16_rdhwr when the call is made from non-PIC code.

-- that is a small piece of regular MIPS code is called, which executes 
RDHWR.

> The problem I have is this break running the binaries built
> by the Sony Linux Toolkit for Playstation 2 which use the
> LQ/SQ instruction:
> 
>  1b00d5c:       0c6c00d4        jal     0x1b00350
>  1b00d60:       7fa90020        sq      t1,32(sp)
>  1b00d64:       a2710000        sb      s1,0(s3)
>  1b00d68:       26730001        addiu   s3,s3,1
>  1b00d6c:       10000004        b       0x1b00d80
>  1b00d70:       7ba90020        lq      t1,32(sp)
>  1b00d74:       00000000        nop
>  1b00d78:       2617ffff        addiu   s7,s0,-1
>  1b00d7c:       25360001        addiu   s6,t1,1
>  1b00d80:       91220000        lbu     v0,0(t1)
>  1b00d84:       02e0802d        move    s0,s7
>  1b00d88:       02c0482d        move    t1,s6
>  1b00d8c:       a2620000        sb      v0,0(s3)
>  1b00d90:       1600fff9        bnez    s0,0x1b00d78
>  1b00d94:       26730001        addiu   s3,s3,1
>  1b00d98:       10000003        b       0x1b00da8
>  1b00d9c:       01342026        xor     a0,t1,s4
>  1b00da0:       253e0003        addiu   s8,t1,3
>  1b00da4:       01342026        xor     a0,t1,s4
>  1b00da8:       7fa90020        sq      t1,32(sp)
>  1b00dac:       2c840001        sltiu   a0,a0,1
>  1b00db0:       0c6c00d4        jal     0x1b00350

 Where exactly do you see a problem, what are the symptoms?  A bug in the 
emulation cannot be ruled out of course.

 While there indeed is an unfortunate opcode overlap between RDHWR and SQ, 
the encoding of the specific operands chosen for TLS pointer access is 
luckily guaranteed to always trap in the user mode, because the address 
requested when the encoding is interpreted as SQ rather than RDHWR is 
within the kernel KSEG2 segment:

$ cat rdhwr.s
	rdhwr	$3, $29
$ gcc -march=mips32r2 -c rdhwr.s
$ objdump -d rdhwr.o
rdhwr.o:     file format elf32-tradlittlemips


Disassembly of section .text:

00000000 <.text>:
   0:	7c03e83b 	rdhwr	v1,$29
	...
$ objdump -m mips:5900 -d rdhwr.o

rdhwr.o:     file format elf32-tradlittlemips


Disassembly of section .text:

00000000 <.text>:
   0:	7c03e83b 	sq	v1,-6085(zero)
	...
$ 

This is because the HWR read is encoded in bits 15:11 of the instruction 
word and $29 has the highest bit set, which lands in bit 15 of the 
instruction word, meaning that the offset encoded in bits 15:0 used where 
the instruction word is interpreted as SQ is negative.  And then bits 
25:21 are hardwired to 0 in the encoding of RDHWR and they correspond to 
the base register encoding with SQ, meaning that it will be interpreted as 
$zero.  So the address ultimately requested is -6085 => 0xffffffffffffe83b 
(the R5900 uses 32-bit addressing and ignores address bits 63:32, but that 
does not matter here; this is KSEG2 either way).

 Consequently an AdES exception is triggered which we can trap and handle, 
reinterpreting the encoding according to our needs and return the TLS 
pointer in $v1 rather than issuing a SIGBUS.  So you are not expected to 
see any issue unless there is a security erratum in the R5900 as well and 
the encoding does not cause an exception for some reason.

 This of course disagrees with what Fredrik wrote in the quotation above, 
as it's the last page rather than the zeroth that is accessed, but the net 
effect is the same, or even better.

  Maciej
Maciej W. Rozycki Nov. 19, 2020, 1:42 p.m. UTC | #4
On Thu, 19 Nov 2020, Maciej W. Rozycki wrote:

>  Consequently an AdES exception is triggered which we can trap and handle, 
> reinterpreting the encoding according to our needs and return the TLS 
> pointer in $v1 rather than issuing a SIGBUS.

 ... while keeping regular SQ instruction use intact, I should have added.

  Maciej
Fredrik Noring Dec. 12, 2020, 10:58 a.m. UTC | #5
Hi Maciej,

On Thu, Nov 19, 2020 at 01:28:00PM +0000, Maciej W. Rozycki wrote:
>  The use of RDHWR, actual or emulated, is a part of the MIPS TLS psABI, 
> see: <https://www.linux-mips.org/wiki/NPTL>, in particular starting from: 
> <https://www.linux-mips.org/wiki/NPTL#Design_Choices> and throughout (the 
> expired certificate of the web site is a known issue, but there is 
> currently no way to get it fixed as nobody knows where Ralf Baechle has 
> gone).

Can the site be wrapped up and published elsewhere?

>  While there indeed is an unfortunate opcode overlap between RDHWR and SQ, 
> the encoding of the specific operands chosen for TLS pointer access is 
> luckily guaranteed to always trap in the user mode, because the address 
> requested when the encoding is interpreted as SQ rather than RDHWR is 
> within the kernel KSEG2 segment:
> 
> $ cat rdhwr.s
> 	rdhwr	$3, $29
> $ gcc -march=mips32r2 -c rdhwr.s
> $ objdump -d rdhwr.o
> rdhwr.o:     file format elf32-tradlittlemips
> 
> 
> Disassembly of section .text:
> 
> 00000000 <.text>:
>    0:	7c03e83b 	rdhwr	v1,$29
> 	...
> $ objdump -m mips:5900 -d rdhwr.o
> 
> rdhwr.o:     file format elf32-tradlittlemips
> 
> 
> Disassembly of section .text:
> 
> 00000000 <.text>:
>    0:	7c03e83b 	sq	v1,-6085(zero)
> 	...
> $ 
> 
> This is because the HWR read is encoded in bits 15:11 of the instruction 
> word and $29 has the highest bit set, which lands in bit 15 of the 
> instruction word, meaning that the offset encoded in bits 15:0 used where 
> the instruction word is interpreted as SQ is negative.  And then bits 
> 25:21 are hardwired to 0 in the encoding of RDHWR and they correspond to 
> the base register encoding with SQ, meaning that it will be interpreted as 
> $zero.  So the address ultimately requested is -6085 => 0xffffffffffffe83b 
> (the R5900 uses 32-bit addressing and ignores address bits 63:32, but that 
> does not matter here; this is KSEG2 either way).
> 
>  Consequently an AdES exception is triggered which we can trap and handle, 
> reinterpreting the encoding according to our needs and return the TLS 
> pointer in $v1 rather than issuing a SIGBUS.  So you are not expected to 
> see any issue unless there is a security erratum in the R5900 as well and 
> the encoding does not cause an exception for some reason.
> 
>  This of course disagrees with what Fredrik wrote in the quotation above, 
> as it's the last page rather than the zeroth that is accessed, but the net 
> effect is the same, or even better.

The first page could be mapped by the application itself, but what about
RDHWR registers $0-$3 having mnemonics CPUNum, SYNC1_Step, CC and CCRes[1],
or in Linux

/* RDHWR register numbers */
#define MIPS_HWR_CPUNUM		0	/* CPU number */
#define MIPS_HWR_SYNCISTEP	1	/* SYNCI step size */
#define MIPS_HWR_CC		2	/* Cycle counter */
#define MIPS_HWR_CCRES		3	/* Cycle counter resolution */
#define MIPS_HWR_ULR		29	/* UserLocal */
#define MIPS_HWR_IMPL1		30	/* Implementation dependent */
#define MIPS_HWR_IMPL2		31	/* Implementation dependent */

in arch/mips/include/asm/mipsregs.h? They land on the first page, no?

Fredrik

References:

[1] "MIPS Architecture for programmers Volume II-A: The MIPS32 Instruction
    Set", revision 5.04, 11 December 2013, p. 248.
Maciej W. Rozycki Dec. 12, 2020, 11:36 a.m. UTC | #6
On Sat, 12 Dec 2020, Fredrik Noring wrote:

> >  The use of RDHWR, actual or emulated, is a part of the MIPS TLS psABI, 
> > see: <https://www.linux-mips.org/wiki/NPTL>, in particular starting from: 
> > <https://www.linux-mips.org/wiki/NPTL#Design_Choices> and throughout (the 
> > expired certificate of the web site is a known issue, but there is 
> > currently no way to get it fixed as nobody knows where Ralf Baechle has 
> > gone).
> 
> Can the site be wrapped up and published elsewhere?

 Well if someone does that, sure!  I guess archive.org will have it too.

> >  This of course disagrees with what Fredrik wrote in the quotation above, 
> > as it's the last page rather than the zeroth that is accessed, but the net 
> > effect is the same, or even better.
> 
> The first page could be mapped by the application itself, but what about

 It doesn't matter given that `rdhwr $3, $29' maps to an R5900 instruction 
that does not access it.

> RDHWR registers $0-$3 having mnemonics CPUNum, SYNC1_Step, CC and CCRes[1],
> or in Linux
> 
> /* RDHWR register numbers */
> #define MIPS_HWR_CPUNUM		0	/* CPU number */
> #define MIPS_HWR_SYNCISTEP	1	/* SYNCI step size */
> #define MIPS_HWR_CC		2	/* Cycle counter */
> #define MIPS_HWR_CCRES		3	/* Cycle counter resolution */
> #define MIPS_HWR_ULR		29	/* UserLocal */
> #define MIPS_HWR_IMPL1		30	/* Implementation dependent */
> #define MIPS_HWR_IMPL2		31	/* Implementation dependent */
> 
> in arch/mips/include/asm/mipsregs.h? They land on the first page, no?

 Unlike TLS pointer access, specifically using $3 as rt, which has been 
made a part of the Linux ABI, they're not supposed to be referred with 
pre-R2 code.  After all RDHWR was only introduced with R2.

 Indeed there's emulation code in the kernel for those encodings even with 
pre-R2 hardware, but it is not guaranteed to give sensible results, and 
given the circumstances I'm not sure what it really is for, e.g. what is 
SYNCI_Step for with processors that do not implement SYNCI?  The cycle 
counter register may be absent too, so even emulated accesses will return 
rubbish; gettimeofday(2) is the standard interface.

 So I think we can safely ignore them, just as we can any ULR access with 
rt != $3.  Unlike standardised TLS pointer accesses such instructions 
won't appear in compiler-generated code and whoever uses them in handcoded 
assembly or otherwise generated machine code will have to make sure they 
make sense for their application (yes, there's rubbish code out there, 
e.g. Firefox has a JIT that unconditionally produces MIPS R2 code even if 
the piece of software has been compiled for an older ISA revision, and 
having not verified that the CPU it runs on actually supports the R2 ISA, 
but that's just the usual imperfection of the world; you just can't fix it 
all).

  Maciej
Fredrik Noring Dec. 12, 2020, 12:14 p.m. UTC | #7
On Sat, Dec 12, 2020 at 11:36:35AM +0000, Maciej W. Rozycki wrote:
>  Unlike TLS pointer access, specifically using $3 as rt, which has been 
> made a part of the Linux ABI, they're not supposed to be referred with 
> pre-R2 code.  After all RDHWR was only introduced with R2.
> 
>  Indeed there's emulation code in the kernel for those encodings even with 
> pre-R2 hardware, but it is not guaranteed to give sensible results, and 
> given the circumstances I'm not sure what it really is for, e.g. what is 
> SYNCI_Step for with processors that do not implement SYNCI?  The cycle 
> counter register may be absent too, so even emulated accesses will return 
> rubbish; gettimeofday(2) is the standard interface.
> 
>  So I think we can safely ignore them, just as we can any ULR access with 
> rt != $3.  Unlike standardised TLS pointer accesses such instructions 
> won't appear in compiler-generated code and whoever uses them in handcoded 
> assembly or otherwise generated machine code will have to make sure they 
> make sense for their application (yes, there's rubbish code out there, 
> e.g. Firefox has a JIT that unconditionally produces MIPS R2 code even if 
> the piece of software has been compiled for an older ISA revision, and 
> having not verified that the CPU it runs on actually supports the R2 ISA, 
> but that's just the usual imperfection of the world; you just can't fix it 
> all).

Many thanks, that resolves it! I'll write it up in the commit message.

Fredrik
Fredrik Noring Dec. 13, 2020, 11:43 a.m. UTC | #8
>  So I think we can safely ignore them, just as we can any ULR access with 
> rt != $3.

The comment is corrected and the conditions on rd and rt are now strict,
as shown in the patch below.

Fredrik

diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index 92bd2b0f0548..89ce42c60c6f 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -90,6 +90,7 @@
 #include <asm/fpu_emulator.h>
 #include <asm/inst.h>
 #include <asm/mmu_context.h>
+#include <asm/traps.h>
 #include <linux/uaccess.h>
 
 #define STR(x)	__STR(x)
@@ -934,7 +935,46 @@ static void emulate_load_store_insn(struct pt_regs *regs,
 		 * interest.
 		 */
 	case spec3_op:
-		if (insn.dsp_format.func == lx_op) {
+		if (IS_ENABLED(CONFIG_CPU_R5900)) {
+			/*
+			 * On the R5900, a valid RDHWR instruction
+			 *
+			 *     +--------+-------+----+----+-------+--------+
+			 *     | 011111 | 00000 | rt | rd | 00000 | 111011 |
+			 *     +--------+-------+----+----+-------+--------+
+			 *          6       5      5    5     5        6
+			 *
+			 * having rt $3 (v1) and rd $29 (MIPS_HWR_ULR) is
+			 * interpreted as the R5900 specific SQ instruction
+			 *
+			 *     +--------+-------+----+---------------------+
+			 *     | 011111 |  base | rt |        offset       |
+			 *     +--------+-------+----+---------------------+
+			 *          6       5      5            16
+			 *
+			 * with
+			 *
+			 *     sq v1,-6085(zero)
+			 *
+			 * that asserts an address exception since -6085(zero)
+			 * always resolves to 0xffffe83b in 32-bit KSEG2.
+			 *
+			 * Other legacy values of rd, such as MIPS_HWR_CPUNUM,
+			 * are ignored.
+			 */
+			if (insn.r_format.func == rdhwr_op &&
+			    insn.r_format.rd == MIPS_HWR_ULR &&
+			    insn.r_format.rt == 3 &&
+			    insn.r_format.rs == 0 &&
+			    insn.r_format.re == 0) {
+				if (compute_return_epc(regs) < 0 ||
+				    simulate_rdhwr(regs, insn.r_format.rd,
+						   insn.r_format.rt) < 0)
+					goto sigill;
+				return;
+			}
+			goto sigbus;
+		} else if (insn.dsp_format.func == lx_op) {
 			switch (insn.dsp_format.op) {
 			case lwx_op:
 				if (!access_ok(addr, 4))
diff mbox series

Patch

diff --git a/arch/mips/include/asm/traps.h b/arch/mips/include/asm/traps.h
index 6a0864bb604d..ed6449f2967b 100644
--- a/arch/mips/include/asm/traps.h
+++ b/arch/mips/include/asm/traps.h
@@ -35,4 +35,6 @@  extern int register_nmi_notifier(struct notifier_block *nb);
 	register_nmi_notifier(&fn##_nb);				\
 })
 
+int simulate_rdhwr(struct pt_regs *regs, int rd, int rt);
+
 #endif /* _ASM_TRAPS_H */
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 342e41de9d64..9423b9a2eb67 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -625,7 +625,7 @@  static int simulate_llsc(struct pt_regs *regs, unsigned int opcode)
  * Simulate trapping 'rdhwr' instructions to provide user accessible
  * registers not implemented in hardware.
  */
-static int simulate_rdhwr(struct pt_regs *regs, int rd, int rt)
+int simulate_rdhwr(struct pt_regs *regs, int rd, int rt)
 {
 	struct thread_info *ti = task_thread_info(current);
 
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c
index 92bd2b0f0548..f490944d73cf 100644
--- a/arch/mips/kernel/unaligned.c
+++ b/arch/mips/kernel/unaligned.c
@@ -90,6 +90,7 @@ 
 #include <asm/fpu_emulator.h>
 #include <asm/inst.h>
 #include <asm/mmu_context.h>
+#include <asm/traps.h>
 #include <linux/uaccess.h>
 
 #define STR(x)	__STR(x)
@@ -934,7 +935,39 @@  static void emulate_load_store_insn(struct pt_regs *regs,
 		 * interest.
 		 */
 	case spec3_op:
-		if (insn.dsp_format.func == lx_op) {
+		if (IS_ENABLED(CONFIG_CPU_R5900)) {
+			/*
+			 * On the R5900, the RDHWR instruction
+			 *
+			 *     +--------+-------+----+----+-------+--------+
+			 *     | 011111 | 00000 | rt | rd | 00000 | 111011 |
+			 *     +--------+-------+----+----+-------+--------+
+			 *          6       5      5    5     5        6
+			 *
+			 * is interpreted as the R5900 specific SQ instruction
+			 *
+			 *     +--------+-------+----+---------------------+
+			 *     | 011111 |  base | rt |        offset       |
+			 *     +--------+-------+----+---------------------+
+			 *          6       5      5            16
+			 *
+			 * that asserts a zero page address exception. Hence
+			 * RDHWR can be trapped and emulated here, provided
+			 * DEFAULT_MMAP_MIN_ADDR isn't zero.
+			 */
+			BUILD_BUG_ON(IS_ENABLED(CONFIG_CPU_R5900) &&
+				CONFIG_DEFAULT_MMAP_MIN_ADDR < PAGE_SIZE);
+			if (insn.r_format.func == rdhwr_op &&
+			    insn.r_format.rs == 0 &&
+			    insn.r_format.re == 0) {
+				if (compute_return_epc(regs) < 0 ||
+				    simulate_rdhwr(regs, insn.r_format.rd,
+						   insn.r_format.rt) < 0)
+					goto sigill;
+				return;
+			}
+			goto sigbus;
+		} else if (insn.dsp_format.func == lx_op) {
 			switch (insn.dsp_format.op) {
 			case lwx_op:
 				if (!access_ok(addr, 4))
@@ -1342,6 +1375,7 @@  static void emulate_load_store_insn(struct pt_regs *regs,
 		cu2_notifier_call_chain(CU2_SDC2_OP, regs);
 		break;
 #endif
+
 	default:
 		/*
 		 * Pheeee...  We encountered an yet unknown instruction or