qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers
@ 2013-07-30 17:55 Peter Maydell
  2013-07-30 17:55 ` [Qemu-devel] [PATCH 1/4] target-arm: Allow raw_read() and raw_write() to handle 64 bit regs Peter Maydell
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Peter Maydell @ 2013-07-30 17:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: patches

This patch series implements support for the 'generic timers',
which are a set of timers defined in the ARM Architecture Reference
Manual and implemented by the Cortex-A15. We've got away without
these up til now because Linux will generally fall back on whatever
random timer is present on the devboard if it can't find the
on-CPU timers, but this is less than ideal. (Among other things,
the proposed mach-virt should just use the generic timers so it
doesn't have to provide an sp804 timer.)

Peter Maydell (4):
  target-arm: Allow raw_read() and raw_write() to handle 64 bit regs
  target-arm: Support coprocessor registers which do I/O
  target-arm: Implement the generic timer
  hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs

 hw/cpu/a15mpcore.c     |   18 ++++
 target-arm/cpu-qom.h   |    9 ++
 target-arm/cpu.c       |    9 ++
 target-arm/cpu.h       |   24 ++++-
 target-arm/helper.c    |  267 ++++++++++++++++++++++++++++++++++++++++++++++--
 target-arm/machine.c   |    8 +-
 target-arm/translate.c |   16 ++-
 7 files changed, 337 insertions(+), 14 deletions(-)

-- 
1.7.9.5

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 1/4] target-arm: Allow raw_read() and raw_write() to handle 64 bit regs
  2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell
@ 2013-07-30 17:55 ` Peter Maydell
  2013-07-30 17:55 ` [Qemu-devel] [PATCH 2/4] target-arm: Support coprocessor registers which do I/O Peter Maydell
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Peter Maydell @ 2013-07-30 17:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: patches

Extend the raw_read() and raw_write() helper accessors so that
they can be used for 64 bit registers as well as 32 bit registers.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 target-arm/helper.c |   12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/target-arm/helper.c b/target-arm/helper.c
index 4968391..fc5f757 100644
--- a/target-arm/helper.c
+++ b/target-arm/helper.c
@@ -67,14 +67,22 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg)
 static int raw_read(CPUARMState *env, const ARMCPRegInfo *ri,
                     uint64_t *value)
 {
-    *value = CPREG_FIELD32(env, ri);
+    if (ri->type & ARM_CP_64BIT) {
+        *value = CPREG_FIELD64(env, ri);
+    } else {
+        *value = CPREG_FIELD32(env, ri);
+    }
     return 0;
 }
 
 static int raw_write(CPUARMState *env, const ARMCPRegInfo *ri,
                      uint64_t value)
 {
-    CPREG_FIELD32(env, ri) = value;
+    if (ri->type & ARM_CP_64BIT) {
+        CPREG_FIELD64(env, ri) = value;
+    } else {
+        CPREG_FIELD32(env, ri) = value;
+    }
     return 0;
 }
 
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 2/4] target-arm: Support coprocessor registers which do I/O
  2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell
  2013-07-30 17:55 ` [Qemu-devel] [PATCH 1/4] target-arm: Allow raw_read() and raw_write() to handle 64 bit regs Peter Maydell
@ 2013-07-30 17:55 ` Peter Maydell
  2013-07-30 17:55 ` [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer Peter Maydell
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Peter Maydell @ 2013-07-30 17:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: patches

Add an ARM_CP_IO flag which an ARMCPRegInfo definition can use to
indicate that the register's implementation does I/O and thus
its accesses need to be surrounded by gen_io_start()/gen_io_end()
in order for icount to work. Most notably, cp registers which
implement clocks or timers need this.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 target-arm/cpu.h       |    6 +++++-
 target-arm/translate.c |   16 +++++++++++++---
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/target-arm/cpu.h b/target-arm/cpu.h
index b2dc494..770a240 100644
--- a/target-arm/cpu.h
+++ b/target-arm/cpu.h
@@ -469,6 +469,9 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid)
  * old must have the OVERRIDE bit set.
  * NO_MIGRATE indicates that this register should be ignored for migration;
  * (eg because any state is accessed via some other coprocessor register).
+ * IO indicates that this register does I/O and therefore its accesses
+ * need to be surrounded by gen_io_start()/gen_io_end(). In particular,
+ * registers which implement clocks or timers require this.
  */
 #define ARM_CP_SPECIAL 1
 #define ARM_CP_CONST 2
