1 #include "kvm/kvm.h"
2 #include "kvm/kvm-cpu.h"
3 #include "kvm/util.h"
4
5 #include <linux/byteorder.h>
6 #include <linux/types.h>
7
8 #define ARM_PVTIME_STRUCT_SIZE (64)
9
10 static void *usr_mem;
11
pvtime__alloc_region(struct kvm * kvm)12 static int pvtime__alloc_region(struct kvm *kvm)
13 {
14 char *mem;
15 int ret = 0;
16
17 mem = mmap(NULL, ARM_PVTIME_SIZE, PROT_RW,
18 MAP_ANON_NORESERVE, -1, 0);
19 if (mem == MAP_FAILED)
20 return -errno;
21
22 ret = kvm__register_ram(kvm, ARM_PVTIME_BASE,
23 ARM_PVTIME_SIZE, mem);
24 if (ret) {
25 munmap(mem, ARM_PVTIME_SIZE);
26 return ret;
27 }
28
29 usr_mem = mem;
30 return ret;
31 }
32
pvtime__teardown_region(struct kvm * kvm)33 static int pvtime__teardown_region(struct kvm *kvm)
34 {
35 if (usr_mem == NULL)
36 return 0;
37
38 kvm__destroy_mem(kvm, ARM_PVTIME_BASE,
39 ARM_PVTIME_SIZE, usr_mem);
40 munmap(usr_mem, ARM_PVTIME_SIZE);
41 usr_mem = NULL;
42 return 0;
43 }
44
kvm_cpu__setup_pvtime(struct kvm_cpu * vcpu)45 int kvm_cpu__setup_pvtime(struct kvm_cpu *vcpu)
46 {
47 int ret;
48 bool has_stolen_time;
49 u64 pvtime_guest_addr = ARM_PVTIME_BASE + vcpu->cpu_id *
50 ARM_PVTIME_STRUCT_SIZE;
51 struct kvm_config_arch *kvm_cfg = NULL;
52 struct kvm_device_attr pvtime_attr = (struct kvm_device_attr) {
53 .group = KVM_ARM_VCPU_PVTIME_CTRL,
54 .attr = KVM_ARM_VCPU_PVTIME_IPA
55 };
56
57 kvm_cfg = &vcpu->kvm->cfg.arch;
58 if (kvm_cfg->no_pvtime)
59 return 0;
60
61 has_stolen_time = kvm__supports_extension(vcpu->kvm,
62 KVM_CAP_STEAL_TIME);
63 if (!has_stolen_time) {
64 kvm_cfg->no_pvtime = true;
65 return 0;
66 }
67
68 ret = ioctl(vcpu->vcpu_fd, KVM_HAS_DEVICE_ATTR, &pvtime_attr);
69 if (ret) {
70 ret = -errno;
71 perror("KVM_HAS_DEVICE_ATTR failed\n");
72 goto out_err;
73 }
74
75 if (!usr_mem) {
76 ret = pvtime__alloc_region(vcpu->kvm);
77 if (ret) {
78 perror("Failed allocating pvtime region\n");
79 goto out_err;
80 }
81 }
82
83 pvtime_attr.addr = (u64)&pvtime_guest_addr;
84 ret = ioctl(vcpu->vcpu_fd, KVM_SET_DEVICE_ATTR, &pvtime_attr);
85 if (!ret)
86 return 0;
87
88 ret = -errno;
89 perror("KVM_SET_DEVICE_ATTR failed\n");
90 pvtime__teardown_region(vcpu->kvm);
91 out_err:
92 return ret;
93 }
94
kvm_cpu__teardown_pvtime(struct kvm * kvm)95 int kvm_cpu__teardown_pvtime(struct kvm *kvm)
96 {
97 return pvtime__teardown_region(kvm);
98 }
99