xref: /netbsd-src/external/gpl3/gdb/dist/sim/common/sim-hw.c (revision 88241920d21b339bf319c0e979ffda80c49a2936)
1 /* Simulator hardware option handling.
2    Copyright (C) 1998-2024 Free Software Foundation, Inc.
3    Contributed by Cygnus Support and Andrew Cagney.
4 
5 This file is part of GDB, the GNU debugger.
6 
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 /* This must come before any other includes.  */
21 #include "defs.h"
22 
23 #include <ctype.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "sim-main.h"
30 #include "sim-assert.h"
31 #include "sim-options.h"
32 #include "sim/callback.h"
33 
34 #include "sim-hw.h"
35 
36 #include "hw-tree.h"
37 #include "hw-device.h"
38 #include "hw-main.h"
39 #include "hw-base.h"
40 
41 struct sim_hw {
42   struct hw *tree;
43   int trace_p;
44   int info_p;
45   /* if called from a processor */
46   sim_cpu *cpu;
47   sim_cia cia;
48 };
49 
50 
51 struct hw *
52 sim_hw_parse (struct sim_state *sd,
53 	      const char *fmt,
54 	      ...)
55 {
56   struct hw *current;
57   va_list ap;
58   va_start (ap, fmt);
59   current = hw_tree_vparse (STATE_HW (sd)->tree, fmt, ap);
60   va_end (ap);
61   return current;
62 }
63 
64 struct printer {
65   struct sim_state *file;
66   void (*print) (struct sim_state *, const char *, va_list ap);
67 };
68 
69 static void
70 do_print (void *file, const char *fmt, ...)
71 {
72   struct printer *p = file;
73   va_list ap;
74   va_start (ap, fmt);
75   p->print (p->file, fmt, ap);
76   va_end (ap);
77 }
78 
79 void
80 sim_hw_print (struct sim_state *sd,
81 	      void (*print) (struct sim_state *, const char *, va_list ap))
82 {
83   struct printer p;
84   p.file = sd;
85   p.print = print;
86   hw_tree_print (STATE_HW (sd)->tree, do_print, &p);
87 }
88 
89 
90 
91 
92 /* command line options. */
93 
94 enum {
95   OPTION_HW_INFO = OPTION_START,
96   OPTION_HW_TRACE,
97   OPTION_HW_DEVICE,
98   OPTION_HW_LIST,
99   OPTION_HW_FILE,
100 };
101 
102 static DECLARE_OPTION_HANDLER (hw_option_handler);
103 
104 static const OPTION hw_options[] =
105 {
106   { {"hw-info", no_argument, NULL, OPTION_HW_INFO },
107       '\0', NULL, "List configurable hw regions",
108       hw_option_handler, NULL },
109   { {"info-hw", no_argument, NULL, OPTION_HW_INFO },
110       '\0', NULL, NULL,
111       hw_option_handler, NULL },
112 
113   { {"hw-trace", optional_argument, NULL, OPTION_HW_TRACE },
114       '\0', "on|off", "Trace all hardware devices",
115       hw_option_handler, NULL },
116   { {"trace-hw", optional_argument, NULL, OPTION_HW_TRACE },
117       '\0', NULL, NULL,
118       hw_option_handler, NULL },
119 
120   { {"hw-device", required_argument, NULL, OPTION_HW_DEVICE },
121       '\0', "DEVICE", "Add the specified device",
122       hw_option_handler, NULL },
123 
124   { {"hw-list", no_argument, NULL, OPTION_HW_LIST },
125       '\0', NULL, "List the device tree",
126       hw_option_handler, NULL },
127 
128   { {"hw-file", required_argument, NULL, OPTION_HW_FILE },
129       '\0', "FILE", "Add the devices listed in the file",
130       hw_option_handler, NULL },
131 
132   { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL, NULL }
133 };
134 
135 
136 
137 /* Copied from ../ppc/psim.c:psim_merge_device_file() */
138 
139 static SIM_RC
140 merge_device_file (struct sim_state *sd,
141 		   const char *file_name)
142 {
143   FILE *description;
144   struct hw *current = STATE_HW (sd)->tree;
145   char *device_path = NULL;
146   size_t buf_size = 0;
147   ssize_t device_path_len;
148 
149   /* try opening the file */
150   description = fopen (file_name, "r");
151   if (description == NULL)
152     {
153       perror (file_name);
154       return SIM_RC_FAIL;
155     }
156 
157   while ((device_path_len = getline (&device_path, &buf_size, description)) > 0)
158     {
159       char *device;
160       char *next_line = NULL;
161 
162       if (device_path[device_path_len - 1] == '\n')
163 	device_path[--device_path_len] = '\0';
164 
165       /* skip comments ("#" or ";") and blank lines lines */
166       for (device = device_path;
167 	   *device != '\0' && isspace (*device);
168 	   device++);
169       if (device[0] == '#'
170 	  || device[0] == ';'
171 	  || device[0] == '\0')
172 	continue;
173 
174       /* merge any appended lines */
175       while (device_path[device_path_len - 1] == '\\')
176 	{
177 	  size_t next_buf_size = 0;
178 	  ssize_t next_line_len;
179 
180 	  /* zap the `\' at the end of the line */
181 	  device_path[--device_path_len] = '\0';
182 
183 	  /* get the next line */
184 	  next_line_len = getline (&next_line, &next_buf_size, description);
185 	  if (next_line_len <= 0)
186 	    break;
187 
188 	  if (next_line[next_line_len - 1] == '\n')
189 	    next_line[--next_line_len] = '\0';
190 
191 	  /* append the next line */
192 	  if (buf_size - device_path_len <= next_line_len)
193 	    {
194 	      ptrdiff_t offset = device - device_path;
195 
196 	      buf_size += next_buf_size;
197 	      device_path = xrealloc (device_path, buf_size);
198 	      device = device_path + offset;
199 	    }
200 	  memcpy (device_path + device_path_len, next_line,
201 		  next_line_len + 1);
202 	  device_path_len += next_line_len;
203 	}
204       free (next_line);
205 
206       /* parse this line */
207       current = hw_tree_parse (current, "%s", device);
208     }
209 
210   free (device_path);
211   fclose (description);
212   return SIM_RC_OK;
213 }
214 
215 
216 static SIM_RC
217 hw_option_handler (struct sim_state *sd, sim_cpu *cpu, int opt,
218 		   char *arg, int is_command)
219 {
220   switch (opt)
221     {
222 
223     case OPTION_HW_INFO:
224       {
225 	/* delay info until after the tree is finished */
226 	STATE_HW (sd)->info_p = 1;
227 	return SIM_RC_OK;
228 	break;
229       }
230 
231     case OPTION_HW_TRACE:
232       {
233 	if (arg == NULL)
234 	  {
235 	    STATE_HW (sd)->trace_p = 1;
236 	  }
237 	else if (strcmp (arg, "yes") == 0
238 		 || strcmp (arg, "on") == 0)
239 	  {
240 	    STATE_HW (sd)->trace_p = 1;
241 	  }
242 	else if (strcmp (arg, "no") == 0
243 		 || strcmp (arg, "off") == 0)
244 	  {
245 	    STATE_HW (sd)->trace_p = 0;
246 	  }
247 	else
248 	  {
249 	    sim_io_eprintf (sd, "Option --hw-trace ignored\n");
250 	    /* set tracing on all devices */
251 	    return SIM_RC_FAIL;
252 	  }
253 	/* FIXME: Not very nice - see also hw-base.c */
254 	if (STATE_HW (sd)->trace_p)
255 	  hw_tree_parse (STATE_HW (sd)->tree, "/global-trace? true");
256 	return SIM_RC_OK;
257 	break;
258       }
259 
260     case OPTION_HW_DEVICE:
261       {
262 	hw_tree_parse (STATE_HW (sd)->tree, "%s", arg);
263 	return SIM_RC_OK;
264       }
265 
266     case OPTION_HW_LIST:
267       {
268 	sim_hw_print (sd, sim_io_vprintf);
269 	return SIM_RC_OK;
270       }
271 
272     case OPTION_HW_FILE:
273       {
274 	return merge_device_file (sd, arg);
275       }
276 
277     default:
278       sim_io_eprintf (sd, "Unknown hw option %d\n", opt);
279       return SIM_RC_FAIL;
280 
281     }
282 
283   return SIM_RC_FAIL;
284 }
285 
286 
287 /* "hw" module install handler.
288 
289    This is called via sim_module_install to install the "hw" subsystem
290    into the simulator.  */
291 
292 static MODULE_INIT_FN sim_hw_init;
293 static MODULE_UNINSTALL_FN sim_hw_uninstall;
294 
295 /* Provide a prototype to silence -Wmissing-prototypes.  */
296 SIM_RC sim_install_hw (struct sim_state *sd);
297 
298 /* Establish this object.  */
299 SIM_RC
300 sim_install_hw (struct sim_state *sd)
301 {
302   SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
303   sim_add_option_table (sd, NULL, hw_options);
304   sim_module_add_uninstall_fn (sd, sim_hw_uninstall);
305   sim_module_add_init_fn (sd, sim_hw_init);
306   STATE_HW (sd) = ZALLOC (struct sim_hw);
307   STATE_HW (sd)->tree = hw_tree_create (sd, "core");
308   return SIM_RC_OK;
309 }
310 
311 
312 static SIM_RC
313 sim_hw_init (struct sim_state *sd)
314 {
315   /* FIXME: anything needed? */
316   hw_tree_finish (STATE_HW (sd)->tree);
317   if (STATE_HW (sd)->info_p)
318     sim_hw_print (sd, sim_io_vprintf);
319   return SIM_RC_OK;
320 }
321 
322 /* Uninstall the "hw" subsystem from the simulator.  */
323 
324 static void
325 sim_hw_uninstall (struct sim_state *sd)
326 {
327   hw_tree_delete (STATE_HW (sd)->tree);
328   free (STATE_HW (sd));
329   STATE_HW (sd) = NULL;
330 }
331 
332 
333 
334 /* Data transfers to/from the hardware device tree.  There are several
335    cases. */
336 
337 
338 /* CPU: The simulation is running and the current CPU/CIA
339    initiates a data transfer. */
340 
341 void
342 sim_cpu_hw_io_read_buffer (sim_cpu *cpu,
343 			   sim_cia cia,
344 			   struct hw *hw,
345 			   void *dest,
346 			   int space,
347 			   unsigned_word addr,
348 			   unsigned nr_bytes)
349 {
350   SIM_DESC sd = CPU_STATE (cpu);
351   STATE_HW (sd)->cpu = cpu;
352   STATE_HW (sd)->cia = cia;
353   if (hw_io_read_buffer (hw, dest, space, addr, nr_bytes) != nr_bytes)
354     sim_engine_abort (sd, cpu, cia, "broken CPU read");
355 }
356 
357 void
358 sim_cpu_hw_io_write_buffer (sim_cpu *cpu,
359 			    sim_cia cia,
360 			    struct hw *hw,
361 			    const void *source,
362 			    int space,
363 			    unsigned_word addr,
364 			    unsigned nr_bytes)
365 {
366   SIM_DESC sd = CPU_STATE (cpu);
367   STATE_HW (sd)->cpu = cpu;
368   STATE_HW (sd)->cia = cia;
369   if (hw_io_write_buffer (hw, source, space, addr, nr_bytes) != nr_bytes)
370     sim_engine_abort (sd, cpu, cia, "broken CPU write");
371 }
372 
373 
374 
375 
376 /* SYSTEM: A data transfer is being initiated by the system. */
377 
378 unsigned
379 sim_hw_io_read_buffer (struct sim_state *sd,
380 		       struct hw *hw,
381 		       void *dest,
382 		       int space,
383 		       unsigned_word addr,
384 		       unsigned nr_bytes)
385 {
386   STATE_HW (sd)->cpu = NULL;
387   return hw_io_read_buffer (hw, dest, space, addr, nr_bytes);
388 }
389 
390 unsigned
391 sim_hw_io_write_buffer (struct sim_state *sd,
392 			struct hw *hw,
393 			const void *source,
394 			int space,
395 			unsigned_word addr,
396 			unsigned nr_bytes)
397 {
398   STATE_HW (sd)->cpu = NULL;
399   return hw_io_write_buffer (hw, source, space, addr, nr_bytes);
400 }
401 
402 
403 
404 /* Abort the simulation specifying HW as the reason */
405 
406 void
407 hw_vabort (struct hw *me,
408 	   const char *fmt,
409 	   va_list ap)
410 {
411   int len;
412   const char *name;
413   char *msg;
414   va_list cpy;
415 
416   /* find an identity */
417   if (me != NULL && hw_path (me) != NULL && hw_path (me) [0] != '\0')
418     name = hw_path (me);
419   else if (me != NULL && hw_name (me) != NULL && hw_name (me)[0] != '\0')
420     name = hw_name (me);
421   else if (me != NULL && hw_family (me) != NULL && hw_family (me)[0] != '\0')
422     name = hw_family (me);
423   else
424     name = "device";
425 
426   /* Expand FMT and AP into MSG buffer.  */
427   va_copy (cpy, ap);
428   len = vsnprintf (NULL, 0, fmt, cpy) + 1;
429   va_end (cpy);
430   msg = alloca (len);
431   vsnprintf (msg, len, fmt, ap);
432 
433   /* report the problem */
434   sim_engine_abort (hw_system (me),
435 		    STATE_HW (hw_system (me))->cpu,
436 		    STATE_HW (hw_system (me))->cia,
437 		    "%s: %s", name, msg);
438 }
439 
440 void
441 hw_abort (struct hw *me,
442 	  const char *fmt,
443 	  ...)
444 {
445   va_list ap;
446   /* report the problem */
447   va_start (ap, fmt);
448   hw_vabort (me, fmt, ap);
449   va_end (ap);
450 }
451 
452 void
453 sim_hw_abort (struct sim_state *sd,
454 	      struct hw *me,
455 	      const char *fmt,
456 	      ...)
457 {
458   va_list ap;
459   va_start (ap, fmt);
460   if (me == NULL)
461     sim_engine_vabort (sd, NULL, NULL_CIA, fmt, ap);
462   else
463     hw_vabort (me, fmt, ap);
464   va_end (ap);
465 }
466 
467 
468 /* MISC routines to tie HW into the rest of the system */
469 
470 void
471 hw_halt (struct hw *me,
472 	 int reason,
473 	 int status)
474 {
475   struct sim_state *sd = hw_system (me);
476   struct sim_hw *sim = STATE_HW (sd);
477   sim_engine_halt (sd, sim->cpu, NULL, sim->cia, reason, status);
478 }
479 
480 struct _sim_cpu *
481 hw_system_cpu (struct hw *me)
482 {
483   return STATE_HW (hw_system (me))->cpu;
484 }
485 
486 void
487 hw_trace (struct hw *me,
488 	  const char *fmt,
489 	  ...)
490 {
491   if (hw_trace_p (me)) /* to be sure, to be sure */
492     {
493       va_list ap;
494       va_start (ap, fmt);
495       sim_io_eprintf (hw_system (me), "%s: ", hw_path (me));
496       sim_io_evprintf (hw_system (me), fmt, ap);
497       sim_io_eprintf (hw_system (me), "\n");
498       va_end (ap);
499     }
500 }
501 
502 
503 /* Based on gdb-4.17/sim/ppc/main.c:sim_io_read_stdin() */
504 
505 int
506 do_hw_poll_read (struct hw *me,
507 		 do_hw_poll_read_method *read,
508 		 int sim_io_fd,
509 		 void *buf,
510 		 unsigned sizeof_buf)
511 {
512   int status = read (hw_system (me), sim_io_fd, buf, sizeof_buf);
513   if (status > 0)
514     return status;
515   else if (status == 0 && sizeof_buf == 0)
516     return 0;
517   else if (status == 0)
518     return HW_IO_EOF;
519   else /* status < 0 */
520     {
521 #ifdef EAGAIN
522       if (STATE_CALLBACK (hw_system (me))->last_errno == EAGAIN)
523 	return HW_IO_NOT_READY;
524       else
525 	return HW_IO_EOF;
526 #else
527       return HW_IO_EOF;
528 #endif
529     }
530 }
531