@@ -476,13 +479,14 @@ static inline uint64_t cpreg_to_kvm_id(uint32_t cpregid)
 #define ARM_CP_SUPPRESS_TB_END 8
 #define ARM_CP_OVERRIDE 16
 #define ARM_CP_NO_MIGRATE 32
+#define ARM_CP_IO 64
 #define ARM_CP_NOP (ARM_CP_SPECIAL | (1 << 8))
 #define ARM_CP_WFI (ARM_CP_SPECIAL | (2 << 8))
 #define ARM_LAST_SPECIAL ARM_CP_WFI
 /* Used only as a terminator for ARMCPRegInfo lists */
 #define ARM_CP_SENTINEL 0xffff
 /* Mask of only the flag bits in a type field */
-#define ARM_CP_FLAG_MASK 0x3f
+#define ARM_CP_FLAG_MASK 0x7f
 
 /* Return true if cptype is a valid type field. This is used to try to
  * catch errors where the sentinel has been accidentally left off the end
diff --git a/target-arm/translate.c b/target-arm/translate.c
index 6db4c50..d1e8538 100644
--- a/target-arm/translate.c
+++ b/target-arm/translate.c
@@ -6280,6 +6280,10 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
             break;
         }
 
+        if (use_icount && (ri->type & ARM_CP_IO)) {
+            gen_io_start();
+        }
+
         if (isread) {
             /* Read */
             if (is64) {
@@ -6369,14 +6373,20 @@ static int disas_coproc_insn(CPUARMState * env, DisasContext *s, uint32_t insn)
                     store_cpu_offset(tmp, ri->fieldoffset);
                 }
             }
+        }
+
+        if (use_icount && (ri->type & ARM_CP_IO)) {
+            /* I/O operations must end the TB here (whether read or write) */
+            gen_io_end();
+            gen_lookup_tb(s);
+        } else if (!isread && !(ri->type & ARM_CP_SUPPRESS_TB_END)) {
             /* We default to ending the TB on a coprocessor register write,
              * but allow this to be suppressed by the register definition
              * (usually only necessary to work around guest bugs).
              */
-            if (!(ri->type & ARM_CP_SUPPRESS_TB_END)) {
-                gen_lookup_tb(s);
-            }
+            gen_lookup_tb(s);
         }
+
         return 0;
     }
 
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer
  2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell
  2013-07-30 17:55 ` [Qemu-devel] [PATCH 1/4] target-arm: Allow raw_read() and raw_write() to handle 64 bit regs Peter Maydell
  2013-07-30 17:55 ` [Qemu-devel] [PATCH 2/4] target-arm: Support coprocessor registers which do I/O Peter Maydell
@ 2013-07-30 17:55 ` Peter Maydell
  2013-08-01 15:50   ` Andreas Färber
  2013-07-30 17:55 ` [Qemu-devel] [PATCH 4/4] hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs Peter Maydell
  2013-08-01  9:39 ` [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Laurent Desnogues
  4 siblings, 1 reply; 7+ messages in thread
From: Peter Maydell @ 2013-07-30 17:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: patches

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 <peter.maydell@linaro.org>
---
 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);
+
 #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);
