Subject: Re: Q: R3000, how to manipulate CP0_EPC?

From: [email-masked]
Date: Wed, 28 Jan 1998 01:58:27 +0100

On Tue, Jan 27, 1998 at 06:32:09PM +0100, [email-masked] wrote:

> This is the begin of the RESTORE_ALL macro, which is used in ret_from_sys_call:
> #define RESTORE_ALL                                      \
>                 mfc0    t0, CP0_STATUS;                  \
>                 nop;                                     \
>                 ori     t0, 0x01;                        \
>                 xori    t0, 0x01;                        \
>                 mtc0    t0, CP0_STATUS;                  \
>                 lw      v0, PT_STATUS(sp);               \
>                 lw      v1, PT_LO(sp);                   \
>                 mtc0    v0, CP0_STATUS;                  \
>                 mtlo    v1;                              \
>                 lw      v0, PT_HI(sp);                   \
>                 lw      v1, PT_EPC(sp);                  \
>                 mthi    v0;                              \
>                 mtc0    v1, CP0_EPC;                     \
> ..
> 
> PT_EPC is manipulated in do_sys to point behind the syscall instruction and a
> PRINT(...) shows that v1 contains the correct value. Nevertheless CP0_EPC
> remains to be unchanged.
> 
> After finding that, I used the following test code:
> #define RESTORE_ALL_TEST                                \
>                 lui     v0,0xffff;                      \
>                 mtc0    v0,CP0_EPC;                     \
>                 nop;                                    \
>                 mfc0    a1,CP0_EPC;                     \
>                 nop;                                    \
>                 PRINT("CP0_EPC   : %08x\n");            \
> 
> which "compiles" into:
> 800375d0 <return_test> lui $v0,0xffff
> 800375d4 <return_test+4> mtc0 $v0,$14
> 800375dc <return_test+c> mfc0 $a1,$14
> 800375e4 <return_test+14> lui $a0,0x800c
> 800375e8 <return_test+18> jal 80054478 <printk>
> 800375ec <return_test+1c> addiu $a0,$a0,5680  
> 
> and the result is:
> CP0_EPC   : 80036aa4
> 
> which is the address of the syscall instruction itself. But the result should be
> 0xFFFF0000, shouldn't it?.
> 
> Has anybody an explanation for this, or am I just completely blind?

On the R3000 c0_epc is a readonly register - as documented.  That's ok
because the R3000 uses a different way of handling exceptions.  What you'll
have to do to return from an exception is about as follows:

   restore c0_status
   move k1, sp				; needed later
   restore all gp registers
   restore hi/lo registers
   lw	k0, PT_EPC(sp)
   jr   k0
   rfe					; in the delay slot of the jr

  Ralf