From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:47978) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1V57Qa-0002kk-1N for qemu-devel@nongnu.org; Fri, 02 Aug 2013 00:57:29 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1V56tg-0007g2-N1 for qemu-devel@nongnu.org; Fri, 02 Aug 2013 00:23:30 -0400 Received: from cantor2.suse.de ([195.135.220.15]:37818 helo=mx2.suse.de) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1V4v9K-0001O9-Gj for qemu-devel@nongnu.org; Thu, 01 Aug 2013 11:50:47 -0400 Message-ID: <51FA83D1.1030308@suse.de> Date: Thu, 01 Aug 2013 17:50:41 +0200 From: =?ISO-8859-15?Q?Andreas_F=E4rber?= MIME-Version: 1.0 References: <1375206933-15053-1-git-send-email-peter.maydell@linaro.org> <1375206933-15053-4-git-send-email-peter.maydell@linaro.org> In-Reply-To: <1375206933-15053-4-git-send-email-peter.maydell@linaro.org> Content-Type: text/plain; charset=ISO-8859-15 Content-Transfer-Encoding: quoted-printable Subject: Re: [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Peter Maydell Cc: qemu-devel@nongnu.org, patches@linaro.org Am 30.07.2013 19:55, schrieb Peter Maydell: > The ARMv7 architecture specifies a 'generic timer' which is implemented > via cp15 registers. Newer kernels will prefer to use this rather than > a devboard-level timer. Implement the generic timer for TCG; for KVM > we will already use the hardware's virtualized timer for this. >=20 > Signed-off-by: Peter Maydell > --- > target-arm/cpu-qom.h | 9 ++ > target-arm/cpu.c | 9 ++ > target-arm/cpu.h | 18 ++++ > target-arm/helper.c | 255 ++++++++++++++++++++++++++++++++++++++++++= +++++++- > target-arm/machine.c | 8 +- > 5 files changed, 291 insertions(+), 8 deletions(-) >=20 > diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h > index cf36587..0b0b790 100644 > --- a/target-arm/cpu-qom.h > +++ b/target-arm/cpu-qom.h > @@ -86,6 +86,11 @@ typedef struct ARMCPU { > uint64_t *cpreg_vmstate_values; > int32_t cpreg_vmstate_array_len; > =20 > + /* Timers used by the generic (architected) timer */ > + QEMUTimer *gt_timer[NUM_GTIMERS]; > + /* GPIO outputs for generic timer */ > + qemu_irq gt_timer_outputs[NUM_GTIMERS]; > + > /* The instance init functions for implementation-specific subclas= ses > * set these fields to specify the implementation-dependent values= of > * various constant registers and reset values of non-constant > @@ -152,4 +157,8 @@ hwaddr arm_cpu_get_phys_page_debug(CPUState *cpu, v= addr addr); > int arm_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); > int arm_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); > =20 > +/* Callback functions for the generic timer's timers. */ > +void gt_ptimer_cb(void *opaque); > +void gt_vtimer_cb(void *opaque); Please use an arm_ prefix for globally visible symbols. One minor nit below although I didn't review in detail. Andreas > + > #endif > diff --git a/target-arm/cpu.c b/target-arm/cpu.c > index 87d35c6..3a10cf7 100644 > --- a/target-arm/cpu.c > +++ b/target-arm/cpu.c > @@ -145,6 +145,15 @@ static void arm_cpu_initfn(Object *obj) > cpu->cp_regs =3D g_hash_table_new_full(g_int_hash, g_int_equal, > g_free, g_free); > =20 > +#ifndef CONFIG_USER_ONLY > + cpu->gt_timer[GTIMER_PHYS] =3D qemu_new_timer(vm_clock, GTIMER_SCA= LE, > + gt_ptimer_cb, cpu); > + cpu->gt_timer[GTIMER_VIRT] =3D qemu_new_timer(vm_clock, GTIMER_SCA= LE, > + gt_vtimer_cb, cpu); > + qdev_init_gpio_out(DEVICE(cpu), cpu->gt_timer_outputs, > + ARRAY_SIZE(cpu->gt_timer_outputs)); > +#endif > + > if (tcg_enabled() && !inited) { > inited =3D true; > arm_translate_init(); > diff --git a/target-arm/cpu.h b/target-arm/cpu.h > index 770a240..27715b5 100644 > --- a/target-arm/cpu.h > +++ b/target-arm/cpu.h > @@ -76,6 +76,21 @@ struct arm_boot_info; > s<2n+1> maps to the most significant half of d > */ > =20 > +/* CPU state for each instance of a generic timer (in cp15 c14) */ > +typedef struct ARMGenericTimer { > + uint64_t cval; /* Timer CompareValue register */ > + uint32_t ctl; /* Timer Control register */ > +} ARMGenericTimer; > + > +#define GTIMER_PHYS 0 > +#define GTIMER_VIRT 1 > +#define NUM_GTIMERS 2 > + > +/* Scale factor for generic timers, ie number of ns per tick. > + * This gives a 62.5MHz timer. > + */ > +#define GTIMER_SCALE 16 > + > typedef struct CPUARMState { > /* Regs for current mode. */ > uint32_t regs[16]; > @@ -143,6 +158,9 @@ typedef struct CPUARMState { > uint32_t c13_tls1; /* User RW Thread register. */ > uint32_t c13_tls2; /* User RO Thread register. */ > uint32_t c13_tls3; /* Privileged Thread register. */ > + uint32_t c14_cntfrq; /* Counter Frequency register */ > + uint32_t c14_cntkctl; /* Timer Control register */ > + ARMGenericTimer c14_timer[NUM_GTIMERS]; > uint32_t c15_cpar; /* XScale Coprocessor Access Register */ > uint32_t c15_ticonfig; /* TI925T configuration byte. */ > uint32_t c15_i_max; /* Maximum D-cache dirty line index. */ > diff --git a/target-arm/helper.c b/target-arm/helper.c > index fc5f757..9f1336a 100644 > --- a/target-arm/helper.c > +++ b/target-arm/helper.c > @@ -695,15 +695,260 @@ static const ARMCPRegInfo v6k_cp_reginfo[] =3D { > REGINFO_SENTINEL > }; > =20 > +#ifndef CONFIG_USER_ONLY > + > +static uint64_t gt_get_countervalue(CPUARMState *env) > +{ > + return qemu_get_clock_ns(vm_clock) / GTIMER_SCALE; > +} > + > +static void gt_recalc_timer(ARMCPU *cpu, int timeridx) > +{ > + ARMGenericTimer *gt =3D &cpu->env.cp15.c14_timer[timeridx]; > + > + if (gt->ctl & 1) { > + /* Timer enabled: calculate and set current ISTATUS, irq, and > + * reset timer to when ISTATUS next has to change > + */ > + uint64_t count =3D gt_get_countervalue(&cpu->env); > + /* Note that this must be unsigned 64 bit arithmetic: */ > + int istatus =3D count >=3D gt->cval; > + uint64_t nexttick; > + > + gt->ctl =3D deposit32(gt->ctl, 2, 1, istatus); > + qemu_set_irq(cpu->gt_timer_outputs[timeridx], > + (istatus && !(gt->ctl & 2))); > + if (istatus) { > + /* Next transition is when count rolls back over to zero *= / > + nexttick =3D UINT64_MAX; > + } else { > + /* Next transition is when we hit cval */ > + nexttick =3D gt->cval; > + } > + /* Note that the desired next expiry time might be beyond the > + * signed-64-bit range of a QEMUTimer -- in this case we just > + * set the timer for as far in the future as possible. When th= e > + * timer expires we will reset the timer for any remaining per= iod. > + */ > + if (nexttick > INT64_MAX / GTIMER_SCALE) { > + nexttick =3D INT64_MAX / GTIMER_SCALE; > + } > + qemu_mod_timer(cpu->gt_timer[timeridx], nexttick); > + } else { > + /* Timer disabled: ISTATUS and timer output always clear */ > + gt->ctl &=3D ~4; > + qemu_set_irq(cpu->gt_timer_outputs[timeridx], 0); > + qemu_del_timer(cpu->gt_timer[timeridx]); > + } > +} > + > +static int gt_cntfrq_read(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t *value) > +{ > + /* Not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero */ > + if (arm_current_pl(env) =3D=3D 0 && !extract32(env->cp15.c14_cntkc= tl, 0, 2)) { > + return EXCP_UDEF; > + } > + *value =3D env->cp15.c14_cntfrq; > + return 0; > +} > + > +static void gt_cnt_reset(CPUARMState *env, const ARMCPRegInfo *ri) > +{ > + ARMCPU *cpu =3D arm_env_get_cpu(env); > + int timeridx =3D ri->opc1 & 1; > + > + qemu_del_timer(cpu->gt_timer[timeridx]); > +} > + > +static int gt_cnt_read(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t *value) > +{ > + int timeridx =3D ri->opc1 & 1; > + > + if (arm_current_pl(env) =3D=3D 0 && > + !extract32(env->cp15.c14_cntkctl, timeridx, 1)) { > + return EXCP_UDEF; > + } > + *value =3D gt_get_countervalue(env); > + return 0; > +} > + > +static int gt_cval_read(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t *value) > +{ > + int timeridx =3D ri->opc1 & 1; > + > + if (arm_current_pl(env) =3D=3D 0 && > + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { > + return EXCP_UDEF; > + } > + *value =3D env->cp15.c14_timer[timeridx].cval; > + return 0; > +} > + > +static int gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t value) > +{ > + int timeridx =3D ri->opc1 & 1; > + > + env->cp15.c14_timer[timeridx].cval =3D value; > + gt_recalc_timer(arm_env_get_cpu(env), timeridx); > + return 0; > +} > +static int gt_tval_read(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t *value) > +{ > + int timeridx =3D ri->crm & 1; > + > + if (arm_current_pl(env) =3D=3D 0 && > + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { > + return EXCP_UDEF; > + } > + *value =3D (uint32_t)(env->cp15.c14_timer[timeridx].cval - > + gt_get_countervalue(env)); > + return 0; > +} > + > +static int gt_tval_write(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t value) > +{ > + int timeridx =3D ri->crm & 1; > + > + env->cp15.c14_timer[timeridx].cval =3D gt_get_countervalue(env) + > + + sextract64(value, 0, 32); > + gt_recalc_timer(arm_env_get_cpu(env), timeridx); > + return 0; > +} > + > +static int gt_ctl_read(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t *value) > +{ > + int timeridx =3D ri->crm & 1; > + > + if (arm_current_pl(env) =3D=3D 0 && > + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) { > + return EXCP_UDEF; > + } > + *value =3D env->cp15.c14_timer[timeridx].ctl; > + return 0; > +} > + > +static int gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, > + uint64_t value) > +{ > + ARMCPU *cpu =3D arm_env_get_cpu(env); > + int timeridx =3D ri->crm & 1; > + uint32_t oldval =3D env->cp15.c14_timer[timeridx].ctl; > + > + env->cp15.c14_timer[timeridx].ctl =3D value & 3; > + if ((oldval ^ value) & 1) { > + /* Enable toggled */ > + gt_recalc_timer(cpu, timeridx); > + } else if ((oldval & value) & 2) { > + /* IMASK toggled: don't need to recalculate, > + * just set the interrupt line based on ISTATUS > + */ > + qemu_set_irq(cpu->gt_timer_outputs[timeridx], > + (oldval & 4) && (value & 2)); > + } > + return 0; > +} > + > +void gt_ptimer_cb(void *opaque) > +{ > + ARMCPU *cpu =3D opaque; > + > + gt_recalc_timer(cpu, GTIMER_PHYS); > +} White line missing. > +void gt_vtimer_cb(void *opaque) > +{ > + ARMCPU *cpu =3D opaque; > + > + gt_recalc_timer(cpu, GTIMER_VIRT); > +} > + > static const ARMCPRegInfo generic_timer_cp_reginfo[] =3D { > - /* Dummy implementation: RAZ/WI the whole crn=3D14 space */ > - { .name =3D "GENERIC_TIMER", .cp =3D 15, .crn =3D 14, > - .crm =3D CP_ANY, .opc1 =3D CP_ANY, .opc2 =3D CP_ANY, > - .access =3D PL1_RW, .type =3D ARM_CP_CONST | ARM_CP_NO_MIGRATE, > - .resetvalue =3D 0 }, > + /* Note that CNTFRQ is purely reads-as-written for the benefit > + * of software; writing it doesn't actually change the timer frequ= ency. > + * Our reset value matches the fixed frequency we implement the ti= mer at. > + */ > + { .name =3D "CNTFRQ", .cp =3D 15, .crn =3D 14, .crm =3D 0, .opc1 =3D= 0, .opc2 =3D 0, > + .access =3D PL1_RW | PL0_R, > + .fieldoffset =3D offsetof(CPUARMState, cp15.c14_cntfrq), > + .resetvalue =3D (1000 * 1000 * 1000) / GTIMER_SCALE, > + .readfn =3D gt_cntfrq_read, .raw_readfn =3D raw_read, > + }, > + /* overall control: mostly access permissions */ > + { .name =3D "CNTKCTL", .cp =3D 15, .crn =3D 14, .crm =3D 1, .opc1 = =3D 0, .opc2 =3D 0, > + .access =3D PL1_RW, > + .fieldoffset =3D offsetof(CPUARMState, cp15.c14_cntkctl), > + .resetvalue =3D 0, > + }, > + /* per-timer control */ > + { .name =3D "CNTP_CTL", .cp =3D 15, .crn =3D 14, .crm =3D 2, .opc1= =3D 0, .opc2 =3D 1, > + .type =3D ARM_CP_IO, .access =3D PL1_RW | PL0_R, > + .fieldoffset =3D offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHY= S].ctl), > + .resetvalue =3D 0, > + .readfn =3D gt_ctl_read, .writefn =3D gt_ctl_write, > + .raw_readfn =3D raw_read, .raw_writefn =3D raw_write, > + }, > + { .name =3D "CNTV_CTL", .cp =3D 15, .crn =3D 14, .crm =3D 3, .opc1= =3D 0, .opc2 =3D 1, > + .type =3D ARM_CP_IO, .access =3D PL1_RW | PL0_R, > + .fieldoffset =3D offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIR= T].ctl), > + .resetvalue =3D 0, > + .readfn =3D gt_ctl_read, .writefn =3D gt_ctl_write, > + .raw_readfn =3D raw_read, .raw_writefn =3D raw_write, > + }, > + /* TimerValue views: a 32 bit downcounting view of the underlying = state */ > + { .name =3D "CNTP_TVAL", .cp =3D 15, .crn =3D 14, .crm =3D 2, .opc= 1 =3D 0, .opc2 =3D 0, > + .type =3D ARM_CP_NO_MIGRATE | ARM_CP_IO, .access =3D PL1_RW | PL= 0_R, > + .readfn =3D gt_tval_read, .writefn =3D gt_tval_write, > + }, > + { .name =3D "CNTV_TVAL", .cp =3D 15, .crn =3D 14, .crm =3D 3, .opc= 1 =3D 0, .opc2 =3D 0, > + .type =3D ARM_CP_NO_MIGRATE | ARM_CP_IO, .access =3D PL1_RW | PL= 0_R, > + .readfn =3D gt_tval_read, .writefn =3D gt_tval_write, > + }, > + /* The counter itself */ > + { .name =3D "CNTPCT", .cp =3D 15, .crm =3D 14, .opc1 =3D 0, > + .access =3D PL0_R, .type =3D ARM_CP_64BIT | ARM_CP_NO_MIGRATE | = ARM_CP_IO, > + .readfn =3D gt_cnt_read, .resetfn =3D gt_cnt_reset, > + }, > + { .name =3D "CNTVCT", .cp =3D 15, .crm =3D 14, .opc1 =3D 1, > + .access =3D PL0_R, .type =3D ARM_CP_64BIT | ARM_CP_NO_MIGRATE | = ARM_CP_IO, > + .readfn =3D gt_cnt_read, .resetfn =3D gt_cnt_reset, > + }, > + /* Comparison value, indicating when the timer goes off */ > + { .name =3D "CNTP_CVAL", .cp =3D 15, .crm =3D 14, .opc1 =3D 2, > + .access =3D PL1_RW | PL0_R, > + .type =3D ARM_CP_64BIT | ARM_CP_IO, > + .fieldoffset =3D offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHY= S].cval), > + .resetvalue =3D 0, > + .readfn =3D gt_cval_read, .writefn =3D gt_cval_write, > + .raw_readfn =3D raw_read, .raw_writefn =3D raw_write, > + }, > + { .name =3D "CNTV_CVAL", .cp =3D 15, .crm =3D 14, .opc1 =3D 3, > + .access =3D PL1_RW | PL0_R, > + .type =3D ARM_CP_64BIT | ARM_CP_IO, > + .fieldoffset =3D offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIR= T].cval), > + .resetvalue =3D 0, > + .readfn =3D gt_cval_read, .writefn =3D gt_cval_write, > + .raw_readfn =3D raw_read, .raw_writefn =3D raw_write, > + }, > REGINFO_SENTINEL > }; > =20 > +#else > +/* In user-mode none of the generic timer registers are accessible, > + * and their implementation depends on vm_clock and qdev gpio outputs, > + * so instead just don't register any of them. > + */ > +static const ARMCPRegInfo generic_timer_cp_reginfo[] =3D { > + REGINFO_SENTINEL > +}; > + > +#endif > + > static int par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_= t value) > { > if (arm_feature(env, ARM_FEATURE_LPAE)) { > diff --git a/target-arm/machine.c b/target-arm/machine.c > index 6d4c2d4..5b6f375 100644 > --- a/target-arm/machine.c > +++ b/target-arm/machine.c > @@ -222,9 +222,9 @@ static int cpu_post_load(void *opaque, int version_= id) > =20 > const VMStateDescription vmstate_arm_cpu =3D { > .name =3D "cpu", > - .version_id =3D 12, > - .minimum_version_id =3D 12, > - .minimum_version_id_old =3D 12, > + .version_id =3D 13, > + .minimum_version_id =3D 13, > + .minimum_version_id_old =3D 13, > .pre_save =3D cpu_pre_save, > .post_load =3D cpu_post_load, > .fields =3D (VMStateField[]) { > @@ -257,6 +257,8 @@ const VMStateDescription vmstate_arm_cpu =3D { > VMSTATE_UINT32(env.exclusive_val, ARMCPU), > VMSTATE_UINT32(env.exclusive_high, ARMCPU), > VMSTATE_UINT64(env.features, ARMCPU), > + VMSTATE_TIMER(gt_timer[GTIMER_PHYS], ARMCPU), > + VMSTATE_TIMER(gt_timer[GTIMER_VIRT], ARMCPU), > VMSTATE_END_OF_LIST() > }, > .subsections =3D (VMStateSubsection[]) { >=20 --=20 SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 N=FCrnberg, Germany GF: Jeff Hawn, Jennifer Guild, Felix Imend=F6rffer; HRB 16746 AG N=FCrnbe= rg