1 | // SPDX-License-Identifier: LGPL-2.1 |
2 | /* |
3 | * trace/beauty/ioctl.c |
4 | * |
5 | * Copyright (C) 2017, Red Hat Inc, Arnaldo Carvalho de Melo <acme@redhat.com> |
6 | */ |
7 | |
8 | #include "trace/beauty/beauty.h" |
9 | #include <linux/kernel.h> |
10 | |
11 | /* |
12 | * FIXME: to support all arches we have to improve this, for |
13 | * now, to build on older systems without things like TIOCGEXCL, |
14 | * get it directly from our copy. |
15 | * |
16 | * Right now only x86 is being supported for beautifying ioctl args |
17 | * in 'perf trace', see tools/perf/trace/beauty/Build and builtin-trace.c |
18 | */ |
19 | #include <uapi/asm-generic/ioctls.h> |
20 | |
21 | static size_t ioctl__scnprintf_tty_cmd(int nr, int dir, char *bf, size_t size) |
22 | { |
23 | static const char *ioctl_tty_cmd[] = { |
24 | [_IOC_NR(TCGETS)] = "TCGETS" , "TCSETS" , "TCSETSW" , "TCSETSF" , "TCGETA" , "TCSETA" , "TCSETAW" , |
25 | "TCSETAF" , "TCSBRK" , "TCXONC" , "TCFLSH" , "TIOCEXCL" , "TIOCNXCL" , "TIOCSCTTY" , |
26 | "TIOCGPGRP" , "TIOCSPGRP" , "TIOCOUTQ" , "TIOCSTI" , "TIOCGWINSZ" , "TIOCSWINSZ" , |
27 | "TIOCMGET" , "TIOCMBIS" , "TIOCMBIC" , "TIOCMSET" , "TIOCGSOFTCAR" , "TIOCSSOFTCAR" , |
28 | "FIONREAD" , "TIOCLINUX" , "TIOCCONS" , "TIOCGSERIAL" , "TIOCSSERIAL" , "TIOCPKT" , |
29 | "FIONBIO" , "TIOCNOTTY" , "TIOCSETD" , "TIOCGETD" , "TCSBRKP" , |
30 | [_IOC_NR(TIOCSBRK)] = "TIOCSBRK" , "TIOCCBRK" , "TIOCGSID" , "TCGETS2" , "TCSETS2" , |
31 | "TCSETSW2" , "TCSETSF2" , "TIOCGRS48" , "TIOCSRS485" , "TIOCGPTN" , "TIOCSPTLCK" , |
32 | "TIOCGDEV" , "TCSETX" , "TCSETXF" , "TCSETXW" , "TIOCSIG" , "TIOCVHANGUP" , "TIOCGPKT" , |
33 | "TIOCGPTLCK" , [_IOC_NR(TIOCGEXCL)] = "TIOCGEXCL" , "TIOCGPTPEER" , |
34 | "TIOCGISO7816" , "TIOCSISO7816" , |
35 | [_IOC_NR(FIONCLEX)] = "FIONCLEX" , "FIOCLEX" , "FIOASYNC" , "TIOCSERCONFIG" , |
36 | "TIOCSERGWILD" , "TIOCSERSWILD" , "TIOCGLCKTRMIOS" , "TIOCSLCKTRMIOS" , |
37 | "TIOCSERGSTRUCT" , "TIOCSERGETLSR" , "TIOCSERGETMULTI" , "TIOCSERSETMULTI" , |
38 | "TIOCMIWAIT" , "TIOCGICOUNT" , }; |
39 | static DEFINE_STRARRAY(ioctl_tty_cmd, "" ); |
40 | |
41 | if (nr < strarray__ioctl_tty_cmd.nr_entries && strarray__ioctl_tty_cmd.entries[nr] != NULL) |
42 | return scnprintf(bf, size, "%s" , strarray__ioctl_tty_cmd.entries[nr]); |
43 | |
44 | return scnprintf(buf: bf, size, fmt: "(%#x, %#x, %#x)" , 'T', nr, dir); |
45 | } |
46 | |
47 | static size_t ioctl__scnprintf_drm_cmd(int nr, int dir, char *bf, size_t size) |
48 | { |
49 | #include "trace/beauty/generated/ioctl/drm_ioctl_array.c" |
50 | static DEFINE_STRARRAY(drm_ioctl_cmds, "" ); |
51 | |
52 | if (nr < strarray__drm_ioctl_cmds.nr_entries && strarray__drm_ioctl_cmds.entries[nr] != NULL) |
53 | return scnprintf(bf, size, "DRM_%s" , strarray__drm_ioctl_cmds.entries[nr]); |
54 | |
55 | return scnprintf(buf: bf, size, fmt: "(%#x, %#x, %#x)" , 'd', nr, dir); |
56 | } |
57 | |
58 | static size_t ioctl__scnprintf_sndrv_pcm_cmd(int nr, int dir, char *bf, size_t size) |
59 | { |
60 | #include "trace/beauty/generated/ioctl/sndrv_pcm_ioctl_array.c" |
61 | static DEFINE_STRARRAY(sndrv_pcm_ioctl_cmds, "" ); |
62 | |
63 | if (nr < strarray__sndrv_pcm_ioctl_cmds.nr_entries && strarray__sndrv_pcm_ioctl_cmds.entries[nr] != NULL) |
64 | return scnprintf(bf, size, "SNDRV_PCM_%s" , strarray__sndrv_pcm_ioctl_cmds.entries[nr]); |
65 | |
66 | return scnprintf(buf: bf, size, fmt: "(%#x, %#x, %#x)" , 'A', nr, dir); |
67 | } |
68 | |
69 | static size_t ioctl__scnprintf_sndrv_ctl_cmd(int nr, int dir, char *bf, size_t size) |
70 | { |
71 | #include "trace/beauty/generated/ioctl/sndrv_ctl_ioctl_array.c" |
72 | static DEFINE_STRARRAY(sndrv_ctl_ioctl_cmds, "" ); |
73 | |
74 | if (nr < strarray__sndrv_ctl_ioctl_cmds.nr_entries && strarray__sndrv_ctl_ioctl_cmds.entries[nr] != NULL) |
75 | return scnprintf(bf, size, "SNDRV_CTL_%s" , strarray__sndrv_ctl_ioctl_cmds.entries[nr]); |
76 | |
77 | return scnprintf(buf: bf, size, fmt: "(%#x, %#x, %#x)" , 'U', nr, dir); |
78 | } |
79 | |
80 | static size_t ioctl__scnprintf_kvm_cmd(int nr, int dir, char *bf, size_t size) |
81 | { |
82 | #include "trace/beauty/generated/ioctl/kvm_ioctl_array.c" |
83 | static DEFINE_STRARRAY(kvm_ioctl_cmds, "" ); |
84 | |
85 | if (nr < strarray__kvm_ioctl_cmds.nr_entries && strarray__kvm_ioctl_cmds.entries[nr] != NULL) |
86 | return scnprintf(bf, size, "KVM_%s" , strarray__kvm_ioctl_cmds.entries[nr]); |
87 | |
88 | return scnprintf(buf: bf, size, fmt: "(%#x, %#x, %#x)" , 0xAE, nr, dir); |
89 | } |
90 | |
91 | static size_t ioctl__scnprintf_vhost_virtio_cmd(int nr, int dir, char *bf, size_t size) |
92 | { |
93 | #include "trace/beauty/generated/ioctl/vhost_virtio_ioctl_array.c" |
94 | static DEFINE_STRARRAY(vhost_virtio_ioctl_cmds, "" ); |
95 | static DEFINE_STRARRAY(vhost_virtio_ioctl_read_cmds, "" ); |
96 | struct strarray *s = (dir & _IOC_READ) ? &strarray__vhost_virtio_ioctl_read_cmds : &strarray__vhost_virtio_ioctl_cmds; |
97 | |
98 | if (nr < s->nr_entries && s->entries[nr] != NULL) |
99 | return scnprintf(buf: bf, size, fmt: "VHOST_%s" , s->entries[nr]); |
100 | |
101 | return scnprintf(buf: bf, size, fmt: "(%#x, %#x, %#x)" , 0xAF, nr, dir); |
102 | } |
103 | |
104 | static size_t ioctl__scnprintf_perf_cmd(int nr, int dir, char *bf, size_t size) |
105 | { |
106 | #include "trace/beauty/generated/ioctl/perf_ioctl_array.c" |
107 | static DEFINE_STRARRAY(perf_ioctl_cmds, "" ); |
108 | |
109 | if (nr < strarray__perf_ioctl_cmds.nr_entries && strarray__perf_ioctl_cmds.entries[nr] != NULL) |
110 | return scnprintf(bf, size, "PERF_%s" , strarray__perf_ioctl_cmds.entries[nr]); |
111 | |
112 | return scnprintf(buf: bf, size, fmt: "(%#x, %#x, %#x)" , 0xAE, nr, dir); |
113 | } |
114 | |
115 | static size_t ioctl__scnprintf_usbdevfs_cmd(int nr, int dir, char *bf, size_t size) |
116 | { |
117 | #include "trace/beauty/generated/ioctl/usbdevfs_ioctl_array.c" |
118 | static DEFINE_STRARRAY(usbdevfs_ioctl_cmds, "" ); |
119 | |
120 | if (nr < strarray__usbdevfs_ioctl_cmds.nr_entries && strarray__usbdevfs_ioctl_cmds.entries[nr] != NULL) |
121 | return scnprintf(bf, size, "USBDEVFS_%s" , strarray__usbdevfs_ioctl_cmds.entries[nr]); |
122 | |
123 | return scnprintf(buf: bf, size, fmt: "(%c, %#x, %#x)" , 'U', nr, dir); |
124 | } |
125 | |
126 | static size_t ioctl__scnprintf_cmd(unsigned long cmd, char *bf, size_t size, bool show_prefix) |
127 | { |
128 | const char *prefix = "_IOC_" ; |
129 | int dir = _IOC_DIR(cmd), |
130 | type = _IOC_TYPE(cmd), |
131 | nr = _IOC_NR(cmd), |
132 | sz = _IOC_SIZE(cmd); |
133 | int printed = 0; |
134 | static const struct ioctl_type { |
135 | int type; |
136 | size_t (*scnprintf)(int nr, int dir, char *bf, size_t size); |
137 | } ioctl_types[] = { /* Must be ordered by type */ |
138 | { .type = '$', .scnprintf = ioctl__scnprintf_perf_cmd, }, |
139 | ['A' - '$'] = { .type = 'A', .scnprintf = ioctl__scnprintf_sndrv_pcm_cmd, }, |
140 | ['T' - '$'] = { .type = 'T', .scnprintf = ioctl__scnprintf_tty_cmd, }, |
141 | ['U' - '$'] = { .type = 'U', .scnprintf = ioctl__scnprintf_sndrv_ctl_cmd, }, |
142 | ['d' - '$'] = { .type = 'd', .scnprintf = ioctl__scnprintf_drm_cmd, }, |
143 | [0xAE - '$'] = { .type = 0xAE, .scnprintf = ioctl__scnprintf_kvm_cmd, }, |
144 | [0xAF - '$'] = { .type = 0xAF, .scnprintf = ioctl__scnprintf_vhost_virtio_cmd, }, |
145 | }; |
146 | const int nr_types = ARRAY_SIZE(ioctl_types); |
147 | |
148 | if (type >= ioctl_types[0].type && type <= ioctl_types[nr_types - 1].type) { |
149 | const int index = type - ioctl_types[0].type; |
150 | |
151 | if (ioctl_types[index].scnprintf != NULL) |
152 | return ioctl_types[index].scnprintf(nr, dir, bf, size); |
153 | } |
154 | |
155 | printed += scnprintf(buf: bf + printed, size: size - printed, fmt: "%c" , '('); |
156 | |
157 | if (dir == _IOC_NONE) { |
158 | printed += scnprintf(buf: bf + printed, size: size - printed, fmt: "%s%s" , show_prefix ? prefix : "" , "NONE" ); |
159 | } else { |
160 | if (dir & _IOC_READ) |
161 | printed += scnprintf(buf: bf + printed, size: size - printed, fmt: "%s%s" , show_prefix ? prefix : "" , "READ" ); |
162 | if (dir & _IOC_WRITE) { |
163 | printed += scnprintf(buf: bf + printed, size: size - printed, fmt: "%s%s%s" , dir & _IOC_READ ? "|" : "" , |
164 | show_prefix ? prefix : "" , "WRITE" ); |
165 | } |
166 | } |
167 | |
168 | return printed + scnprintf(buf: bf + printed, size: size - printed, fmt: ", %#x, %#x, %#x)" , type, nr, sz); |
169 | } |
170 | |
171 | #ifndef USB_DEVICE_MAJOR |
172 | #define USB_DEVICE_MAJOR 189 |
173 | #endif // USB_DEVICE_MAJOR |
174 | |
175 | size_t syscall_arg__scnprintf_ioctl_cmd(char *bf, size_t size, struct syscall_arg *arg) |
176 | { |
177 | unsigned long cmd = arg->val; |
178 | int fd = syscall_arg__val(arg, 0); |
179 | struct file *file = thread__files_entry(arg->thread, fd); |
180 | |
181 | if (file != NULL) { |
182 | if (file->dev_maj == USB_DEVICE_MAJOR) |
183 | return ioctl__scnprintf_usbdevfs_cmd(_IOC_NR(cmd), _IOC_DIR(cmd), bf, size); |
184 | } |
185 | |
186 | return ioctl__scnprintf_cmd(cmd, bf, size, show_prefix: arg->show_string_prefix); |
187 | } |
188 | |