Replace *attach_address() arguments SPACEMASK:ADDR with SPACE:ADDR.
authorAndrew Cagney <cagney@redhat.com>
Sun, 22 Mar 1998 04:18:52 +0000 (04:18 +0000)
committerAndrew Cagney <cagney@redhat.com>
Sun, 22 Mar 1998 04:18:52 +0000 (04:18 +0000)
Add notes to hw-device.h that discuss the interpretation of SPACE:ADDR
on a BUS.

sim/common/ChangeLog
sim/common/dv-core.c [new file with mode: 0644]
sim/common/dv-pal.c [new file with mode: 0644]
sim/common/hw-base.c [new file with mode: 0644]
sim/common/hw-device.h [new file with mode: 0644]

index dda6f5c..65499fd 100644 (file)
@@ -1,3 +1,18 @@
+Sun Mar 22 15:09:52 1998  Andrew Cagney  <cagney@b1.cygnus.com>
+
+       * hw-device.h (hw_attach_address_callback,
+       hw_detach_address_callback): Attach to a single space not a space
+       mask. Clarify interpretation of SPACE:ADDR parameters.
+
+       * hw-base.c (passthrough_hw_attach_address,
+       passthrough_hw_detach_address): Update.
+       * dv-core.c (dv_core_attach_address_callback): Ditto.
+       * dv-pal.c (hw_pal_attach_address): Ditto.
+
+Thu Mar 19 00:41:00 1998  Andrew Cagney  <cagney@b1.cygnus.com>
+
+       * sim-options.h: Document additional CPU arg to OPTION_HANDLER.
+
 Wed Mar 18 14:13:02 1998  Andrew Cagney  <cagney@b1.cygnus.com>
 
        * Make-common.in (SIM_HW_OBJS, SIM_HW_SRC, SIM_DV_OBJS): Define.
