]>
Commit | Line | Data |
---|---|---|
cb7a6892 MM |
1 | /* This file is part of the program psim. |
2 | ||
3 | Copyright (C) 1994-1995, Andrew Cagney <[email protected]> | |
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 | ||
22 | #ifndef _DEVICES_C_ | |
23 | #define _DEVICES_C_ | |
24 | ||
25 | #ifndef STATIC_INLINE_DEVICES | |
26 | #define STATIC_INLINE_DEVICES STATIC_INLINE | |
27 | #endif | |
28 | ||
cb7a6892 | 29 | #include <stdio.h> |
cb7a6892 | 30 | #include <fcntl.h> |
dec38dac MM |
31 | #include <signal.h> |
32 | #include <stdarg.h> | |
cb7a6892 MM |
33 | |
34 | #include "basics.h" | |
cb7a6892 MM |
35 | #include "devices.h" |
36 | #include "events.h" | |
37 | ||
c494cadd MM |
38 | #ifdef HAVE_UNISTD_H |
39 | #include <unistd.h> | |
40 | #endif | |
41 | ||
42 | #ifdef HAVE_STDLIB_H | |
43 | #include <stdlib.h> | |
44 | #endif | |
45 | ||
46 | #ifdef HAVE_STRING_H | |
47 | #include <string.h> | |
48 | #else | |
49 | #ifdef HAVE_STRINGS_H | |
50 | #include <strings.h> | |
51 | #endif | |
52 | #endif | |
53 | ||
dec38dac MM |
54 | #include "cpu.h" |
55 | ||
56 | #include "bfd.h" | |
cb7a6892 MM |
57 | |
58 | /* Helper functions */ | |
59 | ||
dec38dac MM |
60 | /* Generic device init: Attaches the device of size <nr_bytes> (taken |
61 | from <name>@<int>,<nr_bytes>) to its parent at address zero and | |
62 | with read/write access. */ | |
63 | ||
cb7a6892 | 64 | STATIC_INLINE_DEVICES void |
dec38dac MM |
65 | generic_init_callback(const device *me, |
66 | psim *system) | |
cb7a6892 | 67 | { |
dec38dac MM |
68 | unsigned_word addr; |
69 | unsigned nr_bytes; | |
70 | if (scand_uw_u(me->name, &addr, &nr_bytes) != 2) | |
71 | error("generic_init_callback() invalid nr_bytes in %s\n", me->name); | |
72 | me->parent->callback->attach_address(me->parent, | |
73 | me->name, | |
74 | attach_callback, | |
5b4d72dd | 75 | 0 /*space*/, |
dec38dac MM |
76 | addr, |
77 | nr_bytes, | |
78 | access_read_write, | |
79 | me); | |
cb7a6892 MM |
80 | } |
81 | ||
82 | ||
dec38dac MM |
83 | \f |
84 | /* inimplemented versions of each function */ | |
cb7a6892 | 85 | |
dec38dac MM |
86 | INLINE_DEVICES void |
87 | unimp_device_init(const device *me, | |
88 | psim *system) | |
89 | { | |
90 | error("device_init_callback for %s not implemented\n", me->name); | |
91 | } | |
cb7a6892 | 92 | |
dec38dac MM |
93 | INLINE_DEVICES void |
94 | unimp_device_attach_address(const device *me, | |
95 | const char *name, | |
96 | attach_type type, | |
5b4d72dd | 97 | int space, |
dec38dac MM |
98 | unsigned_word addr, |
99 | unsigned nr_bytes, | |
100 | access_type access, | |
101 | const device *who) /*callback/default*/ | |
102 | { | |
103 | error("device_attach_address_callback for %s not implemented\n", me->name); | |
104 | } | |
cb7a6892 | 105 | |
dec38dac MM |
106 | INLINE_DEVICES void |
107 | unimp_device_detach_address(const device *me, | |
108 | const char *name, | |
109 | attach_type type, | |
5b4d72dd | 110 | int space, |
dec38dac MM |
111 | unsigned_word addr, |
112 | unsigned nr_bytes, | |
113 | access_type access, | |
114 | const device *who) /*callback/default*/ | |
115 | { | |
116 | error("device_detach_address_callback for %s not implemented\n", me->name); | |
117 | } | |
cb7a6892 | 118 | |
dec38dac MM |
119 | INLINE_DEVICES unsigned |
120 | unimp_device_io_read_buffer(const device *me, | |
121 | void *dest, | |
5b4d72dd | 122 | int space, |
dec38dac MM |
123 | unsigned_word addr, |
124 | unsigned nr_bytes, | |
125 | cpu *processor, | |
126 | unsigned_word cia) | |
127 | { | |
128 | error("device_io_read_buffer_callback for %s not implemented\n", me->name); | |
129 | return 0; | |
130 | } | |
cb7a6892 | 131 | |
dec38dac MM |
132 | INLINE_DEVICES unsigned |
133 | unimp_device_io_write_buffer(const device *me, | |
134 | const void *source, | |
5b4d72dd | 135 | int space, |
dec38dac MM |
136 | unsigned_word addr, |
137 | unsigned nr_bytes, | |
138 | cpu *processor, | |
139 | unsigned_word cia) | |
140 | { | |
141 | error("device_io_write_buffer_callback for %s not implemented\n", me->name); | |
142 | return 0; | |
143 | } | |
144 | ||
145 | INLINE_DEVICES unsigned | |
146 | unimp_device_dma_read_buffer(const device *me, | |
147 | void *target, | |
5b4d72dd | 148 | int space, |
dec38dac MM |
149 | unsigned_word addr, |
150 | unsigned nr_bytes) | |
151 | { | |
152 | error("device_dma_read_buffer_callback for %s not implemented\n", me->name); | |
153 | return 0; | |
154 | } | |
155 | ||
156 | INLINE_DEVICES unsigned | |
157 | unimp_device_dma_write_buffer(const device *me, | |
158 | const void *source, | |
5b4d72dd | 159 | int space, |
dec38dac MM |
160 | unsigned_word addr, |
161 | unsigned nr_bytes, | |
162 | int violate_read_only_section) | |
163 | { | |
164 | error("device_dma_write_buffer_callback for %s not implemented\n", me->name); | |
165 | return 0; | |
166 | } | |
167 | ||
168 | INLINE_DEVICES void | |
169 | unimp_device_attach_interrupt(const device *me, | |
170 | const device *who, | |
171 | int interrupt_line, | |
172 | const char *name) | |
173 | { | |
174 | error("device_attach_interrupt_callback for %s not implemented\n", me->name); | |
175 | } | |
176 | ||
177 | INLINE_DEVICES void | |
178 | unimp_device_detach_interrupt(const device *me, | |
179 | const device *who, | |
180 | int interrupt_line, | |
181 | const char *name) | |
182 | { | |
183 | error("device_detach_interrupt_callback for %s not implemented\n", me->name); | |
184 | } | |
185 | ||
186 | INLINE_DEVICES void | |
187 | unimp_device_interrupt(const device *me, | |
188 | const device *who, | |
189 | int interrupt_line, | |
190 | int interrupt_status, | |
191 | cpu *processor, | |
192 | unsigned_word cia) | |
193 | { | |
194 | error("device_interrupt_callback for %s not implemented\n", me->name); | |
195 | } | |
196 | ||
197 | INLINE_DEVICES void | |
198 | unimp_device_interrupt_ack(const device *me, | |
199 | int interrupt_line, | |
200 | int interrupt_status) | |
201 | { | |
202 | error("device_interrupt_ack_callback for %s not implemented\n", me->name); | |
203 | } | |
204 | ||
8e20a3ac | 205 | STATIC_DEVICES void |
dec38dac MM |
206 | unimp_device_ioctl(const device *me, |
207 | psim *system, | |
208 | cpu *processor, | |
209 | unsigned_word cia, | |
210 | ...) | |
211 | { | |
212 | error("device_ioctl_callback for %s not implemented\n", me->name); | |
213 | } | |
214 | ||
215 | ||
216 | \f | |
217 | /* ignore/passthrough versions of each function */ | |
218 | ||
219 | INLINE_DEVICES void | |
220 | ignore_device_init(const device *me, | |
221 | psim *system) | |
222 | { | |
223 | /*null*/ | |
224 | } | |
225 | ||
226 | INLINE_DEVICES void | |
227 | pass_device_attach_address(const device *me, | |
228 | const char *name, | |
5b4d72dd MM |
229 | attach_type attach, |
230 | int space, | |
dec38dac MM |
231 | unsigned_word addr, |
232 | unsigned nr_bytes, | |
233 | access_type access, | |
234 | const device *who) /*callback/default*/ | |
235 | { | |
5b4d72dd MM |
236 | DTRACE_ATTACH_ADDRESS(pass); |
237 | me->parent->callback->attach_address(me->parent, name, attach, | |
238 | space, addr, nr_bytes, | |
dec38dac MM |
239 | access, |
240 | who); | |
241 | } | |
242 | ||
243 | INLINE_DEVICES void | |
244 | pass_device_detach_address(const device *me, | |
245 | const char *name, | |
5b4d72dd MM |
246 | attach_type attach, |
247 | int space, | |
dec38dac MM |
248 | unsigned_word addr, |
249 | unsigned nr_bytes, | |
250 | access_type access, | |
251 | const device *who) /*callback/default*/ | |
252 | { | |
5b4d72dd MM |
253 | DTRACE_DETACH_ADDRESS(pass); |
254 | me->parent->callback->detach_address(me->parent, name, attach, | |
255 | space, addr, nr_bytes, access, | |
dec38dac MM |
256 | who); |
257 | } | |
258 | ||
259 | INLINE_DEVICES unsigned | |
260 | pass_device_dma_read_buffer(const device *me, | |
5b4d72dd MM |
261 | void *dest, |
262 | int space, | |
dec38dac MM |
263 | unsigned_word addr, |
264 | unsigned nr_bytes) | |
265 | { | |
5b4d72dd MM |
266 | DTRACE_DMA_READ_BUFFER(pass); |
267 | return me->parent->callback->dma_read_buffer(me->parent, dest, | |
268 | space, addr, nr_bytes); | |
dec38dac MM |
269 | } |
270 | ||
271 | INLINE_DEVICES unsigned | |
272 | pass_device_dma_write_buffer(const device *me, | |
273 | const void *source, | |
5b4d72dd | 274 | int space, |
dec38dac MM |
275 | unsigned_word addr, |
276 | unsigned nr_bytes, | |
277 | int violate_read_only_section) | |
278 | { | |
5b4d72dd | 279 | DTRACE_DMA_WRITE_BUFFER(pass); |
dec38dac | 280 | return me->parent->callback->dma_write_buffer(me->parent, source, |
5b4d72dd | 281 | space, addr, |
dec38dac MM |
282 | nr_bytes, |
283 | violate_read_only_section); | |
284 | } | |
285 | ||
286 | INLINE_DEVICES void | |
287 | pass_device_attach_interrupt(const device *me, | |
288 | const device *who, | |
289 | int interrupt_line, | |
290 | const char *name) | |
291 | { | |
292 | me->parent->callback->attach_interrupt(me->parent, who, | |
293 | interrupt_line, name); | |
294 | } | |
295 | ||
296 | INLINE_DEVICES void | |
297 | pass_device_detach_interrupt(const device *me, | |
298 | const device *who, | |
299 | int interrupt_line, | |
300 | const char *name) | |
301 | { | |
302 | me->parent->callback->detach_interrupt(me->parent, who, | |
303 | interrupt_line, name); | |
304 | } | |
305 | ||
306 | ||
307 | INLINE_DEVICES void | |
308 | pass_device_interrupt(const device *me, | |
309 | const device *who, | |
310 | int interrupt_line, | |
311 | int interrupt_status, | |
312 | cpu *processor, | |
313 | unsigned_word cia) | |
314 | { | |
315 | me->parent->callback->interrupt(me->parent, who, | |
316 | interrupt_line, interrupt_status, | |
317 | processor, cia); | |
318 | } | |
319 | ||
320 | ||
321 | \f | |
322 | /* Simple console device: console@)x<address>,16 | |
323 | ||
324 | Input characters are taken from the keyboard, output characters | |
325 | sent to the terminal. Echoing of characters is not disabled. | |
326 | ||
327 | The device has four registers: | |
328 | ||
329 | 0x0: read | |
330 | 0x4: read-status | |
331 | 0x8: write | |
332 | 0xC: write-status | |
333 | ||
334 | Where a nonzero status register indicates that the device is ready | |
335 | (input fifo contains a character or output fifo has space). */ | |
cb7a6892 MM |
336 | |
337 | typedef struct _console_buffer { | |
338 | char buffer; | |
339 | int status; | |
340 | event_entry_tag event_tag; | |
341 | } console_buffer; | |
342 | ||
343 | typedef struct _console_device { | |
cb7a6892 MM |
344 | console_buffer input; |
345 | console_buffer output; | |
346 | } console_device; | |
347 | ||
348 | typedef enum { | |
349 | console_read_buffer = 0, | |
350 | console_read_status = 4, | |
351 | console_write_buffer = 8, | |
352 | console_write_status = 12, | |
353 | console_offset_mask = 0xc, | |
354 | console_size = 16, | |
355 | } console_offsets; | |
356 | ||
357 | ||
dec38dac MM |
358 | STATIC_INLINE_DEVICES unsigned |
359 | console_io_read_buffer_callback(const device *me, | |
360 | void *dest, | |
5b4d72dd | 361 | int space, |
dec38dac MM |
362 | unsigned_word addr, |
363 | unsigned nr_bytes, | |
364 | cpu *processor, | |
365 | unsigned_word cia) | |
cb7a6892 | 366 | { |
dec38dac MM |
367 | console_device *console = (console_device*)me->data; |
368 | unsigned_1 val; | |
5b4d72dd | 369 | DTRACE_IO_READ_BUFFER(console); |
cb7a6892 | 370 | |
dec38dac | 371 | /* determine what was read */ |
cb7a6892 | 372 | |
dec38dac | 373 | switch (addr) { |
cb7a6892 MM |
374 | |
375 | case console_read_buffer: | |
dec38dac MM |
376 | val = console->input.buffer; |
377 | break; | |
cb7a6892 MM |
378 | |
379 | case console_read_status: | |
380 | { /* check for input */ | |
381 | int flags; | |
382 | int status; | |
383 | /* get the old status */ | |
384 | flags = fcntl(0, F_GETFL, 0); | |
385 | if (flags == -1) { | |
386 | perror("console"); | |
dec38dac MM |
387 | val = 0; |
388 | break; | |
cb7a6892 MM |
389 | } |
390 | /* temp, disable blocking IO */ | |
391 | status = fcntl(0, F_SETFL, flags | O_NDELAY); | |
392 | if (status == -1) { | |
393 | perror("console"); | |
dec38dac MM |
394 | val = 0; |
395 | break; | |
cb7a6892 MM |
396 | } |
397 | /* try for input */ | |
dec38dac | 398 | status = read(0, &console->input.buffer, 1); |
cb7a6892 | 399 | if (status == 1) { |
dec38dac | 400 | console->input.status = 1; |
cb7a6892 MM |
401 | } |
402 | else { | |
dec38dac | 403 | console->input.status = 0; |
cb7a6892 MM |
404 | } |
405 | /* return to regular vewing */ | |
406 | fcntl(0, F_SETFL, flags); | |
407 | } | |
dec38dac MM |
408 | val = console->input.status; |
409 | break; | |
cb7a6892 MM |
410 | |
411 | case console_write_buffer: | |
dec38dac MM |
412 | val = console->output.buffer; |
413 | break; | |
cb7a6892 MM |
414 | |
415 | case console_write_status: | |
dec38dac MM |
416 | val = console->output.status; |
417 | break; | |
cb7a6892 MM |
418 | |
419 | default: | |
420 | error("console_read_callback() internal error\n"); | |
dec38dac MM |
421 | val = 0; |
422 | break; | |
cb7a6892 MM |
423 | |
424 | } | |
425 | ||
dec38dac MM |
426 | bzero(dest, nr_bytes); |
427 | *(unsigned_1*)dest = val; | |
428 | return nr_bytes; | |
cb7a6892 MM |
429 | } |
430 | ||
dec38dac MM |
431 | STATIC_INLINE_DEVICES unsigned |
432 | console_io_write_buffer_callback(const device *me, | |
433 | const void *source, | |
5b4d72dd | 434 | int space, |
dec38dac MM |
435 | unsigned_word addr, |
436 | unsigned nr_bytes, | |
437 | cpu *processor, | |
438 | unsigned_word cia) | |
cb7a6892 | 439 | { |
dec38dac MM |
440 | console_device *console = (console_device*)me->data; |
441 | unsigned_1 val = *(unsigned8*)source; | |
5b4d72dd | 442 | DTRACE_IO_WRITE_BUFFER(console); |
cb7a6892 | 443 | |
dec38dac MM |
444 | switch (addr) { |
445 | case console_read_buffer: | |
446 | console->input.buffer = val; | |
447 | break; | |
448 | case console_read_status: | |
449 | console->input.status = val; | |
450 | break; | |
cb7a6892 | 451 | case console_write_buffer: |
5b4d72dd | 452 | DTRACE(console, ("<%c:%d>", val, val)); |
dec38dac MM |
453 | printf_filtered("%c",val) ; |
454 | console->output.buffer = val; | |
455 | console->output.status = 1; | |
cb7a6892 MM |
456 | break; |
457 | case console_write_status: | |
dec38dac | 458 | console->output.status = val; |
cb7a6892 | 459 | break; |
dec38dac MM |
460 | default: |
461 | error("console_write_callback() internal error\n"); | |
cb7a6892 | 462 | } |
dec38dac MM |
463 | |
464 | return nr_bytes; | |
cb7a6892 MM |
465 | } |
466 | ||
dec38dac MM |
467 | |
468 | static device_callbacks const console_callbacks = { | |
469 | generic_init_callback, | |
470 | unimp_device_attach_address, | |
471 | unimp_device_detach_address, | |
472 | console_io_read_buffer_callback, | |
473 | console_io_write_buffer_callback, | |
474 | unimp_device_dma_read_buffer, | |
475 | unimp_device_dma_write_buffer, | |
476 | unimp_device_attach_interrupt, | |
477 | unimp_device_detach_interrupt, | |
478 | unimp_device_interrupt, | |
479 | unimp_device_interrupt_ack, | |
480 | unimp_device_ioctl, | |
cb7a6892 MM |
481 | }; |
482 | ||
cb7a6892 | 483 | |
dec38dac MM |
484 | STATIC_INLINE_DEVICES const device * |
485 | console_create(const char *name, | |
5b4d72dd | 486 | const char *full_name, |
dec38dac MM |
487 | const device *parent) |
488 | { | |
cb7a6892 MM |
489 | /* create the descriptor */ |
490 | console_device *console = ZALLOC(console_device); | |
491 | ||
cb7a6892 | 492 | /* fill in the details */ |
cb7a6892 MM |
493 | console->output.status = 1; |
494 | console->output.buffer = '\0'; | |
495 | console->input.status = 0; | |
496 | console->input.buffer = '\0'; | |
497 | ||
498 | /* insert into the device tree along with its address info */ | |
dec38dac | 499 | return device_create_from(name, |
5b4d72dd | 500 | full_name, |
dec38dac MM |
501 | console, /* data */ |
502 | &console_callbacks, | |
503 | parent); | |
cb7a6892 MM |
504 | } |
505 | ||
cb7a6892 | 506 | |
dec38dac MM |
507 | \f |
508 | /* ICU device: icu@0x<address>,4 | |
cb7a6892 MM |
509 | |
510 | Single 4 byte register. Read returns processor number. Write | |
511 | interrupts specified processor. | |
512 | ||
513 | Illustrates passing of events to parent device. Passing of | |
514 | interrupts to parent bus. | |
515 | ||
516 | NB: For the sake of illustrating the passing of interrupts. This | |
517 | device doesn't pass interrupt events to its parent. Instead it | |
518 | passes them back to its self. */ | |
519 | ||
dec38dac MM |
520 | STATIC_INLINE_DEVICES unsigned |
521 | icu_io_read_buffer_callback(const device *me, | |
522 | void *dest, | |
5b4d72dd MM |
523 | int space, |
524 | unsigned_word addr, | |
dec38dac MM |
525 | unsigned nr_bytes, |
526 | cpu *processor, | |
527 | unsigned_word cia) | |
cb7a6892 | 528 | { |
dec38dac | 529 | unsigned_1 val; |
5b4d72dd | 530 | DTRACE_IO_READ_BUFFER(icu); |
dec38dac MM |
531 | val = cpu_nr(processor); |
532 | bzero(dest, nr_bytes); | |
533 | *(unsigned_1*)dest = val; | |
534 | return nr_bytes; | |
cb7a6892 MM |
535 | } |
536 | ||
dec38dac MM |
537 | |
538 | STATIC_INLINE_DEVICES unsigned | |
539 | icu_io_write_buffer_callback(const device *me, | |
540 | const void *source, | |
5b4d72dd MM |
541 | int space, |
542 | unsigned_word addr, | |
dec38dac MM |
543 | unsigned nr_bytes, |
544 | cpu *processor, | |
545 | unsigned_word cia) | |
cb7a6892 | 546 | { |
dec38dac | 547 | unsigned_1 val = H2T_1(*(unsigned_1*)source); |
5b4d72dd | 548 | DTRACE_IO_WRITE_BUFFER(icu); |
cb7a6892 MM |
549 | /* tell the parent device that the interrupt lines have changed. |
550 | For this fake ICU. The interrupt lines just indicate the cpu to | |
551 | interrupt next */ | |
dec38dac MM |
552 | me->parent->callback->interrupt(me->parent, me, |
553 | val, val, | |
554 | processor, cia); | |
555 | return nr_bytes; | |
cb7a6892 MM |
556 | } |
557 | ||
dec38dac MM |
558 | |
559 | static device_callbacks const icu_callbacks = { | |
560 | generic_init_callback, | |
561 | unimp_device_attach_address, | |
562 | unimp_device_detach_address, | |
563 | icu_io_read_buffer_callback, | |
564 | icu_io_write_buffer_callback, | |
565 | unimp_device_dma_read_buffer, | |
566 | unimp_device_dma_write_buffer, | |
567 | unimp_device_attach_interrupt, | |
568 | unimp_device_detach_interrupt, | |
569 | unimp_device_interrupt, | |
570 | unimp_device_interrupt_ack, | |
571 | unimp_device_ioctl, | |
572 | }; | |
573 | ||
574 | ||
575 | \f | |
576 | /* HALT device: halt@0x<address>,4 | |
577 | ||
578 | With real hardware, the processor operation is normally terminated | |
579 | through a reset. This device illustrates how a reset device could | |
580 | be attached to an address */ | |
581 | ||
582 | ||
583 | STATIC_INLINE_DEVICES unsigned | |
584 | halt_io_read_buffer_callback(const device *me, | |
585 | void *dest, | |
5b4d72dd MM |
586 | int space, |
587 | unsigned_word addr, | |
dec38dac MM |
588 | unsigned nr_bytes, |
589 | cpu *processor, | |
590 | unsigned_word cia) | |
591 | { | |
5b4d72dd | 592 | DTRACE_IO_READ_BUFFER(halt); |
dec38dac MM |
593 | cpu_halt(processor, cia, was_exited, 0); |
594 | return 0; | |
595 | } | |
596 | ||
597 | ||
598 | STATIC_INLINE_DEVICES unsigned | |
599 | halt_io_write_buffer_callback(const device *me, | |
600 | const void *source, | |
5b4d72dd | 601 | int space, |
dec38dac MM |
602 | unsigned_word addr, |
603 | unsigned nr_bytes, | |
604 | cpu *processor, | |
605 | unsigned_word cia) | |
606 | { | |
5b4d72dd | 607 | DTRACE_IO_WRITE_BUFFER(halt); |
dec38dac MM |
608 | cpu_halt(processor, cia, was_exited, 0); |
609 | return 0; | |
610 | } | |
611 | ||
612 | ||
613 | static device_callbacks const halt_callbacks = { | |
614 | generic_init_callback, | |
615 | unimp_device_attach_address, | |
616 | unimp_device_detach_address, | |
617 | halt_io_read_buffer_callback, | |
618 | halt_io_write_buffer_callback, | |
619 | unimp_device_dma_read_buffer, | |
620 | unimp_device_dma_write_buffer, | |
621 | unimp_device_attach_interrupt, | |
622 | unimp_device_detach_interrupt, | |
623 | unimp_device_interrupt, | |
624 | unimp_device_interrupt_ack, | |
625 | unimp_device_ioctl, | |
626 | }; | |
627 | ||
628 | ||
629 | \f | |
630 | /* Register init device: register@<name>,0x<value>[,<processor>] | |
631 | ||
632 | This strange device is used to initialize the processors registers | |
633 | as part of the initialization. */ | |
634 | ||
635 | STATIC_INLINE_DEVICES void | |
636 | register_init_callback(const device *me, | |
637 | psim *system) | |
638 | { | |
639 | char name[100]; | |
640 | unsigned_word value; | |
641 | unsigned which_cpu; | |
642 | int status; | |
5b4d72dd MM |
643 | DTRACE_INIT(register); |
644 | status = scand_c_uw_u(me->name, name, sizeof(name), &value, &which_cpu); | |
dec38dac MM |
645 | switch (status) { |
646 | case 2: /* register@<name>,<value> */ | |
647 | psim_write_register(system, -1, &value, name, cooked_transfer); | |
648 | break; | |
649 | case 3: /* register@<name>,<value>,<processor> */ | |
650 | psim_write_register(system, which_cpu, &value, name, cooked_transfer); | |
651 | break; | |
652 | default: | |
653 | error("register_init_callback() invalid register init %s\n", me->name); | |
654 | break; | |
655 | } | |
656 | } | |
657 | ||
658 | ||
659 | static device_callbacks const register_callbacks = { | |
660 | register_init_callback, | |
661 | unimp_device_attach_address, | |
662 | unimp_device_detach_address, | |
663 | unimp_device_io_read_buffer, | |
664 | unimp_device_io_write_buffer, | |
665 | unimp_device_dma_read_buffer, | |
666 | unimp_device_dma_write_buffer, | |
667 | unimp_device_attach_interrupt, | |
668 | unimp_device_detach_interrupt, | |
669 | unimp_device_interrupt, | |
670 | unimp_device_interrupt_ack, | |
671 | unimp_device_ioctl, | |
672 | }; | |
673 | ||
674 | ||
675 | \f | |
676 | /* VEA VM device: vm@0x<stack-base>,<nr_bytes> | |
677 | ||
678 | A VEA mode device. This sets its self up as the default memory | |
679 | device capturing all accesses (reads/writes) to currently unmapped | |
680 | addresses. If the unmaped access falls within unallocated stack or | |
681 | heap address ranges then memory is allocated and the access is | |
682 | allowed to continue. | |
683 | ||
684 | During init phase, this device expects to receive `attach' requests | |
685 | from its children for the text/data/bss memory areas. Typically, | |
686 | this would be done by the binary device. | |
687 | ||
688 | STACK: The location of the stack in memory is specified as part of | |
689 | the devices name. Unmaped accesses that fall within the stack | |
690 | space result in the allocated stack being grown downwards so that | |
691 | it includes the page of the culprit access. | |
692 | ||
693 | HEAP: During initialization, the vm device monitors all `attach' | |
694 | operations from its children using this to determine the initial | |
695 | location of the heap. The heap is then extended by system calls | |
696 | that frob the heap upper bound variable (see system.c). */ | |
697 | ||
698 | ||
699 | typedef struct _vm_device { | |
700 | /* area of memory valid for stack addresses */ | |
701 | unsigned_word stack_base; /* min possible stack value */ | |
702 | unsigned_word stack_bound; | |
703 | unsigned_word stack_lower_limit; | |
704 | /* area of memory valid for heap addresses */ | |
705 | unsigned_word heap_base; | |
706 | unsigned_word heap_bound; | |
707 | unsigned_word heap_upper_limit; | |
708 | } vm_device; | |
709 | ||
710 | ||
cb7a6892 | 711 | STATIC_INLINE_DEVICES void |
dec38dac MM |
712 | vm_init_callback(const device *me, |
713 | psim *system) | |
714 | { | |
715 | vm_device *vm = (vm_device*)me->data; | |
5b4d72dd | 716 | DTRACE_INIT(vm); |
dec38dac MM |
717 | |
718 | /* revert the stack/heap variables to their defaults */ | |
719 | vm->stack_lower_limit = vm->stack_bound; | |
720 | vm->heap_base = 0; | |
721 | vm->heap_bound = 0; | |
722 | vm->heap_upper_limit = 0; | |
723 | ||
724 | /* establish this device as the default memory handler */ | |
725 | me->parent->callback->attach_address(me->parent, | |
726 | me->name, | |
727 | attach_default, | |
728 | 0 /*address space - ignore*/, | |
729 | 0 /*addr - ignore*/, | |
730 | 0 /*nr_bytes - ignore*/, | |
731 | access_read_write /*access*/, | |
732 | me); | |
733 | } | |
734 | ||
735 | ||
736 | STATIC_INLINE_DEVICES void | |
737 | vm_attach_address(const device *me, | |
738 | const char *name, | |
5b4d72dd MM |
739 | attach_type attach, |
740 | int space, | |
dec38dac MM |
741 | unsigned_word addr, |
742 | unsigned nr_bytes, | |
743 | access_type access, | |
744 | const device *who) /*callback/default*/ | |
745 | { | |
746 | vm_device *vm = (vm_device*)me->data; | |
5b4d72dd | 747 | DTRACE_ATTACH_ADDRESS(vm); |
dec38dac MM |
748 | /* update end of bss if necessary */ |
749 | if (vm->heap_base < addr + nr_bytes) { | |
750 | vm->heap_base = addr + nr_bytes; | |
751 | vm->heap_bound = addr + nr_bytes; | |
752 | vm->heap_upper_limit = addr + nr_bytes; | |
753 | } | |
754 | me->parent->callback->attach_address(me->parent, | |
755 | "vm@0x0,0", /* stop remap */ | |
756 | attach_raw_memory, | |
757 | 0 /*address space*/, | |
758 | addr, | |
759 | nr_bytes, | |
760 | access, | |
761 | me); | |
762 | } | |
763 | ||
764 | ||
765 | STATIC_INLINE_DEVICES unsigned | |
766 | add_vm_space(const device *me, | |
767 | unsigned_word addr, | |
768 | unsigned nr_bytes, | |
769 | cpu *processor, | |
770 | unsigned_word cia) | |
771 | { | |
772 | vm_device *vm = (vm_device*)me->data; | |
773 | unsigned_word block_addr; | |
774 | unsigned block_nr_bytes; | |
775 | ||
776 | /* an address in the stack area, allocate just down to the addressed | |
777 | page */ | |
778 | if (addr >= vm->stack_base && addr < vm->stack_lower_limit) { | |
779 | block_addr = FLOOR_PAGE(addr); | |
780 | block_nr_bytes = vm->stack_lower_limit - block_addr; | |
781 | vm->stack_lower_limit = block_addr; | |
782 | } | |
783 | /* an address in the heap area, allocate all of the required heap */ | |
784 | else if (addr >= vm->heap_upper_limit && addr < vm->heap_bound) { | |
785 | block_addr = vm->heap_upper_limit; | |
786 | block_nr_bytes = vm->heap_bound - vm->heap_upper_limit; | |
787 | vm->heap_upper_limit = vm->heap_bound; | |
788 | } | |
789 | /* oops - an invalid address - abort the cpu */ | |
790 | else if (processor != NULL) { | |
791 | cpu_halt(processor, cia, was_signalled, SIGSEGV); | |
792 | return 0; | |
793 | } | |
794 | /* 2*oops - an invalid address and no processor */ | |
795 | else { | |
796 | return 0; | |
797 | } | |
798 | ||
799 | /* got the parameters, allocate the space */ | |
800 | me->parent->callback->attach_address(me->parent, | |
801 | "vm@0x0,0", /* stop remap */ | |
802 | attach_raw_memory, | |
803 | 0 /*address space*/, | |
804 | block_addr, | |
805 | block_nr_bytes, | |
806 | access_read_write, | |
807 | me); | |
808 | return block_nr_bytes; | |
809 | } | |
810 | ||
811 | ||
812 | STATIC_INLINE_DEVICES unsigned | |
813 | vm_io_read_buffer_callback(const device *me, | |
814 | void *dest, | |
5b4d72dd | 815 | int space, |
dec38dac MM |
816 | unsigned_word addr, |
817 | unsigned nr_bytes, | |
818 | cpu *processor, | |
819 | unsigned_word cia) | |
820 | { | |
5b4d72dd | 821 | DTRACE_IO_READ_BUFFER(vm); |
dec38dac MM |
822 | if (add_vm_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) { |
823 | bzero(dest, nr_bytes); /* always initialized to zero */ | |
824 | return nr_bytes; | |
825 | } | |
826 | else | |
827 | return 0; | |
828 | } | |
829 | ||
830 | ||
831 | STATIC_INLINE_DEVICES unsigned | |
832 | vm_io_write_buffer_callback(const device *me, | |
833 | const void *source, | |
5b4d72dd | 834 | int space, |
dec38dac MM |
835 | unsigned_word addr, |
836 | unsigned nr_bytes, | |
837 | cpu *processor, | |
838 | unsigned_word cia) | |
839 | { | |
5b4d72dd | 840 | DTRACE_IO_WRITE_BUFFER(vm); |
dec38dac MM |
841 | if (add_vm_space(me, addr, nr_bytes, processor, cia) >= nr_bytes) { |
842 | return me->parent->callback->dma_write_buffer(me->parent, source, | |
5b4d72dd | 843 | space, addr, |
dec38dac MM |
844 | nr_bytes, |
845 | 0/*violate_read_only*/); | |
846 | } | |
847 | else | |
848 | return 0; | |
849 | } | |
850 | ||
851 | ||
8e20a3ac | 852 | static void |
dec38dac MM |
853 | vm_ioctl_callback(const device *me, |
854 | psim *system, | |
855 | cpu *processor, | |
856 | unsigned_word cia, | |
857 | ...) | |
858 | { | |
859 | /* While the caller is notified that the heap has grown by the | |
860 | requested amount, the heap is infact extended out to a page | |
861 | boundary. */ | |
862 | vm_device *vm = (vm_device*)me->data; | |
863 | unsigned_word new_break = ALIGN_8(cpu_registers(processor)->gpr[3]); | |
864 | unsigned_word old_break = vm->heap_bound; | |
865 | signed_word delta = new_break - old_break; | |
866 | if (delta > 0) | |
867 | vm->heap_bound = ALIGN_PAGE(new_break); | |
868 | cpu_registers(processor)->gpr[0] = 0; | |
869 | cpu_registers(processor)->gpr[3] = new_break; | |
870 | } | |
871 | ||
872 | ||
873 | static device_callbacks const vm_callbacks = { | |
874 | vm_init_callback, | |
875 | vm_attach_address, | |
876 | pass_device_detach_address, | |
877 | vm_io_read_buffer_callback, | |
878 | vm_io_write_buffer_callback, | |
879 | unimp_device_dma_read_buffer, | |
880 | pass_device_dma_write_buffer, | |
881 | unimp_device_attach_interrupt, | |
882 | unimp_device_detach_interrupt, | |
883 | unimp_device_interrupt, | |
884 | unimp_device_interrupt_ack, | |
885 | vm_ioctl_callback, | |
886 | }; | |
887 | ||
888 | ||
889 | STATIC_INLINE_DEVICES const device * | |
890 | vea_vm_create(const char *name, | |
5b4d72dd MM |
891 | const char *full_name, |
892 | const device *parent) | |
dec38dac MM |
893 | { |
894 | vm_device *vm = ZALLOC(vm_device); | |
895 | unsigned_word addr; | |
896 | unsigned nr_bytes; | |
897 | ||
898 | /* extract out the stack parameters */ | |
899 | if (scand_uw_u(name, &addr, &nr_bytes) != 2) | |
900 | error("vm_device_create() invalid vm device %s\n", name); | |
901 | vm->stack_base = addr; | |
902 | vm->stack_bound = addr + nr_bytes; | |
903 | ||
904 | /* insert in the tree including the buffer */ | |
905 | return device_create_from(name, | |
5b4d72dd | 906 | full_name, |
dec38dac MM |
907 | vm, /* data */ |
908 | &vm_callbacks, | |
909 | parent); | |
910 | } | |
911 | ||
912 | ||
913 | \f | |
914 | /* Memory init device: memory@0x<addr>,<size>,<access> | |
915 | ||
916 | This strange device is used create sections of memory */ | |
917 | ||
918 | STATIC_INLINE_DEVICES void | |
919 | memory_init_callback(const device *me, | |
920 | psim *system) | |
921 | { | |
922 | unsigned_word addr; | |
923 | unsigned nr_bytes; | |
924 | unsigned access; | |
5b4d72dd MM |
925 | int nr_args; |
926 | DTRACE_INIT(memory); | |
dec38dac | 927 | |
5b4d72dd MM |
928 | nr_args = scand_uw_u_u(me->name, &addr, &nr_bytes, &access); |
929 | switch (nr_args) { | |
930 | case 2: | |
931 | access = access_read_write_exec; | |
932 | break; | |
933 | case 3: | |
934 | break; | |
935 | default: | |
dec38dac | 936 | error("memory_init_callback() invalid memory device %s\n", me->name); |
5b4d72dd MM |
937 | break; |
938 | } | |
dec38dac MM |
939 | |
940 | me->parent->callback->attach_address(me->parent, | |
941 | me->name, | |
942 | attach_raw_memory, | |
943 | 0 /*address space*/, | |
944 | addr, | |
945 | nr_bytes, | |
946 | (access_type)access, | |
947 | me); | |
948 | } | |
949 | ||
950 | ||
951 | static device_callbacks const memory_callbacks = { | |
952 | memory_init_callback, | |
953 | unimp_device_attach_address, | |
954 | unimp_device_detach_address, | |
955 | unimp_device_io_read_buffer, | |
956 | unimp_device_io_write_buffer, | |
957 | unimp_device_dma_read_buffer, | |
958 | unimp_device_dma_write_buffer, | |
959 | unimp_device_attach_interrupt, | |
960 | unimp_device_detach_interrupt, | |
961 | unimp_device_interrupt, | |
962 | unimp_device_interrupt_ack, | |
963 | unimp_device_ioctl, | |
964 | }; | |
965 | ||
966 | ||
dec38dac MM |
967 | \f |
968 | /* IOBUS device: iobus@<address> | |
969 | ||
970 | Simple bus on which some IO devices live */ | |
971 | ||
972 | STATIC_INLINE_DEVICES void | |
973 | iobus_attach_address_callback(const device *me, | |
974 | const char *name, | |
975 | attach_type type, | |
5b4d72dd | 976 | int space, |
dec38dac MM |
977 | unsigned_word addr, |
978 | unsigned nr_bytes, | |
979 | access_type access, | |
980 | const device *who) /*callback/default*/ | |
981 | { | |
982 | unsigned_word iobus_addr; | |
983 | /* sanity check */ | |
984 | if (type == attach_default) | |
985 | error("iobus_attach_address_callback() no default for %s/%s\n", | |
986 | me->name, name); | |
5b4d72dd MM |
987 | if (space != 0) |
988 | error("iobus_attach_address_callback() no space for %s/%s\n", | |
dec38dac MM |
989 | me->name, name); |
990 | /* get the bus address */ | |
991 | if (scand_uw(me->name, &iobus_addr) != 1) | |
992 | error("iobus_attach_address_callback() invalid address for %s\n", | |
993 | me->name); | |
994 | me->parent->callback->attach_address(me->parent, | |
995 | me->name, | |
996 | type, | |
5b4d72dd | 997 | 0 /*space*/, |
dec38dac MM |
998 | iobus_addr + addr, |
999 | nr_bytes, | |
1000 | access, | |
1001 | who); | |
1002 | } | |
1003 | ||
1004 | ||
1005 | STATIC_INLINE_DEVICES void | |
1006 | iobus_do_interrupt(event_queue *queue, | |
1007 | void *data) | |
cb7a6892 MM |
1008 | { |
1009 | cpu *target = (cpu*)data; | |
1010 | /* try to interrupt the processor. If the attempt fails, try again | |
1011 | on the next tick */ | |
1012 | if (!external_interrupt(target)) | |
dec38dac | 1013 | event_queue_schedule(queue, 1, iobus_do_interrupt, target); |
cb7a6892 MM |
1014 | } |
1015 | ||
dec38dac | 1016 | |
cb7a6892 | 1017 | STATIC_INLINE_DEVICES void |
dec38dac MM |
1018 | iobus_interrupt_callback(const device *me, |
1019 | const device *who, | |
1020 | int interrupt_line, | |
1021 | int interrupt_status, | |
1022 | cpu *processor, | |
1023 | unsigned_word cia) | |
cb7a6892 MM |
1024 | { |
1025 | /* the interrupt controler can't interrupt a cpu at any time. | |
1026 | Rather it must synchronize with the system clock before | |
1027 | performing an interrupt on the given processor */ | |
1028 | psim *system = cpu_system(processor); | |
1029 | cpu *target = psim_cpu(system, interrupt_status); | |
1030 | if (target != NULL) { | |
1031 | event_queue *events = cpu_event_queue(target); | |
dec38dac | 1032 | event_queue_schedule(events, 1, iobus_do_interrupt, target); |
cb7a6892 MM |
1033 | } |
1034 | } | |
1035 | ||
dec38dac MM |
1036 | |
1037 | static device_callbacks const iobus_callbacks = { | |
1038 | ignore_device_init, | |
1039 | iobus_attach_address_callback, | |
1040 | unimp_device_detach_address, | |
1041 | unimp_device_io_read_buffer, | |
1042 | unimp_device_io_write_buffer, | |
1043 | unimp_device_dma_read_buffer, | |
1044 | unimp_device_dma_write_buffer, | |
1045 | unimp_device_attach_interrupt, | |
1046 | unimp_device_detach_interrupt, | |
1047 | iobus_interrupt_callback, | |
1048 | unimp_device_interrupt_ack, | |
1049 | unimp_device_ioctl, | |
cb7a6892 MM |
1050 | }; |
1051 | ||
dec38dac MM |
1052 | |
1053 | \f | |
1054 | /* FILE device: file@0x<address>,<file-name> | |
1055 | (later - file@0x<address>,<size>,<file-offset>,<file-name>) | |
1056 | ||
1057 | Specifies a file to read directly into memory starting at <address> */ | |
1058 | ||
1059 | ||
1060 | STATIC_INLINE_DEVICES void | |
1061 | file_init_callback(const device *me, | |
1062 | psim *system) | |
cb7a6892 | 1063 | { |
dec38dac | 1064 | unsigned_word addr; |
5b4d72dd | 1065 | unsigned count; |
dec38dac MM |
1066 | char *file_name; |
1067 | char buf; | |
1068 | FILE *image; | |
5b4d72dd | 1069 | DTRACE_INIT(file); |
dec38dac MM |
1070 | |
1071 | if ((file_name = strchr(me->name, ',')) == NULL | |
1072 | || scand_uw(me->name, &addr) != 1) | |
1073 | error("file_init_callback() invalid file device %s\n", me->name); | |
1074 | ||
1075 | /* open the file to load */ | |
1076 | file_name++; /* skip the `,' */ | |
1077 | image = fopen(file_name, "r"); | |
1078 | if (image == NULL) | |
1079 | error("file_init_callback() file open failed for %s\n", me->name); | |
1080 | ||
1081 | /* read it in slowly */ | |
5b4d72dd | 1082 | count = 0; |
dec38dac | 1083 | while (fread(&buf, 1, 1, image) > 0) { |
5b4d72dd MM |
1084 | if (me->parent->callback->dma_write_buffer(me->parent, |
1085 | &buf, | |
1086 | 0 /*address-space*/, | |
1087 | addr+count, | |
1088 | 1 /*nr-bytes*/, | |
1089 | 1 /*violate ro*/) != 1) | |
1090 | error("file_init_callback() failed to write to address 0x%x, offset %d\n", | |
1091 | addr+count, count); | |
1092 | count++; | |
dec38dac | 1093 | } |
cb7a6892 | 1094 | |
dec38dac MM |
1095 | /* close down again */ |
1096 | fclose(image); | |
1097 | } | |
cb7a6892 | 1098 | |
cb7a6892 | 1099 | |
dec38dac MM |
1100 | static device_callbacks const file_callbacks = { |
1101 | file_init_callback, | |
1102 | unimp_device_attach_address, | |
1103 | unimp_device_detach_address, | |
1104 | unimp_device_io_read_buffer, | |
1105 | unimp_device_io_write_buffer, | |
1106 | unimp_device_dma_read_buffer, | |
1107 | unimp_device_dma_write_buffer, | |
1108 | unimp_device_attach_interrupt, | |
1109 | unimp_device_detach_interrupt, | |
1110 | unimp_device_interrupt, | |
1111 | unimp_device_interrupt_ack, | |
1112 | unimp_device_ioctl, | |
1113 | }; | |
1114 | ||
1115 | ||
1116 | \f | |
1117 | /* HTAB: htab@0x<address>,<nr_bytes> | |
1118 | PTE: pte@0x<effective-address>,0x<real-address>,<nr_bytes> | |
1119 | ||
1120 | HTAB defines the location (in physical memory) of a HASH table. | |
1121 | PTE (as a child of HTAB) defines a mapping that is to be entered | |
1122 | into that table. | |
1123 | ||
1124 | NB: All the work in this device is done during init by the PTE. | |
1125 | The pte, looks up its parent to determine the address of the HTAB | |
1126 | and then uses DMA calls to establish the required mapping. */ | |
1127 | ||
1128 | ||
1129 | STATIC_INLINE_DEVICES void | |
1130 | htab_init_callback(const device *me, | |
1131 | psim *system) | |
1132 | { | |
5b4d72dd | 1133 | DTRACE_INIT(htab); |
dec38dac MM |
1134 | /* only the pte does work */ |
1135 | if (strncmp(me->name, "pte@", strlen("pte@")) == 0) { | |
1136 | unsigned_word htab_ra; | |
1137 | unsigned htab_nr_bytes; | |
1138 | unsigned_word pte_ea; | |
1139 | unsigned_word pte_ra; | |
1140 | unsigned pte_nr_bytes; | |
1141 | /* determine the location/size of the hash table */ | |
1142 | if (scand_uw_u(me->parent->name, &htab_ra, &htab_nr_bytes) != 2) | |
1143 | error("htab_init_callback() htab entry %s invalid\n", | |
1144 | me->parent->name); | |
1145 | /* determine the location/size of the mapping */ | |
1146 | if (scand_uw_uw_u(me->name, &pte_ea, &pte_ra, &pte_nr_bytes) != 3) | |
1147 | error("htab_init_callback() pte entry %s invalid\n", me->name); | |
1148 | error("Map ea=0x%x, ra=0x%x, nr_bytes=%d using htab=0x%x, nr_bytes=%d\n", | |
1149 | pte_ea, pte_ra, pte_nr_bytes, htab_ra, htab_nr_bytes); | |
1150 | } | |
cb7a6892 MM |
1151 | } |
1152 | ||
dec38dac MM |
1153 | |
1154 | static device_callbacks const htab_callbacks = { | |
1155 | htab_init_callback, | |
1156 | unimp_device_attach_address, | |
1157 | unimp_device_detach_address, | |
1158 | unimp_device_io_read_buffer, | |
1159 | unimp_device_io_write_buffer, | |
1160 | unimp_device_dma_read_buffer, | |
1161 | unimp_device_dma_write_buffer, | |
1162 | unimp_device_attach_interrupt, | |
1163 | unimp_device_detach_interrupt, | |
1164 | unimp_device_interrupt, | |
1165 | unimp_device_interrupt_ack, | |
1166 | unimp_device_ioctl, | |
cb7a6892 MM |
1167 | }; |
1168 | ||
1169 | ||
dec38dac MM |
1170 | \f |
1171 | /* Simulator device: sim@0x<address>,<nr_bytes> | |
1172 | ||
1173 | Eventually gives access to the hardware configuration. For | |
1174 | instance, it could allow the setting (on the fly) of variables such | |
1175 | as hardware floating-point or strict-alignment. | |
1176 | ||
1177 | It's intended use is as part of testing the simulators | |
1178 | functionality */ | |
1179 | ||
1180 | static device_callbacks const sim_callbacks = { | |
1181 | ignore_device_init, | |
1182 | unimp_device_attach_address, | |
1183 | unimp_device_detach_address, | |
1184 | unimp_device_io_read_buffer, | |
1185 | unimp_device_io_write_buffer, | |
1186 | unimp_device_dma_read_buffer, | |
1187 | unimp_device_dma_write_buffer, | |
1188 | unimp_device_attach_interrupt, | |
1189 | unimp_device_detach_interrupt, | |
1190 | unimp_device_interrupt, | |
1191 | unimp_device_interrupt_ack, | |
1192 | unimp_device_ioctl, | |
1193 | }; | |
cb7a6892 MM |
1194 | |
1195 | ||
dec38dac MM |
1196 | \f |
1197 | /* Load device: *binary@<file-name> | |
cb7a6892 | 1198 | |
dec38dac MM |
1199 | Assuming that <file-name> is an executable file understood by BFD, |
1200 | this device loads or maps the relevant text/data segments into | |
1201 | memory using dma. */ | |
cb7a6892 | 1202 | |
dec38dac | 1203 | STATIC_INLINE_DEVICES void |
5b4d72dd MM |
1204 | update_for_binary_section(bfd *abfd, |
1205 | asection *the_section, | |
1206 | PTR obj) | |
cb7a6892 | 1207 | { |
dec38dac MM |
1208 | unsigned_word section_vma; |
1209 | unsigned_word section_size; | |
1210 | access_type access; | |
1211 | device *me = (device*)obj; | |
1212 | ||
1213 | /* skip the section if no memory to allocate */ | |
1214 | if (! (bfd_get_section_flags(abfd, the_section) & SEC_ALLOC)) | |
1215 | return; | |
1216 | ||
1217 | /* check/ignore any sections of size zero */ | |
1218 | section_size = bfd_get_section_size_before_reloc(the_section); | |
1219 | if (section_size == 0) | |
1220 | return; | |
1221 | ||
1222 | /* find where it is to go */ | |
1223 | section_vma = bfd_get_section_vma(abfd, the_section); | |
1224 | ||
5b4d72dd MM |
1225 | DTRACE(binary, |
1226 | ("name=%-7s, vma=0x%.8x, size=%6d, flags=%3x(%s%s%s%s )\n", | |
1227 | bfd_get_section_name(abfd, the_section), | |
1228 | section_vma, section_size, | |
1229 | bfd_get_section_flags(abfd, the_section), | |
1230 | bfd_get_section_flags(abfd, the_section) & SEC_LOAD ? " LOAD" : "", | |
1231 | bfd_get_section_flags(abfd, the_section) & SEC_CODE ? " CODE" : "", | |
1232 | bfd_get_section_flags(abfd, the_section) & SEC_DATA ? " DATA" : "", | |
1233 | bfd_get_section_flags(abfd, the_section) & SEC_ALLOC ? " ALLOC" : "", | |
1234 | bfd_get_section_flags(abfd, the_section) & SEC_READONLY ? " READONLY" : "" | |
1235 | )); | |
dec38dac MM |
1236 | |
1237 | /* determine the devices access */ | |
1238 | access = access_read; | |
1239 | if (bfd_get_section_flags(abfd, the_section) & SEC_CODE) | |
1240 | access |= access_exec; | |
1241 | if (!(bfd_get_section_flags(abfd, the_section) & SEC_READONLY)) | |
1242 | access |= access_write; | |
1243 | ||
1244 | /* if a map, pass up a request to create the memory in core */ | |
1245 | if (strncmp(me->name, "map-binary@", strlen("map-binary@")) == 0) | |
1246 | me->parent->callback->attach_address(me->parent, | |
1247 | me->name, | |
1248 | attach_raw_memory, | |
1249 | 0 /*address space*/, | |
1250 | section_vma, | |
1251 | section_size, | |
1252 | access, | |
1253 | me); | |
1254 | ||
1255 | /* if a load dma in the required data */ | |
1256 | if (bfd_get_section_flags(abfd, the_section) & SEC_LOAD) { | |
1257 | void *section_init = zalloc(section_size); | |
1258 | if (!bfd_get_section_contents(abfd, | |
1259 | the_section, | |
1260 | section_init, 0, | |
1261 | section_size)) { | |
1262 | bfd_perror("core:load_section()"); | |
1263 | error("load of data failed"); | |
1264 | return; | |
1265 | } | |
1266 | if (me->parent->callback->dma_write_buffer(me->parent, | |
1267 | section_init, | |
5b4d72dd | 1268 | 0 /*space*/, |
dec38dac MM |
1269 | section_vma, |
1270 | section_size, | |
1271 | 1 /*violate_read_only*/) | |
1272 | != section_size) | |
1273 | error("data_init_callback() broken transfer for %s\n", me->name); | |
1274 | zfree(section_init); /* only free if load */ | |
1275 | } | |
cb7a6892 MM |
1276 | } |
1277 | ||
dec38dac | 1278 | |
cb7a6892 | 1279 | STATIC_INLINE_DEVICES void |
dec38dac MM |
1280 | binary_init_callback(const device *me, |
1281 | psim *system) | |
cb7a6892 | 1282 | { |
dec38dac MM |
1283 | char file_name[100]; |
1284 | bfd *image; | |
5b4d72dd | 1285 | DTRACE_INIT(binary); |
dec38dac MM |
1286 | |
1287 | /* get a file name */ | |
5b4d72dd | 1288 | if (scand_c(me->name, file_name, sizeof(file_name)) != 1) |
dec38dac MM |
1289 | error("load_binary_init_callback() invalid load-binary device %s\n", |
1290 | me->name); | |
1291 | ||
1292 | /* open the file */ | |
1293 | image = bfd_openr(file_name, NULL); | |
1294 | if (image == NULL) { | |
1295 | bfd_perror("open failed:"); | |
1296 | error("nothing loaded\n"); | |
1297 | } | |
1298 | ||
1299 | /* check it is valid */ | |
1300 | if (!bfd_check_format(image, bfd_object)) { | |
1301 | printf_filtered("create_device_tree() - FIXME - should check more bfd bits\n"); | |
1302 | printf_filtered("create_device_tree() - %s not an executable, assume device file\n", file_name); | |
1303 | bfd_close(image); | |
1304 | image = NULL; | |
1305 | } | |
1306 | ||
1307 | /* and the data sections */ | |
1308 | bfd_map_over_sections(image, | |
5b4d72dd | 1309 | update_for_binary_section, |
dec38dac MM |
1310 | (PTR)me); |
1311 | ||
1312 | bfd_close(image); | |
cb7a6892 MM |
1313 | } |
1314 | ||
1315 | ||
dec38dac MM |
1316 | static device_callbacks const binary_callbacks = { |
1317 | binary_init_callback, | |
1318 | unimp_device_attach_address, | |
1319 | unimp_device_detach_address, | |
1320 | unimp_device_io_read_buffer, | |
1321 | unimp_device_io_write_buffer, | |
1322 | unimp_device_dma_read_buffer, | |
1323 | unimp_device_dma_write_buffer, | |
1324 | unimp_device_attach_interrupt, | |
1325 | unimp_device_detach_interrupt, | |
1326 | unimp_device_interrupt, | |
1327 | unimp_device_interrupt_ack, | |
1328 | unimp_device_ioctl, | |
cb7a6892 MM |
1329 | }; |
1330 | ||
dec38dac MM |
1331 | |
1332 | \f | |
1333 | /* Stack device: stack@<type> | |
1334 | ||
1335 | Has a single IOCTL to create a stack frame of the specified type. | |
1336 | If <type> is elf or xcoff then a corresponding stack is created. | |
1337 | Any other value of type is ignored. | |
1338 | ||
1339 | The IOCTL takes the additional arguments: | |
1340 | ||
1341 | unsigned_word stack_end -- where the stack should come down from | |
1342 | char **argv -- ... | |
1343 | char **envp -- ... | |
1344 | ||
1345 | */ | |
1346 | ||
1347 | STATIC_INLINE_DEVICES int | |
1348 | sizeof_argument_strings(char **arg) | |
1349 | { | |
1350 | int sizeof_strings = 0; | |
1351 | ||
1352 | /* robust */ | |
1353 | if (arg == NULL) | |
1354 | return 0; | |
1355 | ||
1356 | /* add up all the string sizes (padding as we go) */ | |
1357 | for (; *arg != NULL; arg++) { | |
1358 | int len = strlen(*arg) + 1; | |
1359 | sizeof_strings += ALIGN_8(len); | |
1360 | } | |
1361 | ||
1362 | return sizeof_strings; | |
1363 | } | |
1364 | ||
1365 | STATIC_INLINE_DEVICES int | |
1366 | number_of_arguments(char **arg) | |
1367 | { | |
1368 | int nr; | |
1369 | if (arg == NULL) | |
1370 | return 0; | |
1371 | for (nr = 0; *arg != NULL; arg++, nr++); | |
1372 | return nr; | |
1373 | } | |
1374 | ||
1375 | STATIC_INLINE_DEVICES int | |
1376 | sizeof_arguments(char **arg) | |
1377 | { | |
1378 | return ALIGN_8((number_of_arguments(arg) + 1) * sizeof(unsigned_word)); | |
1379 | } | |
1380 | ||
1381 | STATIC_INLINE_DEVICES void | |
1382 | write_stack_arguments(psim *system, | |
1383 | char **arg, | |
1384 | unsigned_word start_block, | |
1385 | unsigned_word end_block, | |
1386 | unsigned_word start_arg, | |
1387 | unsigned_word end_arg) | |
1388 | { | |
5b4d72dd MM |
1389 | DTRACE(stack, |
1390 | ("write_stack_arguments(system=0x%x, arg=0x%x, start_block=0x%x, end_block=0x%x, start_arg=0x%x, end_arg=0x%x)\n", | |
1391 | system, arg, start_block, end_block, start_arg, end_arg)); | |
dec38dac MM |
1392 | if (arg == NULL) |
1393 | error("write_arguments: character array NULL\n"); | |
1394 | /* only copy in arguments, memory is already zero */ | |
1395 | for (; *arg != NULL; arg++) { | |
1396 | int len = strlen(*arg)+1; | |
1397 | unsigned_word target_start_block; | |
5b4d72dd MM |
1398 | DTRACE(stack, |
1399 | ("write_stack_arguments() write %s=%s at %s=0x%x %s=0x%x %s=0x%x\n", | |
dec38dac MM |
1400 | "**arg", *arg, "start_block", start_block, |
1401 | "len", len, "start_arg", start_arg)); | |
1402 | if (psim_write_memory(system, 0, *arg, | |
1403 | start_block, len, | |
1404 | 0/*violate_readonly*/) != len) | |
1405 | error("write_stack_arguments() - write of **arg (%s) at 0x%x failed\n", | |
1406 | *arg, start_block); | |
1407 | target_start_block = H2T_word(start_block); | |
1408 | if (psim_write_memory(system, 0, &target_start_block, | |
1409 | start_arg, sizeof(target_start_block), | |
1410 | 0) != sizeof(target_start_block)) | |
1411 | error("write_stack_arguments() - write of *arg failed\n"); | |
1412 | start_block += ALIGN_8(len); | |
1413 | start_arg += sizeof(start_block); | |
1414 | } | |
1415 | start_arg += sizeof(start_block); /*the null at the end*/ | |
1416 | if (start_block != end_block | |
1417 | || ALIGN_8(start_arg) != end_arg) | |
1418 | error("write_stack_arguments - possible corruption\n"); | |
5b4d72dd MM |
1419 | DTRACE(stack, |
1420 | ("write_stack_arguments() = void\n")); | |
dec38dac MM |
1421 | } |
1422 | ||
1423 | STATIC_INLINE_DEVICES void | |
1424 | create_elf_stack_frame(psim *system, | |
1425 | unsigned_word bottom_of_stack, | |
1426 | char **argv, | |
1427 | char **envp) | |
1428 | { | |
1429 | /* fixme - this is over aligned */ | |
1430 | ||
1431 | /* information block */ | |
1432 | const unsigned sizeof_envp_block = sizeof_argument_strings(envp); | |
1433 | const unsigned_word start_envp_block = bottom_of_stack - sizeof_envp_block; | |
1434 | const unsigned sizeof_argv_block = sizeof_argument_strings(argv); | |
1435 | const unsigned_word start_argv_block = start_envp_block - sizeof_argv_block; | |
1436 | ||
1437 | /* auxiliary vector - contains only one entry */ | |
1438 | const unsigned sizeof_aux_entry = 2*sizeof(unsigned_word); /* magic */ | |
1439 | const unsigned_word start_aux = start_argv_block - ALIGN_8(sizeof_aux_entry); | |
1440 | ||
1441 | /* environment points (including null sentinal) */ | |
1442 | const unsigned sizeof_envp = sizeof_arguments(envp); | |
1443 | const unsigned_word start_envp = start_aux - sizeof_envp; | |
1444 | ||
1445 | /* argument pointers (including null sentinal) */ | |
1446 | const int argc = number_of_arguments(argv); | |
1447 | const unsigned sizeof_argv = sizeof_arguments(argv); | |
1448 | const unsigned_word start_argv = start_envp - sizeof_argv; | |
1449 | ||
1450 | /* link register save address - alligned to a 16byte boundary */ | |
1451 | const unsigned_word top_of_stack = ((start_argv | |
1452 | - 2 * sizeof(unsigned_word)) | |
1453 | & ~0xf); | |
1454 | ||
1455 | /* install arguments on stack */ | |
1456 | write_stack_arguments(system, envp, | |
1457 | start_envp_block, bottom_of_stack, | |
1458 | start_envp, start_aux); | |
1459 | write_stack_arguments(system, argv, | |
1460 | start_argv_block, start_envp_block, | |
1461 | start_argv, start_envp); | |
1462 | ||
1463 | /* set up the registers */ | |
1464 | psim_write_register(system, -1, | |
1465 | &top_of_stack, "sp", cooked_transfer); | |
1466 | psim_write_register(system, -1, | |
1467 | &argc, "r3", cooked_transfer); | |
1468 | psim_write_register(system, -1, | |
1469 | &start_argv, "r4", cooked_transfer); | |
1470 | psim_write_register(system, -1, | |
1471 | &start_envp, "r5", cooked_transfer); | |
1472 | psim_write_register(system, -1, | |
1473 | &start_aux, "r6", cooked_transfer); | |
1474 | } | |
1475 | ||
1476 | STATIC_INLINE_DEVICES void | |
1477 | create_aix_stack_frame(psim *system, | |
1478 | unsigned_word bottom_of_stack, | |
1479 | char **argv, | |
1480 | char **envp) | |
cb7a6892 | 1481 | { |
dec38dac MM |
1482 | unsigned_word core_envp; |
1483 | unsigned_word core_argv; | |
1484 | unsigned_word core_argc; | |
1485 | unsigned_word core_aux; | |
1486 | unsigned_word top_of_stack; | |
1487 | ||
1488 | /* cheat - create an elf stack frame */ | |
1489 | create_elf_stack_frame(system, bottom_of_stack, argv, envp); | |
1490 | ||
1491 | /* extract argument addresses from registers */ | |
1492 | psim_read_register(system, 0, &top_of_stack, "r1", cooked_transfer); | |
1493 | psim_read_register(system, 0, &core_argc, "r3", cooked_transfer); | |
1494 | psim_read_register(system, 0, &core_argv, "r4", cooked_transfer); | |
1495 | psim_read_register(system, 0, &core_envp, "r5", cooked_transfer); | |
1496 | psim_read_register(system, 0, &core_aux, "r6", cooked_transfer); | |
1497 | ||
1498 | /* extract arguments from registers */ | |
1499 | error("create_aix_stack_frame() - what happens next?\n"); | |
1500 | } | |
1501 | ||
1502 | ||
cb7a6892 | 1503 | |
8e20a3ac | 1504 | static void |
dec38dac MM |
1505 | stack_ioctl_callback(const device *me, |
1506 | psim *system, | |
1507 | cpu *processor, | |
1508 | unsigned_word cia, | |
1509 | ...) | |
1510 | { | |
1511 | va_list ap; | |
1512 | unsigned_word stack_pointer; | |
1513 | char **argv; | |
1514 | char **envp; | |
1515 | va_start(ap, cia); | |
1516 | stack_pointer = va_arg(ap, unsigned_word); | |
1517 | argv = va_arg(ap, char **); | |
1518 | envp = va_arg(ap, char **); | |
5b4d72dd MM |
1519 | va_end(ap); |
1520 | DTRACE(stack, | |
1521 | ("stack_ioctl_callback(me=0x%x:%s, system=0x%x, processor=0x%x, cia=0x%x, argv=0x%x, envp=0x%x)\n", | |
1522 | me, me->full_name, system, processor, cia, argv, envp)); | |
dec38dac MM |
1523 | if (strcmp(me->name, "stack@elf") == 0) |
1524 | create_elf_stack_frame(system, stack_pointer, argv, envp); | |
1525 | else if (strcmp(me->name, "stack@xcoff") == 0) | |
1526 | create_aix_stack_frame(system, stack_pointer, argv, envp); | |
5b4d72dd MM |
1527 | DTRACE(stack, |
1528 | ("stack_ioctl_callback() = void\n")); | |
cb7a6892 MM |
1529 | } |
1530 | ||
dec38dac MM |
1531 | |
1532 | static device_callbacks const stack_callbacks = { | |
1533 | ignore_device_init, | |
1534 | unimp_device_attach_address, | |
1535 | unimp_device_detach_address, | |
1536 | unimp_device_io_read_buffer, | |
1537 | unimp_device_io_write_buffer, | |
1538 | unimp_device_dma_read_buffer, | |
1539 | unimp_device_dma_write_buffer, | |
1540 | unimp_device_attach_interrupt, | |
1541 | unimp_device_detach_interrupt, | |
1542 | unimp_device_interrupt, | |
1543 | unimp_device_interrupt_ack, | |
1544 | stack_ioctl_callback, | |
cb7a6892 MM |
1545 | }; |
1546 | ||
1547 | ||
dec38dac MM |
1548 | \f |
1549 | /* Table of all the devices and a function to lookup/create a device | |
1550 | from its name */ | |
1551 | ||
1552 | typedef const device *(device_creator) | |
1553 | (const char *name, | |
5b4d72dd | 1554 | const char *full_name, |
dec38dac MM |
1555 | const device *parent); |
1556 | ||
1557 | typedef struct _device_descriptor device_descriptor; | |
1558 | struct _device_descriptor { | |
1559 | const char *name; | |
1560 | device_creator *creator; | |
1561 | const device_callbacks *callbacks; | |
1562 | }; | |
1563 | ||
1564 | static device_descriptor devices[] = { | |
1565 | { "console", console_create, NULL }, | |
5b4d72dd | 1566 | { "memory", NULL, &memory_callbacks }, |
dec38dac MM |
1567 | { "vm", vea_vm_create, NULL }, |
1568 | { "halt", NULL, &halt_callbacks }, | |
1569 | { "icu", NULL, &icu_callbacks }, | |
1570 | { "register", NULL, ®ister_callbacks }, | |
1571 | { "iobus", NULL, &iobus_callbacks }, | |
1572 | { "file", NULL, &file_callbacks }, | |
1573 | { "htab", NULL, &htab_callbacks }, | |
1574 | { "pte", NULL, &htab_callbacks }, /* yep - uses htab's table */ | |
1575 | { "stack", NULL, &stack_callbacks }, | |
1576 | { "sim", NULL, &sim_callbacks }, | |
1577 | { "load-binary", NULL, &binary_callbacks }, | |
1578 | { "map-binary", NULL, &binary_callbacks }, | |
1579 | { NULL }, | |
cb7a6892 MM |
1580 | }; |
1581 | ||
1582 | ||
dec38dac MM |
1583 | INLINE_DEVICES const device * |
1584 | device_create(const char *name, | |
5b4d72dd | 1585 | const char *full_name, |
dec38dac | 1586 | const device *parent) |
cb7a6892 | 1587 | { |
dec38dac | 1588 | device_descriptor *device; |
cb7a6892 MM |
1589 | int name_len; |
1590 | char *chp; | |
1591 | chp = strchr(name, '@'); | |
1592 | name_len = (chp == NULL ? strlen(name) : chp - name); | |
dec38dac MM |
1593 | for (device = devices; device->name != NULL; device++) { |
1594 | if (strncmp(name, device->name, name_len) == 0 | |
1595 | && (device->name[name_len] == '\0' | |
1596 | || device->name[name_len] == '@')) | |
1597 | if (device->creator != NULL) | |
5b4d72dd | 1598 | return device->creator(name, full_name, parent); |
dec38dac MM |
1599 | else |
1600 | return device_create_from(name, | |
5b4d72dd | 1601 | full_name, |
dec38dac MM |
1602 | NULL /* data */, |
1603 | device->callbacks, | |
1604 | parent); | |
cb7a6892 | 1605 | } |
dec38dac | 1606 | error("device_create() unknown device %s\n", name); |
cb7a6892 MM |
1607 | return NULL; |
1608 | } | |
1609 | ||
dec38dac MM |
1610 | |
1611 | INLINE_DEVICES const device * | |
1612 | device_create_from(const char *name, | |
5b4d72dd | 1613 | const char *full_name, |
dec38dac MM |
1614 | void *data, |
1615 | const device_callbacks *callback, | |
1616 | const device *parent) | |
1617 | { | |
1618 | device *me = ZALLOC(device); | |
1619 | me->name = strdup(name); | |
5b4d72dd | 1620 | me->full_name = strdup(full_name); |
dec38dac MM |
1621 | me->data = data; |
1622 | me->callback = callback; | |
1623 | me->parent = parent; | |
1624 | return me; | |
1625 | } | |
1626 | ||
1627 | ||
1628 | INLINE_DEVICES const device_callbacks * | |
1629 | passthrough_device_callbacks(void) | |
1630 | { | |
1631 | static const device_callbacks callbacks = { | |
1632 | ignore_device_init, | |
1633 | pass_device_attach_address, | |
1634 | pass_device_detach_address, | |
1635 | unimp_device_io_read_buffer, | |
1636 | unimp_device_io_write_buffer, | |
1637 | pass_device_dma_read_buffer, | |
1638 | pass_device_dma_write_buffer, | |
1639 | pass_device_attach_interrupt, | |
1640 | pass_device_detach_interrupt, | |
1641 | pass_device_interrupt, | |
1642 | unimp_device_interrupt_ack, | |
1643 | unimp_device_ioctl, | |
1644 | }; | |
1645 | return &callbacks; | |
1646 | } | |
1647 | ||
1648 | ||
1649 | ||
cb7a6892 | 1650 | #endif /* _DEVICES_C_ */ |