Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm64/vmm/io/vtimer.c
104863 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2017 The FreeBSD Foundation
5
*
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
* 3. The name of the company nor the name of the author may be used to
15
* endorse or promote products derived from this software without specific
16
* prior written permission.
17
*
18
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
* SUCH DAMAGE.
29
*/
30
31
#include <sys/cdefs.h>
32
#include <sys/types.h>
33
#include <sys/systm.h>
34
#include <sys/bus.h>
35
#include <sys/kernel.h>
36
#include <sys/module.h>
37
#include <sys/mutex.h>
38
#include <sys/rman.h>
39
#include <sys/sysctl.h>
40
#include <sys/time.h>
41
#include <sys/timeet.h>
42
#include <sys/timetc.h>
43
44
#include <machine/bus.h>
45
#include <machine/machdep.h>
46
#include <machine/vmm.h>
47
48
#include <arm64/vmm/arm64.h>
49
50
#include <dev/vmm/vmm_vm.h>
51
52
#include "vgic.h"
53
#include "vtimer.h"
54
55
#define RES1 0xffffffffffffffffUL
56
57
#define timer_enabled(ctl) \
58
(!((ctl) & CNTP_CTL_IMASK) && ((ctl) & CNTP_CTL_ENABLE))
59
60
static uint32_t tmr_frq;
61
62
#define timer_condition_met(ctl) ((ctl) & CNTP_CTL_ISTATUS)
63
64
SYSCTL_DECL(_hw_vmm);
65
SYSCTL_NODE(_hw_vmm, OID_AUTO, vtimer, CTLFLAG_RW, NULL, NULL);
66
67
static bool allow_ecv_phys = false;
68
SYSCTL_BOOL(_hw_vmm_vtimer, OID_AUTO, allow_ecv_phys, CTLFLAG_RW,
69
&allow_ecv_phys, 0,
70
"Enable hardware access to the physical timer if FEAT_ECV_POFF is supported");
71
72
static void vtimer_schedule_irq(struct hypctx *hypctx, bool phys);
73
74
static int
75
vtimer_virtual_timer_intr(void *arg)
76
{
77
struct hypctx *hypctx;
78
uint64_t cntpct_el0;
79
uint32_t cntv_ctl;
80
81
hypctx = arm64_get_active_vcpu();
82
cntv_ctl = READ_SPECIALREG(cntv_ctl_el0);
83
84
if (!hypctx) {
85
/* vm_destroy() was called. */
86
eprintf("No active vcpu\n");
87
cntv_ctl = READ_SPECIALREG(cntv_ctl_el0);
88
goto out;
89
}
90
if (!timer_enabled(cntv_ctl)) {
91
eprintf("Timer not enabled\n");
92
goto out;
93
}
94
if (!timer_condition_met(cntv_ctl)) {
95
eprintf("Timer condition not met\n");
96
goto out;
97
}
98
99
cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
100
hypctx->hyp->vtimer.cntvoff_el2;
101
if (hypctx->vtimer_cpu.virt_timer.cntx_cval_el0 < cntpct_el0)
102
vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
103
GT_VIRT_IRQ, true);
104
105
cntv_ctl = hypctx->vtimer_cpu.virt_timer.cntx_ctl_el0;
106
107
out:
108
/*
109
* Disable the timer interrupt. This will prevent the interrupt from
110
* being reasserted as soon as we exit the handler and getting stuck
111
* in an infinite loop.
112
*
113
* This is safe to do because the guest disabled the timer, and then
114
* enables it as part of the interrupt handling routine.
115
*/
116
cntv_ctl &= ~CNTP_CTL_ENABLE;
117
WRITE_SPECIALREG(cntv_ctl_el0, cntv_ctl);
118
119
return (FILTER_HANDLED);
120
}
121
122
int
123
vtimer_init(void)
124
{
125
/*
126
* The guest *MUST* use the same timer frequency as the host. The
127
* register CNTFRQ_EL0 is accessible to the guest and a different value
128
* in the guest dts file might have unforseen consequences.
129
*/
130
tmr_frq = READ_SPECIALREG(cntfrq_el0);
131
132
return (0);
133
}
134
135
void
136
vtimer_vminit(struct hyp *hyp)
137
{
138
uint64_t now;
139
bool ecv_poff;
140
141
ecv_poff = false;
142
143
if (allow_ecv_phys && (hyp->feats & HYP_FEAT_ECV_POFF) != 0)
144
ecv_poff = true;
145
146
/*
147
* Configure the Counter-timer Hypervisor Control Register for the VM.
148
*/
149
if (in_vhe()) {
150
/*
151
* CNTHCTL_E2H_EL0PCTEN: trap EL0 access to CNTP{CT,CTSS}_EL0
152
* CNTHCTL_E2H_EL0VCTEN: don't trap EL0 access to
153
* CNTV{CT,CTXX}_EL0
154
* CNTHCTL_E2H_EL0VTEN: don't trap EL0 access to
155
* CNTV_{CTL,CVAL,TVAL}_EL0
156
* CNTHCTL_E2H_EL0PTEN: trap EL0 access to
157
* CNTP_{CTL,CVAL,TVAL}_EL0
158
* CNTHCTL_E2H_EL1PCTEN: trap access to CNTPCT_EL0
159
* CNTHCTL_E2H_EL1PTEN: trap access to
160
* CNTP_{CTL,CVAL,TVAL}_EL0
161
* CNTHCTL_E2H_EL1VCTEN: don't trap EL0 access to
162
* CNTV{CT,CTSS}_EL0
163
* CNTHCTL_E2H_EL1PCEN: trap EL1 access to
164
* CNTP_{CTL,CVAL,TVAL}_EL0
165
*
166
* TODO: Don't trap when FEAT_ECV is present
167
*/
168
hyp->vtimer.cnthctl_el2 =
169
CNTHCTL_E2H_EL0VCTEN_NOTRAP |
170
CNTHCTL_E2H_EL0VTEN_NOTRAP;
171
if (ecv_poff) {
172
hyp->vtimer.cnthctl_el2 |=
173
CNTHCTL_E2H_EL0PCTEN_NOTRAP |
174
CNTHCTL_E2H_EL0PTEN_NOTRAP |
175
CNTHCTL_E2H_EL1PCTEN_NOTRAP |
176
CNTHCTL_E2H_EL1PTEN_NOTRAP;
177
} else {
178
hyp->vtimer.cnthctl_el2 |=
179
CNTHCTL_E2H_EL0PCTEN_TRAP |
180
CNTHCTL_E2H_EL0PTEN_TRAP |
181
CNTHCTL_E2H_EL1PCTEN_TRAP |
182
CNTHCTL_E2H_EL1PTEN_TRAP;
183
}
184
} else {
185
/*
186
* CNTHCTL_EL1PCEN: trap access to CNTP_{CTL, CVAL, TVAL}_EL0
187
* from EL1
188
* CNTHCTL_EL1PCTEN: trap access to CNTPCT_EL0
189
*/
190
if (ecv_poff) {
191
hyp->vtimer.cnthctl_el2 =
192
CNTHCTL_EL1PCTEN_NOTRAP |
193
CNTHCTL_EL1PCEN_NOTRAP;
194
} else {
195
hyp->vtimer.cnthctl_el2 =
196
CNTHCTL_EL1PCTEN_TRAP |
197
CNTHCTL_EL1PCEN_TRAP;
198
}
199
}
200
201
if (ecv_poff)
202
hyp->vtimer.cnthctl_el2 |= CNTHCTL_ECV_EN;
203
204
now = READ_SPECIALREG(cntpct_el0);
205
hyp->vtimer.cntvoff_el2 = now;
206
207
return;
208
}
209
210
void
211
vtimer_cpuinit(struct hypctx *hypctx)
212
{
213
struct vtimer_cpu *vtimer_cpu;
214
215
vtimer_cpu = &hypctx->vtimer_cpu;
216
/*
217
* Configure physical timer interrupts for the VCPU.
218
*
219
* CNTP_CTL_IMASK: mask interrupts
220
* ~CNTP_CTL_ENABLE: disable the timer
221
*/
222
vtimer_cpu->phys_timer.cntx_ctl_el0 = CNTP_CTL_IMASK & ~CNTP_CTL_ENABLE;
223
224
mtx_init(&vtimer_cpu->phys_timer.mtx, "vtimer phys callout mutex", NULL,
225
MTX_DEF);
226
callout_init_mtx(&vtimer_cpu->phys_timer.callout,
227
&vtimer_cpu->phys_timer.mtx, 0);
228
vtimer_cpu->phys_timer.irqid = GT_PHYS_NS_IRQ;
229
230
mtx_init(&vtimer_cpu->virt_timer.mtx, "vtimer virt callout mutex", NULL,
231
MTX_DEF);
232
callout_init_mtx(&vtimer_cpu->virt_timer.callout,
233
&vtimer_cpu->virt_timer.mtx, 0);
234
vtimer_cpu->virt_timer.irqid = GT_VIRT_IRQ;
235
}
236
237
void
238
vtimer_cpucleanup(struct hypctx *hypctx)
239
{
240
struct vtimer_cpu *vtimer_cpu;
241
242
vtimer_cpu = &hypctx->vtimer_cpu;
243
callout_drain(&vtimer_cpu->phys_timer.callout);
244
callout_drain(&vtimer_cpu->virt_timer.callout);
245
mtx_destroy(&vtimer_cpu->phys_timer.mtx);
246
mtx_destroy(&vtimer_cpu->virt_timer.mtx);
247
}
248
249
void
250
vtimer_vmcleanup(struct hyp *hyp)
251
{
252
struct hypctx *hypctx;
253
uint32_t cntv_ctl;
254
255
hypctx = arm64_get_active_vcpu();
256
if (!hypctx) {
257
/* The active VM was destroyed, stop the timer. */
258
cntv_ctl = READ_SPECIALREG(cntv_ctl_el0);
259
cntv_ctl &= ~CNTP_CTL_ENABLE;
260
WRITE_SPECIALREG(cntv_ctl_el0, cntv_ctl);
261
}
262
}
263
264
void
265
vtimer_cleanup(void)
266
{
267
}
268
269
static void
270
vtime_sync_timer(struct hypctx *hypctx, struct vtimer_timer *timer,
271
uint64_t cntpct_el0)
272
{
273
if (!timer_enabled(timer->cntx_ctl_el0)) {
274
vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
275
timer->irqid, false);
276
} else if (timer->cntx_cval_el0 < cntpct_el0) {
277
vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
278
timer->irqid, true);
279
} else {
280
vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
281
timer->irqid, false);
282
vtimer_schedule_irq(hypctx, false);
283
}
284
}
285
286
void
287
vtimer_sync_hwstate(struct hypctx *hypctx)
288
{
289
uint64_t cntpct_el0;
290
291
cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
292
hypctx->hyp->vtimer.cntvoff_el2;
293
vtime_sync_timer(hypctx, &hypctx->vtimer_cpu.virt_timer, cntpct_el0);
294
/* If FEAT_ECV_POFF is in use then we need to sync the physical timer */
295
if ((hypctx->hyp->vtimer.cnthctl_el2 & CNTHCTL_ECV_EN) != 0) {
296
vtime_sync_timer(hypctx, &hypctx->vtimer_cpu.phys_timer,
297
cntpct_el0);
298
}
299
}
300
301
static void
302
vtimer_inject_irq_callout_phys(void *context)
303
{
304
struct hypctx *hypctx;
305
306
hypctx = context;
307
vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
308
hypctx->vtimer_cpu.phys_timer.irqid, true);
309
}
310
311
static void
312
vtimer_inject_irq_callout_virt(void *context)
313
{
314
struct hypctx *hypctx;
315
316
hypctx = context;
317
vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
318
hypctx->vtimer_cpu.virt_timer.irqid, true);
319
}
320
321
static void
322
vtimer_schedule_irq(struct hypctx *hypctx, bool phys)
323
{
324
sbintime_t time;
325
struct vtimer_timer *timer;
326
uint64_t cntpct_el0;
327
uint64_t diff;
328
329
if (phys)
330
timer = &hypctx->vtimer_cpu.phys_timer;
331
else
332
timer = &hypctx->vtimer_cpu.virt_timer;
333
cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
334
hypctx->hyp->vtimer.cntvoff_el2;
335
if (timer->cntx_cval_el0 < cntpct_el0) {
336
/* Timer set in the past, trigger interrupt */
337
vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(hypctx->vcpu),
338
timer->irqid, true);
339
} else {
340
diff = timer->cntx_cval_el0 - cntpct_el0;
341
time = diff * SBT_1S / tmr_frq;
342
if (phys)
343
callout_reset_sbt(&timer->callout, time, 0,
344
vtimer_inject_irq_callout_phys, hypctx, 0);
345
else
346
callout_reset_sbt(&timer->callout, time, 0,
347
vtimer_inject_irq_callout_virt, hypctx, 0);
348
}
349
}
350
351
static void
352
vtimer_remove_irq(struct hypctx *hypctx, struct vcpu *vcpu)
353
{
354
struct vtimer_cpu *vtimer_cpu;
355
struct vtimer_timer *timer;
356
357
vtimer_cpu = &hypctx->vtimer_cpu;
358
timer = &vtimer_cpu->phys_timer;
359
360
callout_drain(&timer->callout);
361
/*
362
* The interrupt needs to be deactivated here regardless of the callout
363
* function having been executed. The timer interrupt can be masked with
364
* the CNTP_CTL_EL0.IMASK bit instead of reading the IAR register.
365
* Masking the interrupt doesn't remove it from the list registers.
366
*/
367
vgic_inject_irq(hypctx->hyp, vcpu_vcpuid(vcpu), timer->irqid, false);
368
}
369
370
/*
371
* Timer emulation functions.
372
*
373
* The guest should use the virtual timer, however some software, e.g. u-boot,
374
* used the physical timer. Emulate this in software for the guest to use.
375
*
376
* Adjust for cntvoff_el2 so the physical and virtual timers are at similar
377
* times. This simplifies interrupt handling in the virtual timer as the
378
* adjustment will have already happened.
379
*/
380
381
int
382
vtimer_phys_ctl_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
383
{
384
struct hyp *hyp;
385
struct hypctx *hypctx;
386
struct vtimer_cpu *vtimer_cpu;
387
uint64_t cntpct_el0;
388
389
hypctx = vcpu_get_cookie(vcpu);
390
hyp = hypctx->hyp;
391
vtimer_cpu = &hypctx->vtimer_cpu;
392
393
cntpct_el0 = READ_SPECIALREG(cntpct_el0) - hyp->vtimer.cntvoff_el2;
394
if (vtimer_cpu->phys_timer.cntx_cval_el0 < cntpct_el0)
395
/* Timer condition met */
396
*rval = vtimer_cpu->phys_timer.cntx_ctl_el0 | CNTP_CTL_ISTATUS;
397
else
398
*rval = vtimer_cpu->phys_timer.cntx_ctl_el0 & ~CNTP_CTL_ISTATUS;
399
400
return (0);
401
}
402
403
int
404
vtimer_phys_ctl_write(struct vcpu *vcpu, uint64_t wval, void *arg)
405
{
406
struct hypctx *hypctx;
407
struct vtimer_cpu *vtimer_cpu;
408
uint64_t ctl_el0;
409
bool timer_toggled_on;
410
411
hypctx = vcpu_get_cookie(vcpu);
412
vtimer_cpu = &hypctx->vtimer_cpu;
413
414
timer_toggled_on = false;
415
ctl_el0 = vtimer_cpu->phys_timer.cntx_ctl_el0;
416
417
if (!timer_enabled(ctl_el0) && timer_enabled(wval))
418
timer_toggled_on = true;
419
else if (timer_enabled(ctl_el0) && !timer_enabled(wval))
420
vtimer_remove_irq(hypctx, vcpu);
421
422
vtimer_cpu->phys_timer.cntx_ctl_el0 = wval;
423
424
if (timer_toggled_on)
425
vtimer_schedule_irq(hypctx, true);
426
427
return (0);
428
}
429
430
int
431
vtimer_phys_cnt_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
432
{
433
struct vm *vm;
434
struct hyp *hyp;
435
436
vm = vcpu_vm(vcpu);
437
hyp = vm_get_cookie(vm);
438
*rval = READ_SPECIALREG(cntpct_el0) - hyp->vtimer.cntvoff_el2;
439
return (0);
440
}
441
442
int
443
vtimer_phys_cnt_write(struct vcpu *vcpu, uint64_t wval, void *arg)
444
{
445
return (0);
446
}
447
448
int
449
vtimer_phys_cval_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
450
{
451
struct hypctx *hypctx;
452
struct vtimer_cpu *vtimer_cpu;
453
454
hypctx = vcpu_get_cookie(vcpu);
455
vtimer_cpu = &hypctx->vtimer_cpu;
456
457
*rval = vtimer_cpu->phys_timer.cntx_cval_el0;
458
459
return (0);
460
}
461
462
int
463
vtimer_phys_cval_write(struct vcpu *vcpu, uint64_t wval, void *arg)
464
{
465
struct hypctx *hypctx;
466
struct vtimer_cpu *vtimer_cpu;
467
468
hypctx = vcpu_get_cookie(vcpu);
469
vtimer_cpu = &hypctx->vtimer_cpu;
470
471
vtimer_cpu->phys_timer.cntx_cval_el0 = wval;
472
473
vtimer_remove_irq(hypctx, vcpu);
474
if (timer_enabled(vtimer_cpu->phys_timer.cntx_ctl_el0)) {
475
vtimer_schedule_irq(hypctx, true);
476
}
477
478
return (0);
479
}
480
481
int
482
vtimer_phys_tval_read(struct vcpu *vcpu, uint64_t *rval, void *arg)
483
{
484
struct hyp *hyp;
485
struct hypctx *hypctx;
486
struct vtimer_cpu *vtimer_cpu;
487
uint32_t cntpct_el0;
488
489
hypctx = vcpu_get_cookie(vcpu);
490
hyp = hypctx->hyp;
491
vtimer_cpu = &hypctx->vtimer_cpu;
492
493
if (!(vtimer_cpu->phys_timer.cntx_ctl_el0 & CNTP_CTL_ENABLE)) {
494
/*
495
* ARMv8 Architecture Manual, p. D7-2702: the result of reading
496
* TVAL when the timer is disabled is UNKNOWN. I have chosen to
497
* return the maximum value possible on 32 bits which means the
498
* timer will fire very far into the future.
499
*/
500
*rval = (uint32_t)RES1;
501
} else {
502
cntpct_el0 = READ_SPECIALREG(cntpct_el0) -
503
hyp->vtimer.cntvoff_el2;
504
*rval = vtimer_cpu->phys_timer.cntx_cval_el0 - cntpct_el0;
505
}
506
507
return (0);
508
}
509
510
int
511
vtimer_phys_tval_write(struct vcpu *vcpu, uint64_t wval, void *arg)
512
{
513
struct hyp *hyp;
514
struct hypctx *hypctx;
515
struct vtimer_cpu *vtimer_cpu;
516
uint64_t cntpct_el0;
517
518
hypctx = vcpu_get_cookie(vcpu);
519
hyp = hypctx->hyp;
520
vtimer_cpu = &hypctx->vtimer_cpu;
521
522
cntpct_el0 = READ_SPECIALREG(cntpct_el0) - hyp->vtimer.cntvoff_el2;
523
vtimer_cpu->phys_timer.cntx_cval_el0 = (int32_t)wval + cntpct_el0;
524
525
vtimer_remove_irq(hypctx, vcpu);
526
if (timer_enabled(vtimer_cpu->phys_timer.cntx_ctl_el0)) {
527
vtimer_schedule_irq(hypctx, true);
528
}
529
530
return (0);
531
}
532
533
struct vtimer_softc {
534
struct resource *res;
535
void *ihl;
536
int rid;
537
};
538
539
static int
540
vtimer_probe(device_t dev)
541
{
542
device_set_desc(dev, "Virtual timer");
543
return (BUS_PROBE_DEFAULT);
544
}
545
546
static int
547
vtimer_attach(device_t dev)
548
{
549
struct vtimer_softc *sc;
550
551
sc = device_get_softc(dev);
552
553
sc->rid = 0;
554
sc->res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->rid, RF_ACTIVE);
555
if (sc->res == NULL)
556
return (ENXIO);
557
558
bus_setup_intr(dev, sc->res, INTR_TYPE_CLK, vtimer_virtual_timer_intr,
559
NULL, NULL, &sc->ihl);
560
561
return (0);
562
}
563
564
static device_method_t vtimer_methods[] = {
565
/* Device interface */
566
DEVMETHOD(device_probe, vtimer_probe),
567
DEVMETHOD(device_attach, vtimer_attach),
568
569
/* End */
570
DEVMETHOD_END
571
};
572
573
DEFINE_CLASS_0(vtimer, vtimer_driver, vtimer_methods,
574
sizeof(struct vtimer_softc));
575
576
DRIVER_MODULE(vtimer, generic_timer, vtimer_driver, 0, 0);
577
578