diff --git a/sim/common/dv-core.c b/sim/common/dv-core.c
new file mode 100644 (file)
index 0000000..bdb612c
--- /dev/null
@@ -0,0 +1,116 @@
+/*  This file is part of the program psim.
+    
+    Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
+    
+    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 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, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+    
+    */
+
+
+#include "sim-main.h"
+#include "hw-base.h"
+
+/* DEVICE
+   
+   core - root of the device tree
+   
+   DESCRIPTION
+   
+   The core device, positioned at the root of the device tree appears
+   to its child devices as a normal device just like every other
+   device in the tree.
+
+   Internally it is implemented using a core object.  Requests to
+   attach (or detach) address spaces are passed to that core object.
+   Requests to transfer (DMA) data are reflected back down the device
+   tree using the core_map data transfer methods.
+
+   PROPERTIES
+
+   None.
+
+   */
+
+
+static void
+dv_core_attach_address_callback (struct hw *me,
+                                int level,
+                                int space,
+                                address_word addr,
+                                address_word nr_bytes,
+                                struct hw *client)
+{
+  /* NOTE: At preset the space is assumed to be zero.  Perhaphs the
+     space should be mapped onto something for instance: space0 -
+     unified memory; space1 - IO memory; ... */
+  if (space != 0)
+    hw_abort (me, "Hey! Unknown space %d", space);
+  sim_core_attach (hw_system (me),
+                  NULL, /*cpu*/
+                  level,
+                  access_read_write_exec,
+                  space, addr,
+                  nr_bytes,
+                  0, /* modulo */
+                  client,
+                  NULL);
+}
+
+
+static unsigned
+dv_core_dma_read_buffer_callback (struct hw *me,
+                                 void *dest,
+                                 int space,
+                                 unsigned_word addr,
+                                 unsigned nr_bytes)
+{
+  return sim_core_read_buffer (hw_system (me),
+                              NULL, /*CPU*/
+                              space, /*???*/
+                              dest,
+                              addr,
+                              nr_bytes);
+}
+
+
+static unsigned
+dv_core_dma_write_buffer_callback (struct hw *me,
+                                  const void *source,
+                                  int space,
+                                  unsigned_word addr,
+                                  unsigned nr_bytes,
+                                  int violate_read_only_section)
+{
+  return sim_core_write_buffer (hw_system (me),
+                               NULL, /*cpu*/
+                               space, /*???*/
+                               source,
+                               addr,
+                               nr_bytes);
+}
+
+
+static void
+dv_core_finish (struct hw *me)
+{
+  set_hw_attach_address (me, dv_core_attach_address_callback);
+  set_hw_dma_write_buffer (me, dv_core_dma_write_buffer_callback);
+  set_hw_dma_read_buffer (me, dv_core_dma_read_buffer_callback);
+}
+
+const struct hw_device_descriptor dv_core_descriptor[] = {
+  { "core", dv_core_finish, },
+  { NULL },
+};
diff --git a/sim/common/dv-pal.c b/sim/common/dv-pal.c
new file mode 100644 (file)
index 0000000..afef9c8
--- /dev/null
@@ -0,0 +1,387 @@
+/*  This file is part of the program psim.
+    
+    Copyright (C) 1994-1996,1998, Andrew Cagney <cagney@highland.com.au>
+    
+    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 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, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+    
+    */
+
+
+#include "sim-main.h"
+#include "hw-base.h"
+
+/* NOTE: pal is naughty and grubs around looking at things outside of
+   its immediate domain */
+#include "hw-tree.h"
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#define DTRACE(x,y)
+
+/* 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.  Except for the interrupt
+   level register, each of the below is 8 bytes in size and must be
+   accessed using correct alignment.  For 16 and 32 bit accesses the
+   bytes not directed to the register are ignored:
+   
+   |0  reset register (write)
+   |4  processor id register (read)
+   |8  interrupt port (write)
+   |9  interrupt level (write)
+   |12 processor count register (read)
+   |16 tty input fifo register (read)
+   |20 tty input status register (read)
+   |24 tty output fifo register (write)
+   |28 tty output status register (read)
+
+   Reset register (write) halts the simulator exiting with the
+   value written.
+   
+   Processor id register (read) returns the processor number (0
+   .. N-1) of the processor performing the read.
+   
+   The interrupt registers should be accessed as a pair (using a 16 or
+   32 bit store).  The low byte specifies the interrupt port while the
+   high byte specifies the level to drive that port at.  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 to
+   be asserted.
+
+   Processor count register (read) returns the total number of
+   processors active in the current simulation.
+
+   TTY input fifo register (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.
+
+   Similarly, the TTY output fifo register (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.
+
+
+   PROPERTIES
+   
+
+   reg = <address> <size> (required)
+
+   Specify the address (within the parent bus) that this device is to
+   live.
+
+
+   */
+
+
+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_address_mask = 0x1f,
+};
+
+
+typedef struct _hw_pal_console_buffer {
+  char buffer;
+  int status;
+} hw_pal_console_buffer;
+
+typedef struct _hw_pal_device {
+  hw_pal_console_buffer input;
+  hw_pal_console_buffer output;
+  struct hw *disk;
+} hw_pal_device;
+
+
+/* check the console for an available character */
+static void
+scan_hw_pal (struct hw *me)
+{
+#if 0
+  hw_pal_struct hw *hw_pal = (hw_pal_struct hw *) hw_data (me);
+#endif
+  char c;
+  int count;
+  count = sim_io_read_stdin (hw_system (me), &c, sizeof(c));
+#if 0
+  switch (count)
+    {
+    case sim_io_not_ready:
+    case sim_io_eof:
+      hw_pal->input.buffer = 0;
+      hw_pal->input.status = 0;
+      break;
+    default:
+      hw_pal->input.buffer = c;
+      hw_pal->input.status = 1;
+    }
+#endif
+}
+
+/* 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;
+}
+
+
+static unsigned
+hw_pal_io_read_buffer (struct hw *me,
+                      void *dest,
+                      int space,
+                      unsigned_word addr,
+                      unsigned nr_bytes,
+                      sim_cpu *cpu,
+                      sim_cia cia)
+{
+  hw_pal_device *hw_pal = (hw_pal_device *) hw_data (me);
+  unsigned_1 val;
+  switch (addr & hw_pal_address_mask)
+    {
+    case hw_pal_cpu_nr_register:
+#ifdef CPU_INDEX
+      val = CPU_INDEX (cpu);
+#else
+      val = 0;
+#endif
+      DTRACE (pal, ("read - cpu-nr %d\n", val));
+      break;
+    case hw_pal_nr_cpu_register:
+      val = hw_tree_find_integer_property (me, "/openprom/options/smp");
+      DTRACE (pal, ("read - nr-cpu %d\n", val));
+      break;
+    case hw_pal_read_fifo:
+      val = hw_pal->input.buffer;
+      DTRACE (pal, ("read - input-fifo %d\n", val));
+      break;
+    case hw_pal_read_status:
+      scan_hw_pal (me);
+      val = hw_pal->input.status;
+      DTRACE (pal, ("read - input-status %d\n", val));
+      break;
+    case hw_pal_write_fifo:
+      val = hw_pal->output.buffer;
+      DTRACE (pal, ("read - output-fifo %d\n", val));
+      break;
+    case hw_pal_write_status:
+      val = hw_pal->output.status;
+      DTRACE (pal, ("read - output-status %d\n", val));
+      break;
+    default:
+      val = 0;
+      DTRACE (pal, ("read - ???\n"));
+    }
+  memset (dest, 0, nr_bytes);
+  *(unsigned_1*)dest = val;
+  return nr_bytes;
+}
+
+
+static unsigned
+hw_pal_io_write_buffer (struct hw *me,
+                       const void *source,
+                       int space,
+                       unsigned_word addr,
+                       unsigned nr_bytes,
+                       sim_cpu *cpu,
+                       sim_cia cia)
+{
+  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:
+      sim_engine_halt (NULL, cpu, NULL, cia, sim_exited, byte[0]);
+      break;
+    case hw_pal_int_register:
+      hw_port_event (me,
+                    byte[0], /*port*/
+                    (nr_bytes > 1 ? byte[1] : 0), /* val */
+                    cpu, cia);
+      break;
+    case hw_pal_read_fifo:
+      hw_pal->input.buffer = byte[0];
+      DTRACE (pal, ("write - input-fifo %d\n", byte[0]));
+      break;
+    case hw_pal_read_status:
+      hw_pal->input.status = byte[0];
+      DTRACE (pal, ("write - input-status %d\n", byte[0]));
+      break;
+    case hw_pal_write_fifo:
+      write_hw_pal (me, byte[0]);
+      DTRACE (pal, ("write - output-fifo %d\n", byte[0]));
+      break;
+    case hw_pal_write_status:
+      hw_pal->output.status = byte[0];
+      DTRACE (pal, ("write - output-status %d\n", byte[0]));
+      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 const struct hw_port_descriptor hw_pal_ports[] = {
+  { "int", 0, MAX_NR_PROCESSORS },
+  { NULL }
+};
+
+
+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 = ZALLOC (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);
+}
+
+
+const struct hw_device_descriptor dv_pal_descriptor[] = {
+  { "pal", hw_pal_finish, },
+  { NULL },
+};
diff --git a/sim/common/hw-base.c b/sim/common/hw-base.c
new file mode 100644 (file)
index 0000000..0c4b8a5
--- /dev/null
@@ -0,0 +1,467 @@
+/*  This file is part of the program psim.
+
+    Copyright (C) 1994-1996, 1998, Andrew Cagney <cagney@highland.com.au>
+
+    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 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, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+    */
+
+
+#include "sim-main.h"
+#include "hw-base.h"
+
+
+/* LATER: #include "hwconfig.h" */
+struct hw_base_data {
+  int finished_p;
+  const struct hw_device_descriptor *descriptor;
+};
+extern const struct hw_device_descriptor dv_core_descriptor[];
+extern const struct hw_device_descriptor dv_pal_descriptor[];
+const struct hw_device_descriptor *hw_descriptors[] = {
+  dv_core_descriptor,
+  dv_pal_descriptor,
+  NULL,
+};
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <ctype.h>
+
+
+static int
+generic_hw_unit_decode (struct hw *bus,
+                       const char *unit,
+                       hw_unit *phys)
+{
+  memset (phys, 0, sizeof (*phys));
+  if (unit == NULL)
+    return 0;
+  else
+    {
+      int nr_cells = 0;
+      const int max_nr_cells = hw_unit_nr_address_cells (bus);
+      while (1)
+       {
+         char *end = NULL;
+         unsigned long val;
+         val = strtoul (unit, &end, 0);
+         /* parse error? */
+         if (unit == end)
+           return -1;
+         /* two many cells? */
+         if (nr_cells >= max_nr_cells)
+           return -1;
+         /* save it */
+         phys->cells[nr_cells] = val;
+         nr_cells++;
+         unit = end;
+         /* more to follow? */
+         if (isspace (*unit) || *unit == '\0')
+           break;
+         if (*unit != ',')
+           return -1;
+         unit++;
+       }
+      if (nr_cells < max_nr_cells) {
+       /* shift everything to correct position */
+       int i;
+       for (i = 1; i <= nr_cells; i++)
+         phys->cells[max_nr_cells - i] = phys->cells[nr_cells - i];
+       for (i = 0; i < (max_nr_cells - nr_cells); i++)
+         phys->cells[i] = 0;
+      }
+      phys->nr_cells = max_nr_cells;
+      return max_nr_cells;
+  }
+}
+
+static int
+generic_hw_unit_encode (struct hw *bus,
+                       const hw_unit *phys,
+                       char *buf,
+                       int sizeof_buf)
+{
+  int i;
+  int len;
+  char *pos = buf;
+  /* skip leading zero's */
+  for (i = 0; i < phys->nr_cells; i++)
+    {
+      if (phys->cells[i] != 0)
+       break;
+    }
+  /* don't output anything if empty */
+  if (phys->nr_cells == 0)
+    {
+      strcpy(pos, "");
+      len = 0;
+    }
+  else if (i == phys->nr_cells)
+    {
+      /* all zero */
+      strcpy(pos, "0");
+      len = 1;
+    }
+  else
+    {
+      for (; i < phys->nr_cells; i++)
+       {
+         if (pos != buf) {
+           strcat(pos, ",");
+           pos = strchr(pos, '\0');
+         }
+         if (phys->cells[i] < 10)
+           sprintf (pos, "%ld", (unsigned long)phys->cells[i]);
+         else
+           sprintf (pos, "0x%lx", (unsigned long)phys->cells[i]);
+         pos = strchr(pos, '\0');
+       }
+      len = pos - buf;
+    }
+  if (len >= sizeof_buf)
+    hw_abort (NULL, "generic_unit_encode - buffer overflow\n");
+  return len;
+}
+
+static int
+generic_hw_unit_address_to_attach_address (struct hw *me,
+                                          const hw_unit *address,
+                                          int *attach_space,
+                                          unsigned_word *attach_address,
+                                          struct hw *client)
+{
+  int i;
+  for (i = 0; i < address->nr_cells - 2; i++)
+    {
+      if (address->cells[i] != 0)
+       hw_abort (me, "Only 32bit addresses supported");
+    }
+  if (address->nr_cells >= 2)
+    *attach_space = address->cells[address->nr_cells - 2];
+  else
+    *attach_space = 0;
+  *attach_address = address->cells[address->nr_cells - 1];
+  return 1;
+}
+
+static int
+generic_hw_unit_size_to_attach_size (struct hw *me,
+                                    const hw_unit *size,
+                                    unsigned *nr_bytes,
+                                    struct hw *client)
+{
+  int i;
+  for (i = 0; i < size->nr_cells - 1; i++)
+    {
+      if (size->cells[i] != 0)
+       hw_abort (me, "Only 32bit sizes supported");
+    }
+  *nr_bytes = size->cells[0];
+  return *nr_bytes;
+}
+
+
+/* ignore/passthrough versions of each function */
+
+static void
+passthrough_hw_attach_address (struct hw *me,
+                              int level,
+                              int space,
+                              address_word addr,
+                              address_word nr_bytes,
+                              struct hw *client) /*callback/default*/
+{
+  if (hw_parent (me) == NULL)
+    hw_abort (client, "hw_attach_address: no parent attach method");
+  hw_attach_address (hw_parent (me), level,
+                    space, addr, nr_bytes,
+                    client);
+}
+
+static void
+passthrough_hw_detach_address (struct hw *me,
+                              int level,
+                              int space,
+                              address_word addr,
+                              address_word nr_bytes,
+                              struct hw *client) /*callback/default*/
+{
+  if (hw_parent (me) == NULL)
+    hw_abort (client, "hw_attach_address: no parent attach method");
+  hw_detach_address (hw_parent (me), level,
+                    space, addr, nr_bytes,
+                    client);
+}
+
+static unsigned
+panic_hw_io_read_buffer (struct hw *me,
+                        void *dest,
+                        int space,
+                        unsigned_word addr,
+                        unsigned nr_bytes,
+                        sim_cpu *processor,
+                        sim_cia cia)
+{
+  hw_abort (me, "no io-read method");
+  return 0;
+}
+
+static unsigned
+panic_hw_io_write_buffer (struct hw *me,
+                         const void *source,
+                         int space,
+                         unsigned_word addr,
+                         unsigned nr_bytes,
+                         sim_cpu *processor,
+                         sim_cia cia)
+{
+  hw_abort (me, "no io-write method");
+  return 0;
+}
+
+static unsigned
+passthrough_hw_dma_read_buffer (struct hw *me,
+                               void *dest,
+                               int space,
+                               unsigned_word addr,
+                               unsigned nr_bytes)
+{
+  if (hw_parent (me) == NULL)
+    hw_abort (me, "no parent dma-read method");
+  return hw_dma_read_buffer (hw_parent (me), dest,
+                            space, addr, nr_bytes);
+}
+
+static unsigned
+passthrough_hw_dma_write_buffer (struct hw *me,
+                                const void *source,
+                                int space,
+                                unsigned_word addr,
+                                unsigned nr_bytes,
+                                int violate_read_only_section)
+{
+  if (hw_parent (me) == NULL)
+    hw_abort (me, "no parent dma-write method");
+  return hw_dma_write_buffer (hw_parent (me), source,
+                             space, addr,
+                             nr_bytes,
+                             violate_read_only_section);
+}
+
+const struct hw_port_descriptor empty_hw_ports[] = {
+  { NULL, },
+};
+
+static void
+panic_hw_port_event (struct hw *me,
+                    int my_port,
+                    struct hw *source,
+                    int source_port,
+                    int level,
+                    sim_cpu *processor,
+                    sim_cia cia)
+{
+  hw_abort (me, "no port method");
+}
+
+
+static const char *
+full_name_of_hw (struct hw *leaf,
+                char *buf,
+                unsigned sizeof_buf)
+{
+  /* get a buffer */
+  char full_name[1024];
+  if (buf == (char*)0)
+    {
+      buf = full_name;
+      sizeof_buf = sizeof (full_name);
+    }
+
+  /* use head recursion to construct the path */
+
+  if (hw_parent (leaf) == NULL)
+    /* root */
+    {
+      if (sizeof_buf < 1)
+       hw_abort (leaf, "buffer overflow");
+      *buf = '\0';
+    }
+  else
+    /* sub node */
+    {
+      char unit[1024];
+      full_name_of_hw (hw_parent (leaf), buf, sizeof_buf);
+      if (hw_unit_encode (hw_parent (leaf),
+                         hw_unit_address (leaf),
+                         unit + 1,
+                         sizeof (unit) - 1)
+         > 0)
+       unit[0] = '@';
+      else
+       unit[0] = '\0';
+      if (strlen (buf) + strlen ("/") + strlen (hw_name (leaf)) + strlen (unit)
+         >= sizeof_buf)
+       hw_abort (leaf, "buffer overflow");
+      strcat (buf, "/");
+      strcat (buf, hw_name (leaf));
+      strcat (buf, unit);
+    }
+  
+  /* return it usefully */
+  if (buf == full_name)
+    buf = (char *) strdup (full_name);
+  return buf;
+}
+
+struct hw *
+hw_create (SIM_DESC sd,
+          struct hw *parent,
+          const char *family,
+          const char *name,
+          const char *unit,
+          const char *args)
+{
+  struct hw *hw = ZALLOC (struct hw);
+
+  /* our identity */
+  hw->family_of_hw = family;
+  hw->name_of_hw = name;
+  hw->args_of_hw = args;
+
+  /* a hook into the system */
+  if (sd != NULL)
+    hw->system_of_hw = sd;
+  else if (parent != NULL)
+    hw->system_of_hw = hw_system (parent);
+  else
+    hw_abort (parent, "No system found");
+
+  /* in a tree */
+  if (parent != NULL)
+    {
+      struct hw **sibling = &parent->child_of_hw;
+      while ((*sibling) != NULL)
+       sibling = &(*sibling)->sibling_of_hw;
+      *sibling = hw;
+      hw->parent_of_hw = parent;
+    }
+
+  /* top of tree */
+  if (parent != NULL)
+    {
+      struct hw *root = parent;
+      while (root->parent_of_hw != NULL)
+       root = root->parent_of_hw;
+      hw->root_of_hw = root;
+    }
+  
+  /* a unique identifier for the device on the parents bus */
+  if (parent != NULL)
+    {
+      hw_unit_decode (parent, unit, &hw->unit_address_of_hw);
+    }
+
+  /* Determine our path */
+  if (parent != NULL)
+    hw->path_of_hw = full_name_of_hw (hw, NULL, 0);
+  else
+    hw->path_of_hw = "/";
+
+  /* our callbacks */
+  set_hw_io_read_buffer (hw, panic_hw_io_read_buffer);
+  set_hw_io_write_buffer (hw, panic_hw_io_write_buffer);
+  set_hw_dma_read_buffer (hw, passthrough_hw_dma_read_buffer);
+  set_hw_dma_write_buffer (hw, passthrough_hw_dma_write_buffer);
+  set_hw_unit_decode (hw, generic_hw_unit_decode);
+  set_hw_unit_encode (hw, generic_hw_unit_encode);
+  set_hw_unit_address_to_attach_address (hw, generic_hw_unit_address_to_attach_address);
+  set_hw_unit_size_to_attach_size (hw, generic_hw_unit_size_to_attach_size);
+  set_hw_attach_address (hw, passthrough_hw_attach_address);
+  set_hw_detach_address (hw, passthrough_hw_detach_address);
+
+  /* locate a descriptor */
+  {
+    const struct hw_device_descriptor **table;
+    for (table = hw_descriptors;
+        *table != NULL;
+        table++)
+      {
+       const struct hw_device_descriptor *entry;
+       for (entry = *table;
+            entry->family != NULL;
+            entry++)
+         {
+           if (strcmp (family, entry->family) == 0)
+             {
+               hw->base_of_hw = ZALLOC (struct hw_base_data);
+               hw->base_of_hw->descriptor = entry;
+               hw->base_of_hw->finished_p = 0;
+             }
+         }
+      }
+    if (hw->base_of_hw == NULL)
+      {
+       hw_abort (parent, "Unknown device `%s'", family);
+      }
+  }
+
+  /* Attach dummy ports */
+  set_hw_ports (hw, empty_hw_ports);
+  set_hw_port_event (hw, panic_hw_port_event);
+  
+  return hw;
+}
+
+
+int
+hw_finished_p (struct hw *me)
+{
+  return (me->base_of_hw->finished_p);
+}
+
+void
+hw_finish (struct hw *me)
+{
+  if (hw_finished_p (me))
+    hw_abort (me, "Attempt to finish finished device");
+
+  /* Fill in the (hopefully) defined address/size cells values */
+  if (hw_find_property (me, "#address-cells") != NULL)
+    me->nr_address_cells_of_hw_unit =
+      hw_find_integer_property (me, "#address-cells");
+  else
+    me->nr_address_cells_of_hw_unit = 2;
+  if (hw_find_property (me, "#size-cells") != NULL)
+    me->nr_size_cells_of_hw_unit =
+      hw_find_integer_property (me, "#size-cells");
+  else
+    me->nr_size_cells_of_hw_unit = 1;
+
+  /* Allow the real device to override any methods */
+  me->base_of_hw->descriptor->to_finish (me);
+  me->base_of_hw->finished_p = 1;
+}
diff --git a/sim/common/hw-device.h b/sim/common/hw-device.h
new file mode 100644 (file)
index 0000000..ecbedca
--- /dev/null
@@ -0,0 +1,496 @@
+/*  This file is part of the program psim.
+
+    Copyright (C) 1994-1998, Andrew Cagney <cagney@highland.com.au>
+
+    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 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, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+    */
+
+
+#ifndef HW_DEVICE_H
+#define HW_DEVICE_H
+
+/* declared in sim-basics.h, this object is used everywhere */
+/* typedef struct _device device; */
+
+
+/* Introduction:
+
+   As explained in earlier sections, the device, device instance,
+   property and ports lie at the heart of PSIM's device model.
+
+   In the below a synopsis of the device object and the operations it
+   supports are given.
+   */
+
+
+/* Creation:
+
+   The devices are created using a sequence of steps.  In particular:
+
+       o       A tree framework is created.
+
+               At this point, properties can be modified and extra
+               devices inserted (or removed?).
+
+#if LATER
+
+               Any properties that have a run-time value (eg ihandle
+               or device instance pointer properties) are entered
+               into the device tree using a named reference to the
+               corresponding runtime object that is to be created.
+
+#endif
+
+       o       Real devices are created for all the dummy devices.
+
+               A device can assume that all of its parents have been
+               initialized.
+
+               A device can assume that all non run-time properties
+               have been initialized.
+
+               As part of being created, the device normally attaches
+               itself to its parent bus.
+
+#if LATER
+
+               Device instance data is initialized.
+
+#endif
+
+#if LATER
+
+       o       Any run-time properties are created.
+
+#endif
+
+#if MUCH_MUCH_LATER
+
+       o       Some devices, as part of their initialization
+               might want to refer to ihandle properties
+               in the device tree.
+
+#endif
+
+   NOTES:
+
+       o       It is important to separate the creation
+               of an actual device from the creation
+               of the tree.  The alternative creating
+               the device in two stages: As a separate
+               entity and then as a part of the tree.
+
+#if LATER
+       o       Run-time properties can not be created
+               until after the devices in the tree
+               have been created.  Hence an extra pass
+               for handling them.
+#endif
+
+   */
+
+/* Relationships:
+
+   A device is able to determine its relationship to other devices
+   within the tree.  Operations include querying for a devices parent,
+   sibling, child, name, and path (from the root).
+
+   */
+
+
+#define hw_parent(hw) ((hw)->parent_of_hw + 0)
+
+#define hw_sibling(hw) ((hw)->sibling_of_hw + 0)
+
+#define hw_child(hw) ((hw)->child_of_hw + 0)
+
+
+
+/* Herritage:
+
+ */
+
+#define hw_family(hw) ((hw)->family_of_hw + 0)
+
+#define hw_name(hw) ((hw)->name_of_hw + 0)
+
+#define hw_args(hw) ((hw)->args_of_hw + 0)
+
+#define hw_path(hw) ((hw)->path_of_hw + 0)
+
+
+
+/* Short cut to the root node of the tree */
+
+#define hw_root(hw) ((hw)->root_of_hw + 0)
+
+/* Short cut back to the simulator object */
+
+#define hw_system(hw) ((hw)->system_of_hw + 0)
+
+/* Device private data */
+
+#define hw_data(hw) ((hw)->data_of_hw)
+
+
+\f
+/* Perform a soft reset of the device */
+
+typedef unsigned (hw_reset_callback)
+     (struct hw *me);
+
+#define hw_reset(hw) ((hw)->to_reset (hw))
+
+\f
+/* Hardware operations:
+
+   Connecting a parent to its children is a common bus. The parent
+   node is described as the bus owner and is responisble for
+   co-ordinating bus operations. On the bus, a SPACE:ADDR pair is used
+   to specify an address.  A device that is both a bus owner (parent)
+   and bus client (child) are refered to as a bridging device.
+
+   A child performing a data (DMA) transfer will pass its request to
+   the bus owner (the devices parent).  The bus owner will then either
+   reflect the request to one of the other devices attached to the bus
+   (a child of the bus owner) or bridge the request up the tree to the
+   next bus. */
+
+
+/* Children attached to a bus can register (attach) themselves to
+   specific addresses on their attached bus.
+
+   (A device may also be implicitly attached to certain bus
+   addresses).
+
+   The SPACE:ADDR pair specify an address on the common bus that
+   connects the parent and child devices. */
+
+typedef void (hw_attach_address_callback)
+     (struct hw *me,
+      int level,
+      int space,
+      address_word addr,
+      address_word nr_bytes,
+      struct hw *client); /*callback/default*/
+
+#define hw_attach_address(me, level, space, addr, nr_bytes, client) \
+((me)->to_attach_address (me, level, space, addr, nr_bytes, client))
+
+
+typedef void (hw_detach_address_callback)
+     (struct hw *me,
+      int level,
+      int space,
+      address_word addr,
+      address_word nr_bytes,
+      struct hw *client); /*callback/default*/
+
+#define hw_detach_address(me, level, space, addr, nr_bytes, client) \
+((me)->to_detach_address (me, level, space, addr, nr_bytes, client))
+
+
+/* An IO operation from a parent to a child via the conecting bus.
+
+   The SPACE:ADDR pair specify an address on the bus shared between
+   the parent and child devices. */
+
+typedef unsigned (hw_io_read_buffer_callback)
+     (struct hw *me,
+      void *dest,
+      int space,
+      unsigned_word addr,
+      unsigned nr_bytes,
+      sim_cpu *processor,
+      sim_cia cia);
+
+#define hw_io_read_buffer(hw, dest, space, addr, nr_bytes, processor, cia) \
+((hw)->to_io_read_buffer (hw, dest, space, addr, nr_bytes, processor, cia))
+
+typedef unsigned (hw_io_write_buffer_callback)
+     (struct hw *me,
+      const void *source,
+      int space,
+      unsigned_word addr,
+      unsigned nr_bytes,
+      sim_cpu *processor,
+      sim_cia cia);
+
+#define hw_io_write_buffer(hw, src, space, addr, nr_bytes, processor, cia) \
+((hw)->to_io_write_buffer (hw, src, space, addr, nr_bytes, processor, cia))
+
+
+
+/* Conversly, the device pci1000,1@1 may need to perform a dma transfer
+   into the cpu/memory core.  Just as I/O moves towards the leaves,
+   dma transfers move towards the core via the initiating devices
+   parent nodes.  The root device (special) converts the DMA transfer
+   into reads/writes to memory.
+
+   The SPACE:ADDR pair specify an address on the common bus connecting
+   the parent and child devices. */
+
+typedef unsigned (hw_dma_read_buffer_callback)
+     (struct hw *bus,
+      void *dest,
+      int space,
+      unsigned_word addr,
+      unsigned nr_bytes);
+
+#define hw_dma_read_buffer(bus, dest, space, addr, nr_bytes) \
+((bus)->to_dma_read_buffer (bus, dest, space, addr, nr_bytes))
+
+typedef unsigned (hw_dma_write_buffer_callback)
+     (struct hw *bus,
+      const void *source,
+      int space,
+      unsigned_word addr,
+      unsigned nr_bytes,
+      int violate_read_only_section);
+
+#define hw_dma_write_buffer(bus, src, space, addr, nr_bytes, violate_ro) \
+((bus)->to_dma_write_buffer (bus, src, space, addr, nr_bytes, violate_ro))
+\f
+/* Address/size specs for devices are encoded following a convention
+   similar to that used by OpenFirmware.  In particular, an
+   address/size is packed into a sequence of up to four cell words.
+   The number of words determined by the number of {address,size}
+   cells attributes of the device. */
+
+typedef struct _hw_unit {
+  int nr_cells;
+  unsigned_cell cells[4]; /* unused cells are zero */
+} hw_unit;
+
+
+/* For the given bus, the number of address and size cells used in a
+   hw_unit. */
+
+#define hw_unit_nr_address_cells(bus) ((bus)->nr_address_cells_of_hw_unit + 0)
+
+#define hw_unit_nr_size_cells(bus) ((bus)->nr_size_cells_of_hw_unit + 0)
+
+
+/* For the given device, its identifying hw_unit address.
+
+   Each device has an identifying hw_unit address.  That address is
+   used when identifying one of a number of identical devices on a
+   common controller bus. ex fd0&fd1. */
+
+const hw_unit *hw_unit_address
+(struct hw *me);
+
+
+/* Convert between a textual and the internal representation of a
+   hw_unit address/size.
+
+   NOTE: A device asks its parent to translate between a hw_unit and
+   textual representation.  This is because the textual address of a
+   device is specified using the parent busses notation. */
+
+typedef int (hw_unit_decode_callback)
+     (struct hw *bus,
+      const char *encoded,
+      hw_unit *unit);
+
+#define hw_unit_decode(bus, encoded, unit) \
+((bus)->to_unit_decode (bus, encoded, unit))
+
+
+typedef int (hw_unit_encode_callback)
+     (struct hw *bus,
+      const hw_unit *unit,
+      char *encoded,
+      int sizeof_buf);
+     
+#define hw_unit_encode(bus, unit, encoded, sizeof_encoded) \
+((bus)->to_unit_encode (bus, unit, encoded, sizeof_encoded))
+
+
+
+/* As the bus that the device is attached too, to translate a devices
+   hw_unit address/size into a form suitable for an attach address
+   call.
+
+   Return a zero result if the address should be ignored when looking
+   for attach addresses. */
+
+typedef int (hw_unit_address_to_attach_address_callback)
+     (struct hw *bus,
+      const hw_unit *unit_addr,
+      int *attach_space,
+      unsigned_word *attach_addr,
+      struct hw *client);
+
+#define hw_unit_address_to_attach_address(bus, unit_addr, attach_space, attach_addr, client) \
+((bus)->to_unit_address_to_attach_address (bus, unit_addr, attach_space, attach_addr, client))
+
+
+typedef int (hw_unit_size_to_attach_size_callback)
+     (struct hw *bus,
+      const hw_unit *unit_size,
+      unsigned *attach_size,
+      struct hw *client);
+
+#define hw_unit_size_to_attach_size(bus, unit_size, attach_size, client) \
+((bus)->to_unit_size_to_attach_size (bus, unit_size, attach_size, client))
+
+
+\f
+/* Utilities:
+
+   */
+
+/* IOCTL::
+
+   Often devices require `out of band' operations to be performed.
+   For instance a pal device may need to notify a PCI bridge device
+   that an interrupt ack cycle needs to be performed on the PCI bus.
+   Within PSIM such operations are performed by using the generic
+   ioctl call <<hw_ioctl()>>.
+
+   */
+
+typedef enum {
+  hw_ioctl_break, /* unsigned_word requested_break */
+  hw_ioctl_set_trace, /* void */
+  hw_ioctl_create_stack, /* unsigned_word *sp, char **argv, char **envp */
+  hw_ioctl_change_media, /* const char *new_image (possibly NULL) */
+  nr_hw_ioctl_requests,
+} hw_ioctl_request;
+
+typedef int (hw_ioctl_callback)
+     (struct hw *me,
+      sim_cpu *processor,
+      sim_cia cia,
+      hw_ioctl_request request,
+      va_list ap);
+
+int hw_ioctl
+(struct hw *me,
+ sim_cpu *processor,
+ sim_cia cia,
+ hw_ioctl_request request,
+ ...);
+
+
+/* Event queue:
+
+   Device specific versions of certain event handlers */
+
+typedef struct _hw_event hw_event;
+typedef void (hw_event_handler) (struct hw *me, void *data);
+
+hw_event *hw_event_queue_schedule
+(struct hw *me,
+ signed64 delta_time,
+ hw_event_handler *handler,
+ void *data);
+
+void hw_event_queue_deschedule
+(struct hw *me,
+ hw_event *event_to_remove);
+
+signed64 hw_event_queue_time
+(struct hw *me);
+
+
+
+/* Error reporting::
+
+   So that errors originating from devices appear in a consistent
+   format, the <<hw_abort()>> function can be used.  Formats and
+   outputs the error message before aborting the simulation
+
+   Devices should use this function to abort the simulation except
+   when the abort reason leaves the simulation in a hazardous
+   condition (for instance a failed malloc).
+
+   */
+
+void volatile NORETURN hw_abort
+(struct hw *me,
+ const char *fmt,
+ ...) __attribute__ ((format (printf, 2, 3)));
+
+#define hw_trace_p(hw) ((hw)->trace_of_hw_p + 0)
+
+
+
+/* Some of the related functions require specific types */
+
+struct hw_property_data;
+struct hw_port_data;
+struct hw_base_data;
+
+/* Finally the hardware device - keep your grubby little mits off of
+   these internals! :-) */
+
+struct hw {
+
+  /* our relatives */
+  struct hw *parent_of_hw;
+  struct hw *sibling_of_hw;
+  struct hw *child_of_hw;
+
+  /* our identity */
+  const char *name_of_hw;
+  const char *family_of_hw;
+  const char *args_of_hw;
+  const char *path_of_hw;
+
+  /* our data */
+  void *data_of_hw;
+
+  /* hot links */
+  struct hw *root_of_hw;
+  SIM_DESC system_of_hw;
+
+  /* identifying data */
+  hw_unit unit_address_of_hw;
+  int nr_address_cells_of_hw_unit;
+  int nr_size_cells_of_hw_unit;
+
+  /* Soft reset */
+  hw_reset_callback *to_reset;
+
+  /* Basic callbacks */
+  hw_io_read_buffer_callback *to_io_read_buffer;
+  hw_io_write_buffer_callback *to_io_write_buffer;
+  hw_dma_read_buffer_callback *to_dma_read_buffer;
+  hw_dma_write_buffer_callback *to_dma_write_buffer;
+  hw_attach_address_callback *to_attach_address;
+  hw_detach_address_callback *to_detach_address;
+
+  /* More complicated callbacks */
+  hw_ioctl_callback *to_ioctl;
+  int trace_of_hw_p;
+
+  /* address callbacks */
+  hw_unit_decode_callback *to_unit_decode;
+  hw_unit_encode_callback *to_unit_encode;
+  hw_unit_address_to_attach_address_callback *to_unit_address_to_attach_address;
+  hw_unit_size_to_attach_size_callback *to_unit_size_to_attach_size;
+
+  /* related data */
+  struct hw_property_data *properties_of_hw;
+  struct hw_port_data *ports_of_hw;
+  struct hw_base_data *base_of_hw;
+
+};
+
+
+#endif