diff mbox

[3/7] target-mips: add CP0.PageGrain.ELPA support

Message ID 1430224874-18513-4-git-send-email-leon.alrae@imgtec.com
State New
Headers show

Commit Message

Leon Alrae April 28, 2015, 12:41 p.m. UTC
CP0.PageGrain.ELPA enables support for large physical addresses. This field
is encoded as follows:
0: Large physical address support is disabled.
1: Large physical address support is enabled.

If this bit is a 1, the following changes occur to coprocessor 0 registers:
- The PFNX field of the EntryLo0 and EntryLo1 registers is writable and
  concatenated with the PFN field to form the full page frame number.
- Access to optional COP0 registers with PA extension, LLAddr, TagLo is
  defined.

P5600 can operate in 32-bit or 40-bit Physical Address Mode. Therefore if
XPA is disabled (CP0.PageGrain.ELPA = 0) then assuming 32-bit Address Mode.
For MIPS64 default PABITS are 36.

env->PABITS value is constant and indicates maximum PABITS available on
a core, whereas env->PAMask is calculated from env->PABITS and is also
affected by CP0.PageGrain.ELPA.

Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
---
 target-mips/cpu.h       | 27 +++++++++++++++++++++++++--
 target-mips/machine.c   |  1 +
 target-mips/mips-defs.h |  4 ++--
 target-mips/op_helper.c | 24 +++++++++++++++++-------
 target-mips/translate.c |  3 ++-
 5 files changed, 47 insertions(+), 12 deletions(-)

Comments

James Hogan April 28, 2015, 3:08 p.m. UTC | #1
Hi Leon,

