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