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_vcpu_wake_up(vcpu); 34 35 return HRTIMER_NORESTART; 36 } 37 38 /* 39 * Initialise the timer to the specified frequency, zero it 40 */ 41 void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long timer_hz) 42 { 43 vcpu->arch.timer_mhz = timer_hz >> 20; 44 45 /* Starting at 0 */ 46 kvm_write_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_TVAL, 0); 47 } 48 49 /* 50 * Restore soft timer state from saved context. 51 */ 52 void kvm_restore_timer(struct kvm_vcpu *vcpu) 53 { 54 unsigned long cfg, estat; 55 unsigned long ticks, delta, period; 56 ktime_t expire, now; 57 struct loongarch_csrs *csr = vcpu->arch.csr; 58 59 /* 60 * Set guest stable timer cfg csr 61 * Disable timer before restore estat CSR register, avoid to 62 * get invalid timer interrupt for old timer cfg 63 */ 64 cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG); 65 66 write_gcsr_timercfg(0); 67 kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_ESTAT); 68 kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TCFG); 69 if (!(cfg & CSR_TCFG_EN)) { 70 /* Guest timer is disabled, just restore timer registers */ 71 kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TVAL); 72 return; 73 } 74 75 /* 76 * Freeze the soft-timer and sync the guest stable timer with it. 77 */ 78 if (kvm_vcpu_is_blocking(vcpu)) 79 hrtimer_cancel(&vcpu->arch.swtimer); 80 81 /* 82 * From LoongArch Reference Manual Volume 1 Chapter 7.6.2 83 * If oneshot timer is fired, CSR TVAL will be -1, there are two 84 * conditions: 85 * 1) timer is fired during exiting to host 86 * 2) timer is fired and vm is doing timer irq, and then exiting to 87 * host. Host should not inject timer irq to avoid spurious 88 * timer interrupt again 89 */ 90 ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL); 91 estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT); 92 if (!(cfg & CSR_TCFG_PERIOD) && (ticks > cfg)) { 93 /* 94 * Writing 0 to LOONGARCH_CSR_TVAL will inject timer irq 95 * and set CSR TVAL with -1 96 */ 97 write_gcsr_timertick(0); 98 99 /* 100 * Writing CSR_TINTCLR_TI to LOONGARCH_CSR_TINTCLR will clear 101 * timer interrupt, and CSR TVAL keeps unchanged with -1, it 102 * avoids spurious timer interrupt 103 */ 104 if (!(estat & CPU_TIMER)) { 105 __delay(2); /* Wait cycles until timer interrupt injected */ 106 107 /* Write TVAL with max value if no TI shot */ 108 estat = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT); 109 if (!(estat & CPU_TIMER)) 110 write_gcsr_timertick(CSR_TCFG_VAL); 111 gcsr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR); 112 } 113 return; 114 } 115 116 /* 117 * Set remainder tick value if not expired 118 */ 119 delta = 0; 120 now = ktime_get(); 121 expire = vcpu->arch.expire; 122 if (!expire) { 123 /* 124 * vcpu->arch.expire is host-internal and is not migrated, 125 * so it is 0 after migration. Reload the remaining countdown 126 * from the migrated TVAL. This covers both one-shot and 127 * periodic timers. 128 */ 129 if (ticks < cfg) 130 delta = tick_to_ns(vcpu, ticks); 131 expire = ktime_add_ns(now, delta); 132 } 133 134 if (ktime_before(now, expire)) 135 delta = ktime_to_tick(vcpu, ktime_sub(expire, now)); 136 else if (cfg & CSR_TCFG_PERIOD) { 137 period = cfg & CSR_TCFG_VAL; 138 if (!period) 139 period = 1; 140 delta = ktime_to_tick(vcpu, ktime_sub(now, expire)); 141 delta = period - (delta % period); 142 143 /* 144 * Inject timer here though sw timer should inject timer 145 * interrupt async already, since sw timer may be cancelled 146 * during injecting intr async 147 */ 148 kvm_queue_irq(vcpu, INT_TI); 149 } 150 151 write_gcsr_timertick(delta); 152 } 153 154 /* 155 * Save guest timer state and switch to software emulation of guest 156 * timer. The hard timer must already be in use, so preemption should be 157 * disabled. 158 */ 159 static void _kvm_save_timer(struct kvm_vcpu *vcpu) 160 { 161 unsigned long ticks, delta, cfg; 162 ktime_t expire; 163 struct loongarch_csrs *csr = vcpu->arch.csr; 164 165 cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG); 166 ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL); 167 168 /* 169 * From LoongArch Reference Manual Volume 1 Chapter 7.6.2 170 * If period timer is fired, CSR TVAL will be reloaded from CSR TCFG 171 * If oneshot timer is fired, CSR TVAL will be -1 172 * Here judge one-shot timer fired by checking whether TVAL is larger 173 * than TCFG 174 */ 175 if (ticks < cfg) 176 delta = tick_to_ns(vcpu, ticks); 177 else 178 delta = 0; 179 180 expire = ktime_add_ns(ktime_get(), delta); 181 vcpu->arch.expire = expire; 182 if (kvm_vcpu_is_blocking(vcpu)) { 183 184 /* 185 * HRTIMER_MODE_PINNED_HARD is suggested since vcpu may run in 186 * the same physical cpu in next time, and the timer should run 187 * in hardirq context even in the PREEMPT_RT case. 188 */ 189 hrtimer_start(&vcpu->arch.swtimer, expire, HRTIMER_MODE_ABS_PINNED_HARD); 190 } 191 } 192 193 /* 194 * Save guest timer state and switch to soft guest timer if hard timer was in 195 * use. 196 */ 197 void kvm_save_timer(struct kvm_vcpu *vcpu) 198 { 199 struct loongarch_csrs *csr = vcpu->arch.csr; 200 201 preempt_disable(); 202 203 /* Save hard timer state */ 204 kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TCFG); 205 kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TVAL); 206 if (kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG) & CSR_TCFG_EN) 207 _kvm_save_timer(vcpu); 208 209 /* Save timer-related state to vCPU context */ 210 kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ESTAT); 211 preempt_enable(); 212 } 213