Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/loongarch/kvm/timer.c
49691 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
4
*/
5
6
#include <linux/kvm_host.h>
7
#include <asm/delay.h>
8
#include <asm/kvm_csr.h>
9
#include <asm/kvm_vcpu.h>
10
11
/*
12
* ktime_to_tick() - Scale ktime_t to timer tick value.
13
*/
14
static inline u64 ktime_to_tick(struct kvm_vcpu *vcpu, ktime_t now)
15
{
16
u64 delta;
17
18
delta = ktime_to_ns(now);
19
return div_u64(delta * vcpu->arch.timer_mhz, MNSEC_PER_SEC);
20
}
21
22
static inline u64 tick_to_ns(struct kvm_vcpu *vcpu, u64 tick)
23
{
24
return div_u64(tick * MNSEC_PER_SEC, vcpu->arch.timer_mhz);
25
}
26
27
/* Low level hrtimer wake routine */
28
enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer)
29
{
30
struct kvm_vcpu *vcpu;
31
32
vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer);
33
kvm_queue_irq(vcpu, INT_TI);
34
rcuwait_wake_up(&vcpu->wait);
35
36
return HRTIMER_NORESTART;
37
}
38
39
/*
40
* Initialise the timer to the specified frequency, zero it
41
*/
42
void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long timer_hz)
43
{
44
vcpu->arch.timer_mhz = timer_hz >> 20;
45
46
/* Starting at 0 */
47
kvm_write_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_TVAL, 0);
48
}
49
50
/*
51
* Restore soft timer state from saved context.
52
*/
53
void kvm_restore_timer(struct kvm_vcpu *vcpu)
54
{
55
unsigned long cfg, estat;
56
unsigned long ticks, delta, period;
57
ktime_t expire, now;
58
struct loongarch_csrs *csr = vcpu->arch.csr;
59
60
/*
61
* Set guest stable timer cfg csr
62
* Disable timer before restore estat CSR register, avoid to
63
* get invalid timer interrupt for old timer cfg
64
*/
65
cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG);
66
67
write_gcsr_timercfg(0);
68
kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_ESTAT);
69
kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TCFG);
70
if (!(cfg & CSR_TCFG_EN)) {
71
/* Guest timer is disabled, just restore timer registers */
72
kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TVAL);
73
return;
74
}
75
76
/*
77
* Freeze the soft-timer and sync the guest stable timer with it.
78
*/
79
if (kvm_vcpu_is_blocking(vcpu))
80
hrtimer_cancel(&vcpu->arch.swtimer);
81
82
/*
83
* From LoongArch Reference Manual Volume 1 Chapter 7.6.2
84
* If oneshot timer is fired, CSR TVAL will be -1, there are two
85
* conditions:
86
* 1) timer is fired during exiting to host
87
* 2) timer is fired and vm is doing timer irq, and then exiting to
88
* host. Host should not inject timer irq to avoid spurious
89
* timer interrupt again
90
*/
91
ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL);
92
estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT);
93
if (!(cfg & CSR_TCFG_PERIOD) && (ticks > cfg)) {
94
/*
95
* Writing 0 to LOONGARCH_CSR_TVAL will inject timer irq
96
* and set CSR TVAL with -1
97
*/
98
write_gcsr_timertick(0);
99
__delay(2); /* Wait cycles until timer interrupt injected */
100
101
/*
102
* Writing CSR_TINTCLR_TI to LOONGARCH_CSR_TINTCLR will clear
103
* timer interrupt, and CSR TVAL keeps unchanged with -1, it
104
* avoids spurious timer interrupt
105
*/
106
if (!(estat & CPU_TIMER))
107
gcsr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
108
return;
109
}
110
111
/*
112
* Set remainder tick value if not expired
113
*/
114
delta = 0;
115
now = ktime_get();
116
expire = vcpu->arch.expire;
117
if (ktime_before(now, expire))
118
delta = ktime_to_tick(vcpu, ktime_sub(expire, now));
119
else if (cfg & CSR_TCFG_PERIOD) {
120
period = cfg & CSR_TCFG_VAL;
121
delta = ktime_to_tick(vcpu, ktime_sub(now, expire));
122
delta = period - (delta % period);
123
124
/*
125
* Inject timer here though sw timer should inject timer
126
* interrupt async already, since sw timer may be cancelled
127
* during injecting intr async
128
*/
129
kvm_queue_irq(vcpu, INT_TI);
130
}
131
132
write_gcsr_timertick(delta);
133
}
134
135
/*
136
* Save guest timer state and switch to software emulation of guest
137
* timer. The hard timer must already be in use, so preemption should be
138
* disabled.
139
*/
140
static void _kvm_save_timer(struct kvm_vcpu *vcpu)
141
{
142
unsigned long ticks, delta, cfg;
143
ktime_t expire;
144
struct loongarch_csrs *csr = vcpu->arch.csr;
145
146
cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG);
147
ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL);
148
149
/*
150
* From LoongArch Reference Manual Volume 1 Chapter 7.6.2
151
* If period timer is fired, CSR TVAL will be reloaded from CSR TCFG
152
* If oneshot timer is fired, CSR TVAL will be -1
153
* Here judge one-shot timer fired by checking whether TVAL is larger
154
* than TCFG
155
*/
156
if (ticks < cfg)
157
delta = tick_to_ns(vcpu, ticks);
158
else
159
delta = 0;
160
161
expire = ktime_add_ns(ktime_get(), delta);
162
vcpu->arch.expire = expire;
163
if (kvm_vcpu_is_blocking(vcpu)) {
164
165
/*
166
* HRTIMER_MODE_PINNED_HARD is suggested since vcpu may run in
167
* the same physical cpu in next time, and the timer should run
168
* in hardirq context even in the PREEMPT_RT case.
169
*/
170
hrtimer_start(&vcpu->arch.swtimer, expire, HRTIMER_MODE_ABS_PINNED_HARD);
171
}
172
}
173
174
/*
175
* Save guest timer state and switch to soft guest timer if hard timer was in
176
* use.
177
*/
178
void kvm_save_timer(struct kvm_vcpu *vcpu)
179
{
180
struct loongarch_csrs *csr = vcpu->arch.csr;
181
182
preempt_disable();
183
184
/* Save hard timer state */
185
kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TCFG);
186
kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TVAL);
187
if (kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG) & CSR_TCFG_EN)
188
_kvm_save_timer(vcpu);
189
190
/* Save timer-related state to vCPU context */
191
kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ESTAT);
192
preempt_enable();
193
}
194
195