Commit | Line | Data |
---|---|---|
cb7a6892 MM |
1 | /* This file is part of the program psim. |
2 | ||
d6103e8e | 3 | Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au> |
cb7a6892 MM |
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 2 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, write to the Free Software | |
17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
18 | ||
19 | */ | |
20 | ||
21 | ||
93fac324 MM |
22 | #ifndef _DEVICE_TABLE_C_ |
23 | #define _DEVICE_TABLE_C_ | |
cb7a6892 | 24 | |
93fac324 MM |
25 | #ifndef STATIC_INLINE_DEVICE_TABLE |
26 | #define STATIC_INLINE_DEVICE_TABLE STATIC_INLINE | |
cb7a6892 MM |
27 | #endif |
28 | ||
cb7a6892 | 29 | #include <stdio.h> |
cb7a6892 | 30 | #include <fcntl.h> |
dec38dac MM |
31 | #include <signal.h> |
32 | #include <stdarg.h> | |
979c3c25 | 33 | #include <ctype.h> |
cb7a6892 | 34 | |
93fac324 MM |
35 | #include "device_table.h" |
36 | ||
cb7a6892 MM |
37 | #include "events.h" |
38 | ||
c494cadd MM |
39 | #ifdef HAVE_UNISTD_H |
40 | #include <unistd.h> | |
41 | #endif | |
42 | ||
43 | #ifdef HAVE_STDLIB_H | |
44 | #include <stdlib.h> | |
45 | #endif | |
46 | ||
dec38dac MM |
47 | #include "cpu.h" |
48 | ||
49 | #include "bfd.h" | |
cb7a6892 MM |
50 | |
51 | /* Helper functions */ | |
52 | ||
dec38dac MM |
53 | /* Generic device init: Attaches the device of size <nr_bytes> (taken |
54 | from <name>@<int>,<nr_bytes>) to its parent at address zero and | |
55 | with read/write access. */ | |
56 | ||
fe098bf4 | 57 | typedef struct _generic_reg_spec { |
d6103e8e MM |
58 | unsigned32 base; |
59 | unsigned32 size; | |
fe098bf4 MM |
60 | } generic_reg_spec; |
61 | ||
d6103e8e MM |
62 | |
63 | void | |
fe098bf4 | 64 | generic_device_init_address(device *me) |
cb7a6892 | 65 | { |
d6103e8e | 66 | const device_property *reg = device_find_array_property(me, "reg"); |
fe098bf4 MM |
67 | const generic_reg_spec *spec = reg->array; |
68 | int nr_entries = reg->sizeof_array / sizeof(generic_reg_spec); | |
d6103e8e | 69 | |
fe098bf4 | 70 | if ((reg->sizeof_array % sizeof(generic_reg_spec)) != 0) |
d6103e8e MM |
71 | error("devices/%s reg property is of wrong size\n", device_name(me)); |
72 | ||
73 | while (nr_entries > 0) { | |
74 | device_attach_address(device_parent(me), | |
75 | device_name(me), | |
76 | attach_callback, | |
77 | 0 /*space*/, | |
78 | BE2H_4(spec->base), | |
79 | BE2H_4(spec->size), | |
80 | access_read_write_exec, | |
81 | me); | |
82 | spec++; | |
83 | nr_entries--; | |
84 | } | |
cb7a6892 MM |
85 | } |
86 | ||
d6103e8e MM |
87 | int |
88 | generic_device_unit_decode(device *me, | |
89 | const char *unit, | |
90 | device_unit *phys) | |
91 | { | |
92 | memset(phys, 0, sizeof(device_unit)); | |
93 | if (unit == NULL) | |
94 | return 0; | |
95 | else { | |
96 | char *pos = (char*)unit; /* force for strtoul() */ | |
97 | while (1) { | |
98 | char *old_pos = pos; | |
99 | long int val = strtoul(pos, &pos, 0); | |
100 | if (old_pos == pos && *pos == '\0') | |
101 | return phys->nr_cells; | |
102 | if (old_pos == pos && *pos != '\0') | |
103 | return -1; | |
104 | if (phys->nr_cells == 4) | |
105 | return -1; | |
106 | phys->cells[phys->nr_cells] = val; | |
107 | phys->nr_cells++; | |
108 | } | |
109 | } | |
110 | } | |
111 | ||
112 | int | |
113 | generic_device_unit_encode(device *me, | |
114 | const device_unit *phys, | |
115 | char *buf, | |
116 | int sizeof_buf) | |
117 | { | |
118 | int i; | |
119 | int len; | |
120 | char *pos = buf; /* force for strtoul() */ | |
121 | for (i = 0; i < phys->nr_cells; i++) { | |
122 | if (pos != buf) { | |
123 | strcat(pos, ","); | |
124 | pos = strchr(pos, '\0'); | |
125 | } | |
126 | sprintf(pos, "0x%lx", (unsigned long)phys->cells[i]); | |
127 | pos = strchr(pos, '\0'); | |
128 | } | |
129 | len = pos - buf; | |
130 | if (len >= sizeof_buf) | |
131 | error("generic_unit_encode - buffer overflow\n"); | |
132 | return len; | |
133 | } | |
cb7a6892 | 134 | |
1dc7c0ed | 135 | /* DMA a file into memory */ |
93fac324 MM |
136 | STATIC_INLINE_DEVICE_TABLE int |
137 | dma_file(device *me, | |
1dc7c0ed MM |
138 | const char *file_name, |
139 | unsigned_word addr) | |
140 | { | |
141 | int count; | |
142 | int inc; | |
143 | FILE *image; | |
144 | char buf[1024]; | |
145 | ||
146 | /* get it open */ | |
147 | image = fopen(file_name, "r"); | |
148 | if (image == NULL) | |
149 | return -1; | |
150 | ||
151 | /* read it in slowly */ | |
152 | count = 0; | |
153 | while (1) { | |
93fac324 MM |
154 | inc = fread(buf, 1, sizeof(buf), image); |
155 | if (feof(image) || ferror(image)) | |
1dc7c0ed | 156 | break; |
93fac324 MM |
157 | if (device_dma_write_buffer(device_parent(me), |
158 | buf, | |
159 | 0 /*address-space*/, | |
160 | addr+count, | |
161 | inc /*nr-bytes*/, | |
162 | 1 /*violate ro*/) != inc) { | |
1dc7c0ed MM |
163 | fclose(image); |
164 | return -1; | |
165 | } | |
166 | count += inc; | |
167 | } | |
168 | ||
169 | /* close down again */ | |
170 | fclose(image); | |
171 | ||
172 | return count; | |
173 | } | |
174 | ||
175 | ||
dec38dac MM |
176 | \f |
177 | /* ignore/passthrough versions of each function */ | |
178 | ||
93fac324 | 179 | void |
fe098bf4 | 180 | passthrough_device_address_attach(device *me, |
93fac324 MM |
181 | const char *name, |
182 | attach_type attach, | |
183 | int space, | |
184 | unsigned_word addr, | |
185 | unsigned nr_bytes, | |
186 | access_type access, | |
187 | device *who) /*callback/default*/ | |
dec38dac | 188 | { |
93fac324 MM |
189 | device_attach_address(device_parent(me), name, attach, |
190 | space, addr, nr_bytes, | |
191 | access, | |
192 | who); | |
dec38dac MM |
193 | } |
194 | ||
93fac324 | 195 | void |
fe098bf4 | 196 | passthrough_device_address_detach(device *me, |
93fac324 MM |
197 | const char *name, |
198 | attach_type attach, | |
199 | int space, | |
200 | unsigned_word addr, | |
d6103e8e MM |
201 | unsigned nr_bytes, |
202 | access_type access, | |
203 | device *who) /*callback/default*/ | |
dec38dac | 204 | { |
93fac324 MM |
205 | device_detach_address(device_parent(me), name, attach, |
206 | space, addr, nr_bytes, access, | |
207 | who); | |
dec38dac MM |
208 | } |
209 | ||
93fac324 MM |
210 | unsigned |
211 | passthrough_device_dma_read_buffer(device *me, | |
d6103e8e MM |
212 | void *dest, |
213 | int space, | |
214 | unsigned_word addr, | |
215 | unsigned nr_bytes) | |
dec38dac | 216 | { |
93fac324 MM |
217 | return device_dma_read_buffer(device_parent(me), dest, |
218 | space, addr, nr_bytes); | |
dec38dac MM |
219 | } |
220 | ||
93fac324 MM |
221 | unsigned |
222 | passthrough_device_dma_write_buffer(device *me, | |
dec38dac | 223 | const void *source, |
5b4d72dd | 224 | int space, |
dec38dac MM |
225 | unsigned_word addr, |
226 | unsigned nr_bytes, | |
227 | int violate_read_only_section) | |
228 | { | |
93fac324 MM |
229 | return device_dma_write_buffer(device_parent(me), source, |
230 | space, addr, | |
231 | nr_bytes, | |
232 | violate_read_only_section); | |
dec38dac MM |
233 | } |
234 | ||
d6103e8e MM |
235 | int |
236 | ignore_device_unit_decode(device *me, | |
237 | const char *unit, | |
238 | device_unit *phys) | |
dec38dac | 239 | { |
d6103e8e MM |
240 | memset(phys, 0, sizeof(device_unit)); |
241 | return 0; | |
dec38dac MM |
242 | } |
243 | ||
244 | ||
93fac324 | 245 | static const device_callbacks passthrough_callbacks = { |
fe098bf4 MM |
246 | { NULL, }, /* init */ |
247 | { passthrough_device_address_attach, | |
248 | passthrough_device_address_detach, }, | |
249 | { NULL, }, /* IO */ | |
250 | { passthrough_device_dma_read_buffer, passthrough_device_dma_write_buffer, }, | |
251 | { NULL, }, /* interrupt */ | |
252 | { generic_device_unit_decode, | |
253 | generic_device_unit_encode, }, | |
dec38dac MM |
254 | }; |
255 | ||
256 | ||
257 | \f | |
d6103e8e | 258 | /* Register init device: register@<nothing> |
dec38dac | 259 | |
93fac324 | 260 | Properties attached to the register device specify the name/value |
979c3c25 MM |
261 | initialization pair for cpu registers. |
262 | ||
d6103e8e MM |
263 | A specific processor can be initialized by creating a property with |
264 | a name like `0.pc'. | |
265 | ||
266 | Properties are normally processed old-to-new and this function | |
267 | needs to allow older (first in) properties to override new (last | |
268 | in) ones. The suport function do_register_init() manages this. */ | |
dec38dac | 269 | |
93fac324 | 270 | static void |
d6103e8e | 271 | do_register_init(device *me, |
d6103e8e MM |
272 | const device_property *prop) |
273 | { | |
fe098bf4 | 274 | psim *system = device_system(me); |
d6103e8e MM |
275 | if (prop != NULL) { |
276 | const char *name = prop->name; | |
277 | unsigned32 value = device_find_integer_property(me, name); | |
278 | int processor; | |
279 | ||
fe098bf4 | 280 | do_register_init(me, device_next_property(prop)); |
d6103e8e MM |
281 | |
282 | if (strchr(name, '.') == NULL) { | |
283 | processor = -1; | |
284 | DTRACE(register, ("%s=0x%lx\n", name, (unsigned long)value)); | |
285 | } | |
286 | else { | |
287 | char *end; | |
288 | processor = strtoul(name, &end, 0); | |
289 | ASSERT(end[0] == '.'); | |
290 | name = end+1; | |
291 | DTRACE(register, ("%d.%s=0x%lx\n", processor, name, | |
292 | (unsigned long)value)); | |
293 | } | |
294 | psim_write_register(system, processor, /* all processors */ | |
295 | &value, | |
296 | name, | |
297 | cooked_transfer); | |
979c3c25 | 298 | } |
93fac324 | 299 | } |
d6103e8e | 300 | |
93fac324 MM |
301 | |
302 | static void | |
fe098bf4 | 303 | register_init_data_callback(device *me) |
dec38dac | 304 | { |
d6103e8e | 305 | const device_property *prop = device_find_property(me, NULL); |
fe098bf4 | 306 | do_register_init(me, prop); |
dec38dac MM |
307 | } |
308 | ||
309 | ||
310 | static device_callbacks const register_callbacks = { | |
fe098bf4 MM |
311 | { NULL, register_init_data_callback, }, |
312 | { NULL, }, /* address */ | |
313 | { NULL, }, /* IO */ | |
314 | { NULL, }, /* DMA */ | |
315 | { NULL, }, /* interrupt */ | |
316 | { NULL, }, /* unit */ | |
dec38dac MM |
317 | }; |
318 | ||
319 | ||
320 | \f | |
d6103e8e MM |
321 | /* Trace device: |
322 | ||
323 | Properties attached to the trace device are names and values for | |
324 | the various trace variables. When initialized trace goes through | |
325 | the propertie and sets the global trace variables so that they | |
326 | match what was specified in the device tree. */ | |
327 | ||
328 | static void | |
fe098bf4 | 329 | trace_init_data_callback(device *me) |
d6103e8e MM |
330 | { |
331 | const device_property *prop = device_find_property(me, NULL); | |
332 | while (prop != NULL) { | |
333 | const char *name = prop->name; | |
334 | unsigned32 value = device_find_integer_property(me, name); | |
335 | trace_option(name, value); | |
336 | prop = device_next_property(prop); | |
337 | } | |
338 | } | |
339 | ||
340 | ||
341 | static device_callbacks const trace_callbacks = { | |
fe098bf4 MM |
342 | { NULL, trace_init_data_callback, }, |
343 | { NULL, }, /* address */ | |
344 | { NULL, }, /* IO */ | |
345 | { NULL, }, /* DMA */ | |
346 | { NULL, }, /* interrupt */ | |
347 | { NULL, }, /* unit */ | |
d6103e8e MM |
348 | }; |
349 | ||
350 | ||
351 | \f | |
352 | /* VEA VM: | |
353 | ||
354 | vm@<stack-base> | |
355 | stack-base = | |
356 | nr-bytes = | |
dec38dac MM |
357 | |
358 | A VEA mode device. This sets its self up as the default memory | |
359 | device capturing all accesses (reads/writes) to currently unmapped | |
360 | addresses. If the unmaped access falls within unallocated stack or | |
361 | heap address ranges then memory is allocated and the access is | |
362 | allowed to continue. | |
363 | ||
364 | During init phase, this device expects to receive `attach' requests | |
365 | from its children for the text/data/bss memory areas. Typically, | |
366 | this would be done by the binary device. | |
367 | ||
368 | STACK: The location of the stack in memory is specified as part of | |
369 | the devices name. Unmaped accesses that fall within the stack | |
370 | space result in the allocated stack being grown downwards so that | |
371 | it includes the page of the culprit access. | |
372 | ||
373 | HEAP: During initialization, the vm device monitors all `attach' | |
374 | operations from its children using this to determine the initial | |
375 | location of the heap. The heap is then extended by system calls | |
376 | that frob the heap upper bound variable (see system.c). */ | |
377 | ||
378 | ||
379 | typedef struct _vm_device { | |
380 | /* area of memory valid for stack addresses */ | |
381 | unsigned_word stack_base; /* min possible stack value */ | |
382 | unsigned_word stack_bound; | |
383 | unsigned_word stack_lower_limit; | |
384 | /* area of memory valid for heap addresses */ | |
385 | unsigned_word heap_base; | |
386 | unsigned_word heap_bound; | |
387 | unsigned_word heap_upper_limit; | |
388 | } vm_device; | |
389 | ||
390 | ||
93fac324 | 391 | static void |
fe098bf4 | 392 | vm_init_address_callback(device *me) |
dec38dac | 393 | { |
93fac324 | 394 | vm_device *vm = (vm_device*)device_data(me); |
dec38dac MM |
395 | |
396 | /* revert the stack/heap variables to their defaults */ | |
d6103e8e MM |
397 | vm->stack_base = device_find_integer_property(me, "stack-base"); |
398 | vm->stack_bound = (vm->stack_base | |
399 | + device_find_integer_property(me, "nr-bytes")); | |
dec38dac MM |
400 | vm->stack_lower_limit = vm->stack_bound; |
401 | vm->heap_base = 0; | |
402 | vm->heap_bound = 0; | |
403 | vm->heap_upper_limit = 0; | |
404 | ||
405 | /* establish this device as the default memory handler */ | |
93fac324 MM |
406 | device_attach_address(device_parent(me), |
407 | device_name(me), | |
fe098bf4 | 408 | attach_callback + 1, |
93fac324 MM |
409 | 0 /*address space - ignore*/, |
410 | 0 /*addr - ignore*/, | |
fe098bf4 | 411 | (((unsigned)0)-1) /*nr_bytes - ignore*/, |
93fac324 MM |
412 | access_read_write /*access*/, |
413 | me); | |
dec38dac MM |
414 | } |
415 | ||
416 | ||
93fac324 MM |
417 | static void |
418 | vm_attach_address(device *me, | |
dec38dac | 419 | const char *name, |
5b4d72dd MM |
420 | attach_type attach, |
421 | int space, | |
dec38dac MM |
422 | unsigned_word addr, |
423 | unsigned nr_bytes, | |
424 | access_type access, | |
93fac324 | 425 | device *who) /*callback/default*/ |
dec38dac | 426 | { |
93fac324 | 427 | vm_device *vm = (vm_device*)device_data(me); |
dec38dac MM |
428 | /* update end of bss if necessary */ |
429 | if (vm->heap_base < addr + nr_bytes) { | |
430 | vm->heap_base = addr + nr_bytes; | |
431 | vm->heap_bound = addr + nr_bytes; | |
432 | vm->heap_upper_limit = addr + nr_bytes; | |
433 | } | |
93fac324 MM |
434 | device_attach_address(device_parent(me), |
435 | "vm@0x0,0", /* stop remap */ | |
436 | attach_raw_memory, | |
437 | 0 /*address space*/, | |
438 | addr, | |
439 | nr_bytes, | |
440 | access, | |
441 | me); | |
dec38dac MM |
442 | } |
443 | ||
444 | ||
93fac324 MM |
445 | STATIC_INLINE_DEVICE_TABLE unsigned |
446 | add_vm_space(device *me, | |
dec38dac MM |
447 | unsigned_word addr, |
448 | unsigned nr_bytes, | |
449 | cpu *processor, | |
450 | unsigned_word cia) | |
451 | { | |
93fac324 | 452 | vm_device *vm = (vm_device*)device_data(me); |
dec38dac MM |
453 | unsigned_word block_addr; |
454 | unsigned block_nr_bytes; | |
455 | ||
456 | /* an address in the stack area, allocate just down to the addressed | |
457 | page */ | |
458 | if (addr >= vm->stack_base && addr < vm->stack_lower_limit) { | |
459 | block_addr = FLOOR_PAGE(addr); | |
460 | block_nr_bytes = vm->stack_lower_limit - block_addr; | |
461 | vm->stack_lower_limit = block_addr; | |
462 | } | |
463 | /* an address in the heap area, allocate all of the required heap */ | |
464 | else if (addr >= vm->heap_upper_limit && addr < vm->heap_bound) { | |
465 | block_addr = vm->heap_upper_limit; | |
466 | block_nr_bytes = vm->heap_bound - vm->heap_upper_limit; | |
467 | vm->heap_upper_limit = vm->heap_bound; | |
468 | } | |
469 | /* oops - an invalid address - abort the cpu */ | |
470 | else if (processor != NULL) { | |
471 | cpu_halt(processor, cia, was_signalled, SIGSEGV); | |
472 | return 0; | |
473 | } | |
474 | /* 2*oops - an invalid address and no processor */ | |
475 | else { | |
476 | return 0; | |
477 | } | |
478 | ||
479 | /* got the parameters, allocate the space */ | |
93fac324 MM |
480 | device_attach_address(device_parent(me), |
481 | "vm@0x0,0", /* stop remap */ | |
482 | attach_raw_memory, | |
483 | 0 /*address space*/, | |
484 | block_addr, | |
485 | block_nr_bytes, | |
486 | access_read_write, | |
487 | me); | |
dec38dac MM |
488 | return block_nr_bytes; |
489 | } | |
490 | ||
491 | ||
93fac324 MM |
492 | static unsigned |
493 | vm_io_read_buffer_callback(device *me, | |
dec38dac | 494 | void *dest, |
5b4d72dd | 495 | int space, |
dec38dac MM |
496 | unsigned_word addr, |
497 | unsigned nr_bytes, | |
498 | cpu *processor, | |
499 | unsigned_word cia) | |
500 | { | |
501 | if (add_vm_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) { | |
1dc7c0ed | 502 | memset(dest, 0, nr_bytes); /* always initialized to zero */ |
dec38dac MM |
503 | return nr_bytes; |
504 | } | |
505 | else | |
506 | return 0; | |
507 | } | |
508 | ||
509 | ||
93fac324 MM |
510 | static unsigned |
511 | vm_io_write_buffer_callback(device *me, | |
dec38dac | 512 | const void *source, |
5b4d72dd | 513 | int space, |
dec38dac MM |
514 | unsigned_word addr, |
515 | unsigned nr_bytes, | |
516 | cpu *processor, | |
517 | unsigned_word cia) | |
518 | { | |
519 | if (add_vm_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) { | |
93fac324 MM |
520 | return device_dma_write_buffer(device_parent(me), source, |
521 | space, addr, | |
522 | nr_bytes, | |
523 | 0/*violate_read_only*/); | |
dec38dac MM |
524 | } |
525 | else | |
526 | return 0; | |
527 | } | |
528 | ||
529 | ||
fe098bf4 | 530 | static int |
93fac324 | 531 | vm_ioctl_callback(device *me, |
dec38dac MM |
532 | psim *system, |
533 | cpu *processor, | |
534 | unsigned_word cia, | |
93fac324 | 535 | va_list ap) |
dec38dac MM |
536 | { |
537 | /* While the caller is notified that the heap has grown by the | |
fe098bf4 | 538 | requested amount, the heap is actually extended out to a page |
dec38dac | 539 | boundary. */ |
93fac324 | 540 | vm_device *vm = (vm_device*)device_data(me); |
fe098bf4 MM |
541 | unsigned_word requested_break = va_arg(ap, unsigned_word); |
542 | unsigned_word new_break = ALIGN_8(requested_break); | |
dec38dac MM |
543 | unsigned_word old_break = vm->heap_bound; |
544 | signed_word delta = new_break - old_break; | |
545 | if (delta > 0) | |
546 | vm->heap_bound = ALIGN_PAGE(new_break); | |
fe098bf4 | 547 | return 0; |
dec38dac MM |
548 | } |
549 | ||
550 | ||
551 | static device_callbacks const vm_callbacks = { | |
fe098bf4 MM |
552 | { vm_init_address_callback, }, |
553 | { vm_attach_address, | |
554 | passthrough_device_address_detach, }, | |
555 | { vm_io_read_buffer_callback, | |
556 | vm_io_write_buffer_callback, }, | |
557 | { NULL, passthrough_device_dma_write_buffer, }, | |
558 | { NULL, }, /* interrupt */ | |
559 | { generic_device_unit_decode, | |
560 | generic_device_unit_encode, }, | |
561 | { NULL, }, /* instance */ | |
dec38dac MM |
562 | vm_ioctl_callback, |
563 | }; | |
564 | ||
565 | ||
93fac324 | 566 | static void * |
dec38dac | 567 | vea_vm_create(const char *name, |
d6103e8e MM |
568 | const device_unit *address, |
569 | const char *args, | |
93fac324 | 570 | device *parent) |
dec38dac MM |
571 | { |
572 | vm_device *vm = ZALLOC(vm_device); | |
93fac324 | 573 | return vm; |
dec38dac MM |
574 | } |
575 | ||
576 | ||
dec38dac MM |
577 | \f |
578 | /* FILE device: file@0x<address>,<file-name> | |
579 | (later - file@0x<address>,<size>,<file-offset>,<file-name>) | |
580 | ||
581 | Specifies a file to read directly into memory starting at <address> */ | |
582 | ||
583 | ||
93fac324 | 584 | static void |
fe098bf4 | 585 | file_init_data_callback(device *me) |
cb7a6892 | 586 | { |
1dc7c0ed | 587 | int count; |
d6103e8e MM |
588 | const char *file_name = device_find_string_property(me, "file-name"); |
589 | unsigned_word addr = device_find_integer_property(me, "real-address"); | |
1dc7c0ed MM |
590 | /* load the file */ |
591 | count = dma_file(me, file_name, addr); | |
592 | if (count < 0) | |
d6103e8e MM |
593 | error("device_table/%s - Problem loading file %s\n", |
594 | device_name(me), file_name); | |
dec38dac | 595 | } |
cb7a6892 | 596 | |
cb7a6892 | 597 | |
dec38dac | 598 | static device_callbacks const file_callbacks = { |
fe098bf4 MM |
599 | { NULL, file_init_data_callback, }, |
600 | { NULL, }, /* address */ | |
601 | { NULL, }, /* IO */ | |
602 | { NULL, }, /* DMA */ | |
603 | { NULL, }, /* interrupt */ | |
604 | { NULL, }, /* unit */ | |
dec38dac MM |
605 | }; |
606 | ||
607 | ||
93fac324 | 608 | \f |
d6103e8e MM |
609 | /* DATA device: data@<address> |
610 | ||
611 | <data> - property containing the value to store | |
612 | <real-address> - address to store data at | |
93fac324 | 613 | |
d6103e8e | 614 | Store <data> at <address> using approperiate byte order */ |
93fac324 MM |
615 | |
616 | static void | |
fe098bf4 | 617 | data_init_data_callback(device *me) |
93fac324 | 618 | { |
d6103e8e MM |
619 | unsigned_word addr = device_find_integer_property(me, "real-address"); |
620 | const device_property *data = device_find_property(me, "data"); | |
621 | if (data == NULL) | |
622 | error("devices/data - missing data property\n"); | |
623 | switch (data->type) { | |
624 | case integer_property: | |
625 | { | |
626 | unsigned32 buf = device_find_integer_property(me, "data"); | |
627 | H2T(buf); | |
628 | if (device_dma_write_buffer(device_parent(me), | |
629 | &buf, | |
630 | 0 /*address-space*/, | |
631 | addr, | |
632 | sizeof(buf), /*nr-bytes*/ | |
633 | 1 /*violate ro*/) != sizeof(buf)) | |
634 | error("devices/%s - Problem storing integer 0x%x at 0x%lx\n", | |
635 | device_name(me), (long)buf, (unsigned long)addr); | |
636 | } | |
93fac324 | 637 | break; |
d6103e8e MM |
638 | default: |
639 | error("devices/%s - write of this data is not yet implemented\n", device_name(me)); | |
93fac324 MM |
640 | break; |
641 | } | |
93fac324 MM |
642 | } |
643 | ||
644 | ||
645 | static device_callbacks const data_callbacks = { | |
fe098bf4 MM |
646 | { NULL, data_init_data_callback, }, |
647 | { NULL, }, /* address */ | |
648 | { NULL, }, /* IO */ | |
649 | { NULL, }, /* DMA */ | |
650 | { NULL, }, /* interrupt */ | |
651 | { NULL, }, /* unit */ | |
93fac324 MM |
652 | }; |
653 | ||
654 | ||
dec38dac | 655 | \f |
d6103e8e MM |
656 | /* HTAB: |
657 | ||
658 | htab@<real-address> | |
659 | real-address = | |
660 | nr-bytes = | |
661 | ||
662 | pte@<real-address> | |
663 | real-address = | |
664 | virtual-address = | |
665 | nr-bytes = | |
666 | wimg = | |
667 | pp = | |
668 | ||
669 | pte@<real-address> | |
670 | real-address = | |
671 | file-name = | |
672 | wimg = | |
673 | pp = | |
674 | ||
dec38dac MM |
675 | HTAB defines the location (in physical memory) of a HASH table. |
676 | PTE (as a child of HTAB) defines a mapping that is to be entered | |
677 | into that table. | |
678 | ||
679 | NB: All the work in this device is done during init by the PTE. | |
680 | The pte, looks up its parent to determine the address of the HTAB | |
681 | and then uses DMA calls to establish the required mapping. */ | |
682 | ||
93fac324 MM |
683 | STATIC_INLINE_DEVICE_TABLE void |
684 | htab_decode_hash_table(device *parent, | |
685 | unsigned32 *htaborg, | |
686 | unsigned32 *htabmask) | |
687 | { | |
688 | unsigned_word htab_ra; | |
689 | unsigned htab_nr_bytes; | |
690 | unsigned n; | |
691 | /* determine the location/size of the hash table */ | |
692 | if (parent == NULL | |
d6103e8e MM |
693 | || strcmp(device_name(parent), "htab") != 0) |
694 | error("devices/htab - missing htab parent device\n"); | |
695 | htab_ra = device_find_integer_property(parent, "real-address"); | |
696 | htab_nr_bytes = device_find_integer_property(parent, "nr-bytes"); | |
93fac324 MM |
697 | for (n = htab_nr_bytes; n > 1; n = n / 2) { |
698 | if (n % 2 != 0) | |
699 | error("devices/%s - htab size 0x%x not a power of two\n", | |
700 | device_name(parent), htab_nr_bytes); | |
701 | } | |
702 | *htaborg = htab_ra; | |
703 | *htabmask = MASKED32(htab_nr_bytes - 1, 7, 31-6); | |
704 | if ((htab_ra & INSERTED32(*htabmask, 7, 15)) != 0) { | |
705 | error("devices/%s - htaborg 0x%x not aligned to htabmask 0x%x\n", | |
706 | device_name(parent), *htaborg, *htabmask); | |
707 | } | |
979c3c25 MM |
708 | DTRACE(htab, ("htab - htaborg=0x%lx htabmask=0x%lx\n", |
709 | (unsigned long)*htaborg, (unsigned long)*htabmask)); | |
93fac324 MM |
710 | } |
711 | ||
93fac324 MM |
712 | STATIC_INLINE void |
713 | htab_map_page(device *me, | |
714 | unsigned_word ra, | |
715 | unsigned64 va, | |
716 | unsigned wimg, | |
717 | unsigned pp, | |
718 | unsigned32 htaborg, | |
719 | unsigned32 htabmask) | |
720 | { | |
721 | unsigned64 vpn = va << 12; | |
722 | unsigned32 vsid = INSERTED32(EXTRACTED64(vpn, 0, 23), 0, 23); | |
723 | unsigned32 page = INSERTED32(EXTRACTED64(vpn, 24, 39), 0, 15); | |
724 | unsigned32 hash = INSERTED32(EXTRACTED32(vsid, 5, 23) | |
725 | ^ EXTRACTED32(page, 0, 15), | |
726 | 7, 31-6); | |
727 | int h; | |
728 | for (h = 0; h < 2; h++) { | |
729 | unsigned32 pteg = (htaborg | (hash & htabmask)); | |
730 | int pti; | |
731 | for (pti = 0; pti < 8; pti++, pteg += 8) { | |
979c3c25 MM |
732 | unsigned32 current_target_pte0; |
733 | unsigned32 current_pte0; | |
93fac324 | 734 | if (device_dma_read_buffer(device_parent(me), |
979c3c25 | 735 | ¤t_target_pte0, |
93fac324 MM |
736 | 0, /*space*/ |
737 | pteg, | |
979c3c25 | 738 | sizeof(current_target_pte0)) != 4) |
93fac324 MM |
739 | error("htab_init_callback() failed to read a pte at 0x%x\n", |
740 | pteg); | |
979c3c25 MM |
741 | current_pte0 = T2H_4(current_target_pte0); |
742 | if (!MASKED32(current_pte0, 0, 0)) { | |
93fac324 MM |
743 | /* empty pte fill it */ |
744 | unsigned32 pte0 = (MASK32(0, 0) | |
745 | | INSERTED32(EXTRACTED32(vsid, 0, 23), 1, 24) | |
746 | | INSERTED32(h, 25, 25) | |
747 | | INSERTED32(EXTRACTED32(page, 0, 5), 26, 31)); | |
979c3c25 | 748 | unsigned32 target_pte0 = H2T_4(pte0); |
93fac324 MM |
749 | unsigned32 pte1 = (INSERTED32(EXTRACTED32(ra, 0, 19), 0, 19) |
750 | | INSERTED32(wimg, 25, 28) | |
751 | | INSERTED32(pp, 30, 31)); | |
979c3c25 | 752 | unsigned32 target_pte1 = H2T_4(pte1); |
93fac324 | 753 | if (device_dma_write_buffer(device_parent(me), |
979c3c25 | 754 | &target_pte0, |
93fac324 MM |
755 | 0, /*space*/ |
756 | pteg, | |
979c3c25 | 757 | sizeof(target_pte0), |
93fac324 MM |
758 | 1/*ro?*/) != 4 |
759 | || device_dma_write_buffer(device_parent(me), | |
979c3c25 | 760 | &target_pte1, |
93fac324 MM |
761 | 0, /*space*/ |
762 | pteg + 4, | |
979c3c25 | 763 | sizeof(target_pte1), |
93fac324 MM |
764 | 1/*ro?*/) != 4) |
765 | error("htab_init_callback() failed to write a pte a 0x%x\n", | |
766 | pteg); | |
767 | DTRACE(htab, ("map - va=0x%lx ra=0x%lx &pte0=0x%lx pte0=0x%lx pte1=0x%lx\n", | |
768 | (unsigned long)va, (unsigned long)ra, | |
769 | (unsigned long)pteg, | |
770 | (unsigned long)pte0, (unsigned long)pte1)); | |
771 | return; | |
772 | } | |
773 | } | |
774 | /* re-hash */ | |
775 | hash = MASKED32(~hash, 0, 18); | |
776 | } | |
777 | } | |
778 | ||
779 | STATIC_INLINE_DEVICE_TABLE void | |
780 | htab_map_region(device *me, | |
781 | unsigned_word pte_ra, | |
782 | unsigned_word pte_va, | |
783 | unsigned nr_bytes, | |
784 | unsigned wimg, | |
785 | unsigned pp, | |
786 | unsigned32 htaborg, | |
787 | unsigned32 htabmask) | |
788 | { | |
789 | unsigned_word ra; | |
790 | unsigned64 va; | |
791 | /* go through all pages and create a pte for each */ | |
792 | for (ra = pte_ra, va = (signed_word)pte_va; | |
793 | ra < pte_ra + nr_bytes; | |
794 | ra += 0x1000, va += 0x1000) { | |
795 | htab_map_page(me, ra, va, wimg, pp, htaborg, htabmask); | |
796 | } | |
797 | } | |
798 | ||
799 | typedef struct _htab_binary_sizes { | |
800 | unsigned_word text_ra; | |
801 | unsigned_word text_base; | |
802 | unsigned_word text_bound; | |
803 | unsigned_word data_ra; | |
804 | unsigned_word data_base; | |
805 | unsigned data_bound; | |
806 | device *me; | |
807 | } htab_binary_sizes; | |
808 | ||
809 | STATIC_INLINE_DEVICE_TABLE void | |
810 | htab_sum_binary(bfd *abfd, | |
811 | sec_ptr sec, | |
812 | PTR data) | |
813 | { | |
814 | htab_binary_sizes *sizes = (htab_binary_sizes*)data; | |
815 | unsigned_word size = bfd_get_section_size_before_reloc (sec); | |
816 | unsigned_word vma = bfd_get_section_vma (abfd, sec); | |
817 | ||
818 | /* skip the section if no memory to allocate */ | |
819 | if (! (bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) | |
820 | return; | |
821 | ||
822 | if ((bfd_get_section_flags (abfd, sec) & SEC_CODE) | |
823 | || (bfd_get_section_flags (abfd, sec) & SEC_READONLY)) { | |
824 | if (sizes->text_bound < vma + size) | |
825 | sizes->text_bound = ALIGN_PAGE(vma + size); | |
826 | if (sizes->text_base > vma) | |
827 | sizes->text_base = FLOOR_PAGE(vma); | |
828 | } | |
829 | else if ((bfd_get_section_flags (abfd, sec) & SEC_DATA) | |
830 | || (bfd_get_section_flags (abfd, sec) & SEC_ALLOC)) { | |
831 | if (sizes->data_bound < vma + size) | |
832 | sizes->data_bound = ALIGN_PAGE(vma + size); | |
833 | if (sizes->data_base > vma) | |
834 | sizes->data_base = FLOOR_PAGE(vma); | |
835 | } | |
836 | } | |
837 | ||
838 | STATIC_INLINE_DEVICE_TABLE void | |
839 | htab_dma_binary(bfd *abfd, | |
840 | sec_ptr sec, | |
841 | PTR data) | |
842 | { | |
843 | htab_binary_sizes *sizes = (htab_binary_sizes*)data; | |
844 | void *section_init; | |
845 | unsigned_word section_vma; | |
846 | unsigned_word section_size; | |
847 | unsigned_word section_ra; | |
848 | device *me = sizes->me; | |
849 | ||
850 | /* skip the section if no memory to allocate */ | |
851 | if (! (bfd_get_section_flags(abfd, sec) & SEC_ALLOC)) | |
852 | return; | |
853 | ||
854 | /* check/ignore any sections of size zero */ | |
855 | section_size = bfd_get_section_size_before_reloc(sec); | |
856 | if (section_size == 0) | |
857 | return; | |
858 | ||
859 | /* if nothing to load, ignore this one */ | |
860 | if (! (bfd_get_section_flags(abfd, sec) & SEC_LOAD)) | |
861 | return; | |
862 | ||
863 | /* find where it is to go */ | |
864 | section_vma = bfd_get_section_vma(abfd, sec); | |
865 | section_ra = 0; | |
866 | if ((bfd_get_section_flags (abfd, sec) & SEC_CODE) | |
867 | || (bfd_get_section_flags (abfd, sec) & SEC_READONLY)) | |
868 | section_ra = (section_vma - sizes->text_base + sizes->text_ra); | |
869 | else if ((bfd_get_section_flags (abfd, sec) & SEC_DATA)) | |
870 | section_ra = (section_vma - sizes->data_base + sizes->data_ra); | |
871 | else | |
872 | return; /* just ignore it */ | |
873 | ||
874 | DTRACE(htab, | |
875 | ("load - name=%-7s vma=0x%.8lx size=%6ld ra=0x%.8lx flags=%3lx(%s%s%s%s%s )\n", | |
876 | bfd_get_section_name(abfd, sec), | |
877 | (long)section_vma, | |
878 | (long)section_size, | |
879 | (long)section_ra, | |
880 | (long)bfd_get_section_flags(abfd, sec), | |
881 | bfd_get_section_flags(abfd, sec) & SEC_LOAD ? " LOAD" : "", | |
882 | bfd_get_section_flags(abfd, sec) & SEC_CODE ? " CODE" : "", | |
883 | bfd_get_section_flags(abfd, sec) & SEC_DATA ? " DATA" : "", | |
884 | bfd_get_section_flags(abfd, sec) & SEC_ALLOC ? " ALLOC" : "", | |
885 | bfd_get_section_flags(abfd, sec) & SEC_READONLY ? " READONLY" : "" | |
886 | )); | |
887 | ||
888 | /* dma in the sections data */ | |
889 | section_init = zalloc(section_size); | |
890 | if (!bfd_get_section_contents(abfd, | |
891 | sec, | |
892 | section_init, 0, | |
893 | section_size)) { | |
894 | bfd_perror("devices/pte"); | |
895 | error("devices/%s - no data loaded\n", device_name(me)); | |
896 | } | |
897 | if (device_dma_write_buffer(device_parent(me), | |
898 | section_init, | |
899 | 0 /*space*/, | |
900 | section_ra, | |
901 | section_size, | |
902 | 1 /*violate_read_only*/) | |
903 | != section_size) | |
904 | error("devices/%s - broken dma transfer\n", device_name(me)); | |
905 | zfree(section_init); /* only free if load */ | |
906 | } | |
907 | ||
93fac324 MM |
908 | STATIC_INLINE_DEVICE_TABLE void |
909 | htab_map_binary(device *me, | |
910 | unsigned_word ra, | |
911 | unsigned wimg, | |
912 | unsigned pp, | |
d6103e8e | 913 | const char *file_name, |
93fac324 MM |
914 | unsigned32 htaborg, |
915 | unsigned32 htabmask) | |
916 | { | |
917 | htab_binary_sizes sizes; | |
918 | bfd *image; | |
919 | sizes.text_base = -1; | |
920 | sizes.data_base = -1; | |
921 | sizes.text_bound = 0; | |
922 | sizes.data_bound = 0; | |
923 | sizes.me = me; | |
924 | ||
925 | /* open the file */ | |
926 | image = bfd_openr(file_name, NULL); | |
927 | if (image == NULL) { | |
928 | bfd_perror("devices/pte"); | |
929 | error("devices/%s - the file %s not loaded\n", device_name(me), file_name); | |
930 | } | |
931 | ||
932 | /* check it is valid */ | |
933 | if (!bfd_check_format(image, bfd_object)) { | |
934 | bfd_close(image); | |
935 | error("devices/%s - the file %s has an invalid binary format\n", | |
936 | device_name(me), file_name); | |
937 | } | |
938 | ||
939 | /* determine the size of each of the files regions */ | |
940 | bfd_map_over_sections (image, htab_sum_binary, (PTR) &sizes); | |
941 | ||
942 | /* determine the real addresses of the sections */ | |
943 | sizes.text_ra = ra; | |
944 | sizes.data_ra = ALIGN_PAGE(sizes.text_ra + | |
945 | (sizes.text_bound - sizes.text_base)); | |
946 | ||
947 | DTRACE(htab, ("text map - base=0x%lx bound=0x%lx ra=0x%lx\n", | |
979c3c25 MM |
948 | (unsigned long)sizes.text_base, |
949 | (unsigned long)sizes.text_bound, | |
950 | (unsigned long)sizes.text_ra)); | |
93fac324 | 951 | DTRACE(htab, ("data map - base=0x%lx bound=0x%lx ra=0x%lx\n", |
979c3c25 MM |
952 | (unsigned long)sizes.data_base, |
953 | (unsigned long)sizes.data_bound, | |
954 | (unsigned long)sizes.data_ra)); | |
93fac324 MM |
955 | |
956 | /* set up virtual memory maps for each of the regions */ | |
957 | htab_map_region(me, sizes.text_ra, sizes.text_base, | |
958 | sizes.text_bound - sizes.text_base, | |
959 | wimg, pp, | |
960 | htaborg, htabmask); | |
961 | htab_map_region(me, sizes.data_ra, sizes.data_base, | |
962 | sizes.data_bound - sizes.data_base, | |
963 | wimg, pp, | |
964 | htaborg, htabmask); | |
965 | ||
966 | /* dma the sections into physical memory */ | |
967 | bfd_map_over_sections (image, htab_dma_binary, (PTR) &sizes); | |
968 | } | |
969 | ||
970 | static void | |
fe098bf4 | 971 | htab_init_data_callback(device *me) |
dec38dac | 972 | { |
1dc7c0ed MM |
973 | if (WITH_TARGET_WORD_BITSIZE != 32) |
974 | error("devices/htab: only 32bit targets currently suported\n"); | |
93fac324 | 975 | |
dec38dac | 976 | /* only the pte does work */ |
d6103e8e | 977 | if (strcmp(device_name(me), "pte") == 0) { |
93fac324 MM |
978 | unsigned32 htaborg; |
979 | unsigned32 htabmask; | |
1dc7c0ed | 980 | |
93fac324 | 981 | htab_decode_hash_table(device_parent(me), &htaborg, &htabmask); |
1dc7c0ed | 982 | |
d6103e8e MM |
983 | if (device_find_property(me, "file-name") != NULL) { |
984 | /* map in a binary */ | |
985 | unsigned32 pte_ra = device_find_integer_property(me, "real-address"); | |
986 | unsigned pte_wimg = device_find_integer_property(me, "wimg"); | |
987 | unsigned pte_pp = device_find_integer_property(me, "pp"); | |
988 | const char *file_name = device_find_string_property(me, "file-name"); | |
989 | DTRACE(htab, ("pte - ra=0x%lx, wimg=%ld, pp=%ld, file-name=%s\n", | |
979c3c25 MM |
990 | (unsigned long)pte_ra, |
991 | (unsigned long)pte_wimg, | |
992 | (long)pte_pp, | |
993 | file_name)); | |
93fac324 MM |
994 | htab_map_binary(me, pte_ra, pte_wimg, pte_pp, file_name, |
995 | htaborg, htabmask); | |
996 | } | |
997 | else { | |
d6103e8e MM |
998 | /* handle a normal mapping definition */ |
999 | /* so that 0xff...0 is make 0xffffff00 */ | |
1000 | signed32 pte_va = device_find_integer_property(me, "virtual-address"); | |
1001 | unsigned32 pte_ra = device_find_integer_property(me, "real-address"); | |
1002 | unsigned pte_nr_bytes = device_find_integer_property(me, "nr-bytes"); | |
1003 | unsigned pte_wimg = device_find_integer_property(me, "wimg"); | |
1004 | unsigned pte_pp = device_find_integer_property(me, "pp"); | |
1005 | DTRACE(htab, ("pte - ra=0x%lx, wimg=%ld, pp=%ld, va=0x%lx, nr_bytes=%ld\n", | |
1006 | (unsigned long)pte_ra, | |
1007 | (long)pte_wimg, | |
1008 | (long)pte_pp, | |
1009 | (unsigned long)pte_va, | |
1010 | (long)pte_nr_bytes)); | |
1011 | htab_map_region(me, pte_ra, pte_va, pte_nr_bytes, pte_wimg, pte_pp, | |
1012 | htaborg, htabmask); | |
1dc7c0ed | 1013 | } |
dec38dac | 1014 | } |
cb7a6892 MM |
1015 | } |
1016 | ||
dec38dac MM |
1017 | |
1018 | static device_callbacks const htab_callbacks = { | |
fe098bf4 MM |
1019 | { NULL, htab_init_data_callback, }, |
1020 | { NULL, }, /* address */ | |
1021 | { NULL, }, /* IO */ | |
1022 | { passthrough_device_dma_read_buffer, | |
1023 | passthrough_device_dma_write_buffer, }, | |
1024 | { NULL, }, /* interrupt */ | |
1025 | { generic_device_unit_decode, | |
1026 | generic_device_unit_encode, }, | |
dec38dac | 1027 | }; |
cb7a6892 MM |
1028 | |
1029 | ||
dec38dac | 1030 | \f |
93fac324 | 1031 | /* Load device: binary |
cb7a6892 | 1032 | |
93fac324 MM |
1033 | Single property the name of which specifies the file (understood by |
1034 | BFD) that is to be DMAed into memory as part of init */ | |
cb7a6892 | 1035 | |
93fac324 | 1036 | STATIC_INLINE_DEVICE_TABLE void |
5b4d72dd MM |
1037 | update_for_binary_section(bfd *abfd, |
1038 | asection *the_section, | |
1039 | PTR obj) | |
cb7a6892 | 1040 | { |
dec38dac MM |
1041 | unsigned_word section_vma; |
1042 | unsigned_word section_size; | |
1043 | access_type access; | |
1044 | device *me = (device*)obj; | |
1045 | ||
1046 | /* skip the section if no memory to allocate */ | |
1047 | if (! (bfd_get_section_flags(abfd, the_section) & SEC_ALLOC)) | |
1048 | return; | |
1049 | ||
1050 | /* check/ignore any sections of size zero */ | |
1051 | section_size = bfd_get_section_size_before_reloc(the_section); | |
1052 | if (section_size == 0) | |
1053 | return; | |
1054 | ||
1055 | /* find where it is to go */ | |
1056 | section_vma = bfd_get_section_vma(abfd, the_section); | |
1057 | ||
5b4d72dd | 1058 | DTRACE(binary, |
c5addc19 | 1059 | ("name=%-7s, vma=0x%.8lx, size=%6ld, flags=%3lx(%s%s%s%s%s )\n", |
5b4d72dd | 1060 | bfd_get_section_name(abfd, the_section), |
c5addc19 MM |
1061 | (long)section_vma, |
1062 | (long)section_size, | |
1063 | (long)bfd_get_section_flags(abfd, the_section), | |
5b4d72dd MM |
1064 | bfd_get_section_flags(abfd, the_section) & SEC_LOAD ? " LOAD" : "", |
1065 | bfd_get_section_flags(abfd, the_section) & SEC_CODE ? " CODE" : "", | |
1066 | bfd_get_section_flags(abfd, the_section) & SEC_DATA ? " DATA" : "", | |
1067 | bfd_get_section_flags(abfd, the_section) & SEC_ALLOC ? " ALLOC" : "", | |
1068 | bfd_get_section_flags(abfd, the_section) & SEC_READONLY ? " READONLY" : "" | |
1069 | )); | |
dec38dac | 1070 | |
fe098bf4 MM |
1071 | /* If there is an .interp section, it means it needs a shared library interpreter. */ |
1072 | if (strcmp(".interp", bfd_get_section_name(abfd, the_section)) == 0) | |
1073 | error("Shared libraries are not yet supported.\n"); | |
1074 | ||
dec38dac MM |
1075 | /* determine the devices access */ |
1076 | access = access_read; | |
1077 | if (bfd_get_section_flags(abfd, the_section) & SEC_CODE) | |
1078 | access |= access_exec; | |
1079 | if (!(bfd_get_section_flags(abfd, the_section) & SEC_READONLY)) | |
1080 | access |= access_write; | |
1081 | ||
1082 | /* if a map, pass up a request to create the memory in core */ | |
93fac324 MM |
1083 | if (strncmp(device_name(me), "map-binary", strlen("map-binary")) == 0) |
1084 | device_attach_address(device_parent(me), | |
1085 | device_name(me), | |
1086 | attach_raw_memory, | |
1087 | 0 /*address space*/, | |
1088 | section_vma, | |
1089 | section_size, | |
1090 | access, | |
1091 | me); | |
dec38dac MM |
1092 | |
1093 | /* if a load dma in the required data */ | |
1094 | if (bfd_get_section_flags(abfd, the_section) & SEC_LOAD) { | |
1095 | void *section_init = zalloc(section_size); | |
1096 | if (!bfd_get_section_contents(abfd, | |
1097 | the_section, | |
1098 | section_init, 0, | |
1099 | section_size)) { | |
1100 | bfd_perror("core:load_section()"); | |
1101 | error("load of data failed"); | |
1102 | return; | |
1103 | } | |
93fac324 MM |
1104 | if (device_dma_write_buffer(device_parent(me), |
1105 | section_init, | |
1106 | 0 /*space*/, | |
1107 | section_vma, | |
1108 | section_size, | |
1109 | 1 /*violate_read_only*/) | |
dec38dac | 1110 | != section_size) |
93fac324 | 1111 | error("data_init_callback() broken transfer for %s\n", device_name(me)); |
dec38dac MM |
1112 | zfree(section_init); /* only free if load */ |
1113 | } | |
cb7a6892 MM |
1114 | } |
1115 | ||
dec38dac | 1116 | |
93fac324 | 1117 | static void |
fe098bf4 | 1118 | binary_init_data_callback(device *me) |
cb7a6892 | 1119 | { |
d6103e8e MM |
1120 | /* get the file name */ |
1121 | const char *file_name = device_find_string_property(me, "file-name"); | |
dec38dac MM |
1122 | bfd *image; |
1123 | ||
dec38dac MM |
1124 | /* open the file */ |
1125 | image = bfd_openr(file_name, NULL); | |
1126 | if (image == NULL) { | |
1dc7c0ed | 1127 | bfd_perror("devices/binary"); |
93fac324 | 1128 | error("devices/%s - the file %s not loaded\n", device_name(me), file_name); |
dec38dac MM |
1129 | } |
1130 | ||
1131 | /* check it is valid */ | |
1132 | if (!bfd_check_format(image, bfd_object)) { | |
dec38dac | 1133 | bfd_close(image); |
93fac324 MM |
1134 | error("devices/%s - the file %s has an invalid binary format\n", |
1135 | device_name(me), file_name); | |
dec38dac MM |
1136 | } |
1137 | ||
1138 | /* and the data sections */ | |
1139 | bfd_map_over_sections(image, | |
5b4d72dd | 1140 | update_for_binary_section, |
dec38dac MM |
1141 | (PTR)me); |
1142 | ||
1143 | bfd_close(image); | |
cb7a6892 MM |
1144 | } |
1145 | ||
1146 | ||
dec38dac | 1147 | static device_callbacks const binary_callbacks = { |
fe098bf4 MM |
1148 | { NULL, binary_init_data_callback, }, |
1149 | { NULL, }, /* address */ | |
1150 | { NULL, }, /* IO */ | |
1151 | { NULL, }, /* DMA */ | |
1152 | { NULL, }, /* interrupt */ | |
1153 | { NULL, }, /* unit */ | |
cb7a6892 MM |
1154 | }; |
1155 | ||
dec38dac MM |
1156 | |
1157 | \f | |
1158 | /* Stack device: stack@<type> | |
1159 | ||
1160 | Has a single IOCTL to create a stack frame of the specified type. | |
1161 | If <type> is elf or xcoff then a corresponding stack is created. | |
1162 | Any other value of type is ignored. | |
1163 | ||
1164 | The IOCTL takes the additional arguments: | |
1165 | ||
1166 | unsigned_word stack_end -- where the stack should come down from | |
1167 | char **argv -- ... | |
1168 | char **envp -- ... | |
1169 | ||
1170 | */ | |
1171 | ||
93fac324 | 1172 | STATIC_INLINE_DEVICE_TABLE int |
dec38dac MM |
1173 | sizeof_argument_strings(char **arg) |
1174 | { | |
1175 | int sizeof_strings = 0; | |
1176 | ||
1177 | /* robust */ | |
1178 | if (arg == NULL) | |
1179 | return 0; | |
1180 | ||
1181 | /* add up all the string sizes (padding as we go) */ | |
1182 | for (; *arg != NULL; arg++) { | |
1183 | int len = strlen(*arg) + 1; | |
1184 | sizeof_strings += ALIGN_8(len); | |
1185 | } | |
1186 | ||
1187 | return sizeof_strings; | |
1188 | } | |
1189 | ||
93fac324 | 1190 | STATIC_INLINE_DEVICE_TABLE int |
dec38dac MM |
1191 | number_of_arguments(char **arg) |
1192 | { | |
1193 | int nr; | |
1194 | if (arg == NULL) | |
1195 | return 0; | |
1196 | for (nr = 0; *arg != NULL; arg++, nr++); | |
1197 | return nr; | |
1198 | } | |
1199 | ||
93fac324 | 1200 | STATIC_INLINE_DEVICE_TABLE int |
dec38dac MM |
1201 | sizeof_arguments(char **arg) |
1202 | { | |
1203 | return ALIGN_8((number_of_arguments(arg) + 1) * sizeof(unsigned_word)); | |
1204 | } | |
1205 | ||
93fac324 | 1206 | STATIC_INLINE_DEVICE_TABLE void |
dec38dac MM |
1207 | write_stack_arguments(psim *system, |
1208 | char **arg, | |
1209 | unsigned_word start_block, | |
1210 | unsigned_word end_block, | |
1211 | unsigned_word start_arg, | |
1212 | unsigned_word end_arg) | |
1213 | { | |
5b4d72dd | 1214 | DTRACE(stack, |
c5addc19 MM |
1215 | ("write_stack_arguments(system=0x%lx, arg=0x%lx, start_block=0x%lx, end_block=0x%lx, start_arg=0x%lx, end_arg=0x%lx)\n", |
1216 | (long)system, (long)arg, (long)start_block, (long)end_block, (long)start_arg, (long)end_arg)); | |
dec38dac MM |
1217 | if (arg == NULL) |
1218 | error("write_arguments: character array NULL\n"); | |
1219 | /* only copy in arguments, memory is already zero */ | |
1220 | for (; *arg != NULL; arg++) { | |
1221 | int len = strlen(*arg)+1; | |
1222 | unsigned_word target_start_block; | |
5b4d72dd | 1223 | DTRACE(stack, |
c5addc19 MM |
1224 | ("write_stack_arguments() write %s=%s at %s=0x%lx %s=0x%lx %s=0x%lx\n", |
1225 | "**arg", *arg, "start_block", (long)start_block, | |
1226 | "len", (long)len, "start_arg", (long)start_arg)); | |
dec38dac MM |
1227 | if (psim_write_memory(system, 0, *arg, |
1228 | start_block, len, | |
1229 | 0/*violate_readonly*/) != len) | |
1230 | error("write_stack_arguments() - write of **arg (%s) at 0x%x failed\n", | |
1231 | *arg, start_block); | |
1232 | target_start_block = H2T_word(start_block); | |
1233 | if (psim_write_memory(system, 0, &target_start_block, | |
1234 | start_arg, sizeof(target_start_block), | |
1235 | 0) != sizeof(target_start_block)) | |
1236 | error("write_stack_arguments() - write of *arg failed\n"); | |
1237 | start_block += ALIGN_8(len); | |
1238 | start_arg += sizeof(start_block); | |
1239 | } | |
1240 | start_arg += sizeof(start_block); /*the null at the end*/ | |
1241 | if (start_block != end_block | |
1242 | || ALIGN_8(start_arg) != end_arg) | |
1243 | error("write_stack_arguments - possible corruption\n"); | |
5b4d72dd MM |
1244 | DTRACE(stack, |
1245 | ("write_stack_arguments() = void\n")); | |
dec38dac MM |
1246 | } |
1247 | ||
93fac324 | 1248 | STATIC_INLINE_DEVICE_TABLE void |
dec38dac MM |
1249 | create_elf_stack_frame(psim *system, |
1250 | unsigned_word bottom_of_stack, | |
1251 | char **argv, | |
1252 | char **envp) | |
1253 | { | |
1254 | /* fixme - this is over aligned */ | |
1255 | ||
1256 | /* information block */ | |
1257 | const unsigned sizeof_envp_block = sizeof_argument_strings(envp); | |
1258 | const unsigned_word start_envp_block = bottom_of_stack - sizeof_envp_block; | |
1259 | const unsigned sizeof_argv_block = sizeof_argument_strings(argv); | |
1260 | const unsigned_word start_argv_block = start_envp_block - sizeof_argv_block; | |
1261 | ||
1262 | /* auxiliary vector - contains only one entry */ | |
1263 | const unsigned sizeof_aux_entry = 2*sizeof(unsigned_word); /* magic */ | |
1264 | const unsigned_word start_aux = start_argv_block - ALIGN_8(sizeof_aux_entry); | |
1265 | ||
1266 | /* environment points (including null sentinal) */ | |
1267 | const unsigned sizeof_envp = sizeof_arguments(envp); | |
1268 | const unsigned_word start_envp = start_aux - sizeof_envp; | |
1269 | ||
1270 | /* argument pointers (including null sentinal) */ | |
1271 | const int argc = number_of_arguments(argv); | |
1272 | const unsigned sizeof_argv = sizeof_arguments(argv); | |
1273 | const unsigned_word start_argv = start_envp - sizeof_argv; | |
1274 | ||
1275 | /* link register save address - alligned to a 16byte boundary */ | |
1276 | const unsigned_word top_of_stack = ((start_argv | |
1277 | - 2 * sizeof(unsigned_word)) | |
1278 | & ~0xf); | |
1279 | ||
1280 | /* install arguments on stack */ | |
1281 | write_stack_arguments(system, envp, | |
1282 | start_envp_block, bottom_of_stack, | |
1283 | start_envp, start_aux); | |
1284 | write_stack_arguments(system, argv, | |
1285 | start_argv_block, start_envp_block, | |
1286 | start_argv, start_envp); | |
1287 | ||
1288 | /* set up the registers */ | |
1289 | psim_write_register(system, -1, | |
1290 | &top_of_stack, "sp", cooked_transfer); | |
1291 | psim_write_register(system, -1, | |
1292 | &argc, "r3", cooked_transfer); | |
1293 | psim_write_register(system, -1, | |
1294 | &start_argv, "r4", cooked_transfer); | |
1295 | psim_write_register(system, -1, | |
1296 | &start_envp, "r5", cooked_transfer); | |
1297 | psim_write_register(system, -1, | |
1298 | &start_aux, "r6", cooked_transfer); | |
1299 | } | |
1300 | ||
93fac324 | 1301 | STATIC_INLINE_DEVICE_TABLE void |
dec38dac MM |
1302 | create_aix_stack_frame(psim *system, |
1303 | unsigned_word bottom_of_stack, | |
1304 | char **argv, | |
1305 | char **envp) | |
cb7a6892 | 1306 | { |
dec38dac MM |
1307 | unsigned_word core_envp; |
1308 | unsigned_word core_argv; | |
1309 | unsigned_word core_argc; | |
1310 | unsigned_word core_aux; | |
1311 | unsigned_word top_of_stack; | |
1312 | ||
1313 | /* cheat - create an elf stack frame */ | |
1314 | create_elf_stack_frame(system, bottom_of_stack, argv, envp); | |
1315 | ||
1316 | /* extract argument addresses from registers */ | |
1317 | psim_read_register(system, 0, &top_of_stack, "r1", cooked_transfer); | |
1318 | psim_read_register(system, 0, &core_argc, "r3", cooked_transfer); | |
1319 | psim_read_register(system, 0, &core_argv, "r4", cooked_transfer); | |
1320 | psim_read_register(system, 0, &core_envp, "r5", cooked_transfer); | |
1321 | psim_read_register(system, 0, &core_aux, "r6", cooked_transfer); | |
1322 | ||
1323 | /* extract arguments from registers */ | |
1324 | error("create_aix_stack_frame() - what happens next?\n"); | |
1325 | } | |
1326 | ||
1327 | ||
cb7a6892 | 1328 | |
fe098bf4 | 1329 | static int |
93fac324 | 1330 | stack_ioctl_callback(device *me, |
dec38dac MM |
1331 | psim *system, |
1332 | cpu *processor, | |
1333 | unsigned_word cia, | |
93fac324 | 1334 | va_list ap) |
dec38dac | 1335 | { |
dec38dac | 1336 | unsigned_word stack_pointer; |
93fac324 | 1337 | const char *stack_type; |
dec38dac MM |
1338 | char **argv; |
1339 | char **envp; | |
dec38dac MM |
1340 | stack_pointer = va_arg(ap, unsigned_word); |
1341 | argv = va_arg(ap, char **); | |
1342 | envp = va_arg(ap, char **); | |
5b4d72dd | 1343 | DTRACE(stack, |
c5addc19 | 1344 | ("stack_ioctl_callback(me=0x%lx:%s, system=0x%lx, processor=0x%lx, cia=0x%lx, argv=0x%lx, envp=0x%lx)\n", |
93fac324 | 1345 | (long)me, device_name(me), (long)system, (long)processor, (long)cia, (long)argv, (long)envp)); |
d6103e8e MM |
1346 | stack_type = device_find_string_property(me, "stack-type"); |
1347 | if (strcmp(stack_type, "elf") == 0) | |
1348 | create_elf_stack_frame(system, stack_pointer, argv, envp); | |
1349 | else if (strcmp(stack_type, "xcoff") == 0) | |
1350 | create_aix_stack_frame(system, stack_pointer, argv, envp); | |
5b4d72dd MM |
1351 | DTRACE(stack, |
1352 | ("stack_ioctl_callback() = void\n")); | |
fe098bf4 | 1353 | return 0; |
cb7a6892 MM |
1354 | } |
1355 | ||
dec38dac | 1356 | static device_callbacks const stack_callbacks = { |
fe098bf4 MM |
1357 | { NULL, }, |
1358 | { NULL, }, /* address */ | |
1359 | { NULL, }, /* IO */ | |
1360 | { NULL, }, /* DMA */ | |
1361 | { NULL, }, /* interrupt */ | |
1362 | { NULL, }, /* unit */ | |
1363 | { NULL, }, /* instance */ | |
dec38dac | 1364 | stack_ioctl_callback, |
cb7a6892 MM |
1365 | }; |
1366 | ||
1367 | ||
dec38dac | 1368 | \f |
fe098bf4 | 1369 | static const device_descriptor old_device_table[] = { |
93fac324 | 1370 | { "vm", vea_vm_create, &vm_callbacks }, |
dec38dac | 1371 | { "register", NULL, ®ister_callbacks }, |
dec38dac | 1372 | { "file", NULL, &file_callbacks }, |
93fac324 | 1373 | { "data", NULL, &data_callbacks }, |
dec38dac MM |
1374 | { "htab", NULL, &htab_callbacks }, |
1375 | { "pte", NULL, &htab_callbacks }, /* yep - uses htab's table */ | |
1376 | { "stack", NULL, &stack_callbacks }, | |
dec38dac MM |
1377 | { "load-binary", NULL, &binary_callbacks }, |
1378 | { "map-binary", NULL, &binary_callbacks }, | |
d6103e8e MM |
1379 | /* standard OpenBoot devices */ |
1380 | { "aliases", NULL, &passthrough_callbacks }, | |
93fac324 | 1381 | { "options", NULL, &passthrough_callbacks }, |
93fac324 | 1382 | { "chosen", NULL, &passthrough_callbacks }, |
d6103e8e MM |
1383 | { "packages", NULL, &passthrough_callbacks }, |
1384 | { "cpus", NULL, &passthrough_callbacks }, | |
1385 | { "openprom", NULL, &passthrough_callbacks }, | |
1386 | { "init", NULL, &passthrough_callbacks }, | |
1387 | { "trace", NULL, &trace_callbacks }, | |
dec38dac | 1388 | { NULL }, |
cb7a6892 MM |
1389 | }; |
1390 | ||
fe098bf4 MM |
1391 | const device_descriptor *const device_table[] = { |
1392 | old_device_table, | |
1393 | #include "hw.c" | |
1394 | NULL, | |
1395 | }; | |
1396 | ||
1397 | ||
93fac324 | 1398 | #endif /* _DEVICE_TABLE_C_ */ |