From 4a0245b62502ee9ca7774712de4bb05d9bf47152 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 20 Dec 2019 14:02:59 +0000 Subject: [PATCH 01/12] target/arm: Remove redundant scaling of nexttick MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The corner-case codepath was adjusting nexttick such that overflow wouldn't occur when timer_mod() scaled the value back up. Remove a use of GTIMER_SCALE and avoid unnecessary operations by calling timer_mod_ns() directly. Signed-off-by: Andrew Jeffery Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Message-id: f8c680720e3abe55476e6d9cb604ad27fdbeb2e0.1576215453.git-series.andrew@aj.id.au Signed-off-by: Peter Maydell --- target/arm/helper.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 5074b5f69c..31fab098c5 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2486,9 +2486,10 @@ static void gt_recalc_timer(ARMCPU *cpu, int timeridx) * timer expires we will reset the timer for any remaining period. */ if (nexttick > INT64_MAX / GTIMER_SCALE) { - nexttick = INT64_MAX / GTIMER_SCALE; + 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 */ From 7def87548272f6f3831c7ae5ba713c5ccbd5d547 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 20 Dec 2019 14:02:59 +0000 Subject: [PATCH 02/12] target/arm: Abstract the generic timer frequency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for SoCs such as the ASPEED AST2600 whose firmware configures CNTFRQ to values significantly larger than the static 62.5MHz value currently derived from GTIMER_SCALE. As the OS potentially derives its timer periods from the CNTFRQ value the lack of support for running QEMUTimers at the appropriate rate leads to sticky behaviour in the guest. Substitute the GTIMER_SCALE constant with use of a helper to derive the period from gt_cntfrq_hz stored in struct ARMCPU. Initially set gt_cntfrq_hz to the frequency associated with GTIMER_SCALE so current behaviour is maintained. Signed-off-by: Andrew Jeffery Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 40bd8df043f66e1ccfb3e9482999d099ac72bb2e.1576215453.git-series.andrew@aj.id.au Signed-off-by: Peter Maydell --- target/arm/cpu.c | 8 ++++++++ target/arm/cpu.h | 5 +++++ target/arm/helper.c | 10 +++++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index dd51adac05..0abe288e38 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -974,6 +974,8 @@ static void arm_cpu_initfn(Object *obj) if (tcg_enabled()) { cpu->psci_version = 2; /* TCG implements PSCI 0.2 */ } + + cpu->gt_cntfrq_hz = NANOSECONDS_PER_SECOND / GTIMER_SCALE; } static Property arm_cpu_reset_cbar_property = @@ -1055,6 +1057,12 @@ 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) +{ + 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); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 5f70e9e043..40f2c45e17 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -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); diff --git a/target/arm/helper.c b/target/arm/helper.c index 31fab098c5..85963789f7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -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,7 +2487,7 @@ 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) { + 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); @@ -2914,11 +2916,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[] = { From 96eec6b2b389215f43d7f07e823cb196f9185052 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 20 Dec 2019 14:02:59 +0000 Subject: [PATCH 03/12] target/arm: Prepare generic timer for per-platform CNTFRQ The ASPEED AST2600 clocks the generic timer at the rate of HPLL. On recent firmwares this is at 1125MHz, which is considerably quicker than the assumed 62.5MHz of the current generic timer implementation. The delta between the value as read from CNTFRQ and the true rate of the underlying QEMUTimer leads to sticky behaviour in AST2600 guests. Add a feature-gated property exposing CNTFRQ for ARM CPUs providing the generic timer. This allows platforms to configure CNTFRQ (and the associated QEMUTimer) to the appropriate frequency prior to starting the guest. As the platform can now determine the rate of CNTFRQ we're exposed to limitations of QEMUTimer that didn't previously materialise: In the course of emulation we need to arbitrarily and accurately convert between guest ticks and time, but we're constrained by QEMUTimer's use of an integer scaling factor. The effect is QEMUTimer cannot exactly capture the period of frequencies that do not cleanly divide NANOSECONDS_PER_SECOND for scaling ticks to time. As such, provide an equally inaccurate scaling factor for scaling time to ticks so at least a self-consistent inverse relationship holds. Signed-off-by: Andrew Jeffery Reviewed-by: Richard Henderson Message-id: a22db9325f96e39f76e3c2baddcb712149f46bf2.1576215453.git-series.andrew@aj.id.au Signed-off-by: Peter Maydell --- target/arm/cpu.c | 61 +++++++++++++++++++++++++++++++++++++-------- target/arm/helper.c | 9 ++++++- 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 0abe288e38..d62fd5fdc6 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -974,10 +974,12 @@ static void arm_cpu_initfn(Object *obj) if (tcg_enabled()) { cpu->psci_version = 2; /* TCG implements PSCI 0.2 */ } - - cpu->gt_cntfrq_hz = NANOSECONDS_PER_SECOND / GTIMER_SCALE; } +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); @@ -1059,6 +1061,24 @@ static void arm_set_init_svtor(Object *obj, Visitor *v, const char *name, 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; } @@ -1180,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) @@ -1259,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); diff --git a/target/arm/helper.c b/target/arm/helper.c index 85963789f7..1d9af2d8b2 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2723,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. @@ -2737,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, From 058d095532dbd50aa3a2b5ef907b76c08f7d6de7 Mon Sep 17 00:00:00 2001 From: Andrew Jeffery Date: Fri, 20 Dec 2019 14:03:00 +0000 Subject: [PATCH 04/12] ast2600: Configure CNTFRQ at 1125MHz MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This matches the configuration set by u-boot on the AST2600. Signed-off-by: Andrew Jeffery Reviewed-by: Richard Henderson Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-id: 080ca1267a09381c43cf3c50d434fb6c186f2b6e.1576215453.git-series.andrew@aj.id.au Signed-off-by: Peter Maydell --- hw/arm/aspeed_ast2600.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index be88005dab..89e4b00950 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -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 From 3d44c60500785f18bb469c9de0aeba7415c0f28f Mon Sep 17 00:00:00 2001 From: Simon Veith Date: Fri, 20 Dec 2019 14:03:00 +0000 Subject: [PATCH 05/12] hw/arm/smmuv3: Apply address mask to linear strtab base address In the SMMU_STRTAB_BASE register, the stream table base address only occupies bits [51:6]. Other bits, such as RA (bit [62]), must be masked out to obtain the base address. The branch for 2-level stream tables correctly applies this mask by way of SMMU_BASE_ADDR_MASK, but the one for linear stream tables does not. Apply the missing mask in that case as well so that the correct stream base address is used by guests which configure a linear stream table. Linux guests are unaffected by this change because they choose a 2-level stream table layout for the QEMU SMMUv3, based on the size of its stream ID space. ref. ARM IHI 0070C, section 6.3.23. Signed-off-by: Simon Veith Acked-by: Eric Auger Tested-by: Eric Auger Message-id: 1576509312-13083-2-git-send-email-sveith@amazon.de Cc: Eric Auger Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Acked-by: Eric Auger Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmuv3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index e2fbb8357e..eef9a18d70 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -429,7 +429,7 @@ 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); + addr = (s->strtab_base & SMMU_BASE_ADDR_MASK) + sid * sizeof(*ste); } if (smmu_get_ste(s, addr, ste, event)) { From 3293b9f514a413e019b7dbc9d543458075b4849e Mon Sep 17 00:00:00 2001 From: Simon Veith Date: Fri, 20 Dec 2019 14:03:00 +0000 Subject: [PATCH 06/12] hw/arm/smmuv3: Correct SMMU_BASE_ADDR_MASK value There are two issues with the current value of SMMU_BASE_ADDR_MASK: - At the lower end, we are clearing bits [4:0]. Per the SMMUv3 spec, we should also be treating bit 5 as zero in the base address. - At the upper end, we are clearing bits [63:48]. Per the SMMUv3 spec, only bits [63:52] must be explicitly treated as zero. Update the SMMU_BASE_ADDR_MASK value to mask out bits [63:52] and [5:0]. ref. ARM IHI 0070C, section 6.3.23. Signed-off-by: Simon Veith Acked-by: Eric Auger Tested-by: Eric Auger Message-id: 1576509312-13083-3-git-send-email-sveith@amazon.de Cc: Eric Auger Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmuv3-internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index d190181ef1..042b435808 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -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) From 05ff2fb80ce4ca85d8a39d48ff8156de739b4f51 Mon Sep 17 00:00:00 2001 From: Simon Veith Date: Fri, 20 Dec 2019 14:03:00 +0000 Subject: [PATCH 07/12] hw/arm/smmuv3: Check stream IDs against actual table LOG2SIZE When checking whether a stream ID is in range of the stream table, we have so far been only checking it against our implementation limit (SMMU_IDR1_SIDSIZE). However, the guest can program the STRTAB_BASE_CFG.LOG2SIZE field to a size that is smaller than this limit. Check the stream ID against this limit as well to match the hardware behavior of raising C_BAD_STREAMID events in case the limit is exceeded. Also, ensure that we do not go one entry beyond the end of the table by checking that its index is strictly smaller than the table size. ref. ARM IHI 0070C, section 6.3.24. Signed-off-by: Simon Veith Acked-by: Eric Auger Tested-by: Eric Auger Message-id: 1576509312-13083-4-git-send-email-sveith@amazon.de Cc: Eric Auger Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmuv3.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index eef9a18d70..727558bcfa 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -377,11 +377,15 @@ static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, SMMUEventInfo *event) { dma_addr_t addr; + uint32_t log2size; 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; } From 41678c33aac61261522b74f08595ccf2221a430a Mon Sep 17 00:00:00 2001 From: Simon Veith Date: Fri, 20 Dec 2019 14:03:00 +0000 Subject: [PATCH 08/12] hw/arm/smmuv3: Align stream table base address to table size Per the specification, and as observed in hardware, the SMMUv3 aligns the SMMU_STRTAB_BASE address to the size of the table by masking out the respective least significant bits in the ADDR field. Apply this masking logic to our smmu_find_ste() lookup function per the specification. ref. ARM IHI 0070C, section 6.3.23. Signed-off-by: Simon Veith Acked-by: Eric Auger Tested-by: Eric Auger Message-id: 1576509312-13083-5-git-send-email-sveith@amazon.de Cc: Eric Auger Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmuv3.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 727558bcfa..31ac3ca32e 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -376,8 +376,9 @@ 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); @@ -391,10 +392,16 @@ static int smmu_find_ste(SMMUv3State *s, uint32_t sid, STE *ste, } 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)); @@ -433,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 & SMMU_BASE_ADDR_MASK) + 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)) { From a7f65ceb851af5a5b639c6e30801076d848db2c2 Mon Sep 17 00:00:00 2001 From: Simon Veith Date: Fri, 20 Dec 2019 14:03:00 +0000 Subject: [PATCH 09/12] hw/arm/smmuv3: Use correct bit positions in EVT_SET_ADDR2 macro The bit offsets in the EVT_SET_ADDR2 macro do not match those specified in the ARM SMMUv3 Architecture Specification. In all events that use this macro, e.g. F_WALK_EABT, the faulting fetch address or IPA actually occupies the 32-bit words 6 and 7 in the event record contiguously, with the upper and lower unused bits clear due to alignment or maximum supported address bits. How many bits are clear depends on the individual event type. Update the macro to write to the correct words in the event record so that guest drivers can obtain accurate address information on events. ref. ARM IHI 0070C, sections 7.3.12 through 7.3.16. Signed-off-by: Simon Veith Acked-by: Eric Auger Tested-by: Eric Auger Message-id: 1576509312-13083-6-git-send-email-sveith@amazon.de Cc: Eric Auger Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Acked-by: Eric Auger Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmuv3-internal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index 042b435808..4112394129 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -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); From b255cafb59578d16716186ed955717bc8f87bdb7 Mon Sep 17 00:00:00 2001 From: Simon Veith Date: Fri, 20 Dec 2019 14:03:00 +0000 Subject: [PATCH 10/12] hw/arm/smmuv3: Report F_STE_FETCH fault address in correct word position The smmuv3_record_event() function that generates the F_STE_FETCH error uses the EVT_SET_ADDR macro to record the fetch address, placing it in 32-bit words 4 and 5. The correct position for this address is in words 6 and 7, per the SMMUv3 Architecture Specification. Update the function to use the EVT_SET_ADDR2 macro instead, which is the macro intended for writing to these words. ref. ARM IHI 0070C, section 7.3.4. Signed-off-by: Simon Veith Acked-by: Eric Auger Tested-by: Eric Auger Message-id: 1576509312-13083-7-git-send-email-sveith@amazon.de Cc: Eric Auger Cc: qemu-devel@nongnu.org Cc: qemu-arm@nongnu.org Acked-by: Eric Auger Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/smmuv3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 31ac3ca32e..8b5f157dc7 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -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); From 0ee8b24a69e410ce5abdc30b7050dc9e26a647ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Dec 2019 14:03:00 +0000 Subject: [PATCH 11/12] target/arm: Display helpful message when hflags mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of crashing in a confuse way, give some hint to the user about why we aborted. He might report the issue without having to use a debugger. Signed-off-by: Philippe Mathieu-Daudé Message-id: 20191209134552.27733-1-philmd@redhat.com Reviewed-by: Richard Henderson Tested-by: Niek Linnenbank Signed-off-by: Peter Maydell --- target/arm/helper.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 1d9af2d8b2..b6bec42f48 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -11512,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) { @@ -11519,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; From c8fa6079eb35888587f1be27c1590da4edcc5098 Mon Sep 17 00:00:00 2001 From: Niek Linnenbank Date: Fri, 20 Dec 2019 14:03:00 +0000 Subject: [PATCH 12/12] arm/arm-powerctl: rebuild hflags after setting CP15 bits in arm_set_cpu_on() After setting CP15 bits in arm_set_cpu_on() the cached hflags must be rebuild to reflect the changed processor state. Without rebuilding, the cached hflags would be inconsistent until the next call to arm_rebuild_hflags(). When QEMU is compiled with debugging enabled (--enable-debug), this problem is captured shortly after the first call to arm_set_cpu_on() for CPUs running in ARM 32-bit non-secure mode: qemu-system-arm: target/arm/helper.c:11359: cpu_get_tb_cpu_state: Assertion `flags == rebuild_hflags_internal(env)' failed. Aborted (core dumped) Fixes: 0c7f8c43daf65 Cc: qemu-stable@nongnu.org Signed-off-by: Niek Linnenbank Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell --- target/arm/arm-powerctl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index b064513d44..b75f813b40 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -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);