1 /******************************************************************************
2 * Arch-specific sysctl.c
3 *
4 * System management operations. For use by node control stack.
5 *
6 * Copyright (c) 2002-2006, K Fraser
7 */
8
9 #include <xen/types.h>
10 #include <xen/lib.h>
11 #include <xen/mm.h>
12 #include <xen/nospec.h>
13 #include <xen/guest_access.h>
14 #include <xen/hypercall.h>
15 #include <public/sysctl.h>
16 #include <xen/sched.h>
17 #include <xen/event.h>
18 #include <xen/domain_page.h>
19 #include <asm/msr.h>
20 #include <xen/trace.h>
21 #include <xen/console.h>
22 #include <xen/iocap.h>
23 #include <asm/irq.h>
24 #include <asm/hvm/hvm.h>
25 #include <asm/hvm/support.h>
26 #include <asm/processor.h>
27 #include <asm/setup.h>
28 #include <asm/smp.h>
29 #include <asm/numa.h>
30 #include <xen/nodemask.h>
31 #include <xen/cpu.h>
32 #include <xsm/xsm.h>
33 #include <asm/psr.h>
34 #include <asm/cpuid.h>
35
36 const struct cpu_policy system_policies[6] = {
37 [ XEN_SYSCTL_cpu_policy_raw ] = {
38 &raw_cpuid_policy,
39 &raw_msr_policy,
40 },
41 [ XEN_SYSCTL_cpu_policy_host ] = {
42 &host_cpuid_policy,
43 &host_msr_policy,
44 },
45 #ifdef CONFIG_PV
46 [ XEN_SYSCTL_cpu_policy_pv_max ] = {
47 &pv_max_cpuid_policy,
48 &pv_max_msr_policy,
49 },
50 [ XEN_SYSCTL_cpu_policy_pv_default ] = {
51 &pv_def_cpuid_policy,
52 &pv_def_msr_policy,
53 },
54 #endif
55 #ifdef CONFIG_HVM
56 [ XEN_SYSCTL_cpu_policy_hvm_max ] = {
57 &hvm_max_cpuid_policy,
58 &hvm_max_msr_policy,
59 },
60 [ XEN_SYSCTL_cpu_policy_hvm_default ] = {
61 &hvm_def_cpuid_policy,
62 &hvm_def_msr_policy,
63 },
64 #endif
65 };
66
67 struct l3_cache_info {
68 int ret;
69 unsigned long size;
70 };
71
l3_cache_get(void * arg)72 static void l3_cache_get(void *arg)
73 {
74 struct cpuid4_info info;
75 struct l3_cache_info *l3_info = arg;
76
77 l3_info->ret = cpuid4_cache_lookup(3, &info);
78 if ( !l3_info->ret )
79 l3_info->size = info.size / 1024; /* in KB unit */
80 }
81
cpu_up_helper(void * data)82 long cpu_up_helper(void *data)
83 {
84 unsigned int cpu = (unsigned long)data;
85 int ret = cpu_up(cpu);
86
87 /* Have one more go on EBUSY. */
88 if ( ret == -EBUSY )
89 ret = cpu_up(cpu);
90
91 if ( !ret && !opt_smt &&
92 cpu_data[cpu].compute_unit_id == INVALID_CUID &&
93 cpumask_weight(per_cpu(cpu_sibling_mask, cpu)) > 1 )
94 {
95 ret = cpu_down_helper(data);
96 if ( ret )
97 printk("Could not re-offline CPU%u (%d)\n", cpu, ret);
98 else
99 ret = -EPERM;
100 }
101
102 return ret;
103 }
104
cpu_down_helper(void * data)105 long cpu_down_helper(void *data)
106 {
107 int cpu = (unsigned long)data;
108 int ret = cpu_down(cpu);
109 /* Have one more go on EBUSY. */
110 if ( ret == -EBUSY )
111 ret = cpu_down(cpu);
112 return ret;
113 }
114
smt_up_down_helper(void * data)115 static long smt_up_down_helper(void *data)
116 {
117 bool up = (bool)data;
118 unsigned int cpu, sibling_mask = boot_cpu_data.x86_num_siblings - 1;
119 int ret = 0;
120
121 opt_smt = up;
122
123 for_each_present_cpu ( cpu )
124 {
125 /* Skip primary siblings (those whose thread id is 0). */
126 if ( !(x86_cpu_to_apicid[cpu] & sibling_mask) )
127 continue;
128
129 if ( !up && core_parking_remove(cpu) )
130 continue;
131
132 ret = up ? cpu_up_helper(_p(cpu))
133 : cpu_down_helper(_p(cpu));
134
135 if ( ret && ret != -EEXIST )
136 break;
137
138 /*
139 * Ensure forward progress by only considering preemption when we have
140 * changed the state of one or more cpus.
141 */
142 if ( ret != -EEXIST && general_preempt_check() )
143 {
144 /* In tasklet context - can't create a contination. */
145 ret = -EBUSY;
146 break;
147 }
148
149 ret = 0; /* Avoid exiting with -EEXIST in the success case. */
150 }
151
152 if ( !ret )
153 printk(XENLOG_INFO "SMT %s - online CPUs 0x%*pb\n",
154 up ? "enabled" : "disabled", CPUMASK_PR(&cpu_online_map));
155
156 return ret;
157 }
158
arch_do_physinfo(struct xen_sysctl_physinfo * pi)159 void arch_do_physinfo(struct xen_sysctl_physinfo *pi)
160 {
161 memcpy(pi->hw_cap, boot_cpu_data.x86_capability,
162 min(sizeof(pi->hw_cap), sizeof(boot_cpu_data.x86_capability)));
163 if ( hvm_enabled )
164 pi->capabilities |= XEN_SYSCTL_PHYSCAP_hvm;
165 if ( IS_ENABLED(CONFIG_PV) )
166 pi->capabilities |= XEN_SYSCTL_PHYSCAP_pv;
167 if ( hvm_hap_supported() )
168 pi->capabilities |= XEN_SYSCTL_PHYSCAP_hap;
169 if ( IS_ENABLED(CONFIG_SHADOW_PAGING) )
170 pi->capabilities |= XEN_SYSCTL_PHYSCAP_shadow;
171 }
172
arch_do_sysctl(struct xen_sysctl * sysctl,XEN_GUEST_HANDLE_PARAM (xen_sysctl_t)u_sysctl)173 long arch_do_sysctl(
174 struct xen_sysctl *sysctl, XEN_GUEST_HANDLE_PARAM(xen_sysctl_t) u_sysctl)
175 {
176 long ret = 0;
177
178 switch ( sysctl->cmd )
179 {
180
181 case XEN_SYSCTL_cpu_hotplug:
182 {
183 unsigned int cpu = sysctl->u.cpu_hotplug.cpu;
184 unsigned int op = sysctl->u.cpu_hotplug.op;
185 bool plug;
186 long (*fn)(void *);
187 void *hcpu;
188
189 switch ( op )
190 {
191 case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
192 plug = true;
193 fn = cpu_up_helper;
194 hcpu = _p(cpu);
195 break;
196
197 case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
198 plug = false;
199 fn = cpu_down_helper;
200 hcpu = _p(cpu);
201 break;
202
203 case XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE:
204 case XEN_SYSCTL_CPU_HOTPLUG_SMT_DISABLE:
205 if ( !cpu_has_htt || boot_cpu_data.x86_num_siblings < 2 )
206 {
207 ret = -EOPNOTSUPP;
208 break;
209 }
210 if ( sched_disable_smt_switching )
211 {
212 ret = -EBUSY;
213 break;
214 }
215 plug = op == XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE;
216 fn = smt_up_down_helper;
217 hcpu = _p(plug);
218 break;
219
220 default:
221 ret = -EOPNOTSUPP;
222 break;
223 }
224
225 if ( !ret )
226 ret = plug ? xsm_resource_plug_core(XSM_HOOK)
227 : xsm_resource_unplug_core(XSM_HOOK);
228
229 if ( !ret )
230 ret = continue_hypercall_on_cpu(0, fn, hcpu);
231 }
232 break;
233
234 case XEN_SYSCTL_psr_cmt_op:
235 if ( !psr_cmt_enabled() )
236 return -ENODEV;
237
238 if ( sysctl->u.psr_cmt_op.flags != 0 )
239 return -EINVAL;
240
241 switch ( sysctl->u.psr_cmt_op.cmd )
242 {
243 case XEN_SYSCTL_PSR_CMT_enabled:
244 sysctl->u.psr_cmt_op.u.data =
245 (psr_cmt->features & PSR_RESOURCE_TYPE_L3) &&
246 (psr_cmt->l3.features & PSR_CMT_L3_OCCUPANCY);
247 break;
248 case XEN_SYSCTL_PSR_CMT_get_total_rmid:
249 sysctl->u.psr_cmt_op.u.data = psr_cmt->rmid_max;
250 break;
251 case XEN_SYSCTL_PSR_CMT_get_l3_upscaling_factor:
252 sysctl->u.psr_cmt_op.u.data = psr_cmt->l3.upscaling_factor;
253 break;
254 case XEN_SYSCTL_PSR_CMT_get_l3_cache_size:
255 {
256 struct l3_cache_info info;
257 unsigned int cpu = sysctl->u.psr_cmt_op.u.l3_cache.cpu;
258
259 if ( (cpu >= nr_cpu_ids) || !cpu_online(cpu) )
260 {
261 ret = -ENODEV;
262 sysctl->u.psr_cmt_op.u.data = 0;
263 break;
264 }
265 if ( cpu == smp_processor_id() )
266 l3_cache_get(&info);
267 else
268 on_selected_cpus(cpumask_of(cpu), l3_cache_get, &info, 1);
269
270 ret = info.ret;
271 sysctl->u.psr_cmt_op.u.data = (ret ? 0 : info.size);
272 break;
273 }
274 case XEN_SYSCTL_PSR_CMT_get_l3_event_mask:
275 sysctl->u.psr_cmt_op.u.data = psr_cmt->l3.features;
276 break;
277 default:
278 sysctl->u.psr_cmt_op.u.data = 0;
279 ret = -ENOSYS;
280 break;
281 }
282
283 if ( __copy_to_guest(u_sysctl, sysctl, 1) )
284 ret = -EFAULT;
285
286 break;
287
288 case XEN_SYSCTL_psr_alloc:
289 {
290 uint32_t data[PSR_INFO_ARRAY_SIZE] = { };
291
292 switch ( sysctl->u.psr_alloc.cmd )
293 {
294 case XEN_SYSCTL_PSR_get_l3_info:
295 ret = psr_get_info(sysctl->u.psr_alloc.target,
296 PSR_TYPE_L3_CBM, data, ARRAY_SIZE(data));
297 if ( ret )
298 break;
299
300 sysctl->u.psr_alloc.u.cat_info.cos_max =
301 data[PSR_INFO_IDX_COS_MAX];
302 sysctl->u.psr_alloc.u.cat_info.cbm_len =
303 data[PSR_INFO_IDX_CAT_CBM_LEN];
304 sysctl->u.psr_alloc.u.cat_info.flags =
305 data[PSR_INFO_IDX_CAT_FLAGS];
306
307 if ( __copy_field_to_guest(u_sysctl, sysctl, u.psr_alloc) )
308 ret = -EFAULT;
309 break;
310
311 case XEN_SYSCTL_PSR_get_l2_info:
312 ret = psr_get_info(sysctl->u.psr_alloc.target,
313 PSR_TYPE_L2_CBM, data, ARRAY_SIZE(data));
314 if ( ret )
315 break;
316
317 sysctl->u.psr_alloc.u.cat_info.cos_max =
318 data[PSR_INFO_IDX_COS_MAX];
319 sysctl->u.psr_alloc.u.cat_info.cbm_len =
320 data[PSR_INFO_IDX_CAT_CBM_LEN];
321 sysctl->u.psr_alloc.u.cat_info.flags =
322 data[PSR_INFO_IDX_CAT_FLAGS];
323
324 if ( __copy_field_to_guest(u_sysctl, sysctl, u.psr_alloc) )
325 ret = -EFAULT;
326 break;
327
328 case XEN_SYSCTL_PSR_get_mba_info:
329 ret = psr_get_info(sysctl->u.psr_alloc.target,
330 PSR_TYPE_MBA_THRTL, data, ARRAY_SIZE(data));
331 if ( ret )
332 break;
333
334 sysctl->u.psr_alloc.u.mba_info.cos_max =
335 data[PSR_INFO_IDX_COS_MAX];
336 sysctl->u.psr_alloc.u.mba_info.thrtl_max =
337 data[PSR_INFO_IDX_MBA_THRTL_MAX];
338 sysctl->u.psr_alloc.u.mba_info.flags =
339 data[PSR_INFO_IDX_MBA_FLAGS];
340
341 if ( __copy_field_to_guest(u_sysctl, sysctl, u.psr_alloc) )
342 ret = -EFAULT;
343 break;
344
345 default:
346 ret = -EOPNOTSUPP;
347 break;
348 }
349 break;
350 }
351
352 case XEN_SYSCTL_get_cpu_levelling_caps:
353 sysctl->u.cpu_levelling_caps.caps = levelling_caps;
354 if ( __copy_field_to_guest(u_sysctl, sysctl, u.cpu_levelling_caps.caps) )
355 ret = -EFAULT;
356 break;
357
358 case XEN_SYSCTL_get_cpu_featureset:
359 {
360 static const struct cpuid_policy *const policy_table[4] = {
361 [XEN_SYSCTL_cpu_featureset_raw] = &raw_cpuid_policy,
362 [XEN_SYSCTL_cpu_featureset_host] = &host_cpuid_policy,
363 #ifdef CONFIG_PV
364 [XEN_SYSCTL_cpu_featureset_pv] = &pv_def_cpuid_policy,
365 #endif
366 #ifdef CONFIG_HVM
367 [XEN_SYSCTL_cpu_featureset_hvm] = &hvm_def_cpuid_policy,
368 #endif
369 };
370 const struct cpuid_policy *p = NULL;
371 uint32_t featureset[FSCAPINTS];
372 unsigned int nr;
373
374 /* Request for maximum number of features? */
375 if ( guest_handle_is_null(sysctl->u.cpu_featureset.features) )
376 {
377 sysctl->u.cpu_featureset.nr_features = FSCAPINTS;
378 if ( __copy_field_to_guest(u_sysctl, sysctl,
379 u.cpu_featureset.nr_features) )
380 ret = -EFAULT;
381 break;
382 }
383
384 /* Clip the number of entries. */
385 nr = min_t(unsigned int, sysctl->u.cpu_featureset.nr_features,
386 FSCAPINTS);
387
388 /* Look up requested featureset. */
389 if ( sysctl->u.cpu_featureset.index < ARRAY_SIZE(policy_table) )
390 {
391 p = policy_table[sysctl->u.cpu_featureset.index];
392
393 if ( !p )
394 ret = -EOPNOTSUPP;
395 }
396 else
397 /* Bad featureset index? */
398 ret = -EINVAL;
399
400 if ( !ret )
401 cpuid_policy_to_featureset(p, featureset);
402
403 /* Copy the requested featureset into place. */
404 if ( !ret && copy_to_guest(sysctl->u.cpu_featureset.features,
405 featureset, nr) )
406 ret = -EFAULT;
407
408 /* Inform the caller of how many features we wrote. */
409 sysctl->u.cpu_featureset.nr_features = nr;
410 if ( !ret && __copy_field_to_guest(u_sysctl, sysctl,
411 u.cpu_featureset.nr_features) )
412 ret = -EFAULT;
413
414 /* Inform the caller if there was more data to provide. */
415 if ( !ret && nr < FSCAPINTS )
416 ret = -ENOBUFS;
417
418 break;
419 }
420
421 case XEN_SYSCTL_get_cpu_policy:
422 {
423 const struct cpu_policy *policy;
424
425 /* Reserved field set, or bad policy index? */
426 if ( sysctl->u.cpu_policy._rsvd ||
427 sysctl->u.cpu_policy.index >= ARRAY_SIZE(system_policies) )
428 {
429 ret = -EINVAL;
430 break;
431 }
432 policy = &system_policies[
433 array_index_nospec(sysctl->u.cpu_policy.index,
434 ARRAY_SIZE(system_policies))];
435
436 if ( !policy->cpuid || !policy->msr )
437 {
438 ret = -EOPNOTSUPP;
439 break;
440 }
441
442 /* Process the CPUID leaves. */
443 if ( guest_handle_is_null(sysctl->u.cpu_policy.cpuid_policy) )
444 sysctl->u.cpu_policy.nr_leaves = CPUID_MAX_SERIALISED_LEAVES;
445 else if ( (ret = x86_cpuid_copy_to_buffer(
446 policy->cpuid,
447 sysctl->u.cpu_policy.cpuid_policy,
448 &sysctl->u.cpu_policy.nr_leaves)) )
449 break;
450
451 if ( __copy_field_to_guest(u_sysctl, sysctl,
452 u.cpu_policy.nr_leaves) )
453 {
454 ret = -EFAULT;
455 break;
456 }
457
458 /* Process the MSR entries. */
459 if ( guest_handle_is_null(sysctl->u.cpu_policy.msr_policy) )
460 sysctl->u.cpu_policy.nr_msrs = MSR_MAX_SERIALISED_ENTRIES;
461 else if ( (ret = x86_msr_copy_to_buffer(
462 policy->msr,
463 sysctl->u.cpu_policy.msr_policy,
464 &sysctl->u.cpu_policy.nr_msrs)) )
465 break;
466
467 if ( __copy_field_to_guest(u_sysctl, sysctl,
468 u.cpu_policy.nr_msrs) )
469 ret = -EFAULT;
470
471 break;
472 }
473
474 default:
475 ret = -ENOSYS;
476 break;
477 }
478
479 return ret;
480 }
481
482 /*
483 * Local variables:
484 * mode: C
485 * c-file-style: "BSD"
486 * c-basic-offset: 4
487 * tab-width: 4
488 * indent-tabs-mode: nil
489 * End:
490 */
491