On 28/04/15 13:41, Leon Alrae wrote:
> CP0.PageGrain.ELPA enables support for large physical addresses. This field
> is encoded as follows:
> 0: Large physical address support is disabled.
> 1: Large physical address support is enabled.
> 
> If this bit is a 1, the following changes occur to coprocessor 0 registers:
> - The PFNX field of the EntryLo0 and EntryLo1 registers is writable and
>   concatenated with the PFN field to form the full page frame number.
> - Access to optional COP0 registers with PA extension, LLAddr, TagLo is
>   defined.
> 
> P5600 can operate in 32-bit or 40-bit Physical Address Mode. Therefore if
> XPA is disabled (CP0.PageGrain.ELPA = 0) then assuming 32-bit Address Mode.
> For MIPS64 default PABITS are 36.
> 
> env->PABITS value is constant and indicates maximum PABITS available on
> a core, whereas env->PAMask is calculated from env->PABITS and is also
> affected by CP0.PageGrain.ELPA.
> 
> Signed-off-by: Leon Alrae <leon.alrae@imgtec.com>
> ---
>  target-mips/cpu.h       | 27 +++++++++++++++++++++++++--
>  target-mips/machine.c   |  1 +
>  target-mips/mips-defs.h |  4 ++--
>  target-mips/op_helper.c | 24 +++++++++++++++++-------
>  target-mips/translate.c |  3 ++-
>  5 files changed, 47 insertions(+), 12 deletions(-)
> 
> diff --git a/target-mips/cpu.h b/target-mips/cpu.h
> index 2dfa139..bcd1e2b 100644
> --- a/target-mips/cpu.h
> +++ b/target-mips/cpu.h
> @@ -224,8 +224,14 @@ struct CPUMIPSState {
>  
>      uint32_t SEGBITS;
>      uint32_t PABITS;
> +#if defined(TARGET_MIPS64)
> +# define DEFAULT_PABITS 36
> +#else
> +# define DEFAULT_PABITS 32
> +#endif
>      target_ulong SEGMask;
>      uint64_t PAMask;
> +#define DEFAULT_PAMASK ((1ULL << DEFAULT_PABITS) - 1)
>  
>      int32_t msair;
>  #define MSAIR_ProcID    8
> @@ -289,6 +295,7 @@ struct CPUMIPSState {
>      int32_t CP0_PageGrain;
>  #define CP0PG_RIE 31
>  #define CP0PG_XIE 30
> +#define CP0PG_ELPA 29
>  #define CP0PG_IEC 27
>      int32_t CP0_Wired;
>      int32_t CP0_SRSConf0_rw_bitmask;
> @@ -517,7 +524,7 @@ struct CPUMIPSState {
>  #define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */
>      uint32_t hflags;    /* CPU State */
>      /* TMASK defines different execution modes */
> -#define MIPS_HFLAG_TMASK  0x35807FF
> +#define MIPS_HFLAG_TMASK  0x75807FF
>  #define MIPS_HFLAG_MODE   0x00007 /* execution modes                    */
>      /* The KSU flags must be the lowest bits in hflags. The flag order
>         must be the same as defined for CP0 Status. This allows to use
> @@ -565,6 +572,7 @@ struct CPUMIPSState {
>  #define MIPS_HFLAG_FBNSLOT 0x800000 /* Forbidden slot                   */
>  #define MIPS_HFLAG_MSA   0x1000000
>  #define MIPS_HFLAG_FRE   0x2000000 /* FRE enabled */
> +#define MIPS_HFLAG_ELPA  0x4000000
>      target_ulong btarget;        /* Jump / branch target               */
>      target_ulong bcond;          /* Branch condition (if needed)       */
>  
> @@ -800,6 +808,15 @@ static inline void restore_msa_fp_status(CPUMIPSState *env)
>      set_flush_inputs_to_zero(flush_to_zero, status);
>  }
>  
> +static inline void restore_pamask(CPUMIPSState *env)
> +{
> +    if (env->hflags & MIPS_HFLAG_ELPA) {
> +        env->PAMask = (1ULL << env->PABITS) - 1;
> +    } else {
> +        env->PAMask = DEFAULT_PAMASK;
> +    }
> +}
> +
>  static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc,
>                                          target_ulong *cs_base, int *flags)
>  {
> @@ -847,7 +864,8 @@ static inline void compute_hflags(CPUMIPSState *env)
>      env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 |
>                       MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU |
>                       MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 |
> -                     MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE);
> +                     MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE |
> +                     MIPS_HFLAG_ELPA);
>      if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
>          !(env->CP0_Status & (1 << CP0St_ERL)) &&
>          !(env->hflags & MIPS_HFLAG_DM)) {
> @@ -933,6 +951,11 @@ static inline void compute_hflags(CPUMIPSState *env)
>              env->hflags |= MIPS_HFLAG_FRE;
>          }
>      }
> +    if (env->CP0_Config3 & (1 << CP0C3_LPA)) {
> +        if (env->CP0_PageGrain & (1 << CP0PG_ELPA)) {
> +            env->hflags |= MIPS_HFLAG_ELPA;
> +        }
> +    }
>  }
>  
>  #ifndef CONFIG_USER_ONLY
> diff --git a/target-mips/machine.c b/target-mips/machine.c
> index 559402c..8fa755c 100644
> --- a/target-mips/machine.c
> +++ b/target-mips/machine.c
> @@ -10,6 +10,7 @@ static int cpu_post_load(void *opaque, int version_id)
>      restore_fp_status(env);
>      restore_msa_fp_status(env);
>      compute_hflags(env);
> +    restore_pamask(env);
>  
>      return 0;
>  }
> diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h
> index 1784227..20aa87c 100644
> --- a/target-mips/mips-defs.h
> +++ b/target-mips/mips-defs.h
> @@ -10,11 +10,11 @@
>  
>  #if defined(TARGET_MIPS64)
>  #define TARGET_LONG_BITS 64
> -#define TARGET_PHYS_ADDR_SPACE_BITS 36
> +#define TARGET_PHYS_ADDR_SPACE_BITS 48
>  #define TARGET_VIRT_ADDR_SPACE_BITS 42
>  #else
>  #define TARGET_LONG_BITS 32
> -#define TARGET_PHYS_ADDR_SPACE_BITS 36
> +#define TARGET_PHYS_ADDR_SPACE_BITS 40

Out of interest, is there a particular reason not to put this up to 59,
the max supported by the architecture, rather than just what P5600 supports?

