xref: /linux/arch/arm64/kernel/paravirt.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2dfd57bc3SStefano Stabellini /*
3dfd57bc3SStefano Stabellini  *
4dfd57bc3SStefano Stabellini  * Copyright (C) 2013 Citrix Systems
5dfd57bc3SStefano Stabellini  *
6dfd57bc3SStefano Stabellini  * Author: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
7dfd57bc3SStefano Stabellini  */
8dfd57bc3SStefano Stabellini 
9e0685fa2SSteven Price #define pr_fmt(fmt) "arm-pv: " fmt
10e0685fa2SSteven Price 
11e0685fa2SSteven Price #include <linux/arm-smccc.h>
12e0685fa2SSteven Price #include <linux/cpuhotplug.h>
13dfd57bc3SStefano Stabellini #include <linux/export.h>
14e0685fa2SSteven Price #include <linux/io.h>
15dfd57bc3SStefano Stabellini #include <linux/jump_label.h>
16e0685fa2SSteven Price #include <linux/printk.h>
17e0685fa2SSteven Price #include <linux/psci.h>
18e0685fa2SSteven Price #include <linux/reboot.h>
19e0685fa2SSteven Price #include <linux/slab.h>
20dfd57bc3SStefano Stabellini #include <linux/types.h>
21a0e2bf7cSJuergen Gross #include <linux/static_call.h>
22e0685fa2SSteven Price 
23dfd57bc3SStefano Stabellini #include <asm/paravirt.h>
24e0685fa2SSteven Price #include <asm/pvclock-abi.h>
25e0685fa2SSteven Price #include <asm/smp_plat.h>
26dfd57bc3SStefano Stabellini 
27dfd57bc3SStefano Stabellini struct static_key paravirt_steal_enabled;
28dfd57bc3SStefano Stabellini struct static_key paravirt_steal_rq_enabled;
29dfd57bc3SStefano Stabellini 
native_steal_clock(int cpu)30a0e2bf7cSJuergen Gross static u64 native_steal_clock(int cpu)
31a0e2bf7cSJuergen Gross {
32a0e2bf7cSJuergen Gross 	return 0;
33a0e2bf7cSJuergen Gross }
34a0e2bf7cSJuergen Gross 
35a0e2bf7cSJuergen Gross DEFINE_STATIC_CALL(pv_steal_clock, native_steal_clock);
36e0685fa2SSteven Price 
37e0685fa2SSteven Price struct pv_time_stolen_time_region {
38*19bef63fSPrakruthi Deepak Heragu 	struct pvclock_vcpu_stolen_time __rcu *kaddr;
39e0685fa2SSteven Price };
40e0685fa2SSteven Price 
41e0685fa2SSteven Price static DEFINE_PER_CPU(struct pv_time_stolen_time_region, stolen_time_region);
42e0685fa2SSteven Price 
43e0685fa2SSteven Price static bool steal_acc = true;
parse_no_stealacc(char * arg)44e0685fa2SSteven Price static int __init parse_no_stealacc(char *arg)
45e0685fa2SSteven Price {
46e0685fa2SSteven Price 	steal_acc = false;
47e0685fa2SSteven Price 	return 0;
48e0685fa2SSteven Price }
49e0685fa2SSteven Price 
50e0685fa2SSteven Price early_param("no-steal-acc", parse_no_stealacc);
51e0685fa2SSteven Price 
52e0685fa2SSteven Price /* return stolen time in ns by asking the hypervisor */
para_steal_clock(int cpu)53a0e2bf7cSJuergen Gross static u64 para_steal_clock(int cpu)
54e0685fa2SSteven Price {
55*19bef63fSPrakruthi Deepak Heragu 	struct pvclock_vcpu_stolen_time *kaddr = NULL;
56e0685fa2SSteven Price 	struct pv_time_stolen_time_region *reg;
57*19bef63fSPrakruthi Deepak Heragu 	u64 ret = 0;
58e0685fa2SSteven Price 
59e0685fa2SSteven Price 	reg = per_cpu_ptr(&stolen_time_region, cpu);
6075df529bSAndrew Jones 
6175df529bSAndrew Jones 	/*
6275df529bSAndrew Jones 	 * paravirt_steal_clock() may be called before the CPU
6375df529bSAndrew Jones 	 * online notification callback runs. Until the callback
6475df529bSAndrew Jones 	 * has run we just return zero.
6575df529bSAndrew Jones 	 */
66*19bef63fSPrakruthi Deepak Heragu 	rcu_read_lock();
67*19bef63fSPrakruthi Deepak Heragu 	kaddr = rcu_dereference(reg->kaddr);
68*19bef63fSPrakruthi Deepak Heragu 	if (!kaddr) {
69*19bef63fSPrakruthi Deepak Heragu 		rcu_read_unlock();
70e0685fa2SSteven Price 		return 0;
71*19bef63fSPrakruthi Deepak Heragu 	}
72e0685fa2SSteven Price 
73*19bef63fSPrakruthi Deepak Heragu 	ret = le64_to_cpu(READ_ONCE(kaddr->stolen_time));
74*19bef63fSPrakruthi Deepak Heragu 	rcu_read_unlock();
75*19bef63fSPrakruthi Deepak Heragu 	return ret;
76e0685fa2SSteven Price }
77e0685fa2SSteven Price 
stolen_time_cpu_down_prepare(unsigned int cpu)7875df529bSAndrew Jones static int stolen_time_cpu_down_prepare(unsigned int cpu)
79e0685fa2SSteven Price {
80*19bef63fSPrakruthi Deepak Heragu 	struct pvclock_vcpu_stolen_time *kaddr = NULL;
81e0685fa2SSteven Price 	struct pv_time_stolen_time_region *reg;
82e0685fa2SSteven Price 
83e0685fa2SSteven Price 	reg = this_cpu_ptr(&stolen_time_region);
84e0685fa2SSteven Price 	if (!reg->kaddr)
85e0685fa2SSteven Price 		return 0;
86e0685fa2SSteven Price 
87*19bef63fSPrakruthi Deepak Heragu 	kaddr = rcu_replace_pointer(reg->kaddr, NULL, true);
88*19bef63fSPrakruthi Deepak Heragu 	synchronize_rcu();
89*19bef63fSPrakruthi Deepak Heragu 	memunmap(kaddr);
90e0685fa2SSteven Price 
91e0685fa2SSteven Price 	return 0;
92e0685fa2SSteven Price }
93e0685fa2SSteven Price 
stolen_time_cpu_online(unsigned int cpu)9475df529bSAndrew Jones static int stolen_time_cpu_online(unsigned int cpu)
95e0685fa2SSteven Price {
96*19bef63fSPrakruthi Deepak Heragu 	struct pvclock_vcpu_stolen_time *kaddr = NULL;
97e0685fa2SSteven Price 	struct pv_time_stolen_time_region *reg;
98e0685fa2SSteven Price 	struct arm_smccc_res res;
99e0685fa2SSteven Price 
100e0685fa2SSteven Price 	reg = this_cpu_ptr(&stolen_time_region);
101e0685fa2SSteven Price 
102e0685fa2SSteven Price 	arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_ST, &res);
103e0685fa2SSteven Price 
104e0685fa2SSteven Price 	if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
105e0685fa2SSteven Price 		return -EINVAL;
106e0685fa2SSteven Price 
107*19bef63fSPrakruthi Deepak Heragu 	kaddr = memremap(res.a0,
108e0685fa2SSteven Price 			      sizeof(struct pvclock_vcpu_stolen_time),
109e0685fa2SSteven Price 			      MEMREMAP_WB);
110e0685fa2SSteven Price 
111*19bef63fSPrakruthi Deepak Heragu 	rcu_assign_pointer(reg->kaddr, kaddr);
112*19bef63fSPrakruthi Deepak Heragu 
113e0685fa2SSteven Price 	if (!reg->kaddr) {
114e0685fa2SSteven Price 		pr_warn("Failed to map stolen time data structure\n");
115e0685fa2SSteven Price 		return -ENOMEM;
116e0685fa2SSteven Price 	}
117e0685fa2SSteven Price 
118*19bef63fSPrakruthi Deepak Heragu 	if (le32_to_cpu(kaddr->revision) != 0 ||
119*19bef63fSPrakruthi Deepak Heragu 	    le32_to_cpu(kaddr->attributes) != 0) {
120e0685fa2SSteven Price 		pr_warn_once("Unexpected revision or attributes in stolen time data\n");
121e0685fa2SSteven Price 		return -ENXIO;
122e0685fa2SSteven Price 	}
123e0685fa2SSteven Price 
124e0685fa2SSteven Price 	return 0;
125e0685fa2SSteven Price }
126e0685fa2SSteven Price 
pv_time_init_stolen_time(void)12775df529bSAndrew Jones static int __init pv_time_init_stolen_time(void)
128e0685fa2SSteven Price {
129e0685fa2SSteven Price 	int ret;
130e0685fa2SSteven Price 
13175df529bSAndrew Jones 	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
13275df529bSAndrew Jones 				"hypervisor/arm/pvtime:online",
13375df529bSAndrew Jones 				stolen_time_cpu_online,
13475df529bSAndrew Jones 				stolen_time_cpu_down_prepare);
135e0685fa2SSteven Price 	if (ret < 0)
136e0685fa2SSteven Price 		return ret;
137e0685fa2SSteven Price 	return 0;
138e0685fa2SSteven Price }
139e0685fa2SSteven Price 
has_pv_steal_clock(void)14075df529bSAndrew Jones static bool __init has_pv_steal_clock(void)
141e0685fa2SSteven Price {
142e0685fa2SSteven Price 	struct arm_smccc_res res;
143e0685fa2SSteven Price 
144e0685fa2SSteven Price 	arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
145e0685fa2SSteven Price 			     ARM_SMCCC_HV_PV_TIME_FEATURES, &res);
146e0685fa2SSteven Price 
147e0685fa2SSteven Price 	if (res.a0 != SMCCC_RET_SUCCESS)
148e0685fa2SSteven Price 		return false;
149e0685fa2SSteven Price 
150e0685fa2SSteven Price 	arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_FEATURES,
151e0685fa2SSteven Price 			     ARM_SMCCC_HV_PV_TIME_ST, &res);
152e0685fa2SSteven Price 
153e0685fa2SSteven Price 	return (res.a0 == SMCCC_RET_SUCCESS);
154e0685fa2SSteven Price }
155e0685fa2SSteven Price 
pv_time_init(void)156e0685fa2SSteven Price int __init pv_time_init(void)
157e0685fa2SSteven Price {
158e0685fa2SSteven Price 	int ret;
159e0685fa2SSteven Price 
160e0685fa2SSteven Price 	if (!has_pv_steal_clock())
161e0685fa2SSteven Price 		return 0;
162e0685fa2SSteven Price 
163e0685fa2SSteven Price 	ret = pv_time_init_stolen_time();
164e0685fa2SSteven Price 	if (ret)
165e0685fa2SSteven Price 		return ret;
166e0685fa2SSteven Price 
167a0e2bf7cSJuergen Gross 	static_call_update(pv_steal_clock, para_steal_clock);
168e0685fa2SSteven Price 
169e0685fa2SSteven Price 	static_key_slow_inc(&paravirt_steal_enabled);
170e0685fa2SSteven Price 	if (steal_acc)
171e0685fa2SSteven Price 		static_key_slow_inc(&paravirt_steal_rq_enabled);
172e0685fa2SSteven Price 
173e0685fa2SSteven Price 	pr_info("using stolen time PV\n");
174e0685fa2SSteven Price 
175e0685fa2SSteven Price 	return 0;
176e0685fa2SSteven Price }
177