[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer
From: |
Andreas Färber |
Subject: |
Re: [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer |
Date: |
Thu, 01 Aug 2013 17:50:41 +0200 |
User-agent: |
Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130620 Thunderbird/17.0.7 |
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.
>
> Signed-off-by: Peter Maydell <address@hidden>
> ---
> 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(-)
>
> 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;
>
> + /* 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 subclasses
> * 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, vaddr
> 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);
>
> +/* 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 = g_hash_table_new_full(g_int_hash, g_int_equal,
> g_free, g_free);
>
> +#ifndef CONFIG_USER_ONLY
> + cpu->gt_timer[GTIMER_PHYS] = qemu_new_timer(vm_clock, GTIMER_SCALE,
> + gt_ptimer_cb, cpu);
> + cpu->gt_timer[GTIMER_VIRT] = qemu_new_timer(vm_clock, GTIMER_SCALE,
> + 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 = 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<n>
> */
>
> +/* 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[] = {
> REGINFO_SENTINEL
> };
>
> +#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 = &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 = gt_get_countervalue(&cpu->env);
> + /* Note that this must be unsigned 64 bit arithmetic: */
> + int istatus = count >= gt->cval;
> + uint64_t nexttick;
> +
> + gt->ctl = 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 = UINT64_MAX;
> + } else {
> + /* Next transition is when we hit cval */
> + nexttick = 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 the
> + * timer expires we will reset the timer for any remaining period.
> + */
> + if (nexttick > INT64_MAX / GTIMER_SCALE) {
> + nexttick = INT64_MAX / GTIMER_SCALE;
> + }
> + qemu_mod_timer(cpu->gt_timer[timeridx], nexttick);
> + } else {
> + /* Timer disabled: ISTATUS and timer output always clear */
> + gt->ctl &= ~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) == 0 && !extract32(env->cp15.c14_cntkctl, 0, 2))
> {
> + return EXCP_UDEF;
> + }
> + *value = env->cp15.c14_cntfrq;
> + return 0;
> +}
> +
> +static void gt_cnt_reset(CPUARMState *env, const ARMCPRegInfo *ri)
> +{
> + ARMCPU *cpu = arm_env_get_cpu(env);
> + int timeridx = 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 = ri->opc1 & 1;
> +
> + if (arm_current_pl(env) == 0 &&
> + !extract32(env->cp15.c14_cntkctl, timeridx, 1)) {
> + return EXCP_UDEF;
> + }
> + *value = gt_get_countervalue(env);
> + return 0;
> +}
> +
> +static int gt_cval_read(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t *value)
> +{
> + int timeridx = ri->opc1 & 1;
> +
> + if (arm_current_pl(env) == 0 &&
> + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) {
> + return EXCP_UDEF;
> + }
> + *value = env->cp15.c14_timer[timeridx].cval;
> + return 0;
> +}
> +
> +static int gt_cval_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> +{
> + int timeridx = ri->opc1 & 1;
> +
> + env->cp15.c14_timer[timeridx].cval = 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 = ri->crm & 1;
> +
> + if (arm_current_pl(env) == 0 &&
> + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) {
> + return EXCP_UDEF;
> + }
> + *value = (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 = ri->crm & 1;
> +
> + env->cp15.c14_timer[timeridx].cval = 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 = ri->crm & 1;
> +
> + if (arm_current_pl(env) == 0 &&
> + !extract32(env->cp15.c14_cntkctl, 9 - timeridx, 1)) {
> + return EXCP_UDEF;
> + }
> + *value = env->cp15.c14_timer[timeridx].ctl;
> + return 0;
> +}
> +
> +static int gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri,
> + uint64_t value)
> +{
> + ARMCPU *cpu = arm_env_get_cpu(env);
> + int timeridx = ri->crm & 1;
> + uint32_t oldval = env->cp15.c14_timer[timeridx].ctl;
> +
> + env->cp15.c14_timer[timeridx].ctl = 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 = opaque;
> +
> + gt_recalc_timer(cpu, GTIMER_PHYS);
> +}
White line missing.
> +void gt_vtimer_cb(void *opaque)
> +{
> + ARMCPU *cpu = opaque;
> +
> + gt_recalc_timer(cpu, GTIMER_VIRT);
> +}
> +
> static const ARMCPRegInfo generic_timer_cp_reginfo[] = {
> - /* Dummy implementation: RAZ/WI the whole crn=14 space */
> - { .name = "GENERIC_TIMER", .cp = 15, .crn = 14,
> - .crm = CP_ANY, .opc1 = CP_ANY, .opc2 = CP_ANY,
> - .access = PL1_RW, .type = ARM_CP_CONST | ARM_CP_NO_MIGRATE,
> - .resetvalue = 0 },
> + /* Note that CNTFRQ is purely reads-as-written for the benefit
> + * of software; writing it doesn't actually change the timer frequency.
> + * Our reset value matches the fixed frequency we implement the timer at.
> + */
> + { .name = "CNTFRQ", .cp = 15, .crn = 14, .crm = 0, .opc1 = 0, .opc2 = 0,
> + .access = PL1_RW | PL0_R,
> + .fieldoffset = offsetof(CPUARMState, cp15.c14_cntfrq),
> + .resetvalue = (1000 * 1000 * 1000) / GTIMER_SCALE,
> + .readfn = gt_cntfrq_read, .raw_readfn = raw_read,
> + },
> + /* overall control: mostly access permissions */
> + { .name = "CNTKCTL", .cp = 15, .crn = 14, .crm = 1, .opc1 = 0, .opc2 = 0,
> + .access = PL1_RW,
> + .fieldoffset = offsetof(CPUARMState, cp15.c14_cntkctl),
> + .resetvalue = 0,
> + },
> + /* per-timer control */
> + { .name = "CNTP_CTL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 =
> 1,
> + .type = ARM_CP_IO, .access = PL1_RW | PL0_R,
> + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl),
> + .resetvalue = 0,
> + .readfn = gt_ctl_read, .writefn = gt_ctl_write,
> + .raw_readfn = raw_read, .raw_writefn = raw_write,
> + },
> + { .name = "CNTV_CTL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 =
> 1,
> + .type = ARM_CP_IO, .access = PL1_RW | PL0_R,
> + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl),
> + .resetvalue = 0,
> + .readfn = gt_ctl_read, .writefn = gt_ctl_write,
> + .raw_readfn = raw_read, .raw_writefn = raw_write,
> + },
> + /* TimerValue views: a 32 bit downcounting view of the underlying state
> */
> + { .name = "CNTP_TVAL", .cp = 15, .crn = 14, .crm = 2, .opc1 = 0, .opc2 =
> 0,
> + .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R,
> + .readfn = gt_tval_read, .writefn = gt_tval_write,
> + },
> + { .name = "CNTV_TVAL", .cp = 15, .crn = 14, .crm = 3, .opc1 = 0, .opc2 =
> 0,
> + .type = ARM_CP_NO_MIGRATE | ARM_CP_IO, .access = PL1_RW | PL0_R,
> + .readfn = gt_tval_read, .writefn = gt_tval_write,
> + },
> + /* The counter itself */
> + { .name = "CNTPCT", .cp = 15, .crm = 14, .opc1 = 0,
> + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO,
> + .readfn = gt_cnt_read, .resetfn = gt_cnt_reset,
> + },
> + { .name = "CNTVCT", .cp = 15, .crm = 14, .opc1 = 1,
> + .access = PL0_R, .type = ARM_CP_64BIT | ARM_CP_NO_MIGRATE | ARM_CP_IO,
> + .readfn = gt_cnt_read, .resetfn = gt_cnt_reset,
> + },
> + /* Comparison value, indicating when the timer goes off */
> + { .name = "CNTP_CVAL", .cp = 15, .crm = 14, .opc1 = 2,
> + .access = PL1_RW | PL0_R,
> + .type = ARM_CP_64BIT | ARM_CP_IO,
> + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval),
> + .resetvalue = 0,
> + .readfn = gt_cval_read, .writefn = gt_cval_write,
> + .raw_readfn = raw_read, .raw_writefn = raw_write,
> + },
> + { .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3,
> + .access = PL1_RW | PL0_R,
> + .type = ARM_CP_64BIT | ARM_CP_IO,
> + .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval),
> + .resetvalue = 0,
> + .readfn = gt_cval_read, .writefn = gt_cval_write,
> + .raw_readfn = raw_read, .raw_writefn = raw_write,
> + },
> REGINFO_SENTINEL
> };
>
> +#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[] = {
> + 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)
>
> const VMStateDescription vmstate_arm_cpu = {
> .name = "cpu",
> - .version_id = 12,
> - .minimum_version_id = 12,
> - .minimum_version_id_old = 12,
> + .version_id = 13,
> + .minimum_version_id = 13,
> + .minimum_version_id_old = 13,
> .pre_save = cpu_pre_save,
> .post_load = cpu_post_load,
> .fields = (VMStateField[]) {
> @@ -257,6 +257,8 @@ const VMStateDescription vmstate_arm_cpu = {
> 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 = (VMStateSubsection[]) {
>
--
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Re: [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer,
Andreas Färber <=