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 102struct bug_map { 103 int value; 104 const char *info; 105}; 106 107static 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 166enum { 167 nr_bugapi_disks = 2, 168}; 169 170 171struct _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 188static os_emul_data * 189emul_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 304static void 305emul_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 326static const char * 327emul_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 342static int 343emul_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 379static void 380emul_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 442static void 443emul_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 473static int 474emul_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 566const 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