Commit | Line | Data |
---|---|---|
920dae64 AT |
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 */ |