diff mbox

[19/24] target-arm: Implement AArch64 TTBR*

Message ID 1390335150-13470-20-git-send-email-peter.maydell@linaro.org
State New
Headers show

Commit Message

Peter Maydell Jan. 21, 2014, 8:12 p.m. UTC
Implement the AArch64 TTBR* registers. For v7 these were already 64 bits
to handle LPAE, but implemented as two separate uint32_t fields.
Combine them into a single uint64_t which can be used for all purposes.
Since this requires touching every use, take the opportunity to rename
the field to the architectural name.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/arm/pxa2xx.c     |  2 +-
 target-arm/cpu.h    |  6 ++--
 target-arm/helper.c | 95 +++++++++++++++++------------------------------------
 3 files changed, 33 insertions(+), 70 deletions(-)

Comments

Peter Crosthwaite Jan. 24, 2014, 11:44 p.m. UTC | #1
On Wed, Jan 22, 2014 at 6:12 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
> Implement the AArch64 TTBR* registers. For v7 these were already 64 bits
> to handle LPAE, but implemented as two separate uint32_t fields.
> Combine them into a single uint64_t which can be used for all purposes.
> Since this requires touching every use, take the opportunity to rename
> the field to the architectural name.
>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
> ---
>  hw/arm/pxa2xx.c     |  2 +-
>  target-arm/cpu.h    |  6 ++--
>  target-arm/helper.c | 95 +++++++++++++++++------------------------------------
>  3 files changed, 33 insertions(+), 70 deletions(-)
>
> diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
> index 02b7016..b6465d6 100644
> --- a/hw/arm/pxa2xx.c
> +++ b/hw/arm/pxa2xx.c
> @@ -279,7 +279,7 @@ static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
>              ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
>          s->cpu->env.cp15.c1_sys = 0;
>          s->cpu->env.cp15.c1_coproc = 0;
> -        s->cpu->env.cp15.c2_base0 = 0;
> +        s->cpu->env.cp15.ttbr0_el1 = 0;
>          s->cpu->env.cp15.c3 = 0;
>          s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
>          s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
> index 6f4d174..7138882 100644
> --- a/target-arm/cpu.h
> +++ b/target-arm/cpu.h
> @@ -173,10 +173,8 @@ typedef struct CPUARMState {
>          uint32_t c1_coproc; /* Coprocessor access register.  */
>          uint32_t c1_xscaleauxcr; /* XScale auxiliary control register.  */
>          uint32_t c1_scr; /* secure config register.  */
> -        uint32_t c2_base0; /* MMU translation table base 0.  */
> -        uint32_t c2_base0_hi; /* MMU translation table base 0, high 32 bits */
> -        uint32_t c2_base1; /* MMU translation table base 0.  */
> -        uint32_t c2_base1_hi; /* MMU translation table base 1, high 32 bits */
> +        uint64_t ttbr0_el1; /* MMU translation table base 0. */
> +        uint32_t ttbr1_el1; /* MMU translation table base 1. */
>          uint64_t c2_control; /* MMU translation table base control.  */
>          uint32_t c2_mask; /* MMU translation table base selection mask.  */
>          uint32_t c2_base_mask; /* MMU translation table base 0 mask. */
> diff --git a/target-arm/helper.c b/target-arm/helper.c
> index e2ae159..1f1dec1 100644
> --- a/target-arm/helper.c
> +++ b/target-arm/helper.c
> @@ -1288,6 +1288,18 @@ static int vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>      return 0;
>  }
>
> +static int vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
> +                             uint64_t value)
> +{
> +    /* 64 bit accesses to the TTBRs can change the ASID and so we
> +     * must flush the TLB.
> +     */
> +    if ((ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT)) {
> +        tlb_flush(env, 1);
> +    }

With the level of complexity this if has reached, is it better to just
check for this ASID change rather than make this overly conservative
flush?

Regards,
Peter

> +    return raw_write(env, ri, value);
> +}
> +
>  static const ARMCPRegInfo vmsa_cp_reginfo[] = {
>      { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
>        .access = PL1_RW,
> @@ -1295,12 +1307,14 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = {
>      { .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1,
>        .access = PL1_RW,
>        .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, },
> -    { .name = "TTBR0", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
> -      .access = PL1_RW,
> -      .fieldoffset = offsetof(CPUARMState, cp15.c2_base0), .resetvalue = 0, },
> -    { .name = "TTBR1", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1,
> -      .access = PL1_RW,
> -      .fieldoffset = offsetof(CPUARMState, cp15.c2_base1), .resetvalue = 0, },
> +    { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH,
> +      .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
> +      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1),
> +      .writefn = vmsa_ttbr_write, .resetvalue = 0 },
> +    { .name = "TTBR1_EL1", .state = ARM_CP_STATE_BOTH,
> +      .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1,
> +      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el1),
> +      .writefn = vmsa_ttbr_write, .resetvalue = 0 },
>      { .name = "TCR_EL1", .state = ARM_CP_STATE_AA64,
>        .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
>        .access = PL1_RW, .writefn = vmsa_tcr_el1_write,
> @@ -1525,56 +1539,6 @@ static void par64_reset(CPUARMState *env, const ARMCPRegInfo *ri)
>      env->cp15.c7_par = 0;
>  }
>
> -static int ttbr064_read(CPUARMState *env, const ARMCPRegInfo *ri,
> -                        uint64_t *value)
> -{
> -    *value = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
> -    return 0;
> -}
> -
> -static int ttbr064_raw_write(CPUARMState *env, const ARMCPRegInfo *ri,
> -                             uint64_t value)
> -{
> -    env->cp15.c2_base0_hi = value >> 32;
> -    env->cp15.c2_base0 = value;
> -    return 0;
> -}
> -
> -static int ttbr064_write(CPUARMState *env, const ARMCPRegInfo *ri,
> -                         uint64_t value)
> -{
> -    /* Writes to the 64 bit format TTBRs may change the ASID */
> -    tlb_flush(env, 1);
> -    return ttbr064_raw_write(env, ri, value);
> -}
> -
> -static void ttbr064_reset(CPUARMState *env, const ARMCPRegInfo *ri)
> -{
> -    env->cp15.c2_base0_hi = 0;
> -    env->cp15.c2_base0 = 0;
> -}
> -
> -static int ttbr164_read(CPUARMState *env, const ARMCPRegInfo *ri,
> -                        uint64_t *value)
> -{
> -    *value = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
> -    return 0;
> -}
> -
> -static int ttbr164_write(CPUARMState *env, const ARMCPRegInfo *ri,
> -                         uint64_t value)
> -{
> -    env->cp15.c2_base1_hi = value >> 32;
> -    env->cp15.c2_base1 = value;
> -    return 0;
> -}
> -
> -static void ttbr164_reset(CPUARMState *env, const ARMCPRegInfo *ri)
> -{
> -    env->cp15.c2_base1_hi = 0;
> -    env->cp15.c2_base1 = 0;
> -}
> -
>  static const ARMCPRegInfo lpae_cp_reginfo[] = {
>      /* NOP AMAIR0/1: the override is because these clash with the rather
>       * broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo.
> @@ -1596,12 +1560,13 @@ static const ARMCPRegInfo lpae_cp_reginfo[] = {
>        .access = PL1_RW, .type = ARM_CP_64BIT,
>        .readfn = par64_read, .writefn = par64_write, .resetfn = par64_reset },
>      { .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0,
> -      .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr064_read,
> -      .writefn = ttbr064_write, .raw_writefn = ttbr064_raw_write,
> -      .resetfn = ttbr064_reset },
> +      .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE,
> +      .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1),
> +      .writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore },
>      { .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1,
> -      .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr164_read,
> -      .writefn = ttbr164_write, .resetfn = ttbr164_reset },
> +      .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE,
> +      .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el1),
> +      .writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore },
>      REGINFO_SENTINEL
>  };
>
> @@ -3024,9 +2989,9 @@ static uint32_t get_level1_table_address(CPUARMState *env, uint32_t address)
>      uint32_t table;
>
>      if (address & env->cp15.c2_mask)
> -        table = env->cp15.c2_base1 & 0xffffc000;
> +        table = env->cp15.ttbr1_el1 & 0xffffc000;
>      else
> -        table = env->cp15.c2_base0 & env->cp15.c2_base_mask;
> +        table = env->cp15.ttbr0_el1 & env->cp15.c2_base_mask;
>
>      table |= (address >> 18) & 0x3ffc;
>      return table;
> @@ -3299,11 +3264,11 @@ static int get_phys_addr_lpae(CPUARMState *env, uint32_t address,
>       * we will always flush the TLB any time the ASID is changed).
>       */
>      if (ttbr_select == 0) {
> -        ttbr = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
> +        ttbr = env->cp15.ttbr0_el1;
>          epd = extract32(env->cp15.c2_control, 7, 1);
>          tsz = t0sz;
>      } else {
> -        ttbr = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
> +        ttbr = env->cp15.ttbr1_el1;
>          epd = extract32(env->cp15.c2_control, 23, 1);
>          tsz = t1sz;
>      }
> --
> 1.8.5
>
>
Peter Maydell Jan. 25, 2014, 12:09 a.m. UTC | #2
On 24 January 2014 23:44, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Wed, Jan 22, 2014 at 6:12 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>> Implement the AArch64 TTBR* registers. For v7 these were already 64 bits
>> to handle LPAE, but implemented as two separate uint32_t fields.
>> Combine them into a single uint64_t which can be used for all purposes.
>> Since this requires touching every use, take the opportunity to rename
>> the field to the architectural name.
>>
>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>> ---
>>  hw/arm/pxa2xx.c     |  2 +-
>>  target-arm/cpu.h    |  6 ++--
>>  target-arm/helper.c | 95 +++++++++++++++++------------------------------------
>>  3 files changed, 33 insertions(+), 70 deletions(-)
>>
>> diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
>> index 02b7016..b6465d6 100644
>> --- a/hw/arm/pxa2xx.c
>> +++ b/hw/arm/pxa2xx.c
>> @@ -279,7 +279,7 @@ static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>              ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
>>          s->cpu->env.cp15.c1_sys = 0;
>>          s->cpu->env.cp15.c1_coproc = 0;
>> -        s->cpu->env.cp15.c2_base0 = 0;
>> +        s->cpu->env.cp15.ttbr0_el1 = 0;
>>          s->cpu->env.cp15.c3 = 0;
>>          s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
>>          s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
>> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
>> index 6f4d174..7138882 100644
>> --- a/target-arm/cpu.h
>> +++ b/target-arm/cpu.h
>> @@ -173,10 +173,8 @@ typedef struct CPUARMState {
>>          uint32_t c1_coproc; /* Coprocessor access register.  */
>>          uint32_t c1_xscaleauxcr; /* XScale auxiliary control register.  */
>>          uint32_t c1_scr; /* secure config register.  */
>> -        uint32_t c2_base0; /* MMU translation table base 0.  */
>> -        uint32_t c2_base0_hi; /* MMU translation table base 0, high 32 bits */
>> -        uint32_t c2_base1; /* MMU translation table base 0.  */
>> -        uint32_t c2_base1_hi; /* MMU translation table base 1, high 32 bits */
>> +        uint64_t ttbr0_el1; /* MMU translation table base 0. */
>> +        uint32_t ttbr1_el1; /* MMU translation table base 1. */
>>          uint64_t c2_control; /* MMU translation table base control.  */
>>          uint32_t c2_mask; /* MMU translation table base selection mask.  */
>>          uint32_t c2_base_mask; /* MMU translation table base 0 mask. */
>> diff --git a/target-arm/helper.c b/target-arm/helper.c
>> index e2ae159..1f1dec1 100644
>> --- a/target-arm/helper.c
>> +++ b/target-arm/helper.c
>> @@ -1288,6 +1288,18 @@ static int vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>      return 0;
>>  }
>>
>> +static int vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>> +                             uint64_t value)
>> +{
>> +    /* 64 bit accesses to the TTBRs can change the ASID and so we
>> +     * must flush the TLB.
>> +     */
>> +    if ((ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT)) {
>> +        tlb_flush(env, 1);
>> +    }
>
> With the level of complexity this if has reached, is it better to just
> check for this ASID change rather than make this overly conservative
> flush?

Adding an "is the ASID the same" would make the check more complicated
again. Maybe we should do it but I'd rather keep that separate from the
"handle 64 bit formats" patches.

thanks
-- PMM
Peter Crosthwaite Jan. 28, 2014, 2:07 a.m. UTC | #3
On Sat, Jan 25, 2014 at 10:09 AM, Peter Maydell
<peter.maydell@linaro.org> wrote:
> On 24 January 2014 23:44, Peter Crosthwaite
> <peter.crosthwaite@xilinx.com> wrote:
>> On Wed, Jan 22, 2014 at 6:12 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>>> Implement the AArch64 TTBR* registers. For v7 these were already 64 bits
>>> to handle LPAE, but implemented as two separate uint32_t fields.
>>> Combine them into a single uint64_t which can be used for all purposes.
>>> Since this requires touching every use, take the opportunity to rename
>>> the field to the architectural name.
>>>
>>> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
>>> ---
>>>  hw/arm/pxa2xx.c     |  2 +-
>>>  target-arm/cpu.h    |  6 ++--
>>>  target-arm/helper.c | 95 +++++++++++++++++------------------------------------
>>>  3 files changed, 33 insertions(+), 70 deletions(-)
>>>
>>> diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
>>> index 02b7016..b6465d6 100644
>>> --- a/hw/arm/pxa2xx.c
>>> +++ b/hw/arm/pxa2xx.c
>>> @@ -279,7 +279,7 @@ static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>>              ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
>>>          s->cpu->env.cp15.c1_sys = 0;
>>>          s->cpu->env.cp15.c1_coproc = 0;
>>> -        s->cpu->env.cp15.c2_base0 = 0;
>>> +        s->cpu->env.cp15.ttbr0_el1 = 0;
>>>          s->cpu->env.cp15.c3 = 0;
>>>          s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
>>>          s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
>>> diff --git a/target-arm/cpu.h b/target-arm/cpu.h
>>> index 6f4d174..7138882 100644
>>> --- a/target-arm/cpu.h
>>> +++ b/target-arm/cpu.h
>>> @@ -173,10 +173,8 @@ typedef struct CPUARMState {
>>>          uint32_t c1_coproc; /* Coprocessor access register.  */
>>>          uint32_t c1_xscaleauxcr; /* XScale auxiliary control register.  */
>>>          uint32_t c1_scr; /* secure config register.  */
>>> -        uint32_t c2_base0; /* MMU translation table base 0.  */
>>> -        uint32_t c2_base0_hi; /* MMU translation table base 0, high 32 bits */
>>> -        uint32_t c2_base1; /* MMU translation table base 0.  */
>>> -        uint32_t c2_base1_hi; /* MMU translation table base 1, high 32 bits */
>>> +        uint64_t ttbr0_el1; /* MMU translation table base 0. */
>>> +        uint32_t ttbr1_el1; /* MMU translation table base 1. */
>>>          uint64_t c2_control; /* MMU translation table base control.  */
>>>          uint32_t c2_mask; /* MMU translation table base selection mask.  */
>>>          uint32_t c2_base_mask; /* MMU translation table base 0 mask. */
>>> diff --git a/target-arm/helper.c b/target-arm/helper.c
>>> index e2ae159..1f1dec1 100644
>>> --- a/target-arm/helper.c
>>> +++ b/target-arm/helper.c
>>> @@ -1288,6 +1288,18 @@ static int vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>>      return 0;
>>>  }
>>>
>>> +static int vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>> +                             uint64_t value)
>>> +{
>>> +    /* 64 bit accesses to the TTBRs can change the ASID and so we
>>> +     * must flush the TLB.
>>> +     */
>>> +    if ((ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT)) {
>>> +        tlb_flush(env, 1);
>>> +    }
>>
>> With the level of complexity this if has reached, is it better to just
>> check for this ASID change rather than make this overly conservative
>> flush?
>
> Adding an "is the ASID the same" would make the check more complicated
> again. Maybe we should do it but I'd rather keep that separate from the
> "handle 64 bit formats" patches.
>

Seems similar compexity to me. But I guess this preserves existing behaviour so:

Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>

> thanks
> -- PMM
>
Peter Maydell Jan. 28, 2014, 8:58 a.m. UTC | #4
On 28 January 2014 02:07, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
> On Sat, Jan 25, 2014 at 10:09 AM, Peter Maydell
> <peter.maydell@linaro.org> wrote:
>> On 24 January 2014 23:44, Peter Crosthwaite
>> <peter.crosthwaite@xilinx.com> wrote:
>>> On Wed, Jan 22, 2014 at 6:12 AM, Peter Maydell <peter.maydell@linaro.org> wrote:
>>>> +static int vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>>>> +                             uint64_t value)
>>>> +{
>>>> +    /* 64 bit accesses to the TTBRs can change the ASID and so we
>>>> +     * must flush the TLB.
>>>> +     */
>>>> +    if ((ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT)) {
>>>> +        tlb_flush(env, 1);
>>>> +    }
>>>
>>> With the level of complexity this if has reached, is it better to just
>>> check for this ASID change rather than make this overly conservative
>>> flush?
>>
>> Adding an "is the ASID the same" would make the check more complicated
>> again. Maybe we should do it but I'd rather keep that separate from the
>> "handle 64 bit formats" patches.
>>
>
> Seems similar compexity to me.

Well, you'd need all the checks we have here, plus another condition
for the ASID match:

  if (((ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT))
       && (value & some_mask != old_value & some_mask)) {
        tlb_flush(env, 1);
   }

thanks
-- PMM
Peter Maydell Jan. 28, 2014, 11:42 a.m. UTC | #5
On 24 January 2014 23:44, Peter Crosthwaite
<peter.crosthwaite@xilinx.com> wrote:
>> +static int vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
>> +                             uint64_t value)
>> +{
>> +    /* 64 bit accesses to the TTBRs can change the ASID and so we
>> +     * must flush the TLB.
>> +     */
>> +    if ((ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT)) {
>> +        tlb_flush(env, 1);
>> +    }
>
> With the level of complexity this if has reached, is it better to just
> check for this ASID change rather than make this overly conservative
> flush?

I just did a test boot on an A15 guest (which uses the long TTBR
format) and in a Linux boot to the login prompt it did 9097
TTBR writes; just six of those involved no change to the ASID.
So it doesn't seem really worth making the check to me.

thanks
-- PMM
diff mbox

Patch

diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index 02b7016..b6465d6 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -279,7 +279,7 @@  static int pxa2xx_pwrmode_write(CPUARMState *env, const ARMCPRegInfo *ri,
             ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
         s->cpu->env.cp15.c1_sys = 0;
         s->cpu->env.cp15.c1_coproc = 0;
-        s->cpu->env.cp15.c2_base0 = 0;
+        s->cpu->env.cp15.ttbr0_el1 = 0;
         s->cpu->env.cp15.c3 = 0;
         s->pm_regs[PSSR >> 2] |= 0x8; /* Set STS */
         s->pm_regs[RCSR >> 2] |= 0x8; /* Set GPR */
diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index 6f4d174..7138882 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -173,10 +173,8 @@  typedef struct CPUARMState {
         uint32_t c1_coproc; /* Coprocessor access register.  */
         uint32_t c1_xscaleauxcr; /* XScale auxiliary control register.  */
         uint32_t c1_scr; /* secure config register.  */
-        uint32_t c2_base0; /* MMU translation table base 0.  */
-        uint32_t c2_base0_hi; /* MMU translation table base 0, high 32 bits */
-        uint32_t c2_base1; /* MMU translation table base 0.  */
-        uint32_t c2_base1_hi; /* MMU translation table base 1, high 32 bits */
+        uint64_t ttbr0_el1; /* MMU translation table base 0. */
+        uint32_t ttbr1_el1; /* MMU translation table base 1. */
         uint64_t c2_control; /* MMU translation table base control.  */
         uint32_t c2_mask; /* MMU translation table base selection mask.  */
         uint32_t c2_base_mask; /* MMU translation table base 0 mask. */
diff --git a/target-arm/helper.c b/target-arm/helper.c
index e2ae159..1f1dec1 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -1288,6 +1288,18 @@  static int vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
     return 0;
 }
 
+static int vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri,
+                             uint64_t value)
+{
+    /* 64 bit accesses to the TTBRs can change the ASID and so we
+     * must flush the TLB.
+     */
+    if ((ri->state == ARM_CP_STATE_AA64) || (ri->type & ARM_CP_64BIT)) {
+        tlb_flush(env, 1);
+    }
+    return raw_write(env, ri, value);
+}
+
 static const ARMCPRegInfo vmsa_cp_reginfo[] = {
     { .name = "DFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 0,
       .access = PL1_RW,
@@ -1295,12 +1307,14 @@  static const ARMCPRegInfo vmsa_cp_reginfo[] = {
     { .name = "IFSR", .cp = 15, .crn = 5, .crm = 0, .opc1 = 0, .opc2 = 1,
       .access = PL1_RW,
       .fieldoffset = offsetof(CPUARMState, cp15.c5_insn), .resetvalue = 0, },
-    { .name = "TTBR0", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
-      .access = PL1_RW,
-      .fieldoffset = offsetof(CPUARMState, cp15.c2_base0), .resetvalue = 0, },
-    { .name = "TTBR1", .cp = 15, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1,
-      .access = PL1_RW,
-      .fieldoffset = offsetof(CPUARMState, cp15.c2_base1), .resetvalue = 0, },
+    { .name = "TTBR0_EL1", .state = ARM_CP_STATE_BOTH,
+      .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0,
+      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1),
+      .writefn = vmsa_ttbr_write, .resetvalue = 0 },
+    { .name = "TTBR1_EL1", .state = ARM_CP_STATE_BOTH,
+      .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 1,
+      .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el1),
+      .writefn = vmsa_ttbr_write, .resetvalue = 0 },
     { .name = "TCR_EL1", .state = ARM_CP_STATE_AA64,
       .opc0 = 3, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 2,
       .access = PL1_RW, .writefn = vmsa_tcr_el1_write,
@@ -1525,56 +1539,6 @@  static void par64_reset(CPUARMState *env, const ARMCPRegInfo *ri)
     env->cp15.c7_par = 0;
 }
 
-static int ttbr064_read(CPUARMState *env, const ARMCPRegInfo *ri,
-                        uint64_t *value)
-{
-    *value = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
-    return 0;
-}
-
-static int ttbr064_raw_write(CPUARMState *env, const ARMCPRegInfo *ri,
-                             uint64_t value)
-{
-    env->cp15.c2_base0_hi = value >> 32;
-    env->cp15.c2_base0 = value;
-    return 0;
-}
-
-static int ttbr064_write(CPUARMState *env, const ARMCPRegInfo *ri,
-                         uint64_t value)
-{
-    /* Writes to the 64 bit format TTBRs may change the ASID */
-    tlb_flush(env, 1);
-    return ttbr064_raw_write(env, ri, value);
-}
-
-static void ttbr064_reset(CPUARMState *env, const ARMCPRegInfo *ri)
-{
-    env->cp15.c2_base0_hi = 0;
-    env->cp15.c2_base0 = 0;
-}
-
-static int ttbr164_read(CPUARMState *env, const ARMCPRegInfo *ri,
-                        uint64_t *value)
-{
-    *value = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
-    return 0;
-}
-
-static int ttbr164_write(CPUARMState *env, const ARMCPRegInfo *ri,
-                         uint64_t value)
-{
-    env->cp15.c2_base1_hi = value >> 32;
-    env->cp15.c2_base1 = value;
-    return 0;
-}
-
-static void ttbr164_reset(CPUARMState *env, const ARMCPRegInfo *ri)
-{
-    env->cp15.c2_base1_hi = 0;
-    env->cp15.c2_base1 = 0;
-}
-
 static const ARMCPRegInfo lpae_cp_reginfo[] = {
     /* NOP AMAIR0/1: the override is because these clash with the rather
      * broadly specified TLB_LOCKDOWN entry in the generic cp_reginfo.
@@ -1596,12 +1560,13 @@  static const ARMCPRegInfo lpae_cp_reginfo[] = {
       .access = PL1_RW, .type = ARM_CP_64BIT,
       .readfn = par64_read, .writefn = par64_write, .resetfn = par64_reset },
     { .name = "TTBR0", .cp = 15, .crm = 2, .opc1 = 0,
-      .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr064_read,
-      .writefn = ttbr064_write, .raw_writefn = ttbr064_raw_write,
-      .resetfn = ttbr064_reset },
+      .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE,
+      .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el1),
+      .writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore },
     { .name = "TTBR1", .cp = 15, .crm = 2, .opc1 = 1,
-      .access = PL1_RW, .type = ARM_CP_64BIT, .readfn = ttbr164_read,
-      .writefn = ttbr164_write, .resetfn = ttbr164_reset },
+      .access = PL1_RW, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE,
+      .fieldoffset = offsetof(CPUARMState, cp15.ttbr1_el1),
+      .writefn = vmsa_ttbr_write, .resetfn = arm_cp_reset_ignore },
     REGINFO_SENTINEL
 };
 
@@ -3024,9 +2989,9 @@  static uint32_t get_level1_table_address(CPUARMState *env, uint32_t address)
     uint32_t table;
 
     if (address & env->cp15.c2_mask)
-        table = env->cp15.c2_base1 & 0xffffc000;
+        table = env->cp15.ttbr1_el1 & 0xffffc000;
     else
-        table = env->cp15.c2_base0 & env->cp15.c2_base_mask;
+        table = env->cp15.ttbr0_el1 & env->cp15.c2_base_mask;
 
     table |= (address >> 18) & 0x3ffc;
     return table;
@@ -3299,11 +3264,11 @@  static int get_phys_addr_lpae(CPUARMState *env, uint32_t address,
      * we will always flush the TLB any time the ASID is changed).
      */
     if (ttbr_select == 0) {
-        ttbr = ((uint64_t)env->cp15.c2_base0_hi << 32) | env->cp15.c2_base0;
+        ttbr = env->cp15.ttbr0_el1;
         epd = extract32(env->cp15.c2_control, 7, 1);
         tsz = t0sz;
     } else {
-        ttbr = ((uint64_t)env->cp15.c2_base1_hi << 32) | env->cp15.c2_base1;
+        ttbr = env->cp15.ttbr1_el1;
         epd = extract32(env->cp15.c2_control, 23, 1);
         tsz = t1sz;
     }