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 113struct bug_map { 114 int value; 115 const char *info; 116}; 117 118static 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 177enum { 178 nr_bugapi_disks = 2, 179}; 180 181 182struct _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 199static os_emul_data * 200emul_bugapi_create(device *root, 201 bfd *image, 202 const char *name) 203{ 204 device *node; 205 os_emul_data *bugapi; 206 char *filename; 207 208 /* check it really is for us */ 209 if (name != NULL 210 && strcmp(name, "bugapi") != 0 211 && strcmp(name, "bug") != 0) 212 return NULL; 213 if (image != NULL 214 && name == NULL 215 && bfd_get_start_address(image) >= BUGAPI_END_ADDRESS) 216 return NULL; 217 218 bugapi = ZALLOC(os_emul_data); 219 220 /* options */ 221 emul_add_tree_options(root, image, "bug", "oea", 222 1 /*oea-interrupt-prefix*/); 223 224 /* add some real hardware, include eeprom memory for the eeprom trap 225 addresses */ 226 emul_add_tree_hardware(root); 227 node = tree_parse(root, "/openprom/memory@0xfff00000"); 228 tree_parse(node, "./psim,description \"eeprom trap addresses"); 229 tree_parse(node, "./reg 0xfff00000 0x3000"); 230 231 bugapi->root = root; 232 233 bugapi->memory_size 234 = tree_find_integer_property(root, "/openprom/options/oea-memory-size"); 235 bugapi->interrupt_prefix = 236 tree_find_integer_property(root, "/openprom/options/oea-interrupt-prefix"); 237 bugapi->interrupt_vector_address = (bugapi->interrupt_prefix 238 ? MASK(0, 43) 239 : 0); 240 bugapi->system_call_address = (bugapi->interrupt_vector_address + 0x00c00); 241 bugapi->stall_cpu_loop_address = (bugapi->system_call_address + 0x000f0); 242 bugapi->top_of_stack = bugapi->memory_size - 0x1000; 243 bugapi->little_endian 244 = tree_find_boolean_property(root, "/options/little-endian?"); 245 bugapi->floating_point_available 246 = tree_find_boolean_property(root, "/openprom/options/floating-point?"); 247 bugapi->input = NULL; 248 bugapi->output = NULL; 249 250 /* initialization */ 251 if (image != NULL) 252 tree_parse(root, "/openprom/init/register/0.pc 0x%lx", 253 (unsigned long)bfd_get_start_address(image)); 254 tree_parse(root, "/openprom/init/register/pc 0x%lx", 255 (unsigned long)bugapi->stall_cpu_loop_address); 256 tree_parse(root, "/openprom/init/register/sp 0x%lx", 257 (unsigned long)(bugapi->top_of_stack - 16)); 258 tree_parse(root, "/openprom/init/register/msr 0x%x", 259 (msr_recoverable_interrupt 260 | (bugapi->little_endian 261 ? (msr_little_endian_mode 262 | msr_interrupt_little_endian_mode) 263 : 0) 264 | (bugapi->floating_point_available 265 ? msr_floating_point_available 266 : 0) 267 | (bugapi->interrupt_prefix 268 ? msr_interrupt_prefix 269 : 0) 270 )); 271 272 /* patch the system call instruction to call this emulation and then 273 do an rfi */ 274 node = tree_parse(root, "/openprom/init/data@0x%lx", 275 (unsigned long)bugapi->system_call_address); 276 tree_parse(node, "./psim,description \"system-call trap instruction"); 277 tree_parse(node, "./real-address 0x%lx", 278 (unsigned long)bugapi->system_call_address); 279 tree_parse(node, "./data 0x%x", emul_call_instruction); 280 node = tree_parse(root, "/openprom/init/data@0x%lx", 281 (unsigned long)bugapi->system_call_address + 4); 282 tree_parse(node, "./psim,description \"return from interrupt instruction"); 283 tree_parse(node, "./real-address 0x%lx", 284 (unsigned long)bugapi->system_call_address + 4); 285 tree_parse(node, "./data 0x%x", 286 emul_rfi_instruction); 287 288 /* patch the end of the system call instruction so that it contains 289 a loop to self instruction and point all the cpu's at this */ 290 node = tree_parse(root, "/openprom/init/data@0x%lx", 291 (unsigned long)bugapi->stall_cpu_loop_address); 292 tree_parse(node, "./psim,description \"cpu-loop instruction"); 293 tree_parse(node, "./real-address 0x%lx", 294 (unsigned long)bugapi->stall_cpu_loop_address); 295 tree_parse(node, "./data 0x%lx", 296 (unsigned long)emul_loop_instruction); 297 298 if (image != NULL) 299 tree_parse(root, "/openprom/init/stack/stack-type %s", 300 (image->xvec->flavour == bfd_target_elf_flavour 301 ? "ppc-elf" 302 : "ppc-xcoff")); 303 304 if (image != NULL) 305 { 306 filename = tree_quote_property (bfd_get_filename(image)); 307 tree_parse(root, "/openprom/init/load-binary/file-name %s", 308 filename); 309 free (filename); 310 } 311 312 return bugapi; 313} 314 315static void 316emul_bugapi_init(os_emul_data *bugapi, 317 int nr_cpus) 318{ 319 int i; 320 /* get the current input/output devices that were created during 321 device tree initialization */ 322 bugapi->input = tree_find_ihandle_property(bugapi->root, "/chosen/stdin"); 323 bugapi->output = tree_find_ihandle_property(bugapi->root, "/chosen/stdout"); 324 /* if present, extract the selected disk devices */ 325 for (i = 0; i < nr_bugapi_disks; i++) { 326 char disk[32]; 327 char *chp; 328 strcpy(disk, "/chosen/disk0"); 329 ASSERT(sizeof(disk) > strlen(disk)); 330 chp = strchr(disk, '0'); 331 *chp = *chp + i; 332 if (tree_find_property(bugapi->root, disk) != NULL) 333 bugapi->disk[i] = tree_find_ihandle_property(bugapi->root, disk); 334 } 335} 336 337static const char * 338emul_bugapi_instruction_name(int call_id) 339{ 340 static char buffer[40]; 341 int i; 342 343 for (i = 0; i < sizeof (bug_mapping) / sizeof (bug_mapping[0]); i++) 344 { 345 if (bug_mapping[i].value == call_id) 346 return bug_mapping[i].info; 347 } 348 349 (void) sprintf (buffer, "Unknown bug call 0x%x", call_id); 350 return buffer; 351} 352 353static int 354emul_bugapi_do_read(os_emul_data *bugapi, 355 cpu *processor, 356 unsigned_word cia, 357 unsigned_word buf, 358 int nbytes) 359{ 360 unsigned char *scratch_buffer; 361 int status; 362 363 /* get a tempoary bufer */ 364 scratch_buffer = (unsigned char *) zalloc(nbytes); 365 366 /* check if buffer exists by reading it */ 367 emul_read_buffer((void *)scratch_buffer, buf, nbytes, processor, cia); 368 369 /* read */ 370 status = device_instance_read(bugapi->input, 371 (void *)scratch_buffer, nbytes); 372 373 /* -1 = error, -2 = nothing available - see "serial" [IEEE1275] */ 374 if (status < 0) { 375 status = 0; 376 } 377 378 if (status > 0) { 379 emul_write_buffer((void *)scratch_buffer, buf, status, processor, cia); 380 381 /* Bugapi chops off the trailing n, but leaves it in the buffer */ 382 if (scratch_buffer[status-1] == '\n' || scratch_buffer[status-1] == '\r') 383 status--; 384 } 385 386 zfree(scratch_buffer); 387 return status; 388} 389 390static void 391emul_bugapi_do_diskio(os_emul_data *bugapi, 392 cpu *processor, 393 unsigned_word cia, 394 unsigned_word descriptor_addr, 395 int call_id) 396{ 397 struct dskio_descriptor { 398 unsigned_1 ctrl_lun; 399 unsigned_1 dev_lun; 400 unsigned_2 status; 401 unsigned_word pbuffer; 402 unsigned_4 blk_num; 403 unsigned_2 blk_cnt; 404 unsigned_1 flag; 405#define BUG_FILE_MARK 0x80 406#define IGNORE_FILENUM 0x02 407#define END_OF_FILE 0x01 408 unsigned_1 addr_mod; 409 } descriptor; 410 int block; 411 emul_read_buffer(&descriptor, descriptor_addr, sizeof(descriptor), 412 processor, cia); 413 T2H(descriptor.ctrl_lun); 414 T2H(descriptor.dev_lun); 415 T2H(descriptor.status); 416 T2H(descriptor.pbuffer); 417 T2H(descriptor.blk_num); 418 T2H(descriptor.blk_cnt); 419 T2H(descriptor.flag); 420 T2H(descriptor.addr_mod); 421 if (descriptor.dev_lun >= nr_bugapi_disks 422 || bugapi->disk[descriptor.dev_lun] == NULL) { 423 error("emul_bugapi_do_diskio: attempt to access unconfigured disk /chosen/disk%d", 424 descriptor.dev_lun); 425 } 426 else { 427 for (block = 0; block < descriptor.blk_cnt; block++) { 428 device_instance *disk = bugapi->disk[descriptor.dev_lun]; 429 unsigned_1 buf[512]; /*????*/ 430 unsigned_word block_nr = descriptor.blk_num + block; 431 unsigned_word byte_nr = block_nr * sizeof(buf); 432 unsigned_word block_addr = descriptor.pbuffer + block*sizeof(buf); 433 if (device_instance_seek(disk, 0, byte_nr) < 0) 434 error("emul_bugapi_do_diskio: bad seek\n"); 435 switch (call_id) { 436 case _DSKRD: 437 if (device_instance_read(disk, buf, sizeof(buf)) != sizeof(buf)) 438 error("emul_`bugapi_do_diskio: bad read\n"); 439 emul_write_buffer(buf, block_addr, sizeof(buf), processor, cia); 440 break; 441 case _DSKWR: 442 emul_read_buffer(buf, block_addr, sizeof(buf), processor, cia); 443 if (device_instance_write(disk, buf, sizeof(buf)) != sizeof(buf)) 444 error("emul_bugapi_do_diskio: bad write\n"); 445 break; 446 default: 447 error("emul_bugapi_do_diskio: bad switch\n"); 448 } 449 } 450 } 451} 452 453static void 454emul_bugapi_do_write(os_emul_data *bugapi, 455 cpu *processor, 456 unsigned_word cia, 457 unsigned_word buf, 458 int nbytes, 459 const char *suffix) 460{ 461 void *scratch_buffer = NULL; 462 463 /* get a tempoary bufer */ 464 if (nbytes > 0) 465 { 466 scratch_buffer = zalloc(nbytes); 467 468 /* copy in */ 469 emul_read_buffer(scratch_buffer, buf, nbytes, 470 processor, cia); 471 472 /* write */ 473 device_instance_write(bugapi->output, scratch_buffer, nbytes); 474 475 zfree(scratch_buffer); 476 } 477 478 if (suffix) 479 device_instance_write(bugapi->output, suffix, strlen(suffix)); 480 481 flush_stdoutput (); 482} 483 484static int 485emul_bugapi_instruction_call(cpu *processor, 486 unsigned_word cia, 487 unsigned_word ra, 488 os_emul_data *bugapi) 489{ 490 const int call_id = cpu_registers(processor)->gpr[10]; 491 unsigned char uc; 492 493#define MY_INDEX itable_instruction_call 494 ITRACE (trace_os_emul, 495 (" 0x%x %s, r3 = 0x%lx, r4 = 0x%lx\n", 496 call_id, emul_bugapi_instruction_name (call_id), 497 (long)cpu_registers(processor)->gpr[3], 498 (long)cpu_registers(processor)->gpr[4]));; 499 500 /* check that this isn't an invalid instruction */ 501 if (cia != bugapi->system_call_address) 502 return 0; 503 504 switch (call_id) { 505 default: 506 error("emul-bugapi: unimplemented bugapi %s from address 0x%lx\n", 507 emul_bugapi_instruction_name (call_id), SRR0); 508 break; 509 510 /* read a single character, output r3 = byte */ 511 /* FIXME: Add support to unbuffer input */ 512 case _INCHR: 513 if (device_instance_read(bugapi->input, (void *)&uc, 1) <= 0) 514 uc = 0; 515 cpu_registers(processor)->gpr[3] = uc; 516 break; 517 518 /* read a line of at most 256 bytes, r3 = ptr to 1st byte, output r3 = ptr to last byte+1 */ 519 case _INLN: 520 cpu_registers(processor)->gpr[3] += emul_bugapi_do_read(bugapi, 521 processor, cia, 522 cpu_registers(processor)->gpr[3], 523 256); 524 break; 525 526 /* output a character, r3 = character */ 527 case _OUTCHR: 528 { 529 char out = (char)cpu_registers(processor)->gpr[3]; 530 device_instance_write(bugapi->output, &out, 1); 531 break; 532 } 533 534 /* output a string, r3 = ptr to 1st byte, r4 = ptr to last byte+1 */ 535 case _OUTSTR: 536 emul_bugapi_do_write(bugapi, 537 processor, cia, 538 cpu_registers(processor)->gpr[3], 539 cpu_registers(processor)->gpr[4] - cpu_registers(processor)->gpr[3], 540 (const char *)0); 541 break; 542 543 /* output a string followed by \r\n, r3 = ptr to 1st byte, r4 = ptr to last byte+1 */ 544 case _OUTLN: 545 546 emul_bugapi_do_write(bugapi, 547 processor, cia, 548 cpu_registers(processor)->gpr[3], 549 cpu_registers(processor)->gpr[4] - cpu_registers(processor)->gpr[3], 550 "\n"); 551 break; 552 553 /* output a \r\n */ 554 case _PCRLF: 555 device_instance_write(bugapi->output, "\n", 1); 556 break; 557 558 /* read/write blocks of data to/from the disk */ 559 case _DSKWR: 560 case _DSKRD: 561 emul_bugapi_do_diskio(bugapi, processor, cia, 562 cpu_registers(processor)->gpr[3], 563 call_id); 564 break; 565 566 /* return to ppcbug monitor (exiting with gpr[3] as status is not 567 part of the bug monitor) */ 568 case _RETURN: 569 cpu_halt(processor, cia, was_exited, cpu_registers(processor)->gpr[3]); 570 break; 571 } 572 return 1; 573 /* the instruction following this one is a RFI. Thus by just 574 continuing the return from system call is performed */ 575} 576 577const os_emul emul_bugapi = { 578 "bugapi", 579 emul_bugapi_create, 580 emul_bugapi_init, 581 0, /*system_call*/ 582 emul_bugapi_instruction_call, 583 0 /*data*/ 584}; 585 586#endif 587