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