target/arm: Add VHE timer register redirection and aliasing
Apart from the wholesale redirection that HCR_EL2.E2H performs for EL2, there's a separate redirection specific to the timers that happens for EL0 when running in the EL2&0 regime. Tested-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200206105448.4726-30-richard.henderson@linaro.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
e2cce18f5c
commit
bb5972e439
@ -2695,6 +2695,70 @@ static void gt_phys_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
gt_ctl_write(env, ri, GTIMER_PHYS, value);
|
||||
}
|
||||
|
||||
static int gt_phys_redir_timeridx(CPUARMState *env)
|
||||
{
|
||||
switch (arm_mmu_idx(env)) {
|
||||
case ARMMMUIdx_E20_0:
|
||||
case ARMMMUIdx_E20_2:
|
||||
return GTIMER_HYP;
|
||||
default:
|
||||
return GTIMER_PHYS;
|
||||
}
|
||||
}
|
||||
|
||||
static int gt_virt_redir_timeridx(CPUARMState *env)
|
||||
{
|
||||
switch (arm_mmu_idx(env)) {
|
||||
case ARMMMUIdx_E20_0:
|
||||
case ARMMMUIdx_E20_2:
|
||||
return GTIMER_HYPVIRT;
|
||||
default:
|
||||
return GTIMER_VIRT;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t gt_phys_redir_cval_read(CPUARMState *env,
|
||||
const ARMCPRegInfo *ri)
|
||||
{
|
||||
int timeridx = gt_phys_redir_timeridx(env);
|
||||
return env->cp15.c14_timer[timeridx].cval;
|
||||
}
|
||||
|
||||
static void gt_phys_redir_cval_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
int timeridx = gt_phys_redir_timeridx(env);
|
||||
gt_cval_write(env, ri, timeridx, value);
|
||||
}
|
||||
|
||||
static uint64_t gt_phys_redir_tval_read(CPUARMState *env,
|
||||
const ARMCPRegInfo *ri)
|
||||
{
|
||||
int timeridx = gt_phys_redir_timeridx(env);
|
||||
return gt_tval_read(env, ri, timeridx);
|
||||
}
|
||||
|
||||
static void gt_phys_redir_tval_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
int timeridx = gt_phys_redir_timeridx(env);
|
||||
gt_tval_write(env, ri, timeridx, value);
|
||||
}
|
||||
|
||||
static uint64_t gt_phys_redir_ctl_read(CPUARMState *env,
|
||||
const ARMCPRegInfo *ri)
|
||||
{
|
||||
int timeridx = gt_phys_redir_timeridx(env);
|
||||
return env->cp15.c14_timer[timeridx].ctl;
|
||||
}
|
||||
|
||||
static void gt_phys_redir_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
int timeridx = gt_phys_redir_timeridx(env);
|
||||
gt_ctl_write(env, ri, timeridx, value);
|
||||
}
|
||||
|
||||
static void gt_virt_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
gt_timer_reset(env, ri, GTIMER_VIRT);
|
||||
@ -2733,6 +2797,48 @@ static void gt_cntvoff_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
gt_recalc_timer(cpu, GTIMER_VIRT);
|
||||
}
|
||||
|
||||
static uint64_t gt_virt_redir_cval_read(CPUARMState *env,
|
||||
const ARMCPRegInfo *ri)
|
||||
{
|
||||
int timeridx = gt_virt_redir_timeridx(env);
|
||||
return env->cp15.c14_timer[timeridx].cval;
|
||||
}
|
||||
|
||||
static void gt_virt_redir_cval_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
int timeridx = gt_virt_redir_timeridx(env);
|
||||
gt_cval_write(env, ri, timeridx, value);
|
||||
}
|
||||
|
||||
static uint64_t gt_virt_redir_tval_read(CPUARMState *env,
|
||||
const ARMCPRegInfo *ri)
|
||||
{
|
||||
int timeridx = gt_virt_redir_timeridx(env);
|
||||
return gt_tval_read(env, ri, timeridx);
|
||||
}
|
||||
|
||||
static void gt_virt_redir_tval_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
int timeridx = gt_virt_redir_timeridx(env);
|
||||
gt_tval_write(env, ri, timeridx, value);
|
||||
}
|
||||
|
||||
static uint64_t gt_virt_redir_ctl_read(CPUARMState *env,
|
||||
const ARMCPRegInfo *ri)
|
||||
{
|
||||
int timeridx = gt_virt_redir_timeridx(env);
|
||||
return env->cp15.c14_timer[timeridx].ctl;
|
||||
}
|
||||
|
||||
static void gt_virt_redir_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
uint64_t value)
|
||||
{
|
||||
int timeridx = gt_virt_redir_timeridx(env);
|
||||
gt_ctl_write(env, ri, timeridx, value);
|
||||
}
|
||||
|
||||
static void gt_hyp_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
gt_timer_reset(env, ri, GTIMER_HYP);
|
||||
@ -2889,7 +2995,8 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.accessfn = gt_ptimer_access,
|
||||
.fieldoffset = offsetoflow32(CPUARMState,
|
||||
cp15.c14_timer[GTIMER_PHYS].ctl),
|
||||
.writefn = gt_phys_ctl_write, .raw_writefn = raw_write,
|
||||
.readfn = gt_phys_redir_ctl_read, .raw_readfn = raw_read,
|
||||
.writefn = gt_phys_redir_ctl_write, .raw_writefn = raw_write,
|
||||
},
|
||||
{ .name = "CNTP_CTL_S",
|
||||
.cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 1,
|
||||
@ -2906,14 +3013,16 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.accessfn = gt_ptimer_access,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl),
|
||||
.resetvalue = 0,
|
||||
.writefn = gt_phys_ctl_write, .raw_writefn = raw_write,
|
||||
.readfn = gt_phys_redir_ctl_read, .raw_readfn = raw_read,
|
||||
.writefn = gt_phys_redir_ctl_write, .raw_writefn = raw_write,
|
||||
},
|
||||
{ .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 1,
|
||||
.type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL0_RW,
|
||||
.accessfn = gt_vtimer_access,
|
||||
.fieldoffset = offsetoflow32(CPUARMState,
|
||||
cp15.c14_timer[GTIMER_VIRT].ctl),
|
||||
.writefn = gt_virt_ctl_write, .raw_writefn = raw_write,
|
||||
.readfn = gt_virt_redir_ctl_read, .raw_readfn = raw_read,
|
||||
.writefn = gt_virt_redir_ctl_write, .raw_writefn = raw_write,
|
||||
},
|
||||
{ .name = "CNTV_CTL_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1,
|
||||
@ -2921,14 +3030,15 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.accessfn = gt_vtimer_access,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl),
|
||||
.resetvalue = 0,
|
||||
.writefn = gt_virt_ctl_write, .raw_writefn = raw_write,
|
||||
.readfn = gt_virt_redir_ctl_read, .raw_readfn = raw_read,
|
||||
.writefn = gt_virt_redir_ctl_write, .raw_writefn = raw_write,
|
||||
},
|
||||
/* TimerValue views: a 32 bit downcounting view of the underlying state */
|
||||
{ .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0,
|
||||
.secure = ARM_CP_SECSTATE_NS,
|
||||
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW,
|
||||
.accessfn = gt_ptimer_access,
|
||||
.readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write,
|
||||
.readfn = gt_phys_redir_tval_read, .writefn = gt_phys_redir_tval_write,
|
||||
},
|
||||
{ .name = "CNTP_TVAL_S",
|
||||
.cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 = 0,
|
||||
@ -2941,18 +3051,18 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 0,
|
||||
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW,
|
||||
.accessfn = gt_ptimer_access, .resetfn = gt_phys_timer_reset,
|
||||
.readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write,
|
||||
.readfn = gt_phys_redir_tval_read, .writefn = gt_phys_redir_tval_write,
|
||||
},
|
||||
{ .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 = 0,
|
||||
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW,
|
||||
.accessfn = gt_vtimer_access,
|
||||
.readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write,
|
||||
.readfn = gt_virt_redir_tval_read, .writefn = gt_virt_redir_tval_write,
|
||||
},
|
||||
{ .name = "CNTV_TVAL_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 0,
|
||||
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW,
|
||||
.accessfn = gt_vtimer_access, .resetfn = gt_virt_timer_reset,
|
||||
.readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write,
|
||||
.readfn = gt_virt_redir_tval_read, .writefn = gt_virt_redir_tval_write,
|
||||
},
|
||||
/* The counter itself */
|
||||
{ .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0,
|
||||
@ -2982,7 +3092,8 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
|
||||
.accessfn = gt_ptimer_access,
|
||||
.writefn = gt_phys_cval_write, .raw_writefn = raw_write,
|
||||
.readfn = gt_phys_redir_cval_read, .raw_readfn = raw_read,
|
||||
.writefn = gt_phys_redir_cval_write, .raw_writefn = raw_write,
|
||||
},
|
||||
{ .name = "CNTP_CVAL_S", .cp = 15, .crm = 14, .opc1 = 2,
|
||||
.secure = ARM_CP_SECSTATE_S,
|
||||
@ -2998,14 +3109,16 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.type = ARM_CP_IO,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
|
||||
.resetvalue = 0, .accessfn = gt_ptimer_access,
|
||||
.writefn = gt_phys_cval_write, .raw_writefn = raw_write,
|
||||
.readfn = gt_phys_redir_cval_read, .raw_readfn = raw_read,
|
||||
.writefn = gt_phys_redir_cval_write, .raw_writefn = raw_write,
|
||||
},
|
||||
{ .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3,
|
||||
.access = PL0_RW,
|
||||
.type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
|
||||
.accessfn = gt_vtimer_access,
|
||||
.writefn = gt_virt_cval_write, .raw_writefn = raw_write,
|
||||
.readfn = gt_virt_redir_cval_read, .raw_readfn = raw_read,
|
||||
.writefn = gt_virt_redir_cval_write, .raw_writefn = raw_write,
|
||||
},
|
||||
{ .name = "CNTV_CVAL_EL0", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2,
|
||||
@ -3013,7 +3126,8 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.type = ARM_CP_IO,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
|
||||
.resetvalue = 0, .accessfn = gt_vtimer_access,
|
||||
.writefn = gt_virt_cval_write, .raw_writefn = raw_write,
|
||||
.readfn = gt_virt_redir_cval_read, .raw_readfn = raw_read,
|
||||
.writefn = gt_virt_redir_cval_write, .raw_writefn = raw_write,
|
||||
},
|
||||
/* Secure timer -- this is actually restricted to only EL3
|
||||
* and configurably Secure-EL1 via the accessfn.
|
||||
@ -3044,6 +3158,15 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
||||
static CPAccessResult e2h_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||
bool isread)
|
||||
{
|
||||
if (!(arm_hcr_el2_eff(env) & HCR_E2H)) {
|
||||
return CP_ACCESS_TRAP;
|
||||
}
|
||||
return CP_ACCESS_OK;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* In user-mode most of the generic timer registers are inaccessible
|
||||
@ -6431,6 +6554,40 @@ static const ARMCPRegInfo vhe_reginfo[] = {
|
||||
.access = PL2_RW,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_HYPVIRT].ctl),
|
||||
.writefn = gt_hv_ctl_write, .raw_writefn = raw_write },
|
||||
{ .name = "CNTP_CTL_EL02", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 1,
|
||||
.type = ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.access = PL2_RW, .accessfn = e2h_access,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl),
|
||||
.writefn = gt_phys_ctl_write, .raw_writefn = raw_write },
|
||||
{ .name = "CNTV_CTL_EL02", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 1,
|
||||
.type = ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.access = PL2_RW, .accessfn = e2h_access,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl),
|
||||
.writefn = gt_virt_ctl_write, .raw_writefn = raw_write },
|
||||
{ .name = "CNTP_TVAL_EL02", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 0,
|
||||
.type = ARM_CP_NO_RAW | ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.access = PL2_RW, .accessfn = e2h_access,
|
||||
.readfn = gt_phys_tval_read, .writefn = gt_phys_tval_write },
|
||||
{ .name = "CNTV_TVAL_EL02", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 0,
|
||||
.type = ARM_CP_NO_RAW | ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.access = PL2_RW, .accessfn = e2h_access,
|
||||
.readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write },
|
||||
{ .name = "CNTP_CVAL_EL02", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 2, .opc2 = 2,
|
||||
.type = ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
|
||||
.access = PL2_RW, .accessfn = e2h_access,
|
||||
.writefn = gt_phys_cval_write, .raw_writefn = raw_write },
|
||||
{ .name = "CNTV_CVAL_EL02", .state = ARM_CP_STATE_AA64,
|
||||
.opc0 = 3, .opc1 = 5, .crn = 14, .crm = 3, .opc2 = 2,
|
||||
.type = ARM_CP_IO | ARM_CP_ALIAS,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
|
||||
.access = PL2_RW, .accessfn = e2h_access,
|
||||
.writefn = gt_virt_cval_write, .raw_writefn = raw_write },
|
||||
#endif
|
||||
REGINFO_SENTINEL
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user