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