Merge tag 'vfio-v5.5-rc1' of git://github.com/awilliam/linux-vfio
[linux-2.6-microblaze.git] / arch / arm64 / kernel / paravirt.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *
4  * Copyright (C) 2013 Citrix Systems
5  *
6  * Author: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
7  */
8
9 #define pr_fmt(fmt) "arm-pv: " fmt
10
11 #include <linux/arm-smccc.h>
12 #include <linux/cpuhotplug.h>
13 #include <linux/export.h>
14 #include <linux/io.h>
15 #include <linux/jump_label.h>
16 #include <linux/printk.h>
17 #include <linux/psci.h>
18 #include <linux/reboot.h>
19 #include <linux/slab.h>
20 #include <linux/types.h>
21
22 #include <asm/paravirt.h>
23 #include <asm/pvclock-abi.h>
24 #include <asm/smp_plat.h>
25
26 struct static_key paravirt_steal_enabled;
27 struct static_key paravirt_steal_rq_enabled;
28
29 struct paravirt_patch_template pv_ops;
30 EXPORT_SYMBOL_GPL(pv_ops);
31
32 struct pv_time_stolen_time_region {
33         struct pvclock_vcpu_stolen_time *kaddr;
34 };
35
36 static DEFINE_PER_CPU(struct pv_time_stolen_time_region, stolen_time_region);
37
38 static bool steal_acc = true;
39 static int __init parse_no_stealacc(char *arg)
40 {
41         steal_acc = false;
42         return 0;
43 }
44
45 early_param("no-steal-acc", parse_no_stealacc);
46
47 /* return stolen time in ns by asking the hypervisor */
48 static u64 pv_steal_clock(int cpu)
49 {
50         struct pv_time_stolen_time_region *reg;
51
52         reg = per_cpu_ptr(&stolen_time_region, cpu);
53         if (!reg->kaddr) {
54                 pr_warn_once("stolen time enabled but not configured for cpu %d\n",
55                              cpu);
56                 return 0;
57         }
58
59         return le64_to_cpu(READ_ONCE(reg->kaddr->stolen_time));
60 }
61
62 static int stolen_time_dying_cpu(unsigned int cpu)
63 {
64         struct pv_time_stolen_time_region *reg;
65
66         reg = this_cpu_ptr(&stolen_time_region);
67         if (!reg->kaddr)
68                 return 0;
69
70         memunmap(reg->kaddr);
71         memset(reg, 0, sizeof(*reg));
72
73         return 0;
74 }
75
76 static int init_stolen_time_cpu(unsigned int cpu)
77 {
78         struct pv_time_stolen_time_region *reg;
79         struct arm_smccc_res res;
80
81         reg = this_cpu_ptr(&stolen_time_region);
82
83         arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_ST, &res);
84
85         if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
86                 return -EINVAL;
87
88         reg->kaddr = memremap(res.a0,
89                               sizeof(struct pvclock_vcpu_stolen_time),
90                               MEMREMAP_WB);
91
92         if (!reg->kaddr) {
93                 pr_warn("Failed to map stolen time data structure\n");
94                 return -ENOMEM;
95         }
96
97         if (le32_to_cpu(reg->kaddr->revision) != 0 ||
98             le32_to_cpu(reg->kaddr->attributes) != 0) {
99                 pr_warn_once("Unexpected revision or attributes in stolen time data\n");
100                 return -ENXIO;
101         }
102
103         return 0;
104 }
105
106 static int pv_time_init_stolen_time(void)
107 {
108         int ret;
109
110         ret = cpuhp_setup_state(CPUHP_AP_ARM_KVMPV_STARTING,
111                                 "hypervisor/arm/pvtime:starting",
112                                 init_stolen_time_cpu, stolen_time_dying_cpu);
113         if (ret < 0)
114                 return ret;
115         return 0;
116 }
117
118 static bool has_pv_steal_clock(void)
119 {
120         struct arm_smccc_res res;
121
122         /* To detect the presence of PV time support we require SMCCC 1.1+ */
123         if (psci_ops.smccc_version < SMCCC_VERSION_1_1)
124                 return false;
125
126         arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
127                              ARM_SMCCC_HV_PV_TIME_FEATURES, &res);
128
129         if (res.a0 != SMCCC_RET_SUCCESS)
130                 return false;
131
132         arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_FEATURES,
133                              ARM_SMCCC_HV_PV_TIME_ST, &res);
134
135         return (res.a0 == SMCCC_RET_SUCCESS);
136 }
137
138 int __init pv_time_init(void)
139 {
140         int ret;
141
142         if (!has_pv_steal_clock())
143                 return 0;
144
145         ret = pv_time_init_stolen_time();
146         if (ret)
147                 return ret;
148
149         pv_ops.time.steal_clock = pv_steal_clock;
150
151         static_key_slow_inc(&paravirt_steal_enabled);
152         if (steal_acc)
153                 static_key_slow_inc(&paravirt_steal_rq_enabled);
154
155         pr_info("using stolen time PV\n");
156
157         return 0;
158 }