Message ID | 20190201160653.13829-36-peter.maydell@linaro.org |
---|---|
State | New |
Headers | show |
Series | [PULL,01/47] hw/arm/nrf51_soc: set object owner in memory_region_init_ram | expand |
On Fri, 1 Feb 2019 at 16:07, Peter Maydell <peter.maydell@linaro.org> wrote: > > From: Aaron Lindsay OS <aaron@os.amperecomputing.com> > > Whenever we notice that a counter overflow has occurred, send an > interrupt. This is made more reliable with the addition of a timer in a > follow-on commit. > > Signed-off-by: Aaron Lindsay <aaron@os.amperecomputing.com> > Reviewed-by: Richard Henderson <richard.henderson@linaro.org> > Message-id: 20190124162401.5111-2-aaron@os.amperecomputing.com > Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Hi Aaron -- I've just noticed a problem with this patch that went into QEMU recently. The problem is that we can end up calling pmu_update_irq(), which injects an interrupt, from a raw-write function for some of the PMU registers. This is bad because when we're using KVM the raw-write functions are called as part of syncing state to and from the kernel. In particular, if using '-cpu host,pmu=off' we don't set up the PMU interrupt because we don't want to provide the guest a PMU but then we can still find ourselves in this function, and then we assert because we try to set a bogus interrupt. Here's the backtrace: #1 0x0000fffff6be68b4 in __GI_abort () at abort.c:79 #2 0x0000aaaaaae20820 in kvm_set_irq (s=0xaaaaabf8a020, irq=33554455, level=0) at /home/pm/qemu-bisect/accel/kvm/kvm-all.c:1277 #3 0x0000aaaaaafb7890 in kvm_arm_set_irq (cpu=0, irqtype=2, irq=23, level=0) at /home/pm/qemu-bisect/target/arm/kvm.c:897 #4 0x0000aaaaaae729dc in kvm_arm_gic_set_irq (num_irq=288, irq=23, level=0) at /home/pm/qemu-bisect/hw/intc/arm_gic_kvm.c:75 #5 0x0000aaaaaae72a1c in kvm_arm_gicv2_set_irq (opaque=0xaaaaac169ff0, irq=279, level=0) at /home/pm/qemu-bisect/hw/intc/arm_gic_kvm.c:82 #6 0x0000aaaaab1ba15c in qemu_set_irq (irq=0xaaaaac186090, level=0) at /home/pm/qemu-bisect/hw/core/irq.c:44 #7 0x0000aaaaaaf86050 in pmu_update_irq (env=0xaaaaac0fa470) at /home/pm/qemu-bisect/target/arm/helper.c:1412 #8 0x0000aaaaaaf8747c in pmintenclr_write (env=0xaaaaac0fa470, ri=0xaaaaac12c3e0, value=2154950974777589790) at /home/pm/qemu-bisect/target/arm/helper.c:1903 #9 0x0000aaaaaaf83e68 in write_raw_cp_reg (env=0xaaaaac0fa470, ri=0xaaaaac12c3e0, v=2154950976315703518) at /home/pm/qemu-bisect/target/arm/helper.c:206 #10 0x0000aaaaaaf840d4 in write_cpustate_to_list (cpu=0xaaaaac0f0b90, kvm_sync=true) at /home/pm/qemu-bisect/target/arm/helper.c:290 #11 0x0000aaaaaafbb1ac in kvm_arch_put_registers (cs=0xaaaaac0f0b90, level=3) at /home/pm/qemu-bisect/target/arm/kvm64.c:1108 #12 0x0000aaaaaae22ea0 in do_kvm_cpu_synchronize_post_init (cpu=0xaaaaac0f0b90, arg=...) at /home/pm/qemu-bisect/accel/kvm/kvm-all.c:2223 #13 0x0000aaaaab107fa0 in process_queued_cpu_work (cpu=0xaaaaac0f0b90) at /home/pm/qemu-bisect/cpus-common.c:338 #14 0x0000aaaaaadf4ff4 in qemu_wait_io_event_common (cpu=0xaaaaac0f0b90) at /home/pm/qemu-bisect/cpus.c:1175 #15 0x0000aaaaaadf51a8 in qemu_wait_io_event (cpu=0xaaaaac0f0b90) at /home/pm/qemu-bisect/cpus.c:1215 #16 0x0000aaaaaadf52cc in qemu_kvm_cpu_thread_fn (arg=0xaaaaac0f0b90) at /home/pm/qemu-bisect/cpus.c:1251 #17 0x0000aaaaab690268 in qemu_thread_start (args=0xaaaaac14b1d0) at /home/pm/qemu-bisect/util/qemu-thread-posix.c:519 The point of the 'raw_read/write' accessors is that they're supposed to not have side effects but just to be usable to read and write any underlying register state. If the regdef doesn't define them we fall back to the usual readfn/writefn on the assumption that they're side-effect-free. So I think the fix here would be to provide a raw_writefn everywhere where we've made the normal writefn have a "sets an interrupt" side effect. thanks -- PMM
On Feb 25 17:08, Peter Maydell wrote: > On Fri, 1 Feb 2019 at 16:07, Peter Maydell <peter.maydell@linaro.org> wrote: > > > > From: Aaron Lindsay OS <aaron@os.amperecomputing.com> > > > > Whenever we notice that a counter overflow has occurred, send an > > interrupt. This is made more reliable with the addition of a timer in a > > follow-on commit. > > > > Signed-off-by: Aaron Lindsay <aaron@os.amperecomputing.com> > > Reviewed-by: Richard Henderson <richard.henderson@linaro.org> > > Message-id: 20190124162401.5111-2-aaron@os.amperecomputing.com > > Signed-off-by: Peter Maydell <peter.maydell@linaro.org> > > Hi Aaron -- I've just noticed a problem with this patch that > went into QEMU recently. The problem is that we can end up > calling pmu_update_irq(), which injects an interrupt, from > a raw-write function for some of the PMU registers. This is > bad because when we're using KVM the raw-write functions are > called as part of syncing state to and from the kernel. In > particular, if using '-cpu host,pmu=off' we don't set up the > PMU interrupt because we don't want to provide the guest a > PMU but then we can still find ourselves in this function, > and then we assert because we try to set a bogus interrupt. > Here's the backtrace: > > #1 0x0000fffff6be68b4 in __GI_abort () at abort.c:79 > #2 0x0000aaaaaae20820 in kvm_set_irq (s=0xaaaaabf8a020, irq=33554455, level=0) > at /home/pm/qemu-bisect/accel/kvm/kvm-all.c:1277 > #3 0x0000aaaaaafb7890 in kvm_arm_set_irq (cpu=0, irqtype=2, irq=23, level=0) > at /home/pm/qemu-bisect/target/arm/kvm.c:897 > #4 0x0000aaaaaae729dc in kvm_arm_gic_set_irq (num_irq=288, irq=23, level=0) > at /home/pm/qemu-bisect/hw/intc/arm_gic_kvm.c:75 > #5 0x0000aaaaaae72a1c in kvm_arm_gicv2_set_irq > (opaque=0xaaaaac169ff0, irq=279, level=0) > at /home/pm/qemu-bisect/hw/intc/arm_gic_kvm.c:82 > #6 0x0000aaaaab1ba15c in qemu_set_irq (irq=0xaaaaac186090, level=0) > at /home/pm/qemu-bisect/hw/core/irq.c:44 > #7 0x0000aaaaaaf86050 in pmu_update_irq (env=0xaaaaac0fa470) > at /home/pm/qemu-bisect/target/arm/helper.c:1412 > #8 0x0000aaaaaaf8747c in pmintenclr_write (env=0xaaaaac0fa470, > ri=0xaaaaac12c3e0, value=2154950974777589790) at > /home/pm/qemu-bisect/target/arm/helper.c:1903 > #9 0x0000aaaaaaf83e68 in write_raw_cp_reg (env=0xaaaaac0fa470, > ri=0xaaaaac12c3e0, v=2154950976315703518) at > /home/pm/qemu-bisect/target/arm/helper.c:206 > #10 0x0000aaaaaaf840d4 in write_cpustate_to_list (cpu=0xaaaaac0f0b90, > kvm_sync=true) > at /home/pm/qemu-bisect/target/arm/helper.c:290 > #11 0x0000aaaaaafbb1ac in kvm_arch_put_registers (cs=0xaaaaac0f0b90, level=3) > at /home/pm/qemu-bisect/target/arm/kvm64.c:1108 > #12 0x0000aaaaaae22ea0 in do_kvm_cpu_synchronize_post_init > (cpu=0xaaaaac0f0b90, arg=...) > at /home/pm/qemu-bisect/accel/kvm/kvm-all.c:2223 > #13 0x0000aaaaab107fa0 in process_queued_cpu_work (cpu=0xaaaaac0f0b90) > at /home/pm/qemu-bisect/cpus-common.c:338 > #14 0x0000aaaaaadf4ff4 in qemu_wait_io_event_common (cpu=0xaaaaac0f0b90) > at /home/pm/qemu-bisect/cpus.c:1175 > #15 0x0000aaaaaadf51a8 in qemu_wait_io_event (cpu=0xaaaaac0f0b90) > at /home/pm/qemu-bisect/cpus.c:1215 > #16 0x0000aaaaaadf52cc in qemu_kvm_cpu_thread_fn (arg=0xaaaaac0f0b90) > at /home/pm/qemu-bisect/cpus.c:1251 > #17 0x0000aaaaab690268 in qemu_thread_start (args=0xaaaaac14b1d0) > at /home/pm/qemu-bisect/util/qemu-thread-posix.c:519 > > > The point of the 'raw_read/write' accessors is that they're supposed > to not have side effects but just to be usable to read and write > any underlying register state. If the regdef doesn't define them > we fall back to the usual readfn/writefn on the assumption that > they're side-effect-free. So I think the fix here would be to > provide a raw_writefn everywhere where we've made > the normal writefn have a "sets an interrupt" side effect. Ouch - I'm sorry this slipped through the cracks in my inbox for so long. I assume you mean something like: diff --git a/target/arm/helper.c b/target/arm/helper.c index dc9c29f998..9b917f9425 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2271,13 +2271,13 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), - .writefn = pmintenclr_write, }, + .writefn = pmintenclr_write, .raw_writefn = raw_write }, { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2, .access = PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), - .writefn = pmintenclr_write }, + .writefn = pmintenclr_write, .raw_writefn = raw_write }, { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_R, One thing I'm trying to figure out (talking non-KVM here) is whether skipping calling pmu_update_irq() can mean an interrupt would not be set when it should have been. It looks like the ARMCPRegInfo's for PMINTENSET already do `.raw_writefn = raw_write`, so I suppose at least we would be consistent with this change. But I can never remember - is it guaranteed that the raw functions are only ever called when the interrupt state would already be taken care of separately (i.e. when restoring a checkpoint)? -Aaron
On Wed, 1 Jul 2020 at 16:11, Aaron Lindsay <aaron@os.amperecomputing.com> wrote: > Ouch - I'm sorry this slipped through the cracks in my inbox for so > long. > > I assume you mean something like: > > diff --git a/target/arm/helper.c b/target/arm/helper.c > index dc9c29f998..9b917f9425 100644 > --- a/target/arm/helper.c > +++ b/target/arm/helper.c > @@ -2271,13 +2271,13 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { > .access = PL1_RW, .accessfn = access_tpm, > .type = ARM_CP_ALIAS | ARM_CP_IO, > .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), > - .writefn = pmintenclr_write, }, > + .writefn = pmintenclr_write, .raw_writefn = raw_write }, > { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64, > .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2, > .access = PL1_RW, .accessfn = access_tpm, > .type = ARM_CP_ALIAS | ARM_CP_IO, > .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), > - .writefn = pmintenclr_write }, > + .writefn = pmintenclr_write, .raw_writefn = raw_write }, > { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, > .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, > .access = PL1_R, For cases like this where you have a 'set' and a 'clr' register that really are accessing the same state under the hood, the way to do it is: * the 'set' register provides the raw_readfn/raw_writefn as raw_read/raw_write * the 'clr' register adds ARM_CP_NO_RAW to its .type flags, which means "don't do any raw accesses to this, it doesn't have any underlying state that's not already synced or migrated via some other mechanism". > One thing I'm trying to figure out (talking non-KVM here) is whether > skipping calling pmu_update_irq() can mean an interrupt would not be set > when it should have been. It looks like the ARMCPRegInfo's for > PMINTENSET already do `.raw_writefn = raw_write`, so I suppose at least > we would be consistent with this change. But I can never remember - is > it guaranteed that the raw functions are only ever called when the > interrupt state would already be taken care of separately (i.e. when > restoring a checkpoint)? Yes, the raw writes only happen for migration or for when we're syncing state from a KVM kernel, so the state of the device at the other end of the irq line should already be correct. thanks -- PMM
diff --git a/target/arm/helper.c b/target/arm/helper.c index 66faebea8ec..c27e349dd05 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -977,6 +977,7 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { /* Definitions for the PMU registers */ #define PMCRN_MASK 0xf800 #define PMCRN_SHIFT 11 +#define PMCRLC 0x40 #define PMCRDP 0x10 #define PMCRD 0x8 #define PMCRC 0x4 @@ -1293,6 +1294,13 @@ static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter) return enabled && !prohibited && !filtered; } +static void pmu_update_irq(CPUARMState *env) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + qemu_set_irq(cpu->pmu_interrupt, (env->cp15.c9_pmcr & PMCRE) && + (env->cp15.c9_pminten & env->cp15.c9_pmovsr)); +} + /* * Ensure c15_ccnt is the guest-visible count so that operations such as * enabling/disabling the counter or filtering, modifying the count itself, @@ -1310,7 +1318,16 @@ void pmccntr_op_start(CPUARMState *env) eff_cycles /= 64; } - env->cp15.c15_ccnt = eff_cycles - env->cp15.c15_ccnt_delta; + uint64_t new_pmccntr = eff_cycles - env->cp15.c15_ccnt_delta; + + uint64_t overflow_mask = env->cp15.c9_pmcr & PMCRLC ? \ + 1ull << 63 : 1ull << 31; + if (env->cp15.c15_ccnt & ~new_pmccntr & overflow_mask) { + env->cp15.c9_pmovsr |= (1 << 31); + pmu_update_irq(env); + } + + env->cp15.c15_ccnt = new_pmccntr; } env->cp15.c15_ccnt_delta = cycles; } @@ -1345,8 +1362,13 @@ static void pmevcntr_op_start(CPUARMState *env, uint8_t counter) } if (pmu_counter_enabled(env, counter)) { - env->cp15.c14_pmevcntr[counter] = - count - env->cp15.c14_pmevcntr_delta[counter]; + uint32_t new_pmevcntr = count - env->cp15.c14_pmevcntr_delta[counter]; + + if (env->cp15.c14_pmevcntr[counter] & ~new_pmevcntr & INT32_MIN) { + env->cp15.c9_pmovsr |= (1 << counter); + pmu_update_irq(env); + } + env->cp15.c14_pmevcntr[counter] = new_pmevcntr; } env->cp15.c14_pmevcntr_delta[counter] = count; } @@ -1423,7 +1445,20 @@ static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri, /* counter is SW_INCR */ (env->cp15.c14_pmevtyper[i] & PMXEVTYPER_EVTCOUNT) == 0x0) { pmevcntr_op_start(env, i); - env->cp15.c14_pmevcntr[i]++; + + /* + * Detect if this write causes an overflow since we can't predict + * PMSWINC overflows like we can for other events + */ + uint32_t new_pmswinc = env->cp15.c14_pmevcntr[i] + 1; + + if (env->cp15.c14_pmevcntr[i] & ~new_pmswinc & INT32_MIN) { + env->cp15.c9_pmovsr |= (1 << i); + pmu_update_irq(env); + } + + env->cp15.c14_pmevcntr[i] = new_pmswinc; + pmevcntr_op_finish(env, i); } } @@ -1508,6 +1543,7 @@ static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri, { value &= pmu_counter_mask(env); env->cp15.c9_pmovsr &= ~value; + pmu_update_irq(env); } static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1515,6 +1551,7 @@ static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri, { value &= pmu_counter_mask(env); env->cp15.c9_pmovsr |= value; + pmu_update_irq(env); } static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1701,6 +1738,7 @@ static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri, /* We have no event counters so only the C bit can be changed */ value &= pmu_counter_mask(env); env->cp15.c9_pminten |= value; + pmu_update_irq(env); } static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1708,6 +1746,7 @@ static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, { value &= pmu_counter_mask(env); env->cp15.c9_pminten &= ~value; + pmu_update_irq(env); } static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1846,7 +1885,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenclr_write }, { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, - .access = PL0_RW, + .access = PL0_RW, .type = ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), .accessfn = pmreg_access, .writefn = pmovsr_write, @@ -1854,16 +1893,18 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3, .access = PL0_RW, .accessfn = pmreg_access, - .type = ARM_CP_ALIAS, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsr_write, .raw_writefn = raw_write }, { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, - .access = PL0_W, .accessfn = pmreg_access_swinc, .type = ARM_CP_NO_RAW, + .access = PL0_W, .accessfn = pmreg_access_swinc, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .writefn = pmswinc_write }, { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4, - .access = PL0_W, .accessfn = pmreg_access_swinc, .type = ARM_CP_NO_RAW, + .access = PL0_W, .accessfn = pmreg_access_swinc, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .writefn = pmswinc_write }, { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5, .access = PL0_RW, .type = ARM_CP_ALIAS, @@ -2050,14 +2091,14 @@ static const ARMCPRegInfo pmovsset_cp_reginfo[] = { /* PMOVSSET is not implemented in v7 before v7ve */ { .name = "PMOVSSET", .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 3, .access = PL0_RW, .accessfn = pmreg_access, - .type = ARM_CP_ALIAS, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsset_write, .raw_writefn = raw_write }, { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3, .access = PL0_RW, .accessfn = pmreg_access, - .type = ARM_CP_ALIAS, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), .writefn = pmovsset_write, .raw_writefn = raw_write },