>  #define TARGET_VIRT_ADDR_SPACE_BITS 32
>  #endif
>  
> diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
> index 6bff927..4b1b0ec 100644
> --- a/target-mips/op_helper.c
> +++ b/target-mips/op_helper.c
> @@ -1067,19 +1067,28 @@ void helper_mtc0_vpeopt(CPUMIPSState *env, target_ulong arg1)
>      env->CP0_VPEOpt = arg1 & 0x0000ffff;
>  }
>  
> +static inline target_ulong get_mtc0_entrylo_mask(const CPUMIPSState *env)
> +{
> +#if defined(TARGET_MIPS64)
> +    return env->PAMask >> 6;

I think this case is suitable for dmtc0 EntryLo regardless of MIPS64/MIPS32?

> +#else
> +    return (env->PAMask >> 6) & 0x3FFFFFFF;

mtc0 sets bits 61:30 of EntryLo to 0 on MIPS64 too, so this case is
suitable for mtc0 regardless of MIPS64/MIPS32?

Cheers
James

> +#endif
> +}
> +
>  void helper_mtc0_entrylo0(CPUMIPSState *env, target_ulong arg1)
>  {
> -    /* Large physaddr (PABITS) not implemented */
>      /* 1k pages not implemented */
>      target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE));
> -    env->CP0_EntryLo0 = (arg1 & 0x3FFFFFFF) | (rxi << (CP0EnLo_XI - 30));
> +    env->CP0_EntryLo0 = (arg1 & get_mtc0_entrylo_mask(env))
> +                        | (rxi << (CP0EnLo_XI - 30));
>  }
>  
>  #if defined(TARGET_MIPS64)
>  void helper_dmtc0_entrylo0(CPUMIPSState *env, uint64_t arg1)
>  {
>      uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32);
> -    env->CP0_EntryLo0 = (arg1 & 0x3FFFFFFF) | rxi;
> +    env->CP0_EntryLo0 = (arg1 & get_mtc0_entrylo_mask(env)) | rxi;
>  }
>  #endif
>  
> @@ -1245,17 +1254,17 @@ void helper_mttc0_tcschefback(CPUMIPSState *env, target_ulong arg1)
>  
>  void helper_mtc0_entrylo1(CPUMIPSState *env, target_ulong arg1)
>  {
> -    /* Large physaddr (PABITS) not implemented */
>      /* 1k pages not implemented */
>      target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE));
> -    env->CP0_EntryLo1 = (arg1 & 0x3FFFFFFF) | (rxi << (CP0EnLo_XI - 30));
> +    env->CP0_EntryLo1 = (arg1 & get_mtc0_entrylo_mask(env))
> +                        | (rxi << (CP0EnLo_XI - 30));
>  }
>  
>  #if defined(TARGET_MIPS64)
>  void helper_dmtc0_entrylo1(CPUMIPSState *env, uint64_t arg1)
>  {
>      uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32);
> -    env->CP0_EntryLo1 = (arg1 & 0x3FFFFFFF) | rxi;
> +    env->CP0_EntryLo1 = (arg1 & get_mtc0_entrylo_mask(env)) | rxi;
>  }
>  #endif
>  
> @@ -1278,10 +1287,11 @@ void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1)
>  void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1)
>  {
>      /* SmartMIPS not implemented */
> -    /* Large physaddr (PABITS) not implemented */
>      /* 1k pages not implemented */
>      env->CP0_PageGrain = (arg1 & env->CP0_PageGrain_rw_bitmask) |
>                           (env->CP0_PageGrain & ~env->CP0_PageGrain_rw_bitmask);
> +    compute_hflags(env);
> +    restore_pamask(env);
>  }
>  
>  void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1)
> diff --git a/target-mips/translate.c b/target-mips/translate.c
> index 0f875be..bb219ea 100644
> --- a/target-mips/translate.c
> +++ b/target-mips/translate.c
> @@ -5666,6 +5666,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
>              check_insn(ctx, ISA_MIPS32R2);
>              gen_helper_mtc0_pagegrain(cpu_env, arg);
>              rn = "PageGrain";
> +            ctx->bstate = BS_STOP;
>              break;
>          default:
>              goto cp0_unimplemented;
> @@ -19526,7 +19527,6 @@ void cpu_state_reset(CPUMIPSState *env)
>      }
>  #endif
>      env->PABITS = env->cpu_model->PABITS;
> -    env->PAMask = (1ULL << env->cpu_model->PABITS) - 1;
>      env->CP0_SRSConf0_rw_bitmask = env->cpu_model->CP0_SRSConf0_rw_bitmask;
>      env->CP0_SRSConf0 = env->cpu_model->CP0_SRSConf0;
>      env->CP0_SRSConf1_rw_bitmask = env->cpu_model->CP0_SRSConf1_rw_bitmask;
> @@ -19647,6 +19647,7 @@ void cpu_state_reset(CPUMIPSState *env)
>      compute_hflags(env);
>      restore_rounding_mode(env);
>      restore_flush_mode(env);
> +    restore_pamask(env);
>      cs->exception_index = EXCP_NONE;
>  }
>  
>
Leon Alrae April 29, 2015, 11:35 a.m. UTC | #2
Hi James,

