mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-02 23:04:09 +08:00
1d506c26d9
This commit is the result of the following actions: - Running gdb/copyright.py to update all of the copyright headers to include 2024, - Manually updating a few files the copyright.py script told me to update, these files had copyright headers embedded within the file, - Regenerating gdbsupport/Makefile.in to refresh it's copyright date, - Using grep to find other files that still mentioned 2023. If these files were updated last year from 2022 to 2023 then I've updated them this year to 2024. I'm sure I've probably missed some dates. Feel free to fix them up as you spot them.
1259 lines
30 KiB
C
1259 lines
30 KiB
C
/* The common simulator framework for GDB, the GNU Debugger.
|
|
|
|
Copyright 2002-2024 Free Software Foundation, Inc.
|
|
|
|
Contributed by Andrew Cagney and Red Hat.
|
|
|
|
This file is part of GDB.
|
|
|
|
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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
#ifndef _SIM_EVENTS_C_
|
|
#define _SIM_EVENTS_C_
|
|
|
|
/* This must come before any other includes. */
|
|
#include "defs.h"
|
|
|
|
#include <signal.h> /* For SIGPROCMASK et al. */
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "libiberty.h"
|
|
|
|
#include "sim-main.h"
|
|
#include "sim-assert.h"
|
|
#include "sim-cpu.h"
|
|
|
|
typedef enum {
|
|
watch_invalid,
|
|
|
|
/* core - target byte order */
|
|
watch_core_targ_1,
|
|
watch_core_targ_2,
|
|
watch_core_targ_4,
|
|
watch_core_targ_8,
|
|
/* core - big-endian */
|
|
watch_core_be_1,
|
|
watch_core_be_2,
|
|
watch_core_be_4,
|
|
watch_core_be_8,
|
|
/* core - little-endian */
|
|
watch_core_le_1,
|
|
watch_core_le_2,
|
|
watch_core_le_4,
|
|
watch_core_le_8,
|
|
|
|
/* sim - host byte order */
|
|
watch_sim_host_1,
|
|
watch_sim_host_2,
|
|
watch_sim_host_4,
|
|
watch_sim_host_8,
|
|
/* sim - big-endian */
|
|
watch_sim_be_1,
|
|
watch_sim_be_2,
|
|
watch_sim_be_4,
|
|
watch_sim_be_8,
|
|
/* sim - little-endian */
|
|
watch_sim_le_1,
|
|
watch_sim_le_2,
|
|
watch_sim_le_4,
|
|
watch_sim_le_8,
|
|
|
|
/* pc */
|
|
watch_pc,
|
|
|
|
/* wallclock */
|
|
watch_clock,
|
|
|
|
/* timer */
|
|
watch_timer,
|
|
} sim_event_watchpoints;
|
|
|
|
|
|
struct _sim_event {
|
|
sim_event_watchpoints watching;
|
|
void *data;
|
|
sim_event_handler *handler;
|
|
/* timer event */
|
|
int64_t time_of_event;
|
|
/* watch wallclock event */
|
|
unsigned wallclock;
|
|
/* watch core address */
|
|
address_word core_addr;
|
|
unsigned core_map;
|
|
/* watch sim addr */
|
|
void *host_addr;
|
|
/* watch core/sim range */
|
|
int is_within; /* 0/1 */
|
|
unsigned ub;
|
|
unsigned lb;
|
|
uint64_t ub64;
|
|
uint64_t lb64;
|
|
/* trace info (if any) */
|
|
char *trace;
|
|
/* list */
|
|
sim_event *next;
|
|
};
|
|
|
|
|
|
/* The event queue maintains a single absolute time using two
|
|
variables.
|
|
|
|
TIME_OF_EVENT: this holds the time at which the next event is ment
|
|
to occur. If no next event it will hold the time of the last
|
|
event.
|
|
|
|
TIME_FROM_EVENT: The current distance from TIME_OF_EVENT. A value
|
|
<= 0 (except when poll-event is being processed) indicates that
|
|
event processing is due. This variable is decremented once for
|
|
each iteration of a clock cycle.
|
|
|
|
Initially, the clock is started at time one (0) with TIME_OF_EVENT
|
|
== 0 and TIME_FROM_EVENT == 0 and with NR_TICKS_TO_PROCESS == 1.
|
|
|
|
Clearly there is a bug in that this code assumes that the absolute
|
|
time counter will never become greater than 2^62.
|
|
|
|
To avoid the need to use 64bit arithmetic, the event queue always
|
|
contains at least one event scheduled every 16 000 ticks. This
|
|
limits the time from event counter to values less than
|
|
16 000. */
|
|
|
|
|
|
#if !defined (SIM_EVENTS_POLL_RATE)
|
|
#define SIM_EVENTS_POLL_RATE 0x1000
|
|
#endif
|
|
|
|
|
|
#define _ETRACE sd, NULL
|
|
|
|
#undef ETRACE
|
|
#define ETRACE(ARGS) \
|
|
do \
|
|
{ \
|
|
if (STRACE_EVENTS_P (sd)) \
|
|
{ \
|
|
if (STRACE_DEBUG_P (sd)) \
|
|
trace_printf (sd, NULL, "%s:%d: ", lbasename (__FILE__), __LINE__); \
|
|
trace_printf ARGS; \
|
|
} \
|
|
} \
|
|
while (0)
|
|
|
|
|
|
/* event queue iterator - don't iterate over the held queue. */
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
static sim_event **
|
|
next_event_queue (SIM_DESC sd,
|
|
sim_event **queue)
|
|
{
|
|
if (queue == NULL)
|
|
return &STATE_EVENTS (sd)->queue;
|
|
else if (queue == &STATE_EVENTS (sd)->queue)
|
|
return &STATE_EVENTS (sd)->watchpoints;
|
|
else if (queue == &STATE_EVENTS (sd)->watchpoints)
|
|
return &STATE_EVENTS (sd)->watchedpoints;
|
|
else if (queue == &STATE_EVENTS (sd)->watchedpoints)
|
|
return NULL;
|
|
else
|
|
sim_io_error (sd, "next_event_queue - bad queue");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
STATIC_INLINE_SIM_EVENTS\
|
|
(void)
|
|
sim_events_poll (SIM_DESC sd,
|
|
void *data)
|
|
{
|
|
/* just re-schedule in 1000 million ticks time */
|
|
sim_events_schedule (sd, SIM_EVENTS_POLL_RATE, sim_events_poll, sd);
|
|
sim_io_poll_quit (sd);
|
|
}
|
|
|
|
|
|
/* "events" module install handler.
|
|
This is called via sim_module_install to install the "events" subsystem
|
|
into the simulator. */
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
STATIC_SIM_EVENTS (MODULE_UNINSTALL_FN) sim_events_uninstall;
|
|
STATIC_SIM_EVENTS (MODULE_INIT_FN) sim_events_init;
|
|
STATIC_SIM_EVENTS (MODULE_RESUME_FN) sim_events_resume;
|
|
STATIC_SIM_EVENTS (MODULE_SUSPEND_FN) sim_events_suspend;
|
|
#endif
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
SIM_RC
|
|
sim_events_install (SIM_DESC sd)
|
|
{
|
|
SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
|
|
sim_module_add_uninstall_fn (sd, sim_events_uninstall);
|
|
sim_module_add_init_fn (sd, sim_events_init);
|
|
sim_module_add_resume_fn (sd, sim_events_resume);
|
|
sim_module_add_suspend_fn (sd, sim_events_suspend);
|
|
return SIM_RC_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Suspend/resume the event queue manager when the simulator is not
|
|
running */
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
static SIM_RC
|
|
sim_events_resume (SIM_DESC sd)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
|
|
SIM_ASSERT (events->resume_wallclock == 0);
|
|
events->resume_wallclock = sim_elapsed_time_get ();
|
|
return SIM_RC_OK;
|
|
}
|
|
#endif
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
static SIM_RC
|
|
sim_events_suspend (SIM_DESC sd)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
|
|
SIM_ASSERT (events->resume_wallclock != 0);
|
|
events->elapsed_wallclock += sim_elapsed_time_since (events->resume_wallclock);
|
|
events->resume_wallclock = 0;
|
|
return SIM_RC_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Uninstall the "events" subsystem from the simulator. */
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
static void
|
|
sim_events_uninstall (SIM_DESC sd)
|
|
{
|
|
SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
|
|
/* FIXME: free buffers, etc. */
|
|
}
|
|
#endif
|
|
|
|
|
|
/* malloc/free */
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
static sim_event *
|
|
sim_events_zalloc (SIM_DESC sd)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
sim_event *new = events->free_list;
|
|
if (new != NULL)
|
|
{
|
|
events->free_list = new->next;
|
|
memset (new, 0, sizeof (*new));
|
|
}
|
|
else
|
|
{
|
|
#if defined (HAVE_SIGPROCMASK) && defined (SIG_SETMASK)
|
|
/*-LOCK-*/
|
|
sigset_t old_mask;
|
|
sigset_t new_mask;
|
|
sigfillset (&new_mask);
|
|
sigprocmask (SIG_SETMASK, &new_mask, &old_mask);
|
|
#endif
|
|
new = ZALLOC (sim_event);
|
|
#if defined (HAVE_SIGPROCMASK) && defined (SIG_SETMASK)
|
|
/*-UNLOCK-*/
|
|
sigprocmask (SIG_SETMASK, &old_mask, NULL);
|
|
#endif
|
|
}
|
|
return new;
|
|
}
|
|
#endif
|
|
|
|
STATIC_INLINE_SIM_EVENTS\
|
|
(void)
|
|
sim_events_free (SIM_DESC sd,
|
|
sim_event *dead)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
dead->next = events->free_list;
|
|
events->free_list = dead;
|
|
if (dead->trace != NULL)
|
|
{
|
|
free (dead->trace); /* NB: asprintf returns a `free' buf */
|
|
dead->trace = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/* Initialize the simulator event manager */
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
SIM_RC
|
|
sim_events_init (SIM_DESC sd)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
|
|
/* drain the interrupt queue */
|
|
events->nr_held = 0;
|
|
if (events->held == NULL)
|
|
events->held = NZALLOC (sim_event, MAX_NR_SIGNAL_SIM_EVENTS);
|
|
|
|
/* drain the normal queues */
|
|
{
|
|
sim_event **queue = NULL;
|
|
while ((queue = next_event_queue (sd, queue)) != NULL)
|
|
{
|
|
if (queue == NULL) break;
|
|
while (*queue != NULL)
|
|
{
|
|
sim_event *dead = *queue;
|
|
*queue = dead->next;
|
|
sim_events_free (sd, dead);
|
|
}
|
|
*queue = NULL;
|
|
}
|
|
}
|
|
|
|
/* wind time back to zero */
|
|
events->nr_ticks_to_process = 1; /* start by doing queue */
|
|
events->time_of_event = 0;
|
|
events->time_from_event = 0;
|
|
events->elapsed_wallclock = 0;
|
|
events->resume_wallclock = 0;
|
|
|
|
/* schedule our initial counter event */
|
|
sim_events_schedule (sd, 0, sim_events_poll, sd);
|
|
|
|
/* from now on, except when the large-int event is being processed
|
|
the event queue is non empty */
|
|
SIM_ASSERT (events->queue != NULL);
|
|
|
|
return SIM_RC_OK;
|
|
}
|
|
#endif
|
|
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(int64_t)
|
|
sim_events_time (SIM_DESC sd)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
return (events->time_of_event - events->time_from_event);
|
|
}
|
|
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(unsigned long)
|
|
sim_events_elapsed_time (SIM_DESC sd)
|
|
{
|
|
unsigned long elapsed = STATE_EVENTS (sd)->elapsed_wallclock;
|
|
|
|
/* Are we being called inside sim_resume?
|
|
(Is there a simulation in progress?) */
|
|
if (STATE_EVENTS (sd)->resume_wallclock != 0)
|
|
elapsed += sim_elapsed_time_since (STATE_EVENTS (sd)->resume_wallclock);
|
|
|
|
return elapsed;
|
|
}
|
|
|
|
|
|
/* Returns the time that remains before the event is raised. */
|
|
INLINE_SIM_EVENTS\
|
|
(int64_t)
|
|
sim_events_remain_time (SIM_DESC sd, sim_event *event)
|
|
{
|
|
if (event == 0)
|
|
return 0;
|
|
|
|
return (event->time_of_event - sim_events_time (sd));
|
|
}
|
|
|
|
|
|
|
|
STATIC_INLINE_SIM_EVENTS\
|
|
(void)
|
|
update_time_from_event (SIM_DESC sd)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
int64_t current_time = sim_events_time (sd);
|
|
if (events->queue != NULL)
|
|
{
|
|
events->time_of_event = events->queue->time_of_event;
|
|
events->time_from_event = (events->queue->time_of_event - current_time);
|
|
}
|
|
else
|
|
{
|
|
events->time_of_event = current_time - 1;
|
|
events->time_from_event = -1;
|
|
}
|
|
if (STRACE_EVENTS_P (sd))
|
|
{
|
|
sim_event *event;
|
|
int i;
|
|
for (event = events->queue, i = 0;
|
|
event != NULL;
|
|
event = event->next, i++)
|
|
{
|
|
ETRACE ((_ETRACE,
|
|
"event time-from-event - "
|
|
"time %" PRIi64 ", delta %" PRIi64 " - "
|
|
"event %i, tag %p, time %" PRIi64 ", handler %p, data "
|
|
"%p%s%s\n",
|
|
current_time,
|
|
events->time_from_event,
|
|
i,
|
|
event,
|
|
event->time_of_event,
|
|
event->handler,
|
|
event->data,
|
|
(event->trace != NULL) ? ", " : "",
|
|
(event->trace != NULL) ? event->trace : ""));
|
|
}
|
|
}
|
|
SIM_ASSERT (current_time == sim_events_time (sd));
|
|
}
|
|
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
static void
|
|
insert_sim_event (SIM_DESC sd,
|
|
sim_event *new_event,
|
|
int64_t delta)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
sim_event *curr;
|
|
sim_event **prev;
|
|
int64_t time_of_event;
|
|
|
|
if (delta < 0)
|
|
sim_io_error (sd, "what is past is past!\n");
|
|
|
|
/* compute when the event should occur */
|
|
time_of_event = sim_events_time (sd) + delta;
|
|
|
|
/* find the queue insertion point - things are time ordered */
|
|
prev = &events->queue;
|
|
curr = events->queue;
|
|
while (curr != NULL && time_of_event >= curr->time_of_event)
|
|
{
|
|
SIM_ASSERT (curr->next == NULL
|
|
|| curr->time_of_event <= curr->next->time_of_event);
|
|
prev = &curr->next;
|
|
curr = curr->next;
|
|
}
|
|
SIM_ASSERT (curr == NULL || time_of_event < curr->time_of_event);
|
|
|
|
/* insert it */
|
|
new_event->next = curr;
|
|
*prev = new_event;
|
|
new_event->time_of_event = time_of_event;
|
|
|
|
/* adjust the time until the first event */
|
|
update_time_from_event (sd);
|
|
}
|
|
#endif
|
|
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
sim_event *
|
|
sim_events_schedule (SIM_DESC sd,
|
|
int64_t delta_time,
|
|
sim_event_handler *handler,
|
|
void *data)
|
|
{
|
|
return sim_events_schedule_tracef (sd, delta_time, handler, data, NULL);
|
|
}
|
|
#endif
|
|
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
sim_event *
|
|
sim_events_schedule_tracef (SIM_DESC sd,
|
|
int64_t delta_time,
|
|
sim_event_handler *handler,
|
|
void *data,
|
|
const char *fmt,
|
|
...)
|
|
{
|
|
sim_event *new_event;
|
|
va_list ap;
|
|
va_start (ap, fmt);
|
|
new_event = sim_events_schedule_vtracef (sd, delta_time, handler, data, fmt, ap);
|
|
va_end (ap);
|
|
return new_event;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
sim_event *
|
|
sim_events_schedule_vtracef (SIM_DESC sd,
|
|
int64_t delta_time,
|
|
sim_event_handler *handler,
|
|
void *data,
|
|
const char *fmt,
|
|
va_list ap)
|
|
{
|
|
sim_event *new_event = sim_events_zalloc (sd);
|
|
new_event->data = data;
|
|
new_event->handler = handler;
|
|
new_event->watching = watch_timer;
|
|
if (fmt == NULL || !STRACE_EVENTS_P (sd) || vasprintf (&new_event->trace, fmt, ap) < 0)
|
|
new_event->trace = NULL;
|
|
insert_sim_event (sd, new_event, delta_time);
|
|
ETRACE ((_ETRACE,
|
|
"event scheduled at %" PRIi64 " - "
|
|
"tag %p - time %" PRIi64 ", handler %p, data %p%s%s\n",
|
|
sim_events_time (sd),
|
|
new_event,
|
|
new_event->time_of_event,
|
|
new_event->handler,
|
|
new_event->data,
|
|
(new_event->trace != NULL) ? ", " : "",
|
|
(new_event->trace != NULL) ? new_event->trace : ""));
|
|
return new_event;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
void
|
|
sim_events_schedule_after_signal (SIM_DESC sd,
|
|
int64_t delta_time,
|
|
sim_event_handler *handler,
|
|
void *data)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
sim_event *new_event;
|
|
#if defined (HAVE_SIGPROCMASK) && defined (SIG_SETMASK)
|
|
/*-LOCK-*/
|
|
sigset_t old_mask;
|
|
sigset_t new_mask;
|
|
sigfillset (&new_mask);
|
|
sigprocmask (SIG_SETMASK, &new_mask, &old_mask);
|
|
#endif
|
|
|
|
/* allocate an event entry from the signal buffer */
|
|
new_event = &events->held [events->nr_held];
|
|
events->nr_held ++;
|
|
if (events->nr_held > MAX_NR_SIGNAL_SIM_EVENTS)
|
|
{
|
|
sim_engine_abort (NULL, NULL, NULL_CIA,
|
|
"sim_events_schedule_after_signal - buffer overflow");
|
|
}
|
|
|
|
new_event->data = data;
|
|
new_event->handler = handler;
|
|
new_event->time_of_event = delta_time; /* work it out later */
|
|
new_event->next = NULL;
|
|
|
|
events->work_pending = 1; /* notify main process */
|
|
|
|
#if defined (HAVE_SIGPROCMASK) && defined (SIG_SETMASK)
|
|
/*-UNLOCK-*/
|
|
sigprocmask (SIG_SETMASK, &old_mask, NULL);
|
|
#endif
|
|
|
|
ETRACE ((_ETRACE,
|
|
"signal scheduled at %" PRIi64 " - "
|
|
"tag %p - time %" PRIi64 ", handler %p, data %p\n",
|
|
sim_events_time (sd),
|
|
new_event,
|
|
new_event->time_of_event,
|
|
new_event->handler,
|
|
new_event->data));
|
|
}
|
|
#endif
|
|
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
sim_event *
|
|
sim_events_watch_clock (SIM_DESC sd,
|
|
unsigned delta_ms_time,
|
|
sim_event_handler *handler,
|
|
void *data)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
sim_event *new_event = sim_events_zalloc (sd);
|
|
/* type */
|
|
new_event->watching = watch_clock;
|
|
/* handler */
|
|
new_event->data = data;
|
|
new_event->handler = handler;
|
|
/* data */
|
|
if (events->resume_wallclock == 0)
|
|
new_event->wallclock = (events->elapsed_wallclock + delta_ms_time);
|
|
else
|
|
new_event->wallclock = (events->elapsed_wallclock
|
|
+ sim_elapsed_time_since (events->resume_wallclock)
|
|
+ delta_ms_time);
|
|
/* insert */
|
|
new_event->next = events->watchpoints;
|
|
events->watchpoints = new_event;
|
|
events->work_pending = 1;
|
|
ETRACE ((_ETRACE,
|
|
"event watching clock at %" PRIi64 " - "
|
|
"tag %p - wallclock %u, handler %p, data %p\n",
|
|
sim_events_time (sd),
|
|
new_event,
|
|
new_event->wallclock,
|
|
new_event->handler,
|
|
new_event->data));
|
|
return new_event;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
sim_event *
|
|
sim_events_watch_pc (SIM_DESC sd,
|
|
int is_within,
|
|
uint64_t lb,
|
|
uint64_t ub,
|
|
sim_event_handler *handler,
|
|
void *data)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
sim_event *new_event = sim_events_zalloc (sd);
|
|
/* type */
|
|
new_event->watching = watch_pc;
|
|
/* handler */
|
|
new_event->data = data;
|
|
new_event->handler = handler;
|
|
/* data */
|
|
new_event->lb = lb;
|
|
new_event->lb64 = lb;
|
|
new_event->ub = ub;
|
|
new_event->ub64 = ub;
|
|
new_event->is_within = (is_within != 0);
|
|
/* insert */
|
|
new_event->next = events->watchpoints;
|
|
events->watchpoints = new_event;
|
|
events->work_pending = 1;
|
|
ETRACE ((_ETRACE,
|
|
"event watching pc at %" PRIi64 " - "
|
|
"tag %p - pc 0x%x..0x%x, handler %p, data %p\n",
|
|
sim_events_time (sd),
|
|
new_event,
|
|
new_event->lb,
|
|
new_event->ub,
|
|
new_event->handler,
|
|
new_event->data));
|
|
return new_event;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
sim_event *
|
|
sim_events_watch_sim (SIM_DESC sd,
|
|
void *host_addr,
|
|
int nr_bytes,
|
|
enum bfd_endian byte_order,
|
|
int is_within,
|
|
uint64_t lb,
|
|
uint64_t ub,
|
|
sim_event_handler *handler,
|
|
void *data)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
sim_event *new_event = sim_events_zalloc (sd);
|
|
/* type */
|
|
switch (byte_order)
|
|
{
|
|
case BFD_ENDIAN_UNKNOWN:
|
|
switch (nr_bytes)
|
|
{
|
|
case 1: new_event->watching = watch_sim_host_1; break;
|
|
case 2: new_event->watching = watch_sim_host_2; break;
|
|
case 4: new_event->watching = watch_sim_host_4; break;
|
|
case 8: new_event->watching = watch_sim_host_8; break;
|
|
default: sim_io_error (sd, "sim_events_watch_sim - invalid nr bytes");
|
|
}
|
|
break;
|
|
case BFD_ENDIAN_BIG:
|
|
switch (nr_bytes)
|
|
{
|
|
case 1: new_event->watching = watch_sim_be_1; break;
|
|
case 2: new_event->watching = watch_sim_be_2; break;
|
|
case 4: new_event->watching = watch_sim_be_4; break;
|
|
case 8: new_event->watching = watch_sim_be_8; break;
|
|
default: sim_io_error (sd, "sim_events_watch_sim - invalid nr bytes");
|
|
}
|
|
break;
|
|
case BFD_ENDIAN_LITTLE:
|
|
switch (nr_bytes)
|
|
{
|
|
case 1: new_event->watching = watch_sim_le_1; break;
|
|
case 2: new_event->watching = watch_sim_le_2; break;
|
|
case 4: new_event->watching = watch_sim_le_4; break;
|
|
case 8: new_event->watching = watch_sim_le_8; break;
|
|
default: sim_io_error (sd, "sim_events_watch_sim - invalid nr bytes");
|
|
}
|
|
break;
|
|
default:
|
|
sim_io_error (sd, "sim_events_watch_sim - invalid byte order");
|
|
}
|
|
/* handler */
|
|
new_event->data = data;
|
|
new_event->handler = handler;
|
|
/* data */
|
|
new_event->host_addr = host_addr;
|
|
new_event->lb = lb;
|
|
new_event->lb64 = lb;
|
|
new_event->ub = ub;
|
|
new_event->ub64 = ub;
|
|
new_event->is_within = (is_within != 0);
|
|
/* insert */
|
|
new_event->next = events->watchpoints;
|
|
events->watchpoints = new_event;
|
|
events->work_pending = 1;
|
|
ETRACE ((_ETRACE,
|
|
"event watching host at %" PRIi64 " - "
|
|
"tag %p - host-addr %p, 0x%x..0x%x, handler %p, data %p\n",
|
|
sim_events_time (sd),
|
|
new_event,
|
|
new_event->host_addr,
|
|
new_event->lb,
|
|
new_event->ub,
|
|
new_event->handler,
|
|
new_event->data));
|
|
return new_event;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
sim_event *
|
|
sim_events_watch_core (SIM_DESC sd,
|
|
address_word core_addr,
|
|
unsigned core_map,
|
|
int nr_bytes,
|
|
enum bfd_endian byte_order,
|
|
int is_within,
|
|
uint64_t lb,
|
|
uint64_t ub,
|
|
sim_event_handler *handler,
|
|
void *data)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
sim_event *new_event = sim_events_zalloc (sd);
|
|
/* type */
|
|
switch (byte_order)
|
|
{
|
|
case BFD_ENDIAN_UNKNOWN:
|
|
switch (nr_bytes)
|
|
{
|
|
case 1: new_event->watching = watch_core_targ_1; break;
|
|
case 2: new_event->watching = watch_core_targ_2; break;
|
|
case 4: new_event->watching = watch_core_targ_4; break;
|
|
case 8: new_event->watching = watch_core_targ_8; break;
|
|
default: sim_io_error (sd, "sim_events_watch_core - invalid nr bytes");
|
|
}
|
|
break;
|
|
case BFD_ENDIAN_BIG:
|
|
switch (nr_bytes)
|
|
{
|
|
case 1: new_event->watching = watch_core_be_1; break;
|
|
case 2: new_event->watching = watch_core_be_2; break;
|
|
case 4: new_event->watching = watch_core_be_4; break;
|
|
case 8: new_event->watching = watch_core_be_8; break;
|
|
default: sim_io_error (sd, "sim_events_watch_core - invalid nr bytes");
|
|
}
|
|
break;
|
|
case BFD_ENDIAN_LITTLE:
|
|
switch (nr_bytes)
|
|
{
|
|
case 1: new_event->watching = watch_core_le_1; break;
|
|
case 2: new_event->watching = watch_core_le_2; break;
|
|
case 4: new_event->watching = watch_core_le_4; break;
|
|
case 8: new_event->watching = watch_core_le_8; break;
|
|
default: sim_io_error (sd, "sim_events_watch_core - invalid nr bytes");
|
|
}
|
|
break;
|
|
default:
|
|
sim_io_error (sd, "sim_events_watch_core - invalid byte order");
|
|
}
|
|
/* handler */
|
|
new_event->data = data;
|
|
new_event->handler = handler;
|
|
/* data */
|
|
new_event->core_addr = core_addr;
|
|
new_event->core_map = core_map;
|
|
new_event->lb = lb;
|
|
new_event->lb64 = lb;
|
|
new_event->ub = ub;
|
|
new_event->ub64 = ub;
|
|
new_event->is_within = (is_within != 0);
|
|
/* insert */
|
|
new_event->next = events->watchpoints;
|
|
events->watchpoints = new_event;
|
|
events->work_pending = 1;
|
|
ETRACE ((_ETRACE,
|
|
"event watching host at %" PRIi64 " - "
|
|
"tag %p - host-addr %p, 0x%x..0x%x, handler %p, data %p\n",
|
|
sim_events_time (sd),
|
|
new_event,
|
|
new_event->host_addr,
|
|
new_event->lb,
|
|
new_event->ub,
|
|
new_event->handler,
|
|
new_event->data));
|
|
return new_event;
|
|
}
|
|
#endif
|
|
|
|
|
|
#if EXTERN_SIM_EVENTS_P
|
|
void
|
|
sim_events_deschedule (SIM_DESC sd,
|
|
sim_event *event_to_remove)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
sim_event *to_remove = (sim_event*)event_to_remove;
|
|
if (event_to_remove != NULL)
|
|
{
|
|
sim_event **queue = NULL;
|
|
while ((queue = next_event_queue (sd, queue)) != NULL)
|
|
{
|
|
sim_event **ptr_to_current;
|
|
for (ptr_to_current = queue;
|
|
*ptr_to_current != NULL && *ptr_to_current != to_remove;
|
|
ptr_to_current = &(*ptr_to_current)->next);
|
|
if (*ptr_to_current == to_remove)
|
|
{
|
|
sim_event *dead = *ptr_to_current;
|
|
*ptr_to_current = dead->next;
|
|
ETRACE ((_ETRACE,
|
|
"event/watch descheduled at %" PRIi64 " - "
|
|
"tag %p - time %" PRIi64 ", handler %p, data %p%s%s\n",
|
|
sim_events_time (sd),
|
|
event_to_remove,
|
|
dead->time_of_event,
|
|
dead->handler,
|
|
dead->data,
|
|
(dead->trace != NULL) ? ", " : "",
|
|
(dead->trace != NULL) ? dead->trace : ""));
|
|
sim_events_free (sd, dead);
|
|
update_time_from_event (sd);
|
|
SIM_ASSERT ((events->time_from_event >= 0) == (events->queue != NULL));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
ETRACE ((_ETRACE,
|
|
"event/watch descheduled at %" PRIi64 " - tag %p - not found\n",
|
|
sim_events_time (sd),
|
|
event_to_remove));
|
|
}
|
|
#endif
|
|
|
|
|
|
STATIC_INLINE_SIM_EVENTS\
|
|
(int)
|
|
sim_watch_valid (SIM_DESC sd,
|
|
sim_event *to_do)
|
|
{
|
|
switch (to_do->watching)
|
|
{
|
|
|
|
#define WATCH_CORE(N,OP,EXT) \
|
|
int ok; \
|
|
unsigned_##N word = 0; \
|
|
int nr_read = sim_core_read_buffer (sd, NULL, to_do->core_map, &word, \
|
|
to_do->core_addr, sizeof (word)); \
|
|
OP (word); \
|
|
ok = (nr_read == sizeof (unsigned_##N) \
|
|
&& (to_do->is_within \
|
|
== (word >= to_do->lb##EXT \
|
|
&& word <= to_do->ub##EXT)));
|
|
|
|
case watch_core_targ_1:
|
|
{
|
|
WATCH_CORE (1, T2H,);
|
|
return ok;
|
|
}
|
|
case watch_core_targ_2:
|
|
{
|
|
WATCH_CORE (2, T2H,);
|
|
return ok;
|
|
}
|
|
case watch_core_targ_4:
|
|
{
|
|
WATCH_CORE (4, T2H,);
|
|
return ok;
|
|
}
|
|
case watch_core_targ_8:
|
|
{
|
|
WATCH_CORE (8, T2H,64);
|
|
return ok;
|
|
}
|
|
|
|
case watch_core_be_1:
|
|
{
|
|
WATCH_CORE (1, BE2H,);
|
|
return ok;
|
|
}
|
|
case watch_core_be_2:
|
|
{
|
|
WATCH_CORE (2, BE2H,);
|
|
return ok;
|
|
}
|
|
case watch_core_be_4:
|
|
{
|
|
WATCH_CORE (4, BE2H,);
|
|
return ok;
|
|
}
|
|
case watch_core_be_8:
|
|
{
|
|
WATCH_CORE (8, BE2H,64);
|
|
return ok;
|
|
}
|
|
|
|
case watch_core_le_1:
|
|
{
|
|
WATCH_CORE (1, LE2H,);
|
|
return ok;
|
|
}
|
|
case watch_core_le_2:
|
|
{
|
|
WATCH_CORE (2, LE2H,);
|
|
return ok;
|
|
}
|
|
case watch_core_le_4:
|
|
{
|
|
WATCH_CORE (4, LE2H,);
|
|
return ok;
|
|
}
|
|
case watch_core_le_8:
|
|
{
|
|
WATCH_CORE (8, LE2H,64);
|
|
return ok;
|
|
}
|
|
#undef WATCH_CORE
|
|
|
|
#define WATCH_SIM(N,OP,EXT) \
|
|
int ok; \
|
|
unsigned_##N word = *(unsigned_##N*)to_do->host_addr; \
|
|
OP (word); \
|
|
ok = (to_do->is_within \
|
|
== (word >= to_do->lb##EXT \
|
|
&& word <= to_do->ub##EXT));
|
|
|
|
case watch_sim_host_1:
|
|
{
|
|
WATCH_SIM (1, word = ,);
|
|
return ok;
|
|
}
|
|
case watch_sim_host_2:
|
|
{
|
|
WATCH_SIM (2, word = ,);
|
|
return ok;
|
|
}
|
|
case watch_sim_host_4:
|
|
{
|
|
WATCH_SIM (4, word = ,);
|
|
return ok;
|
|
}
|
|
case watch_sim_host_8:
|
|
{
|
|
WATCH_SIM (8, word = ,64);
|
|
return ok;
|
|
}
|
|
|
|
case watch_sim_be_1:
|
|
{
|
|
WATCH_SIM (1, BE2H,);
|
|
return ok;
|
|
}
|
|
case watch_sim_be_2:
|
|
{
|
|
WATCH_SIM (2, BE2H,);
|
|
return ok;
|
|
}
|
|
case watch_sim_be_4:
|
|
{
|
|
WATCH_SIM (4, BE2H,);
|
|
return ok;
|
|
}
|
|
case watch_sim_be_8:
|
|
{
|
|
WATCH_SIM (8, BE2H,64);
|
|
return ok;
|
|
}
|
|
|
|
case watch_sim_le_1:
|
|
{
|
|
WATCH_SIM (1, LE2H,);
|
|
return ok;
|
|
}
|
|
case watch_sim_le_2:
|
|
{
|
|
WATCH_SIM (1, LE2H,);
|
|
return ok;
|
|
}
|
|
case watch_sim_le_4:
|
|
{
|
|
WATCH_SIM (1, LE2H,);
|
|
return ok;
|
|
}
|
|
case watch_sim_le_8:
|
|
{
|
|
WATCH_SIM (1, LE2H,64);
|
|
return ok;
|
|
}
|
|
#undef WATCH_SIM
|
|
|
|
case watch_pc:
|
|
{
|
|
int c;
|
|
|
|
for (c = 0; c < MAX_NR_PROCESSORS; ++c)
|
|
{
|
|
sim_cpu *cpu = STATE_CPU (sd, c);
|
|
sim_cia cia = sim_pc_get (cpu);
|
|
|
|
if (to_do->is_within == (cia >= to_do->lb64 && cia <= to_do->ub64))
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case watch_clock: /* wallclock */
|
|
{
|
|
unsigned long elapsed_time = sim_events_elapsed_time (sd);
|
|
return (elapsed_time >= to_do->wallclock);
|
|
}
|
|
|
|
default:
|
|
sim_io_error (sd, "sim_watch_valid - bad switch");
|
|
break;
|
|
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(int)
|
|
sim_events_tick (SIM_DESC sd)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
|
|
/* this should only be called after the previous ticks have been
|
|
fully processed */
|
|
|
|
/* Advance the time but *only* if there is nothing to process */
|
|
if (events->work_pending
|
|
|| events->time_from_event == 0)
|
|
{
|
|
events->nr_ticks_to_process += 1;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
events->time_from_event -= 1;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(int)
|
|
sim_events_tickn (SIM_DESC sd,
|
|
int n)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
SIM_ASSERT (n > 0);
|
|
|
|
/* this should only be called after the previous ticks have been
|
|
fully processed */
|
|
|
|
/* Advance the time but *only* if there is nothing to process */
|
|
if (events->work_pending || events->time_from_event < n)
|
|
{
|
|
events->nr_ticks_to_process += n;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
events->time_from_event -= n;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(void)
|
|
sim_events_slip (SIM_DESC sd,
|
|
int slip)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
SIM_ASSERT (slip > 0);
|
|
|
|
/* Flag a ready event with work_pending instead of number of ticks
|
|
to process so that the time continues to be correct */
|
|
if (events->time_from_event < slip)
|
|
{
|
|
events->work_pending = 1;
|
|
}
|
|
events->time_from_event -= slip;
|
|
}
|
|
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(void)
|
|
sim_events_preprocess (SIM_DESC sd,
|
|
int events_were_last,
|
|
int events_were_next)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
if (events_were_last)
|
|
{
|
|
/* Halted part way through event processing */
|
|
ASSERT (events->nr_ticks_to_process != 0);
|
|
/* The external world can't tell if the event that stopped the
|
|
simulator was the last event to process. */
|
|
ASSERT (events_were_next);
|
|
sim_events_process (sd);
|
|
}
|
|
else if (events_were_next)
|
|
{
|
|
/* Halted by the last processor */
|
|
if (sim_events_tick (sd))
|
|
sim_events_process (sd);
|
|
}
|
|
}
|
|
|
|
|
|
INLINE_SIM_EVENTS\
|
|
(void)
|
|
sim_events_process (SIM_DESC sd)
|
|
{
|
|
sim_events *events = STATE_EVENTS (sd);
|
|
int64_t event_time = sim_events_time (sd);
|
|
|
|
/* Clear work_pending before checking nr_held. Clearing
|
|
work_pending after nr_held (with out a lock could loose an
|
|
event). */
|
|
events->work_pending = 0;
|
|
|
|
/* move any events that were asynchronously queued by any signal
|
|
handlers onto the real event queue. */
|
|
if (events->nr_held > 0)
|
|
{
|
|
int i;
|
|
|
|
#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
|
|
/*-LOCK-*/
|
|
sigset_t old_mask;
|
|
sigset_t new_mask;
|
|
sigfillset (&new_mask);
|
|
sigprocmask (SIG_SETMASK, &new_mask, &old_mask);
|
|
#endif
|
|
|
|
for (i = 0; i < events->nr_held; i++)
|
|
{
|
|
sim_event *entry = &events->held [i];
|
|
sim_events_schedule (sd,
|
|
entry->time_of_event,
|
|
entry->handler,
|
|
entry->data);
|
|
}
|
|
events->nr_held = 0;
|
|
|
|
#if defined(HAVE_SIGPROCMASK) && defined(SIG_SETMASK)
|
|
/*-UNLOCK-*/
|
|
sigprocmask (SIG_SETMASK, &old_mask, NULL);
|
|
#endif
|
|
|
|
}
|
|
|
|
/* Process any watchpoints. Be careful to allow a watchpoint to
|
|
appear/disappear under our feet.
|
|
To ensure that watchpoints are processed only once per cycle,
|
|
they are moved onto a watched queue, this returned to the
|
|
watchpoint queue when all queue processing has been
|
|
completed. */
|
|
while (events->watchpoints != NULL)
|
|
{
|
|
sim_event *to_do = events->watchpoints;
|
|
events->watchpoints = to_do->next;
|
|
if (sim_watch_valid (sd, to_do))
|
|
{
|
|
sim_event_handler *handler = to_do->handler;
|
|
void *data = to_do->data;
|
|
ETRACE ((_ETRACE,
|
|
"event issued at %" PRIi64 " - "
|
|
"tag %p - handler %p, data %p%s%s\n",
|
|
event_time,
|
|
to_do,
|
|
handler,
|
|
data,
|
|
(to_do->trace != NULL) ? ", " : "",
|
|
(to_do->trace != NULL) ? to_do->trace : ""));
|
|
sim_events_free (sd, to_do);
|
|
handler (sd, data);
|
|
}
|
|
else
|
|
{
|
|
to_do->next = events->watchedpoints;
|
|
events->watchedpoints = to_do;
|
|
}
|
|
}
|
|
|
|
/* consume all events for this or earlier times. Be careful to
|
|
allow an event to appear/disappear under our feet */
|
|
while (events->queue->time_of_event <
|
|
(event_time + events->nr_ticks_to_process))
|
|
{
|
|
sim_event *to_do = events->queue;
|
|
sim_event_handler *handler = to_do->handler;
|
|
void *data = to_do->data;
|
|
events->queue = to_do->next;
|
|
update_time_from_event (sd);
|
|
ETRACE ((_ETRACE,
|
|
"event issued at %" PRIi64 " - tag %p - handler %p, data %p%s%s\n",
|
|
event_time,
|
|
to_do,
|
|
handler,
|
|
data,
|
|
(to_do->trace != NULL) ? ", " : "",
|
|
(to_do->trace != NULL) ? to_do->trace : ""));
|
|
sim_events_free (sd, to_do);
|
|
handler (sd, data);
|
|
}
|
|
|
|
/* put things back where they belong ready for the next iteration */
|
|
events->watchpoints = events->watchedpoints;
|
|
events->watchedpoints = NULL;
|
|
if (events->watchpoints != NULL)
|
|
events->work_pending = 1;
|
|
|
|
/* advance the time */
|
|
SIM_ASSERT (events->time_from_event >= events->nr_ticks_to_process);
|
|
SIM_ASSERT (events->queue != NULL); /* always poll event */
|
|
events->time_from_event -= events->nr_ticks_to_process;
|
|
|
|
/* this round of processing complete */
|
|
events->nr_ticks_to_process = 0;
|
|
}
|
|
|
|
#endif
|