|  | /* The common simulator framework for GDB, the GNU Debugger. | 
|  |  | 
|  | Copyright 2002-2021 Free Software Foundation, Inc. | 
|  |  | 
|  | Contributed by Andrew Cagney and Red Hat. | 
|  |  | 
|  | This file is part of GDB. | 
|  |  | 
|  | This program is free software; you can redistribute it and/or modify | 
|  | it under the terms of the GNU General Public License as published by | 
|  | the Free Software Foundation; either version 3 of the License, or | 
|  | (at your option) any later version. | 
|  |  | 
|  | This program is distributed in the hope that it will be useful, | 
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | GNU General Public License for more details. | 
|  |  | 
|  | You should have received a copy of the GNU General Public License | 
|  | along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
|  |  | 
|  | /* This must come before any other includes.  */ | 
|  | #include "defs.h" | 
|  |  | 
|  | #include "hw-main.h" | 
|  | #include "hw-base.h" | 
|  |  | 
|  | #include "sim-io.h" | 
|  | #include "sim-assert.h" | 
|  |  | 
|  | #include <string.h> | 
|  |  | 
|  | /* property entries */ | 
|  |  | 
|  | struct hw_property_data | 
|  | { | 
|  | struct hw_property_data *next; | 
|  | struct hw_property *property; | 
|  | const void *init_array; | 
|  | unsigned sizeof_init_array; | 
|  | }; | 
|  |  | 
|  | void | 
|  | create_hw_property_data (struct hw *me) | 
|  | { | 
|  | } | 
|  |  | 
|  | void | 
|  | delete_hw_property_data (struct hw *me) | 
|  | { | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Device Properties: */ | 
|  |  | 
|  | static struct hw_property_data * | 
|  | find_property_data (struct hw *me, | 
|  | const char *property) | 
|  | { | 
|  | struct hw_property_data *entry; | 
|  | ASSERT (property != NULL); | 
|  | entry = me->properties_of_hw; | 
|  | while (entry != NULL) | 
|  | { | 
|  | if (strcmp (entry->property->name, property) == 0) | 
|  | return entry; | 
|  | entry = entry->next; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | hw_add_property (struct hw *me, | 
|  | const char *property, | 
|  | hw_property_type type, | 
|  | const void *init_array, | 
|  | unsigned sizeof_init_array, | 
|  | const void *array, | 
|  | unsigned sizeof_array, | 
|  | const struct hw_property *original, | 
|  | object_disposition disposition) | 
|  | { | 
|  | struct hw_property_data *new_entry = NULL; | 
|  | struct hw_property *new_value = NULL; | 
|  |  | 
|  | /* find the list end */ | 
|  | struct hw_property_data **insertion_point = &me->properties_of_hw; | 
|  | while (*insertion_point != NULL) | 
|  | { | 
|  | if (strcmp ((*insertion_point)->property->name, property) == 0) | 
|  | return; | 
|  | insertion_point = &(*insertion_point)->next; | 
|  | } | 
|  |  | 
|  | /* create a new value */ | 
|  | new_value = HW_ZALLOC (me, struct hw_property); | 
|  | new_value->name = (char *) strdup (property); | 
|  | new_value->type = type; | 
|  | if (sizeof_array > 0) | 
|  | { | 
|  | void *new_array = hw_zalloc (me, sizeof_array); | 
|  | memcpy (new_array, array, sizeof_array); | 
|  | new_value->array = new_array; | 
|  | new_value->sizeof_array = sizeof_array; | 
|  | } | 
|  | new_value->owner = me; | 
|  | new_value->original = original; | 
|  | new_value->disposition = disposition; | 
|  |  | 
|  | /* insert the value into the list */ | 
|  | new_entry = HW_ZALLOC (me, struct hw_property_data); | 
|  | *insertion_point = new_entry; | 
|  | if (sizeof_init_array > 0) | 
|  | { | 
|  | void *new_init_array = hw_zalloc (me, sizeof_init_array); | 
|  | memcpy (new_init_array, init_array, sizeof_init_array); | 
|  | new_entry->init_array = new_init_array; | 
|  | new_entry->sizeof_init_array = sizeof_init_array; | 
|  | } | 
|  | new_entry->property = new_value; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void | 
|  | hw_set_property (struct hw *me, | 
|  | const char *property, | 
|  | hw_property_type type, | 
|  | const void *array, | 
|  | int sizeof_array) | 
|  | { | 
|  | /* find the property */ | 
|  | struct hw_property_data *entry = find_property_data (me, property); | 
|  | if (entry != NULL) | 
|  | { | 
|  | /* existing property - update it */ | 
|  | void *new_array = 0; | 
|  | struct hw_property *value = entry->property; | 
|  | /* check the type matches */ | 
|  | if (value->type != type) | 
|  | hw_abort (me, "conflict between type of new and old value for property %s", property); | 
|  | /* replace its value */ | 
|  | if (value->array != NULL) | 
|  | hw_free (me, (void*)value->array); | 
|  | new_array = (sizeof_array > 0 | 
|  | ? hw_zalloc (me, sizeof_array) | 
|  | : (void*)0); | 
|  | value->array = new_array; | 
|  | value->sizeof_array = sizeof_array; | 
|  | if (sizeof_array > 0) | 
|  | memcpy (new_array, array, sizeof_array); | 
|  | return; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* new property - create it */ | 
|  | hw_add_property (me, property, type, | 
|  | NULL, 0, array, sizeof_array, | 
|  | NULL, temporary_object); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | #if 0 | 
|  | static void | 
|  | clean_hw_properties (struct hw *me) | 
|  | { | 
|  | struct hw_property_data **delete_point = &me->properties_of_hw; | 
|  | while (*delete_point != NULL) | 
|  | { | 
|  | struct hw_property_data *current = *delete_point; | 
|  | switch (current->property->disposition) | 
|  | { | 
|  | case permenant_object: | 
|  | /* zap the current value, will be initialized later */ | 
|  | ASSERT (current->init_array != NULL); | 
|  | if (current->property->array != NULL) | 
|  | { | 
|  | hw_free (me, (void*)current->property->array); | 
|  | current->property->array = NULL; | 
|  | } | 
|  | delete_point = &(*delete_point)->next; | 
|  | break; | 
|  | case temporary_object: | 
|  | /* zap the actual property, was created during simulation run */ | 
|  | ASSERT (current->init_array == NULL); | 
|  | *delete_point = current->next; | 
|  | if (current->property->array != NULL) | 
|  | hw_free (me, (void*)current->property->array); | 
|  | hw_free (me, current->property); | 
|  | hw_free (me, current); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #if 0 | 
|  | void | 
|  | hw_init_static_properties (SIM_DESC sd, | 
|  | struct hw *me, | 
|  | void *data) | 
|  | { | 
|  | struct hw_property_data *property; | 
|  | for (property = me->properties_of_hw; | 
|  | property != NULL; | 
|  | property = property->next) | 
|  | { | 
|  | ASSERT (property->init_array != NULL); | 
|  | ASSERT (property->property->array == NULL); | 
|  | ASSERT (property->property->disposition == permenant_object); | 
|  | switch (property->property->type) | 
|  | { | 
|  | case array_property: | 
|  | case boolean_property: | 
|  | case range_array_property: | 
|  | case reg_array_property: | 
|  | case string_property: | 
|  | case string_array_property: | 
|  | case integer_property: | 
|  | /* delete the property, and replace it with the original */ | 
|  | hw_set_property (me, property->property->name, | 
|  | property->property->type, | 
|  | property->init_array, | 
|  | property->sizeof_init_array); | 
|  | break; | 
|  | #if 0 | 
|  | case ihandle_property: | 
|  | break; | 
|  | #endif | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  | #if 0 | 
|  | void | 
|  | hw_init_runtime_properties (SIM_DESC sd, | 
|  | struct hw *me, | 
|  | void *data) | 
|  | { | 
|  | struct hw_property_data *property; | 
|  | for (property = me->properties_of_hw; | 
|  | property != NULL; | 
|  | property = property->next) | 
|  | { | 
|  | switch (property->property->disposition) | 
|  | { | 
|  | case permenant_object: | 
|  | switch (property->property->type) | 
|  | { | 
|  | #if 0 | 
|  | case ihandle_property: | 
|  | { | 
|  | struct hw_instance *ihandle; | 
|  | ihandle_runtime_property_spec spec; | 
|  | ASSERT (property->init_array != NULL); | 
|  | ASSERT (property->property->array == NULL); | 
|  | hw_find_ihandle_runtime_property (me, property->property->name, &spec); | 
|  | ihandle = tree_instance (me, spec.full_path); | 
|  | hw_set_ihandle_property (me, property->property->name, ihandle); | 
|  | break; | 
|  | } | 
|  | #endif | 
|  | case array_property: | 
|  | case boolean_property: | 
|  | case range_array_property: | 
|  | case integer_property: | 
|  | case reg_array_property: | 
|  | case string_property: | 
|  | case string_array_property: | 
|  | ASSERT (property->init_array != NULL); | 
|  | ASSERT (property->property->array != NULL); | 
|  | break; | 
|  | } | 
|  | break; | 
|  | case temporary_object: | 
|  | ASSERT (property->init_array == NULL); | 
|  | ASSERT (property->property->array != NULL); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  |  | 
|  | const struct hw_property * | 
|  | hw_next_property (const struct hw_property *property) | 
|  | { | 
|  | /* find the property in the list */ | 
|  | struct hw *owner = property->owner; | 
|  | struct hw_property_data *entry = owner->properties_of_hw; | 
|  | while (entry != NULL && entry->property != property) | 
|  | entry = entry->next; | 
|  | /* now return the following property */ | 
|  | ASSERT (entry != NULL); /* must be a member! */ | 
|  | if (entry->next != NULL) | 
|  | return entry->next->property; | 
|  | else | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | const struct hw_property * | 
|  | hw_find_property (struct hw *me, | 
|  | const char *property) | 
|  | { | 
|  | if (me == NULL) | 
|  | { | 
|  | return NULL; | 
|  | } | 
|  | else if (property == NULL || strcmp (property, "") == 0) | 
|  | { | 
|  | if (me->properties_of_hw == NULL) | 
|  | return NULL; | 
|  | else | 
|  | return me->properties_of_hw->property; | 
|  | } | 
|  | else | 
|  | { | 
|  | struct hw_property_data *entry = find_property_data (me, property); | 
|  | if (entry != NULL) | 
|  | return entry->property; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | hw_add_array_property (struct hw *me, | 
|  | const char *property, | 
|  | const void *array, | 
|  | int sizeof_array) | 
|  | { | 
|  | hw_add_property (me, property, array_property, | 
|  | array, sizeof_array, array, sizeof_array, | 
|  | NULL, permenant_object); | 
|  | } | 
|  |  | 
|  | void | 
|  | hw_set_array_property (struct hw *me, | 
|  | const char *property, | 
|  | const void *array, | 
|  | int sizeof_array) | 
|  | { | 
|  | hw_set_property (me, property, array_property, array, sizeof_array); | 
|  | } | 
|  |  | 
|  | const struct hw_property * | 
|  | hw_find_array_property (struct hw *me, | 
|  | const char *property) | 
|  | { | 
|  | const struct hw_property *node; | 
|  | node = hw_find_property (me, property); | 
|  | if (node == NULL) | 
|  | hw_abort (me, "property \"%s\" not found", property); | 
|  | if (node->type != array_property) | 
|  | hw_abort (me, "property \"%s\" of wrong type (array)", property); | 
|  | return node; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | void | 
|  | hw_add_boolean_property (struct hw *me, | 
|  | const char *property, | 
|  | int boolean) | 
|  | { | 
|  | signed32 new_boolean = (boolean ? -1 : 0); | 
|  | hw_add_property (me, property, boolean_property, | 
|  | &new_boolean, sizeof (new_boolean), | 
|  | &new_boolean, sizeof (new_boolean), | 
|  | NULL, permenant_object); | 
|  | } | 
|  |  | 
|  | int | 
|  | hw_find_boolean_property (struct hw *me, | 
|  | const char *property) | 
|  | { | 
|  | const struct hw_property *node; | 
|  | unsigned_cell boolean; | 
|  | node = hw_find_property (me, property); | 
|  | if (node == NULL) | 
|  | hw_abort (me, "property \"%s\" not found", property); | 
|  | if (node->type != boolean_property) | 
|  | hw_abort (me, "property \"%s\" of wrong type (boolean)", property); | 
|  | ASSERT (sizeof (boolean) == node->sizeof_array); | 
|  | memcpy (&boolean, node->array, sizeof (boolean)); | 
|  | return boolean; | 
|  | } | 
|  |  | 
|  |  | 
|  |  | 
|  | #if 0 | 
|  | void | 
|  | hw_add_ihandle_runtime_property (struct hw *me, | 
|  | const char *property, | 
|  | const ihandle_runtime_property_spec *ihandle) | 
|  | { | 
|  | /* enter the full path as the init array */ | 
|  | hw_add_property (me, property, ihandle_property, | 
|  | ihandle->full_path, strlen (ihandle->full_path) + 1, | 
|  | NULL, 0, | 
|  | NULL, permenant_object); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #if 0 | 
|  | void | 
|  | hw_find_ihandle_runtime_property (struct hw *me, | 
|  | const char *property, | 
|  | ihandle_runtime_property_spec *ihandle) | 
|  | { | 
|  | struct hw_property_data *entry = find_property_data (me, property); | 
|  | if (entry == NULL) | 
|  | hw_abort (me, "property \"%s\" not found", property); | 
|  | if (entry->property->type != ihandle_property | 
|  | || entry->property->disposition != permenant_object) | 
|  | hw_abort (me, "property \"%s\" of wrong type", property); | 
|  | ASSERT (entry->init_array != NULL); | 
|  | /* the full path */ | 
|  | ihandle->full_path = entry->init_array; | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  |  | 
|  | #if 0 | 
|  | void | 
|  | hw_set_ihandle_property (struct hw *me, | 
|  | const char *property, | 
|  | hw_instance *ihandle) | 
|  | { | 
|  | unsigned_cell cells; | 
|  | cells = H2BE_cell (hw_instance_to_external (ihandle)); | 
|  | hw_set_property (me, property, ihandle_property, | 
|  | &cells, sizeof (cells)); | 
|  |  | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #if 0 | 
|  | hw_instance * | 
|  | hw_find_ihandle_property (struct hw *me, | 
|  | const char *property) | 
|  | { | 
|  | const hw_property_data *node; | 
|  | unsigned_cell ihandle; | 
|  | hw_instance *instance; | 
|  |  | 
|  | node = hw_find_property (me, property); | 
|  | if (node == NULL) | 
|  | hw_abort (me, "property \"%s\" not found", property); | 
|  | if (node->type != ihandle_property) | 
|  | hw_abort (me, "property \"%s\" of wrong type (ihandle)", property); | 
|  | if (node->array == NULL) | 
|  | hw_abort (me, "runtime property \"%s\" not yet initialized", property); | 
|  |  | 
|  | ASSERT (sizeof (ihandle) == node->sizeof_array); | 
|  | memcpy (&ihandle, node->array, sizeof (ihandle)); | 
|  | instance = external_to_hw_instance (me, BE2H_cell (ihandle)); | 
|  | ASSERT (instance != NULL); | 
|  | return instance; | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  | void | 
|  | hw_add_integer_property (struct hw *me, | 
|  | const char *property, | 
|  | signed_cell integer) | 
|  | { | 
|  | H2BE (integer); | 
|  | hw_add_property (me, property, integer_property, | 
|  | &integer, sizeof (integer), | 
|  | &integer, sizeof (integer), | 
|  | NULL, permenant_object); | 
|  | } | 
|  |  | 
|  | signed_cell | 
|  | hw_find_integer_property (struct hw *me, | 
|  | const char *property) | 
|  | { | 
|  | const struct hw_property *node; | 
|  | signed_cell integer; | 
|  | node = hw_find_property (me, property); | 
|  | if (node == NULL) | 
|  | hw_abort (me, "property \"%s\" not found", property); | 
|  | if (node->type != integer_property) | 
|  | hw_abort (me, "property \"%s\" of wrong type (integer)", property); | 
|  | ASSERT (sizeof (integer) == node->sizeof_array); | 
|  | memcpy (&integer, node->array, sizeof (integer)); | 
|  | return BE2H_cell (integer); | 
|  | } | 
|  |  | 
|  | int | 
|  | hw_find_integer_array_property (struct hw *me, | 
|  | const char *property, | 
|  | unsigned index, | 
|  | signed_cell *integer) | 
|  | { | 
|  | const struct hw_property *node; | 
|  | int sizeof_integer = sizeof (*integer); | 
|  | signed_cell *cell; | 
|  |  | 
|  | /* check things sane */ | 
|  | node = hw_find_property (me, property); | 
|  | if (node == NULL) | 
|  | hw_abort (me, "property \"%s\" not found", property); | 
|  | if (node->type != integer_property | 
|  | && node->type != array_property) | 
|  | hw_abort (me, "property \"%s\" of wrong type (integer or array)", property); | 
|  | if ((node->sizeof_array % sizeof_integer) != 0) | 
|  | hw_abort (me, "property \"%s\" contains an incomplete number of cells", property); | 
|  | if (node->sizeof_array <= sizeof_integer * index) | 
|  | return 0; | 
|  |  | 
|  | /* Find and convert the value */ | 
|  | cell = ((signed_cell*)node->array) + index; | 
|  | *integer = BE2H_cell (*cell); | 
|  |  | 
|  | return node->sizeof_array / sizeof_integer; | 
|  | } | 
|  |  | 
|  |  | 
|  | static unsigned_cell * | 
|  | unit_address_to_cells (const hw_unit *unit, | 
|  | unsigned_cell *cell, | 
|  | int nr_cells) | 
|  | { | 
|  | int i; | 
|  | ASSERT (nr_cells == unit->nr_cells); | 
|  | for (i = 0; i < unit->nr_cells; i++) | 
|  | { | 
|  | *cell = H2BE_cell (unit->cells[i]); | 
|  | cell += 1; | 
|  | } | 
|  | return cell; | 
|  | } | 
|  |  | 
|  |  | 
|  | static const unsigned_cell * | 
|  | cells_to_unit_address (const unsigned_cell *cell, | 
|  | hw_unit *unit, | 
|  | int nr_cells) | 
|  | { | 
|  | int i; | 
|  | memset (unit, 0, sizeof (*unit)); | 
|  | unit->nr_cells = nr_cells; | 
|  | for (i = 0; i < unit->nr_cells; i++) | 
|  | { | 
|  | unit->cells[i] = BE2H_cell (*cell); | 
|  | cell += 1; | 
|  | } | 
|  | return cell; | 
|  | } | 
|  |  | 
|  |  | 
|  | static unsigned | 
|  | nr_range_property_cells (struct hw *me, | 
|  | int nr_ranges) | 
|  | { | 
|  | return ((hw_unit_nr_address_cells (me) | 
|  | + hw_unit_nr_address_cells (hw_parent (me)) | 
|  | + hw_unit_nr_size_cells (me)) | 
|  | ) * nr_ranges; | 
|  | } | 
|  |  | 
|  | void | 
|  | hw_add_range_array_property (struct hw *me, | 
|  | const char *property, | 
|  | const range_property_spec *ranges, | 
|  | unsigned nr_ranges) | 
|  | { | 
|  | unsigned sizeof_cells = (nr_range_property_cells (me, nr_ranges) | 
|  | * sizeof (unsigned_cell)); | 
|  | unsigned_cell *cells = hw_zalloc (me, sizeof_cells); | 
|  | unsigned_cell *cell; | 
|  | int i; | 
|  |  | 
|  | /* copy the property elements over */ | 
|  | cell = cells; | 
|  | for (i = 0; i < nr_ranges; i++) | 
|  | { | 
|  | const range_property_spec *range = &ranges[i]; | 
|  | /* copy the child address */ | 
|  | cell = unit_address_to_cells (&range->child_address, cell, | 
|  | hw_unit_nr_address_cells (me)); | 
|  | /* copy the parent address */ | 
|  | cell = unit_address_to_cells (&range->parent_address, cell, | 
|  | hw_unit_nr_address_cells (hw_parent (me))); | 
|  | /* copy the size */ | 
|  | cell = unit_address_to_cells (&range->size, cell, | 
|  | hw_unit_nr_size_cells (me)); | 
|  | } | 
|  | ASSERT (cell == &cells[nr_range_property_cells (me, nr_ranges)]); | 
|  |  | 
|  | /* add it */ | 
|  | hw_add_property (me, property, range_array_property, | 
|  | cells, sizeof_cells, | 
|  | cells, sizeof_cells, | 
|  | NULL, permenant_object); | 
|  |  | 
|  | hw_free (me, cells); | 
|  | } | 
|  |  | 
|  | int | 
|  | hw_find_range_array_property (struct hw *me, | 
|  | const char *property, | 
|  | unsigned index, | 
|  | range_property_spec *range) | 
|  | { | 
|  | const struct hw_property *node; | 
|  | unsigned sizeof_entry = (nr_range_property_cells (me, 1) | 
|  | * sizeof (unsigned_cell)); | 
|  | const unsigned_cell *cells; | 
|  |  | 
|  | /* locate the property */ | 
|  | node = hw_find_property (me, property); | 
|  | if (node == NULL) | 
|  | hw_abort (me, "property \"%s\" not found", property); | 
|  | if (node->type != range_array_property) | 
|  | hw_abort (me, "property \"%s\" of wrong type (range array)", property); | 
|  |  | 
|  | /* aligned ? */ | 
|  | if ((node->sizeof_array % sizeof_entry) != 0) | 
|  | hw_abort (me, "property \"%s\" contains an incomplete number of entries", | 
|  | property); | 
|  |  | 
|  | /* within bounds? */ | 
|  | if (node->sizeof_array < sizeof_entry * (index + 1)) | 
|  | return 0; | 
|  |  | 
|  | /* find the range of interest */ | 
|  | cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index); | 
|  |  | 
|  | /* copy the child address out - converting as we go */ | 
|  | cells = cells_to_unit_address (cells, &range->child_address, | 
|  | hw_unit_nr_address_cells (me)); | 
|  |  | 
|  | /* copy the parent address out - converting as we go */ | 
|  | cells = cells_to_unit_address (cells, &range->parent_address, | 
|  | hw_unit_nr_address_cells (hw_parent (me))); | 
|  |  | 
|  | /* copy the size - converting as we go */ | 
|  | cells = cells_to_unit_address (cells, &range->size, | 
|  | hw_unit_nr_size_cells (me)); | 
|  |  | 
|  | return node->sizeof_array / sizeof_entry; | 
|  | } | 
|  |  | 
|  |  | 
|  | static unsigned | 
|  | nr_reg_property_cells (struct hw *me, | 
|  | int nr_regs) | 
|  | { | 
|  | return (hw_unit_nr_address_cells (hw_parent (me)) | 
|  | + hw_unit_nr_size_cells (hw_parent (me)) | 
|  | ) * nr_regs; | 
|  | } | 
|  |  | 
|  | void | 
|  | hw_add_reg_array_property (struct hw *me, | 
|  | const char *property, | 
|  | const reg_property_spec *regs, | 
|  | unsigned nr_regs) | 
|  | { | 
|  | unsigned sizeof_cells = (nr_reg_property_cells (me, nr_regs) | 
|  | * sizeof (unsigned_cell)); | 
|  | unsigned_cell *cells = hw_zalloc (me, sizeof_cells); | 
|  | unsigned_cell *cell; | 
|  | int i; | 
|  |  | 
|  | /* copy the property elements over */ | 
|  | cell = cells; | 
|  | for (i = 0; i < nr_regs; i++) | 
|  | { | 
|  | const reg_property_spec *reg = ®s[i]; | 
|  | /* copy the address */ | 
|  | cell = unit_address_to_cells (®->address, cell, | 
|  | hw_unit_nr_address_cells (hw_parent (me))); | 
|  | /* copy the size */ | 
|  | cell = unit_address_to_cells (®->size, cell, | 
|  | hw_unit_nr_size_cells (hw_parent (me))); | 
|  | } | 
|  | ASSERT (cell == &cells[nr_reg_property_cells (me, nr_regs)]); | 
|  |  | 
|  | /* add it */ | 
|  | hw_add_property (me, property, reg_array_property, | 
|  | cells, sizeof_cells, | 
|  | cells, sizeof_cells, | 
|  | NULL, permenant_object); | 
|  |  | 
|  | hw_free (me, cells); | 
|  | } | 
|  |  | 
|  | int | 
|  | hw_find_reg_array_property (struct hw *me, | 
|  | const char *property, | 
|  | unsigned index, | 
|  | reg_property_spec *reg) | 
|  | { | 
|  | const struct hw_property *node; | 
|  | unsigned sizeof_entry = (nr_reg_property_cells (me, 1) | 
|  | * sizeof (unsigned_cell)); | 
|  | const unsigned_cell *cells; | 
|  |  | 
|  | /* locate the property */ | 
|  | node = hw_find_property (me, property); | 
|  | if (node == NULL) | 
|  | hw_abort (me, "property \"%s\" not found", property); | 
|  | if (node->type != reg_array_property) | 
|  | hw_abort (me, "property \"%s\" of wrong type (reg array)", property); | 
|  |  | 
|  | /* aligned ? */ | 
|  | if ((node->sizeof_array % sizeof_entry) != 0) | 
|  | hw_abort (me, "property \"%s\" contains an incomplete number of entries", | 
|  | property); | 
|  |  | 
|  | /* within bounds? */ | 
|  | if (node->sizeof_array < sizeof_entry * (index + 1)) | 
|  | return 0; | 
|  |  | 
|  | /* find the range of interest */ | 
|  | cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index); | 
|  |  | 
|  | /* copy the address out - converting as we go */ | 
|  | cells = cells_to_unit_address (cells, ®->address, | 
|  | hw_unit_nr_address_cells (hw_parent (me))); | 
|  |  | 
|  | /* copy the size out - converting as we go */ | 
|  | cells = cells_to_unit_address (cells, ®->size, | 
|  | hw_unit_nr_size_cells (hw_parent (me))); | 
|  |  | 
|  | return node->sizeof_array / sizeof_entry; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | hw_add_string_property (struct hw *me, | 
|  | const char *property, | 
|  | const char *string) | 
|  | { | 
|  | hw_add_property (me, property, string_property, | 
|  | string, strlen (string) + 1, | 
|  | string, strlen (string) + 1, | 
|  | NULL, permenant_object); | 
|  | } | 
|  |  | 
|  | const char * | 
|  | hw_find_string_property (struct hw *me, | 
|  | const char *property) | 
|  | { | 
|  | const struct hw_property *node; | 
|  | const char *string; | 
|  | node = hw_find_property (me, property); | 
|  | if (node == NULL) | 
|  | hw_abort (me, "property \"%s\" not found", property); | 
|  | if (node->type != string_property) | 
|  | hw_abort (me, "property \"%s\" of wrong type (string)", property); | 
|  | string = node->array; | 
|  | ASSERT (strlen (string) + 1 == node->sizeof_array); | 
|  | return string; | 
|  | } | 
|  |  | 
|  | void | 
|  | hw_add_string_array_property (struct hw *me, | 
|  | const char *property, | 
|  | const string_property_spec *strings, | 
|  | unsigned nr_strings) | 
|  | { | 
|  | int sizeof_array; | 
|  | int string_nr; | 
|  | char *array; | 
|  | char *chp; | 
|  | if (nr_strings == 0) | 
|  | hw_abort (me, "property \"%s\" must be non-null", property); | 
|  | /* total up the size of the needed array */ | 
|  | for (sizeof_array = 0, string_nr = 0; | 
|  | string_nr < nr_strings; | 
|  | string_nr ++) | 
|  | { | 
|  | sizeof_array += strlen (strings[string_nr]) + 1; | 
|  | } | 
|  | /* create the array */ | 
|  | array = (char*) hw_zalloc (me, sizeof_array); | 
|  | chp = array; | 
|  | for (string_nr = 0; | 
|  | string_nr < nr_strings; | 
|  | string_nr++) | 
|  | { | 
|  | strcpy (chp, strings[string_nr]); | 
|  | chp += strlen (chp) + 1; | 
|  | } | 
|  | ASSERT (chp == array + sizeof_array); | 
|  | /* now enter it */ | 
|  | hw_add_property (me, property, string_array_property, | 
|  | array, sizeof_array, | 
|  | array, sizeof_array, | 
|  | NULL, permenant_object); | 
|  | } | 
|  |  | 
|  | int | 
|  | hw_find_string_array_property (struct hw *me, | 
|  | const char *property, | 
|  | unsigned index, | 
|  | string_property_spec *string) | 
|  | { | 
|  | const struct hw_property *node; | 
|  | node = hw_find_property (me, property); | 
|  | if (node == NULL) | 
|  | hw_abort (me, "property \"%s\" not found", property); | 
|  | switch (node->type) | 
|  | { | 
|  | default: | 
|  | hw_abort (me, "property \"%s\" of wrong type", property); | 
|  | break; | 
|  | case string_property: | 
|  | if (index == 0) | 
|  | { | 
|  | *string = node->array; | 
|  | ASSERT (strlen (*string) + 1 == node->sizeof_array); | 
|  | return 1; | 
|  | } | 
|  | break; | 
|  | case array_property: | 
|  | if (node->sizeof_array == 0 | 
|  | || ((char*)node->array)[node->sizeof_array - 1] != '\0') | 
|  | hw_abort (me, "property \"%s\" invalid for string array", property); | 
|  | /* FALL THROUGH */ | 
|  | case string_array_property: | 
|  | ASSERT (node->sizeof_array > 0); | 
|  | ASSERT (((char*)node->array)[node->sizeof_array - 1] == '\0'); | 
|  | { | 
|  | const char *chp = node->array; | 
|  | int nr_entries = 0; | 
|  | /* count the number of strings, keeping an eye out for the one | 
|  | we're looking for */ | 
|  | *string = chp; | 
|  | do | 
|  | { | 
|  | if (*chp == '\0') | 
|  | { | 
|  | /* next string */ | 
|  | nr_entries++; | 
|  | chp++; | 
|  | if (nr_entries == index) | 
|  | *string = chp; | 
|  | } | 
|  | else | 
|  | { | 
|  | chp++; | 
|  | } | 
|  | } while (chp < (char*)node->array + node->sizeof_array); | 
|  | if (index < nr_entries) | 
|  | return nr_entries; | 
|  | else | 
|  | { | 
|  | *string = NULL; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | hw_add_duplicate_property (struct hw *me, | 
|  | const char *property, | 
|  | const struct hw_property *original) | 
|  | { | 
|  | struct hw_property_data *master; | 
|  | if (original->disposition != permenant_object) | 
|  | hw_abort (me, "Can only duplicate permenant objects"); | 
|  | /* find the original's master */ | 
|  | master = original->owner->properties_of_hw; | 
|  | while (master->property != original) | 
|  | { | 
|  | master = master->next; | 
|  | ASSERT (master != NULL); | 
|  | } | 
|  | /* now duplicate it */ | 
|  | hw_add_property (me, property, | 
|  | original->type, | 
|  | master->init_array, master->sizeof_init_array, | 
|  | original->array, original->sizeof_array, | 
|  | original, permenant_object); | 
|  | } |