On 28/04/2015 16:08, James Hogan wrote:
>> diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h
>> index 1784227..20aa87c 100644
>> --- a/target-mips/mips-defs.h
>> +++ b/target-mips/mips-defs.h
>> @@ -10,11 +10,11 @@
>>  
>>  #if defined(TARGET_MIPS64)
>>  #define TARGET_LONG_BITS 64
>> -#define TARGET_PHYS_ADDR_SPACE_BITS 36
>> +#define TARGET_PHYS_ADDR_SPACE_BITS 48
>>  #define TARGET_VIRT_ADDR_SPACE_BITS 42
>>  #else
>>  #define TARGET_LONG_BITS 32
>> -#define TARGET_PHYS_ADDR_SPACE_BITS 36
>> +#define TARGET_PHYS_ADDR_SPACE_BITS 40
> 
> Out of interest, is there a particular reason not to put this up to 59,
> the max supported by the architecture, rather than just what P5600 supports?

More bits we declare then more levels of page tables QEMU will have,
which means more time spent on page walking. Therefore I don't think
it's a good idea to put the architectural limit if we don't have to.

>>  #define TARGET_VIRT_ADDR_SPACE_BITS 32
>>  #endif
>>  
>> diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
>> index 6bff927..4b1b0ec 100644
>> --- a/target-mips/op_helper.c
>> +++ b/target-mips/op_helper.c
>> @@ -1067,19 +1067,28 @@ void helper_mtc0_vpeopt(CPUMIPSState *env, target_ulong arg1)
>>      env->CP0_VPEOpt = arg1 & 0x0000ffff;
>>  }
>>  
>> +static inline target_ulong get_mtc0_entrylo_mask(const CPUMIPSState *env)
>> +{
>> +#if defined(TARGET_MIPS64)
>> +    return env->PAMask >> 6;
> 
> I think this case is suitable for dmtc0 EntryLo regardless of MIPS64/MIPS32?
> 
>> +#else
>> +    return (env->PAMask >> 6) & 0x3FFFFFFF;
> 
> mtc0 sets bits 61:30 of EntryLo to 0 on MIPS64 too, so this case is
> suitable for mtc0 regardless of MIPS64/MIPS32?

I must have assumed here that mtc0 and dmtc0 do the same thing in MIPS64
(apart from having RI/XI bits in different places). I'll fix it, thanks.

Leon
diff mbox

Patch

diff --git a/target-mips/cpu.h b/target-mips/cpu.h
index 2dfa139..bcd1e2b 100644
--- a/target-mips/cpu.h
+++ b/target-mips/cpu.h
@@ -224,8 +224,14 @@  struct CPUMIPSState {
 
     uint32_t SEGBITS;
     uint32_t PABITS;
+#if defined(TARGET_MIPS64)
+# define DEFAULT_PABITS 36
+#else
+# define DEFAULT_PABITS 32
+#endif
     target_ulong SEGMask;
     uint64_t PAMask;
+#define DEFAULT_PAMASK ((1ULL << DEFAULT_PABITS) - 1)
 
     int32_t msair;
 #define MSAIR_ProcID    8
@@ -289,6 +295,7 @@  struct CPUMIPSState {
     int32_t CP0_PageGrain;
 #define CP0PG_RIE 31
 #define CP0PG_XIE 30
+#define CP0PG_ELPA 29
 #define CP0PG_IEC 27
     int32_t CP0_Wired;
     int32_t CP0_SRSConf0_rw_bitmask;
@@ -517,7 +524,7 @@  struct CPUMIPSState {
 #define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */
     uint32_t hflags;    /* CPU State */
     /* TMASK defines different execution modes */
-#define MIPS_HFLAG_TMASK  0x35807FF
+#define MIPS_HFLAG_TMASK  0x75807FF
 #define MIPS_HFLAG_MODE   0x00007 /* execution modes                    */
     /* The KSU flags must be the lowest bits in hflags. The flag order
        must be the same as defined for CP0 Status. This allows to use
@@ -565,6 +572,7 @@  struct CPUMIPSState {
 #define MIPS_HFLAG_FBNSLOT 0x800000 /* Forbidden slot                   */
 #define MIPS_HFLAG_MSA   0x1000000
 #define MIPS_HFLAG_FRE   0x2000000 /* FRE enabled */
+#define MIPS_HFLAG_ELPA  0x4000000
     target_ulong btarget;        /* Jump / branch target               */
     target_ulong bcond;          /* Branch condition (if needed)       */
 
@@ -800,6 +808,15 @@  static inline void restore_msa_fp_status(CPUMIPSState *env)
     set_flush_inputs_to_zero(flush_to_zero, status);
 }
 
+static inline void restore_pamask(CPUMIPSState *env)
+{
+    if (env->hflags & MIPS_HFLAG_ELPA) {
+        env->PAMask = (1ULL << env->PABITS) - 1;
+    } else {
+        env->PAMask = DEFAULT_PAMASK;
+    }
+}
+
 static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, target_ulong *pc,
                                         target_ulong *cs_base, int *flags)
 {
@@ -847,7 +864,8 @@  static inline void compute_hflags(CPUMIPSState *env)
     env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 |
                      MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU |
                      MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 |
-                     MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE);
+                     MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE |
+                     MIPS_HFLAG_ELPA);
     if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
         !(env->CP0_Status & (1 << CP0St_ERL)) &&
         !(env->hflags & MIPS_HFLAG_DM)) {
@@ -933,6 +951,11 @@  static inline void compute_hflags(CPUMIPSState *env)
             env->hflags |= MIPS_HFLAG_FRE;
         }
     }