+}
+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[]) {
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH 4/4] hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs
  2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell
                   ` (2 preceding siblings ...)
  2013-07-30 17:55 ` [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer Peter Maydell
@ 2013-07-30 17:55 ` Peter Maydell
  2013-08-01  9:39 ` [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Laurent Desnogues
  4 siblings, 0 replies; 7+ messages in thread
From: Peter Maydell @ 2013-07-30 17:55 UTC (permalink / raw)
  To: qemu-devel; +Cc: patches

Now our A15 CPU implements the generic timers, we can wire them
up to the appropriate inputs on the GIC.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 hw/cpu/a15mpcore.c |   18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c
index c736257..8c23020 100644
--- a/hw/cpu/a15mpcore.c
+++ b/hw/cpu/a15mpcore.c
@@ -42,6 +42,8 @@ static int a15mp_priv_init(SysBusDevice *dev)
     A15MPPrivState *s = FROM_SYSBUS(A15MPPrivState, dev);
     SysBusDevice *busdev;
     const char *gictype = "arm_gic";
+    int i;
+    CPUState *cpu;
 
     if (kvm_irqchip_in_kernel()) {
         gictype = "kvm-arm-gic";
@@ -60,6 +62,22 @@ static int a15mp_priv_init(SysBusDevice *dev)
     /* Pass through inbound GPIO lines to the GIC */
     qdev_init_gpio_in(&s->busdev.qdev, a15mp_priv_set_irq, s->num_irq - 32);
 
+    /* Wire the outputs from each CPU's generic timer to the
+     * appropriate GIC PPI inputs
+     */
+    for (i = 0, cpu = first_cpu; i < s->num_cpu; i++, cpu = cpu->next_cpu) {
+        DeviceState *cpudev = DEVICE(cpu);
+        int ppibase = s->num_irq - 32 + i * 32;
+        /* physical timer; we wire it up to the non-secure timer's ID,
+         * since a real A15 always has TrustZone but QEMU doesn't.
+         */
+        qdev_connect_gpio_out(cpudev, 0,
+                              qdev_get_gpio_in(s->gic, ppibase + 30));
+        /* virtual timer */
+        qdev_connect_gpio_out(cpudev, 1,
+                              qdev_get_gpio_in(s->gic, ppibase + 27));
+    }
+
     /* Memory map (addresses are offsets from PERIPHBASE):
      *  0x0000-0x0fff -- reserved
      *  0x1000-0x1fff -- GIC Distributor
-- 
1.7.9.5

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers
  2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell
                   ` (3 preceding siblings ...)
  2013-07-30 17:55 ` [Qemu-devel] [PATCH 4/4] hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs Peter Maydell
@ 2013-08-01  9:39 ` Laurent Desnogues
  4 siblings, 0 replies; 7+ messages in thread
From: Laurent Desnogues @ 2013-08-01  9:39 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel@nongnu.org, patches

Hi,

On Tue, Jul 30, 2013 at 7:55 PM, Peter Maydell <peter.maydell@linaro.org> wrote:
> This patch series implements support for the 'generic timers',
> which are a set of timers defined in the ARM Architecture Reference
> Manual and implemented by the Cortex-A15. We've got away without
> these up til now because Linux will generally fall back on whatever
> random timer is present on the devboard if it can't find the
> on-CPU timers, but this is less than ideal. (Among other things,
> the proposed mach-virt should just use the generic timers so it
> doesn't have to provide an sp804 timer.)
>
> Peter Maydell (4):
>   target-arm: Allow raw_read() and raw_write() to handle 64 bit regs
>   target-arm: Support coprocessor registers which do I/O
>   target-arm: Implement the generic timer
>   hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs
>
>  hw/cpu/a15mpcore.c     |   18 ++++
>  target-arm/cpu-qom.h   |    9 ++
>  target-arm/cpu.c       |    9 ++
>  target-arm/cpu.h       |   24 ++++-
>  target-arm/helper.c    |  267 ++++++++++++++++++++++++++++++++++++++++++++++--
>  target-arm/machine.c   |    8 +-
>  target-arm/translate.c |   16 ++-
>  7 files changed, 337 insertions(+), 14 deletions(-)

I tested this patch series with a 3.9 kernel for Vexpress
with Cortex-A15.  It works fine with the generic timer
correctly detected and used.

Note the 4th patch doesn't apply as is any more.

Tested-by: Laurent Desnogues <laurent.desnogues@gmail.com>

Thanks,

Laurent

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer
  2013-07-30 17:55 ` [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer Peter Maydell
@ 2013-08-01 15:50   ` Andreas Färber
  0 siblings, 0 replies; 7+ messages in thread
From: Andreas Färber @ 2013-08-01 15:50 UTC (permalink / raw)
  To: Peter Maydell; +Cc: qemu-devel, patches

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 <peter.maydell@linaro.org>
> ---
>  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

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2013-08-02  4:57 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-07-30 17:55 [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Peter Maydell
2013-07-30 17:55 ` [Qemu-devel] [PATCH 1/4] target-arm: Allow raw_read() and raw_write() to handle 64 bit regs Peter Maydell
2013-07-30 17:55 ` [Qemu-devel] [PATCH 2/4] target-arm: Support coprocessor registers which do I/O Peter Maydell
2013-07-30 17:55 ` [Qemu-devel] [PATCH 3/4] target-arm: Implement the generic timer Peter Maydell
2013-08-01 15:50   ` Andreas Färber
2013-07-30 17:55 ` [Qemu-devel] [PATCH 4/4] hw/cpu/a15mpcore: Wire generic timer outputs to GIC inputs Peter Maydell
2013-08-01  9:39 ` [Qemu-devel] [PATCH 0/4] target-arm: Implement support for generic timers Laurent Desnogues

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).