Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

RISC-V: KVM: Add timer functionality

The RISC-V hypervisor specification doesn't have any virtual timer
feature.

Due to this, the guest VCPU timer will be programmed via SBI calls.
The host will use a separate hrtimer event for each guest VCPU to
provide timer functionality. We inject a virtual timer interrupt to
the guest VCPU whenever the guest VCPU hrtimer event expires.

This patch adds guest VCPU timer implementation along with ONE_REG
interface to access VCPU timer state from user space.

Signed-off-by: Atish Patra <atish.patra@wdc.com>
Signed-off-by: Anup Patel <anup.patel@wdc.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Acked-by: Palmer Dabbelt <palmerdabbelt@google.com>

authored by

Atish Patra and committed by
Anup Patel
3a9f66cb 9955371c

+334 -1
+7
arch/riscv/include/asm/kvm_host.h
··· 12 12 #include <linux/types.h> 13 13 #include <linux/kvm.h> 14 14 #include <linux/kvm_types.h> 15 + #include <asm/kvm_vcpu_timer.h> 15 16 16 17 #ifdef CONFIG_64BIT 17 18 #define KVM_MAX_VCPUS (1U << 16) ··· 61 60 /* stage2 page table */ 62 61 pgd_t *pgd; 63 62 phys_addr_t pgd_phys; 63 + 64 + /* Guest Timer */ 65 + struct kvm_guest_timer timer; 64 66 }; 65 67 66 68 struct kvm_mmio_decode { ··· 178 174 */ 179 175 unsigned long irqs_pending; 180 176 unsigned long irqs_pending_mask; 177 + 178 + /* VCPU Timer */ 179 + struct kvm_vcpu_timer timer; 181 180 182 181 /* MMIO instruction details */ 183 182 struct kvm_mmio_decode mmio_decode;
+44
arch/riscv/include/asm/kvm_vcpu_timer.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2019 Western Digital Corporation or its affiliates. 4 + * 5 + * Authors: 6 + * Atish Patra <atish.patra@wdc.com> 7 + */ 8 + 9 + #ifndef __KVM_VCPU_RISCV_TIMER_H 10 + #define __KVM_VCPU_RISCV_TIMER_H 11 + 12 + #include <linux/hrtimer.h> 13 + 14 + struct kvm_guest_timer { 15 + /* Mult & Shift values to get nanoseconds from cycles */ 16 + u32 nsec_mult; 17 + u32 nsec_shift; 18 + /* Time delta value */ 19 + u64 time_delta; 20 + }; 21 + 22 + struct kvm_vcpu_timer { 23 + /* Flag for whether init is done */ 24 + bool init_done; 25 + /* Flag for whether timer event is configured */ 26 + bool next_set; 27 + /* Next timer event cycles */ 28 + u64 next_cycles; 29 + /* Underlying hrtimer instance */ 30 + struct hrtimer hrt; 31 + }; 32 + 33 + int kvm_riscv_vcpu_timer_next_event(struct kvm_vcpu *vcpu, u64 ncycles); 34 + int kvm_riscv_vcpu_get_reg_timer(struct kvm_vcpu *vcpu, 35 + const struct kvm_one_reg *reg); 36 + int kvm_riscv_vcpu_set_reg_timer(struct kvm_vcpu *vcpu, 37 + const struct kvm_one_reg *reg); 38 + int kvm_riscv_vcpu_timer_init(struct kvm_vcpu *vcpu); 39 + int kvm_riscv_vcpu_timer_deinit(struct kvm_vcpu *vcpu); 40 + int kvm_riscv_vcpu_timer_reset(struct kvm_vcpu *vcpu); 41 + void kvm_riscv_vcpu_timer_restore(struct kvm_vcpu *vcpu); 42 + int kvm_riscv_guest_timer_init(struct kvm *kvm); 43 + 44 + #endif
+17
arch/riscv/include/uapi/asm/kvm.h
··· 74 74 unsigned long scounteren; 75 75 }; 76 76 77 + /* TIMER registers for KVM_GET_ONE_REG and KVM_SET_ONE_REG */ 78 + struct kvm_riscv_timer { 79 + __u64 frequency; 80 + __u64 time; 81 + __u64 compare; 82 + __u64 state; 83 + }; 84 + 85 + /* Possible states for kvm_riscv_timer */ 86 + #define KVM_RISCV_TIMER_STATE_OFF 0 87 + #define KVM_RISCV_TIMER_STATE_ON 1 88 + 77 89 #define KVM_REG_SIZE(id) \ 78 90 (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) 79 91 ··· 107 95 #define KVM_REG_RISCV_CSR (0x03 << KVM_REG_RISCV_TYPE_SHIFT) 108 96 #define KVM_REG_RISCV_CSR_REG(name) \ 109 97 (offsetof(struct kvm_riscv_csr, name) / sizeof(unsigned long)) 98 + 99 + /* Timer registers are mapped as type 4 */ 100 + #define KVM_REG_RISCV_TIMER (0x04 << KVM_REG_RISCV_TYPE_SHIFT) 101 + #define KVM_REG_RISCV_TIMER_REG(name) \ 102 + (offsetof(struct kvm_riscv_timer, name) / sizeof(__u64)) 110 103 111 104 #endif 112 105
+1
arch/riscv/kvm/Makefile
··· 21 21 kvm-y += vcpu.o 22 22 kvm-y += vcpu_exit.o 23 23 kvm-y += vcpu_switch.o 24 + kvm-y += vcpu_timer.o
+14
arch/riscv/kvm/vcpu.c
··· 58 58 59 59 memcpy(cntx, reset_cntx, sizeof(*cntx)); 60 60 61 + kvm_riscv_vcpu_timer_reset(vcpu); 62 + 61 63 WRITE_ONCE(vcpu->arch.irqs_pending, 0); 62 64 WRITE_ONCE(vcpu->arch.irqs_pending_mask, 0); 63 65 } ··· 87 85 cntx->hstatus |= HSTATUS_SPVP; 88 86 cntx->hstatus |= HSTATUS_SPV; 89 87 88 + /* Setup VCPU timer */ 89 + kvm_riscv_vcpu_timer_init(vcpu); 90 + 90 91 /* Reset VCPU */ 91 92 kvm_riscv_reset_vcpu(vcpu); 92 93 ··· 102 97 103 98 void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) 104 99 { 100 + /* Cleanup VCPU timer */ 101 + kvm_riscv_vcpu_timer_deinit(vcpu); 102 + 105 103 /* Flush the pages pre-allocated for Stage2 page table mappings */ 106 104 kvm_riscv_stage2_flush_cache(vcpu); 107 105 } ··· 340 332 return kvm_riscv_vcpu_set_reg_core(vcpu, reg); 341 333 else if ((reg->id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_CSR) 342 334 return kvm_riscv_vcpu_set_reg_csr(vcpu, reg); 335 + else if ((reg->id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_TIMER) 336 + return kvm_riscv_vcpu_set_reg_timer(vcpu, reg); 343 337 344 338 return -EINVAL; 345 339 } ··· 355 345 return kvm_riscv_vcpu_get_reg_core(vcpu, reg); 356 346 else if ((reg->id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_CSR) 357 347 return kvm_riscv_vcpu_get_reg_csr(vcpu, reg); 348 + else if ((reg->id & KVM_REG_RISCV_TYPE_MASK) == KVM_REG_RISCV_TIMER) 349 + return kvm_riscv_vcpu_get_reg_timer(vcpu, reg); 358 350 359 351 return -EINVAL; 360 352 } ··· 590 578 csr_write(CSR_VSATP, csr->vsatp); 591 579 592 580 kvm_riscv_stage2_update_hgatp(vcpu); 581 + 582 + kvm_riscv_vcpu_timer_restore(vcpu); 593 583 594 584 vcpu->cpu = cpu; 595 585 }
+225
arch/riscv/kvm/vcpu_timer.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2019 Western Digital Corporation or its affiliates. 4 + * 5 + * Authors: 6 + * Atish Patra <atish.patra@wdc.com> 7 + */ 8 + 9 + #include <linux/errno.h> 10 + #include <linux/err.h> 11 + #include <linux/kvm_host.h> 12 + #include <linux/uaccess.h> 13 + #include <clocksource/timer-riscv.h> 14 + #include <asm/csr.h> 15 + #include <asm/delay.h> 16 + #include <asm/kvm_vcpu_timer.h> 17 + 18 + static u64 kvm_riscv_current_cycles(struct kvm_guest_timer *gt) 19 + { 20 + return get_cycles64() + gt->time_delta; 21 + } 22 + 23 + static u64 kvm_riscv_delta_cycles2ns(u64 cycles, 24 + struct kvm_guest_timer *gt, 25 + struct kvm_vcpu_timer *t) 26 + { 27 + unsigned long flags; 28 + u64 cycles_now, cycles_delta, delta_ns; 29 + 30 + local_irq_save(flags); 31 + cycles_now = kvm_riscv_current_cycles(gt); 32 + if (cycles_now < cycles) 33 + cycles_delta = cycles - cycles_now; 34 + else 35 + cycles_delta = 0; 36 + delta_ns = (cycles_delta * gt->nsec_mult) >> gt->nsec_shift; 37 + local_irq_restore(flags); 38 + 39 + return delta_ns; 40 + } 41 + 42 + static enum hrtimer_restart kvm_riscv_vcpu_hrtimer_expired(struct hrtimer *h) 43 + { 44 + u64 delta_ns; 45 + struct kvm_vcpu_timer *t = container_of(h, struct kvm_vcpu_timer, hrt); 46 + struct kvm_vcpu *vcpu = container_of(t, struct kvm_vcpu, arch.timer); 47 + struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; 48 + 49 + if (kvm_riscv_current_cycles(gt) < t->next_cycles) { 50 + delta_ns = kvm_riscv_delta_cycles2ns(t->next_cycles, gt, t); 51 + hrtimer_forward_now(&t->hrt, ktime_set(0, delta_ns)); 52 + return HRTIMER_RESTART; 53 + } 54 + 55 + t->next_set = false; 56 + kvm_riscv_vcpu_set_interrupt(vcpu, IRQ_VS_TIMER); 57 + 58 + return HRTIMER_NORESTART; 59 + } 60 + 61 + static int kvm_riscv_vcpu_timer_cancel(struct kvm_vcpu_timer *t) 62 + { 63 + if (!t->init_done || !t->next_set) 64 + return -EINVAL; 65 + 66 + hrtimer_cancel(&t->hrt); 67 + t->next_set = false; 68 + 69 + return 0; 70 + } 71 + 72 + int kvm_riscv_vcpu_timer_next_event(struct kvm_vcpu *vcpu, u64 ncycles) 73 + { 74 + struct kvm_vcpu_timer *t = &vcpu->arch.timer; 75 + struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; 76 + u64 delta_ns; 77 + 78 + if (!t->init_done) 79 + return -EINVAL; 80 + 81 + kvm_riscv_vcpu_unset_interrupt(vcpu, IRQ_VS_TIMER); 82 + 83 + delta_ns = kvm_riscv_delta_cycles2ns(ncycles, gt, t); 84 + t->next_cycles = ncycles; 85 + hrtimer_start(&t->hrt, ktime_set(0, delta_ns), HRTIMER_MODE_REL); 86 + t->next_set = true; 87 + 88 + return 0; 89 + } 90 + 91 + int kvm_riscv_vcpu_get_reg_timer(struct kvm_vcpu *vcpu, 92 + const struct kvm_one_reg *reg) 93 + { 94 + struct kvm_vcpu_timer *t = &vcpu->arch.timer; 95 + struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; 96 + u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr; 97 + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | 98 + KVM_REG_SIZE_MASK | 99 + KVM_REG_RISCV_TIMER); 100 + u64 reg_val; 101 + 102 + if (KVM_REG_SIZE(reg->id) != sizeof(u64)) 103 + return -EINVAL; 104 + if (reg_num >= sizeof(struct kvm_riscv_timer) / sizeof(u64)) 105 + return -EINVAL; 106 + 107 + switch (reg_num) { 108 + case KVM_REG_RISCV_TIMER_REG(frequency): 109 + reg_val = riscv_timebase; 110 + break; 111 + case KVM_REG_RISCV_TIMER_REG(time): 112 + reg_val = kvm_riscv_current_cycles(gt); 113 + break; 114 + case KVM_REG_RISCV_TIMER_REG(compare): 115 + reg_val = t->next_cycles; 116 + break; 117 + case KVM_REG_RISCV_TIMER_REG(state): 118 + reg_val = (t->next_set) ? KVM_RISCV_TIMER_STATE_ON : 119 + KVM_RISCV_TIMER_STATE_OFF; 120 + break; 121 + default: 122 + return -EINVAL; 123 + }; 124 + 125 + if (copy_to_user(uaddr, &reg_val, KVM_REG_SIZE(reg->id))) 126 + return -EFAULT; 127 + 128 + return 0; 129 + } 130 + 131 + int kvm_riscv_vcpu_set_reg_timer(struct kvm_vcpu *vcpu, 132 + const struct kvm_one_reg *reg) 133 + { 134 + struct kvm_vcpu_timer *t = &vcpu->arch.timer; 135 + struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; 136 + u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr; 137 + unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | 138 + KVM_REG_SIZE_MASK | 139 + KVM_REG_RISCV_TIMER); 140 + u64 reg_val; 141 + int ret = 0; 142 + 143 + if (KVM_REG_SIZE(reg->id) != sizeof(u64)) 144 + return -EINVAL; 145 + if (reg_num >= sizeof(struct kvm_riscv_timer) / sizeof(u64)) 146 + return -EINVAL; 147 + 148 + if (copy_from_user(&reg_val, uaddr, KVM_REG_SIZE(reg->id))) 149 + return -EFAULT; 150 + 151 + switch (reg_num) { 152 + case KVM_REG_RISCV_TIMER_REG(frequency): 153 + ret = -EOPNOTSUPP; 154 + break; 155 + case KVM_REG_RISCV_TIMER_REG(time): 156 + gt->time_delta = reg_val - get_cycles64(); 157 + break; 158 + case KVM_REG_RISCV_TIMER_REG(compare): 159 + t->next_cycles = reg_val; 160 + break; 161 + case KVM_REG_RISCV_TIMER_REG(state): 162 + if (reg_val == KVM_RISCV_TIMER_STATE_ON) 163 + ret = kvm_riscv_vcpu_timer_next_event(vcpu, reg_val); 164 + else 165 + ret = kvm_riscv_vcpu_timer_cancel(t); 166 + break; 167 + default: 168 + ret = -EINVAL; 169 + break; 170 + }; 171 + 172 + return ret; 173 + } 174 + 175 + int kvm_riscv_vcpu_timer_init(struct kvm_vcpu *vcpu) 176 + { 177 + struct kvm_vcpu_timer *t = &vcpu->arch.timer; 178 + 179 + if (t->init_done) 180 + return -EINVAL; 181 + 182 + hrtimer_init(&t->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 183 + t->hrt.function = kvm_riscv_vcpu_hrtimer_expired; 184 + t->init_done = true; 185 + t->next_set = false; 186 + 187 + return 0; 188 + } 189 + 190 + int kvm_riscv_vcpu_timer_deinit(struct kvm_vcpu *vcpu) 191 + { 192 + int ret; 193 + 194 + ret = kvm_riscv_vcpu_timer_cancel(&vcpu->arch.timer); 195 + vcpu->arch.timer.init_done = false; 196 + 197 + return ret; 198 + } 199 + 200 + int kvm_riscv_vcpu_timer_reset(struct kvm_vcpu *vcpu) 201 + { 202 + return kvm_riscv_vcpu_timer_cancel(&vcpu->arch.timer); 203 + } 204 + 205 + void kvm_riscv_vcpu_timer_restore(struct kvm_vcpu *vcpu) 206 + { 207 + struct kvm_guest_timer *gt = &vcpu->kvm->arch.timer; 208 + 209 + #ifdef CONFIG_64BIT 210 + csr_write(CSR_HTIMEDELTA, gt->time_delta); 211 + #else 212 + csr_write(CSR_HTIMEDELTA, (u32)(gt->time_delta)); 213 + csr_write(CSR_HTIMEDELTAH, (u32)(gt->time_delta >> 32)); 214 + #endif 215 + } 216 + 217 + int kvm_riscv_guest_timer_init(struct kvm *kvm) 218 + { 219 + struct kvm_guest_timer *gt = &kvm->arch.timer; 220 + 221 + riscv_cs_get_mult_shift(&gt->nsec_mult, &gt->nsec_shift); 222 + gt->time_delta = -get_cycles64(); 223 + 224 + return 0; 225 + }
+1 -1
arch/riscv/kvm/vm.c
··· 41 41 return r; 42 42 } 43 43 44 - return 0; 44 + return kvm_riscv_guest_timer_init(kvm); 45 45 } 46 46 47 47 void kvm_arch_destroy_vm(struct kvm *kvm)
+9
drivers/clocksource/timer-riscv.c
··· 13 13 #include <linux/delay.h> 14 14 #include <linux/irq.h> 15 15 #include <linux/irqdomain.h> 16 + #include <linux/module.h> 16 17 #include <linux/sched_clock.h> 17 18 #include <linux/io-64-nonatomic-lo-hi.h> 18 19 #include <linux/interrupt.h> 19 20 #include <linux/of_irq.h> 21 + #include <clocksource/timer-riscv.h> 20 22 #include <asm/smp.h> 21 23 #include <asm/sbi.h> 22 24 #include <asm/timex.h> ··· 80 78 disable_percpu_irq(riscv_clock_event_irq); 81 79 return 0; 82 80 } 81 + 82 + void riscv_cs_get_mult_shift(u32 *mult, u32 *shift) 83 + { 84 + *mult = riscv_clocksource.mult; 85 + *shift = riscv_clocksource.shift; 86 + } 87 + EXPORT_SYMBOL_GPL(riscv_cs_get_mult_shift); 83 88 84 89 /* called directly from the low-level interrupt handler */ 85 90 static irqreturn_t riscv_timer_interrupt(int irq, void *dev_id)
+16
include/clocksource/timer-riscv.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * Copyright (C) 2019 Western Digital Corporation or its affiliates. 4 + * 5 + * Authors: 6 + * Atish Patra <atish.patra@wdc.com> 7 + */ 8 + 9 + #ifndef __TIMER_RISCV_H 10 + #define __TIMER_RISCV_H 11 + 12 + #include <linux/types.h> 13 + 14 + extern void riscv_cs_get_mult_shift(u32 *mult, u32 *shift); 15 + 16 + #endif