| /* 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/>.  */ | 
 |  | 
 | /* This must come before any other includes.  */ | 
 | #include "defs.h" | 
 |  | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include "sim-main.h" | 
 | #include "hw-main.h" | 
 | #include "sim-io.h" | 
 |  | 
 | /* NOTE: pal is naughty and grubs around looking at things outside of | 
 |    its immediate domain */ | 
 | #include "hw-tree.h" | 
 |  | 
 | /* DEVICE | 
 |  | 
 |  | 
 |    pal - glue logic device containing assorted junk | 
 |  | 
 |  | 
 |    DESCRIPTION | 
 |  | 
 |  | 
 |    Typical hardware dependant hack.  This device allows the firmware | 
 |    to gain access to all the things the firmware needs (but the OS | 
 |    doesn't). | 
 |  | 
 |    The pal contains the following registers: | 
 |  | 
 |    |0	reset register (write, 8bit) | 
 |    |4	processor id register (read, 8bit) | 
 |    |8	interrupt register (8 - port, 9 - level) (write, 16bit) | 
 |    |12	processor count register (read, 8bit) | 
 |  | 
 |    |16	tty input fifo register (read, 8bit) | 
 |    |20	tty input status register (read, 8bit) | 
 |    |24	tty output fifo register (write, 8bit) | 
 |    |28	tty output status register (read, 8bit) | 
 |  | 
 |    |32  countdown register (read/write, 32bit, big-endian) | 
 |    |36  countdown value register (read, 32bit, big-endian) | 
 |    |40  timer register (read/write, 32bit, big-endian) | 
 |    |44  timer value register (read, 32bit, big-endian) | 
 |  | 
 |    RESET (write): halts the simulator.  The value written to the | 
 |    register is used as an exit status. | 
 |  | 
 |    PROCESSOR ID (read): returns the processor identifier (0 .. N-1) of | 
 |    the processor performing the read. | 
 |  | 
 |    INTERRUPT (write): This register must be written using a two byte | 
 |    store.  The low byte specifies a port and the upper byte specifies | 
 |    the a level.  LEVEL is driven on the specified port.  By | 
 |    convention, the pal's interrupt ports (int0, int1, ...) are wired | 
 |    up to the corresponding processor's level sensative external | 
 |    interrupt pin.  Eg: A two byte write to address 8 of 0x0102 | 
 |    (big-endian) will result in processor 2's external interrupt pin | 
 |    being asserted. | 
 |  | 
 |    PROCESSOR COUNT (read): returns the total number of processors | 
 |    active in the current simulation. | 
 |  | 
 |    TTY INPUT FIFO (read): if the TTY input status register indicates a | 
 |    character is available by being nonzero, returns the next available | 
 |    character from the pal's tty input port. | 
 |  | 
 |    TTY OUTPUT FIFO (write): if the TTY output status register | 
 |    indicates the output fifo is not full by being nonzero, outputs the | 
 |    character written to the tty's output port. | 
 |  | 
 |    COUNDOWN (read/write): The countdown registers provide a | 
 |    non-repeating timed interrupt source.  Writing a 32 bit big-endian | 
 |    zero value to this register clears the countdown timer.  Writing a | 
 |    non-zero 32 bit big-endian value to this register sets the | 
 |    countdown timer to expire in VALUE ticks (ticks is target | 
 |    dependant).  Reading the countdown register returns the last value | 
 |    writen. | 
 |  | 
 |    COUNTDOWN VALUE (read): Reading this 32 bit big-endian register | 
 |    returns the number of ticks remaining until the countdown timer | 
 |    expires. | 
 |  | 
 |    TIMER (read/write): The timer registers provide a periodic timed | 
 |    interrupt source.  Writing a 32 bit big-endian zero value to this | 
 |    register clears the periodic timer.  Writing a 32 bit non-zero | 
 |    value to this register sets the periodic timer to triger every | 
 |    VALUE ticks (ticks is target dependant).  Reading the timer | 
 |    register returns the last value written. | 
 |  | 
 |    TIMER VALUE (read): Reading this 32 bit big-endian register returns | 
 |    the number of ticks until the next periodic interrupt. | 
 |  | 
 |  | 
 |    PROPERTIES | 
 |  | 
 |  | 
 |    reg = <address> <size> (required) | 
 |  | 
 |    Specify the address (within the parent bus) that this device is to | 
 |    be located. | 
 |  | 
 |    poll? = <boolean> | 
 |  | 
 |    If present and true, indicates that the device should poll its | 
 |    input. | 
 |  | 
 |  | 
 |    PORTS | 
 |  | 
 |  | 
 |    int[0..NR_PROCESSORS] (output) | 
 |  | 
 |    Driven as a result of a write to the interrupt-port / | 
 |    interrupt-level register pair. | 
 |  | 
 |  | 
 |    countdown | 
 |  | 
 |    Driven whenever the countdown counter reaches zero. | 
 |  | 
 |  | 
 |    timer | 
 |  | 
 |    Driven whenever the timer counter reaches zero. | 
 |  | 
 |  | 
 |    BUGS | 
 |  | 
 |  | 
 |    At present the common simulator framework does not support input | 
 |    polling. | 
 |  | 
 |    */ | 
 |  | 
 |  | 
 | enum { | 
 |   hw_pal_reset_register = 0x0, | 
 |   hw_pal_cpu_nr_register = 0x4, | 
 |   hw_pal_int_register = 0x8, | 
 |   hw_pal_nr_cpu_register = 0xa, | 
 |   hw_pal_read_fifo = 0x10, | 
 |   hw_pal_read_status = 0x14, | 
 |   hw_pal_write_fifo = 0x18, | 
 |   hw_pal_write_status = 0x1a, | 
 |   hw_pal_countdown = 0x20, | 
 |   hw_pal_countdown_value = 0x24, | 
 |   hw_pal_timer = 0x28, | 
 |   hw_pal_timer_value = 0x2c, | 
 |   hw_pal_address_mask = 0x3f, | 
 | }; | 
 |  | 
 |  | 
 | typedef struct _hw_pal_console_buffer { | 
 |   char buffer; | 
 |   int status; | 
 | } hw_pal_console_buffer; | 
 |  | 
 | typedef struct _hw_pal_counter { | 
 |   struct hw_event *handler; | 
 |   int64_t start; | 
 |   uint32_t delta; | 
 |   int periodic_p; | 
 | } hw_pal_counter; | 
 |  | 
 |  | 
 | typedef struct _hw_pal_device { | 
 |   hw_pal_console_buffer input; | 
 |   hw_pal_console_buffer output; | 
 |   hw_pal_counter countdown; | 
 |   hw_pal_counter timer; | 
 |   struct hw *disk; | 
 |   do_hw_poll_read_method *reader; | 
 | } hw_pal_device; | 
 |  | 
 | enum { | 
 |   COUNTDOWN_PORT, | 
 |   TIMER_PORT, | 
 |   INT_PORT, | 
 | }; | 
 |  | 
 | static const struct hw_port_descriptor hw_pal_ports[] = { | 
 |   { "countdown", COUNTDOWN_PORT, 0, output_port, }, | 
 |   { "timer", TIMER_PORT, 0, output_port, }, | 
 |   { "int", INT_PORT, MAX_NR_PROCESSORS, output_port, }, | 
 |   { NULL, 0, 0, 0 } | 
 | }; | 
 |  | 
 |  | 
 | /* countdown and simple timer */ | 
 |  | 
 | static void | 
 | do_counter_event (struct hw *me, | 
 | 		  void *data) | 
 | { | 
 |   hw_pal_counter *counter = (hw_pal_counter *) data; | 
 |   if (counter->periodic_p) | 
 |     { | 
 |       HW_TRACE ((me, "timer expired")); | 
 |       counter->start = hw_event_queue_time (me); | 
 |       hw_port_event (me, TIMER_PORT, 1); | 
 |       hw_event_queue_schedule (me, counter->delta, do_counter_event, counter); | 
 |     } | 
 |   else | 
 |     { | 
 |       HW_TRACE ((me, "countdown expired")); | 
 |       counter->delta = 0; | 
 |       hw_port_event (me, COUNTDOWN_PORT, 1); | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | do_counter_read (struct hw *me, | 
 | 		 hw_pal_device *pal, | 
 | 		 const char *reg, | 
 | 		 hw_pal_counter *counter, | 
 | 		 uint32_t *word, | 
 | 		 unsigned nr_bytes) | 
 | { | 
 |   uint32_t val; | 
 |   if (nr_bytes != 4) | 
 |     hw_abort (me, "%s - bad read size must be 4 bytes", reg); | 
 |   val = counter->delta; | 
 |   HW_TRACE ((me, "read - %s %ld", reg, (long) val)); | 
 |   *word = H2BE_4 (val); | 
 | } | 
 |  | 
 | static void | 
 | do_counter_value (struct hw *me, | 
 | 		  hw_pal_device *pal, | 
 | 		  const char *reg, | 
 | 		  hw_pal_counter *counter, | 
 | 		  uint32_t *word, | 
 | 		  unsigned nr_bytes) | 
 | { | 
 |   uint32_t val; | 
 |   if (nr_bytes != 4) | 
 |     hw_abort (me, "%s - bad read size must be 4 bytes", reg); | 
 |   if (counter->delta != 0) | 
 |     val = (counter->start + counter->delta | 
 | 	   - hw_event_queue_time (me)); | 
 |   else | 
 |     val = 0; | 
 |   HW_TRACE ((me, "read - %s %ld", reg, (long) val)); | 
 |   *word = H2BE_4 (val); | 
 | } | 
 |  | 
 | static void | 
 | do_counter_write (struct hw *me, | 
 | 		  hw_pal_device *pal, | 
 | 		  const char *reg, | 
 | 		  hw_pal_counter *counter, | 
 | 		  const uint32_t *word, | 
 | 		  unsigned nr_bytes) | 
 | { | 
 |   if (nr_bytes != 4) | 
 |     hw_abort (me, "%s - bad write size must be 4 bytes", reg); | 
 |   if (counter->handler != NULL) | 
 |     { | 
 |       hw_event_queue_deschedule (me, counter->handler); | 
 |       counter->handler = NULL; | 
 |     } | 
 |   counter->delta = BE2H_4 (*word); | 
 |   counter->start = hw_event_queue_time (me); | 
 |   HW_TRACE ((me, "write - %s %ld", reg, (long) counter->delta)); | 
 |   if (counter->delta > 0) | 
 |     hw_event_queue_schedule (me, counter->delta, do_counter_event, counter); | 
 | } | 
 |  | 
 |  | 
 |  | 
 |  | 
 | /* check the console for an available character */ | 
 | static void | 
 | scan_hw_pal (struct hw *me) | 
 | { | 
 |   hw_pal_device *hw_pal = (hw_pal_device *)hw_data (me); | 
 |   char c; | 
 |   int count; | 
 |   count = do_hw_poll_read (me, hw_pal->reader, 0/*STDIN*/, &c, sizeof (c)); | 
 |   switch (count) | 
 |     { | 
 |     case HW_IO_NOT_READY: | 
 |     case HW_IO_EOF: | 
 |       hw_pal->input.buffer = 0; | 
 |       hw_pal->input.status = 0; | 
 |       break; | 
 |     default: | 
 |       hw_pal->input.buffer = c; | 
 |       hw_pal->input.status = 1; | 
 |     } | 
 | } | 
 |  | 
 | /* write the character to the hw_pal */ | 
 |  | 
 | static void | 
 | write_hw_pal (struct hw *me, | 
 | 	      char val) | 
 | { | 
 |   hw_pal_device *hw_pal = (hw_pal_device *) hw_data (me); | 
 |   sim_io_write_stdout (hw_system (me), &val, 1); | 
 |   hw_pal->output.buffer = val; | 
 |   hw_pal->output.status = 1; | 
 | } | 
 |  | 
 |  | 
 | /* Reads/writes */ | 
 |  | 
 | static unsigned | 
 | hw_pal_io_read_buffer (struct hw *me, | 
 | 		       void *dest, | 
 | 		       int space, | 
 | 		       unsigned_word addr, | 
 | 		       unsigned nr_bytes) | 
 | { | 
 |   hw_pal_device *hw_pal = (hw_pal_device *) hw_data (me); | 
 |   unsigned_1 *byte = (unsigned_1 *) dest; | 
 |   memset (dest, 0, nr_bytes); | 
 |   switch (addr & hw_pal_address_mask) | 
 |     { | 
 |  | 
 |     case hw_pal_cpu_nr_register: | 
 |       *byte = CPU_INDEX (hw_system_cpu (me)); | 
 |       HW_TRACE ((me, "read - cpu-nr %d\n", *byte)); | 
 |       break; | 
 |  | 
 |     case hw_pal_nr_cpu_register: | 
 |       if (hw_tree_find_property (me, "/openprom/options/smp") == NULL) | 
 | 	{ | 
 | 	  *byte = 1; | 
 | 	  HW_TRACE ((me, "read - nr-cpu %d (not defined)\n", *byte)); | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  *byte = hw_tree_find_integer_property (me, "/openprom/options/smp"); | 
 | 	  HW_TRACE ((me, "read - nr-cpu %d\n", *byte)); | 
 | 	} | 
 |       break; | 
 |  | 
 |     case hw_pal_read_fifo: | 
 |       *byte = hw_pal->input.buffer; | 
 |       HW_TRACE ((me, "read - input-fifo %d\n", *byte)); | 
 |       break; | 
 |  | 
 |     case hw_pal_read_status: | 
 |       scan_hw_pal (me); | 
 |       *byte = hw_pal->input.status; | 
 |       HW_TRACE ((me, "read - input-status %d\n", *byte)); | 
 |       break; | 
 |  | 
 |     case hw_pal_write_fifo: | 
 |       *byte = hw_pal->output.buffer; | 
 |       HW_TRACE ((me, "read - output-fifo %d\n", *byte)); | 
 |       break; | 
 |  | 
 |     case hw_pal_write_status: | 
 |       *byte = hw_pal->output.status; | 
 |       HW_TRACE ((me, "read - output-status %d\n", *byte)); | 
 |       break; | 
 |  | 
 |     case hw_pal_countdown: | 
 |       do_counter_read (me, hw_pal, "countdown", | 
 | 		       &hw_pal->countdown, dest, nr_bytes); | 
 |       break; | 
 |  | 
 |     case hw_pal_countdown_value: | 
 |       do_counter_value (me, hw_pal, "countdown-value", | 
 | 			&hw_pal->countdown, dest, nr_bytes); | 
 |       break; | 
 |  | 
 |     case hw_pal_timer: | 
 |       do_counter_read (me, hw_pal, "timer", | 
 | 		       &hw_pal->timer, dest, nr_bytes); | 
 |       break; | 
 |  | 
 |     case hw_pal_timer_value: | 
 |       do_counter_value (me, hw_pal, "timer-value", | 
 | 			&hw_pal->timer, dest, nr_bytes); | 
 |       break; | 
 |  | 
 |     default: | 
 |       HW_TRACE ((me, "read - ???\n")); | 
 |       break; | 
 |  | 
 |     } | 
 |   return nr_bytes; | 
 | } | 
 |  | 
 |  | 
 | static unsigned | 
 | hw_pal_io_write_buffer (struct hw *me, | 
 | 			const void *source, | 
 | 			int space, | 
 | 			unsigned_word addr, | 
 | 			unsigned nr_bytes) | 
 | { | 
 |   hw_pal_device *hw_pal = (hw_pal_device*) hw_data (me); | 
 |   unsigned_1 *byte = (unsigned_1 *) source; | 
 |  | 
 |   switch (addr & hw_pal_address_mask) | 
 |     { | 
 |  | 
 |     case hw_pal_reset_register: | 
 |       hw_halt (me, sim_exited, byte[0]); | 
 |       break; | 
 |  | 
 |     case hw_pal_int_register: | 
 |       hw_port_event (me, | 
 | 		     INT_PORT + byte[0], /*port*/ | 
 | 		     (nr_bytes > 1 ? byte[1] : 0)); /* val */ | 
 |       break; | 
 |  | 
 |     case hw_pal_read_fifo: | 
 |       hw_pal->input.buffer = byte[0]; | 
 |       HW_TRACE ((me, "write - input-fifo %d\n", byte[0])); | 
 |       break; | 
 |  | 
 |     case hw_pal_read_status: | 
 |       hw_pal->input.status = byte[0]; | 
 |       HW_TRACE ((me, "write - input-status %d\n", byte[0])); | 
 |       break; | 
 |  | 
 |     case hw_pal_write_fifo: | 
 |       write_hw_pal (me, byte[0]); | 
 |       HW_TRACE ((me, "write - output-fifo %d\n", byte[0])); | 
 |       break; | 
 |  | 
 |     case hw_pal_write_status: | 
 |       hw_pal->output.status = byte[0]; | 
 |       HW_TRACE ((me, "write - output-status %d\n", byte[0])); | 
 |       break; | 
 |  | 
 |     case hw_pal_countdown: | 
 |       do_counter_write (me, hw_pal, "countdown", | 
 | 			&hw_pal->countdown, source, nr_bytes); | 
 |       break; | 
 |  | 
 |     case hw_pal_timer: | 
 |       do_counter_write (me, hw_pal, "timer", | 
 | 			&hw_pal->timer, source, nr_bytes); | 
 |       break; | 
 |  | 
 |     } | 
 |   return nr_bytes; | 
 | } | 
 |  | 
 |  | 
 | /* instances of the hw_pal struct hw */ | 
 |  | 
 | #if NOT_YET | 
 | static void | 
 | hw_pal_instance_delete_callback (hw_instance *instance) | 
 | { | 
 |   /* nothing to delete, the hw_pal is attached to the struct hw */ | 
 |   return; | 
 | } | 
 | #endif | 
 |  | 
 | #if NOT_YET | 
 | static int | 
 | hw_pal_instance_read_callback (hw_instance *instance, | 
 | 			      void *buf, | 
 | 			      unsigned_word len) | 
 | { | 
 |   DITRACE (pal, ("read - %s (%ld)", (const char*) buf, (long int) len)); | 
 |   return sim_io_read_stdin (buf, len); | 
 | } | 
 | #endif | 
 |  | 
 | #if NOT_YET | 
 | static int | 
 | hw_pal_instance_write_callback (hw_instance *instance, | 
 | 				const void *buf, | 
 | 				unsigned_word len) | 
 | { | 
 |   int i; | 
 |   const char *chp = buf; | 
 |   hw_pal_device *hw_pal = hw_instance_data (instance); | 
 |   DITRACE (pal, ("write - %s (%ld)", (const char*) buf, (long int) len)); | 
 |   for (i = 0; i < len; i++) | 
 |     write_hw_pal (hw_pal, chp[i]); | 
 |   sim_io_flush_stdoutput (); | 
 |   return i; | 
 | } | 
 | #endif | 
 |  | 
 | #if NOT_YET | 
 | static const hw_instance_callbacks hw_pal_instance_callbacks = { | 
 |   hw_pal_instance_delete_callback, | 
 |   hw_pal_instance_read_callback, | 
 |   hw_pal_instance_write_callback, | 
 | }; | 
 | #endif | 
 |  | 
 | #if 0 | 
 | static hw_instance * | 
 | hw_pal_create_instance (struct hw *me, | 
 | 			const char *path, | 
 | 			const char *args) | 
 | { | 
 |   return hw_create_instance_from (me, NULL, | 
 | 				      hw_data (me), | 
 | 				      path, args, | 
 | 				      &hw_pal_instance_callbacks); | 
 | } | 
 | #endif | 
 |  | 
 |  | 
 | static void | 
 | hw_pal_attach_address (struct hw *me, | 
 | 		       int level, | 
 | 		       int space, | 
 | 		       address_word addr, | 
 | 		       address_word nr_bytes, | 
 | 		       struct hw *client) | 
 | { | 
 |   hw_pal_device *pal = (hw_pal_device*) hw_data (me); | 
 |   pal->disk = client; | 
 | } | 
 |  | 
 |  | 
 | #if 0 | 
 | static hw_callbacks const hw_pal_callbacks = { | 
 |   { generic_hw_init_address, }, | 
 |   { hw_pal_attach_address, }, /* address */ | 
 |   { hw_pal_io_read_buffer_callback, | 
 |       hw_pal_io_write_buffer_callback, }, | 
 |   { NULL, }, /* DMA */ | 
 |   { NULL, NULL, hw_pal_interrupt_ports }, /* interrupt */ | 
 |   { generic_hw_unit_decode, | 
 |     generic_hw_unit_encode, | 
 |     generic_hw_address_to_attach_address, | 
 |     generic_hw_size_to_attach_size }, | 
 |   hw_pal_create_instance, | 
 | }; | 
 | #endif | 
 |  | 
 |  | 
 | static void | 
 | hw_pal_finish (struct hw *hw) | 
 | { | 
 |   /* create the descriptor */ | 
 |   hw_pal_device *hw_pal = HW_ZALLOC (hw, hw_pal_device); | 
 |   hw_pal->output.status = 1; | 
 |   hw_pal->output.buffer = '\0'; | 
 |   hw_pal->input.status = 0; | 
 |   hw_pal->input.buffer = '\0'; | 
 |   set_hw_data (hw, hw_pal); | 
 |   set_hw_attach_address (hw, hw_pal_attach_address); | 
 |   set_hw_io_read_buffer (hw, hw_pal_io_read_buffer); | 
 |   set_hw_io_write_buffer (hw, hw_pal_io_write_buffer); | 
 |   set_hw_ports (hw, hw_pal_ports); | 
 |   /* attach ourselves */ | 
 |   do_hw_attach_regs (hw); | 
 |   /* If so configured, enable polled input */ | 
 |   if (hw_find_property (hw, "poll?") != NULL | 
 |       && hw_find_boolean_property (hw, "poll?")) | 
 |     { | 
 |       hw_pal->reader = sim_io_poll_read; | 
 |     } | 
 |   else | 
 |     { | 
 |       hw_pal->reader = sim_io_read; | 
 |     } | 
 |   /* tag the periodic timer */ | 
 |   hw_pal->timer.periodic_p = 1; | 
 | } | 
 |  | 
 |  | 
 | const struct hw_descriptor dv_pal_descriptor[] = { | 
 |   { "pal", hw_pal_finish, }, | 
 |   { NULL, NULL }, | 
 | }; |