xref: /netbsd-src/external/gpl3/gdb.old/dist/sim/bpf/bpf-helpers.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /* Emulation of eBPF helpers.
2    Copyright (C) 2020 Free Software Foundation, Inc.
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 
24 #define WANT_CPU_BPFBF
25 #define WANT_CPU bpfbf
26 
27 #include "sim-main.h"
28 #include "cgen-mem.h"
29 #include "cgen-ops.h"
30 #include "cpu.h"
31 
32 /* bpf_trace_printk is a printk-like facility for debugging.
33 
34    In the kernel, it appends a line to the Linux's tracing debugging
35    interface.
36 
37    In this simulator, it uses the simulator's tracing interface
38    instead.
39 
40    The format tags recognized by this helper are:
41    %d, %i, %u, %x, %ld, %li, %lu, %lx, %lld, %lli, %llu, %llx,
42    %p, %s
43 
44    A maximum of three tags are supported.
45 
46    This helper returns the number of bytes written, or a negative
47    value in case of failure.  */
48 
49 int
50 bpf_trace_printk (SIM_CPU *current_cpu)
51 {
52   va_list ap;
53   SIM_DESC sd = CPU_STATE (current_cpu);
54 
55   DI fmt_address;
56   uint32_t size, tags_processed;
57   size_t i, bytes_written = 0;
58 
59   /* The first argument is the format string, which is passed as a
60      pointer in %r1.  */
61   fmt_address = GET_H_GPR (1);
62 
63   /* The second argument is the length of the format string, as an
64      unsigned 32-bit number in %r2.  */
65   size = GET_H_GPR (2);
66 
67   /* Read the format string from the memory pointed by %r2, printing
68      out the stuff as we go.  There is a maximum of three format tags
69      supported, which are read from %r3, %r4 and %r5 respectively.  */
70   for (i = 0, tags_processed = 0; i < size;)
71     {
72       QI c = GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu),
73                         fmt_address + i);
74 
75       switch (c)
76         {
77         case '%':
78           /* Check we are not exceeding the limit of three format
79              tags.  */
80           if (tags_processed > 2)
81             return -1; /* XXX look for kernel error code.  */
82 
83           /* Depending on the kind of tag, extract the value from the
84              proper argument.  */
85           if (i++ >= size)
86             return -1; /* XXX look for kernel error code.  */
87 
88           UDI value = GET_H_GPR (3 + tags_processed);
89 
90           switch ((GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu),
91                              fmt_address + i)))
92             {
93             case 'd':
94               trace_printf (sd, current_cpu, "%d", value);
95               break;
96             case 'i':
97               trace_printf (sd, current_cpu, "%i", value);
98               break;
99             case 'u':
100               trace_printf (sd, current_cpu, "%u", value);
101               break;
102             case 'x':
103               trace_printf (sd, current_cpu, "%x", value);
104               break;
105             case 'l':
106               {
107                 if (i++ >= size)
108                   return -1;
109                 switch (GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu),
110                              fmt_address + i))
111                   {
112                   case 'd':
113                     trace_printf (sd, current_cpu, "%ld", value);
114                     break;
115                   case 'i':
116                     trace_printf (sd, current_cpu, "%li", value);
117                     break;
118                   case 'u':
119                     trace_printf (sd, current_cpu, "%lu", value);
120                     break;
121                   case 'x':
122                     trace_printf (sd, current_cpu, "%lx", value);
123                     break;
124                   case 'l':
125                     {
126                       if (i++ >= size)
127                         return -1;
128                       switch (GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu),
129                                 fmt_address + i)) {
130                         case 'd':
131                           trace_printf (sd, current_cpu, "%lld", value);
132                           break;
133                         case 'i':
134                           trace_printf (sd, current_cpu, "%lli", value);
135                           break;
136                         case 'u':
137                           trace_printf (sd, current_cpu, "%llu", value);
138                           break;
139                         case 'x':
140                           trace_printf (sd, current_cpu, "%llx", value);
141                           break;
142                         default:
143                           assert (0);
144                           break;
145                       }
146                       break;
147                     }
148                   default:
149                     assert (0);
150                     break;
151                 }
152                 break;
153               }
154             default:
155               /* XXX completeme */
156               assert (0);
157               break;
158             }
159 
160           tags_processed++;
161           i++;
162           break;
163         case '\0':
164           i = size;
165           break;
166         default:
167           trace_printf (sd, current_cpu, "%c", c);
168           bytes_written++;
169           i++;
170           break;
171         }
172     }
173 
174   return bytes_written;
175 }
176