1 /******************************************************************************
2  * kernel.c
3  *
4  * Copyright (c) 2002-2005 K A Fraser
5  */
6 
7 #include <xen/init.h>
8 #include <xen/lib.h>
9 #include <xen/errno.h>
10 #include <xen/param.h>
11 #include <xen/version.h>
12 #include <xen/sched.h>
13 #include <xen/paging.h>
14 #include <xen/guest_access.h>
15 #include <xen/hypercall.h>
16 #include <xen/hypfs.h>
17 #include <xsm/xsm.h>
18 #include <asm/current.h>
19 #include <public/version.h>
20 
21 #ifdef CONFIG_COMPAT
22 #include <compat/version.h>
23 
24 CHECK_build_id;
25 CHECK_compile_info;
26 CHECK_feature_info;
27 CHECK_varbuf;
28 #endif
29 
30 enum system_state system_state = SYS_STATE_early_boot;
31 
32 #ifdef CONFIG_HAS_DIT
33 bool __ro_after_init opt_dit = IS_ENABLED(CONFIG_DIT_DEFAULT);
34 boolean_param("dit", opt_dit);
35 #endif
36 
37 static xen_commandline_t saved_cmdline;
38 static const char __initconst opt_builtin_cmdline[] = CONFIG_CMDLINE;
39 char __ro_after_init xen_cap_info[128];
40 
assign_integer_param(const struct kernel_param * param,uint64_t val)41 static int assign_integer_param(const struct kernel_param *param, uint64_t val)
42 {
43     switch ( param->len )
44     {
45     case sizeof(uint8_t):
46         if ( val > UINT8_MAX && val < (uint64_t)INT8_MIN )
47             return -EOVERFLOW;
48         *(uint8_t *)param->par.var = val;
49         break;
50     case sizeof(uint16_t):
51         if ( val > UINT16_MAX && val < (uint64_t)INT16_MIN )
52             return -EOVERFLOW;
53         *(uint16_t *)param->par.var = val;
54         break;
55     case sizeof(uint32_t):
56         if ( val > UINT32_MAX && val < (uint64_t)INT32_MIN )
57             return -EOVERFLOW;
58         *(uint32_t *)param->par.var = val;
59         break;
60     case sizeof(uint64_t):
61         *(uint64_t *)param->par.var = val;
62         break;
63     default:
64         BUG();
65     }
66 
67     return 0;
68 }
69 
parse_params(const char * cmdline,const struct kernel_param * start,const struct kernel_param * end)70 static int __init parse_params(
71     const char *cmdline, const struct kernel_param *start,
72     const struct kernel_param *end)
73 {
74     char opt[MAX_PARAM_SIZE], *optval, *optkey, *q;
75     const char *p = cmdline, *key;
76     const struct kernel_param *param;
77     int rc, final_rc = 0;
78     bool bool_assert, found;
79 
80     for ( ; ; )
81     {
82         /* Skip whitespace. */
83         while ( *p == ' ' )
84             p++;
85         if ( *p == '\0' )
86             break;
87 
88         /* Grab the next whitespace-delimited option. */
89         q = optkey = opt;
90         while ( (*p != ' ') && (*p != '\0') )
91         {
92             if ( (q-opt) < (sizeof(opt)-1) ) /* avoid overflow */
93                 *q++ = *p;
94             p++;
95         }
96         *q = '\0';
97 
98         /* Search for value part of a key=value option. */
99         optval = strchr(opt, '=');
100         if ( optval != NULL )
101         {
102             *optval++ = '\0'; /* nul-terminate the option value */
103             q = strpbrk(opt, "([{<");
104         }
105         else
106         {
107             optval = q;       /* default option value is empty string */
108             q = NULL;
109         }
110 
111         /* Boolean parameters can be inverted with 'no-' prefix. */
112         key = optkey;
113         bool_assert = !!strncmp("no-", optkey, 3);
114         if ( !bool_assert )
115             optkey += 3;
116 
117         rc = 0;
118         found = false;
119         for ( param = start; param < end; param++ )
120         {
121             int rctmp;
122             const char *s;
123 
124             if ( strcmp(param->name, optkey) )
125             {
126                 if ( param->type == OPT_CUSTOM && q &&
127                      strlen(param->name) == q + 1 - opt &&
128                      !strncmp(param->name, opt, q + 1 - opt) )
129                 {
130                     found = true;
131                     optval[-1] = '=';
132                     rctmp = param->par.func(q);
133                     optval[-1] = '\0';
134                     if ( !rc )
135                         rc = rctmp;
136                 }
137                 continue;
138             }
139 
140             rctmp = 0;
141             found = true;
142             switch ( param->type )
143             {
144             case OPT_STR:
145                 strlcpy(param->par.var, optval, param->len);
146                 break;
147             case OPT_UINT:
148                 rctmp = assign_integer_param(
149                     param,
150                     simple_strtoll(optval, &s, 0));
151                 if ( *s )
152                     rctmp = -EINVAL;
153                 break;
154             case OPT_BOOL:
155                 rctmp = *optval ? parse_bool(optval, NULL) : 1;
156                 if ( rctmp < 0 )
157                     break;
158                 if ( !rctmp )
159                     bool_assert = !bool_assert;
160                 rctmp = 0;
161                 assign_integer_param(param, bool_assert);
162                 break;
163             case OPT_SIZE:
164                 rctmp = assign_integer_param(
165                     param,
166                     parse_size_and_unit(optval, &s));
167                 if ( *s )
168                     rctmp = -EINVAL;
169                 break;
170             case OPT_CUSTOM:
171                 rctmp = -EINVAL;
172                 if ( !bool_assert )
173                 {
174                     if ( *optval )
175                         break;
176                     safe_strcpy(opt, "no");
177                     optval = opt;
178                 }
179                 rctmp = param->par.func(optval);
180                 break;
181             case OPT_IGNORE:
182                 break;
183             default:
184                 BUG();
185                 break;
186             }
187 
188             if ( !rc )
189                 rc = rctmp;
190         }
191 
192         if ( rc )
193         {
194             printk("parameter \"%s\" has invalid value \"%s\", rc=%d!\n",
195                     key, optval, rc);
196             final_rc = rc;
197         }
198         if ( !found )
199         {
200             printk("parameter \"%s\" unknown!\n", key);
201             final_rc = -EINVAL;
202         }
203     }
204 
205     return final_rc;
206 }
207 
_cmdline_parse(const char * cmdline)208 static void __init _cmdline_parse(const char *cmdline)
209 {
210     parse_params(cmdline, __setup_start, __setup_end);
211 }
212 
213 /**
214  *    cmdline_parse -- parses the xen command line.
215  * If CONFIG_CMDLINE is set, it would be parsed prior to @cmdline.
216  * But if CONFIG_CMDLINE_OVERRIDE is set to y, @cmdline will be ignored.
217  */
cmdline_parse(const char * cmdline)218 void __init cmdline_parse(const char *cmdline)
219 {
220     if ( opt_builtin_cmdline[0] )
221     {
222         printk("Built-in command line: %s\n", opt_builtin_cmdline);
223         _cmdline_parse(opt_builtin_cmdline);
224     }
225 
226 #ifndef CONFIG_CMDLINE_OVERRIDE
227     if ( cmdline == NULL )
228         return;
229 
230     safe_strcpy(saved_cmdline, cmdline);
231     _cmdline_parse(cmdline);
232 #endif
233 }
234 
parse_bool(const char * s,const char * e)235 int parse_bool(const char *s, const char *e)
236 {
237     size_t len = e ? ({ ASSERT(e >= s); e - s; }) : strlen(s);
238 
239     switch ( len )
240     {
241     case 1:
242         if ( *s == '1' )
243             return 1;
244         if ( *s == '0' )
245             return 0;
246         break;
247 
248     case 2:
249         if ( !strncmp("on", s, 2) )
250             return 1;
251         if ( !strncmp("no", s, 2) )
252             return 0;
253         break;
254 
255     case 3:
256         if ( !strncmp("yes", s, 3) )
257             return 1;
258         if ( !strncmp("off", s, 3) )
259             return 0;
260         break;
261 
262     case 4:
263         if ( !strncmp("true", s, 4) )
264             return 1;
265         break;
266 
267     case 5:
268         if ( !strncmp("false", s, 5) )
269             return 0;
270         break;
271 
272     case 6:
273         if ( !strncmp("enable", s, 6) )
274             return 1;
275         break;
276 
277     case 7:
278         if ( !strncmp("disable", s, 7) )
279             return 0;
280         break;
281     }
282 
283     return -1;
284 }
285 
parse_boolean(const char * name,const char * s,const char * e)286 int parse_boolean(const char *name, const char *s, const char *e)
287 {
288     size_t slen, nlen;
289     bool has_neg_prefix = !strncmp(s, "no-", 3);
290 
291     if ( has_neg_prefix )
292         s += 3;
293 
294     slen = e ? ({ ASSERT(e >= s); e - s; }) : strlen(s);
295     nlen = strlen(name);
296 
297     /* Does s now start with name? */
298     if ( slen < nlen || strncmp(s, name, nlen) )
299         return -1;
300 
301     /* Exact, unadorned name?  Result depends on the 'no-' prefix. */
302     if ( slen == nlen )
303         return !has_neg_prefix;
304 
305     /* Inexact match with a 'no-' prefix?  Not valid. */
306     if ( has_neg_prefix )
307         return -1;
308 
309     /* =$SOMETHING?  Defer to the regular boolean parsing. */
310     if ( s[nlen] == '=' )
311     {
312         int b = parse_bool(&s[nlen + 1], e);
313 
314         if ( b >= 0 )
315             return b;
316 
317         /* Not a boolean, but the name matched.  Signal specially. */
318         return -2;
319     }
320 
321     /* Unrecognised.  Give up. */
322     return -1;
323 }
324 
parse_signed_integer(const char * name,const char * s,const char * e,long long * val)325 int __init parse_signed_integer(const char *name, const char *s, const char *e,
326                                 long long *val)
327 {
328     size_t slen, nlen;
329     const char *str;
330     long long pval;
331 
332     slen = e ? ({ ASSERT(e >= s); e - s; }) : strlen(s);
333     nlen = strlen(name);
334 
335     if ( !e )
336         e = s + slen;
337 
338     /* Check that this is the name we're looking for and a value was provided */
339     if ( slen <= nlen || strncmp(s, name, nlen) || s[nlen] != '=' )
340         return -1;
341 
342     pval = simple_strtoll(&s[nlen + 1], &str, 10);
343 
344     /* Number not recognised */
345     if ( str != e )
346         return -2;
347 
348     *val = pval;
349 
350     return 0;
351 }
352 
cmdline_strcmp(const char * frag,const char * name)353 int cmdline_strcmp(const char *frag, const char *name)
354 {
355     for ( ; ; frag++, name++ )
356     {
357         unsigned char f = *frag, n = *name;
358         int res = f - n;
359 
360         if ( res || n == '\0' )
361         {
362             /*
363              * NUL in 'name' matching a comma, colon, semicolon or equals in
364              * 'frag' implies success.
365              */
366             if ( n == '\0' && (f == ',' || f == ':' || f == ';' || f == '=') )
367                 res = 0;
368 
369             return res;
370         }
371     }
372 }
373 
374 unsigned int tainted;
375 
376 /**
377  *      print_tainted - return a string to represent the kernel taint state.
378  *
379  *  'C' - Console output is synchronous.
380  *  'E' - An error (e.g. a machine check exceptions) has been injected.
381  *  'H' - HVM forced emulation prefix is permitted.
382  *  'I' - Platform is insecure (usually due to an errata on the platform).
383  *  'M' - Machine had a machine check experience.
384  *  'S' - Out of spec CPU (Incompatible features on one or more cores).
385  *
386  *      The string is overwritten by the next call to print_taint().
387  */
print_tainted(char * str)388 char *print_tainted(char *str)
389 {
390     if ( tainted )
391     {
392         snprintf(str, TAINT_STRING_MAX_LEN, "Tainted: %c%c%c%c%c%c",
393                  tainted & TAINT_MACHINE_INSECURE ? 'I' : ' ',
394                  tainted & TAINT_MACHINE_CHECK ? 'M' : ' ',
395                  tainted & TAINT_SYNC_CONSOLE ? 'C' : ' ',
396                  tainted & TAINT_ERROR_INJECT ? 'E' : ' ',
397                  tainted & TAINT_HVM_FEP ? 'H' : ' ',
398                  tainted & TAINT_CPU_OUT_OF_SPEC ? 'S' : ' ');
399     }
400     else
401     {
402         snprintf(str, TAINT_STRING_MAX_LEN, "Not tainted");
403     }
404 
405     return str;
406 }
407 
add_taint(unsigned int taint)408 void add_taint(unsigned int taint)
409 {
410     tainted |= taint;
411 }
412 
413 extern const initcall_t __initcall_start[], __presmp_initcall_end[],
414     __initcall_end[];
415 
do_presmp_initcalls(void)416 void __init do_presmp_initcalls(void)
417 {
418     const initcall_t *call;
419     for ( call = __initcall_start; call < __presmp_initcall_end; call++ )
420         (*call)();
421 }
422 
do_initcalls(void)423 void __init do_initcalls(void)
424 {
425     const initcall_t *call;
426     for ( call = __presmp_initcall_end; call < __initcall_end; call++ )
427         (*call)();
428 }
429 
430 #ifdef CONFIG_HYPFS
431 static unsigned int __read_mostly major_version;
432 static unsigned int __read_mostly minor_version;
433 
434 static HYPFS_DIR_INIT(buildinfo, "buildinfo");
435 static HYPFS_DIR_INIT(compileinfo, "compileinfo");
436 static HYPFS_DIR_INIT(version, "version");
437 static HYPFS_UINT_INIT(major, "major", major_version);
438 static HYPFS_UINT_INIT(minor, "minor", minor_version);
439 static HYPFS_STRING_INIT(changeset, "changeset");
440 static HYPFS_STRING_INIT(compiler, "compiler");
441 static HYPFS_STRING_INIT(compile_by, "compile_by");
442 static HYPFS_STRING_INIT(compile_date, "compile_date");
443 static HYPFS_STRING_INIT(compile_domain, "compile_domain");
444 static HYPFS_STRING_INIT(extra, "extra");
445 
446 #ifdef CONFIG_HYPFS_CONFIG
447 static HYPFS_STRING_INIT(config, "config");
448 #endif
449 
buildinfo_init(void)450 static int __init cf_check buildinfo_init(void)
451 {
452     hypfs_add_dir(&hypfs_root, &buildinfo, true);
453 
454     hypfs_string_set_reference(&changeset, xen_changeset());
455     hypfs_add_leaf(&buildinfo, &changeset, true);
456 
457     hypfs_add_dir(&buildinfo, &compileinfo, true);
458     hypfs_string_set_reference(&compiler, xen_compiler());
459     hypfs_string_set_reference(&compile_by, xen_compile_by());
460     hypfs_string_set_reference(&compile_date, xen_compile_date());
461     hypfs_string_set_reference(&compile_domain, xen_compile_domain());
462     hypfs_add_leaf(&compileinfo, &compiler, true);
463     hypfs_add_leaf(&compileinfo, &compile_by, true);
464     hypfs_add_leaf(&compileinfo, &compile_date, true);
465     hypfs_add_leaf(&compileinfo, &compile_domain, true);
466 
467     major_version = xen_major_version();
468     minor_version = xen_minor_version();
469     hypfs_add_dir(&buildinfo, &version, true);
470     hypfs_string_set_reference(&extra, xen_extra_version());
471     hypfs_add_leaf(&version, &extra, true);
472     hypfs_add_leaf(&version, &major, true);
473     hypfs_add_leaf(&version, &minor, true);
474 
475 #ifdef CONFIG_HYPFS_CONFIG
476     config.e.encoding = XEN_HYPFS_ENC_GZIP;
477     config.e.size = xen_config_data_size;
478     config.u.content = xen_config_data;
479     hypfs_add_leaf(&buildinfo, &config, true);
480 #endif
481 
482     return 0;
483 }
484 __initcall(buildinfo_init);
485 
486 static HYPFS_DIR_INIT(params, "params");
487 
param_init(void)488 static int __init cf_check param_init(void)
489 {
490     struct param_hypfs *param;
491 
492     hypfs_add_dir(&hypfs_root, &params, true);
493 
494     for ( param = __paramhypfs_start; param < __paramhypfs_end; param++ )
495     {
496         if ( param->init_leaf )
497             param->init_leaf(param);
498         else if ( param->hypfs.e.type == XEN_HYPFS_TYPE_STRING )
499             param->hypfs.e.size = strlen(param->hypfs.u.content) + 1;
500         hypfs_add_leaf(&params, &param->hypfs, true);
501     }
502 
503     return 0;
504 }
505 __initcall(param_init);
506 #endif
507 
xenver_varbuf_op(int cmd,XEN_GUEST_HANDLE_PARAM (void)arg)508 static long xenver_varbuf_op(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
509 {
510     struct xen_varbuf user_str;
511     const char *str = NULL;
512     size_t sz;
513 
514     switch ( cmd )
515     {
516     case XENVER_build_id:
517         str = xen_build_id;
518         sz  = xen_build_id_len;
519         if ( !sz )
520             return -ENODATA;
521         goto have_len;
522 
523     case XENVER_extraversion2:
524         str = xen_extra_version();
525         break;
526 
527     case XENVER_changeset2:
528         str = xen_changeset();
529         break;
530 
531     case XENVER_commandline2:
532         str = saved_cmdline;
533         break;
534 
535     case XENVER_capabilities2:
536         str = xen_cap_info;
537         break;
538 
539     default:
540         ASSERT_UNREACHABLE();
541         return -ENODATA;
542     }
543 
544     sz = strlen(str);
545 
546  have_len:
547     if ( sz > KB(64) ) /* Arbitrary limit.  Avoid long-running operations. */
548         return -E2BIG;
549 
550     if ( guest_handle_is_null(arg) ) /* Length request */
551         return sz;
552 
553     if ( copy_from_guest(&user_str, arg, 1) )
554         return -EFAULT;
555 
556     if ( sz > user_str.len )
557         return -ENOBUFS;
558 
559     if ( copy_to_guest_offset(arg, offsetof(struct xen_varbuf, buf),
560                               str, sz) )
561         return -EFAULT;
562 
563     return sz;
564 }
565 
do_xen_version(int cmd,XEN_GUEST_HANDLE_PARAM (void)arg)566 long do_xen_version(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
567 {
568     bool deny = xsm_xen_version(XSM_OTHER, cmd);
569 
570     switch ( cmd )
571     {
572     case XENVER_version:
573         return (xen_major_version() << 16) | xen_minor_version();
574 
575     case XENVER_extraversion:
576     {
577         xen_extraversion_t extraversion;
578 
579         memset(extraversion, 0, sizeof(extraversion));
580         safe_strcpy(extraversion, deny ? xen_deny() : xen_extra_version());
581         if ( copy_to_guest(arg, extraversion, ARRAY_SIZE(extraversion)) )
582             return -EFAULT;
583         return 0;
584     }
585 
586     case XENVER_compile_info:
587     {
588         xen_compile_info_t info;
589 
590         memset(&info, 0, sizeof(info));
591         safe_strcpy(info.compiler,       deny ? xen_deny() : xen_compiler());
592         safe_strcpy(info.compile_by,     deny ? xen_deny() : xen_compile_by());
593         safe_strcpy(info.compile_domain, deny ? xen_deny() : xen_compile_domain());
594         safe_strcpy(info.compile_date,   deny ? xen_deny() : xen_compile_date());
595         if ( copy_to_guest(arg, &info, 1) )
596             return -EFAULT;
597         return 0;
598     }
599 
600     case XENVER_capabilities:
601     {
602         xen_capabilities_info_t info;
603 
604         memset(info, 0, sizeof(info));
605         if ( !deny )
606             safe_strcpy(info, xen_cap_info);
607 
608         if ( copy_to_guest(arg, info, ARRAY_SIZE(info)) )
609             return -EFAULT;
610         return 0;
611     }
612 
613     case XENVER_platform_parameters:
614     {
615         const struct vcpu *curr = current;
616 
617 #ifdef CONFIG_COMPAT
618         if ( curr->hcall_compat )
619         {
620             compat_platform_parameters_t params = {
621                 .virt_start = is_pv_vcpu(curr)
622                             ? HYPERVISOR_COMPAT_VIRT_START(curr->domain)
623                             : 0,
624             };
625 
626             if ( copy_to_guest(arg, &params, 1) )
627                 return -EFAULT;
628         }
629         else
630 #endif
631         {
632             xen_platform_parameters_t params = {
633                 /*
634                  * Out of an abundance of caution, retain the useless return
635                  * value for 64bit PV guests, but in release builds only.
636                  *
637                  * This is not expected to cause any problems, but if it does,
638                  * the developer impacted will be the one best suited to fix
639                  * the caller not to issue this hypercall.
640                  */
641                 .virt_start = !IS_ENABLED(CONFIG_DEBUG) && is_pv_vcpu(curr)
642                               ? HYPERVISOR_VIRT_START
643                               : 0,
644             };
645 
646             if ( copy_to_guest(arg, &params, 1) )
647                 return -EFAULT;
648         }
649 
650         return 0;
651     }
652 
653     case XENVER_changeset:
654     {
655         xen_changeset_info_t chgset;
656 
657         memset(chgset, 0, sizeof(chgset));
658         safe_strcpy(chgset, deny ? xen_deny() : xen_changeset());
659         if ( copy_to_guest(arg, chgset, ARRAY_SIZE(chgset)) )
660             return -EFAULT;
661         return 0;
662     }
663 
664     case XENVER_get_features:
665     {
666         xen_feature_info_t fi;
667         struct domain *d = current->domain;
668 
669         if ( copy_from_guest(&fi, arg, 1) )
670             return -EFAULT;
671 
672         switch ( fi.submap_idx )
673         {
674         case 0:
675             fi.submap = (1U << XENFEAT_memory_op_vnode_supported) |
676 #ifdef CONFIG_X86
677                         (1U << XENFEAT_vcpu_time_phys_area) |
678 #endif
679                         (1U << XENFEAT_runstate_phys_area);
680             if ( VM_ASSIST(d, pae_extended_cr3) )
681                 fi.submap |= (1U << XENFEAT_pae_pgdir_above_4gb);
682             if ( paging_mode_translate(d) )
683                 fi.submap |=
684                     (1U << XENFEAT_writable_page_tables) |
685                     (1U << XENFEAT_auto_translated_physmap);
686             if ( is_hardware_domain(d) )
687                 fi.submap |= 1U << XENFEAT_dom0;
688 #ifdef CONFIG_ARM
689             fi.submap |= (1U << XENFEAT_ARM_SMCCC_supported);
690 #endif
691 #ifdef CONFIG_X86
692             if ( is_pv_domain(d) )
693                 fi.submap |= (1U << XENFEAT_mmu_pt_update_preserve_ad) |
694                              (1U << XENFEAT_highmem_assist) |
695                              (1U << XENFEAT_gnttab_map_avail_bits);
696             else
697                 fi.submap |= (1U << XENFEAT_hvm_safe_pvclock) |
698                              (1U << XENFEAT_hvm_callback_vector) |
699                              (has_pirq(d) ? (1U << XENFEAT_hvm_pirqs) : 0);
700             fi.submap |= (1U << XENFEAT_dm_msix_all_writes);
701 #endif
702             if ( !paging_mode_translate(d) || is_domain_direct_mapped(d) )
703                 fi.submap |= (1U << XENFEAT_direct_mapped);
704             else
705                 fi.submap |= (1U << XENFEAT_not_direct_mapped);
706             break;
707         default:
708             return -EINVAL;
709         }
710 
711         if ( __copy_to_guest(arg, &fi, 1) )
712             return -EFAULT;
713         return 0;
714     }
715 
716     case XENVER_pagesize:
717         if ( deny )
718             return 0;
719         return (!guest_handle_is_null(arg) ? -EINVAL : PAGE_SIZE);
720 
721     case XENVER_guest_handle:
722     {
723         xen_domain_handle_t hdl;
724 
725         if ( deny )
726             memset(&hdl, 0, ARRAY_SIZE(hdl));
727 
728         BUILD_BUG_ON(ARRAY_SIZE(current->domain->handle) != ARRAY_SIZE(hdl));
729 
730         if ( copy_to_guest(arg, deny ? hdl : current->domain->handle,
731                            ARRAY_SIZE(hdl) ) )
732             return -EFAULT;
733         return 0;
734     }
735 
736     case XENVER_commandline:
737     {
738         size_t len = ARRAY_SIZE(saved_cmdline);
739 
740         if ( deny )
741             len = strlen(xen_deny()) + 1;
742 
743         if ( copy_to_guest(arg, deny ? xen_deny() : saved_cmdline, len) )
744             return -EFAULT;
745         return 0;
746     }
747 
748     case XENVER_build_id:
749     case XENVER_extraversion2:
750     case XENVER_capabilities2:
751     case XENVER_changeset2:
752     case XENVER_commandline2:
753         if ( deny )
754             return -EPERM;
755         return xenver_varbuf_op(cmd, arg);
756     }
757 
758     return -ENOSYS;
759 }
760 
761 /*
762  * Local variables:
763  * mode: C
764  * c-file-style: "BSD"
765  * c-basic-offset: 4
766  * tab-width: 4
767  * indent-tabs-mode: nil
768  * End:
769  */
770