diff options
author | Stan Shebs <shebs@codesourcery.com> | 1999-04-16 01:35:26 +0000 |
---|---|---|
committer | Stan Shebs <shebs@codesourcery.com> | 1999-04-16 01:35:26 +0000 |
commit | c906108c21474dfb4ed285bcc0ac6fe02cd400cc (patch) | |
tree | a0015aa5cedc19ccbab307251353a41722a3ae13 /sim/common/hw-base.c | |
parent | cd946cff9ede3f30935803403f06f6ed30cad136 (diff) | |
download | fsf-binutils-gdb-c906108c21474dfb4ed285bcc0ac6fe02cd400cc.zip fsf-binutils-gdb-c906108c21474dfb4ed285bcc0ac6fe02cd400cc.tar.gz fsf-binutils-gdb-c906108c21474dfb4ed285bcc0ac6fe02cd400cc.tar.bz2 |
Initial creation of sourceware repositorygdb-4_18-branchpoint
Diffstat (limited to 'sim/common/hw-base.c')
-rw-r--r-- | sim/common/hw-base.c | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/sim/common/hw-base.c b/sim/common/hw-base.c new file mode 100644 index 0000000..a13ac64 --- /dev/null +++ b/sim/common/hw-base.c @@ -0,0 +1,571 @@ +/* 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 "hw-main.h" +#include "hw-base.h" + + +#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> + +#include "hw-config.h" + +struct hw_base_data { + int finished_p; + const struct hw_descriptor *descriptor; + hw_delete_callback *to_delete; +}; + +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) +{ + 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) +{ + 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); +} + +static void +ignore_hw_delete (struct hw *me) +{ + /* NOP */ +} + + + + +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 = hw_strdup (leaf, full_name); + return buf; +} + +struct hw * +hw_create (struct sim_state *sd, + struct hw *parent, + const char *family, + const char *name, + const char *unit, + const char *args) +{ + /* NOTE: HW must be allocated using ZALLOC, others use HW_ZALLOC */ + struct hw *hw = ZALLOC (struct hw); + + /* our identity */ + hw->family_of_hw = hw_strdup (hw, family); + hw->name_of_hw = hw_strdup (hw, name); + hw->args_of_hw = hw_strdup (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 = "/"; + + /* create our base type */ + hw->base_of_hw = HW_ZALLOC (hw, struct hw_base_data); + hw->base_of_hw->finished_p = 0; + + /* 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); + set_hw_delete (hw, ignore_hw_delete); + + /* locate a descriptor */ + { + const struct hw_descriptor **table; + for (table = hw_descriptors; + *table != NULL; + table++) + { + const struct hw_descriptor *entry; + for (entry = *table; + entry->family != NULL; + entry++) + { + if (strcmp (family, entry->family) == 0) + { + hw->base_of_hw->descriptor = entry; + break; + } + } + } + if (hw->base_of_hw->descriptor == NULL) + { + hw_abort (parent, "Unknown device `%s'", family); + } + } + + /* Attach dummy ports */ + create_hw_alloc_data (hw); + create_hw_property_data (hw); + create_hw_port_data (hw); + create_hw_event_data (hw); + create_hw_handle_data (hw); + create_hw_instance_data (hw); + + 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; + + /* Fill in the (hopefully) defined trace variable */ + if (hw_find_property (me, "trace?") != NULL) + me->trace_of_hw_p = hw_find_boolean_property (me, "trace?"); + /* allow global variable to define default tracing */ + else if (! hw_trace_p (me) + && hw_find_property (hw_root (me), "global-trace?") != NULL + && hw_find_boolean_property (hw_root (me), "global-trace?")) + me->trace_of_hw_p = 1; + + + /* Allow the real device to override any methods */ + me->base_of_hw->descriptor->to_finish (me); + me->base_of_hw->finished_p = 1; +} + + +void +hw_delete (struct hw *me) +{ + /* give the object a chance to tidy up */ + me->base_of_hw->to_delete (me); + + delete_hw_instance_data (me); + delete_hw_handle_data (me); + delete_hw_event_data (me); + delete_hw_port_data (me); + delete_hw_property_data (me); + + /* now unlink us from the tree */ + if (hw_parent (me)) + { + struct hw **sibling = &hw_parent (me)->child_of_hw; + while (*sibling != NULL) + { + if (*sibling == me) + { + *sibling = me->sibling_of_hw; + me->sibling_of_hw = NULL; + me->parent_of_hw = NULL; + break; + } + } + } + + /* some sanity checks */ + if (hw_child (me) != NULL) + { + hw_abort (me, "attempt to delete device with children"); + } + if (hw_sibling (me) != NULL) + { + hw_abort (me, "attempt to delete device with siblings"); + } + + /* blow away all memory belonging to the device */ + delete_hw_alloc_data (me); + + /* finally */ + zfree (me->base_of_hw); + zfree (me); +} + + +/* Go through the devices various reg properties for those that + specify attach addresses */ + + +void +do_hw_attach_regs (struct hw *hw) +{ + static const char *(reg_property_names[]) = { + "attach-addresses", + "assigned-addresses", + "reg", + "alternate-reg" , + NULL + }; + const char **reg_property_name; + int nr_valid_reg_properties = 0; + for (reg_property_name = reg_property_names; + *reg_property_name != NULL; + reg_property_name++) + { + if (hw_find_property (hw, *reg_property_name) != NULL) + { + reg_property_spec reg; + int reg_entry; + for (reg_entry = 0; + hw_find_reg_array_property (hw, *reg_property_name, reg_entry, + ®); + reg_entry++) + { + unsigned_word attach_address; + int attach_space; + unsigned attach_size; + if (!hw_unit_address_to_attach_address (hw_parent (hw), + ®.address, + &attach_space, + &attach_address, + hw)) + continue; + if (!hw_unit_size_to_attach_size (hw_parent (hw), + ®.size, + &attach_size, hw)) + continue; + hw_attach_address (hw_parent (hw), + 0, + attach_space, attach_address, attach_size, + hw); + nr_valid_reg_properties++; + } + /* if first option matches don't try for any others */ + if (reg_property_name == reg_property_names) + break; + } + } +} |