diff options
Diffstat (limited to 'sim/common/sim-hw.c')
-rw-r--r-- | sim/common/sim-hw.c | 508 |
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 0000000..f438462 --- /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 + } +} |