+    if (env->CP0_Config3 & (1 << CP0C3_LPA)) {
+        if (env->CP0_PageGrain & (1 << CP0PG_ELPA)) {
+            env->hflags |= MIPS_HFLAG_ELPA;
+        }
+    }
 }
 
 #ifndef CONFIG_USER_ONLY
diff --git a/target-mips/machine.c b/target-mips/machine.c
index 559402c..8fa755c 100644
--- a/target-mips/machine.c
+++ b/target-mips/machine.c
@@ -10,6 +10,7 @@  static int cpu_post_load(void *opaque, int version_id)
     restore_fp_status(env);
     restore_msa_fp_status(env);
     compute_hflags(env);
+    restore_pamask(env);
 
     return 0;
 }
diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h
index 1784227..20aa87c 100644
--- a/target-mips/mips-defs.h
+++ b/target-mips/mips-defs.h
@@ -10,11 +10,11 @@ 
 
 #if defined(TARGET_MIPS64)
 #define TARGET_LONG_BITS 64
-#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#define TARGET_PHYS_ADDR_SPACE_BITS 48
 #define TARGET_VIRT_ADDR_SPACE_BITS 42
 #else
 #define TARGET_LONG_BITS 32
-#define TARGET_PHYS_ADDR_SPACE_BITS 36
+#define TARGET_PHYS_ADDR_SPACE_BITS 40
 #define TARGET_VIRT_ADDR_SPACE_BITS 32
 #endif
 
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 6bff927..4b1b0ec 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -1067,19 +1067,28 @@  void helper_mtc0_vpeopt(CPUMIPSState *env, target_ulong arg1)
     env->CP0_VPEOpt = arg1 & 0x0000ffff;
 }
 
+static inline target_ulong get_mtc0_entrylo_mask(const CPUMIPSState *env)
+{
+#if defined(TARGET_MIPS64)
+    return env->PAMask >> 6;
+#else
+    return (env->PAMask >> 6) & 0x3FFFFFFF;
+#endif
+}
+
 void helper_mtc0_entrylo0(CPUMIPSState *env, target_ulong arg1)
 {
-    /* Large physaddr (PABITS) not implemented */
     /* 1k pages not implemented */
     target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE));
-    env->CP0_EntryLo0 = (arg1 & 0x3FFFFFFF) | (rxi << (CP0EnLo_XI - 30));
+    env->CP0_EntryLo0 = (arg1 & get_mtc0_entrylo_mask(env))
+                        | (rxi << (CP0EnLo_XI - 30));
 }
 
 #if defined(TARGET_MIPS64)
 void helper_dmtc0_entrylo0(CPUMIPSState *env, uint64_t arg1)
 {
     uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32);
-    env->CP0_EntryLo0 = (arg1 & 0x3FFFFFFF) | rxi;
+    env->CP0_EntryLo0 = (arg1 & get_mtc0_entrylo_mask(env)) | rxi;
 }
 #endif
 
@@ -1245,17 +1254,17 @@  void helper_mttc0_tcschefback(CPUMIPSState *env, target_ulong arg1)
 
 void helper_mtc0_entrylo1(CPUMIPSState *env, target_ulong arg1)
 {
-    /* Large physaddr (PABITS) not implemented */
     /* 1k pages not implemented */
     target_ulong rxi = arg1 & (env->CP0_PageGrain & (3u << CP0PG_XIE));
-    env->CP0_EntryLo1 = (arg1 & 0x3FFFFFFF) | (rxi << (CP0EnLo_XI - 30));
+    env->CP0_EntryLo1 = (arg1 & get_mtc0_entrylo_mask(env))
+                        | (rxi << (CP0EnLo_XI - 30));
 }
 
 #if defined(TARGET_MIPS64)
 void helper_dmtc0_entrylo1(CPUMIPSState *env, uint64_t arg1)
 {
     uint64_t rxi = arg1 & ((env->CP0_PageGrain & (3ull << CP0PG_XIE)) << 32);
-    env->CP0_EntryLo1 = (arg1 & 0x3FFFFFFF) | rxi;
+    env->CP0_EntryLo1 = (arg1 & get_mtc0_entrylo_mask(env)) | rxi;
 }
 #endif
 
@@ -1278,10 +1287,11 @@  void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1)
 void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1)
 {
     /* SmartMIPS not implemented */
-    /* Large physaddr (PABITS) not implemented */
     /* 1k pages not implemented */
     env->CP0_PageGrain = (arg1 & env->CP0_PageGrain_rw_bitmask) |
                          (env->CP0_PageGrain & ~env->CP0_PageGrain_rw_bitmask);
+    compute_hflags(env);
+    restore_pamask(env);
 }
 
 void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1)
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 0f875be..bb219ea 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -5666,6 +5666,7 @@  static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel)
             check_insn(ctx, ISA_MIPS32R2);
             gen_helper_mtc0_pagegrain(cpu_env, arg);
             rn = "PageGrain";
+            ctx->bstate = BS_STOP;
             break;
         default:
             goto cp0_unimplemented;
@@ -19526,7 +19527,6 @@  void cpu_state_reset(CPUMIPSState *env)
     }
 #endif
     env->PABITS = env->cpu_model->PABITS;
-    env->PAMask = (1ULL << env->cpu_model->PABITS) - 1;
     env->CP0_SRSConf0_rw_bitmask = env->cpu_model->CP0_SRSConf0_rw_bitmask;
     env->CP0_SRSConf0 = env->cpu_model->CP0_SRSConf0;
     env->CP0_SRSConf1_rw_bitmask = env->cpu_model->CP0_SRSConf1_rw_bitmask;
@@ -19647,6 +19647,7 @@  void cpu_state_reset(CPUMIPSState *env)
     compute_hflags(env);
     restore_rounding_mode(env);
     restore_flush_mode(env);
+    restore_pamask(env);
     cs->exception_index = EXCP_NONE;
 }