Commit | Line | Data |
---|---|---|
b26e2ae7 | 1 | /* Emulation of eBPF helpers. |
3666a048 | 2 | Copyright (C) 2020-2021 Free Software Foundation, Inc. |
b26e2ae7 JM |
3 | |
4 | This file is part of GDB, the GNU debugger. | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 3 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
18 | ||
19 | /* BPF programs rely on the existence of several helper functions, | |
20 | which are provided by the kernel. This simulator provides an | |
21 | implementation of the helpers, which can be customized by the | |
22 | user. */ | |
23 | ||
6df01ab8 MF |
24 | /* This must come before any other includes. */ |
25 | #include "defs.h" | |
26 | ||
b26e2ae7 JM |
27 | #define WANT_CPU_BPFBF |
28 | #define WANT_CPU bpfbf | |
29 | ||
30 | #include "sim-main.h" | |
31 | #include "cgen-mem.h" | |
32 | #include "cgen-ops.h" | |
33 | #include "cpu.h" | |
34 | ||
35 | /* bpf_trace_printk is a printk-like facility for debugging. | |
36 | ||
37 | In the kernel, it appends a line to the Linux's tracing debugging | |
38 | interface. | |
39 | ||
40 | In this simulator, it uses the simulator's tracing interface | |
41 | instead. | |
42 | ||
43 | The format tags recognized by this helper are: | |
44 | %d, %i, %u, %x, %ld, %li, %lu, %lx, %lld, %lli, %llu, %llx, | |
45 | %p, %s | |
46 | ||
47 | A maximum of three tags are supported. | |
48 | ||
49 | This helper returns the number of bytes written, or a negative | |
50 | value in case of failure. */ | |
51 | ||
52 | int | |
53 | bpf_trace_printk (SIM_CPU *current_cpu) | |
54 | { | |
55 | va_list ap; | |
56 | SIM_DESC sd = CPU_STATE (current_cpu); | |
57 | ||
58 | DI fmt_address; | |
59 | uint32_t size, tags_processed; | |
60 | size_t i, bytes_written = 0; | |
61 | ||
62 | /* The first argument is the format string, which is passed as a | |
63 | pointer in %r1. */ | |
64 | fmt_address = GET_H_GPR (1); | |
65 | ||
66 | /* The second argument is the length of the format string, as an | |
67 | unsigned 32-bit number in %r2. */ | |
68 | size = GET_H_GPR (2); | |
69 | ||
70 | /* Read the format string from the memory pointed by %r2, printing | |
71 | out the stuff as we go. There is a maximum of three format tags | |
72 | supported, which are read from %r3, %r4 and %r5 respectively. */ | |
73 | for (i = 0, tags_processed = 0; i < size;) | |
74 | { | |
75 | QI c = GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu), | |
76 | fmt_address + i); | |
77 | ||
78 | switch (c) | |
79 | { | |
80 | case '%': | |
81 | /* Check we are not exceeding the limit of three format | |
82 | tags. */ | |
83 | if (tags_processed > 2) | |
84 | return -1; /* XXX look for kernel error code. */ | |
85 | ||
86 | /* Depending on the kind of tag, extract the value from the | |
87 | proper argument. */ | |
88 | if (i++ >= size) | |
89 | return -1; /* XXX look for kernel error code. */ | |
90 | ||
91 | UDI value = GET_H_GPR (3 + tags_processed); | |
92 | ||
93 | switch ((GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu), | |
94 | fmt_address + i))) | |
95 | { | |
96 | case 'd': | |
97 | trace_printf (sd, current_cpu, "%d", value); | |
98 | break; | |
99 | case 'i': | |
100 | trace_printf (sd, current_cpu, "%i", value); | |
101 | break; | |
102 | case 'u': | |
103 | trace_printf (sd, current_cpu, "%u", value); | |
104 | break; | |
105 | case 'x': | |
106 | trace_printf (sd, current_cpu, "%x", value); | |
107 | break; | |
108 | case 'l': | |
109 | { | |
110 | if (i++ >= size) | |
111 | return -1; | |
112 | switch (GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu), | |
113 | fmt_address + i)) | |
114 | { | |
115 | case 'd': | |
116 | trace_printf (sd, current_cpu, "%ld", value); | |
117 | break; | |
118 | case 'i': | |
119 | trace_printf (sd, current_cpu, "%li", value); | |
120 | break; | |
121 | case 'u': | |
122 | trace_printf (sd, current_cpu, "%lu", value); | |
123 | break; | |
124 | case 'x': | |
125 | trace_printf (sd, current_cpu, "%lx", value); | |
126 | break; | |
127 | case 'l': | |
128 | { | |
129 | if (i++ >= size) | |
130 | return -1; | |
131 | switch (GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu), | |
132 | fmt_address + i)) { | |
133 | case 'd': | |
134 | trace_printf (sd, current_cpu, "%lld", value); | |
135 | break; | |
136 | case 'i': | |
137 | trace_printf (sd, current_cpu, "%lli", value); | |
138 | break; | |
139 | case 'u': | |
140 | trace_printf (sd, current_cpu, "%llu", value); | |
141 | break; | |
142 | case 'x': | |
143 | trace_printf (sd, current_cpu, "%llx", value); | |
144 | break; | |
145 | default: | |
146 | assert (0); | |
147 | break; | |
148 | } | |
149 | break; | |
150 | } | |
151 | default: | |
152 | assert (0); | |
153 | break; | |
154 | } | |
155 | break; | |
156 | } | |
157 | default: | |
158 | /* XXX completeme */ | |
159 | assert (0); | |
160 | break; | |
161 | } | |
162 | ||
163 | tags_processed++; | |
164 | i++; | |
165 | break; | |
166 | case '\0': | |
167 | i = size; | |
168 | break; | |
169 | default: | |
170 | trace_printf (sd, current_cpu, "%c", c); | |
171 | bytes_written++; | |
172 | i++; | |
173 | break; | |
174 | } | |
175 | } | |
176 | ||
177 | return bytes_written; | |
178 | } |