Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / legion / src / devices / mem_bus / libdumbserial / dumbserial.c
CommitLineData
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
110typedef 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
127typedef 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 */
141static void ds_parse(config_dev_t *);
142static void ds_init(config_dev_t *);
143static void ds_dump(config_dev_t *);
144static 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
149dev_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
160typedef 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
212static void ds_rxfifo_alloc(ds_state_t * dsp);
213static void ds_parse_rxfifo_contents(ds_state_t * dsp);
214static void ds_reg_init(ds_state_t * dsp, int regnum, int val);
215
216
217
218 /*
219 * Internal functions
220 */
221
222static void create_term(ds_state_t * dsp);
223static void * ds_input_thr(void * ptr);
224static void ds_insert_bytes(ds_state_t * dsp, uint8_t * bufp, int len);
225static void dump_buf(char * strp, uint8_t * bufp, int len);
226static void ds_dump_instruction_counts(ds_state_t * dsp);
227static void ds_output(ds_state_t *dsp, char *str);
228static void ds_save_state(ds_state_t * dsp);
229static void ds_debug_hook_dump(ds_state_t * dsp);
230
231
232#if !NDEBUG /* { */
233 /* Nasty hack for TLB debugging - to go away FIXME */
234static void ds_dump_tlb(ds_state_t * dsp, bool_t is_itlb);
235#endif /* } */
236
237#if ERROR_TRAP_GEN /* { */
238static void ds_load_error_file(ds_state_t * dsp);
239static void ds_dump_error_active(ds_state_t * dsp);
240static 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
250void ds_parse(config_dev_t * config_devp)
251{
252 ds_state_t * dsp;
253 lexer_tok_t tok;
254
255DBG( 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:
283unexpected:
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
410static 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
420static 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
449done:;
450 dsp->in.count = dsp->in.tail;
451}
452
453
454
455
456
457 /*
458 * Initialise the device after parsing is complete
459 */
460
461void 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
489void 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
535static 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
559bool_t
560ds_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 }
722write_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
800void 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
868void * 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 */
894retry:;
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
969DBGX( 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 /* { */
979DBGX( 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 {
1001DBGX( 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 */
1018void
1019ds_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 /* { */
1047void
1048ds_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
1080void
1081ds_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
1094DBGX( 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
1336void 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
1346static 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
1363static 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
1379static 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 ... */
1417static 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 /* { */
1439static 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
1452static 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
1464static 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 */