Blame view

arch/arm64/kernel/paravirt.c 3.41 KB
1802d0bee   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
dfd57bc3a   Stefano Stabellini   arm64: introduce ...
2
  /*
dfd57bc3a   Stefano Stabellini   arm64: introduce ...
3
4
5
6
7
   *
   * Copyright (C) 2013 Citrix Systems
   *
   * Author: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
   */
e0685fa22   Steven Price   arm64: Retrieve s...
8
9
10
11
  #define pr_fmt(fmt) "arm-pv: " fmt
  
  #include <linux/arm-smccc.h>
  #include <linux/cpuhotplug.h>
dfd57bc3a   Stefano Stabellini   arm64: introduce ...
12
  #include <linux/export.h>
e0685fa22   Steven Price   arm64: Retrieve s...
13
  #include <linux/io.h>
dfd57bc3a   Stefano Stabellini   arm64: introduce ...
14
  #include <linux/jump_label.h>
e0685fa22   Steven Price   arm64: Retrieve s...
15
16
17
18
  #include <linux/printk.h>
  #include <linux/psci.h>
  #include <linux/reboot.h>
  #include <linux/slab.h>
dfd57bc3a   Stefano Stabellini   arm64: introduce ...
19
  #include <linux/types.h>
e0685fa22   Steven Price   arm64: Retrieve s...
20

dfd57bc3a   Stefano Stabellini   arm64: introduce ...
21
  #include <asm/paravirt.h>
e0685fa22   Steven Price   arm64: Retrieve s...
22
23
  #include <asm/pvclock-abi.h>
  #include <asm/smp_plat.h>
dfd57bc3a   Stefano Stabellini   arm64: introduce ...
24
25
26
  
  struct static_key paravirt_steal_enabled;
  struct static_key paravirt_steal_rq_enabled;
5c83511bd   Juergen Gross   x86/paravirt: Use...
27
28
  struct paravirt_patch_template pv_ops;
  EXPORT_SYMBOL_GPL(pv_ops);
e0685fa22   Steven Price   arm64: Retrieve s...
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  
  struct pv_time_stolen_time_region {
  	struct pvclock_vcpu_stolen_time *kaddr;
  };
  
  static DEFINE_PER_CPU(struct pv_time_stolen_time_region, stolen_time_region);
  
  static bool steal_acc = true;
  static int __init parse_no_stealacc(char *arg)
  {
  	steal_acc = false;
  	return 0;
  }
  
  early_param("no-steal-acc", parse_no_stealacc);
  
  /* return stolen time in ns by asking the hypervisor */
  static u64 pv_steal_clock(int cpu)
  {
  	struct pv_time_stolen_time_region *reg;
  
  	reg = per_cpu_ptr(&stolen_time_region, cpu);
75df529be   Andrew Jones   arm64: paravirt: ...
51
52
53
54
55
56
57
  
  	/*
  	 * paravirt_steal_clock() may be called before the CPU
  	 * online notification callback runs. Until the callback
  	 * has run we just return zero.
  	 */
  	if (!reg->kaddr)
e0685fa22   Steven Price   arm64: Retrieve s...
58
  		return 0;
e0685fa22   Steven Price   arm64: Retrieve s...
59
60
61
  
  	return le64_to_cpu(READ_ONCE(reg->kaddr->stolen_time));
  }
75df529be   Andrew Jones   arm64: paravirt: ...
62
  static int stolen_time_cpu_down_prepare(unsigned int cpu)
e0685fa22   Steven Price   arm64: Retrieve s...
63
64
65
66
67
68
69
70
71
72
73
74
  {
  	struct pv_time_stolen_time_region *reg;
  
  	reg = this_cpu_ptr(&stolen_time_region);
  	if (!reg->kaddr)
  		return 0;
  
  	memunmap(reg->kaddr);
  	memset(reg, 0, sizeof(*reg));
  
  	return 0;
  }
75df529be   Andrew Jones   arm64: paravirt: ...
75
  static int stolen_time_cpu_online(unsigned int cpu)
e0685fa22   Steven Price   arm64: Retrieve s...
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
  {
  	struct pv_time_stolen_time_region *reg;
  	struct arm_smccc_res res;
  
  	reg = this_cpu_ptr(&stolen_time_region);
  
  	arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_ST, &res);
  
  	if (res.a0 == SMCCC_RET_NOT_SUPPORTED)
  		return -EINVAL;
  
  	reg->kaddr = memremap(res.a0,
  			      sizeof(struct pvclock_vcpu_stolen_time),
  			      MEMREMAP_WB);
  
  	if (!reg->kaddr) {
  		pr_warn("Failed to map stolen time data structure
  ");
  		return -ENOMEM;
  	}
  
  	if (le32_to_cpu(reg->kaddr->revision) != 0 ||
  	    le32_to_cpu(reg->kaddr->attributes) != 0) {
  		pr_warn_once("Unexpected revision or attributes in stolen time data
  ");
  		return -ENXIO;
  	}
  
  	return 0;
  }
75df529be   Andrew Jones   arm64: paravirt: ...
106
  static int __init pv_time_init_stolen_time(void)
e0685fa22   Steven Price   arm64: Retrieve s...
107
108
  {
  	int ret;
75df529be   Andrew Jones   arm64: paravirt: ...
109
110
111
112
  	ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
  				"hypervisor/arm/pvtime:online",
  				stolen_time_cpu_online,
  				stolen_time_cpu_down_prepare);
e0685fa22   Steven Price   arm64: Retrieve s...
113
114
115
116
  	if (ret < 0)
  		return ret;
  	return 0;
  }
75df529be   Andrew Jones   arm64: paravirt: ...
117
  static bool __init has_pv_steal_clock(void)
e0685fa22   Steven Price   arm64: Retrieve s...
118
119
120
121
  {
  	struct arm_smccc_res res;
  
  	/* To detect the presence of PV time support we require SMCCC 1.1+ */
ad5a57dfe   Sudeep Holla   firmware: smccc: ...
122
  	if (arm_smccc_1_1_get_conduit() == SMCCC_CONDUIT_NONE)
e0685fa22   Steven Price   arm64: Retrieve s...
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  		return false;
  
  	arm_smccc_1_1_invoke(ARM_SMCCC_ARCH_FEATURES_FUNC_ID,
  			     ARM_SMCCC_HV_PV_TIME_FEATURES, &res);
  
  	if (res.a0 != SMCCC_RET_SUCCESS)
  		return false;
  
  	arm_smccc_1_1_invoke(ARM_SMCCC_HV_PV_TIME_FEATURES,
  			     ARM_SMCCC_HV_PV_TIME_ST, &res);
  
  	return (res.a0 == SMCCC_RET_SUCCESS);
  }
  
  int __init pv_time_init(void)
  {
  	int ret;
  
  	if (!has_pv_steal_clock())
  		return 0;
  
  	ret = pv_time_init_stolen_time();
  	if (ret)
  		return ret;
  
  	pv_ops.time.steal_clock = pv_steal_clock;
  
  	static_key_slow_inc(&paravirt_steal_enabled);
  	if (steal_acc)
  		static_key_slow_inc(&paravirt_steal_rq_enabled);
  
  	pr_info("using stolen time PV
  ");
  
  	return 0;
  }