diff options
author | Andrew Cagney <cagney@redhat.com> | 1998-03-22 04:18:52 +0000 |
---|---|---|
committer | Andrew Cagney <cagney@redhat.com> | 1998-03-22 04:18:52 +0000 |
commit | b1e9223cee0728b89f6b909c445a896d9fe41452 (patch) | |
tree | b38bb06b7ec6075dcebfca1705743b29cc5a79be /sim/common | |
parent | 6cfaad4d884e380f3a0f9aa06618bcda1e5762d3 (diff) | |
download | gdb-b1e9223cee0728b89f6b909c445a896d9fe41452.zip gdb-b1e9223cee0728b89f6b909c445a896d9fe41452.tar.gz gdb-b1e9223cee0728b89f6b909c445a896d9fe41452.tar.bz2 |
Replace *attach_address() arguments SPACEMASK:ADDR with SPACE:ADDR.
Add notes to hw-device.h that discuss the interpretation of SPACE:ADDR
on a BUS.
Diffstat (limited to 'sim/common')
-rw-r--r-- | sim/common/ChangeLog | 15 | ||||
-rw-r--r-- | sim/common/dv-core.c | 116 | ||||
-rw-r--r-- | sim/common/dv-pal.c | 387 | ||||
-rw-r--r-- | sim/common/hw-base.c | 467 | ||||
-rw-r--r-- | sim/common/hw-device.h | 496 |
5 files changed, 1481 insertions, 0 deletions
diff --git a/sim/common/ChangeLog b/sim/common/ChangeLog index dda6f5c..65499fd 100644 --- a/sim/common/ChangeLog +++ b/sim/common/ChangeLog @@ -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 index 0000000..bdb612c --- /dev/null +++ b/sim/common/dv-core.c @@ -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 index 0000000..afef9c8 --- /dev/null +++ b/sim/common/dv-pal.c @@ -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 index 0000000..0c4b8a5 --- /dev/null +++ b/sim/common/hw-base.c @@ -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 index 0000000..ecbedca --- /dev/null +++ b/sim/common/hw-device.h @@ -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) + + + +/* Perform a soft reset of the device */ + +typedef unsigned (hw_reset_callback) + (struct hw *me); + +#define hw_reset(hw) ((hw)->to_reset (hw)) + + +/* 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)) + +/* 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)) + + + +/* 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 |