diff mbox

[4/6] target-mips: add restrictions for possible values in registers

Message ID 1405354795-25884-5-git-send-email-leon.alrae@imgtec.com
State New
Headers show

Commit Message

Leon Alrae July 14, 2014, 4:19 p.m. UTC
In Release 6 not all the values are allowed to be written to a register.
If the value is not valid or unsupported then it should stay unchanged.

For pre-R6 the existing behaviour has been changed only for CP0_Index register
as the current implementation does not seem to be correct - it looks like it
tries to limit the input value but the limit is higher than the actual
number of tlb entries.

Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
---
 target-mips/op_helper.c |   63 ++++++++++++++++++++++++++++++++++------------
 1 files changed, 46 insertions(+), 17 deletions(-)

Comments

Yongbok Kim Oct. 20, 2014, 10:19 a.m. UTC | #1
On 14/07/2014 17:19, Leon Alrae wrote:
> In Release 6 not all the values are allowed to be written to a register.
> If the value is not valid or unsupported then it should stay unchanged.
>
> For pre-R6 the existing behaviour has been changed only for CP0_Index register
> as the current implementation does not seem to be correct - it looks like it
> tries to limit the input value but the limit is higher than the actual
> number of tlb entries.
>
> Signed-off-by: Leon Alrae<leon.alrae@imgtec.com>
> ---
>   target-mips/op_helper.c |   63 ++++++++++++++++++++++++++++++++++------------
>   1 files changed, 46 insertions(+), 17 deletions(-)
>
> diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
> index 431f3a1..4be435c 100644
> --- a/target-mips/op_helper.c
> +++ b/target-mips/op_helper.c
> @@ -959,14 +959,14 @@ target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel)
>   
>   void helper_mtc0_index(CPUMIPSState *env, target_ulong arg1)
>   {
> -    int num = 1;
> -    unsigned int tmp = env->tlb->nb_tlb;
> -
> -    do {
> -        tmp >>= 1;
> -        num <<= 1;
> -    } while (tmp);
> -    env->CP0_Index = (env->CP0_Index & 0x80000000) | (arg1 & (num - 1));
> +    uint32_t index_p = env->CP0_Index & 0x80000000;
> +    uint32_t tlb_index = arg1 & 0x7fffffff;
> +    if (tlb_index < env->tlb->nb_tlb) {
> +        if (env->insn_flags & ISA_MIPS32R6) {
> +            index_p |= arg1 & 0x80000000;
> +        }
> +        env->CP0_Index = index_p | tlb_index;
> +    }

Agree to restrict index for pre R6 as well. It is UNDEFINED and software 
shouldn't rely on the undefined behaviour.

>   }
>   
>   void helper_mtc0_mvpcontrol(CPUMIPSState *env, target_ulong arg1)
> @@ -1294,8 +1294,13 @@ void helper_mtc0_context(CPUMIPSState *env, target_ulong arg1)
>   
>   void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1)
>   {
> -    /* 1k pages not implemented */

This comment is still valid but it is appeared several times in the code 
already.
Agreed to remove the comment here.

> -    env->CP0_PageMask = arg1 & (0x1FFFFFFF & (TARGET_PAGE_MASK << 1));
> +    uint64_t mask = arg1 >> (TARGET_PAGE_BITS + 1);
> +    if (!(env->insn_flags & ISA_MIPS32R6) || (arg1 == ~0) ||
> +        (mask == 0x0000 || mask == 0x0003 || mask == 0x000F ||
> +         mask == 0x003F || mask == 0x00FF || mask == 0x03FF ||
> +         mask == 0x0FFF || mask == 0x3FFF || mask == 0xFFFF)) {
> +        env->CP0_PageMask = arg1 & (0x1FFFFFFF & (TARGET_PAGE_MASK << 1));
> +    }
>   }
>   
>   void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1)
> @@ -1309,7 +1314,13 @@ void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1)
>   
>   void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1)
>   {
> -    env->CP0_Wired = arg1 % env->tlb->nb_tlb;
> +    if (env->insn_flags & ISA_MIPS32R6) {
> +        if (arg1 < env->tlb->nb_tlb) {
> +            env->CP0_Wired = arg1;

Wired field should be compared with Limit field (and as a result, number 
of entries in the TLB).

> +        }
> +    } else {
> +        env->CP0_Wired = arg1 % env->tlb->nb_tlb;
> +    }
>   }
>   
>   void helper_mtc0_srsconf0(CPUMIPSState *env, target_ulong arg1)
> @@ -1368,11 +1379,14 @@ void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1)
>       }
>   
>       /* 1k pages not implemented */
> -    val = arg1 & mask;
>   #if defined(TARGET_MIPS64)
> -    val &= env->SEGMask;
> +    if ((env->insn_flags & ISA_MIPS32R6) && extract64(arg1, 62, 2) == 0x2) {
> +        mask &= ~(0x3ull << 62);

If Config0_AT = 1, R field is restricted for 1 as well.

> +    }
> +    mask &= env->SEGMask;
>   #endif
>       old = env->CP0_EntryHi;
> +    val = (arg1 & mask) | (old & ~mask);
>       env->CP0_EntryHi = val;
>       if (env->CP0_Config3 & (1 << CP0C3_MT)) {
>           sync_c0_entryhi(env, env->current_tc);
> @@ -1402,6 +1416,13 @@ void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1)
>       uint32_t val, old;
>       uint32_t mask = env->CP0_Status_rw_bitmask;
>   
> +    if (env->insn_flags & ISA_MIPS32R6) {
> +        if (extract32(env->CP0_Status, CP0St_KSU, 2) == 0x3) {
> +            mask &= ~(3 << CP0St_KSU);
> +        }
> +        mask &= ~(0x00180000 & arg1);
> +    }
> +
>       val = arg1 & mask;
>       old = env->CP0_Status;
>       env->CP0_Status = (env->CP0_Status & ~mask) | val;
> @@ -1457,6 +1478,9 @@ static void mtc0_cause(CPUMIPSState *cpu, target_ulong arg1)
>       if (cpu->insn_flags & ISA_MIPS32R2) {
>           mask |= 1 << CP0Ca_DC;
>       }
> +    if (cpu->insn_flags & ISA_MIPS32R6) {
> +        mask &= ~((1 << CP0Ca_WP) & arg1);
> +    }
>   
>       cpu->CP0_Cause = (cpu->CP0_Cause & ~mask) | (arg1 & mask);
>   
> @@ -2381,8 +2405,9 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt)
>           }
>           break;
>       case 25:
> -        if (arg1 & 0xffffff00)
> +        if (env->insn_flags & ISA_MIPS32R6 || arg1 & 0xffffff00) {
>               return;
> +        }
>           env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0x017fffff) | ((arg1 & 0xfe) << 24) |
>                        ((arg1 & 0x1) << 23);
>           break;
> @@ -2398,9 +2423,13 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt)
>                        ((arg1 & 0x4) << 22);
>           break;
>       case 31:
> -        if (arg1 & 0x007c0000)
> -            return;
> -        env->active_fpu.fcr31 = arg1;
> +        if (env->insn_flags & ISA_MIPS32R6) {
> +            uint32_t mask = 0xfefc0000;
> +            env->active_fpu.fcr31 = (arg1 & ~mask) |
> +                (env->active_fpu.fcr31 & mask);
> +        } else if (!(arg1 & 0x007c0000)) {
> +            env->active_fpu.fcr31 = arg1;
> +        }
>           break;
>       default:
>           return;


