Merge branch 'for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck...
[linux-2.6-microblaze.git] / tools / bpf / bpftool / prog.c
1 /*
2  * Copyright (C) 2017 Netronome Systems, Inc.
3  *
4  * This software is dual licensed under the GNU General License Version 2,
5  * June 1991 as shown in the file COPYING in the top-level directory of this
6  * source tree or the BSD 2-Clause License provided below.  You have the
7  * option to license this software under the complete terms of either license.
8  *
9  * The BSD 2-Clause License:
10  *
11  *     Redistribution and use in source and binary forms, with or
12  *     without modification, are permitted provided that the following
13  *     conditions are met:
14  *
15  *      1. Redistributions of source code must retain the above
16  *         copyright notice, this list of conditions and the following
17  *         disclaimer.
18  *
19  *      2. Redistributions in binary form must reproduce the above
20  *         copyright notice, this list of conditions and the following
21  *         disclaimer in the documentation and/or other materials
22  *         provided with the distribution.
23  *
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
28  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
29  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
30  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31  * SOFTWARE.
32  */
33
34 /* Author: Jakub Kicinski <kubakici@wp.pl> */
35
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46
47 #include <bpf.h>
48 #include <libbpf.h>
49
50 #include "cfg.h"
51 #include "main.h"
52 #include "xlated_dumper.h"
53
54 static const char * const prog_type_name[] = {
55         [BPF_PROG_TYPE_UNSPEC]          = "unspec",
56         [BPF_PROG_TYPE_SOCKET_FILTER]   = "socket_filter",
57         [BPF_PROG_TYPE_KPROBE]          = "kprobe",
58         [BPF_PROG_TYPE_SCHED_CLS]       = "sched_cls",
59         [BPF_PROG_TYPE_SCHED_ACT]       = "sched_act",
60         [BPF_PROG_TYPE_TRACEPOINT]      = "tracepoint",
61         [BPF_PROG_TYPE_XDP]             = "xdp",
62         [BPF_PROG_TYPE_PERF_EVENT]      = "perf_event",
63         [BPF_PROG_TYPE_CGROUP_SKB]      = "cgroup_skb",
64         [BPF_PROG_TYPE_CGROUP_SOCK]     = "cgroup_sock",
65         [BPF_PROG_TYPE_LWT_IN]          = "lwt_in",
66         [BPF_PROG_TYPE_LWT_OUT]         = "lwt_out",
67         [BPF_PROG_TYPE_LWT_XMIT]        = "lwt_xmit",
68         [BPF_PROG_TYPE_SOCK_OPS]        = "sock_ops",
69         [BPF_PROG_TYPE_SK_SKB]          = "sk_skb",
70         [BPF_PROG_TYPE_CGROUP_DEVICE]   = "cgroup_device",
71         [BPF_PROG_TYPE_SK_MSG]          = "sk_msg",
72         [BPF_PROG_TYPE_RAW_TRACEPOINT]  = "raw_tracepoint",
73         [BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr",
74         [BPF_PROG_TYPE_LIRC_MODE2]      = "lirc_mode2",
75 };
76
77 static void print_boot_time(__u64 nsecs, char *buf, unsigned int size)
78 {
79         struct timespec real_time_ts, boot_time_ts;
80         time_t wallclock_secs;
81         struct tm load_tm;
82
83         buf[--size] = '\0';
84
85         if (clock_gettime(CLOCK_REALTIME, &real_time_ts) ||
86             clock_gettime(CLOCK_BOOTTIME, &boot_time_ts)) {
87                 perror("Can't read clocks");
88                 snprintf(buf, size, "%llu", nsecs / 1000000000);
89                 return;
90         }
91
92         wallclock_secs = (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
93                 (real_time_ts.tv_nsec - boot_time_ts.tv_nsec + nsecs) /
94                 1000000000;
95
96
97         if (!localtime_r(&wallclock_secs, &load_tm)) {
98                 snprintf(buf, size, "%llu", nsecs / 1000000000);
99                 return;
100         }
101
102         if (json_output)
103                 strftime(buf, size, "%s", &load_tm);
104         else
105                 strftime(buf, size, "%FT%T%z", &load_tm);
106 }
107
108 static int prog_fd_by_tag(unsigned char *tag)
109 {
110         struct bpf_prog_info info = {};
111         __u32 len = sizeof(info);
112         unsigned int id = 0;
113         int err;
114         int fd;
115
116         while (true) {
117                 err = bpf_prog_get_next_id(id, &id);
118                 if (err) {
119                         p_err("%s", strerror(errno));
120                         return -1;
121                 }
122
123                 fd = bpf_prog_get_fd_by_id(id);
124                 if (fd < 0) {
125                         p_err("can't get prog by id (%u): %s",
126                               id, strerror(errno));
127                         return -1;
128                 }
129
130                 err = bpf_obj_get_info_by_fd(fd, &info, &len);
131                 if (err) {
132                         p_err("can't get prog info (%u): %s",
133                               id, strerror(errno));
134                         close(fd);
135                         return -1;
136                 }
137
138                 if (!memcmp(tag, info.tag, BPF_TAG_SIZE))
139                         return fd;
140
141                 close(fd);
142         }
143 }
144
145 int prog_parse_fd(int *argc, char ***argv)
146 {
147         int fd;
148
149         if (is_prefix(**argv, "id")) {
150                 unsigned int id;
151                 char *endptr;
152
153                 NEXT_ARGP();
154
155                 id = strtoul(**argv, &endptr, 0);
156                 if (*endptr) {
157                         p_err("can't parse %s as ID", **argv);
158                         return -1;
159                 }
160                 NEXT_ARGP();
161
162                 fd = bpf_prog_get_fd_by_id(id);
163                 if (fd < 0)
164                         p_err("get by id (%u): %s", id, strerror(errno));
165                 return fd;
166         } else if (is_prefix(**argv, "tag")) {
167                 unsigned char tag[BPF_TAG_SIZE];
168
169                 NEXT_ARGP();
170
171                 if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
172                            tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
173                     != BPF_TAG_SIZE) {
174                         p_err("can't parse tag");
175                         return -1;
176                 }
177                 NEXT_ARGP();
178
179                 return prog_fd_by_tag(tag);
180         } else if (is_prefix(**argv, "pinned")) {
181                 char *path;
182
183                 NEXT_ARGP();
184
185                 path = **argv;
186                 NEXT_ARGP();
187
188                 return open_obj_pinned_any(path, BPF_OBJ_PROG);
189         }
190
191         p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv);
192         return -1;
193 }
194
195 static void show_prog_maps(int fd, u32 num_maps)
196 {
197         struct bpf_prog_info info = {};
198         __u32 len = sizeof(info);
199         __u32 map_ids[num_maps];
200         unsigned int i;
201         int err;
202
203         info.nr_map_ids = num_maps;
204         info.map_ids = ptr_to_u64(map_ids);
205
206         err = bpf_obj_get_info_by_fd(fd, &info, &len);
207         if (err || !info.nr_map_ids)
208                 return;
209
210         if (json_output) {
211                 jsonw_name(json_wtr, "map_ids");
212                 jsonw_start_array(json_wtr);
213                 for (i = 0; i < info.nr_map_ids; i++)
214                         jsonw_uint(json_wtr, map_ids[i]);
215                 jsonw_end_array(json_wtr);
216         } else {
217                 printf("  map_ids ");
218                 for (i = 0; i < info.nr_map_ids; i++)
219                         printf("%u%s", map_ids[i],
220                                i == info.nr_map_ids - 1 ? "" : ",");
221         }
222 }
223
224 static void print_prog_json(struct bpf_prog_info *info, int fd)
225 {
226         char *memlock;
227
228         jsonw_start_object(json_wtr);
229         jsonw_uint_field(json_wtr, "id", info->id);
230         if (info->type < ARRAY_SIZE(prog_type_name))
231                 jsonw_string_field(json_wtr, "type",
232                                    prog_type_name[info->type]);
233         else
234                 jsonw_uint_field(json_wtr, "type", info->type);
235
236         if (*info->name)
237                 jsonw_string_field(json_wtr, "name", info->name);
238
239         jsonw_name(json_wtr, "tag");
240         jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
241                      info->tag[0], info->tag[1], info->tag[2], info->tag[3],
242                      info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
243
244         jsonw_bool_field(json_wtr, "gpl_compatible", info->gpl_compatible);
245
246         print_dev_json(info->ifindex, info->netns_dev, info->netns_ino);
247
248         if (info->load_time) {
249                 char buf[32];
250
251                 print_boot_time(info->load_time, buf, sizeof(buf));
252
253                 /* Piggy back on load_time, since 0 uid is a valid one */
254                 jsonw_name(json_wtr, "loaded_at");
255                 jsonw_printf(json_wtr, "%s", buf);
256                 jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
257         }
258
259         jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
260
261         if (info->jited_prog_len) {
262                 jsonw_bool_field(json_wtr, "jited", true);
263                 jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
264         } else {
265                 jsonw_bool_field(json_wtr, "jited", false);
266         }
267
268         memlock = get_fdinfo(fd, "memlock");
269         if (memlock)
270                 jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock));
271         free(memlock);
272
273         if (info->nr_map_ids)
274                 show_prog_maps(fd, info->nr_map_ids);
275
276         if (!hash_empty(prog_table.table)) {
277                 struct pinned_obj *obj;
278
279                 jsonw_name(json_wtr, "pinned");
280                 jsonw_start_array(json_wtr);
281                 hash_for_each_possible(prog_table.table, obj, hash, info->id) {
282                         if (obj->id == info->id)
283                                 jsonw_string(json_wtr, obj->path);
284                 }
285                 jsonw_end_array(json_wtr);
286         }
287
288         jsonw_end_object(json_wtr);
289 }
290
291 static void print_prog_plain(struct bpf_prog_info *info, int fd)
292 {
293         char *memlock;
294
295         printf("%u: ", info->id);
296         if (info->type < ARRAY_SIZE(prog_type_name))
297                 printf("%s  ", prog_type_name[info->type]);
298         else
299                 printf("type %u  ", info->type);
300
301         if (*info->name)
302                 printf("name %s  ", info->name);
303
304         printf("tag ");
305         fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
306         print_dev_plain(info->ifindex, info->netns_dev, info->netns_ino);
307         printf("%s", info->gpl_compatible ? "  gpl" : "");
308         printf("\n");
309
310         if (info->load_time) {
311                 char buf[32];
312
313                 print_boot_time(info->load_time, buf, sizeof(buf));
314
315                 /* Piggy back on load_time, since 0 uid is a valid one */
316                 printf("\tloaded_at %s  uid %u\n", buf, info->created_by_uid);
317         }
318
319         printf("\txlated %uB", info->xlated_prog_len);
320
321         if (info->jited_prog_len)
322                 printf("  jited %uB", info->jited_prog_len);
323         else
324                 printf("  not jited");
325
326         memlock = get_fdinfo(fd, "memlock");
327         if (memlock)
328                 printf("  memlock %sB", memlock);
329         free(memlock);
330
331         if (info->nr_map_ids)
332                 show_prog_maps(fd, info->nr_map_ids);
333
334         if (!hash_empty(prog_table.table)) {
335                 struct pinned_obj *obj;
336
337                 printf("\n");
338                 hash_for_each_possible(prog_table.table, obj, hash, info->id) {
339                         if (obj->id == info->id)
340                                 printf("\tpinned %s\n", obj->path);
341                 }
342         }
343
344         printf("\n");
345 }
346
347 static int show_prog(int fd)
348 {
349         struct bpf_prog_info info = {};
350         __u32 len = sizeof(info);
351         int err;
352
353         err = bpf_obj_get_info_by_fd(fd, &info, &len);
354         if (err) {
355                 p_err("can't get prog info: %s", strerror(errno));
356                 return -1;
357         }
358
359         if (json_output)
360                 print_prog_json(&info, fd);
361         else
362                 print_prog_plain(&info, fd);
363
364         return 0;
365 }
366
367 static int do_show(int argc, char **argv)
368 {
369         __u32 id = 0;
370         int err;
371         int fd;
372
373         if (show_pinned)
374                 build_pinned_obj_table(&prog_table, BPF_OBJ_PROG);
375
376         if (argc == 2) {
377                 fd = prog_parse_fd(&argc, &argv);
378                 if (fd < 0)
379                         return -1;
380
381                 return show_prog(fd);
382         }
383
384         if (argc)
385                 return BAD_ARG();
386
387         if (json_output)
388                 jsonw_start_array(json_wtr);
389         while (true) {
390                 err = bpf_prog_get_next_id(id, &id);
391                 if (err) {
392                         if (errno == ENOENT) {
393                                 err = 0;
394                                 break;
395                         }
396                         p_err("can't get next program: %s%s", strerror(errno),
397                               errno == EINVAL ? " -- kernel too old?" : "");
398                         err = -1;
399                         break;
400                 }
401
402                 fd = bpf_prog_get_fd_by_id(id);
403                 if (fd < 0) {
404                         if (errno == ENOENT)
405                                 continue;
406                         p_err("can't get prog by id (%u): %s",
407                               id, strerror(errno));
408                         err = -1;
409                         break;
410                 }
411
412                 err = show_prog(fd);
413                 close(fd);
414                 if (err)
415                         break;
416         }
417
418         if (json_output)
419                 jsonw_end_array(json_wtr);
420
421         return err;
422 }
423
424 static int do_dump(int argc, char **argv)
425 {
426         unsigned long *func_ksyms = NULL;
427         struct bpf_prog_info info = {};
428         unsigned int *func_lens = NULL;
429         unsigned int nr_func_ksyms;
430         unsigned int nr_func_lens;
431         struct dump_data dd = {};
432         __u32 len = sizeof(info);
433         unsigned int buf_size;
434         char *filepath = NULL;
435         bool opcodes = false;
436         bool visual = false;
437         unsigned char *buf;
438         __u32 *member_len;
439         __u64 *member_ptr;
440         ssize_t n;
441         int err;
442         int fd;
443
444         if (is_prefix(*argv, "jited")) {
445                 member_len = &info.jited_prog_len;
446                 member_ptr = &info.jited_prog_insns;
447         } else if (is_prefix(*argv, "xlated")) {
448                 member_len = &info.xlated_prog_len;
449                 member_ptr = &info.xlated_prog_insns;
450         } else {
451                 p_err("expected 'xlated' or 'jited', got: %s", *argv);
452                 return -1;
453         }
454         NEXT_ARG();
455
456         if (argc < 2)
457                 usage();
458
459         fd = prog_parse_fd(&argc, &argv);
460         if (fd < 0)
461                 return -1;
462
463         if (is_prefix(*argv, "file")) {
464                 NEXT_ARG();
465                 if (!argc) {
466                         p_err("expected file path");
467                         return -1;
468                 }
469
470                 filepath = *argv;
471                 NEXT_ARG();
472         } else if (is_prefix(*argv, "opcodes")) {
473                 opcodes = true;
474                 NEXT_ARG();
475         } else if (is_prefix(*argv, "visual")) {
476                 visual = true;
477                 NEXT_ARG();
478         }
479
480         if (argc) {
481                 usage();
482                 return -1;
483         }
484
485         err = bpf_obj_get_info_by_fd(fd, &info, &len);
486         if (err) {
487                 p_err("can't get prog info: %s", strerror(errno));
488                 return -1;
489         }
490
491         if (!*member_len) {
492                 p_info("no instructions returned");
493                 close(fd);
494                 return 0;
495         }
496
497         buf_size = *member_len;
498
499         buf = malloc(buf_size);
500         if (!buf) {
501                 p_err("mem alloc failed");
502                 close(fd);
503                 return -1;
504         }
505
506         nr_func_ksyms = info.nr_jited_ksyms;
507         if (nr_func_ksyms) {
508                 func_ksyms = malloc(nr_func_ksyms * sizeof(__u64));
509                 if (!func_ksyms) {
510                         p_err("mem alloc failed");
511                         close(fd);
512                         goto err_free;
513                 }
514         }
515
516         nr_func_lens = info.nr_jited_func_lens;
517         if (nr_func_lens) {
518                 func_lens = malloc(nr_func_lens * sizeof(__u32));
519                 if (!func_lens) {
520                         p_err("mem alloc failed");
521                         close(fd);
522                         goto err_free;
523                 }
524         }
525
526         memset(&info, 0, sizeof(info));
527
528         *member_ptr = ptr_to_u64(buf);
529         *member_len = buf_size;
530         info.jited_ksyms = ptr_to_u64(func_ksyms);
531         info.nr_jited_ksyms = nr_func_ksyms;
532         info.jited_func_lens = ptr_to_u64(func_lens);
533         info.nr_jited_func_lens = nr_func_lens;
534
535         err = bpf_obj_get_info_by_fd(fd, &info, &len);
536         close(fd);
537         if (err) {
538                 p_err("can't get prog info: %s", strerror(errno));
539                 goto err_free;
540         }
541
542         if (*member_len > buf_size) {
543                 p_err("too many instructions returned");
544                 goto err_free;
545         }
546
547         if (info.nr_jited_ksyms > nr_func_ksyms) {
548                 p_err("too many addresses returned");
549                 goto err_free;
550         }
551
552         if (info.nr_jited_func_lens > nr_func_lens) {
553                 p_err("too many values returned");
554                 goto err_free;
555         }
556
557         if ((member_len == &info.jited_prog_len &&
558              info.jited_prog_insns == 0) ||
559             (member_len == &info.xlated_prog_len &&
560              info.xlated_prog_insns == 0)) {
561                 p_err("error retrieving insn dump: kernel.kptr_restrict set?");
562                 goto err_free;
563         }
564
565         if (filepath) {
566                 fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
567                 if (fd < 0) {
568                         p_err("can't open file %s: %s", filepath,
569                               strerror(errno));
570                         goto err_free;
571                 }
572
573                 n = write(fd, buf, *member_len);
574                 close(fd);
575                 if (n != *member_len) {
576                         p_err("error writing output file: %s",
577                               n < 0 ? strerror(errno) : "short write");
578                         goto err_free;
579                 }
580
581                 if (json_output)
582                         jsonw_null(json_wtr);
583         } else if (member_len == &info.jited_prog_len) {
584                 const char *name = NULL;
585
586                 if (info.ifindex) {
587                         name = ifindex_to_bfd_name_ns(info.ifindex,
588                                                       info.netns_dev,
589                                                       info.netns_ino);
590                         if (!name)
591                                 goto err_free;
592                 }
593
594                 if (info.nr_jited_func_lens && info.jited_func_lens) {
595                         struct kernel_sym *sym = NULL;
596                         char sym_name[SYM_MAX_NAME];
597                         unsigned char *img = buf;
598                         __u64 *ksyms = NULL;
599                         __u32 *lens;
600                         __u32 i;
601
602                         if (info.nr_jited_ksyms) {
603                                 kernel_syms_load(&dd);
604                                 ksyms = (__u64 *) info.jited_ksyms;
605                         }
606
607                         if (json_output)
608                                 jsonw_start_array(json_wtr);
609
610                         lens = (__u32 *) info.jited_func_lens;
611                         for (i = 0; i < info.nr_jited_func_lens; i++) {
612                                 if (ksyms) {
613                                         sym = kernel_syms_search(&dd, ksyms[i]);
614                                         if (sym)
615                                                 sprintf(sym_name, "%s", sym->name);
616                                         else
617                                                 sprintf(sym_name, "0x%016llx", ksyms[i]);
618                                 } else {
619                                         strcpy(sym_name, "unknown");
620                                 }
621
622                                 if (json_output) {
623                                         jsonw_start_object(json_wtr);
624                                         jsonw_name(json_wtr, "name");
625                                         jsonw_string(json_wtr, sym_name);
626                                         jsonw_name(json_wtr, "insns");
627                                 } else {
628                                         printf("%s:\n", sym_name);
629                                 }
630
631                                 disasm_print_insn(img, lens[i], opcodes, name);
632                                 img += lens[i];
633
634                                 if (json_output)
635                                         jsonw_end_object(json_wtr);
636                                 else
637                                         printf("\n");
638                         }
639
640                         if (json_output)
641                                 jsonw_end_array(json_wtr);
642                 } else {
643                         disasm_print_insn(buf, *member_len, opcodes, name);
644                 }
645         } else if (visual) {
646                 if (json_output)
647                         jsonw_null(json_wtr);
648                 else
649                         dump_xlated_cfg(buf, *member_len);
650         } else {
651                 kernel_syms_load(&dd);
652                 dd.nr_jited_ksyms = info.nr_jited_ksyms;
653                 dd.jited_ksyms = (__u64 *) info.jited_ksyms;
654
655                 if (json_output)
656                         dump_xlated_json(&dd, buf, *member_len, opcodes);
657                 else
658                         dump_xlated_plain(&dd, buf, *member_len, opcodes);
659                 kernel_syms_destroy(&dd);
660         }
661
662         free(buf);
663         free(func_ksyms);
664         free(func_lens);
665         return 0;
666
667 err_free:
668         free(buf);
669         free(func_ksyms);
670         free(func_lens);
671         return -1;
672 }
673
674 static int do_pin(int argc, char **argv)
675 {
676         int err;
677
678         err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id);
679         if (!err && json_output)
680                 jsonw_null(json_wtr);
681         return err;
682 }
683
684 static int do_load(int argc, char **argv)
685 {
686         struct bpf_object *obj;
687         int prog_fd;
688
689         if (argc != 2)
690                 usage();
691
692         if (bpf_prog_load(argv[0], BPF_PROG_TYPE_UNSPEC, &obj, &prog_fd)) {
693                 p_err("failed to load program");
694                 return -1;
695         }
696
697         if (do_pin_fd(prog_fd, argv[1]))
698                 goto err_close_obj;
699
700         if (json_output)
701                 jsonw_null(json_wtr);
702
703         bpf_object__close(obj);
704
705         return 0;
706
707 err_close_obj:
708         bpf_object__close(obj);
709         return -1;
710 }
711
712 static int do_help(int argc, char **argv)
713 {
714         if (json_output) {
715                 jsonw_null(json_wtr);
716                 return 0;
717         }
718
719         fprintf(stderr,
720                 "Usage: %s %s { show | list } [PROG]\n"
721                 "       %s %s dump xlated PROG [{ file FILE | opcodes | visual }]\n"
722                 "       %s %s dump jited  PROG [{ file FILE | opcodes }]\n"
723                 "       %s %s pin   PROG FILE\n"
724                 "       %s %s load  OBJ  FILE\n"
725                 "       %s %s help\n"
726                 "\n"
727                 "       " HELP_SPEC_PROGRAM "\n"
728                 "       " HELP_SPEC_OPTIONS "\n"
729                 "",
730                 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
731                 bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
732
733         return 0;
734 }
735
736 static const struct cmd cmds[] = {
737         { "show",       do_show },
738         { "list",       do_show },
739         { "help",       do_help },
740         { "dump",       do_dump },
741         { "pin",        do_pin },
742         { "load",       do_load },
743         { 0 }
744 };
745
746 int do_prog(int argc, char **argv)
747 {
748         return cmd_select(cmds, argc, argv, do_help);
749 }