| 1 | /* |
| 2 | * ========== Copyright Header Begin ========================================== |
| 3 | * |
| 4 | * OpenSPARC T2 Processor File: dumbserial.c |
| 5 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. |
| 6 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. |
| 7 | * |
| 8 | * The above named program is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU General Public |
| 10 | * License version 2 as published by the Free Software Foundation. |
| 11 | * |
| 12 | * The above named program is distributed in the hope that it will be |
| 13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public |
| 18 | * License along with this work; if not, write to the Free Software |
| 19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. |
| 20 | * |
| 21 | * ========== Copyright Header End ============================================ |
| 22 | */ |
| 23 | /* |
| 24 | * Copyright 2007 Sun Microsystems, Inc. All rights reserved. |
| 25 | * Use is subject to license terms. |
| 26 | */ |
| 27 | #pragma ident "@(#)dumbserial.c 1.57 07/10/12 SMI" |
| 28 | |
| 29 | /* |
| 30 | * This is a very generic serial I/O / console device |
| 31 | * |
| 32 | * Basically, write to the out register and output the |
| 33 | * value to the display. |
| 34 | * Read from the in register and get a character of |
| 35 | * input. |
| 36 | * A status register is a avilable to poll to indicate if |
| 37 | * a character is available for input .. |
| 38 | * ... not entirely dis-similar to the NEC 16550 device .. |
| 39 | */ |
| 40 | |
| 41 | #include <stdio.h> |
| 42 | #include <stdlib.h> |
| 43 | #include <unistd.h> |
| 44 | #include <sys/types.h> |
| 45 | #include <sys/param.h> /* MAXPATHLEN */ |
| 46 | #include <sys/stat.h> |
| 47 | #include <poll.h> |
| 48 | #include <string.h> |
| 49 | #include <pthread.h> |
| 50 | #include <errno.h> |
| 51 | #include <signal.h> |
| 52 | #include <sys/socket.h> |
| 53 | #include <netinet/in.h> |
| 54 | #include <sys/un.h> |
| 55 | #include <arpa/inet.h> |
| 56 | #include <netdb.h> |
| 57 | #include <pthread.h> |
| 58 | |
| 59 | #include "basics.h" |
| 60 | #include "allocate.h" |
| 61 | #include "createthr.h" |
| 62 | #include "lexer.h" |
| 63 | #include "simcore.h" |
| 64 | #include "config.h" |
| 65 | #include "dumpinfo.h" |
| 66 | #include "strutil.h" |
| 67 | #include "fatal.h" |
| 68 | #include "device.h" |
| 69 | #include "jbus_mondo.h" |
| 70 | #include "pcie_device.h" |
| 71 | |
| 72 | #define DBGX(s) do { } while (0) |
| 73 | |
| 74 | |
| 75 | /* |
| 76 | * A dumb serial device - briefly modeled on the NS 16550 device |
| 77 | * ... i.e. register bit fields are similar, but that's about it. |
| 78 | * |
| 79 | * We support input and output character buffering. |
| 80 | * Eventually these sizes will be settable from the configfile |
| 81 | * Also eventually we will understand baud rates etc. and fake |
| 82 | * these appropriately. |
| 83 | * For the moment, we support only a simple status poll, byte |
| 84 | * get and byte put. |
| 85 | * Status simply returns, byte-available, underrun, and overrun |
| 86 | * for input info. Nothing for output (yet). |
| 87 | */ |
| 88 | |
| 89 | |
| 90 | /* |
| 91 | * Registers (8-bits only - 8byte addressing, upper bits read 0 write ignored) |
| 92 | * num RW Function: |
| 93 | * 0 R Input data register |
| 94 | * 0 W Output data register |
| 95 | * 1 RW Interrupt enable register |
| 96 | * 2 R Interrupt indentification register |
| 97 | * 2 W FIFO control register |
| 98 | * 3 RW Line control register |
| 99 | * 4 RW Modem control register |
| 100 | * 5 RW Line status register |
| 101 | * 6 RW Modem status register |
| 102 | * 7 RW Scratch register |
| 103 | * 8 RW Divisor lo |
| 104 | * 9 RW Divisor hi |
| 105 | * |
| 106 | * See 16550 data sheet p 14 - table M |
| 107 | */ |
| 108 | |
| 109 | |
| 110 | typedef enum { |
| 111 | DS_Input = 0x0, /* RO */ |
| 112 | DS_Output = 0x0, /* WO */ |
| 113 | DS_IntEnable = 0x1, |
| 114 | DS_IntIdent = 0x2, /* RO */ |
| 115 | DS_FIFOCtrl = 0x2, /* WO */ |
| 116 | DS_LineCtrl = 0x3, |
| 117 | DS_ModemCtrl = 0x4, |
| 118 | DS_LineStatus = 0x5, |
| 119 | DS_ModemStatus = 0x6, |
| 120 | DS_Scratch = 0x7, |
| 121 | DS_DivLo = 0x8, |
| 122 | DS_DivHi = 0x9 |
| 123 | } ds_reg_t; |
| 124 | |
| 125 | #define DS_NREGS 8 |
| 126 | |
| 127 | typedef enum { |
| 128 | TE_Normal, |
| 129 | TE_SawCR, |
| 130 | TE_SawTilde |
| 131 | #if ERROR_TRAP_GEN /* { */ |
| 132 | ,TE_SawTildeE |
| 133 | #endif /* } ERROR_TRAP_GEN */ |
| 134 | } ds_tildestate_t; |
| 135 | |
| 136 | /* |
| 137 | * static only to avoid name clashes with other |
| 138 | * modules ... in reality these are exported |
| 139 | * via the dev_type_t struct pointers |
| 140 | */ |
| 141 | static void ds_parse(config_dev_t *); |
| 142 | static void ds_init(config_dev_t *); |
| 143 | static void ds_dump(config_dev_t *); |
| 144 | static bool_t ds_cpu_access(simcpu_t *, config_addr_t *, tpaddr_t offset, maccess_t op, uint64_t * regp); |
| 145 | |
| 146 | #define ptest(_s) if (fds.revents & _s) printf(","#_s) |
| 147 | |
| 148 | |
| 149 | dev_type_t dev_type_dumbserial={ |
| 150 | "dumbserial", |
| 151 | ds_parse, |
| 152 | ds_init, |
| 153 | ds_dump, |
| 154 | generic_device_non_cacheable, |
| 155 | ds_cpu_access, |
| 156 | DEV_MAGIC |
| 157 | }; |
| 158 | |
| 159 | |
| 160 | typedef struct { |
| 161 | config_dev_t * config_devp; /* back pointer to device record */ |
| 162 | |
| 163 | bool_t has_registers; |
| 164 | bool_t uses_xterm; |
| 165 | bool_t dlab; /* DLAB bit from LCR */ |
| 166 | #define DS_LCR_DLAB 0x80 |
| 167 | |
| 168 | /* device registers .. */ |
| 169 | uint8_t scratch; /* ! */ |
| 170 | uint8_t line_status; |
| 171 | #define DS_LSTAT_DATA_READY 0x1 |
| 172 | #define DS_LSTAT_OVERRUN 0x2 |
| 173 | #define DS_LSTAT_PARTIY_ERR 0x4 |
| 174 | #define DS_LSTAT_FRAMING_ERR 0x8 |
| 175 | #define DS_LSTAT_BREAK 0x10 |
| 176 | #define DS_LSTAT_TX_HOLD 0x20 |
| 177 | #define DS_LSTAT_TX_EMPTY 0x40 |
| 178 | #define DS_LSTAT_RCV_ERR 0x80 |
| 179 | |
| 180 | #define DEFAULT_RXFIFOSZ 1024 |
| 181 | |
| 182 | struct { |
| 183 | uint8_t *bufp; /* Non null if allocated */ |
| 184 | int head; |
| 185 | int tail; |
| 186 | int count; |
| 187 | int size; |
| 188 | } in; |
| 189 | |
| 190 | int reg_shift; /* register alignment 0 .. 3 (default = 0 = byte align) */ |
| 191 | #define DS_REG_SHIFT_DEF 0 |
| 192 | |
| 193 | /* the following if uses_xterm is set */ |
| 194 | #define DEFAULT_XTERM_STR "/usr/openwin/bin/xterm -T TTY -e /bin/telnet %s %d &" |
| 195 | char * term_strp; |
| 196 | int tty_skt; /* socket to send / receive to client on */ |
| 197 | bool_t tty_attached; |
| 198 | pthread_t pthread_id; |
| 199 | pthread_mutex_t tty_lock; |
| 200 | pthread_cond_t tty_cv; |
| 201 | ds_tildestate_t tilde_state; |
| 202 | |
| 203 | /* following are for exit_string and time_string */ |
| 204 | char * xterm_string; |
| 205 | bool_t exit_on_string; |
| 206 | int xterm_string_pos; |
| 207 | time_t start_time; |
| 208 | time_t stop_time; |
| 209 | |
| 210 | } ds_state_t; |
| 211 | |
| 212 | static void ds_rxfifo_alloc(ds_state_t * dsp); |
| 213 | static void ds_parse_rxfifo_contents(ds_state_t * dsp); |
| 214 | static void ds_reg_init(ds_state_t * dsp, int regnum, int val); |
| 215 | |
| 216 | |
| 217 | |
| 218 | /* |
| 219 | * Internal functions |
| 220 | */ |
| 221 | |
| 222 | static void create_term(ds_state_t * dsp); |
| 223 | static void * ds_input_thr(void * ptr); |
| 224 | static void ds_insert_bytes(ds_state_t * dsp, uint8_t * bufp, int len); |
| 225 | static void dump_buf(char * strp, uint8_t * bufp, int len); |
| 226 | static void ds_dump_instruction_counts(ds_state_t * dsp); |
| 227 | static void ds_output(ds_state_t *dsp, char *str); |
| 228 | static void ds_save_state(ds_state_t * dsp); |
| 229 | static void ds_debug_hook_dump(ds_state_t * dsp); |
| 230 | |
| 231 | |
| 232 | #if !NDEBUG /* { */ |
| 233 | /* Nasty hack for TLB debugging - to go away FIXME */ |
| 234 | static void ds_dump_tlb(ds_state_t * dsp, bool_t is_itlb); |
| 235 | #endif /* } */ |
| 236 | |
| 237 | #if ERROR_TRAP_GEN /* { */ |
| 238 | static void ds_load_error_file(ds_state_t * dsp); |
| 239 | static void ds_dump_error_active(ds_state_t * dsp); |
| 240 | static void ds_dump_error_supported(ds_state_t * dsp); |
| 241 | |
| 242 | #endif /* } */ |
| 243 | |
| 244 | /* |
| 245 | * Complete the creation and parsing of this specific cpu |
| 246 | * |
| 247 | * .. basically parse and allocate what is necessary. |
| 248 | */ |
| 249 | |
| 250 | void ds_parse(config_dev_t * config_devp) |
| 251 | { |
| 252 | ds_state_t * dsp; |
| 253 | lexer_tok_t tok; |
| 254 | |
| 255 | DBG( printf("dumbserial_parse: parsing device %d\n", config_devp->device_id); ); |
| 256 | |
| 257 | dsp = (void*)Xcalloc(1, ds_state_t); |
| 258 | config_devp->devp = (void*)dsp; |
| 259 | |
| 260 | dsp->has_registers = false; |
| 261 | dsp->uses_xterm = false; /* default: output but no input */ |
| 262 | dsp->term_strp = NULL; |
| 263 | dsp->in.size = -1; |
| 264 | dsp->reg_shift = DS_REG_SHIFT_DEF; |
| 265 | |
| 266 | /* setup initial register values */ |
| 267 | |
| 268 | dsp->line_status = DS_LSTAT_TX_EMPTY | DS_LSTAT_TX_HOLD; |
| 269 | dsp->dlab = false; |
| 270 | dsp->scratch = 0; |
| 271 | |
| 272 | dsp->xterm_string = NULL; |
| 273 | dsp->xterm_string_pos = 0; |
| 274 | dsp->exit_on_string = false; |
| 275 | dsp->start_time = 0; |
| 276 | dsp->stop_time = 0; |
| 277 | |
| 278 | tok = lex_get_token(); |
| 279 | switch (tok) { |
| 280 | case T_S_Colon: return; |
| 281 | case T_L_Brace: break; |
| 282 | default: |
| 283 | unexpected: |
| 284 | lex_fatal("unexpected token"); |
| 285 | } |
| 286 | |
| 287 | while ( (tok = lex_get_token()) != T_R_Brace) { |
| 288 | if (tok != T_Token) |
| 289 | goto unexpected; |
| 290 | if (streq(lex.strp, "xterm")) { |
| 291 | if (dsp->term_strp != NULL) lex_fatal("xterm directive given more than once"); |
| 292 | tok = lex_get_token(); |
| 293 | switch(tok) { |
| 294 | case T_S_Colon: |
| 295 | dsp->term_strp = Xstrdup(DEFAULT_XTERM_STR); |
| 296 | break; |
| 297 | case T_String: |
| 298 | dsp->term_strp = Xstrdup(lex.strp); |
| 299 | lex_get(T_S_Colon); |
| 300 | break; |
| 301 | default: |
| 302 | goto unexpected; |
| 303 | } |
| 304 | dsp->uses_xterm = true; |
| 305 | } else |
| 306 | if (streq(lex.strp, "exit_string") || streq(lex.strp, "time_string")) { |
| 307 | if (dsp->xterm_string != NULL) |
| 308 | lex_fatal("exit_string or time_string already declared"); |
| 309 | time(&(dsp->start_time)); |
| 310 | if (streq(lex.strp, "exit_string")) |
| 311 | dsp->exit_on_string = true; |
| 312 | tok = lex_get_token(); |
| 313 | switch (tok) { |
| 314 | case T_S_Colon: |
| 315 | lex_fatal("exit/time_string needs a valid 'string'"); |
| 316 | case T_Number: |
| 317 | if ((int)lex.val > 127) { |
| 318 | lex_fatal("time_string must be string or Ascii value <127 "); |
| 319 | } |
| 320 | dsp->xterm_string = Xcalloc(2, char); |
| 321 | dsp->xterm_string[0] = (char)lex.val; |
| 322 | dsp->xterm_string[1] = '\0'; |
| 323 | lex_get(T_S_Colon); |
| 324 | break; |
| 325 | case T_String: |
| 326 | dsp->xterm_string = Xstrdup(lex.strp); |
| 327 | if (strlen(dsp->xterm_string) == 0) |
| 328 | lex_fatal("exit/time_string 'string' length is 0"); |
| 329 | lex_get(T_S_Colon); |
| 330 | break; |
| 331 | default: |
| 332 | goto unexpected; |
| 333 | } |
| 334 | } else |
| 335 | if (streq(lex.strp, "rxfifosz")) { |
| 336 | EXEC_WARNING(("dumbserial: rxfifosz is a depricated directive. Use rxfifo instead.")); |
| 337 | if (dsp->in.bufp != NULL) lex_fatal("fxfifosz or rxfifo already declared"); |
| 338 | lex_get(T_Number); |
| 339 | if ((int)lex.val < 0) { |
| 340 | lex_fatal("rxfifosz not positive"); |
| 341 | } |
| 342 | dsp->in.size = (int)lex.val; |
| 343 | lex_get(T_S_Colon); |
| 344 | ds_rxfifo_alloc(dsp); |
| 345 | } else |
| 346 | if (streq(lex.strp, "rxfifo")) { |
| 347 | if (dsp->in.bufp != NULL) lex_fatal("fxfifosz or rxfifo already declared"); |
| 348 | lex_get(T_Number); |
| 349 | if ((int)lex.val < 0) { |
| 350 | lex_fatal("rxfifosz not positive"); |
| 351 | } |
| 352 | dsp->in.size = (int)lex.val; |
| 353 | tok = lex_get_token(); |
| 354 | switch (tok) { |
| 355 | case T_L_Brace: |
| 356 | ds_parse_rxfifo_contents(dsp); |
| 357 | break; |
| 358 | case T_S_Colon: |
| 359 | break; |
| 360 | default: |
| 361 | goto unexpected; |
| 362 | } |
| 363 | } else |
| 364 | if (streq(lex.strp, "regshift")) { |
| 365 | tok = lex_get_token(); |
| 366 | switch (tok) { |
| 367 | case T_Number: |
| 368 | if ((int)lex.val < 0 || (int)lex.val>3) { |
| 369 | lex_fatal("regshift must be an integer from 0 to 3"); |
| 370 | } |
| 371 | dsp->reg_shift = (int)lex.val; |
| 372 | lex_get(T_S_Colon); |
| 373 | break; |
| 374 | default: |
| 375 | goto unexpected; |
| 376 | } |
| 377 | } else |
| 378 | if (streq(lex.strp, "reg")) { |
| 379 | int regnum; |
| 380 | lex_get(T_Number); |
| 381 | regnum = (int)lex.val; |
| 382 | lex_get(T_Number); |
| 383 | ds_reg_init(dsp, regnum, (int)lex.val); |
| 384 | lex_get(T_S_Colon); |
| 385 | } else |
| 386 | goto unexpected; |
| 387 | } |
| 388 | |
| 389 | if ((config_devp->addrp->range >> dsp->reg_shift) < 0x8) |
| 390 | fatal("dumbserial: dumbserial requires address space size of 0x%x", 0x8<<dsp->reg_shift); |
| 391 | |
| 392 | /* If xterm requested make sure we have an input buffer */ |
| 393 | |
| 394 | if (dsp->uses_xterm && dsp->in.size == -1) { |
| 395 | dsp->in.size = DEFAULT_RXFIFOSZ; |
| 396 | ds_rxfifo_alloc(dsp); |
| 397 | } |
| 398 | |
| 399 | /* If we have an input buffer but no xterm .. fail */ |
| 400 | if (dsp->in.bufp != NULL && !dsp->uses_xterm) lex_fatal("receive fifo has been specified, but no xterm directive for input delivery"); |
| 401 | } |
| 402 | |
| 403 | |
| 404 | |
| 405 | |
| 406 | /* |
| 407 | * Basic allocation of fifo space |
| 408 | */ |
| 409 | |
| 410 | static void ds_rxfifo_alloc(ds_state_t * dsp) |
| 411 | { |
| 412 | dsp->in.count = 0; |
| 413 | dsp->in.head = 0; |
| 414 | dsp->in.tail = 0; |
| 415 | dsp->in.bufp = Xcalloc(dsp->in.size, uint8_t); |
| 416 | } |
| 417 | |
| 418 | |
| 419 | |
| 420 | static void ds_parse_rxfifo_contents(ds_state_t * dsp) |
| 421 | { |
| 422 | lexer_tok_t tok; |
| 423 | |
| 424 | ds_rxfifo_alloc(dsp); |
| 425 | |
| 426 | while ( dsp->in.tail<dsp->in.size ) { |
| 427 | lex_get(T_Number); |
| 428 | |
| 429 | if (lex.val<0 || lex.val >255) lex_fatal("Illegal rxfifo contents"); |
| 430 | dsp->in.bufp[dsp->in.tail] = (int)lex.val; |
| 431 | dsp->in.tail++; |
| 432 | |
| 433 | tok = lex_get_token(); |
| 434 | switch (tok) { |
| 435 | case T_Comma: |
| 436 | break; |
| 437 | |
| 438 | case T_R_Brace: |
| 439 | goto done; |
| 440 | |
| 441 | default: |
| 442 | lex_fatal("Unexpected token parsing rxfifo contents"); |
| 443 | } |
| 444 | } |
| 445 | lex_fatal("Too many initialisers in rxfifo directive\n"); |
| 446 | |
| 447 | abort(); |
| 448 | |
| 449 | done:; |
| 450 | dsp->in.count = dsp->in.tail; |
| 451 | } |
| 452 | |
| 453 | |
| 454 | |
| 455 | |
| 456 | |
| 457 | /* |
| 458 | * Initialise the device after parsing is complete |
| 459 | */ |
| 460 | |
| 461 | void ds_init(config_dev_t * config_devp) |
| 462 | { |
| 463 | ds_state_t * dsp; |
| 464 | |
| 465 | dsp = (ds_state_t*)config_devp->devp; |
| 466 | |
| 467 | /* OK register values are now live */ |
| 468 | dsp->has_registers = true; |
| 469 | |
| 470 | /* if input fifo required it was created in parse phase */ |
| 471 | ASSERT(!dsp->uses_xterm || (dsp->uses_xterm && dsp->in.bufp!=NULL)); |
| 472 | |
| 473 | /* if TTY to be created then do so ... */ |
| 474 | if (dsp->uses_xterm) create_term(dsp); |
| 475 | |
| 476 | dsp->config_devp = config_devp; /* back pointer */ |
| 477 | } |
| 478 | |
| 479 | |
| 480 | |
| 481 | |
| 482 | |
| 483 | |
| 484 | |
| 485 | /* |
| 486 | * Dumb serial configuration dump |
| 487 | */ |
| 488 | |
| 489 | void ds_dump(config_dev_t * config_devp) |
| 490 | { |
| 491 | ds_state_t * dsp; |
| 492 | |
| 493 | dsp = (ds_state_t*)config_devp->devp; |
| 494 | |
| 495 | dumpinfo.indent++; |
| 496 | |
| 497 | pi("regshift %d ;\n", dsp->reg_shift); |
| 498 | if (dsp->uses_xterm) { |
| 499 | if (strcmp(dsp->term_strp, DEFAULT_XTERM_STR)==0) { |
| 500 | pi("xterm ;\n"); |
| 501 | } else { |
| 502 | pi("xterm \"%s\" ;\n", dsp->term_strp); |
| 503 | } |
| 504 | if (dsp->in.bufp != NULL) { |
| 505 | ASSERT( dsp->in.count <= dsp->in.size ); |
| 506 | if (dsp->in.count>0) { |
| 507 | int idx, cnt; |
| 508 | pi("rxfifo %d { 0x%02x", dsp->in.size, dsp->in.bufp[dsp->in.head]); |
| 509 | for (cnt=1, idx=dsp->in.head; cnt!=dsp->in.count; cnt++) { |
| 510 | idx++; |
| 511 | if (idx>=dsp->in.size) idx=0; |
| 512 | fprintf(dumpinfo.outp, ", 0x%02x", dsp->in.bufp[idx]); |
| 513 | } |
| 514 | fprintf(dumpinfo.outp, " }\n"); |
| 515 | } else { |
| 516 | pi("rxfifo %d ;\n", dsp->in.size); |
| 517 | } |
| 518 | } |
| 519 | } |
| 520 | |
| 521 | if (dsp->has_registers) { |
| 522 | /* Dump live register contents here ... */ |
| 523 | pi("reg 0x%x 0x%x ;\n", (int)DS_LineStatus, dsp->line_status); |
| 524 | pi("reg 0x%x 0x%x ;\n", (int)DS_LineCtrl, dsp->dlab ? DS_LCR_DLAB : 0); |
| 525 | pi("reg 0x%x 0x%x ;\n", (int)DS_Scratch, dsp->scratch); |
| 526 | } |
| 527 | |
| 528 | dumpinfo.indent--; |
| 529 | } |
| 530 | |
| 531 | |
| 532 | |
| 533 | |
| 534 | |
| 535 | static void ds_reg_init(ds_state_t * dsp, int regnum, int val) |
| 536 | { |
| 537 | switch (regnum) { |
| 538 | case DS_LineStatus: |
| 539 | dsp->line_status = val; |
| 540 | break; |
| 541 | case DS_LineCtrl: |
| 542 | dsp->dlab = (val & DS_LCR_DLAB)!=0 ? true : false; |
| 543 | break; |
| 544 | case DS_Scratch: |
| 545 | dsp->scratch = val; |
| 546 | break; |
| 547 | } |
| 548 | dsp->has_registers = true; |
| 549 | } |
| 550 | |
| 551 | |
| 552 | |
| 553 | |
| 554 | |
| 555 | /* |
| 556 | * Returns true if device access completes successfully |
| 557 | */ |
| 558 | |
| 559 | bool_t |
| 560 | ds_cpu_access(simcpu_t *sp, config_addr_t * cap, tpaddr_t offset, maccess_t op, uint64_t * regp) |
| 561 | { |
| 562 | ds_reg_t reg; |
| 563 | ds_state_t * dsp = (ds_state_t*)cap->config_devp->devp; |
| 564 | uint64_t val; |
| 565 | int size, acc_size; |
| 566 | char timebuf[32]; |
| 567 | char buf[2]; |
| 568 | |
| 569 | /* |
| 570 | * Unique identifier to tag guest/hv console output with legion time |
| 571 | * dump. This works when you have multiple dumbserial devices (one |
| 572 | * for hv, another for guest)as each line of console output on |
| 573 | * either serial device gets a unique tag. |
| 574 | */ |
| 575 | static int output_tag = 0; |
| 576 | |
| 577 | size = op & MA_Size_Mask; |
| 578 | op &= MA_Op_Mask; |
| 579 | |
| 580 | acc_size = (1<<dsp->reg_shift); |
| 581 | if ((offset & (((tpaddr_t)acc_size)-1))!=0) { |
| 582 | EXEC_WARNING(("dumbserial: Illegal device access - offset " |
| 583 | "(0x%llx) not %d byte aligned", |
| 584 | (uint64_t)offset, acc_size)); |
| 585 | return false; |
| 586 | } |
| 587 | |
| 588 | if (MA_Size8 != size) { |
| 589 | EXEC_WARNING(("dumbserial: Illegal device access - op size " |
| 590 | "(%d) not byte", size)); |
| 591 | return false; |
| 592 | } |
| 593 | |
| 594 | reg = offset >> dsp->reg_shift; |
| 595 | if (reg >= DS_NREGS) { |
| 596 | EXEC_WARNING(("dumbserial: Illegal device access - no reg at " |
| 597 | "offset 0x%llx", offset)); |
| 598 | return false; |
| 599 | } |
| 600 | if (dsp->dlab && (reg == DS_Input || reg == DS_IntEnable)) |
| 601 | reg += DS_DivLo; |
| 602 | |
| 603 | switch (op) { |
| 604 | case MA_Ld: |
| 605 | case MA_LdSigned: |
| 606 | val = 0; |
| 607 | switch (reg) { |
| 608 | case DS_Input: |
| 609 | pthread_mutex_lock(&dsp->tty_lock); |
| 610 | if (dsp->in.count == 0) { |
| 611 | val = 0; |
| 612 | dsp->line_status &= ~DS_LSTAT_DATA_READY; |
| 613 | } else { |
| 614 | val = dsp->in.bufp[dsp->in.head]; |
| 615 | dsp->in.head ++; |
| 616 | if (dsp->in.head >= dsp->in.size) dsp->in.head = 0; |
| 617 | dsp->in.count --; |
| 618 | if (dsp->in.count == 0) { |
| 619 | dsp->line_status &= ~DS_LSTAT_DATA_READY; |
| 620 | } |
| 621 | } |
| 622 | pthread_mutex_unlock(&dsp->tty_lock); |
| 623 | break; |
| 624 | |
| 625 | case DS_IntEnable: |
| 626 | IMPL_WARNING(("dumbserial: Interrupt enable not yet supported")); |
| 627 | break; |
| 628 | |
| 629 | case DS_IntIdent: |
| 630 | IMPL_WARNING(("dumbserial: FIFO control not yet supported")); |
| 631 | break; |
| 632 | |
| 633 | case DS_LineCtrl: |
| 634 | IMPL_WARNING(("dumbserial: Line control not yet supported")); |
| 635 | if (dsp->dlab) val |= DS_LCR_DLAB; |
| 636 | break; |
| 637 | |
| 638 | case DS_ModemCtrl: |
| 639 | IMPL_WARNING(("dumbserial: Modem control not yet supported")); |
| 640 | break; |
| 641 | |
| 642 | case DS_LineStatus: |
| 643 | /* FIXME - more to do here */ |
| 644 | /* TX always empty for the moment */ |
| 645 | pthread_mutex_lock(&dsp->tty_lock); |
| 646 | val = dsp->line_status; |
| 647 | pthread_mutex_unlock(&dsp->tty_lock); |
| 648 | break; |
| 649 | |
| 650 | case DS_ModemStatus: |
| 651 | IMPL_WARNING(("dumbserial: Modem status not yet supported")); |
| 652 | break; |
| 653 | |
| 654 | case DS_Scratch: |
| 655 | val = dsp->scratch; |
| 656 | break; |
| 657 | |
| 658 | case DS_DivLo: |
| 659 | IMPL_WARNING(("dumbserial: Divisor (low) not yet supported")); |
| 660 | break; |
| 661 | |
| 662 | case DS_DivHi: |
| 663 | IMPL_WARNING(("dumbserial: Divisor (high) not yet supported")); |
| 664 | break; |
| 665 | |
| 666 | default: |
| 667 | EXEC_WARNING(("dumbserial: illegal register accessed - offset 0x%x", offset)); |
| 668 | return false; |
| 669 | } |
| 670 | |
| 671 | if (MA_LdSigned == op) val = (uint64_t)(sint64_t)(sint8_t)val; |
| 672 | *regp = val; |
| 673 | break; |
| 674 | |
| 675 | case MA_St: |
| 676 | |
| 677 | val = *regp & 0xff; /* byte acces only */ |
| 678 | |
| 679 | switch (reg) { |
| 680 | case DS_Output: |
| 681 | if (dsp->xterm_string != NULL) { |
| 682 | |
| 683 | /* |
| 684 | * Catch the case where it's just a single char (or an ascii char) |
| 685 | * that we are looking for |
| 686 | */ |
| 687 | if (strlen(dsp->xterm_string) == 1) { |
| 688 | if ((int)dsp->xterm_string[0] == val) { |
| 689 | /* print instn counts on legion console */ |
| 690 | time(&(dsp->stop_time)); |
| 691 | printf("(T-%d) (%d sec) ->", output_tag, |
| 692 | ((dsp->stop_time) - (dsp->start_time))); |
| 693 | ds_dump_instruction_counts(dsp); |
| 694 | |
| 695 | /* print time tag on guest console */ |
| 696 | sprintf(timebuf, "->(T-%d)", output_tag++); |
| 697 | ds_output(dsp, timebuf); |
| 698 | |
| 699 | if (dsp->exit_on_string == true) |
| 700 | fatal("exit_string enabled exit\n"); |
| 701 | |
| 702 | goto write_output; |
| 703 | } |
| 704 | } |
| 705 | |
| 706 | if (val == (dsp->xterm_string[dsp->xterm_string_pos])) |
| 707 | dsp->xterm_string_pos += 1; |
| 708 | else |
| 709 | if (val == dsp->xterm_string[0]) |
| 710 | dsp->xterm_string_pos = 1; |
| 711 | else |
| 712 | dsp->xterm_string_pos = 0; |
| 713 | if (dsp->xterm_string[dsp->xterm_string_pos] == '\0') { |
| 714 | time(&(dsp->stop_time)); |
| 715 | printf("\nTime from sim start to '%s' = %d seconds\n",\ |
| 716 | dsp->xterm_string, (dsp->stop_time) - (dsp->start_time)); |
| 717 | ds_dump_instruction_counts(dsp); |
| 718 | if (dsp->exit_on_string == true) |
| 719 | fatal("exit_string enabled exit\n"); |
| 720 | } |
| 721 | } |
| 722 | write_output: |
| 723 | buf[0] = val; |
| 724 | buf[1] = '\0'; |
| 725 | ds_output(dsp, buf); |
| 726 | |
| 727 | break; |
| 728 | |
| 729 | case DS_IntEnable: |
| 730 | IMPL_WARNING(("dumbserial: Interrupt enable not yet supported")); |
| 731 | break; |
| 732 | |
| 733 | case DS_FIFOCtrl: |
| 734 | IMPL_WARNING(("dumbserial: FIFO control not yet supported")); |
| 735 | break; |
| 736 | |
| 737 | case DS_LineCtrl: |
| 738 | IMPL_WARNING(("dumbserial: Line control not yet supported")); |
| 739 | dsp->dlab = ((val & DS_LCR_DLAB) != 0); |
| 740 | break; |
| 741 | |
| 742 | case DS_ModemCtrl: |
| 743 | IMPL_WARNING(("dumbserial: Modem control not yet supported")); |
| 744 | break; |
| 745 | |
| 746 | case DS_LineStatus: |
| 747 | /* FIXME - more to do here */ |
| 748 | /* TX always empty for the moment */ |
| 749 | pthread_mutex_lock(&dsp->tty_lock); |
| 750 | dsp->line_status = (val & 0xff) | DS_LSTAT_TX_EMPTY | DS_LSTAT_TX_HOLD; |
| 751 | pthread_mutex_unlock(&dsp->tty_lock); |
| 752 | break; |
| 753 | |
| 754 | case DS_ModemStatus: |
| 755 | IMPL_WARNING(("dumbserial: Modem status not yet supported")); |
| 756 | break; |
| 757 | |
| 758 | case DS_Scratch: |
| 759 | dsp->scratch = val; |
| 760 | break; |
| 761 | |
| 762 | case DS_DivLo: |
| 763 | IMPL_WARNING(("dumbserial: Divisor (low) not yet supported")); |
| 764 | break; |
| 765 | |
| 766 | case DS_DivHi: |
| 767 | IMPL_WARNING(("dumbserial: Divisor (high) not yet supported")); |
| 768 | break; |
| 769 | |
| 770 | default: |
| 771 | EXEC_WARNING(("dumbserial: illegal register accessed - offset 0x%x", offset)); |
| 772 | return false; |
| 773 | } |
| 774 | break; |
| 775 | |
| 776 | default: |
| 777 | fatal("ds_cpu_access: Internal error unexpected op=0x%x\n", op); |
| 778 | } |
| 779 | |
| 780 | return true; |
| 781 | } |
| 782 | |
| 783 | |
| 784 | |
| 785 | |
| 786 | |
| 787 | |
| 788 | |
| 789 | /* |
| 790 | * Support routines ... |
| 791 | */ |
| 792 | |
| 793 | |
| 794 | |
| 795 | /* |
| 796 | * Create term ... during initialisation, create the thread to run the |
| 797 | * terminal. |
| 798 | */ |
| 799 | |
| 800 | void create_term(ds_state_t * dsp) |
| 801 | { |
| 802 | dsp->tty_skt = -1; |
| 803 | dsp->tty_attached = false; |
| 804 | /* dsp->tilde_state = TE_Normal; */ |
| 805 | dsp->tilde_state = TE_SawCR; /* shouldn't have to CR to use ~ as first input char */ |
| 806 | pthread_mutex_init(&dsp->tty_lock, NULL); |
| 807 | pthread_cond_init(&dsp->tty_cv, NULL); |
| 808 | |
| 809 | /* |
| 810 | * OK first we create the management thread for this console's input |
| 811 | * - then wait for it to tell us that it is happily up and running |
| 812 | * before we return to the simulator to continue initialisation. |
| 813 | */ |
| 814 | |
| 815 | pthread_mutex_lock(&dsp->tty_lock); |
| 816 | |
| 817 | create_thread(ds_input_thr, (void*)dsp, &(dsp->pthread_id) ); |
| 818 | |
| 819 | /* should probably timeout here incase child doesn't start */ |
| 820 | while (!dsp->tty_attached) pthread_cond_wait(&dsp->tty_cv, &dsp->tty_lock); |
| 821 | pthread_mutex_unlock(&dsp->tty_lock); |
| 822 | |
| 823 | #if 0 /* { */ |
| 824 | /* |
| 825 | * Dummy test code ... |
| 826 | */ |
| 827 | do { |
| 828 | static int count = 0; |
| 829 | |
| 830 | pthread_mutex_lock(&dsp->tty_lock); |
| 831 | if (dsp->tty_attached) { |
| 832 | struct pollfd fds; |
| 833 | extern void tcparams(int fd); |
| 834 | char temp[MAXPATHLEN]; |
| 835 | |
| 836 | fds.fd = dsp->tty_skt; |
| 837 | fds.events = POLLOUT; |
| 838 | fds.revents = 0; |
| 839 | res = poll(&fds, 1, -1); |
| 840 | |
| 841 | printf("out: "); |
| 842 | ptest(POLLIN); ptest(POLLRDNORM); ptest(POLLRDBAND); ptest(POLLOUT); ptest(POLLWRNORM); ptest(POLLWRBAND); ptest(POLLERR); ptest(POLLHUP); ptest(POLLNVAL); |
| 843 | printf("\n"); |
| 844 | fflush(stdout); |
| 845 | |
| 846 | sprintf(temp, "test connect %d\n\r", count); |
| 847 | write(dsp->tty_skt, temp, strlen(temp)); |
| 848 | } |
| 849 | pthread_mutex_unlock(&dsp->tty_lock); |
| 850 | |
| 851 | count ++; |
| 852 | sleep(1); |
| 853 | } while (1); |
| 854 | #endif /* } */ |
| 855 | } |
| 856 | |
| 857 | |
| 858 | |
| 859 | |
| 860 | |
| 861 | /* |
| 862 | * Thread to manage external connections to this serial port. |
| 863 | * |
| 864 | * By default it creates an xterm with a telnet connection to |
| 865 | * the appropriate i/o port. |
| 866 | */ |
| 867 | |
| 868 | void * ds_input_thr(void * ptr) |
| 869 | { |
| 870 | ds_state_t * dsp; |
| 871 | uint8_t buf[1024]; |
| 872 | #define MAXHOSTNAME 256 |
| 873 | char myhostname[MAXHOSTNAME]; |
| 874 | int ds_sv_skt; |
| 875 | struct hostent * hp; |
| 876 | int on, length; |
| 877 | struct sockaddr_in ds_server, from; |
| 878 | char * froms; |
| 879 | |
| 880 | dsp = (ds_state_t*)ptr; |
| 881 | |
| 882 | /* |
| 883 | * Create our socket to listen to |
| 884 | */ |
| 885 | |
| 886 | ds_sv_skt = socket(AF_INET, SOCK_STREAM, 0); |
| 887 | if (ds_sv_skt < 0) fatal("opening stream socket"); |
| 888 | |
| 889 | /* enable the reuse of this socket if this process dies */ |
| 890 | if (setsockopt(ds_sv_skt, SOL_SOCKET, SO_REUSEADDR, (uint8_t*)&on, sizeof(on))<0) |
| 891 | fatal("turning on REUSEADDR"); |
| 892 | |
| 893 | /* bind it */ |
| 894 | retry:; |
| 895 | ds_server.sin_family = AF_INET; |
| 896 | ds_server.sin_addr.s_addr = INADDR_ANY; |
| 897 | ds_server.sin_port = htons(0); /* bind to an OS selected local port */ |
| 898 | |
| 899 | if (bind(ds_sv_skt, (struct sockaddr *)&ds_server, sizeof(ds_server)) < 0) { |
| 900 | switch (errno) { |
| 901 | case EAGAIN: |
| 902 | goto retry; |
| 903 | |
| 904 | case EADDRINUSE: |
| 905 | fatal("Port is already in use\n"); |
| 906 | default: |
| 907 | fatal("binding tcp stream socket"); |
| 908 | } |
| 909 | } |
| 910 | |
| 911 | length = sizeof(ds_server); |
| 912 | if (getsockname(ds_sv_skt, (struct sockaddr *) &ds_server, &length)==-1) |
| 913 | fatal("getting socket name"); |
| 914 | |
| 915 | listen(ds_sv_skt, 1); |
| 916 | |
| 917 | |
| 918 | /* |
| 919 | * Create the client xterm etc |
| 920 | */ |
| 921 | |
| 922 | gethostname(myhostname, MAXHOSTNAME); |
| 923 | sprintf((char*)buf, dsp->term_strp, myhostname, ntohs(ds_server.sin_port)); |
| 924 | |
| 925 | system((char*)buf); |
| 926 | |
| 927 | /* |
| 928 | * OK main loop for processing connections and data traffic |
| 929 | */ |
| 930 | |
| 931 | |
| 932 | do { |
| 933 | if (!dsp->tty_attached) { |
| 934 | PRINTF(("\ndumbserial: Waiting for connection to : %s:%d\n", |
| 935 | myhostname, ntohs(ds_server.sin_port))); |
| 936 | |
| 937 | length = sizeof(from); |
| 938 | dsp->tty_skt = accept(ds_sv_skt, (struct sockaddr *)&from, (int*)&length); |
| 939 | |
| 940 | hp = gethostbyaddr((char *)&from.sin_addr, 4, AF_INET); |
| 941 | if (hp == (struct hostent *)0) { |
| 942 | froms = inet_ntoa(from.sin_addr); |
| 943 | fprintf(stderr,"cant resolve hostname for %s\n", froms); |
| 944 | } else { |
| 945 | froms = hp->h_name; |
| 946 | } |
| 947 | PRINTF(("dumbserial: Accepted connection on %s:%d from %s:%d\n", |
| 948 | myhostname, ntohs(ds_server.sin_port), |
| 949 | froms, ntohs(from.sin_port))); |
| 950 | |
| 951 | pthread_mutex_lock(&dsp->tty_lock); |
| 952 | dsp->tty_attached = true; |
| 953 | pthread_mutex_unlock(&dsp->tty_lock); |
| 954 | pthread_cond_signal(&dsp->tty_cv); |
| 955 | } else { |
| 956 | int res; |
| 957 | struct pollfd fds; |
| 958 | |
| 959 | #define POLL_TIMEOUT -1 /* wait forever ? FIXME ? */ |
| 960 | fds.fd = dsp->tty_skt; |
| 961 | fds.events = POLLIN|POLLPRI; |
| 962 | #if HOST_OS_SOLARIS9 /* { */ |
| 963 | fds.events |= POLLRDNORM|POLLRDBAND; |
| 964 | #endif /* } */ |
| 965 | fds.revents = 0; |
| 966 | |
| 967 | res = poll(&fds, 1, POLL_TIMEOUT); |
| 968 | |
| 969 | DBGX( printf("tty: "); |
| 970 | ptest(POLLIN); |
| 971 | ptest(POLLOUT); |
| 972 | ptest(POLLERR); |
| 973 | ptest(POLLHUP); |
| 974 | ptest(POLLNVAL); |
| 975 | printf("\n"); |
| 976 | fflush(stdout); ); |
| 977 | |
| 978 | #if HOST_OS_SOLARIS9 /* { */ |
| 979 | DBGX( printf("\t"); |
| 980 | ptest(POLLRDNORM); |
| 981 | ptest(POLLRDBAND); |
| 982 | ptest(POLLWRNORM); |
| 983 | ptest(POLLWRBAND); |
| 984 | printf("\n"); |
| 985 | fflush(stdout); ); |
| 986 | #endif /* } */ |
| 987 | |
| 988 | if (fds.revents & POLLIN) { |
| 989 | res = read(dsp->tty_skt, buf, sizeof (buf)); |
| 990 | if (res == 0) { |
| 991 | /* a read of 0 bytes is an EOF */ |
| 992 | pthread_mutex_lock(&dsp->tty_lock); |
| 993 | dsp->tty_attached = false; |
| 994 | pthread_mutex_unlock(&dsp->tty_lock); |
| 995 | pthread_cond_signal(&dsp->tty_cv); |
| 996 | close(dsp->tty_skt); |
| 997 | SANITY(dsp->tty_skt = -1;); |
| 998 | } else if (res<0) { |
| 999 | perror("read"); |
| 1000 | } else { |
| 1001 | DBGX( dump_buf("input bytes:", buf, res); ); |
| 1002 | |
| 1003 | ds_insert_bytes(dsp, buf, res); |
| 1004 | } |
| 1005 | } |
| 1006 | } |
| 1007 | } while (1); |
| 1008 | |
| 1009 | /*NOTREACHED*/ |
| 1010 | } |
| 1011 | |
| 1012 | |
| 1013 | |
| 1014 | |
| 1015 | |
| 1016 | |
| 1017 | /* emits string and newline */ |
| 1018 | void |
| 1019 | ds_output(ds_state_t *dsp, char *str) |
| 1020 | { |
| 1021 | if (dsp->uses_xterm) { |
| 1022 | if (dsp->tty_attached) { |
| 1023 | (void) write(dsp->tty_skt, str, strlen(str)); |
| 1024 | } |
| 1025 | /* drop char if tty is not attached */ |
| 1026 | } else { |
| 1027 | |
| 1028 | fputs(str, stdout); |
| 1029 | fflush(stdout); |
| 1030 | } |
| 1031 | } |
| 1032 | |
| 1033 | |
| 1034 | |
| 1035 | |
| 1036 | |
| 1037 | |
| 1038 | |
| 1039 | /* FIXME: This compile option |
| 1040 | * enables the console to force an external chip reset of any procs |
| 1041 | * attached to the same domain. |
| 1042 | * This is somewhat of a hack, and should be handled else where ... |
| 1043 | * ... which is why it is not a device option in the config file |
| 1044 | */ |
| 1045 | |
| 1046 | #if CONSOLE_RESET /* { */ |
| 1047 | void |
| 1048 | ds_domain_reset(ds_state_t * dsp) |
| 1049 | { |
| 1050 | domain_t * dp; |
| 1051 | int i; |
| 1052 | |
| 1053 | /* |
| 1054 | * FIXME: this is a hack ... |
| 1055 | * walk the procs in my domain, and reset them all |
| 1056 | */ |
| 1057 | |
| 1058 | dp = dsp->config_devp->domainp; |
| 1059 | |
| 1060 | for (i=0; i<dp->procs.count; i++) { |
| 1061 | config_proc_t * cp; |
| 1062 | |
| 1063 | cp = LIST_ENTRY(dp->procs, i); |
| 1064 | |
| 1065 | cp->proc_typep->ext_signal(cp, ES_XIR, NULL); |
| 1066 | } |
| 1067 | } |
| 1068 | #endif /* } */ |
| 1069 | |
| 1070 | |
| 1071 | |
| 1072 | |
| 1073 | |
| 1074 | |
| 1075 | |
| 1076 | |
| 1077 | |
| 1078 | |
| 1079 | |
| 1080 | void |
| 1081 | ds_insert_bytes(ds_state_t *dsp, uint8_t *bufp, int len) |
| 1082 | { |
| 1083 | int i; |
| 1084 | |
| 1085 | pthread_mutex_lock(&dsp->tty_lock); |
| 1086 | |
| 1087 | /* |
| 1088 | * process all characters in buffer, don't check for overrun |
| 1089 | * right away |
| 1090 | */ |
| 1091 | for (i = 0; i < len; i++) { |
| 1092 | int c = bufp[i]; |
| 1093 | |
| 1094 | DBGX( printf("State %d : Char typed 0x%x [%c]\n", (int)dsp->tilde_state, c, c>=' ' && c<127 ? c : '.'); fflush(stdout); ); |
| 1095 | |
| 1096 | switch (dsp->tilde_state) { |
| 1097 | case TE_Normal: /* waiting for CR */ |
| 1098 | if (c == '\n') |
| 1099 | dsp->tilde_state = TE_SawCR; |
| 1100 | break; |
| 1101 | |
| 1102 | case TE_SawCR: /* waiting for tilde */ |
| 1103 | if (c == '~') { |
| 1104 | dsp->tilde_state = TE_SawTilde; |
| 1105 | goto skip; |
| 1106 | } |
| 1107 | if (c != '\n') dsp->tilde_state = TE_Normal; |
| 1108 | break; |
| 1109 | |
| 1110 | case TE_SawTilde: /* tilde command */ |
| 1111 | dsp->tilde_state = TE_SawCR; |
| 1112 | /* Flush logging output. */ |
| 1113 | log_flush(); |
| 1114 | switch (c) { |
| 1115 | case '~': /* emit single tilde */ |
| 1116 | dsp->tilde_state = TE_Normal; |
| 1117 | break; |
| 1118 | |
| 1119 | case '#': /* BREAK */ |
| 1120 | dsp->line_status |= DS_LSTAT_BREAK; |
| 1121 | ds_output(dsp, "BREAK\n"); |
| 1122 | /* allow another ~ cmd without CR */ |
| 1123 | goto skip; |
| 1124 | |
| 1125 | case '?': /* help */ |
| 1126 | ds_output(dsp, "dumb_serial tilde escapes:\n"); |
| 1127 | ds_output(dsp, "\t# BREAK\n"); |
| 1128 | ds_output(dsp, "\t~ generate tilde\n"); |
| 1129 | ds_output(dsp, "\tz exit simulator\n"); |
| 1130 | #if ERROR_TRAP_GEN /* { */ |
| 1131 | ds_output(dsp, "\ter reload (append) error config file into running legion\n"); |
| 1132 | ds_output(dsp, "\t error config filename specified in " \ |
| 1133 | "legion config \n\t file used at startup using " \ |
| 1134 | "error_reload_file_name directive\n"); |
| 1135 | ds_output(dsp, "\ted dump current error_event and error_asi lists\n"); |
| 1136 | ds_output(dsp, "\tes dump supported (built-in) error types\n"); |
| 1137 | #endif /* } */ |
| 1138 | #if CONSOLE_RESET /* { */ |
| 1139 | ds_output(dsp, "\tx external reset for CPUs in same domain\n"); |
| 1140 | #endif /* } */ |
| 1141 | #if !NDEBUG /* { */ |
| 1142 | ds_output(dsp, "\tj generate JBus interrupt\n"); |
| 1143 | ds_output(dsp, "\ts generate SSI interrupt\n"); |
| 1144 | ds_output(dsp, "\tp generate PCIe interrupt\n"); |
| 1145 | ds_output(dsp, "\tr generate FPGA RX intr\n"); |
| 1146 | ds_output(dsp, "\tt generate FPGA TX intr\n"); |
| 1147 | #endif /* } */ |
| 1148 | #if !NDEBUG /* { */ |
| 1149 | ds_output(dsp, "\ti dump I-TLB contents for CPUs in same domain\n"); |
| 1150 | ds_output(dsp, "\td dump D-TLB contents for CPUs in same domain\n"); |
| 1151 | #endif /* } */ |
| 1152 | #if !NDEBUG /* { */ |
| 1153 | ds_output(dsp, "\tb toggle the debug output enable bits\n"); |
| 1154 | #endif /* } */ |
| 1155 | ds_output(dsp, "\tl dump data for the debug_hook"\ |
| 1156 | " function you are using\n\t\t(coverage, tracing, "\ |
| 1157 | "debug_log, lockstep)\n"); |
| 1158 | ds_output(dsp, "\tn dump the current and delta instruction count for each CPU\n"); |
| 1159 | ds_output(dsp, "\t? this message\n"); |
| 1160 | /* allow another ~ cmd without CR */ |
| 1161 | goto skip; |
| 1162 | |
| 1163 | case 'z': |
| 1164 | fatal("dumbserial requested exit"); |
| 1165 | goto skip; |
| 1166 | |
| 1167 | #if CONSOLE_RESET /* { */ |
| 1168 | case 'x': /* Hack to enable extern chip reset easily */ |
| 1169 | ds_output(dsp, "Resetting cpu(s) in domain\n"); |
| 1170 | ds_domain_reset(dsp); |
| 1171 | goto skip; /* swallow 'x' character after sending reset */ |
| 1172 | #endif /* } */ |
| 1173 | |
| 1174 | #if !NDEBUG /* { */ |
| 1175 | case 'j': { |
| 1176 | config_proc_t * cpp; |
| 1177 | jbus_mondo_t mondo; |
| 1178 | |
| 1179 | ds_output(dsp, "JBUS intr\n"); |
| 1180 | |
| 1181 | mondo.adr.source = 0; |
| 1182 | mondo.adr.target = 0; |
| 1183 | mondo.adr.type = 0x14; /* INT */ |
| 1184 | mondo.data0 = 0xaabbccdd; |
| 1185 | mondo.data1 = 0x11223344; |
| 1186 | |
| 1187 | cpp = LIST_ENTRY( |
| 1188 | dsp->config_devp->domainp->procs, 0); |
| 1189 | cpp->proc_typep->ext_signal(cpp, ES_JBUS, &mondo); |
| 1190 | goto skip; |
| 1191 | } |
| 1192 | |
| 1193 | case 'p': { |
| 1194 | config_proc_t * cpp; |
| 1195 | pcie_mondo_t *mondo = (pcie_mondo_t *)Xcalloc(1, pcie_mondo_t); |
| 1196 | |
| 1197 | ds_output(dsp, "PCIE mondo intr\n"); |
| 1198 | |
| 1199 | mondo->thread_id = 0; |
| 1200 | mondo->data[0] = 0xaabbccdd; |
| 1201 | mondo->data[1] = 0x11223344; |
| 1202 | |
| 1203 | cpp = LIST_ENTRY( |
| 1204 | dsp->config_devp->domainp->procs, 0); |
| 1205 | cpp->proc_typep->ext_signal(cpp, ES_PCIE, mondo); |
| 1206 | goto skip; |
| 1207 | } |
| 1208 | |
| 1209 | case 's': { |
| 1210 | config_proc_t * cpp; |
| 1211 | ds_output(dsp, "SSI intr\n"); |
| 1212 | |
| 1213 | cpp = LIST_ENTRY( |
| 1214 | dsp->config_devp->domainp->procs, 0); |
| 1215 | cpp->proc_typep->ext_signal(cpp, ES_SSI, NULL); |
| 1216 | goto skip; |
| 1217 | } |
| 1218 | |
| 1219 | case 'r': { |
| 1220 | extern void (*fpga_intr)(int a); |
| 1221 | |
| 1222 | if (fpga_intr != NULL) { |
| 1223 | config_proc_t * cpp; |
| 1224 | ds_output(dsp, "fpga RX intr\n"); |
| 1225 | |
| 1226 | cpp = LIST_ENTRY( |
| 1227 | dsp->config_devp->domainp->procs, |
| 1228 | 0); |
| 1229 | cpp->proc_typep->ext_signal(cpp, |
| 1230 | ES_SSI, NULL); |
| 1231 | (fpga_intr)(1); |
| 1232 | } else { |
| 1233 | ds_output(dsp, |
| 1234 | "no fpga intr attached\n"); |
| 1235 | } |
| 1236 | goto skip; |
| 1237 | } |
| 1238 | case 't': { |
| 1239 | extern void (*fpga_intr)(int a); |
| 1240 | if (fpga_intr != NULL) { |
| 1241 | config_proc_t * cpp; |
| 1242 | ds_output(dsp, "fpga TX intr\n"); |
| 1243 | cpp = LIST_ENTRY( |
| 1244 | dsp->config_devp->domainp->procs, |
| 1245 | 0); |
| 1246 | cpp->proc_typep->ext_signal(cpp, |
| 1247 | ES_SSI, NULL); |
| 1248 | (fpga_intr)(2); |
| 1249 | } else { |
| 1250 | ds_output(dsp, |
| 1251 | "no fpga intr attached\n"); |
| 1252 | } |
| 1253 | goto skip; |
| 1254 | } |
| 1255 | #if INTERNAL_BUILD /* { */ |
| 1256 | case '%': { |
| 1257 | printf("Initiating Simulator state save\n"); |
| 1258 | ds_save_state(dsp); |
| 1259 | goto skip; |
| 1260 | } |
| 1261 | #endif /* INTERNAL_BUILD } */ |
| 1262 | case 'i': { |
| 1263 | ds_dump_tlb(dsp, false); |
| 1264 | goto skip; |
| 1265 | } |
| 1266 | case 'd': { |
| 1267 | ds_dump_tlb(dsp, true); |
| 1268 | goto skip; |
| 1269 | } |
| 1270 | case 'b': { |
| 1271 | simcore_update_debug_bits(debug_bits ^ debug_bits_xor); |
| 1272 | log_flush(); |
| 1273 | goto skip; |
| 1274 | } |
| 1275 | #endif /* } */ |
| 1276 | case 'n': |
| 1277 | ds_dump_instruction_counts(dsp); |
| 1278 | goto skip; |
| 1279 | case 'l': |
| 1280 | ds_debug_hook_dump(dsp); |
| 1281 | goto skip; |
| 1282 | |
| 1283 | #if ERROR_TRAP_GEN /* { */ |
| 1284 | case 'e': |
| 1285 | dsp->tilde_state = TE_SawTildeE; |
| 1286 | goto skip; |
| 1287 | #endif /* ERROR_TRAP_GEN } */ |
| 1288 | |
| 1289 | default: /* eat current char */ |
| 1290 | /* |
| 1291 | * FIXME could emit ~ and current char |
| 1292 | * but it's harder |
| 1293 | */ |
| 1294 | ds_output(dsp, "~? for tilde help\n"); |
| 1295 | goto skip; |
| 1296 | } |
| 1297 | break; |
| 1298 | #if ERROR_TRAP_GEN /* { */ |
| 1299 | case TE_SawTildeE: /* tilde e command */ |
| 1300 | dsp->tilde_state = TE_SawCR; |
| 1301 | switch (c) { |
| 1302 | case 'r': |
| 1303 | ds_load_error_file(dsp); |
| 1304 | goto skip; |
| 1305 | case 'd': |
| 1306 | ds_dump_error_active(dsp); |
| 1307 | goto skip; |
| 1308 | case 's': |
| 1309 | ds_dump_error_supported(dsp); |
| 1310 | goto skip; |
| 1311 | } |
| 1312 | break; |
| 1313 | #endif /* } ERROR_TRAP_GEN */ |
| 1314 | } |
| 1315 | if (dsp->in.count < dsp->in.size) { |
| 1316 | dsp->in.bufp[dsp->in.tail] = bufp[i]; |
| 1317 | dsp->in.tail++; |
| 1318 | if (dsp->in.tail >= dsp->in.size) |
| 1319 | dsp->in.tail = 0; |
| 1320 | dsp->in.count++; |
| 1321 | dsp->line_status |= DS_LSTAT_DATA_READY; |
| 1322 | } else { |
| 1323 | if (!(dsp->line_status & DS_LSTAT_OVERRUN)) |
| 1324 | warning("dumbserial: buffer overrun"); |
| 1325 | dsp->line_status |= DS_LSTAT_OVERRUN; |
| 1326 | } |
| 1327 | skip:; |
| 1328 | } |
| 1329 | pthread_mutex_unlock(&dsp->tty_lock); |
| 1330 | } |
| 1331 | |
| 1332 | |
| 1333 | |
| 1334 | |
| 1335 | |
| 1336 | void dump_buf(char * strp, uint8_t * bufp, int len) |
| 1337 | { |
| 1338 | int i; |
| 1339 | |
| 1340 | printf("%s read %d bytes: ", strp, len); |
| 1341 | for(i=0; i<len; i++) printf("0x%02x [%c]",bufp[i],bufp[i]>=32 && bufp[i]<127 ? bufp[i] : '.'); |
| 1342 | printf("\n"); |
| 1343 | } |
| 1344 | |
| 1345 | |
| 1346 | static void ds_save_state(ds_state_t * dsp) |
| 1347 | { |
| 1348 | domain_t * dp = dsp->config_devp->domainp; |
| 1349 | int i; |
| 1350 | |
| 1351 | for (i=0; i<dp->procs.count; i++) { |
| 1352 | config_proc_t * cp; |
| 1353 | |
| 1354 | cp = LIST_ENTRY(dp->procs, i); |
| 1355 | |
| 1356 | cp->proc_typep->ext_signal(cp, ES_LEGION_SAVE_STATE, NULL); |
| 1357 | } |
| 1358 | #if !NDEBUG /* { */ |
| 1359 | log_flush(); |
| 1360 | #endif /* } */ |
| 1361 | } |
| 1362 | |
| 1363 | static void ds_dump_instruction_counts(ds_state_t * dsp) |
| 1364 | { |
| 1365 | domain_t * dp = dsp->config_devp->domainp; |
| 1366 | int i; |
| 1367 | |
| 1368 | for (i=0; i<dp->procs.count; i++) { |
| 1369 | config_proc_t * cp; |
| 1370 | |
| 1371 | cp = LIST_ENTRY(dp->procs, i); |
| 1372 | |
| 1373 | cp->proc_typep->instn_cnt_dump(cp); |
| 1374 | } |
| 1375 | log_flush(); |
| 1376 | } |
| 1377 | |
| 1378 | |
| 1379 | static void ds_debug_hook_dump(ds_state_t * dsp) |
| 1380 | { |
| 1381 | domain_t *dp = dsp->config_devp->domainp; |
| 1382 | int i; |
| 1383 | void (*debug_hook_dumpp)(void); |
| 1384 | bool_t did_dump = false; |
| 1385 | |
| 1386 | |
| 1387 | log_flush(); |
| 1388 | fflush(stdout); |
| 1389 | for (i=0; i<dp->procs.count; i++) { |
| 1390 | config_proc_t * cp; |
| 1391 | |
| 1392 | cp = LIST_ENTRY(dp->procs, i); |
| 1393 | |
| 1394 | debug_hook_dumpp = |
| 1395 | (void(*)(void))cp->proc_typep->debug_hook_dumpp; |
| 1396 | if (debug_hook_dumpp != NULL) { |
| 1397 | if (!did_dump) { |
| 1398 | printf("\n"); |
| 1399 | did_dump = true; |
| 1400 | } |
| 1401 | debug_hook_dumpp(); |
| 1402 | } |
| 1403 | } |
| 1404 | if (did_dump) |
| 1405 | printf("\n"); |
| 1406 | else |
| 1407 | printf("\nNo debug_hook dump configured.\n"); |
| 1408 | log_flush(); |
| 1409 | fflush(stdout); |
| 1410 | } |
| 1411 | |
| 1412 | |
| 1413 | #if !NDEBUG /* { */ |
| 1414 | /* Nasty hack for TLB debugging - to go away FIXME */ |
| 1415 | |
| 1416 | /* First find all the cpus, then dump their TLBs ... */ |
| 1417 | static void ds_dump_tlb(ds_state_t * dsp, bool_t is_dtlb) |
| 1418 | { |
| 1419 | domain_t * dp = dsp->config_devp->domainp; |
| 1420 | int i; |
| 1421 | |
| 1422 | /* |
| 1423 | * FIXME: this is a hack ... |
| 1424 | * walk the procs in my domain, and dump the tlb contents |
| 1425 | */ |
| 1426 | for (i=0; i<dp->procs.count; i++) { |
| 1427 | config_proc_t * cp; |
| 1428 | |
| 1429 | cp = LIST_ENTRY(dp->procs, i); |
| 1430 | |
| 1431 | cp->proc_typep->tlb_dump(cp, is_dtlb); |
| 1432 | } |
| 1433 | log_flush(); |
| 1434 | } |
| 1435 | |
| 1436 | #endif /* } */ |
| 1437 | |
| 1438 | #if ERROR_TRAP_GEN /* { */ |
| 1439 | static void ds_load_error_file(ds_state_t * dsp) |
| 1440 | { |
| 1441 | |
| 1442 | domain_t * dp = dsp->config_devp->domainp; |
| 1443 | config_proc_t * cp; |
| 1444 | |
| 1445 | cp = LIST_ENTRY(dp->procs, 0); |
| 1446 | |
| 1447 | cp->proc_typep->load_error_file(cp); |
| 1448 | |
| 1449 | log_flush(); |
| 1450 | } |
| 1451 | |
| 1452 | static void ds_dump_error_active(ds_state_t * dsp) |
| 1453 | { |
| 1454 | domain_t * dp = dsp->config_devp->domainp; |
| 1455 | config_proc_t * cp; |
| 1456 | |
| 1457 | cp = LIST_ENTRY(dp->procs, 0); |
| 1458 | |
| 1459 | cp->proc_typep->dump_error_active(cp); |
| 1460 | |
| 1461 | log_flush(); |
| 1462 | } |
| 1463 | |
| 1464 | static void ds_dump_error_supported(ds_state_t * dsp) |
| 1465 | { |
| 1466 | domain_t * dp = dsp->config_devp->domainp; |
| 1467 | config_proc_t * cp; |
| 1468 | |
| 1469 | cp = LIST_ENTRY(dp->procs, 0); |
| 1470 | |
| 1471 | cp->proc_typep->dump_error_supported(cp); |
| 1472 | |
| 1473 | log_flush(); |
| 1474 | } |
| 1475 | #endif /* } ERROR_TRAP_GEN */ |