mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 16:23:55 -06:00
target/arm: Implement SEL2 physical and virtual timers
When FEAT_SEL2 was implemented the SEL2 timers were missed. This
shows up when building the latest Hafnium with SPMC_AT_EL=2. The
actual implementation utilises the same logic as the rest of the
timers so all we need to do is:
- define the timers and their access functions
- conditionally add the correct system registers
- create a new accessfn as the rules are subtly different to the
existing secure timer
Fixes: e9152ee91c
(target/arm: add ARMv8.4-SEL2 system registers)
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20250204125009.2281315-7-peter.maydell@linaro.org
Cc: qemu-stable@nongnu.org
Cc: Andrei Homescu <ahomescu@google.com>
Cc: Arve Hjønnevåg <arve@google.com>
Cc: Rémi Denis-Courmont <remi.denis.courmont@huawei.com>
[PMM: CP_ACCESS_TRAP_UNCATEGORIZED -> CP_ACCESS_UNDEFINED;
offset logic now in gt_{indirect,direct}_access_timer_offset() ]
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
02c648a0a1
commit
f9f99d7ca5
5 changed files with 174 additions and 1 deletions
|
@ -22,6 +22,8 @@
|
||||||
#define QEMU_ARM_BSA_H
|
#define QEMU_ARM_BSA_H
|
||||||
|
|
||||||
/* These are architectural INTID values */
|
/* These are architectural INTID values */
|
||||||
|
#define ARCH_TIMER_S_EL2_VIRT_IRQ 19
|
||||||
|
#define ARCH_TIMER_S_EL2_IRQ 20
|
||||||
#define VIRTUAL_PMU_IRQ 23
|
#define VIRTUAL_PMU_IRQ 23
|
||||||
#define ARCH_GIC_MAINT_IRQ 25
|
#define ARCH_GIC_MAINT_IRQ 25
|
||||||
#define ARCH_TIMER_NS_EL2_IRQ 26
|
#define ARCH_TIMER_NS_EL2_IRQ 26
|
||||||
|
|
|
@ -2069,6 +2069,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||||
arm_gt_stimer_cb, cpu);
|
arm_gt_stimer_cb, cpu);
|
||||||
cpu->gt_timer[GTIMER_HYPVIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
|
cpu->gt_timer[GTIMER_HYPVIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
|
||||||
arm_gt_hvtimer_cb, cpu);
|
arm_gt_hvtimer_cb, cpu);
|
||||||
|
cpu->gt_timer[GTIMER_S_EL2_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
|
||||||
|
arm_gt_sel2timer_cb, cpu);
|
||||||
|
cpu->gt_timer[GTIMER_S_EL2_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
|
||||||
|
arm_gt_sel2vtimer_cb, cpu);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1171,6 +1171,8 @@ void arm_gt_vtimer_cb(void *opaque);
|
||||||
void arm_gt_htimer_cb(void *opaque);
|
void arm_gt_htimer_cb(void *opaque);
|
||||||
void arm_gt_stimer_cb(void *opaque);
|
void arm_gt_stimer_cb(void *opaque);
|
||||||
void arm_gt_hvtimer_cb(void *opaque);
|
void arm_gt_hvtimer_cb(void *opaque);
|
||||||
|
void arm_gt_sel2timer_cb(void *opaque);
|
||||||
|
void arm_gt_sel2vtimer_cb(void *opaque);
|
||||||
|
|
||||||
unsigned int gt_cntfrq_period_ns(ARMCPU *cpu);
|
unsigned int gt_cntfrq_period_ns(ARMCPU *cpu);
|
||||||
void gt_rme_post_el_change(ARMCPU *cpu, void *opaque);
|
void gt_rme_post_el_change(ARMCPU *cpu, void *opaque);
|
||||||
|
|
|
@ -15,7 +15,9 @@ enum {
|
||||||
GTIMER_HYP = 2,
|
GTIMER_HYP = 2,
|
||||||
GTIMER_SEC = 3,
|
GTIMER_SEC = 3,
|
||||||
GTIMER_HYPVIRT = 4,
|
GTIMER_HYPVIRT = 4,
|
||||||
#define NUM_GTIMERS 5
|
GTIMER_S_EL2_PHYS = 5, /* CNTHPS_* ; only if FEAT_SEL2 */
|
||||||
|
GTIMER_S_EL2_VIRT = 6, /* CNTHVS_* ; only if FEAT_SEL2 */
|
||||||
|
#define NUM_GTIMERS 7
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2404,6 +2404,45 @@ static CPAccessResult gt_stimer_access(CPUARMState *env,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CPAccessResult gt_sel2timer_access(CPUARMState *env,
|
||||||
|
const ARMCPRegInfo *ri,
|
||||||
|
bool isread)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The AArch64 register view of the secure EL2 timers are mostly
|
||||||
|
* accessible from EL3 and EL2 although can also be trapped to EL2
|
||||||
|
* from EL1 depending on nested virt config.
|
||||||
|
*/
|
||||||
|
switch (arm_current_el(env)) {
|
||||||
|
case 0: /* UNDEFINED */
|
||||||
|
return CP_ACCESS_UNDEFINED;
|
||||||
|
case 1:
|
||||||
|
if (!arm_is_secure(env)) {
|
||||||
|
/* UNDEFINED */
|
||||||
|
return CP_ACCESS_UNDEFINED;
|
||||||
|
} else if (arm_hcr_el2_eff(env) & HCR_NV) {
|
||||||
|
/* Aarch64.SystemAccessTrap(EL2, 0x18) */
|
||||||
|
return CP_ACCESS_TRAP_EL2;
|
||||||
|
}
|
||||||
|
/* UNDEFINED */
|
||||||
|
return CP_ACCESS_UNDEFINED;
|
||||||
|
case 2:
|
||||||
|
if (!arm_is_secure(env)) {
|
||||||
|
/* UNDEFINED */
|
||||||
|
return CP_ACCESS_UNDEFINED;
|
||||||
|
}
|
||||||
|
return CP_ACCESS_OK;
|
||||||
|
case 3:
|
||||||
|
if (env->cp15.scr_el3 & SCR_EEL2) {
|
||||||
|
return CP_ACCESS_OK;
|
||||||
|
} else {
|
||||||
|
return CP_ACCESS_UNDEFINED;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t gt_get_countervalue(CPUARMState *env)
|
uint64_t gt_get_countervalue(CPUARMState *env)
|
||||||
{
|
{
|
||||||
ARMCPU *cpu = env_archcpu(env);
|
ARMCPU *cpu = env_archcpu(env);
|
||||||
|
@ -2475,6 +2514,8 @@ static uint64_t gt_indirect_access_timer_offset(CPUARMState *env, int timeridx)
|
||||||
case GTIMER_HYP:
|
case GTIMER_HYP:
|
||||||
case GTIMER_SEC:
|
case GTIMER_SEC:
|
||||||
case GTIMER_HYPVIRT:
|
case GTIMER_HYPVIRT:
|
||||||
|
case GTIMER_S_EL2_PHYS:
|
||||||
|
case GTIMER_S_EL2_VIRT:
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
|
@ -2521,6 +2562,8 @@ uint64_t gt_direct_access_timer_offset(CPUARMState *env, int timeridx)
|
||||||
case GTIMER_HYP:
|
case GTIMER_HYP:
|
||||||
case GTIMER_SEC:
|
case GTIMER_SEC:
|
||||||
case GTIMER_HYPVIRT:
|
case GTIMER_HYPVIRT:
|
||||||
|
case GTIMER_S_EL2_PHYS:
|
||||||
|
case GTIMER_S_EL2_VIRT:
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
|
@ -2953,6 +2996,62 @@ static void gt_sec_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
gt_ctl_write(env, ri, GTIMER_SEC, value);
|
gt_ctl_write(env, ri, GTIMER_SEC, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gt_sec_pel2_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
|
{
|
||||||
|
gt_timer_reset(env, ri, GTIMER_S_EL2_PHYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gt_sec_pel2_cval_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
gt_cval_write(env, ri, GTIMER_S_EL2_PHYS, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t gt_sec_pel2_tval_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
|
{
|
||||||
|
return gt_tval_read(env, ri, GTIMER_S_EL2_PHYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gt_sec_pel2_tval_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
gt_tval_write(env, ri, GTIMER_S_EL2_PHYS, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gt_sec_pel2_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
gt_ctl_write(env, ri, GTIMER_S_EL2_PHYS, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gt_sec_vel2_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
|
{
|
||||||
|
gt_timer_reset(env, ri, GTIMER_S_EL2_VIRT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gt_sec_vel2_cval_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
gt_cval_write(env, ri, GTIMER_S_EL2_VIRT, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint64_t gt_sec_vel2_tval_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
|
{
|
||||||
|
return gt_tval_read(env, ri, GTIMER_S_EL2_VIRT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gt_sec_vel2_tval_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
gt_tval_write(env, ri, GTIMER_S_EL2_VIRT, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gt_sec_vel2_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
uint64_t value)
|
||||||
|
{
|
||||||
|
gt_ctl_write(env, ri, GTIMER_S_EL2_VIRT, value);
|
||||||
|
}
|
||||||
|
|
||||||
static void gt_hv_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
static void gt_hv_timer_reset(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||||
{
|
{
|
||||||
gt_timer_reset(env, ri, GTIMER_HYPVIRT);
|
gt_timer_reset(env, ri, GTIMER_HYPVIRT);
|
||||||
|
@ -3009,6 +3108,20 @@ void arm_gt_stimer_cb(void *opaque)
|
||||||
gt_recalc_timer(cpu, GTIMER_SEC);
|
gt_recalc_timer(cpu, GTIMER_SEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void arm_gt_sel2timer_cb(void *opaque)
|
||||||
|
{
|
||||||
|
ARMCPU *cpu = opaque;
|
||||||
|
|
||||||
|
gt_recalc_timer(cpu, GTIMER_S_EL2_PHYS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void arm_gt_sel2vtimer_cb(void *opaque)
|
||||||
|
{
|
||||||
|
ARMCPU *cpu = opaque;
|
||||||
|
|
||||||
|
gt_recalc_timer(cpu, GTIMER_S_EL2_VIRT);
|
||||||
|
}
|
||||||
|
|
||||||
void arm_gt_hvtimer_cb(void *opaque)
|
void arm_gt_hvtimer_cb(void *opaque)
|
||||||
{
|
{
|
||||||
ARMCPU *cpu = opaque;
|
ARMCPU *cpu = opaque;
|
||||||
|
@ -5733,6 +5846,56 @@ static const ARMCPRegInfo el2_sec_cp_reginfo[] = {
|
||||||
.access = PL2_RW, .accessfn = sel2_access,
|
.access = PL2_RW, .accessfn = sel2_access,
|
||||||
.nv2_redirect_offset = 0x48,
|
.nv2_redirect_offset = 0x48,
|
||||||
.fieldoffset = offsetof(CPUARMState, cp15.vstcr_el2) },
|
.fieldoffset = offsetof(CPUARMState, cp15.vstcr_el2) },
|
||||||
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
/* Secure EL2 Physical Timer */
|
||||||
|
{ .name = "CNTHPS_TVAL_EL2", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 0,
|
||||||
|
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW,
|
||||||
|
.accessfn = gt_sel2timer_access,
|
||||||
|
.readfn = gt_sec_pel2_tval_read,
|
||||||
|
.writefn = gt_sec_pel2_tval_write,
|
||||||
|
.resetfn = gt_sec_pel2_timer_reset,
|
||||||
|
},
|
||||||
|
{ .name = "CNTHPS_CTL_EL2", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 1,
|
||||||
|
.type = ARM_CP_IO, .access = PL2_RW,
|
||||||
|
.accessfn = gt_sel2timer_access,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_PHYS].ctl),
|
||||||
|
.resetvalue = 0,
|
||||||
|
.writefn = gt_sec_pel2_ctl_write, .raw_writefn = raw_write,
|
||||||
|
},
|
||||||
|
{ .name = "CNTHPS_CVAL_EL2", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 5, .opc2 = 2,
|
||||||
|
.type = ARM_CP_IO, .access = PL2_RW,
|
||||||
|
.accessfn = gt_sel2timer_access,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_PHYS].cval),
|
||||||
|
.writefn = gt_sec_pel2_cval_write, .raw_writefn = raw_write,
|
||||||
|
},
|
||||||
|
/* Secure EL2 Virtual Timer */
|
||||||
|
{ .name = "CNTHVS_TVAL_EL2", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 0,
|
||||||
|
.type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL2_RW,
|
||||||
|
.accessfn = gt_sel2timer_access,
|
||||||
|
.readfn = gt_sec_vel2_tval_read,
|
||||||
|
.writefn = gt_sec_vel2_tval_write,
|
||||||
|
.resetfn = gt_sec_vel2_timer_reset,
|
||||||
|
},
|
||||||
|
{ .name = "CNTHVS_CTL_EL2", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 1,
|
||||||
|
.type = ARM_CP_IO, .access = PL2_RW,
|
||||||
|
.accessfn = gt_sel2timer_access,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_VIRT].ctl),
|
||||||
|
.resetvalue = 0,
|
||||||
|
.writefn = gt_sec_vel2_ctl_write, .raw_writefn = raw_write,
|
||||||
|
},
|
||||||
|
{ .name = "CNTHVS_CVAL_EL2", .state = ARM_CP_STATE_AA64,
|
||||||
|
.opc0 = 3, .opc1 = 4, .crn = 14, .crm = 4, .opc2 = 2,
|
||||||
|
.type = ARM_CP_IO, .access = PL2_RW,
|
||||||
|
.accessfn = gt_sel2timer_access,
|
||||||
|
.fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_S_EL2_VIRT].cval),
|
||||||
|
.writefn = gt_sec_vel2_cval_write, .raw_writefn = raw_write,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue