1/* The common simulator framework for GDB, the GNU Debugger.
2
3   Copyright 2002, 2007 Free Software Foundation, Inc.
4
5   Contributed by Andrew Cagney and Red Hat.
6
7   This file is part of GDB.
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 3 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
21
22#include "hw-main.h"
23#include "hw-base.h"
24
25#include "sim-io.h"
26#include "sim-assert.h"
27
28#ifdef HAVE_STRING_H
29#include <string.h>
30#else
31#ifdef HAVE_STRINGS_H
32#include <strings.h>
33#endif
34#endif
35
36#define TRACE(A,B)
37
38/* property entries */
39
40struct hw_property_data {
41  struct hw_property_data *next;
42  struct hw_property *property;
43  const void *init_array;
44  unsigned sizeof_init_array;
45};
46
47void
48create_hw_property_data (struct hw *me)
49{
50}
51
52void
53delete_hw_property_data (struct hw *me)
54{
55}
56
57
58/* Device Properties: */
59
60static struct hw_property_data *
61find_property_data (struct hw *me,
62		    const char *property)
63{
64  struct hw_property_data *entry;
65  ASSERT (property != NULL);
66  entry = me->properties_of_hw;
67  while (entry != NULL)
68    {
69      if (strcmp (entry->property->name, property) == 0)
70	return entry;
71      entry = entry->next;
72    }
73  return NULL;
74}
75
76
77static void
78hw_add_property (struct hw *me,
79		 const char *property,
80		 hw_property_type type,
81		 const void *init_array,
82		 unsigned sizeof_init_array,
83		 const void *array,
84		 unsigned sizeof_array,
85		 const struct hw_property *original,
86		 object_disposition disposition)
87{
88  struct hw_property_data *new_entry = NULL;
89  struct hw_property *new_value = NULL;
90
91  /* find the list end */
92  struct hw_property_data **insertion_point = &me->properties_of_hw;
93  while (*insertion_point != NULL)
94    {
95      if (strcmp ((*insertion_point)->property->name, property) == 0)
96	return;
97      insertion_point = &(*insertion_point)->next;
98    }
99
100  /* create a new value */
101  new_value = HW_ZALLOC (me, struct hw_property);
102  new_value->name = (char *) strdup (property);
103  new_value->type = type;
104  if (sizeof_array > 0)
105    {
106      void *new_array = hw_zalloc (me, sizeof_array);
107      memcpy (new_array, array, sizeof_array);
108      new_value->array = new_array;
109      new_value->sizeof_array = sizeof_array;
110    }
111  new_value->owner = me;
112  new_value->original = original;
113  new_value->disposition = disposition;
114
115  /* insert the value into the list */
116  new_entry = HW_ZALLOC (me, struct hw_property_data);
117  *insertion_point = new_entry;
118  if (sizeof_init_array > 0)
119    {
120      void *new_init_array = hw_zalloc (me, sizeof_init_array);
121      memcpy (new_init_array, init_array, sizeof_init_array);
122      new_entry->init_array = new_init_array;
123      new_entry->sizeof_init_array = sizeof_init_array;
124    }
125  new_entry->property = new_value;
126}
127
128
129static void
130hw_set_property (struct hw *me,
131		 const char *property,
132		 hw_property_type type,
133		 const void *array,
134		 int sizeof_array)
135{
136  /* find the property */
137  struct hw_property_data *entry = find_property_data (me, property);
138  if (entry != NULL)
139    {
140      /* existing property - update it */
141      void *new_array = 0;
142      struct hw_property *value = entry->property;
143      /* check the type matches */
144      if (value->type != type)
145	hw_abort (me, "conflict between type of new and old value for property %s", property);
146      /* replace its value */
147      if (value->array != NULL)
148	hw_free (me, (void*)value->array);
149      new_array = (sizeof_array > 0
150		   ? hw_zalloc (me, sizeof_array)
151		   : (void*)0);
152      value->array = new_array;
153      value->sizeof_array = sizeof_array;
154      if (sizeof_array > 0)
155	memcpy (new_array, array, sizeof_array);
156      return;
157    }
158  else
159    {
160      /* new property - create it */
161      hw_add_property (me, property, type,
162		       NULL, 0, array, sizeof_array,
163		       NULL, temporary_object);
164    }
165}
166
167
168#if 0
169static void
170clean_hw_properties (struct hw *me)
171{
172  struct hw_property_data **delete_point = &me->properties_of_hw;
173  while (*delete_point != NULL)
174    {
175      struct hw_property_data *current = *delete_point;
176      switch (current->property->disposition)
177	{
178	case permenant_object:
179	  /* zap the current value, will be initialized later */
180	  ASSERT (current->init_array != NULL);
181	  if (current->property->array != NULL)
182	    {
183	      hw_free (me, (void*)current->property->array);
184	      current->property->array = NULL;
185	    }
186	  delete_point = &(*delete_point)->next;
187	  break;
188	case temporary_object:
189	  /* zap the actual property, was created during simulation run */
190	  ASSERT (current->init_array == NULL);
191	  *delete_point = current->next;
192	  if (current->property->array != NULL)
193	    hw_free (me, (void*)current->property->array);
194	  hw_free (me, current->property);
195	  hw_free (me, current);
196	  break;
197	}
198    }
199}
200#endif
201
202#if 0
203void
204hw_init_static_properties (SIM_DESC sd,
205			   struct hw *me,
206			   void *data)
207{
208  struct hw_property_data *property;
209  for (property = me->properties_of_hw;
210       property != NULL;
211       property = property->next)
212    {
213      ASSERT (property->init_array != NULL);
214      ASSERT (property->property->array == NULL);
215      ASSERT(property->property->disposition == permenant_object);
216      switch (property->property->type)
217	{
218	case array_property:
219	case boolean_property:
220	case range_array_property:
221	case reg_array_property:
222	case string_property:
223	case string_array_property:
224	case integer_property:
225	  /* delete the property, and replace it with the original */
226	  hw_set_property (me, property->property->name,
227			   property->property->type,
228			   property->init_array,
229			   property->sizeof_init_array);
230	  break;
231#if 0
232	case ihandle_property:
233	  break;
234#endif
235	}
236    }
237}
238#endif
239
240
241#if 0
242void
243hw_init_runtime_properties (SIM_DESC sd,
244			    struct hw *me,
245			    void *data)
246{
247  struct hw_property_data *property;
248  for (property = me->properties_of_hw;
249       property != NULL;
250       property = property->next)
251    {
252      switch (property->property->disposition)
253	{
254	case permenant_object:
255	  switch (property->property->type)
256	    {
257#if 0
258	    case ihandle_property:
259	      {
260		struct hw_instance *ihandle;
261		ihandle_runtime_property_spec spec;
262		ASSERT (property->init_array != NULL);
263		ASSERT (property->property->array == NULL);
264		hw_find_ihandle_runtime_property (me, property->property->name, &spec);
265		ihandle = tree_instance (me, spec.full_path);
266		hw_set_ihandle_property (me, property->property->name, ihandle);
267		break;
268	      }
269#endif
270	    case array_property:
271	    case boolean_property:
272	    case range_array_property:
273	    case integer_property:
274	    case reg_array_property:
275	    case string_property:
276	    case string_array_property:
277	      ASSERT (property->init_array != NULL);
278	      ASSERT (property->property->array != NULL);
279	      break;
280	    }
281	  break;
282	case temporary_object:
283	  ASSERT (property->init_array == NULL);
284	  ASSERT (property->property->array != NULL);
285	  break;
286	}
287    }
288}
289#endif
290
291
292
293const struct hw_property *
294hw_next_property (const struct hw_property *property)
295{
296  /* find the property in the list */
297  struct hw *owner = property->owner;
298  struct hw_property_data *entry = owner->properties_of_hw;
299  while (entry != NULL && entry->property != property)
300    entry = entry->next;
301  /* now return the following property */
302  ASSERT (entry != NULL); /* must be a member! */
303  if (entry->next != NULL)
304    return entry->next->property;
305  else
306    return NULL;
307}
308
309
310const struct hw_property *
311hw_find_property (struct hw *me,
312		  const char *property)
313{
314  if (me == NULL)
315    {
316      return NULL;
317    }
318  else if (property == NULL || strcmp (property, "") == 0)
319    {
320      if (me->properties_of_hw == NULL)
321	return NULL;
322      else
323	return me->properties_of_hw->property;
324    }
325  else
326    {
327      struct hw_property_data *entry = find_property_data (me, property);
328      if (entry != NULL)
329	return entry->property;
330    }
331  return NULL;
332}
333
334
335void
336hw_add_array_property (struct hw *me,
337		       const char *property,
338		       const void *array,
339		       int sizeof_array)
340{
341  hw_add_property (me, property, array_property,
342		   array, sizeof_array, array, sizeof_array,
343		   NULL, permenant_object);
344}
345
346void
347hw_set_array_property (struct hw *me,
348		       const char *property,
349		       const void *array,
350		       int sizeof_array)
351{
352  hw_set_property (me, property, array_property, array, sizeof_array);
353}
354
355const struct hw_property *
356hw_find_array_property (struct hw *me,
357			const char *property)
358{
359  const struct hw_property *node;
360  node = hw_find_property (me, property);
361  if (node == NULL)
362    hw_abort (me, "property \"%s\" not found", property);
363  if (node->type != array_property)
364    hw_abort (me, "property \"%s\" of wrong type (array)", property);
365  return node;
366}
367
368
369
370void
371hw_add_boolean_property (struct hw *me,
372			 const char *property,
373			 int boolean)
374{
375  signed32 new_boolean = (boolean ? -1 : 0);
376  hw_add_property (me, property, boolean_property,
377		   &new_boolean, sizeof(new_boolean),
378		   &new_boolean, sizeof(new_boolean),
379		   NULL, permenant_object);
380}
381
382int
383hw_find_boolean_property (struct hw *me,
384			  const char *property)
385{
386  const struct hw_property *node;
387  unsigned_cell boolean;
388  node = hw_find_property (me, property);
389  if (node == NULL)
390    hw_abort (me, "property \"%s\" not found", property);
391  if (node->type != boolean_property)
392    hw_abort (me, "property \"%s\" of wrong type (boolean)", property);
393  ASSERT (sizeof (boolean) == node->sizeof_array);
394  memcpy (&boolean, node->array, sizeof (boolean));
395  return boolean;
396}
397
398
399
400#if 0
401void
402hw_add_ihandle_runtime_property (struct hw *me,
403				 const char *property,
404				 const ihandle_runtime_property_spec *ihandle)
405{
406  /* enter the full path as the init array */
407  hw_add_property (me, property, ihandle_property,
408		   ihandle->full_path, strlen(ihandle->full_path) + 1,
409		   NULL, 0,
410		   NULL, permenant_object);
411}
412#endif
413
414#if 0
415void
416hw_find_ihandle_runtime_property (struct hw *me,
417				  const char *property,
418				  ihandle_runtime_property_spec *ihandle)
419{
420  struct hw_property_data *entry = find_property_data (me, property);
421  TRACE (trace_devices,
422	 ("hw_find_ihandle_runtime_property(me=0x%lx, property=%s)\n",
423	  (long)me, property));
424  if (entry == NULL)
425    hw_abort (me, "property \"%s\" not found", property);
426  if (entry->property->type != ihandle_property
427      || entry->property->disposition != permenant_object)
428    hw_abort (me, "property \"%s\" of wrong type", property);
429  ASSERT (entry->init_array != NULL);
430  /* the full path */
431  ihandle->full_path = entry->init_array;
432}
433#endif
434
435
436
437#if 0
438void
439hw_set_ihandle_property (struct hw *me,
440			 const char *property,
441			 hw_instance *ihandle)
442{
443  unsigned_cell cells;
444  cells = H2BE_cell (hw_instance_to_external (ihandle));
445  hw_set_property (me, property, ihandle_property,
446		   &cells, sizeof (cells));
447
448}
449#endif
450
451#if 0
452hw_instance *
453hw_find_ihandle_property (struct hw *me,
454			  const char *property)
455{
456  const hw_property_data *node;
457  unsigned_cell ihandle;
458  hw_instance *instance;
459
460  node = hw_find_property (me, property);
461  if (node == NULL)
462    hw_abort (me, "property \"%s\" not found", property);
463  if (node->type != ihandle_property)
464    hw_abort(me, "property \"%s\" of wrong type (ihandle)", property);
465  if (node->array == NULL)
466    hw_abort(me, "runtime property \"%s\" not yet initialized", property);
467
468  ASSERT (sizeof(ihandle) == node->sizeof_array);
469  memcpy (&ihandle, node->array, sizeof(ihandle));
470  instance = external_to_hw_instance (me, BE2H_cell(ihandle));
471  ASSERT (instance != NULL);
472  return instance;
473}
474#endif
475
476
477void
478hw_add_integer_property (struct hw *me,
479			 const char *property,
480			 signed_cell integer)
481{
482  H2BE (integer);
483  hw_add_property (me, property, integer_property,
484		   &integer, sizeof(integer),
485		   &integer, sizeof(integer),
486		   NULL, permenant_object);
487}
488
489signed_cell
490hw_find_integer_property (struct hw *me,
491			  const char *property)
492{
493  const struct hw_property *node;
494  signed_cell integer;
495  TRACE (trace_devices,
496	 ("hw_find_integer(me=0x%lx, property=%s)\n",
497	  (long)me, property));
498  node = hw_find_property (me, property);
499  if (node == NULL)
500    hw_abort (me, "property \"%s\" not found", property);
501  if (node->type != integer_property)
502    hw_abort (me, "property \"%s\" of wrong type (integer)", property);
503  ASSERT (sizeof(integer) == node->sizeof_array);
504  memcpy (&integer, node->array, sizeof (integer));
505  return BE2H_cell (integer);
506}
507
508int
509hw_find_integer_array_property (struct hw *me,
510				const char *property,
511				unsigned index,
512				signed_cell *integer)
513{
514  const struct hw_property *node;
515  int sizeof_integer = sizeof (*integer);
516  signed_cell *cell;
517  TRACE (trace_devices,
518	 ("hw_find_integer(me=0x%lx, property=%s)\n",
519	  (long)me, property));
520
521  /* check things sane */
522  node = hw_find_property (me, property);
523  if (node == NULL)
524    hw_abort (me, "property \"%s\" not found", property);
525  if (node->type != integer_property
526      && node->type != array_property)
527    hw_abort (me, "property \"%s\" of wrong type (integer or array)", property);
528  if ((node->sizeof_array % sizeof_integer) != 0)
529    hw_abort (me, "property \"%s\" contains an incomplete number of cells", property);
530  if (node->sizeof_array <= sizeof_integer * index)
531    return 0;
532
533  /* Find and convert the value */
534  cell = ((signed_cell*)node->array) + index;
535  *integer = BE2H_cell (*cell);
536
537  return node->sizeof_array / sizeof_integer;
538}
539
540
541static unsigned_cell *
542unit_address_to_cells (const hw_unit *unit,
543		       unsigned_cell *cell,
544		       int nr_cells)
545{
546  int i;
547  ASSERT(nr_cells == unit->nr_cells);
548  for (i = 0; i < unit->nr_cells; i++)
549    {
550      *cell = H2BE_cell (unit->cells[i]);
551      cell += 1;
552    }
553  return cell;
554}
555
556
557static const unsigned_cell *
558cells_to_unit_address (const unsigned_cell *cell,
559		       hw_unit *unit,
560		       int nr_cells)
561{
562  int i;
563  memset(unit, 0, sizeof(*unit));
564  unit->nr_cells = nr_cells;
565  for (i = 0; i < unit->nr_cells; i++)
566    {
567      unit->cells[i] = BE2H_cell (*cell);
568      cell += 1;
569    }
570  return cell;
571}
572
573
574static unsigned
575nr_range_property_cells (struct hw *me,
576			 int nr_ranges)
577{
578  return ((hw_unit_nr_address_cells (me)
579	   + hw_unit_nr_address_cells (hw_parent (me))
580	   + hw_unit_nr_size_cells (me))
581	  ) * nr_ranges;
582}
583
584void
585hw_add_range_array_property (struct hw *me,
586			     const char *property,
587			     const range_property_spec *ranges,
588			     unsigned nr_ranges)
589{
590  unsigned sizeof_cells = (nr_range_property_cells (me, nr_ranges)
591			   * sizeof (unsigned_cell));
592  unsigned_cell *cells = hw_zalloc (me, sizeof_cells);
593  unsigned_cell *cell;
594  int i;
595
596  /* copy the property elements over */
597  cell = cells;
598  for (i = 0; i < nr_ranges; i++)
599    {
600      const range_property_spec *range = &ranges[i];
601      /* copy the child address */
602      cell = unit_address_to_cells (&range->child_address, cell,
603				    hw_unit_nr_address_cells (me));
604      /* copy the parent address */
605      cell = unit_address_to_cells (&range->parent_address, cell,
606				    hw_unit_nr_address_cells (hw_parent (me)));
607      /* copy the size */
608      cell = unit_address_to_cells (&range->size, cell,
609				    hw_unit_nr_size_cells (me));
610    }
611  ASSERT (cell == &cells[nr_range_property_cells (me, nr_ranges)]);
612
613  /* add it */
614  hw_add_property (me, property, range_array_property,
615		   cells, sizeof_cells,
616		   cells, sizeof_cells,
617		   NULL, permenant_object);
618
619  hw_free (me, cells);
620}
621
622int
623hw_find_range_array_property (struct hw *me,
624			      const char *property,
625			      unsigned index,
626			      range_property_spec *range)
627{
628  const struct hw_property *node;
629  unsigned sizeof_entry = (nr_range_property_cells (me, 1)
630			   * sizeof (unsigned_cell));
631  const unsigned_cell *cells;
632
633  /* locate the property */
634  node = hw_find_property (me, property);
635  if (node == NULL)
636    hw_abort (me, "property \"%s\" not found", property);
637  if (node->type != range_array_property)
638    hw_abort (me, "property \"%s\" of wrong type (range array)", property);
639
640  /* aligned ? */
641  if ((node->sizeof_array % sizeof_entry) != 0)
642    hw_abort (me, "property \"%s\" contains an incomplete number of entries",
643	      property);
644
645  /* within bounds? */
646  if (node->sizeof_array < sizeof_entry * (index + 1))
647    return 0;
648
649  /* find the range of interest */
650  cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index);
651
652  /* copy the child address out - converting as we go */
653  cells = cells_to_unit_address (cells, &range->child_address,
654				 hw_unit_nr_address_cells (me));
655
656  /* copy the parent address out - converting as we go */
657  cells = cells_to_unit_address (cells, &range->parent_address,
658				 hw_unit_nr_address_cells (hw_parent (me)));
659
660  /* copy the size - converting as we go */
661  cells = cells_to_unit_address (cells, &range->size,
662				 hw_unit_nr_size_cells (me));
663
664  return node->sizeof_array / sizeof_entry;
665}
666
667
668static unsigned
669nr_reg_property_cells (struct hw *me,
670		       int nr_regs)
671{
672  return (hw_unit_nr_address_cells (hw_parent(me))
673	  + hw_unit_nr_size_cells (hw_parent(me))
674	  ) * nr_regs;
675}
676
677void
678hw_add_reg_array_property (struct hw *me,
679			   const char *property,
680			   const reg_property_spec *regs,
681			   unsigned nr_regs)
682{
683  unsigned sizeof_cells = (nr_reg_property_cells (me, nr_regs)
684			   * sizeof (unsigned_cell));
685  unsigned_cell *cells = hw_zalloc (me, sizeof_cells);
686  unsigned_cell *cell;
687  int i;
688
689  /* copy the property elements over */
690  cell = cells;
691  for (i = 0; i < nr_regs; i++)
692    {
693      const reg_property_spec *reg = &regs[i];
694      /* copy the address */
695      cell = unit_address_to_cells (&reg->address, cell,
696				    hw_unit_nr_address_cells (hw_parent (me)));
697      /* copy the size */
698      cell = unit_address_to_cells (&reg->size, cell,
699				    hw_unit_nr_size_cells (hw_parent (me)));
700    }
701  ASSERT (cell == &cells[nr_reg_property_cells (me, nr_regs)]);
702
703  /* add it */
704  hw_add_property (me, property, reg_array_property,
705		   cells, sizeof_cells,
706		   cells, sizeof_cells,
707		   NULL, permenant_object);
708
709  hw_free (me, cells);
710}
711
712int
713hw_find_reg_array_property (struct hw *me,
714			    const char *property,
715			    unsigned index,
716			    reg_property_spec *reg)
717{
718  const struct hw_property *node;
719  unsigned sizeof_entry = (nr_reg_property_cells (me, 1)
720			   * sizeof (unsigned_cell));
721  const unsigned_cell *cells;
722
723  /* locate the property */
724  node = hw_find_property (me, property);
725  if (node == NULL)
726    hw_abort (me, "property \"%s\" not found", property);
727  if (node->type != reg_array_property)
728    hw_abort (me, "property \"%s\" of wrong type (reg array)", property);
729
730  /* aligned ? */
731  if ((node->sizeof_array % sizeof_entry) != 0)
732    hw_abort (me, "property \"%s\" contains an incomplete number of entries",
733	      property);
734
735  /* within bounds? */
736  if (node->sizeof_array < sizeof_entry * (index + 1))
737    return 0;
738
739  /* find the range of interest */
740  cells = (unsigned_cell*)((char*)node->array + sizeof_entry * index);
741
742  /* copy the address out - converting as we go */
743  cells = cells_to_unit_address (cells, &reg->address,
744				 hw_unit_nr_address_cells (hw_parent (me)));
745
746  /* copy the size out - converting as we go */
747  cells = cells_to_unit_address (cells, &reg->size,
748				 hw_unit_nr_size_cells (hw_parent (me)));
749
750  return node->sizeof_array / sizeof_entry;
751}
752
753
754void
755hw_add_string_property (struct hw *me,
756			const char *property,
757			const char *string)
758{
759  hw_add_property (me, property, string_property,
760		   string, strlen(string) + 1,
761		   string, strlen(string) + 1,
762		   NULL, permenant_object);
763}
764
765const char *
766hw_find_string_property (struct hw *me,
767			 const char *property)
768{
769  const struct hw_property *node;
770  const char *string;
771  node = hw_find_property (me, property);
772  if (node == NULL)
773    hw_abort (me, "property \"%s\" not found", property);
774  if (node->type != string_property)
775    hw_abort (me, "property \"%s\" of wrong type (string)", property);
776  string = node->array;
777  ASSERT (strlen(string) + 1 == node->sizeof_array);
778  return string;
779}
780
781void
782hw_add_string_array_property (struct hw *me,
783			      const char *property,
784			      const string_property_spec *strings,
785			      unsigned nr_strings)
786{
787  int sizeof_array;
788  int string_nr;
789  char *array;
790  char *chp;
791  if (nr_strings == 0)
792    hw_abort (me, "property \"%s\" must be non-null", property);
793  /* total up the size of the needed array */
794  for (sizeof_array = 0, string_nr = 0;
795       string_nr < nr_strings;
796       string_nr ++)
797    {
798      sizeof_array += strlen (strings[string_nr]) + 1;
799    }
800  /* create the array */
801  array = (char*) hw_zalloc (me, sizeof_array);
802  chp = array;
803  for (string_nr = 0;
804       string_nr < nr_strings;
805       string_nr++)
806    {
807      strcpy (chp, strings[string_nr]);
808      chp += strlen (chp) + 1;
809    }
810  ASSERT (chp == array + sizeof_array);
811  /* now enter it */
812  hw_add_property (me, property, string_array_property,
813		   array, sizeof_array,
814		   array, sizeof_array,
815		   NULL, permenant_object);
816}
817
818int
819hw_find_string_array_property (struct hw *me,
820			       const char *property,
821			       unsigned index,
822			       string_property_spec *string)
823{
824  const struct hw_property *node;
825  node = hw_find_property (me, property);
826  if (node == NULL)
827    hw_abort (me, "property \"%s\" not found", property);
828  switch (node->type)
829    {
830    default:
831      hw_abort (me, "property \"%s\" of wrong type", property);
832      break;
833    case string_property:
834      if (index == 0)
835	{
836	  *string = node->array;
837	  ASSERT (strlen(*string) + 1 == node->sizeof_array);
838	  return 1;
839	}
840      break;
841    case array_property:
842      if (node->sizeof_array == 0
843	  || ((char*)node->array)[node->sizeof_array - 1] != '\0')
844	hw_abort (me, "property \"%s\" invalid for string array", property);
845      /* FALL THROUGH */
846    case string_array_property:
847      ASSERT (node->sizeof_array > 0);
848      ASSERT (((char*)node->array)[node->sizeof_array - 1] == '\0');
849      {
850	const char *chp = node->array;
851	int nr_entries = 0;
852	/* count the number of strings, keeping an eye out for the one
853	   we're looking for */
854	*string = chp;
855	do
856	  {
857	    if (*chp == '\0')
858	      {
859		/* next string */
860		nr_entries++;
861		chp++;
862		if (nr_entries == index)
863		  *string = chp;
864	      }
865	    else
866	      {
867		chp++;
868	      }
869	  } while (chp < (char*)node->array + node->sizeof_array);
870	if (index < nr_entries)
871	  return nr_entries;
872	else
873	  {
874	    *string = NULL;
875	    return 0;
876	  }
877      }
878      break;
879    }
880  return 0;
881}
882
883void
884hw_add_duplicate_property (struct hw *me,
885			   const char *property,
886			   const struct hw_property *original)
887{
888  struct hw_property_data *master;
889  TRACE (trace_devices,
890	 ("hw_add_duplicate_property(me=0x%lx, property=%s, ...)\n",
891	  (long)me, property));
892  if (original->disposition != permenant_object)
893    hw_abort (me, "Can only duplicate permenant objects");
894  /* find the original's master */
895  master = original->owner->properties_of_hw;
896  while (master->property != original)
897    {
898      master = master->next;
899      ASSERT(master != NULL);
900    }
901  /* now duplicate it */
902  hw_add_property (me, property,
903		   original->type,
904		   master->init_array, master->sizeof_init_array,
905		   original->array, original->sizeof_array,
906		   original, permenant_object);
907}
908