PowerPC64 ld segfault with code in non-executable sections
[external/binutils.git] / sim / ppc / device_table.c
1 /*  This file is part of the program psim.
2
3     Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 3 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14  
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, see <http://www.gnu.org/licenses/>.
17  
18     */
19
20
21 #ifndef _DEVICE_TABLE_C_
22 #define _DEVICE_TABLE_C_
23
24 #include "device_table.h"
25
26 #if HAVE_STDLIB_H
27 #include <stdlib.h>
28 #endif
29
30 #include <ctype.h>
31
32
33 /* Helper functions */
34
35
36 /* Go through the devices various reg properties for those that
37    specify attach addresses */
38
39
40 void
41 generic_device_init_address(device *me)
42 {
43   static const char *(reg_property_names[]) = {
44     "attach-addresses",
45     "assigned-addresses",
46     "reg",
47     "alternate-reg" ,
48     NULL
49   };
50   const char **reg_property_name;
51   int nr_valid_reg_properties = 0;
52   for (reg_property_name = reg_property_names;
53        *reg_property_name != NULL;
54        reg_property_name++) {
55     if (device_find_property(me, *reg_property_name) != NULL) {
56       reg_property_spec reg;
57       int reg_entry;
58       for (reg_entry = 0;
59            device_find_reg_array_property(me, *reg_property_name, reg_entry,
60                                           &reg);
61            reg_entry++) {
62         unsigned_word attach_address;
63         int attach_space;
64         unsigned attach_size;
65         if (!device_address_to_attach_address(device_parent(me),
66                                               &reg.address,
67                                               &attach_space, &attach_address,
68                                               me))
69           continue;
70         if (!device_size_to_attach_size(device_parent(me),
71                                         &reg.size,
72                                         &attach_size, me))
73           continue;
74         device_attach_address(device_parent(me),
75                               attach_callback,
76                               attach_space, attach_address, attach_size,
77                               access_read_write_exec,
78                               me);
79         nr_valid_reg_properties++;
80       }
81       /* if first option matches don't try for any others */
82       if (reg_property_name == reg_property_names)
83         break;
84     }
85   }
86 }
87
88 int
89 generic_device_unit_decode(device *bus,
90                            const char *unit,
91                            device_unit *phys)
92 {
93   memset(phys, 0, sizeof(device_unit));
94   if (unit == NULL)
95     return 0;
96   else {
97     int nr_cells = 0;
98     const int max_nr_cells = device_nr_address_cells(bus);
99     while (1) {
100       char *end = NULL;
101       unsigned long val;
102       val = strtoul(unit, &end, 0);
103       /* parse error? */
104       if (unit == end)
105         return -1;
106       /* two many cells? */
107       if (nr_cells >= max_nr_cells)
108         return -1;
109       /* save it */
110       phys->cells[nr_cells] = val;
111       nr_cells++;
112       unit = end;
113       /* more to follow? */
114       if (isspace(*unit) || *unit == '\0')
115         break;
116       if (*unit != ',')
117         return -1;
118       unit++;
119     }
120     if (nr_cells < max_nr_cells) {
121       /* shift everything to correct position */
122       int i;
123       for (i = 1; i <= nr_cells; i++)
124         phys->cells[max_nr_cells - i] = phys->cells[nr_cells - i];
125       for (i = 0; i < (max_nr_cells - nr_cells); i++)
126         phys->cells[i] = 0;
127     }
128     phys->nr_cells = max_nr_cells;
129     return max_nr_cells;
130   }
131 }
132
133 int
134 generic_device_unit_encode(device *bus,
135                            const device_unit *phys,
136                            char *buf,
137                            int sizeof_buf)
138 {
139   int i;
140   int len;
141   char *pos = buf;
142   /* skip leading zero's */
143   for (i = 0; i < phys->nr_cells; i++) {
144     if (phys->cells[i] != 0)
145       break;
146   }
147   /* don't output anything if empty */
148   if (phys->nr_cells == 0) {
149     strcpy(pos, "");
150     len = 0;
151   }
152   else if (i == phys->nr_cells) {
153     /* all zero */
154     strcpy(pos, "0");
155     len = 1;
156   }
157   else {
158     for (; i < phys->nr_cells; i++) {
159       if (pos != buf) {
160         strcat(pos, ",");
161         pos = strchr(pos, '\0');
162       }
163       if (phys->cells[i] < 10)
164         sprintf(pos, "%ld", (unsigned long)phys->cells[i]);
165       else
166         sprintf(pos, "0x%lx", (unsigned long)phys->cells[i]);
167       pos = strchr(pos, '\0');
168     }
169     len = pos - buf;
170   }
171   if (len >= sizeof_buf)
172     error("generic_unit_encode - buffer overflow\n");
173   return len;
174 }
175
176 int
177 generic_device_address_to_attach_address(device *me,
178                                          const device_unit *address,
179                                          int *attach_space,
180                                          unsigned_word *attach_address,
181                                          device *client)
182 {
183   int i;
184   for (i = 0; i < address->nr_cells - 2; i++) {
185     if (address->cells[i] != 0)
186       device_error(me, "Only 32bit addresses supported");
187   }
188   if (address->nr_cells >= 2)
189     *attach_space = address->cells[address->nr_cells - 2];
190   else
191     *attach_space = 0;
192   *attach_address = address->cells[address->nr_cells - 1];
193   return 1;
194 }
195
196 int
197 generic_device_size_to_attach_size(device *me,
198                                    const device_unit *size,
199                                    unsigned *nr_bytes,
200                                    device *client)
201 {
202   int i;
203   for (i = 0; i < size->nr_cells - 1; i++) {
204     if (size->cells[i] != 0)
205       device_error(me, "Only 32bit sizes supported");
206   }
207   *nr_bytes = size->cells[0];
208   return *nr_bytes;
209 }
210
211
212 /* ignore/passthrough versions of each function */
213
214 void
215 passthrough_device_address_attach(device *me,
216                                   attach_type attach,
217                                   int space,
218                                   unsigned_word addr,
219                                   unsigned nr_bytes,
220                                   access_type access,
221                                   device *client) /*callback/default*/
222 {
223   device_attach_address(device_parent(me), attach,
224                         space, addr, nr_bytes,
225                         access,
226                         client);
227 }
228
229 void
230 passthrough_device_address_detach(device *me,
231                                   attach_type attach,
232                                   int space,
233                                   unsigned_word addr,
234                                   unsigned nr_bytes,
235                                   access_type access,
236                                   device *client) /*callback/default*/
237 {
238   device_detach_address(device_parent(me), attach,
239                         space, addr, nr_bytes, access,
240                         client);
241 }
242
243 unsigned
244 passthrough_device_dma_read_buffer(device *me,
245                                    void *dest,
246                                    int space,
247                                    unsigned_word addr,
248                                    unsigned nr_bytes)
249 {
250   return device_dma_read_buffer(device_parent(me), dest,
251                                 space, addr, nr_bytes);
252 }
253
254 unsigned
255 passthrough_device_dma_write_buffer(device *me,
256                              const void *source,
257                              int space,
258                              unsigned_word addr,
259                              unsigned nr_bytes,
260                              int violate_read_only_section)
261 {
262   return device_dma_write_buffer(device_parent(me), source,
263                                  space, addr,
264                                  nr_bytes,
265                                  violate_read_only_section);
266 }
267
268 int
269 ignore_device_unit_decode(device *me,
270                           const char *unit,
271                           device_unit *phys)
272 {
273   memset(phys, 0, sizeof(device_unit));
274   return 0;
275 }
276
277
278 static const device_callbacks passthrough_callbacks = {
279   { NULL, }, /* init */
280   { passthrough_device_address_attach,
281     passthrough_device_address_detach, },
282   { NULL, }, /* IO */
283   { passthrough_device_dma_read_buffer, passthrough_device_dma_write_buffer, },
284   { NULL, }, /* interrupt */
285   { generic_device_unit_decode,
286     generic_device_unit_encode, },
287 };
288
289
290 static const device_descriptor ob_device_table[] = {
291   /* standard OpenBoot devices */
292   { "aliases", NULL, &passthrough_callbacks },
293   { "options", NULL, &passthrough_callbacks },
294   { "chosen", NULL, &passthrough_callbacks },
295   { "packages", NULL, &passthrough_callbacks },
296   { "cpus", NULL, &passthrough_callbacks },
297   { "openprom", NULL, &passthrough_callbacks },
298   { "init", NULL, &passthrough_callbacks },
299   { NULL },
300 };
301
302 const device_descriptor *const device_table[] = {
303   ob_device_table,
304 #include "hw.c"
305   NULL,
306 };
307
308
309 #endif /* _DEVICE_TABLE_C_ */