1241675Suqs/* Emulation of eBPF helpers.
2241675Suqs   Copyright (C) 2020 Free Software Foundation, Inc.
3241675Suqs
4241675Suqs   This file is part of GDB, the GNU debugger.
5241675Suqs
6241675Suqs   This program is free software; you can redistribute it and/or modify
7241675Suqs   it under the terms of the GNU General Public License as published by
8241675Suqs   the Free Software Foundation; either version 3 of the License, or
9241675Suqs   (at your option) any later version.
10241675Suqs
11241675Suqs   This program is distributed in the hope that it will be useful,
12241675Suqs   but WITHOUT ANY WARRANTY; without even the implied warranty of
13241675Suqs   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14241675Suqs   GNU General Public License for more details.
15241675Suqs
16241675Suqs   You should have received a copy of the GNU General Public License
17241675Suqs   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18241675Suqs
19241675Suqs/* BPF programs rely on the existence of several helper functions,
20241675Suqs   which are provided by the kernel.  This simulator provides an
21241675Suqs   implementation of the helpers, which can be customized by the
22241675Suqs   user.  */
23241675Suqs
24241675Suqs#define WANT_CPU_BPFBF
25241675Suqs#define WANT_CPU bpfbf
26241675Suqs
27241675Suqs#include "sim-main.h"
28241675Suqs#include "cgen-mem.h"
29241675Suqs#include "cgen-ops.h"
30241675Suqs#include "cpu.h"
31241675Suqs
32241675Suqs/* bpf_trace_printk is a printk-like facility for debugging.
33241675Suqs
34241675Suqs   In the kernel, it appends a line to the Linux's tracing debugging
35241675Suqs   interface.
36241675Suqs
37241675Suqs   In this simulator, it uses the simulator's tracing interface
38241675Suqs   instead.
39241675Suqs
40241675Suqs   The format tags recognized by this helper are:
41241675Suqs   %d, %i, %u, %x, %ld, %li, %lu, %lx, %lld, %lli, %llu, %llx,
42241675Suqs   %p, %s
43241675Suqs
44241675Suqs   A maximum of three tags are supported.
45241675Suqs
46241675Suqs   This helper returns the number of bytes written, or a negative
47241675Suqs   value in case of failure.  */
48241675Suqs
49241675Suqsint
50241675Suqsbpf_trace_printk (SIM_CPU *current_cpu)
51241675Suqs{
52241675Suqs  va_list ap;
53241675Suqs  SIM_DESC sd = CPU_STATE (current_cpu);
54241675Suqs
55241675Suqs  DI fmt_address;
56241675Suqs  uint32_t size, tags_processed;
57241675Suqs  size_t i, bytes_written = 0;
58241675Suqs
59241675Suqs  /* The first argument is the format string, which is passed as a
60241675Suqs     pointer in %r1.  */
61241675Suqs  fmt_address = GET_H_GPR (1);
62241675Suqs
63241675Suqs  /* The second argument is the length of the format string, as an
64241675Suqs     unsigned 32-bit number in %r2.  */
65241675Suqs  size = GET_H_GPR (2);
66241675Suqs
67241675Suqs  /* Read the format string from the memory pointed by %r2, printing
68241675Suqs     out the stuff as we go.  There is a maximum of three format tags
69241675Suqs     supported, which are read from %r3, %r4 and %r5 respectively.  */
70241675Suqs  for (i = 0, tags_processed = 0; i < size;)
71241675Suqs    {
72241675Suqs      QI c = GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu),
73241675Suqs                        fmt_address + i);
74241675Suqs
75241675Suqs      switch (c)
76241675Suqs        {
77241675Suqs        case '%':
78241675Suqs          /* Check we are not exceeding the limit of three format
79241675Suqs             tags.  */
80241675Suqs          if (tags_processed > 2)
81241675Suqs            return -1; /* XXX look for kernel error code.  */
82241675Suqs
83241675Suqs          /* Depending on the kind of tag, extract the value from the
84241675Suqs             proper argument.  */
85241675Suqs          if (i++ >= size)
86241675Suqs            return -1; /* XXX look for kernel error code.  */
87241675Suqs
88241675Suqs          UDI value = GET_H_GPR (3 + tags_processed);
89241675Suqs
90241675Suqs          switch ((GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu),
91241675Suqs                             fmt_address + i)))
92241675Suqs            {
93241675Suqs            case 'd':
94241675Suqs              trace_printf (sd, current_cpu, "%d", value);
95241675Suqs              break;
96241675Suqs            case 'i':
97241675Suqs              trace_printf (sd, current_cpu, "%i", value);
98241675Suqs              break;
99241675Suqs            case 'u':
100241675Suqs              trace_printf (sd, current_cpu, "%u", value);
101241675Suqs              break;
102241675Suqs            case 'x':
103241675Suqs              trace_printf (sd, current_cpu, "%x", value);
104241675Suqs              break;
105241675Suqs            case 'l':
106241675Suqs              {
107241675Suqs                if (i++ >= size)
108241675Suqs                  return -1;
109241675Suqs                switch (GETMEMUQI (current_cpu, CPU_PC_GET (current_cpu),
110241675Suqs                             fmt_address + i))
111241675Suqs                  {
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