Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / tools / bpf / bpftool / feature.c
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (c) 2019 Netronome Systems, Inc. */
3
4 #include <ctype.h>
5 #include <errno.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <net/if.h>
9 #include <sys/utsname.h>
10 #include <sys/vfs.h>
11
12 #include <linux/filter.h>
13 #include <linux/limits.h>
14
15 #include <bpf.h>
16 #include <libbpf.h>
17
18 #include "main.h"
19
20 #ifndef PROC_SUPER_MAGIC
21 # define PROC_SUPER_MAGIC       0x9fa0
22 #endif
23
24 enum probe_component {
25         COMPONENT_UNSPEC,
26         COMPONENT_KERNEL,
27         COMPONENT_DEVICE,
28 };
29
30 #define BPF_HELPER_MAKE_ENTRY(name)     [BPF_FUNC_ ## name] = "bpf_" # name
31 static const char * const helper_name[] = {
32         __BPF_FUNC_MAPPER(BPF_HELPER_MAKE_ENTRY)
33 };
34
35 #undef BPF_HELPER_MAKE_ENTRY
36
37 /* Miscellaneous utility functions */
38
39 static bool check_procfs(void)
40 {
41         struct statfs st_fs;
42
43         if (statfs("/proc", &st_fs) < 0)
44                 return false;
45         if ((unsigned long)st_fs.f_type != PROC_SUPER_MAGIC)
46                 return false;
47
48         return true;
49 }
50
51 static void uppercase(char *str, size_t len)
52 {
53         size_t i;
54
55         for (i = 0; i < len && str[i] != '\0'; i++)
56                 str[i] = toupper(str[i]);
57 }
58
59 /* Printing utility functions */
60
61 static void
62 print_bool_feature(const char *feat_name, const char *plain_name,
63                    const char *define_name, bool res, const char *define_prefix)
64 {
65         if (json_output)
66                 jsonw_bool_field(json_wtr, feat_name, res);
67         else if (define_prefix)
68                 printf("#define %s%sHAVE_%s\n", define_prefix,
69                        res ? "" : "NO_", define_name);
70         else
71                 printf("%s is %savailable\n", plain_name, res ? "" : "NOT ");
72 }
73
74 static void print_kernel_option(const char *name, const char *value)
75 {
76         char *endptr;
77         int res;
78
79         /* No support for C-style ouptut */
80
81         if (json_output) {
82                 if (!value) {
83                         jsonw_null_field(json_wtr, name);
84                         return;
85                 }
86                 errno = 0;
87                 res = strtol(value, &endptr, 0);
88                 if (!errno && *endptr == '\n')
89                         jsonw_int_field(json_wtr, name, res);
90                 else
91                         jsonw_string_field(json_wtr, name, value);
92         } else {
93                 if (value)
94                         printf("%s is set to %s\n", name, value);
95                 else
96                         printf("%s is not set\n", name);
97         }
98 }
99
100 static void
101 print_start_section(const char *json_title, const char *plain_title,
102                     const char *define_comment, const char *define_prefix)
103 {
104         if (json_output) {
105                 jsonw_name(json_wtr, json_title);
106                 jsonw_start_object(json_wtr);
107         } else if (define_prefix) {
108                 printf("%s\n", define_comment);
109         } else {
110                 printf("%s\n", plain_title);
111         }
112 }
113
114 static void
115 print_end_then_start_section(const char *json_title, const char *plain_title,
116                              const char *define_comment,
117                              const char *define_prefix)
118 {
119         if (json_output)
120                 jsonw_end_object(json_wtr);
121         else
122                 printf("\n");
123
124         print_start_section(json_title, plain_title, define_comment,
125                             define_prefix);
126 }
127
128 /* Probing functions */
129
130 static int read_procfs(const char *path)
131 {
132         char *endptr, *line = NULL;
133         size_t len = 0;
134         FILE *fd;
135         int res;
136
137         fd = fopen(path, "r");
138         if (!fd)
139                 return -1;
140
141         res = getline(&line, &len, fd);
142         fclose(fd);
143         if (res < 0)
144                 return -1;
145
146         errno = 0;
147         res = strtol(line, &endptr, 10);
148         if (errno || *line == '\0' || *endptr != '\n')
149                 res = -1;
150         free(line);
151
152         return res;
153 }
154
155 static void probe_unprivileged_disabled(void)
156 {
157         int res;
158
159         /* No support for C-style ouptut */
160
161         res = read_procfs("/proc/sys/kernel/unprivileged_bpf_disabled");
162         if (json_output) {
163                 jsonw_int_field(json_wtr, "unprivileged_bpf_disabled", res);
164         } else {
165                 switch (res) {
166                 case 0:
167                         printf("bpf() syscall for unprivileged users is enabled\n");
168                         break;
169                 case 1:
170                         printf("bpf() syscall restricted to privileged users\n");
171                         break;
172                 case -1:
173                         printf("Unable to retrieve required privileges for bpf() syscall\n");
174                         break;
175                 default:
176                         printf("bpf() syscall restriction has unknown value %d\n", res);
177                 }
178         }
179 }
180
181 static void probe_jit_enable(void)
182 {
183         int res;
184
185         /* No support for C-style ouptut */
186
187         res = read_procfs("/proc/sys/net/core/bpf_jit_enable");
188         if (json_output) {
189                 jsonw_int_field(json_wtr, "bpf_jit_enable", res);
190         } else {
191                 switch (res) {
192                 case 0:
193                         printf("JIT compiler is disabled\n");
194                         break;
195                 case 1:
196                         printf("JIT compiler is enabled\n");
197                         break;
198                 case 2:
199                         printf("JIT compiler is enabled with debugging traces in kernel logs\n");
200                         break;
201                 case -1:
202                         printf("Unable to retrieve JIT-compiler status\n");
203                         break;
204                 default:
205                         printf("JIT-compiler status has unknown value %d\n",
206                                res);
207                 }
208         }
209 }
210
211 static void probe_jit_harden(void)
212 {
213         int res;
214
215         /* No support for C-style ouptut */
216
217         res = read_procfs("/proc/sys/net/core/bpf_jit_harden");
218         if (json_output) {
219                 jsonw_int_field(json_wtr, "bpf_jit_harden", res);
220         } else {
221                 switch (res) {
222                 case 0:
223                         printf("JIT compiler hardening is disabled\n");
224                         break;
225                 case 1:
226                         printf("JIT compiler hardening is enabled for unprivileged users\n");
227                         break;
228                 case 2:
229                         printf("JIT compiler hardening is enabled for all users\n");
230                         break;
231                 case -1:
232                         printf("Unable to retrieve JIT hardening status\n");
233                         break;
234                 default:
235                         printf("JIT hardening status has unknown value %d\n",
236                                res);
237                 }
238         }
239 }
240
241 static void probe_jit_kallsyms(void)
242 {
243         int res;
244
245         /* No support for C-style ouptut */
246
247         res = read_procfs("/proc/sys/net/core/bpf_jit_kallsyms");
248         if (json_output) {
249                 jsonw_int_field(json_wtr, "bpf_jit_kallsyms", res);
250         } else {
251                 switch (res) {
252                 case 0:
253                         printf("JIT compiler kallsyms exports are disabled\n");
254                         break;
255                 case 1:
256                         printf("JIT compiler kallsyms exports are enabled for root\n");
257                         break;
258                 case -1:
259                         printf("Unable to retrieve JIT kallsyms export status\n");
260                         break;
261                 default:
262                         printf("JIT kallsyms exports status has unknown value %d\n", res);
263                 }
264         }
265 }
266
267 static void probe_jit_limit(void)
268 {
269         int res;
270
271         /* No support for C-style ouptut */
272
273         res = read_procfs("/proc/sys/net/core/bpf_jit_limit");
274         if (json_output) {
275                 jsonw_int_field(json_wtr, "bpf_jit_limit", res);
276         } else {
277                 switch (res) {
278                 case -1:
279                         printf("Unable to retrieve global memory limit for JIT compiler for unprivileged users\n");
280                         break;
281                 default:
282                         printf("Global memory limit for JIT compiler for unprivileged users is %d bytes\n", res);
283                 }
284         }
285 }
286
287 static char *get_kernel_config_option(FILE *fd, const char *option)
288 {
289         size_t line_n = 0, optlen = strlen(option);
290         char *res, *strval, *line = NULL;
291         ssize_t n;
292
293         rewind(fd);
294         while ((n = getline(&line, &line_n, fd)) > 0) {
295                 if (strncmp(line, option, optlen))
296                         continue;
297                 /* Check we have at least '=', value, and '\n' */
298                 if (strlen(line) < optlen + 3)
299                         continue;
300                 if (*(line + optlen) != '=')
301                         continue;
302
303                 /* Trim ending '\n' */
304                 line[strlen(line) - 1] = '\0';
305
306                 /* Copy and return config option value */
307                 strval = line + optlen + 1;
308                 res = strdup(strval);
309                 free(line);
310                 return res;
311         }
312         free(line);
313
314         return NULL;
315 }
316
317 static void probe_kernel_image_config(void)
318 {
319         static const char * const options[] = {
320                 /* Enable BPF */
321                 "CONFIG_BPF",
322                 /* Enable bpf() syscall */
323                 "CONFIG_BPF_SYSCALL",
324                 /* Does selected architecture support eBPF JIT compiler */
325                 "CONFIG_HAVE_EBPF_JIT",
326                 /* Compile eBPF JIT compiler */
327                 "CONFIG_BPF_JIT",
328                 /* Avoid compiling eBPF interpreter (use JIT only) */
329                 "CONFIG_BPF_JIT_ALWAYS_ON",
330
331                 /* cgroups */
332                 "CONFIG_CGROUPS",
333                 /* BPF programs attached to cgroups */
334                 "CONFIG_CGROUP_BPF",
335                 /* bpf_get_cgroup_classid() helper */
336                 "CONFIG_CGROUP_NET_CLASSID",
337                 /* bpf_skb_{,ancestor_}cgroup_id() helpers */
338                 "CONFIG_SOCK_CGROUP_DATA",
339
340                 /* Tracing: attach BPF to kprobes, tracepoints, etc. */
341                 "CONFIG_BPF_EVENTS",
342                 /* Kprobes */
343                 "CONFIG_KPROBE_EVENTS",
344                 /* Uprobes */
345                 "CONFIG_UPROBE_EVENTS",
346                 /* Tracepoints */
347                 "CONFIG_TRACING",
348                 /* Syscall tracepoints */
349                 "CONFIG_FTRACE_SYSCALLS",
350                 /* bpf_override_return() helper support for selected arch */
351                 "CONFIG_FUNCTION_ERROR_INJECTION",
352                 /* bpf_override_return() helper */
353                 "CONFIG_BPF_KPROBE_OVERRIDE",
354
355                 /* Network */
356                 "CONFIG_NET",
357                 /* AF_XDP sockets */
358                 "CONFIG_XDP_SOCKETS",
359                 /* BPF_PROG_TYPE_LWT_* and related helpers */
360                 "CONFIG_LWTUNNEL_BPF",
361                 /* BPF_PROG_TYPE_SCHED_ACT, TC (traffic control) actions */
362                 "CONFIG_NET_ACT_BPF",
363                 /* BPF_PROG_TYPE_SCHED_CLS, TC filters */
364                 "CONFIG_NET_CLS_BPF",
365                 /* TC clsact qdisc */
366                 "CONFIG_NET_CLS_ACT",
367                 /* Ingress filtering with TC */
368                 "CONFIG_NET_SCH_INGRESS",
369                 /* bpf_skb_get_xfrm_state() helper */
370                 "CONFIG_XFRM",
371                 /* bpf_get_route_realm() helper */
372                 "CONFIG_IP_ROUTE_CLASSID",
373                 /* BPF_PROG_TYPE_LWT_SEG6_LOCAL and related helpers */
374                 "CONFIG_IPV6_SEG6_BPF",
375                 /* BPF_PROG_TYPE_LIRC_MODE2 and related helpers */
376                 "CONFIG_BPF_LIRC_MODE2",
377                 /* BPF stream parser and BPF socket maps */
378                 "CONFIG_BPF_STREAM_PARSER",
379                 /* xt_bpf module for passing BPF programs to netfilter  */
380                 "CONFIG_NETFILTER_XT_MATCH_BPF",
381                 /* bpfilter back-end for iptables */
382                 "CONFIG_BPFILTER",
383                 /* bpftilter module with "user mode helper" */
384                 "CONFIG_BPFILTER_UMH",
385
386                 /* test_bpf module for BPF tests */
387                 "CONFIG_TEST_BPF",
388         };
389         char *value, *buf = NULL;
390         struct utsname utsn;
391         char path[PATH_MAX];
392         size_t i, n;
393         ssize_t ret;
394         FILE *fd;
395
396         if (uname(&utsn))
397                 goto no_config;
398
399         snprintf(path, sizeof(path), "/boot/config-%s", utsn.release);
400
401         fd = fopen(path, "r");
402         if (!fd && errno == ENOENT) {
403                 /* Some distributions put the config file at /proc/config, give
404                  * it a try.
405                  * Sometimes it is also at /proc/config.gz but we do not try
406                  * this one for now, it would require linking against libz.
407                  */
408                 fd = fopen("/proc/config", "r");
409         }
410         if (!fd) {
411                 p_info("skipping kernel config, can't open file: %s",
412                        strerror(errno));
413                 goto no_config;
414         }
415         /* Sanity checks */
416         ret = getline(&buf, &n, fd);
417         ret = getline(&buf, &n, fd);
418         if (!buf || !ret) {
419                 p_info("skipping kernel config, can't read from file: %s",
420                        strerror(errno));
421                 free(buf);
422                 goto no_config;
423         }
424         if (strcmp(buf, "# Automatically generated file; DO NOT EDIT.\n")) {
425                 p_info("skipping kernel config, can't find correct file");
426                 free(buf);
427                 goto no_config;
428         }
429         free(buf);
430
431         for (i = 0; i < ARRAY_SIZE(options); i++) {
432                 value = get_kernel_config_option(fd, options[i]);
433                 print_kernel_option(options[i], value);
434                 free(value);
435         }
436         fclose(fd);
437         return;
438
439 no_config:
440         for (i = 0; i < ARRAY_SIZE(options); i++)
441                 print_kernel_option(options[i], NULL);
442 }
443
444 static bool probe_bpf_syscall(const char *define_prefix)
445 {
446         bool res;
447
448         bpf_load_program(BPF_PROG_TYPE_UNSPEC, NULL, 0, NULL, 0, NULL, 0);
449         res = (errno != ENOSYS);
450
451         print_bool_feature("have_bpf_syscall",
452                            "bpf() syscall",
453                            "BPF_SYSCALL",
454                            res, define_prefix);
455
456         return res;
457 }
458
459 static void
460 probe_prog_type(enum bpf_prog_type prog_type, bool *supported_types,
461                 const char *define_prefix, __u32 ifindex)
462 {
463         char feat_name[128], plain_desc[128], define_name[128];
464         const char *plain_comment = "eBPF program_type ";
465         size_t maxlen;
466         bool res;
467
468         if (ifindex)
469                 /* Only test offload-able program types */
470                 switch (prog_type) {
471                 case BPF_PROG_TYPE_SCHED_CLS:
472                 case BPF_PROG_TYPE_XDP:
473                         break;
474                 default:
475                         return;
476                 }
477
478         res = bpf_probe_prog_type(prog_type, ifindex);
479
480         supported_types[prog_type] |= res;
481
482         maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
483         if (strlen(prog_type_name[prog_type]) > maxlen) {
484                 p_info("program type name too long");
485                 return;
486         }
487
488         sprintf(feat_name, "have_%s_prog_type", prog_type_name[prog_type]);
489         sprintf(define_name, "%s_prog_type", prog_type_name[prog_type]);
490         uppercase(define_name, sizeof(define_name));
491         sprintf(plain_desc, "%s%s", plain_comment, prog_type_name[prog_type]);
492         print_bool_feature(feat_name, plain_desc, define_name, res,
493                            define_prefix);
494 }
495
496 static void
497 probe_map_type(enum bpf_map_type map_type, const char *define_prefix,
498                __u32 ifindex)
499 {
500         char feat_name[128], plain_desc[128], define_name[128];
501         const char *plain_comment = "eBPF map_type ";
502         size_t maxlen;
503         bool res;
504
505         res = bpf_probe_map_type(map_type, ifindex);
506
507         maxlen = sizeof(plain_desc) - strlen(plain_comment) - 1;
508         if (strlen(map_type_name[map_type]) > maxlen) {
509                 p_info("map type name too long");
510                 return;
511         }
512
513         sprintf(feat_name, "have_%s_map_type", map_type_name[map_type]);
514         sprintf(define_name, "%s_map_type", map_type_name[map_type]);
515         uppercase(define_name, sizeof(define_name));
516         sprintf(plain_desc, "%s%s", plain_comment, map_type_name[map_type]);
517         print_bool_feature(feat_name, plain_desc, define_name, res,
518                            define_prefix);
519 }
520
521 static void
522 probe_helpers_for_progtype(enum bpf_prog_type prog_type, bool supported_type,
523                            const char *define_prefix, __u32 ifindex)
524 {
525         const char *ptype_name = prog_type_name[prog_type];
526         char feat_name[128];
527         unsigned int id;
528         bool res;
529
530         if (ifindex)
531                 /* Only test helpers for offload-able program types */
532                 switch (prog_type) {
533                 case BPF_PROG_TYPE_SCHED_CLS:
534                 case BPF_PROG_TYPE_XDP:
535                         break;
536                 default:
537                         return;
538                 }
539
540         if (json_output) {
541                 sprintf(feat_name, "%s_available_helpers", ptype_name);
542                 jsonw_name(json_wtr, feat_name);
543                 jsonw_start_array(json_wtr);
544         } else if (!define_prefix) {
545                 printf("eBPF helpers supported for program type %s:",
546                        ptype_name);
547         }
548
549         for (id = 1; id < ARRAY_SIZE(helper_name); id++) {
550                 if (!supported_type)
551                         res = false;
552                 else
553                         res = bpf_probe_helper(id, prog_type, ifindex);
554
555                 if (json_output) {
556                         if (res)
557                                 jsonw_string(json_wtr, helper_name[id]);
558                 } else if (define_prefix) {
559                         printf("#define %sBPF__PROG_TYPE_%s__HELPER_%s %s\n",
560                                define_prefix, ptype_name, helper_name[id],
561                                res ? "1" : "0");
562                 } else {
563                         if (res)
564                                 printf("\n\t- %s", helper_name[id]);
565                 }
566         }
567
568         if (json_output)
569                 jsonw_end_array(json_wtr);
570         else if (!define_prefix)
571                 printf("\n");
572 }
573
574 static int do_probe(int argc, char **argv)
575 {
576         enum probe_component target = COMPONENT_UNSPEC;
577         const char *define_prefix = NULL;
578         bool supported_types[128] = {};
579         __u32 ifindex = 0;
580         unsigned int i;
581         char *ifname;
582
583         /* Detection assumes user has sufficient privileges (CAP_SYS_ADMIN).
584          * Let's approximate, and restrict usage to root user only.
585          */
586         if (geteuid()) {
587                 p_err("please run this command as root user");
588                 return -1;
589         }
590
591         set_max_rlimit();
592
593         while (argc) {
594                 if (is_prefix(*argv, "kernel")) {
595                         if (target != COMPONENT_UNSPEC) {
596                                 p_err("component to probe already specified");
597                                 return -1;
598                         }
599                         target = COMPONENT_KERNEL;
600                         NEXT_ARG();
601                 } else if (is_prefix(*argv, "dev")) {
602                         NEXT_ARG();
603
604                         if (target != COMPONENT_UNSPEC || ifindex) {
605                                 p_err("component to probe already specified");
606                                 return -1;
607                         }
608                         if (!REQ_ARGS(1))
609                                 return -1;
610
611                         target = COMPONENT_DEVICE;
612                         ifname = GET_ARG();
613                         ifindex = if_nametoindex(ifname);
614                         if (!ifindex) {
615                                 p_err("unrecognized netdevice '%s': %s", ifname,
616                                       strerror(errno));
617                                 return -1;
618                         }
619                 } else if (is_prefix(*argv, "macros") && !define_prefix) {
620                         define_prefix = "";
621                         NEXT_ARG();
622                 } else if (is_prefix(*argv, "prefix")) {
623                         if (!define_prefix) {
624                                 p_err("'prefix' argument can only be use after 'macros'");
625                                 return -1;
626                         }
627                         if (strcmp(define_prefix, "")) {
628                                 p_err("'prefix' already defined");
629                                 return -1;
630                         }
631                         NEXT_ARG();
632
633                         if (!REQ_ARGS(1))
634                                 return -1;
635                         define_prefix = GET_ARG();
636                 } else {
637                         p_err("expected no more arguments, 'kernel', 'dev', 'macros' or 'prefix', got: '%s'?",
638                               *argv);
639                         return -1;
640                 }
641         }
642
643         if (json_output) {
644                 define_prefix = NULL;
645                 jsonw_start_object(json_wtr);
646         }
647
648         switch (target) {
649         case COMPONENT_KERNEL:
650         case COMPONENT_UNSPEC:
651                 if (define_prefix)
652                         break;
653
654                 print_start_section("system_config",
655                                     "Scanning system configuration...",
656                                     NULL, /* define_comment never used here */
657                                     NULL); /* define_prefix always NULL here */
658                 if (check_procfs()) {
659                         probe_unprivileged_disabled();
660                         probe_jit_enable();
661                         probe_jit_harden();
662                         probe_jit_kallsyms();
663                         probe_jit_limit();
664                 } else {
665                         p_info("/* procfs not mounted, skipping related probes */");
666                 }
667                 probe_kernel_image_config();
668                 if (json_output)
669                         jsonw_end_object(json_wtr);
670                 else
671                         printf("\n");
672                 break;
673         default:
674                 break;
675         }
676
677         print_start_section("syscall_config",
678                             "Scanning system call availability...",
679                             "/*** System call availability ***/",
680                             define_prefix);
681
682         if (!probe_bpf_syscall(define_prefix))
683                 /* bpf() syscall unavailable, don't probe other BPF features */
684                 goto exit_close_json;
685
686         print_end_then_start_section("program_types",
687                                      "Scanning eBPF program types...",
688                                      "/*** eBPF program types ***/",
689                                      define_prefix);
690
691         for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
692                 probe_prog_type(i, supported_types, define_prefix, ifindex);
693
694         print_end_then_start_section("map_types",
695                                      "Scanning eBPF map types...",
696                                      "/*** eBPF map types ***/",
697                                      define_prefix);
698
699         for (i = BPF_MAP_TYPE_UNSPEC + 1; i < map_type_name_size; i++)
700                 probe_map_type(i, define_prefix, ifindex);
701
702         print_end_then_start_section("helpers",
703                                      "Scanning eBPF helper functions...",
704                                      "/*** eBPF helper functions ***/",
705                                      define_prefix);
706
707         if (define_prefix)
708                 printf("/*\n"
709                        " * Use %sHAVE_PROG_TYPE_HELPER(prog_type_name, helper_name)\n"
710                        " * to determine if <helper_name> is available for <prog_type_name>,\n"
711                        " * e.g.\n"
712                        " *      #if %sHAVE_PROG_TYPE_HELPER(xdp, bpf_redirect)\n"
713                        " *              // do stuff with this helper\n"
714                        " *      #elif\n"
715                        " *              // use a workaround\n"
716                        " *      #endif\n"
717                        " */\n"
718                        "#define %sHAVE_PROG_TYPE_HELPER(prog_type, helper)      \\\n"
719                        "        %sBPF__PROG_TYPE_ ## prog_type ## __HELPER_ ## helper\n",
720                        define_prefix, define_prefix, define_prefix,
721                        define_prefix);
722         for (i = BPF_PROG_TYPE_UNSPEC + 1; i < ARRAY_SIZE(prog_type_name); i++)
723                 probe_helpers_for_progtype(i, supported_types[i],
724                                            define_prefix, ifindex);
725
726 exit_close_json:
727         if (json_output) {
728                 /* End current "section" of probes */
729                 jsonw_end_object(json_wtr);
730                 /* End root object */
731                 jsonw_end_object(json_wtr);
732         }
733
734         return 0;
735 }
736
737 static int do_help(int argc, char **argv)
738 {
739         if (json_output) {
740                 jsonw_null(json_wtr);
741                 return 0;
742         }
743
744         fprintf(stderr,
745                 "Usage: %s %s probe [COMPONENT] [macros [prefix PREFIX]]\n"
746                 "       %s %s help\n"
747                 "\n"
748                 "       COMPONENT := { kernel | dev NAME }\n"
749                 "",
750                 bin_name, argv[-2], bin_name, argv[-2]);
751
752         return 0;
753 }
754
755 static const struct cmd cmds[] = {
756         { "probe",      do_probe },
757         { "help",       do_help },
758         { 0 }
759 };
760
761 int do_feature(int argc, char **argv)
762 {
763         return cmd_select(cmds, argc, argv, do_help);
764 }