Index: arm/ChangeLog
[platform/upstream/binutils.git] / sim / ppc / emul_bugapi.c
1 /*  This file is part of the program psim.
2
3     Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 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 _EMUL_BUGAPI_C_
23 #define _EMUL_BUGAPI_C_
24
25 /* Note: this module is called via a table.  There is no benefit in
26    making it inline */
27
28 #include "emul_generic.h"
29 #include "emul_bugapi.h"
30
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34
35 #ifdef HAVE_STDLIB_H
36 #include <stdlib.h>
37 #endif
38
39 #ifdef HAVE_STRING_H
40 #include <string.h>
41 #else
42 #ifdef HAVE_STRINGS_H
43 #include <strings.h>
44 #endif
45 #endif
46
47
48 /* EMULATION
49
50    BUG - Motorola's embeded firmware BUG interface
51
52    DESCRIPTION
53
54    
55
56    */
57
58
59 /* from PowerPCBug Debugging Package User's Manual, part 2 of 2 and also bug.S - Dale Rahn */
60 #define _INCHR          0x000           /* Input character */
61 #define _INSTAT         0x001           /* Input serial port status */
62 #define _INLN           0x002           /* Input line (pointer / pointer format) */
63 #define _READSTR        0x003           /* Input string (pointer / count format) */
64 #define _READLN         0x004           /* Input line (pointer / count format) */
65 #define _CHKBRK         0x005           /* Check for break */
66 #define _DSKRD          0x010           /* Disk read */
67 #define _DSKWR          0x011           /* Disk write */
68 #define _DSKCFIG        0x012           /* Disk configure */
69 #define _DSKFMT         0x014           /* Disk format */
70 #define _DSKCTRL        0x015           /* Disk control */
71 #define _NETRD          0x018           /* Read from host */
72 #define _NETWR          0x019           /* Write to host */
73 #define _NETCFIG        0x01a           /* Configure network parameters */
74 #define _NETOPN         0x01b           /* Open file for reading */
75 #define _NETFRD         0x01c           /* Retreive specified file blocks */
76 #define _NETCTRL        0x01d           /* Implement special control functions */
77 #define _OUTCHR         0x020           /* Output character (pointer / pointer format) */
78 #define _OUTSTR         0x021           /* Output string (pointer / pointer format) */
79 #define _OUTLN          0x022           /* Output line (pointer / pointer format) */
80 #define _WRITE          0x023           /* Output string (pointer / count format) */
81 #define _WRITELN        0x024           /* Output line (pointer / count format) */
82 #define _WRITDLN        0x025           /* Output line with data (pointer / count format) */
83 #define _PCRLF          0x026           /* Output carriage return and line feed */
84 #define _ERASLN         0x027           /* Erase line */
85 #define _WRITD          0x028           /* Output string with data (pointer / count format) */
86 #define _SNDBRK         0x029           /* Send break */
87 #define _DELAY          0x043           /* Timer delay */
88 #define _RTC_TM         0x050           /* Time initialization for RTC */
89 #define _RTC_DT         0x051           /* Date initialization for RTC */
90 #define _RTC_DSP        0x052           /* Display RTC time and date */
91 #define _RTC_RD         0x053           /* Read the RTC registers */
92 #define _REDIR          0x060           /* Redirect I/O of a system call function */
93 #define _REDIR_I        0x061           /* Redirect input */
94 #define _REDIR_O        0x062           /* Redirect output */
95 #define _RETURN         0x063           /* Return to PPCbug */
96 #define _BINDEC         0x064           /* Convert binary to binary coded decimal (BCD) */
97 #define _CHANGEV        0x067           /* Parse value */
98 #define _STRCMP         0x068           /* Compare two strings (pointer / count format) */
99 #define _MULU32         0x069           /* Multiply two 32-bit unsigned integers */
100 #define _DIVU32         0x06a           /* Divide two 32-bit unsigned integers */
101 #define _CHK_SUM        0x06b           /* Generate checksum */
102 #define _BRD_ID         0x070           /* Return pointer to board ID packet */
103 #define _ENVIRON        0x071           /* Access boot environment parameters */
104 #define _DIAGFCN        0x074           /* Diagnostic function(s) */
105 #define _SIOPEPS        0x090           /* Retrieve SCSI pointers */
106 #define _IOINQ          0x120           /* Port inquire */
107 #define _IOINFORM       0x124           /* Port inform */
108 #define _IOCONFIG       0x128           /* Port configure */
109 #define _IODELETE       0x12c           /* Port delete */
110 #define _SYMBOLTA       0x130           /* Attach symbol table */
111 #define _SYMBOLDA       0x131           /* Detach symbol table */
112
113 struct bug_map {
114   int value;
115   const char *info;
116 };
117
118 static const struct bug_map bug_mapping[] = {
119   { _INCHR,     ".INCHR -- Input character" },
120   { _INSTAT,    ".INSTAT -- Input serial port status" },
121   { _INLN,      ".INLN -- Input line (pointer / pointer format)" },
122   { _READSTR,   ".READSTR -- Input string (pointer / count format)" },
123   { _READLN,    ".READLN -- Input line (pointer / count format)" },
124   { _CHKBRK,    ".CHKBRK -- Check for break" },
125   { _DSKRD,     ".DSKRD -- Disk read" },
126   { _DSKWR,     ".DSKWR -- Disk write" },
127   { _DSKCFIG,   ".DSKCFIG -- Disk configure" },
128   { _DSKFMT,    ".DSKFMT -- Disk format" },
129   { _DSKCTRL,   ".DSKCTRL -- Disk control" },
130   { _NETRD,     ".NETRD -- Read from host" },
131   { _NETWR,     ".NETWR -- Write to host" },
132   { _NETCFIG,   ".NETCFIG -- Configure network parameters" },
133   { _NETOPN,    ".NETOPN -- Open file for reading" },
134   { _NETFRD,    ".NETFRD -- Retreive specified file blocks" },
135   { _NETCTRL,   ".NETCTRL -- Implement special control functions" },
136   { _OUTCHR,    ".OUTCHR -- Output character" },
137   { _OUTSTR,    ".OUTSTR -- Output string (pointer / pointer format)" },
138   { _OUTLN,     ".OUTLN -- Output line (pointer / pointer format)" },
139   { _WRITE,     ".WRITE -- Output string (pointer / count format)" },
140   { _WRITELN,   ".WRITELN -- Output line (pointer / count format)" },
141   { _WRITDLN,   ".WRITDLN -- Output line with data (pointer / count format)" },
142   { _PCRLF,     ".PCRLF -- Output carriage return and line feed" },
143   { _ERASLN,    ".ERASLN -- Erase line" },
144   { _WRITD,     ".WRITD -- Output string with data (pointer / count format)" },
145   { _SNDBRK,    ".SNDBRK -- Send break" },
146   { _DELAY,     ".DELAY -- Timer delay" },
147   { _RTC_TM,    ".RTC_TM -- Time initialization for RTC" },
148   { _RTC_DT,    ".RTC_DT -- Date initialization for RTC" },
149   { _RTC_DSP,   ".RTC_DSP -- Display RTC time and date" },
150   { _RTC_RD,    ".RTC_RD -- Read the RTC registers" },
151   { _REDIR,     ".REDIR -- Redirect I/O of a system call function" },
152   { _REDIR,     ".REDIR -- Redirect input" },
153   { _REDIR,     ".REDIR -- Redirect output" },
154   { _RETURN,    ".RETURN -- Return to PPCbug" },
155   { _BINDEC,    ".BINDEC -- Convert binary to binary coded decimal (BCD)" },
156   { _CHANGEV,   ".CHANGEV -- Parse value" },
157   { _STRCMP,    ".STRCMP -- Compare two strings (pointer / count format)" },
158   { _MULU32,    ".MULU32 -- Multiply two 32-bit unsigned integers" },
159   { _DIVU32,    ".DIVU32 -- Divide two 32-bit unsigned integers" },
160   { _CHK_SUM,   ".CHK_SUM -- Generate checksum" },
161   { _BRD_ID,    ".BRD_ID -- Return pointer to board ID packet" },
162   { _ENVIRON,   ".ENVIRON -- Access boot environment parameters" },
163   { _DIAGFCN,   ".DIAGFCN -- Diagnostic function(s)" },
164   { _SIOPEPS,   ".SIOPEPS -- Retrieve SCSI pointers" },
165   { _IOINQ,     ".IOINQ -- Port inquire" },
166   { _IOINFORM,  ".IOINFORM -- Port inform" },
167   { _IOCONFIG,  ".IOCONFIG -- Port configure" },
168   { _IODELETE,  ".IODELETE -- Port delete" },
169   { _SYMBOLTA,  ".SYMBOLTA -- Attach symbol table" },
170   { _SYMBOLDA,  ".SYMBOLDA -- Detach symbol table" },
171 };
172
173 #ifndef BUGAPI_END_ADDRESS
174 #define BUGAPI_END_ADDRESS 0x100000
175 #endif
176
177 enum {
178   nr_bugapi_disks = 2,
179 };
180
181
182 struct _os_emul_data {
183   device *root;
184   unsigned_word memory_size;
185   unsigned_word top_of_stack;
186   int interrupt_prefix;
187   unsigned_word interrupt_vector_address;
188   unsigned_word system_call_address;
189   unsigned_word stall_cpu_loop_address;
190   int little_endian;
191   int floating_point_available;
192   /* I/O devices */
193   device_instance *output;
194   device_instance *input;
195   device_instance *(disk[nr_bugapi_disks]);
196 };
197
198
199 static os_emul_data *
200 emul_bugapi_create(device *root,
201                    bfd *image,
202                    const char *name)
203 {
204   device *node;
205   os_emul_data *bugapi;
206
207   /* check it really is for us */
208   if (name != NULL
209       && strcmp(name, "bugapi") != 0
210       && strcmp(name, "bug") != 0)
211     return NULL;
212   if (image != NULL
213       && name == NULL
214       && bfd_get_start_address(image) >= BUGAPI_END_ADDRESS)
215     return NULL;
216
217   bugapi = ZALLOC(os_emul_data);
218
219   /* options */
220   emul_add_tree_options(root, image, "bug", "oea",
221                         1 /*oea-interrupt-prefix*/);
222
223   /* add some real hardware, include eeprom memory for the eeprom trap
224      addresses */
225   emul_add_tree_hardware(root);
226   node = tree_parse(root, "/openprom/memory@0xfff00000");
227   tree_parse(node, "./psim,description \"eeprom trap addresses");
228   tree_parse(node, "./reg 0xfff00000 0x3000");
229
230   bugapi->root = root;
231
232   bugapi->memory_size
233     = tree_find_integer_property(root, "/openprom/options/oea-memory-size");
234   bugapi->interrupt_prefix =
235     tree_find_integer_property(root, "/openprom/options/oea-interrupt-prefix");
236   bugapi->interrupt_vector_address = (bugapi->interrupt_prefix
237                                       ? MASK(0, 43)
238                                       : 0);
239   bugapi->system_call_address = (bugapi->interrupt_vector_address + 0x00c00);
240   bugapi->stall_cpu_loop_address = (bugapi->system_call_address + 0x000f0);
241   bugapi->top_of_stack = bugapi->memory_size - 0x1000;
242   bugapi->little_endian
243     = tree_find_boolean_property(root, "/options/little-endian?");
244   bugapi->floating_point_available
245     = tree_find_boolean_property(root, "/openprom/options/floating-point?");
246   bugapi->input = NULL;
247   bugapi->output = NULL;
248
249   /* initialization */
250   if (image != NULL)
251     tree_parse(root, "/openprom/init/register/0.pc 0x%lx",
252                (unsigned long)bfd_get_start_address(image));
253   tree_parse(root, "/openprom/init/register/pc 0x%lx",
254              (unsigned long)bugapi->stall_cpu_loop_address);
255   tree_parse(root, "/openprom/init/register/sp 0x%lx",
256              (unsigned long)(bugapi->top_of_stack - 16));
257   tree_parse(root, "/openprom/init/register/msr 0x%x",
258              (msr_recoverable_interrupt
259               | (bugapi->little_endian
260                  ? (msr_little_endian_mode
261                     | msr_interrupt_little_endian_mode)
262                  : 0)
263               | (bugapi->floating_point_available
264                  ? msr_floating_point_available
265                  : 0)
266               | (bugapi->interrupt_prefix
267                  ? msr_interrupt_prefix
268                  : 0)
269               ));
270
271   /* patch the system call instruction to call this emulation and then
272      do an rfi */
273   node = tree_parse(root, "/openprom/init/data@0x%lx",
274                     (unsigned long)bugapi->system_call_address);
275   tree_parse(node, "./psim,description \"system-call trap instruction");
276   tree_parse(node, "./real-address 0x%lx",
277              (unsigned long)bugapi->system_call_address);
278   tree_parse(node, "./data 0x%x", emul_call_instruction);
279   node = tree_parse(root, "/openprom/init/data@0x%lx",
280                     (unsigned long)bugapi->system_call_address + 4);
281   tree_parse(node, "./psim,description \"return from interrupt instruction");
282   tree_parse(node, "./real-address 0x%lx",
283              (unsigned long)bugapi->system_call_address + 4);
284   tree_parse(node, "./data 0x%x",
285              emul_rfi_instruction);
286
287   /* patch the end of the system call instruction so that it contains
288      a loop to self instruction and point all the cpu's at this */
289   node = tree_parse(root, "/openprom/init/data@0x%lx",
290                     (unsigned long)bugapi->stall_cpu_loop_address);
291   tree_parse(node, "./psim,description \"cpu-loop instruction");
292   tree_parse(node, "./real-address 0x%lx",
293              (unsigned long)bugapi->stall_cpu_loop_address);
294   tree_parse(node, "./data 0x%lx",
295              (unsigned long)emul_loop_instruction);
296
297   if (image != NULL)
298     tree_parse(root, "/openprom/init/stack/stack-type %s",
299                (image->xvec->flavour == bfd_target_elf_flavour
300                 ? "ppc-elf"
301                 : "ppc-xcoff"));
302
303   if (image != NULL)
304     tree_parse(root, "/openprom/init/load-binary/file-name \"%s",
305                bfd_get_filename(image));
306
307   return bugapi;
308 }
309
310 static void
311 emul_bugapi_init(os_emul_data *bugapi,
312                  int nr_cpus)
313 {
314   int i;
315   /* get the current input/output devices that were created during
316      device tree initialization */
317   bugapi->input = tree_find_ihandle_property(bugapi->root, "/chosen/stdin");
318   bugapi->output = tree_find_ihandle_property(bugapi->root, "/chosen/stdout");
319   /* if present, extract the selected disk devices */
320   for (i = 0; i < nr_bugapi_disks; i++) {
321     char disk[32];
322     char *chp;
323     strcpy(disk, "/chosen/disk0");
324     ASSERT(sizeof(disk) > strlen(disk));
325     chp = strchr(disk, '0');
326     *chp = *chp + i;
327     if (tree_find_property(bugapi->root, disk) != NULL)
328       bugapi->disk[i] = tree_find_ihandle_property(bugapi->root, disk);
329   }
330 }
331
332 static const char *
333 emul_bugapi_instruction_name(int call_id)
334 {
335   static char buffer[40];
336   int i;
337
338   for (i = 0; i < sizeof (bug_mapping) / sizeof (bug_mapping[0]); i++)
339     {
340       if (bug_mapping[i].value == call_id)
341         return bug_mapping[i].info;
342     }
343
344   (void) sprintf (buffer, "Unknown bug call 0x%x", call_id);
345   return buffer;
346 }
347
348 static int
349 emul_bugapi_do_read(os_emul_data *bugapi,
350                     cpu *processor,
351                     unsigned_word cia,
352                     unsigned_word buf,
353                     int nbytes)
354 {
355   unsigned char *scratch_buffer;
356   int status;
357
358   /* get a tempoary bufer */
359   scratch_buffer = (unsigned char *) zalloc(nbytes);
360
361   /* check if buffer exists by reading it */
362   emul_read_buffer((void *)scratch_buffer, buf, nbytes, processor, cia);
363
364   /* read */
365   status = device_instance_read(bugapi->input,
366                                 (void *)scratch_buffer, nbytes);
367
368   /* -1 = error, -2 = nothing available - see "serial" [IEEE1275] */
369   if (status < 0) {
370     status = 0;
371   }
372
373   if (status > 0) {
374     emul_write_buffer((void *)scratch_buffer, buf, status, processor, cia);
375
376     /* Bugapi chops off the trailing n, but leaves it in the buffer */
377     if (scratch_buffer[status-1] == '\n' || scratch_buffer[status-1] == '\r')
378       status--;
379   }
380
381   zfree(scratch_buffer);
382   return status;
383 }
384
385 static void
386 emul_bugapi_do_diskio(os_emul_data *bugapi,
387                       cpu *processor,
388                       unsigned_word cia,
389                       unsigned_word descriptor_addr,
390                       int call_id)
391 {
392   struct dskio_descriptor {
393     unsigned_1 ctrl_lun;
394     unsigned_1 dev_lun;
395     unsigned_2 status;
396     unsigned_word pbuffer;
397     unsigned_4 blk_num;
398     unsigned_2 blk_cnt;
399     unsigned_1 flag;
400 #define BUG_FILE_MARK    0x80
401 #define IGNORE_FILENUM   0x02
402 #define END_OF_FILE      0x01
403     unsigned_1 addr_mod;
404   } descriptor;
405   int block;
406   emul_read_buffer(&descriptor, descriptor_addr, sizeof(descriptor),
407                    processor, cia);
408   T2H(descriptor.ctrl_lun);
409   T2H(descriptor.dev_lun);
410   T2H(descriptor.status);
411   T2H(descriptor.pbuffer);
412   T2H(descriptor.blk_num);
413   T2H(descriptor.blk_cnt);
414   T2H(descriptor.flag);
415   T2H(descriptor.addr_mod);
416   if (descriptor.dev_lun >= nr_bugapi_disks
417       || bugapi->disk[descriptor.dev_lun] == NULL) {
418     error("emul_bugapi_do_diskio: attempt to access unconfigured disk /chosen/disk%d",
419           descriptor.dev_lun);
420   }
421   else {
422     for (block = 0; block < descriptor.blk_cnt; block++) {
423       device_instance *disk = bugapi->disk[descriptor.dev_lun];
424       unsigned_1 buf[512]; /*????*/
425       unsigned_word block_nr = descriptor.blk_num + block;
426       unsigned_word byte_nr = block_nr * sizeof(buf);
427       unsigned_word block_addr = descriptor.pbuffer + block*sizeof(buf);
428       if (device_instance_seek(disk, 0, byte_nr) < 0)
429         error("emul_bugapi_do_diskio: bad seek\n");
430       switch (call_id) {
431       case _DSKRD:
432         if (device_instance_read(disk, buf, sizeof(buf)) != sizeof(buf))
433           error("emul_`bugapi_do_diskio: bad read\n");
434         emul_write_buffer(buf, block_addr, sizeof(buf), processor, cia);
435         break;
436       case _DSKWR:
437         emul_read_buffer(buf, block_addr, sizeof(buf), processor, cia);
438         if (device_instance_write(disk, buf, sizeof(buf)) != sizeof(buf))
439           error("emul_bugapi_do_diskio: bad write\n");
440         break;
441       default:
442         error("emul_bugapi_do_diskio: bad switch\n");
443       }
444     }
445   }
446 }
447
448 static void
449 emul_bugapi_do_write(os_emul_data *bugapi,
450                      cpu *processor,
451                      unsigned_word cia,
452                      unsigned_word buf,
453                      int nbytes,
454                      const char *suffix)
455 {
456   void *scratch_buffer = NULL;
457
458   /* get a tempoary bufer */
459   if (nbytes > 0)
460     {
461       scratch_buffer = zalloc(nbytes);
462
463       /* copy in */
464       emul_read_buffer(scratch_buffer, buf, nbytes,
465                        processor, cia);
466
467       /* write */
468       device_instance_write(bugapi->output, scratch_buffer, nbytes);
469
470       zfree(scratch_buffer);
471     }
472
473   if (suffix)
474     device_instance_write(bugapi->output, suffix, strlen(suffix));
475
476   flush_stdoutput ();
477 }
478
479 static int
480 emul_bugapi_instruction_call(cpu *processor,
481                              unsigned_word cia,
482                              unsigned_word ra,
483                              os_emul_data *bugapi)
484 {
485   const int call_id = cpu_registers(processor)->gpr[10];
486   unsigned char uc;
487
488 #define MY_INDEX itable_instruction_call
489   ITRACE (trace_os_emul,
490           (" 0x%x %s, r3 = 0x%lx, r4 = 0x%lx\n",
491            call_id, emul_bugapi_instruction_name (call_id),
492            (long)cpu_registers(processor)->gpr[3],
493            (long)cpu_registers(processor)->gpr[4]));;
494
495   /* check that this isn't an invalid instruction */
496   if (cia != bugapi->system_call_address)
497     return 0;
498
499   switch (call_id) {
500   default:
501     error("emul-bugapi: unimplemented bugapi %s from address 0x%lx\n",
502           emul_bugapi_instruction_name (call_id), SRR0);
503     break;
504
505   /* read a single character, output r3 = byte */
506   /* FIXME: Add support to unbuffer input */
507   case _INCHR:
508     if (device_instance_read(bugapi->input, (void *)&uc, 1) <= 0)
509       uc = 0;
510     cpu_registers(processor)->gpr[3] = uc;
511     break;
512
513   /* read a line of at most 256 bytes, r3 = ptr to 1st byte, output r3 = ptr to last byte+1  */
514   case _INLN:
515     cpu_registers(processor)->gpr[3] += emul_bugapi_do_read(bugapi,
516                                                             processor, cia,
517                                                             cpu_registers(processor)->gpr[3],
518                                                             256);
519     break;
520
521   /* output a character, r3 = character */
522   case _OUTCHR:
523     {
524       char out = (char)cpu_registers(processor)->gpr[3];
525       device_instance_write(bugapi->output, &out, 1);
526       break;
527     }
528
529   /* output a string, r3 = ptr to 1st byte, r4 = ptr to last byte+1 */
530   case _OUTSTR:
531     emul_bugapi_do_write(bugapi,
532                          processor, cia,
533                          cpu_registers(processor)->gpr[3],
534                          cpu_registers(processor)->gpr[4] - cpu_registers(processor)->gpr[3],
535                          (const char *)0);
536     break;
537
538   /* output a string followed by \r\n, r3 = ptr to 1st byte, r4 = ptr to last byte+1 */
539   case _OUTLN:
540
541     emul_bugapi_do_write(bugapi,
542                          processor, cia,
543                          cpu_registers(processor)->gpr[3],
544                          cpu_registers(processor)->gpr[4] - cpu_registers(processor)->gpr[3],
545                          "\n");
546     break;
547
548   /* output a \r\n */
549   case _PCRLF:
550     device_instance_write(bugapi->output, "\n", 1);
551     break;
552
553   /* read/write blocks of data to/from the disk */
554   case _DSKWR:
555   case _DSKRD:
556     emul_bugapi_do_diskio(bugapi, processor, cia,
557                           cpu_registers(processor)->gpr[3],
558                           call_id);
559     break;
560
561   /* return to ppcbug monitor (exiting with gpr[3] as status is not
562      part of the bug monitor) */
563   case _RETURN:
564     cpu_halt(processor, cia, was_exited, cpu_registers(processor)->gpr[3]);
565     break;
566   }
567   return 1;
568   /* the instruction following this one is a RFI.  Thus by just
569      continuing the return from system call is performed */
570 }
571
572 const os_emul emul_bugapi = {
573   "bugapi",
574   emul_bugapi_create,
575   emul_bugapi_init,
576   0, /*system_call*/
577   emul_bugapi_instruction_call,
578   0 /*data*/
579 };
580
581 #endif