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