target-arm queue:
* Support emulating the generic timers at frequencies other than 62.5MHz * Various fixes for SMMUv3 emulation bugs * Improve assert error message for hflags mismatches * arm-powerctl: rebuild hflags after setting CP15 bits in arm_set_cpu_on() -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAl382e8ZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3uXjEACX9Gf7OUNusIwNqiiTL7St l+SD3tuiWPAFup5/0+5bN+SMEpbAjgqzCBdcMJztMBxMwmVnho1smjRHFzY+f5Cv pHWDU2V37W9ANgHEoyg4pNI8hyuDZ26yFvmxZ2R/qeDvVtTbHaxZ6yS8A85qq6vl WZm1NjiPUdQiO4QbJl9nnx4RsVLz3UrGxS23Cs8MQ7eKU28gFpauwDbKg8doDyfe +fC0MSJCQsugNklspde5aGeHCKWuBUNNHbGAmu23W8vdOWAENSQKnC9SGaAMW76g 8TjvISXMNWE4kCh1nQYiTqpQxv0QQ7MuTctG1Mqf4xcJYUeBDA53euiaeyu8WfIE sOVEvf6G/y3i6tN2QbFGohwW4q77k3QErlnWFe1Viii3J4i62bGaeFLtmuPKohLh Oo8PCK4yRKoSUVsUaFY5cCkEVfeVuIGhNpsoL8IP+lK+/tUJeD6tjncHlRGMbt/u //oa5hgiTz+/qUfeQqebJrmyAjOhOH2gaQOzwHbg2GoPcuNA3qUbJkO8GnKNzxDh 2YntQbmv2QeAiBYb05Qdwo90IfIWnxoZpM5lXHsuK0/00hGomQPdGSl7+ofK/aCz G65rYYGUwi3tRTA/NOllBX2R5lFp7I9P1/l2bdjlhRuJIdGwbRUm/OIc53FoE4RU EBbugC2FW/dCncHEBXfySg== =n3MZ -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20191220' into staging target-arm queue: * Support emulating the generic timers at frequencies other than 62.5MHz * Various fixes for SMMUv3 emulation bugs * Improve assert error message for hflags mismatches * arm-powerctl: rebuild hflags after setting CP15 bits in arm_set_cpu_on() # gpg: Signature made Fri 20 Dec 2019 14:25:51 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20191220: arm/arm-powerctl: rebuild hflags after setting CP15 bits in arm_set_cpu_on() target/arm: Display helpful message when hflags mismatch hw/arm/smmuv3: Report F_STE_FETCH fault address in correct word position hw/arm/smmuv3: Use correct bit positions in EVT_SET_ADDR2 macro hw/arm/smmuv3: Align stream table base address to table size hw/arm/smmuv3: Check stream IDs against actual table LOG2SIZE hw/arm/smmuv3: Correct SMMU_BASE_ADDR_MASK value hw/arm/smmuv3: Apply address mask to linear strtab base address ast2600: Configure CNTFRQ at 1125MHz target/arm: Prepare generic timer for per-platform CNTFRQ target/arm: Abstract the generic timer frequency target/arm: Remove redundant scaling of nexttick Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
f17783e706
@ -250,6 +250,9 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
|
||||
object_property_set_int(OBJECT(&s->cpu[i]), aspeed_calc_affinity(i),
|
||||
"mp-affinity", &error_abort);
|
||||
|
||||
object_property_set_int(OBJECT(&s->cpu[i]), 1125000000, "cntfrq",
|
||||
&error_abort);
|
||||
|
||||
/*
|
||||
* TODO: the secondary CPUs are started and a boot helper
|
||||
* is needed when using -kernel
|
||||
|
@ -99,7 +99,7 @@ REG32(GERROR_IRQ_CFG2, 0x74)
|
||||
|
||||
#define A_STRTAB_BASE 0x80 /* 64b */
|
||||
|
||||
#define SMMU_BASE_ADDR_MASK 0xffffffffffe0
|
||||
#define SMMU_BASE_ADDR_MASK 0xfffffffffffc0
|
||||
|
||||
REG32(STRTAB_BASE_CFG, 0x88)
|
||||
FIELD(STRTAB_BASE_CFG, FMT, 16, 2)
|
||||
@ -461,8 +461,8 @@ typedef struct SMMUEventInfo {
|
||||
} while (0)
|
||||
#define EVT_SET_ADDR2(x, addr) \
|
||||
do { \
|
||||
(x)->word[7] = deposit32((x)->word[7], 3, 29, addr >> 16); \
|
||||
(x)->word[7] = deposit32((x)->word[7], 0, 16, addr & 0xffff);\
|
||||
(x)->word[7] = (uint32_t)(addr >> 32); \
|
||||
(x)->word[6] = (uint32_t)(addr & 0xffffffff); \
|
||||
} while (0)
|
||||
|
||||
void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event);
|
||||
|
@ -172,7 +172,7 @@ void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *info)
|
||||
case SMMU_EVT_F_STE_FETCH:
|
||||
EVT_SET_SSID(&evt, info->u.f_ste_fetch.ssid);
|
||||
EVT_SET_SSV(&evt, info->u.f_ste_fetch.ssv);
|
||||
EVT_SET_ADDR(&evt, info->u.f_ste_fetch.addr);
|
||||
EVT_SET_ADDR2(&evt, info->u.f_ste_fetch.addr);
|
||||
break;
|
||||
case SMMU_EVT_C_BAD_STE:
|
||||
EVT_SET_SSID(&evt, info->u.c_bad_ste.ssid);
|
||||
@ -376,21 +376,32 @@ bad_ste:
|
||||
static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste,
|
||||
SMMUEventInfo *event)
|
||||
{
|
||||
dma_addr_t addr;
|
||||
dma_addr_t addr, strtab_base;
|
||||
uint32_t log2size;
|
||||
int strtab_size_shift;
|
||||
int ret;
|
||||
|
||||
trace_smmuv3_find_ste(sid, s->features, s->sid_split);
|
||||
/* Check SID range */
|
||||
if (sid > (1 << SMMU_IDR1_SIDSIZE)) {
|
||||
log2size = FIELD_EX32(s->strtab_base_cfg, STRTAB_BASE_CFG, LOG2SIZE);
|
||||
/*
|
||||
* Check SID range against both guest-configured and implementation limits
|
||||
*/
|
||||
if (sid >= (1 << MIN(log2size, SMMU_IDR1_SIDSIZE))) {
|
||||
event->type = SMMU_EVT_C_BAD_STREAMID;
|
||||
return -EINVAL;
|
||||
}
|
||||
if (s->features & SMMU_FEATURE_2LVL_STE) {
|
||||
int l1_ste_offset, l2_ste_offset, max_l2_ste, span;
|
||||
dma_addr_t strtab_base, l1ptr, l2ptr;
|
||||
dma_addr_t l1ptr, l2ptr;
|
||||
STEDesc l1std;
|
||||
|
||||
strtab_base = s->strtab_base & SMMU_BASE_ADDR_MASK;
|
||||
/*
|
||||
* Align strtab base address to table size. For this purpose, assume it
|
||||
* is not bounded by SMMU_IDR1_SIDSIZE.
|
||||
*/
|
||||
strtab_size_shift = MAX(5, (int)log2size - s->sid_split - 1 + 3);
|
||||
strtab_base = s->strtab_base & SMMU_BASE_ADDR_MASK &
|
||||
~MAKE_64BIT_MASK(0, strtab_size_shift);
|
||||
l1_ste_offset = sid >> s->sid_split;
|
||||
l2_ste_offset = sid & ((1 << s->sid_split) - 1);
|
||||
l1ptr = (dma_addr_t)(strtab_base + l1_ste_offset * sizeof(l1std));
|
||||
@ -429,7 +440,10 @@ static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste,
|
||||
}
|
||||
addr = l2ptr + l2_ste_offset * sizeof(*ste);
|
||||
} else {
|
||||
addr = s->strtab_base + sid * sizeof(*ste);
|
||||
strtab_size_shift = log2size + 5;
|
||||
strtab_base = s->strtab_base & SMMU_BASE_ADDR_MASK &
|
||||
~MAKE_64BIT_MASK(0, strtab_size_shift);
|
||||
addr = strtab_base + sid * sizeof(*ste);
|
||||
}
|
||||
|
||||
if (smmu_get_ste(s, addr, ste, event)) {
|
||||
|
@ -127,6 +127,9 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state,
|
||||
target_cpu->env.regs[0] = info->context_id;
|
||||
}
|
||||
|
||||
/* CP15 update requires rebuilding hflags */
|
||||
arm_rebuild_hflags(&target_cpu->env);
|
||||
|
||||
/* Start the new CPU at the requested address */
|
||||
cpu_set_pc(target_cpu_state, info->entry);
|
||||
|
||||
|
@ -976,6 +976,10 @@ static void arm_cpu_initfn(Object *obj)
|
||||
}
|
||||
}
|
||||
|
||||
static Property arm_cpu_gt_cntfrq_property =
|
||||
DEFINE_PROP_UINT64("cntfrq", ARMCPU, gt_cntfrq_hz,
|
||||
NANOSECONDS_PER_SECOND / GTIMER_SCALE);
|
||||
|
||||
static Property arm_cpu_reset_cbar_property =
|
||||
DEFINE_PROP_UINT64("reset-cbar", ARMCPU, reset_cbar, 0);
|
||||
|
||||
@ -1055,6 +1059,30 @@ static void arm_set_init_svtor(Object *obj, Visitor *v, const char *name,
|
||||
visit_type_uint32(v, name, &cpu->init_svtor, errp);
|
||||
}
|
||||
|
||||
unsigned int gt_cntfrq_period_ns(ARMCPU *cpu)
|
||||
{
|
||||
/*
|
||||
* The exact approach to calculating guest ticks is:
|
||||
*
|
||||
* muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), cpu->gt_cntfrq_hz,
|
||||
* NANOSECONDS_PER_SECOND);
|
||||
*
|
||||
* We don't do that. Rather we intentionally use integer division
|
||||
* truncation below and in the caller for the conversion of host monotonic
|
||||
* time to guest ticks to provide the exact inverse for the semantics of
|
||||
* the QEMUTimer scale factor. QEMUTimer's scale facter is an integer, so
|
||||
* it loses precision when representing frequencies where
|
||||
* `(NANOSECONDS_PER_SECOND % cpu->gt_cntfrq) > 0` holds. Failing to
|
||||
* provide an exact inverse leads to scheduling timers with negative
|
||||
* periods, which in turn leads to sticky behaviour in the guest.
|
||||
*
|
||||
* Finally, CNTFRQ is effectively capped at 1GHz to ensure our scale factor
|
||||
* cannot become zero.
|
||||
*/
|
||||
return NANOSECONDS_PER_SECOND > cpu->gt_cntfrq_hz ?
|
||||
NANOSECONDS_PER_SECOND / cpu->gt_cntfrq_hz : 1;
|
||||
}
|
||||
|
||||
void arm_cpu_post_init(Object *obj)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(obj);
|
||||
@ -1172,6 +1200,11 @@ void arm_cpu_post_init(Object *obj)
|
||||
|
||||
qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property,
|
||||
&error_abort);
|
||||
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER)) {
|
||||
qdev_property_add_static(DEVICE(cpu), &arm_cpu_gt_cntfrq_property,
|
||||
&error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
static void arm_cpu_finalizefn(Object *obj)
|
||||
@ -1251,14 +1284,30 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE,
|
||||
arm_gt_ptimer_cb, cpu);
|
||||
cpu->gt_timer[GTIMER_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE,
|
||||
arm_gt_vtimer_cb, cpu);
|
||||
cpu->gt_timer[GTIMER_HYP] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE,
|
||||
arm_gt_htimer_cb, cpu);
|
||||
cpu->gt_timer[GTIMER_SEC] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE,
|
||||
arm_gt_stimer_cb, cpu);
|
||||
|
||||
{
|
||||
uint64_t scale;
|
||||
|
||||
if (arm_feature(env, ARM_FEATURE_GENERIC_TIMER)) {
|
||||
if (!cpu->gt_cntfrq_hz) {
|
||||
error_setg(errp, "Invalid CNTFRQ: %"PRId64"Hz",
|
||||
cpu->gt_cntfrq_hz);
|
||||
return;
|
||||
}
|
||||
scale = gt_cntfrq_period_ns(cpu);
|
||||
} else {
|
||||
scale = GTIMER_SCALE;
|
||||
}
|
||||
|
||||
cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
|
||||
arm_gt_ptimer_cb, cpu);
|
||||
cpu->gt_timer[GTIMER_VIRT] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
|
||||
arm_gt_vtimer_cb, cpu);
|
||||
cpu->gt_timer[GTIMER_HYP] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
|
||||
arm_gt_htimer_cb, cpu);
|
||||
cpu->gt_timer[GTIMER_SEC] = timer_new(QEMU_CLOCK_VIRTUAL, scale,
|
||||
arm_gt_stimer_cb, cpu);
|
||||
}
|
||||
#endif
|
||||
|
||||
cpu_exec_realizefn(cs, &local_err);
|
||||
|
@ -932,8 +932,13 @@ struct ARMCPU {
|
||||
*/
|
||||
DECLARE_BITMAP(sve_vq_map, ARM_MAX_VQ);
|
||||
DECLARE_BITMAP(sve_vq_init, ARM_MAX_VQ);
|
||||
|
||||
/* Generic timer counter frequency, in Hz */
|
||||
uint64_t gt_cntfrq_hz;
|
||||
};
|
||||
|
||||
unsigned int gt_cntfrq_period_ns(ARMCPU *cpu);
|
||||
|
||||
void arm_cpu_post_init(Object *obj);
|
||||
|
||||
uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz);
|
||||
|
@ -2449,7 +2449,9 @@ static CPAccessResult gt_stimer_access(CPUARMState *env,
|
||||
|
||||
static uint64_t gt_get_countervalue(CPUARMState *env)
|
||||
{
|
||||
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / GTIMER_SCALE;
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
|
||||
return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / gt_cntfrq_period_ns(cpu);
|
||||
}
|
||||
|
||||
static void gt_recalc_timer(ARMCPU *cpu, int timeridx)
|
||||
@ -2485,10 +2487,11 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx)
|
||||
* set the timer for as far in the future as possible. When the
|
||||
* timer expires we will reset the timer for any remaining period.
|
||||
*/
|
||||
if (nexttick > INT64_MAX / GTIMER_SCALE) {
|
||||
nexttick = INT64_MAX / GTIMER_SCALE;
|
||||
if (nexttick > INT64_MAX / gt_cntfrq_period_ns(cpu)) {
|
||||
timer_mod_ns(cpu->gt_timer[timeridx], INT64_MAX);
|
||||
} else {
|
||||
timer_mod(cpu->gt_timer[timeridx], nexttick);
|
||||
}
|
||||
timer_mod(cpu->gt_timer[timeridx], nexttick);
|
||||
trace_arm_gt_recalc(timeridx, irqstate, nexttick);
|
||||
} else {
|
||||
/* Timer disabled: ISTATUS and timer output always clear */
|
||||
@ -2720,6 +2723,13 @@ void arm_gt_stimer_cb(void *opaque)
|
||||
gt_recalc_timer(cpu, GTIMER_SEC);
|
||||
}
|
||||
|
||||
static void arm_gt_cntfrq_reset(CPUARMState *env, const ARMCPRegInfo *opaque)
|
||||
{
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
|
||||
cpu->env.cp15.c14_cntfrq = cpu->gt_cntfrq_hz;
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
/* Note that CNTFRQ is purely reads-as-written for the benefit
|
||||
* of software; writing it doesn't actually change the timer frequency.
|
||||
@ -2734,7 +2744,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
.opc0 = 3, .opc1 = 3, .crn = 14, .crm = 0, .opc2 = 0,
|
||||
.access = PL1_RW | PL0_R, .accessfn = gt_cntfrq_access,
|
||||
.fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq),
|
||||
.resetvalue = (1000 * 1000 * 1000) / GTIMER_SCALE,
|
||||
.resetfn = arm_gt_cntfrq_reset,
|
||||
},
|
||||
/* overall control: mostly access permissions */
|
||||
{ .name = "CNTKCTL", .state = ARM_CP_STATE_BOTH,
|
||||
@ -2913,11 +2923,13 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
|
||||
static uint64_t gt_virt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
|
||||
/* Currently we have no support for QEMUTimer in linux-user so we
|
||||
* can't call gt_get_countervalue(env), instead we directly
|
||||
* call the lower level functions.
|
||||
*/
|
||||
return cpu_get_clock() / GTIMER_SCALE;
|
||||
return cpu_get_clock() / gt_cntfrq_period_ns(cpu);
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
|
||||
@ -11500,6 +11512,20 @@ void HELPER(rebuild_hflags_a64)(CPUARMState *env, int el)
|
||||
env->hflags = rebuild_hflags_a64(env, el, fp_el, mmu_idx);
|
||||
}
|
||||
|
||||
static inline void assert_hflags_rebuild_correctly(CPUARMState *env)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_TCG
|
||||
uint32_t env_flags_current = env->hflags;
|
||||
uint32_t env_flags_rebuilt = rebuild_hflags_internal(env);
|
||||
|
||||
if (unlikely(env_flags_current != env_flags_rebuilt)) {
|
||||
fprintf(stderr, "TCG hflags mismatch (current:0x%08x rebuilt:0x%08x)\n",
|
||||
env_flags_current, env_flags_rebuilt);
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
|
||||
target_ulong *cs_base, uint32_t *pflags)
|
||||
{
|
||||
@ -11507,9 +11533,7 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc,
|
||||
uint32_t pstate_for_ss;
|
||||
|
||||
*cs_base = 0;
|
||||
#ifdef CONFIG_DEBUG_TCG
|
||||
assert(flags == rebuild_hflags_internal(env));
|
||||
#endif
|
||||
assert_hflags_rebuild_correctly(env);
|
||||
|
||||
if (FIELD_EX32(flags, TBFLAG_ANY, AARCH64_STATE)) {
|
||||
*pc = env->pc;
|
||||
|
Loading…
Reference in New Issue
Block a user