Regards,
Yongbok
Leon Alrae Oct. 21, 2014, 1:54 p.m. UTC | #2
On 20/10/2014 11:19, Yongbok Kim wrote:
>>  void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1)
>>  {
>> -    env->CP0_Wired = arg1 % env->tlb->nb_tlb;
>> +    if (env->insn_flags & ISA_MIPS32R6) {
>> +        if (arg1 < env->tlb->nb_tlb) {
>> +            env->CP0_Wired = arg1;
> 
> Wired field should be compared with Limit field (and as a result, number
> of entries in the TLB).

From spec's point of view the implementation is correct. It supports
"Limit = 0" - maximum number of wired entries is equal to the number of
TLB entries minus one. Obviously we will have to add Limit field once we
decide to support Limit > 0.

> 
>> +        }
>> +    } else {
>> +        env->CP0_Wired = arg1 % env->tlb->nb_tlb;
>> +    }
>>  }
>>  
>>  void helper_mtc0_srsconf0(CPUMIPSState *env, target_ulong arg1)
>> @@ -1368,11 +1379,14 @@ void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1)
>>      }
>>  
>>      /* 1k pages not implemented */
>> -    val = arg1 & mask;
>>  #if defined(TARGET_MIPS64)
>> -    val &= env->SEGMask;
>> +    if ((env->insn_flags & ISA_MIPS32R6) && extract64(arg1, 62, 2) == 0x2) {
>> +        mask &= ~(0x3ull << 62);
> 
> If Config0_AT = 1, R field is restricted for 1 as well.

Good spot, thanks.

Regards,
Leon
diff mbox

Patch

diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 431f3a1..4be435c 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -959,14 +959,14 @@  target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel)
 
 void helper_mtc0_index(CPUMIPSState *env, target_ulong arg1)
 {
-    int num = 1;
-    unsigned int tmp = env->tlb->nb_tlb;
-
-    do {
-        tmp >>= 1;
-        num <<= 1;
-    } while (tmp);
-    env->CP0_Index = (env->CP0_Index & 0x80000000) | (arg1 & (num - 1));
+    uint32_t index_p = env->CP0_Index & 0x80000000;
+    uint32_t tlb_index = arg1 & 0x7fffffff;
+    if (tlb_index < env->tlb->nb_tlb) {
+        if (env->insn_flags & ISA_MIPS32R6) {
+            index_p |= arg1 & 0x80000000;
+        }
+        env->CP0_Index = index_p | tlb_index;
+    }
 }
 
 void helper_mtc0_mvpcontrol(CPUMIPSState *env, target_ulong arg1)
@@ -1294,8 +1294,13 @@  void helper_mtc0_context(CPUMIPSState *env, target_ulong arg1)
 
 void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1)
 {
-    /* 1k pages not implemented */
-    env->CP0_PageMask = arg1 & (0x1FFFFFFF & (TARGET_PAGE_MASK << 1));
+    uint64_t mask = arg1 >> (TARGET_PAGE_BITS + 1);
+    if (!(env->insn_flags & ISA_MIPS32R6) || (arg1 == ~0) ||
+        (mask == 0x0000 || mask == 0x0003 || mask == 0x000F ||
+         mask == 0x003F || mask == 0x00FF || mask == 0x03FF ||
+         mask == 0x0FFF || mask == 0x3FFF || mask == 0xFFFF)) {
+        env->CP0_PageMask = arg1 & (0x1FFFFFFF & (TARGET_PAGE_MASK << 1));
+    }
 }
 
 void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1)
@@ -1309,7 +1314,13 @@  void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1)
 
 void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1)
 {
-    env->CP0_Wired = arg1 % env->tlb->nb_tlb;
+    if (env->insn_flags & ISA_MIPS32R6) {
+        if (arg1 < env->tlb->nb_tlb) {
+            env->CP0_Wired = arg1;
+        }
+    } else {
+        env->CP0_Wired = arg1 % env->tlb->nb_tlb;
+    }
 }
 
 void helper_mtc0_srsconf0(CPUMIPSState *env, target_ulong arg1)
@@ -1368,11 +1379,14 @@  void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1)
     }
 
     /* 1k pages not implemented */
-    val = arg1 & mask;
 #if defined(TARGET_MIPS64)
-    val &= env->SEGMask;
+    if ((env->insn_flags & ISA_MIPS32R6) && extract64(arg1, 62, 2) == 0x2) {
+        mask &= ~(0x3ull << 62);
+    }
+    mask &= env->SEGMask;
 #endif
     old = env->CP0_EntryHi;
+    val = (arg1 & mask) | (old & ~mask);
     env->CP0_EntryHi = val;
     if (env->CP0_Config3 & (1 << CP0C3_MT)) {
         sync_c0_entryhi(env, env->current_tc);
@@ -1402,6 +1416,13 @@  void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1)
     uint32_t val, old;
     uint32_t mask = env->CP0_Status_rw_bitmask;
 
+    if (env->insn_flags & ISA_MIPS32R6) {
+        if (extract32(env->CP0_Status, CP0St_KSU, 2) == 0x3) {
+            mask &= ~(3 << CP0St_KSU);
+        }
+        mask &= ~(0x00180000 & arg1);
+    }
+
     val = arg1 & mask;
     old = env->CP0_Status;
     env->CP0_Status = (env->CP0_Status & ~mask) | val;
@@ -1457,6 +1478,9 @@  static void mtc0_cause(CPUMIPSState *cpu, target_ulong arg1)
     if (cpu->insn_flags & ISA_MIPS32R2) {
         mask |= 1 << CP0Ca_DC;
     }
+    if (cpu->insn_flags & ISA_MIPS32R6) {
+        mask &= ~((1 << CP0Ca_WP) & arg1);
+    }
 
     cpu->CP0_Cause = (cpu->CP0_Cause & ~mask) | (arg1 & mask);
 
@@ -2381,8 +2405,9 @@  void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt)
         }
         break;
     case 25:
-        if (arg1 & 0xffffff00)
+        if (env->insn_flags & ISA_MIPS32R6 || arg1 & 0xffffff00) {
             return;
+        }
         env->active_fpu.fcr31 = (env->active_fpu.fcr31 & 0x017fffff) | ((arg1 & 0xfe) << 24) |
                      ((arg1 & 0x1) << 23);
         break;
@@ -2398,9 +2423,13 @@  void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt)
                      ((arg1 & 0x4) << 22);
         break;
     case 31:
-        if (arg1 & 0x007c0000)
-            return;
-        env->active_fpu.fcr31 = arg1;
+        if (env->insn_flags & ISA_MIPS32R6) {
+            uint32_t mask = 0xfefc0000;
+            env->active_fpu.fcr31 = (arg1 & ~mask) |
+                (env->active_fpu.fcr31 & mask);
+        } else if (!(arg1 & 0x007c0000)) {
+            env->active_fpu.fcr31 = arg1;
+        }
         break;
     default:
         return;