From 2a6332d968297266dbabf9d33f959e3a5efdd0f9 Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Mon, 6 Jul 2015 10:05:43 +0100 Subject: [PATCH 1/7] target-arm: fix write helper for TLBI ALLE1IS TLBI ALLE1IS is an operation that does invalidate TLB entries on all PEs in the same Inner Sharable domain, not just on the current CPU. So we must use tlbiall_is_write() here. Signed-off-by: Sergey Fedorov Message-id: 1435676538-31345-1-git-send-email-serge.fdrv@gmail.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target-arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target-arm/helper.c b/target-arm/helper.c index aa341599cf..b87afe7cde 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -2441,7 +2441,7 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { { .name = "TLBI_ALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 4, .crn = 8, .crm = 3, .opc2 = 4, .access = PL2_W, .type = ARM_CP_NO_RAW, - .writefn = tlbiall_write }, + .writefn = tlbiall_is_write }, { .name = "TLBI_VMALLE1IS", .state = ARM_CP_STATE_AA64, .opc0 = 1, .opc1 = 0, .crn = 8, .crm = 3, .opc2 = 0, .access = PL1_W, .type = ARM_CP_NO_RAW, From a7ffaf5c96e26820edffa94eeac766fe60bfdd31 Mon Sep 17 00:00:00 2001 From: Johannes Schlatow Date: Mon, 6 Jul 2015 10:05:44 +0100 Subject: [PATCH 2/7] Fix interval interrupt of cadence ttc when timer is in decrement mode The interval interrupt is not set if the timer is in decrement mode. This is because x >=0 and x < interval after leaving the while-loop. Signed-off-by: Johannes Schlatow Message-id: 20150630135821.51f3b4fd@johanness-latitude Reviewed-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- hw/timer/cadence_ttc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hw/timer/cadence_ttc.c b/hw/timer/cadence_ttc.c index d46db3c0e2..35bc88033e 100644 --- a/hw/timer/cadence_ttc.c +++ b/hw/timer/cadence_ttc.c @@ -208,15 +208,14 @@ static void cadence_timer_sync(CadenceTimerState *s) s->reg_intr |= (2 << i); } } + if ((x < 0) || (x >= interval)) { + s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ? + COUNTER_INTR_IV : COUNTER_INTR_OV; + } while (x < 0) { x += interval; } s->reg_value = (uint32_t)(x % interval); - - if (s->reg_value != x) { - s->reg_intr |= (s->reg_count & COUNTER_CTRL_INT) ? - COUNTER_INTR_IV : COUNTER_INTR_OV; - } cadence_timer_update(s); } From 049e24a191c212d9468db84169197887f2c91586 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Jul 2015 10:05:44 +0100 Subject: [PATCH 3/7] target-arm: Split DISAS_YIELD from DISAS_WFE Currently we use DISAS_WFE for both WFE and YIELD instructions. This is functionally correct because at the moment both of them are implemented as "yield this CPU back to the top level loop so another CPU has a chance to run". However it's rather confusing that YIELD ends up calling HELPER(wfe), and if we ever want to implement real behaviour for WFE and SEV it's likely to trip us up. Split out the yield codepath to use DISAS_YIELD and a new HELPER(yield) function, and have HELPER(wfe) call HELPER(yield). Signed-off-by: Peter Maydell Message-id: 1435672316-3311-2-git-send-email-peter.maydell@linaro.org Reviewed-by: Peter Crosthwaite --- target-arm/helper.h | 1 + target-arm/op_helper.c | 18 +++++++++++++++--- target-arm/translate-a64.c | 6 ++++++ target-arm/translate.h | 1 + 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/target-arm/helper.h b/target-arm/helper.h index fc885dea43..827b33dfec 100644 --- a/target-arm/helper.h +++ b/target-arm/helper.h @@ -50,6 +50,7 @@ DEF_HELPER_2(exception_internal, void, env, i32) DEF_HELPER_4(exception_with_syndrome, void, env, i32, i32, i32) DEF_HELPER_1(wfi, void, env) DEF_HELPER_1(wfe, void, env) +DEF_HELPER_1(yield, void, env) DEF_HELPER_1(pre_hvc, void, env) DEF_HELPER_2(pre_smc, void, env, i32) diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index 7fa32c4707..663c05d1d2 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -323,13 +323,25 @@ void HELPER(wfi)(CPUARMState *env) void HELPER(wfe)(CPUARMState *env) { - CPUState *cs = CPU(arm_env_get_cpu(env)); - - /* Don't actually halt the CPU, just yield back to top + /* This is a hint instruction that is semantically different + * from YIELD even though we currently implement it identically. + * Don't actually halt the CPU, just yield back to top * level loop. This is not going into a "low power state" * (ie halting until some event occurs), so we never take * a configurable trap to a different exception level. */ + HELPER(yield)(env); +} + +void HELPER(yield)(CPUARMState *env) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + /* This is a non-trappable hint instruction that generally indicates + * that the guest is currently busy-looping. Yield control back to the + * top level loop so that a more deserving VCPU has a chance to run. + */ cs->exception_index = EXCP_YIELD; cpu_loop_exit(cs); } diff --git a/target-arm/translate-a64.c b/target-arm/translate-a64.c index e077f2dc30..689f2be896 100644 --- a/target-arm/translate-a64.c +++ b/target-arm/translate-a64.c @@ -1199,6 +1199,8 @@ static void handle_hint(DisasContext *s, uint32_t insn, s->is_jmp = DISAS_WFI; return; case 1: /* YIELD */ + s->is_jmp = DISAS_YIELD; + return; case 2: /* WFE */ s->is_jmp = DISAS_WFE; return; @@ -11107,6 +11109,10 @@ void gen_intermediate_code_internal_a64(ARMCPU *cpu, gen_a64_set_pc_im(dc->pc); gen_helper_wfe(cpu_env); break; + case DISAS_YIELD: + gen_a64_set_pc_im(dc->pc); + gen_helper_yield(cpu_env); + break; case DISAS_WFI: /* This is a special case because we don't want to just halt the CPU * if trying to debug across a WFI. diff --git a/target-arm/translate.h b/target-arm/translate.h index bcdcf11718..9ab978fb75 100644 --- a/target-arm/translate.h +++ b/target-arm/translate.h @@ -103,6 +103,7 @@ static inline int default_exception_el(DisasContext *s) #define DISAS_WFE 7 #define DISAS_HVC 8 #define DISAS_SMC 9 +#define DISAS_YIELD 10 #ifdef TARGET_AARCH64 void a64_translate_init(void); From c87e5a61c2b3024116f52f7e68273f864ff7ab82 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 6 Jul 2015 10:05:44 +0100 Subject: [PATCH 4/7] target-arm: Implement YIELD insn to yield in ARM and Thumb translators Implement the YIELD instruction in the ARM and Thumb translators to actually yield control back to the top level loop rather than being a simple no-op. (We already do this for A64.) Signed-off-by: Peter Maydell Reviewed-by: Peter Crosthwaite Message-id: 1435672316-3311-3-git-send-email-peter.maydell@linaro.org --- target-arm/translate.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target-arm/translate.c b/target-arm/translate.c index 971b6db061..69ac18c108 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -4080,6 +4080,10 @@ static void gen_rfe(DisasContext *s, TCGv_i32 pc, TCGv_i32 cpsr) static void gen_nop_hint(DisasContext *s, int val) { switch (val) { + case 1: /* yield */ + gen_set_pc_im(s, s->pc); + s->is_jmp = DISAS_YIELD; + break; case 3: /* wfi */ gen_set_pc_im(s, s->pc); s->is_jmp = DISAS_WFI; @@ -11459,6 +11463,9 @@ static inline void gen_intermediate_code_internal(ARMCPU *cpu, case DISAS_WFE: gen_helper_wfe(cpu_env); break; + case DISAS_YIELD: + gen_helper_yield(cpu_env); + break; case DISAS_SWI: gen_exception(EXCP_SWI, syn_aa32_svc(dc->svc_imm, dc->thumb), default_exception_el(dc)); From 12dc273e98e4e111880b25c12bf671dc8951b8e6 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 29 Jun 2015 19:25:45 +0100 Subject: [PATCH 5/7] hw/intc/arm_gic_common.c: Reset all registers The arm_gic_common reset function was missing reset code for several of the GIC's state fields: * bpr[] * abpr[] * priority1[] * priority2[] * sgi_pending[] * irq_target[] (SMP configurations only) These probably went unnoticed because most guests will either never touch them, or will write to them in the process of configuring the GIC before enabling interrupts. Signed-off-by: Peter Maydell Message-id: 1435602345-32210-1-git-send-email-peter.maydell@linaro.org Reviewed-by: Edgar E. Iglesias --- hw/intc/arm_gic_common.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 044ad66730..a64d0714ea 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -123,7 +123,7 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp) static void arm_gic_common_reset(DeviceState *dev) { GICState *s = ARM_GIC_COMMON(dev); - int i; + int i, j; memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); for (i = 0 ; i < s->num_cpu; i++) { if (s->revision == REV_11MPCORE) { @@ -135,15 +135,30 @@ static void arm_gic_common_reset(DeviceState *dev) s->running_irq[i] = 1023; s->running_priority[i] = 0x100; s->cpu_ctlr[i] = 0; + s->bpr[i] = GIC_MIN_BPR; + s->abpr[i] = GIC_MIN_ABPR; + for (j = 0; j < GIC_INTERNAL; j++) { + s->priority1[j][i] = 0; + } + for (j = 0; j < GIC_NR_SGIS; j++) { + s->sgi_pending[j][i] = 0; + } } for (i = 0; i < GIC_NR_SGIS; i++) { GIC_SET_ENABLED(i, ALL_CPU_MASK); GIC_SET_EDGE_TRIGGER(i); } - if (s->num_cpu == 1) { + + for (i = 0; i < ARRAY_SIZE(s->priority2); i++) { + s->priority2[i] = 0; + } + + for (i = 0; i < GIC_MAXIRQ; i++) { /* For uniprocessor GICs all interrupts always target the sole CPU */ - for (i = 0; i < GIC_MAXIRQ; i++) { + if (s->num_cpu == 1) { s->irq_target[i] = 1; + } else { + s->irq_target[i] = 0; } } s->ctlr = 0; From 8a52340cbaf60d4dd0a78bbfe12632639fe3da6d Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jul 2015 01:47:47 +0300 Subject: [PATCH 6/7] arm_mptimer: Fix timer shutdown and mode change The running timer can't be stopped because timer control code just doesn't handle disabling the timer. Fix it by deleting the timer if the enable bit is cleared. The timer won't start periodic ticking if a ONE-SHOT -> PERIODIC mode change happens after a one-shot tick was completed. Fix it by re-starting ticking if the timer isn't ticking right now. To avoid code churning, these two fixes are squashed in one commit. Signed-off-by: Dmitry Osipenko Reviewed-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- hw/timer/arm_mptimer.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index 8b93b3c1ae..0e132b15b5 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -122,11 +122,18 @@ static void timerblock_write(void *opaque, hwaddr addr, case 8: /* Control. */ old = tb->control; tb->control = value; - if (((old & 1) == 0) && (value & 1)) { - if (tb->count == 0 && (tb->control & 2)) { + if (value & 1) { + if ((old & 1) && (tb->count != 0)) { + /* Do nothing if timer is ticking right now. */ + break; + } + if (tb->control & 2) { tb->count = tb->load; } timerblock_reload(tb, 1); + } else if (old & 1) { + /* Shutdown the timer. */ + timer_del(tb->timer); } break; case 12: /* Interrupt status. */ From 257621a9566054472d1d55a819880d0f9da02bda Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Mon, 6 Jul 2015 04:27:12 +0300 Subject: [PATCH 7/7] arm_mptimer: Respect IT bit state The timer should fire the interrupt only if the IT (interrupt enable) bit state of the control register is enabled. Signed-off-by: Dmitry Osipenko Reviewed-by: Peter Crosthwaite Signed-off-by: Peter Maydell --- hw/timer/arm_mptimer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index 0e132b15b5..3e59c2a288 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -38,7 +38,7 @@ static inline int get_current_cpu(ARMMPTimerState *s) static inline void timerblock_update_irq(TimerBlock *tb) { - qemu_set_irq(tb->irq, tb->status); + qemu_set_irq(tb->irq, tb->status && (tb->control & 4)); } /* Return conversion factor from mpcore timer ticks to qemu timer ticks. */