diff options
author | Andrew Cagney <cagney@redhat.com> | 1998-05-21 08:17:31 +0000 |
---|---|---|
committer | Andrew Cagney <cagney@redhat.com> | 1998-05-21 08:17:31 +0000 |
commit | 643878d01705dbc37908583984ab14da155a8b28 (patch) | |
tree | 999f66b78bae2631023aeb0d882345c0161de76d /sim/common/sim-hw.c | |
parent | 78af0a0094aee006d64d8dfae00e276a940864fd (diff) | |
download | gdb-643878d01705dbc37908583984ab14da155a8b28.zip gdb-643878d01705dbc37908583984ab14da155a8b28.tar.gz gdb-643878d01705dbc37908583984ab14da155a8b28.tar.bz2 |
* sim-hw.c: Include ctype.h.
(do_hw_poll_read): Do not assume EAGAIN.
Diffstat (limited to 'sim/common/sim-hw.c')
-rw-r--r-- | sim/common/sim-hw.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/sim/common/sim-hw.c b/sim/common/sim-hw.c new file mode 100644 index 0000000..51b7b0f --- /dev/null +++ b/sim/common/sim-hw.c @@ -0,0 +1,564 @@ +/* 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-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; +}; + + +void +sim_hw_parse (struct sim_state *sd, + const char *fmt, + ...) +{ + va_list ap; + va_start (ap, fmt); + hw_tree_vparse (STATE_HW (sd)->tree, fmt, ap); + va_end (ap); +} + +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 + } +} + + +/* The event queue abstraction (for devices) */ + + +struct _hw_event { + void *data; + struct hw *me; + hw_event_handler *handler; + sim_event *real; +}; + +/* Pass the H/W event onto the real handler */ + +static void +bounce_hw_event (SIM_DESC sd, + void *data) +{ + hw_event event = * (hw_event*) data; + zfree (data); + /* if we are delivering an event, we don't have a CPU. */ + STATE_HW (sd)->cpu = NULL; + event.handler (event.me, event.data); +} + + +/* Map onto the event functions */ + +hw_event * +hw_event_queue_schedule (struct hw *me, + signed64 delta_time, + hw_event_handler *handler, + void *data) +{ + hw_event *event = ZALLOC (hw_event); + event->data = data; + event->handler = handler; + event->me = me; + event->real = sim_events_schedule (hw_system (me), + delta_time, + bounce_hw_event, + event); + return event; +} + +void +hw_event_queue_deschedule (struct hw *me, + hw_event *event_to_remove) +{ + sim_events_deschedule (hw_system (me), + event_to_remove->real); + zfree (event_to_remove); +} + +signed64 +hw_event_queue_time (struct hw *me) +{ + return sim_events_time (hw_system (me)); +} |