74814920feff78476d005d16059a1ed946936680
[platform/upstream/binutils.git] / sim / common / sim-hw.c
1 /* Simulator hardware option handling.
2    Copyright (C) 1998, 2007, 2008, 2009, 2010 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 #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
46 struct 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
56 struct hw *
57 sim_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
69 struct printer {
70   struct sim_state *file;
71   void (*print) (struct sim_state *, const char *, va_list ap);
72 };
73
74 static void
75 do_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
84 void
85 sim_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
99 enum {
100   OPTION_HW_INFO = OPTION_START,
101   OPTION_HW_TRACE,
102   OPTION_HW_DEVICE,
103   OPTION_HW_LIST,
104   OPTION_HW_FILE,
105 };
106
107 static DECLARE_OPTION_HANDLER (hw_option_handler);
108
109 static 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
144 static SIM_RC
145 merge_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
214 static SIM_RC
215 hw_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, "%s", 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
290 static MODULE_INIT_FN sim_hw_init;
291 static MODULE_UNINSTALL_FN sim_hw_uninstall;
292
293 SIM_RC
294 sim_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
306 static SIM_RC
307 sim_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
318 static void
319 sim_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 \f
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
335 void 
336 sim_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
351 void 
352 sim_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
372 unsigned 
373 sim_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
384 unsigned
385 sim_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 \f
398 /* Abort the simulation specifying HW as the reason */
399
400 void
401 hw_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
428 void
429 hw_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
440 void
441 sim_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
458 void
459 hw_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
468 struct _sim_cpu *
469 hw_system_cpu (struct hw *me)
470 {
471   return STATE_HW (hw_system (me))->cpu;
472 }
473
474 void
475 hw_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
493 int
494 do_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 }