summaryrefslogtreecommitdiff
path: root/sim/common/sim-hw.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/common/sim-hw.c')
-rw-r--r--sim/common/sim-hw.c508
1 files changed, 508 insertions, 0 deletions
diff --git a/sim/common/sim-hw.c b/sim/common/sim-hw.c
new file mode 100644
index 00000000000..f438462a9ba
--- /dev/null
+++ b/sim/common/sim-hw.c
@@ -0,0 +1,508 @@
+/* Simulator hardware option handling.
+ Copyright (C) 1998 Free Software Foundation, Inc.
+ Contributed by Cygnus Support and Andrew Cagney.
+
+This file is part of GDB, the GNU debugger.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "sim-main.h"
+#include "sim-assert.h"
+#include "sim-options.h"
+
+#include "sim-hw.h"
+
+#include "hw-tree.h"
+#include "hw-device.h"
+#include "hw-main.h"
+#include "hw-base.h"
+
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#include <ctype.h>
+#include <sys/errno.h>
+
+
+struct sim_hw {
+ struct hw *tree;
+ int trace_p;
+ int info_p;
+ /* if called from a processor */
+ sim_cpu *cpu;
+ sim_cia cia;
+};
+
+
+struct hw *
+sim_hw_parse (struct sim_state *sd,
+ const char *fmt,
+ ...)
+{
+ struct hw *current;
+ va_list ap;
+ va_start (ap, fmt);
+ current = hw_tree_vparse (STATE_HW (sd)->tree, fmt, ap);
+ va_end (ap);
+ return current;
+}
+
+struct printer {
+ struct sim_state *file;
+ void (*print) (struct sim_state *, const char *, va_list ap);
+};
+
+static void
+do_print (void *file, const char *fmt, ...)
+{
+ struct printer *p = file;
+ va_list ap;
+ va_start (ap, fmt);
+ p->print (p->file, fmt, ap);
+ va_end (ap);
+}
+
+void
+sim_hw_print (struct sim_state *sd,
+ void (*print) (struct sim_state *, const char *, va_list ap))
+{
+ struct printer p;
+ p.file = sd;
+ p.print = print;
+ hw_tree_print (STATE_HW (sd)->tree, do_print, &p);
+}
+
+
+
+
+/* command line options. */
+
+enum {
+ OPTION_HW_INFO = OPTION_START,
+ OPTION_HW_TRACE,
+ OPTION_HW_DEVICE,
+ OPTION_HW_FILE,
+};
+
+static DECLARE_OPTION_HANDLER (hw_option_handler);
+
+static const OPTION hw_options[] =
+{
+ { {"hw-info", no_argument, NULL, OPTION_HW_INFO },
+ '\0', NULL, "List configurable hw regions",
+ hw_option_handler },
+ { {"info-hw", no_argument, NULL, OPTION_HW_INFO },
+ '\0', NULL, NULL,
+ hw_option_handler },
+
+ { {"hw-trace", optional_argument, NULL, OPTION_HW_TRACE },
+ '\0', "on|off", "Trace all hardware devices",
+ hw_option_handler },
+ { {"trace-hw", optional_argument, NULL, OPTION_HW_TRACE },
+ '\0', NULL, NULL,
+ hw_option_handler },
+
+ { {"hw-device", required_argument, NULL, OPTION_HW_DEVICE },
+ '\0', "DEVICE", "Add the specified device",
+ hw_option_handler },
+
+ { {"hw-file", required_argument, NULL, OPTION_HW_FILE },
+ '\0', "FILE", "Add the devices listed in the file",
+ hw_option_handler },
+
+ { {NULL, no_argument, NULL, 0}, '\0', NULL, NULL, NULL }
+};
+
+
+
+/* Copied from ../ppc/psim.c:psim_merge_device_file() */
+
+static SIM_RC
+merge_device_file (struct sim_state *sd,
+ const char *file_name)
+{
+ FILE *description;
+ struct hw *current = STATE_HW (sd)->tree;
+ int line_nr;
+ char device_path[1000];
+
+ /* try opening the file */
+ description = fopen (file_name, "r");
+ if (description == NULL)
+ {
+ perror (file_name);
+ return SIM_RC_FAIL;
+ }
+
+ line_nr = 0;
+ while (fgets (device_path, sizeof(device_path), description))
+ {
+ char *device;
+ /* check that a complete line was read */
+ if (strchr (device_path, '\n') == NULL)
+ {
+ fclose (description);
+ sim_io_eprintf (sd, "%s:%d: line to long", file_name, line_nr);
+ return SIM_RC_FAIL;
+ }
+ *strchr (device_path, '\n') = '\0';
+ line_nr++;
+ /* skip comments ("#" or ";") and blank lines lines */
+ for (device = device_path;
+ *device != '\0' && isspace (*device);
+ device++);
+ if (device[0] == '#'
+ || device[0] == ';'
+ || device[0] == '\0')
+ continue;
+ /* merge any appended lines */
+ while (device_path[strlen (device_path) - 1] == '\\')
+ {
+ int curlen = strlen (device_path) - 1;
+ /* zap the `\' at the end of the line */
+ device_path[curlen] = '\0';
+ /* append the next line */
+ if (!fgets (device_path + curlen,
+ sizeof (device_path) - curlen,
+ description))
+ {
+ fclose (description);
+ sim_io_eprintf (sd, "%s:%d: unexpected eof", file_name, line_nr);
+ return SIM_RC_FAIL;
+ }
+ if (strchr(device_path, '\n') == NULL)
+ {
+ fclose(description);
+ sim_io_eprintf (sd, "%s:%d: line to long", file_name, line_nr);
+ return SIM_RC_FAIL;
+ }
+ *strchr(device_path, '\n') = '\0';
+ line_nr++;
+ }
+ /* parse this line */
+ current = hw_tree_parse (current, "%s", device);
+ }
+ fclose (description);
+ return SIM_RC_OK;
+}
+
+
+static SIM_RC
+hw_option_handler (struct sim_state *sd, sim_cpu *cpu, int opt,
+ char *arg, int is_command)
+{
+ switch (opt)
+ {
+
+ case OPTION_HW_INFO:
+ {
+ /* delay info until after the tree is finished */
+ STATE_HW (sd)->info_p = 1;
+ return SIM_RC_OK;
+ break;
+ }
+
+ case OPTION_HW_TRACE:
+ {
+ if (arg == NULL)
+ {
+ STATE_HW (sd)->trace_p = 1;
+ }
+ else if (strcmp (arg, "yes") == 0
+ || strcmp (arg, "on") == 0)
+ {
+ STATE_HW (sd)->trace_p = 1;
+ }
+ else if (strcmp (arg, "no") == 0
+ || strcmp (arg, "off") == 0)
+ {
+ STATE_HW (sd)->trace_p = 0;
+ }
+ else
+ {
+ sim_io_eprintf (sd, "Option --hw-trace ignored\n");
+ /* set tracing on all devices */
+ return SIM_RC_FAIL;
+ }
+ /* FIXME: Not very nice - see also hw-base.c */
+ if (STATE_HW (sd)->trace_p)
+ hw_tree_parse (STATE_HW (sd)->tree, "/global-trace? true");
+ return SIM_RC_OK;
+ break;
+ }
+
+ case OPTION_HW_DEVICE:
+ {
+ hw_tree_parse (STATE_HW (sd)->tree, arg);
+ return SIM_RC_OK;
+ }
+
+ case OPTION_HW_FILE:
+ {
+ return merge_device_file (sd, arg);
+ }
+
+ default:
+ sim_io_eprintf (sd, "Unknown hw option %d\n", opt);
+ return SIM_RC_FAIL;
+
+ }
+
+ return SIM_RC_FAIL;
+}
+
+
+/* "hw" module install handler.
+
+ This is called via sim_module_install to install the "hw" subsystem
+ into the simulator. */
+
+static MODULE_INIT_FN sim_hw_init;
+static MODULE_UNINSTALL_FN sim_hw_uninstall;
+
+SIM_RC
+sim_hw_install (struct sim_state *sd)
+{
+ SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
+ sim_add_option_table (sd, NULL, hw_options);
+ sim_module_add_uninstall_fn (sd, sim_hw_uninstall);
+ sim_module_add_init_fn (sd, sim_hw_init);
+ STATE_HW (sd) = ZALLOC (struct sim_hw);
+ STATE_HW (sd)->tree = hw_tree_create (sd, "core");
+ return SIM_RC_OK;
+}
+
+
+static SIM_RC
+sim_hw_init (struct sim_state *sd)
+{
+ /* FIXME: anything needed? */
+ hw_tree_finish (STATE_HW (sd)->tree);
+ if (STATE_HW (sd)->info_p)
+ sim_hw_print (sd, sim_io_vprintf);
+ return SIM_RC_OK;
+}
+
+/* Uninstall the "hw" subsystem from the simulator. */
+
+static void
+sim_hw_uninstall (struct sim_state *sd)
+{
+ /* hw_tree_delete (STATE_HW (sd)->tree); */
+ zfree (STATE_HW (sd));
+ STATE_HW (sd) = NULL;
+}
+
+
+
+/* Data transfers to/from the hardware device tree. There are several
+ cases. */
+
+
+/* CPU: The simulation is running and the current CPU/CIA
+ initiates a data transfer. */
+
+void
+sim_cpu_hw_io_read_buffer (sim_cpu *cpu,
+ sim_cia cia,
+ struct hw *hw,
+ void *dest,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes)
+{
+ SIM_DESC sd = CPU_STATE (cpu);
+ STATE_HW (sd)->cpu = cpu;
+ STATE_HW (sd)->cia = cia;
+ if (hw_io_read_buffer (hw, dest, space, addr, nr_bytes) != nr_bytes)
+ sim_engine_abort (sd, cpu, cia, "broken CPU read");
+}
+
+void
+sim_cpu_hw_io_write_buffer (sim_cpu *cpu,
+ sim_cia cia,
+ struct hw *hw,
+ const void *source,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes)
+{
+ SIM_DESC sd = CPU_STATE (cpu);
+ STATE_HW (sd)->cpu = cpu;
+ STATE_HW (sd)->cia = cia;
+ if (hw_io_write_buffer (hw, source, space, addr, nr_bytes) != nr_bytes)
+ sim_engine_abort (sd, cpu, cia, "broken CPU write");
+}
+
+
+
+
+/* SYSTEM: A data transfer is being initiated by the system. */
+
+unsigned
+sim_hw_io_read_buffer (struct sim_state *sd,
+ struct hw *hw,
+ void *dest,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes)
+{
+ STATE_HW (sd)->cpu = NULL;
+ return hw_io_read_buffer (hw, dest, space, addr, nr_bytes);
+}
+
+unsigned
+sim_hw_io_write_buffer (struct sim_state *sd,
+ struct hw *hw,
+ const void *source,
+ int space,
+ unsigned_word addr,
+ unsigned nr_bytes)
+{
+ STATE_HW (sd)->cpu = NULL;
+ return hw_io_write_buffer (hw, source, space, addr, nr_bytes);
+}
+
+
+
+/* Abort the simulation specifying HW as the reason */
+
+void
+hw_vabort (struct hw *me,
+ const char *fmt,
+ va_list ap)
+{
+ const char *name;
+ char *msg;
+ /* find an identity */
+ if (me != NULL && hw_path (me) != NULL && hw_path (me) [0] != '\0')
+ name = hw_path (me);
+ else if (me != NULL && hw_name (me) != NULL && hw_name (me)[0] != '\0')
+ name = hw_name (me);
+ else if (me != NULL && hw_family (me) != NULL && hw_family (me)[0] != '\0')
+ name = hw_family (me);
+ else
+ name = "device";
+ /* construct an updated format string */
+ msg = alloca (strlen (name) + strlen (": ") + strlen (fmt) + 1);
+ strcpy (msg, name);
+ strcat (msg, ": ");
+ strcat (msg, fmt);
+ /* report the problem */
+ sim_engine_vabort (hw_system (me),
+ STATE_HW (hw_system (me))->cpu,
+ STATE_HW (hw_system (me))->cia,
+ msg, ap);
+}
+
+void
+hw_abort (struct hw *me,
+ const char *fmt,
+ ...)
+{
+ va_list ap;
+ /* report the problem */
+ va_start (ap, fmt);
+ hw_vabort (me, fmt, ap);
+ va_end (ap);
+}
+
+void
+sim_hw_abort (struct sim_state *sd,
+ struct hw *me,
+ const char *fmt,
+ ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ if (me == NULL)
+ sim_engine_vabort (sd, NULL, NULL_CIA, fmt, ap);
+ else
+ hw_vabort (me, fmt, ap);
+ va_end (ap);
+}
+
+
+/* MISC routines to tie HW into the rest of the system */
+
+void
+hw_halt (struct hw *me,
+ int reason,
+ int status)
+{
+ struct sim_state *sd = hw_system (me);
+ struct sim_hw *sim = STATE_HW (sd);
+ sim_engine_halt (sd, sim->cpu, NULL, sim->cia, reason, status);
+}
+
+struct _sim_cpu *
+hw_system_cpu (struct hw *me)
+{
+ return STATE_HW (hw_system (me))->cpu;
+}
+
+void
+hw_trace (struct hw *me,
+ const char *fmt,
+ ...)
+{
+ if (hw_trace_p (me)) /* to be sure, to be sure */
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ sim_io_eprintf (hw_system (me), "%s: ", hw_path (me));
+ sim_io_evprintf (hw_system (me), fmt, ap);
+ sim_io_eprintf (hw_system (me), "\n");
+ va_end (ap);
+ }
+}
+
+
+/* Based on gdb-4.17/sim/ppc/main.c:sim_io_read_stdin() */
+
+int
+do_hw_poll_read (struct hw *me,
+ do_hw_poll_read_method *read,
+ int sim_io_fd,
+ void *buf,
+ unsigned sizeof_buf)
+{
+ int status = read (hw_system (me), sim_io_fd, buf, sizeof_buf);
+ if (status > 0)
+ return status;
+ else if (status == 0 && sizeof_buf == 0)
+ return 0;
+ else if (status == 0)
+ return HW_IO_EOF;
+ else /* status < 0 */
+ {
+#ifdef EAGAIN
+ if (STATE_CALLBACK (hw_system (me))->last_errno == EAGAIN)
+ return HW_IO_NOT_READY;
+ else
+ return HW_IO_EOF;
+#else
+ return HW_IO_EOF;
+#endif
+ }
+}