diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2013-01-23 13:21:58 -0500 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2013-02-11 14:05:11 -0500 |
commit | 53e724067a4ee9373972079e225d0d5f683b9c5a (patch) | |
tree | de8cd94b10d2cd768e990a38a95d8a40ab3dac0a /arch/arm | |
parent | 9ae9e2535d7dd1c21d6a7db1a7f2fc507a5e4080 (diff) |
ARM: KVM: arch_timers: Add guest timer core support
Add some the architected timer related infrastructure, and support timer
interrupt injection, which can happen as a resultof three possible
events:
- The virtual timer interrupt has fired while we were still
executing the guest
- The timer interrupt hasn't fired, but it expired while we
were doing the world switch
- A hrtimer we programmed earlier has fired
Reviewed-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Christoffer Dall <c.dall@virtualopensystems.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Diffstat (limited to 'arch/arm')
-rw-r--r-- | arch/arm/include/asm/kvm_arch_timer.h | 85 | ||||
-rw-r--r-- | arch/arm/include/asm/kvm_host.h | 5 | ||||
-rw-r--r-- | arch/arm/kvm/arch_timer.c | 271 | ||||
-rw-r--r-- | arch/arm/kvm/interrupts.S | 2 | ||||
-rw-r--r-- | arch/arm/kvm/interrupts_head.S | 31 |
5 files changed, 394 insertions, 0 deletions
diff --git a/arch/arm/include/asm/kvm_arch_timer.h b/arch/arm/include/asm/kvm_arch_timer.h new file mode 100644 index 000000000000..68cb9e1dfb81 --- /dev/null +++ b/arch/arm/include/asm/kvm_arch_timer.h | |||
@@ -0,0 +1,85 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 ARM Ltd. | ||
3 | * Author: Marc Zyngier <marc.zyngier@arm.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | */ | ||
18 | |||
19 | #ifndef __ASM_ARM_KVM_ARCH_TIMER_H | ||
20 | #define __ASM_ARM_KVM_ARCH_TIMER_H | ||
21 | |||
22 | #include <linux/clocksource.h> | ||
23 | #include <linux/hrtimer.h> | ||
24 | #include <linux/workqueue.h> | ||
25 | |||
26 | struct arch_timer_kvm { | ||
27 | #ifdef CONFIG_KVM_ARM_TIMER | ||
28 | /* Is the timer enabled */ | ||
29 | bool enabled; | ||
30 | |||
31 | /* Virtual offset */ | ||
32 | cycle_t cntvoff; | ||
33 | #endif | ||
34 | }; | ||
35 | |||
36 | struct arch_timer_cpu { | ||
37 | #ifdef CONFIG_KVM_ARM_TIMER | ||
38 | /* Registers: control register, timer value */ | ||
39 | u32 cntv_ctl; /* Saved/restored */ | ||
40 | cycle_t cntv_cval; /* Saved/restored */ | ||
41 | |||
42 | /* | ||
43 | * Anything that is not used directly from assembly code goes | ||
44 | * here. | ||
45 | */ | ||
46 | |||
47 | /* Background timer used when the guest is not running */ | ||
48 | struct hrtimer timer; | ||
49 | |||
50 | /* Work queued with the above timer expires */ | ||
51 | struct work_struct expired; | ||
52 | |||
53 | /* Background timer active */ | ||
54 | bool armed; | ||
55 | |||
56 | /* Timer IRQ */ | ||
57 | const struct kvm_irq_level *irq; | ||
58 | #endif | ||
59 | }; | ||
60 | |||
61 | #ifdef CONFIG_KVM_ARM_TIMER | ||
62 | int kvm_timer_hyp_init(void); | ||
63 | int kvm_timer_init(struct kvm *kvm); | ||
64 | void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu); | ||
65 | void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu); | ||
66 | void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu); | ||
67 | void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu); | ||
68 | #else | ||
69 | static inline int kvm_timer_hyp_init(void) | ||
70 | { | ||
71 | return 0; | ||
72 | }; | ||
73 | |||
74 | static inline int kvm_timer_init(struct kvm *kvm) | ||
75 | { | ||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static inline void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) {} | ||
80 | static inline void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) {} | ||
81 | static inline void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) {} | ||
82 | static inline void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) {} | ||
83 | #endif | ||
84 | |||
85 | #endif | ||
diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 6791c888b9f4..dfe98866a992 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <asm/kvm_asm.h> | 23 | #include <asm/kvm_asm.h> |
24 | #include <asm/kvm_mmio.h> | 24 | #include <asm/kvm_mmio.h> |
25 | #include <asm/fpstate.h> | 25 | #include <asm/fpstate.h> |
26 | #include <asm/kvm_arch_timer.h> | ||
26 | 27 | ||
27 | #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS | 28 | #define KVM_MAX_VCPUS CONFIG_KVM_ARM_MAX_VCPUS |
28 | #define KVM_MEMORY_SLOTS 32 | 29 | #define KVM_MEMORY_SLOTS 32 |
@@ -49,6 +50,9 @@ struct kvm_arch { | |||
49 | /* VTTBR value associated with below pgd and vmid */ | 50 | /* VTTBR value associated with below pgd and vmid */ |
50 | u64 vttbr; | 51 | u64 vttbr; |
51 | 52 | ||
53 | /* Timer */ | ||
54 | struct arch_timer_kvm timer; | ||
55 | |||
52 | /* | 56 | /* |
53 | * Anything that is not used directly from assembly code goes | 57 | * Anything that is not used directly from assembly code goes |
54 | * here. | 58 | * here. |
@@ -99,6 +103,7 @@ struct kvm_vcpu_arch { | |||
99 | 103 | ||
100 | /* VGIC state */ | 104 | /* VGIC state */ |
101 | struct vgic_cpu vgic_cpu; | 105 | struct vgic_cpu vgic_cpu; |
106 | struct arch_timer_cpu timer_cpu; | ||
102 | 107 | ||
103 | /* | 108 | /* |
104 | * Anything that is not used directly from assembly code goes | 109 | * Anything that is not used directly from assembly code goes |
diff --git a/arch/arm/kvm/arch_timer.c b/arch/arm/kvm/arch_timer.c new file mode 100644 index 000000000000..6ac938d46297 --- /dev/null +++ b/arch/arm/kvm/arch_timer.c | |||
@@ -0,0 +1,271 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2012 ARM Ltd. | ||
3 | * Author: Marc Zyngier <marc.zyngier@arm.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | * You should have received a copy of the GNU General Public License | ||
15 | * along with this program; if not, write to the Free Software | ||
16 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
17 | */ | ||
18 | |||
19 | #include <linux/cpu.h> | ||
20 | #include <linux/of_irq.h> | ||
21 | #include <linux/kvm.h> | ||
22 | #include <linux/kvm_host.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | |||
25 | #include <asm/arch_timer.h> | ||
26 | |||
27 | #include <asm/kvm_vgic.h> | ||
28 | #include <asm/kvm_arch_timer.h> | ||
29 | |||
30 | static struct timecounter *timecounter; | ||
31 | static struct workqueue_struct *wqueue; | ||
32 | static struct kvm_irq_level timer_irq = { | ||
33 | .level = 1, | ||
34 | }; | ||
35 | |||
36 | static cycle_t kvm_phys_timer_read(void) | ||
37 | { | ||
38 | return timecounter->cc->read(timecounter->cc); | ||
39 | } | ||
40 | |||
41 | static bool timer_is_armed(struct arch_timer_cpu *timer) | ||
42 | { | ||
43 | return timer->armed; | ||
44 | } | ||
45 | |||
46 | /* timer_arm: as in "arm the timer", not as in ARM the company */ | ||
47 | static void timer_arm(struct arch_timer_cpu *timer, u64 ns) | ||
48 | { | ||
49 | timer->armed = true; | ||
50 | hrtimer_start(&timer->timer, ktime_add_ns(ktime_get(), ns), | ||
51 | HRTIMER_MODE_ABS); | ||
52 | } | ||
53 | |||
54 | static void timer_disarm(struct arch_timer_cpu *timer) | ||
55 | { | ||
56 | if (timer_is_armed(timer)) { | ||
57 | hrtimer_cancel(&timer->timer); | ||
58 | cancel_work_sync(&timer->expired); | ||
59 | timer->armed = false; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | static void kvm_timer_inject_irq(struct kvm_vcpu *vcpu) | ||
64 | { | ||
65 | struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; | ||
66 | |||
67 | timer->cntv_ctl |= 1 << 1; /* Mask the interrupt in the guest */ | ||
68 | kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, | ||
69 | vcpu->arch.timer_cpu.irq->irq, | ||
70 | vcpu->arch.timer_cpu.irq->level); | ||
71 | } | ||
72 | |||
73 | static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id) | ||
74 | { | ||
75 | struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id; | ||
76 | |||
77 | /* | ||
78 | * We disable the timer in the world switch and let it be | ||
79 | * handled by kvm_timer_sync_hwstate(). Getting a timer | ||
80 | * interrupt at this point is a sure sign of some major | ||
81 | * breakage. | ||
82 | */ | ||
83 | pr_warn("Unexpected interrupt %d on vcpu %p\n", irq, vcpu); | ||
84 | return IRQ_HANDLED; | ||
85 | } | ||
86 | |||
87 | static void kvm_timer_inject_irq_work(struct work_struct *work) | ||
88 | { | ||
89 | struct kvm_vcpu *vcpu; | ||
90 | |||
91 | vcpu = container_of(work, struct kvm_vcpu, arch.timer_cpu.expired); | ||
92 | vcpu->arch.timer_cpu.armed = false; | ||
93 | kvm_timer_inject_irq(vcpu); | ||
94 | } | ||
95 | |||
96 | static enum hrtimer_restart kvm_timer_expire(struct hrtimer *hrt) | ||
97 | { | ||
98 | struct arch_timer_cpu *timer; | ||
99 | timer = container_of(hrt, struct arch_timer_cpu, timer); | ||
100 | queue_work(wqueue, &timer->expired); | ||
101 | return HRTIMER_NORESTART; | ||
102 | } | ||
103 | |||
104 | /** | ||
105 | * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu | ||
106 | * @vcpu: The vcpu pointer | ||
107 | * | ||
108 | * Disarm any pending soft timers, since the world-switch code will write the | ||
109 | * virtual timer state back to the physical CPU. | ||
110 | */ | ||
111 | void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu) | ||
112 | { | ||
113 | struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; | ||
114 | |||
115 | /* | ||
116 | * We're about to run this vcpu again, so there is no need to | ||
117 | * keep the background timer running, as we're about to | ||
118 | * populate the CPU timer again. | ||
119 | */ | ||
120 | timer_disarm(timer); | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * kvm_timer_sync_hwstate - sync timer state from cpu | ||
125 | * @vcpu: The vcpu pointer | ||
126 | * | ||
127 | * Check if the virtual timer was armed and either schedule a corresponding | ||
128 | * soft timer or inject directly if already expired. | ||
129 | */ | ||
130 | void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu) | ||
131 | { | ||
132 | struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; | ||
133 | cycle_t cval, now; | ||
134 | u64 ns; | ||
135 | |||
136 | /* Check if the timer is enabled and unmasked first */ | ||
137 | if ((timer->cntv_ctl & 3) != 1) | ||
138 | return; | ||
139 | |||
140 | cval = timer->cntv_cval; | ||
141 | now = kvm_phys_timer_read() - vcpu->kvm->arch.timer.cntvoff; | ||
142 | |||
143 | BUG_ON(timer_is_armed(timer)); | ||
144 | |||
145 | if (cval <= now) { | ||
146 | /* | ||
147 | * Timer has already expired while we were not | ||
148 | * looking. Inject the interrupt and carry on. | ||
149 | */ | ||
150 | kvm_timer_inject_irq(vcpu); | ||
151 | return; | ||
152 | } | ||
153 | |||
154 | ns = cyclecounter_cyc2ns(timecounter->cc, cval - now); | ||
155 | timer_arm(timer, ns); | ||
156 | } | ||
157 | |||
158 | void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) | ||
159 | { | ||
160 | struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; | ||
161 | |||
162 | INIT_WORK(&timer->expired, kvm_timer_inject_irq_work); | ||
163 | hrtimer_init(&timer->timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); | ||
164 | timer->timer.function = kvm_timer_expire; | ||
165 | timer->irq = &timer_irq; | ||
166 | } | ||
167 | |||
168 | static void kvm_timer_init_interrupt(void *info) | ||
169 | { | ||
170 | enable_percpu_irq(timer_irq.irq, 0); | ||
171 | } | ||
172 | |||
173 | |||
174 | static int kvm_timer_cpu_notify(struct notifier_block *self, | ||
175 | unsigned long action, void *cpu) | ||
176 | { | ||
177 | switch (action) { | ||
178 | case CPU_STARTING: | ||
179 | case CPU_STARTING_FROZEN: | ||
180 | kvm_timer_init_interrupt(NULL); | ||
181 | break; | ||
182 | case CPU_DYING: | ||
183 | case CPU_DYING_FROZEN: | ||
184 | disable_percpu_irq(timer_irq.irq); | ||
185 | break; | ||
186 | } | ||
187 | |||
188 | return NOTIFY_OK; | ||
189 | } | ||
190 | |||
191 | static struct notifier_block kvm_timer_cpu_nb = { | ||
192 | .notifier_call = kvm_timer_cpu_notify, | ||
193 | }; | ||
194 | |||
195 | static const struct of_device_id arch_timer_of_match[] = { | ||
196 | { .compatible = "arm,armv7-timer", }, | ||
197 | {}, | ||
198 | }; | ||
199 | |||
200 | int kvm_timer_hyp_init(void) | ||
201 | { | ||
202 | struct device_node *np; | ||
203 | unsigned int ppi; | ||
204 | int err; | ||
205 | |||
206 | timecounter = arch_timer_get_timecounter(); | ||
207 | if (!timecounter) | ||
208 | return -ENODEV; | ||
209 | |||
210 | np = of_find_matching_node(NULL, arch_timer_of_match); | ||
211 | if (!np) { | ||
212 | kvm_err("kvm_arch_timer: can't find DT node\n"); | ||
213 | return -ENODEV; | ||
214 | } | ||
215 | |||
216 | ppi = irq_of_parse_and_map(np, 2); | ||
217 | if (!ppi) { | ||
218 | kvm_err("kvm_arch_timer: no virtual timer interrupt\n"); | ||
219 | err = -EINVAL; | ||
220 | goto out; | ||
221 | } | ||
222 | |||
223 | err = request_percpu_irq(ppi, kvm_arch_timer_handler, | ||
224 | "kvm guest timer", kvm_get_running_vcpus()); | ||
225 | if (err) { | ||
226 | kvm_err("kvm_arch_timer: can't request interrupt %d (%d)\n", | ||
227 | ppi, err); | ||
228 | goto out; | ||
229 | } | ||
230 | |||
231 | timer_irq.irq = ppi; | ||
232 | |||
233 | err = register_cpu_notifier(&kvm_timer_cpu_nb); | ||
234 | if (err) { | ||
235 | kvm_err("Cannot register timer CPU notifier\n"); | ||
236 | goto out_free; | ||
237 | } | ||
238 | |||
239 | wqueue = create_singlethread_workqueue("kvm_arch_timer"); | ||
240 | if (!wqueue) { | ||
241 | err = -ENOMEM; | ||
242 | goto out_free; | ||
243 | } | ||
244 | |||
245 | kvm_info("%s IRQ%d\n", np->name, ppi); | ||
246 | on_each_cpu(kvm_timer_init_interrupt, NULL, 1); | ||
247 | |||
248 | goto out; | ||
249 | out_free: | ||
250 | free_percpu_irq(ppi, kvm_get_running_vcpus()); | ||
251 | out: | ||
252 | of_node_put(np); | ||
253 | return err; | ||
254 | } | ||
255 | |||
256 | void kvm_timer_vcpu_terminate(struct kvm_vcpu *vcpu) | ||
257 | { | ||
258 | struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu; | ||
259 | |||
260 | timer_disarm(timer); | ||
261 | } | ||
262 | |||
263 | int kvm_timer_init(struct kvm *kvm) | ||
264 | { | ||
265 | if (timecounter && wqueue) { | ||
266 | kvm->arch.timer.cntvoff = kvm_phys_timer_read(); | ||
267 | kvm->arch.timer.enabled = 1; | ||
268 | } | ||
269 | |||
270 | return 0; | ||
271 | } | ||
diff --git a/arch/arm/kvm/interrupts.S b/arch/arm/kvm/interrupts.S index 5f113bedfaee..8ca87ab0919d 100644 --- a/arch/arm/kvm/interrupts.S +++ b/arch/arm/kvm/interrupts.S | |||
@@ -95,6 +95,7 @@ ENTRY(__kvm_vcpu_run) | |||
95 | save_host_regs | 95 | save_host_regs |
96 | 96 | ||
97 | restore_vgic_state | 97 | restore_vgic_state |
98 | restore_timer_state | ||
98 | 99 | ||
99 | @ Store hardware CP15 state and load guest state | 100 | @ Store hardware CP15 state and load guest state |
100 | read_cp15_state store_to_vcpu = 0 | 101 | read_cp15_state store_to_vcpu = 0 |
@@ -189,6 +190,7 @@ after_vfp_restore: | |||
189 | read_cp15_state store_to_vcpu = 1 | 190 | read_cp15_state store_to_vcpu = 1 |
190 | write_cp15_state read_from_vcpu = 0 | 191 | write_cp15_state read_from_vcpu = 0 |
191 | 192 | ||
193 | save_timer_state | ||
192 | save_vgic_state | 194 | save_vgic_state |
193 | 195 | ||
194 | restore_host_regs | 196 | restore_host_regs |
diff --git a/arch/arm/kvm/interrupts_head.S b/arch/arm/kvm/interrupts_head.S index 8c875d54a089..06f251395bec 100644 --- a/arch/arm/kvm/interrupts_head.S +++ b/arch/arm/kvm/interrupts_head.S | |||
@@ -453,6 +453,37 @@ vcpu .req r0 @ vcpu pointer always in r0 | |||
453 | #endif | 453 | #endif |
454 | .endm | 454 | .endm |
455 | 455 | ||
456 | #define CNTHCTL_PL1PCTEN (1 << 0) | ||
457 | #define CNTHCTL_PL1PCEN (1 << 1) | ||
458 | |||
459 | /* | ||
460 | * Save the timer state onto the VCPU and allow physical timer/counter access | ||
461 | * for the host. | ||
462 | * | ||
463 | * Assumes vcpu pointer in vcpu reg | ||
464 | */ | ||
465 | .macro save_timer_state | ||
466 | @ Allow physical timer/counter access for the host | ||
467 | mrc p15, 4, r2, c14, c1, 0 @ CNTHCTL | ||
468 | orr r2, r2, #(CNTHCTL_PL1PCEN | CNTHCTL_PL1PCTEN) | ||
469 | mcr p15, 4, r2, c14, c1, 0 @ CNTHCTL | ||
470 | .endm | ||
471 | |||
472 | /* | ||
473 | * Load the timer state from the VCPU and deny physical timer/counter access | ||
474 | * for the host. | ||
475 | * | ||
476 | * Assumes vcpu pointer in vcpu reg | ||
477 | */ | ||
478 | .macro restore_timer_state | ||
479 | @ Disallow physical timer access for the guest | ||
480 | @ Physical counter access is allowed | ||
481 | mrc p15, 4, r2, c14, c1, 0 @ CNTHCTL | ||
482 | orr r2, r2, #CNTHCTL_PL1PCTEN | ||
483 | bic r2, r2, #CNTHCTL_PL1PCEN | ||
484 | mcr p15, 4, r2, c14, c1, 0 @ CNTHCTL | ||
485 | .endm | ||
486 | |||
456 | .equ vmentry, 0 | 487 | .equ vmentry, 0 |
457 | .equ vmexit, 1 | 488 | .equ vmexit, 1 |
458 | 489 | ||