In legion build config, updated path to GNU tools and updated deprecated Sun CC flag...
[OpenSPARC-T2-SAM] / legion / src / devices / mem_bus / libdumbserial / dumbserial.c
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: dumbserial.c
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
*
* The above named program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License version 2 as published by the Free Software Foundation.
*
* The above named program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ========== Copyright Header End ============================================
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "@(#)dumbserial.c 1.57 07/10/12 SMI"
/*
* This is a very generic serial I/O / console device
*
* Basically, write to the out register and output the
* value to the display.
* Read from the in register and get a character of
* input.
* A status register is a avilable to poll to indicate if
* a character is available for input ..
* ... not entirely dis-similar to the NEC 16550 device ..
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h> /* MAXPATHLEN */
#include <sys/stat.h>
#include <poll.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pthread.h>
#include "basics.h"
#include "allocate.h"
#include "createthr.h"
#include "lexer.h"
#include "simcore.h"
#include "config.h"
#include "dumpinfo.h"
#include "strutil.h"
#include "fatal.h"
#include "device.h"
#include "jbus_mondo.h"
#include "pcie_device.h"
#define DBGX(s) do { } while (0)
/*
* A dumb serial device - briefly modeled on the NS 16550 device
* ... i.e. register bit fields are similar, but that's about it.
*
* We support input and output character buffering.
* Eventually these sizes will be settable from the configfile
* Also eventually we will understand baud rates etc. and fake
* these appropriately.
* For the moment, we support only a simple status poll, byte
* get and byte put.
* Status simply returns, byte-available, underrun, and overrun
* for input info. Nothing for output (yet).
*/
/*
* Registers (8-bits only - 8byte addressing, upper bits read 0 write ignored)
* num RW Function:
* 0 R Input data register
* 0 W Output data register
* 1 RW Interrupt enable register
* 2 R Interrupt indentification register
* 2 W FIFO control register
* 3 RW Line control register
* 4 RW Modem control register
* 5 RW Line status register
* 6 RW Modem status register
* 7 RW Scratch register
* 8 RW Divisor lo
* 9 RW Divisor hi
*
* See 16550 data sheet p 14 - table M
*/
typedef enum {
DS_Input = 0x0, /* RO */
DS_Output = 0x0, /* WO */
DS_IntEnable = 0x1,
DS_IntIdent = 0x2, /* RO */
DS_FIFOCtrl = 0x2, /* WO */
DS_LineCtrl = 0x3,
DS_ModemCtrl = 0x4,
DS_LineStatus = 0x5,
DS_ModemStatus = 0x6,
DS_Scratch = 0x7,
DS_DivLo = 0x8,
DS_DivHi = 0x9
} ds_reg_t;
#define DS_NREGS 8
typedef enum {
TE_Normal,
TE_SawCR,
TE_SawTilde
#if ERROR_TRAP_GEN /* { */
,TE_SawTildeE
#endif /* } ERROR_TRAP_GEN */
} ds_tildestate_t;
/*
* static only to avoid name clashes with other
* modules ... in reality these are exported
* via the dev_type_t struct pointers
*/
static void ds_parse(config_dev_t *);
static void ds_init(config_dev_t *);
static void ds_dump(config_dev_t *);
static bool_t ds_cpu_access(simcpu_t *, config_addr_t *, tpaddr_t offset, maccess_t op, uint64_t * regp);
#define ptest(_s) if (fds.revents & _s) printf(","#_s)
dev_type_t dev_type_dumbserial={
"dumbserial",
ds_parse,
ds_init,
ds_dump,
generic_device_non_cacheable,
ds_cpu_access,
DEV_MAGIC
};
typedef struct {
config_dev_t * config_devp; /* back pointer to device record */
bool_t has_registers;
bool_t uses_xterm;
bool_t dlab; /* DLAB bit from LCR */
#define DS_LCR_DLAB 0x80
/* device registers .. */
uint8_t scratch; /* ! */
uint8_t line_status;
#define DS_LSTAT_DATA_READY 0x1
#define DS_LSTAT_OVERRUN 0x2
#define DS_LSTAT_PARTIY_ERR 0x4
#define DS_LSTAT_FRAMING_ERR 0x8
#define DS_LSTAT_BREAK 0x10
#define DS_LSTAT_TX_HOLD 0x20
#define DS_LSTAT_TX_EMPTY 0x40
#define DS_LSTAT_RCV_ERR 0x80
#define DEFAULT_RXFIFOSZ 1024
struct {
uint8_t *bufp; /* Non null if allocated */
int head;
int tail;
int count;
int size;
} in;
int reg_shift; /* register alignment 0 .. 3 (default = 0 = byte align) */
#define DS_REG_SHIFT_DEF 0
/* the following if uses_xterm is set */
#define DEFAULT_XTERM_STR "/usr/openwin/bin/xterm -T TTY -e /bin/telnet %s %d &"
char * term_strp;
int tty_skt; /* socket to send / receive to client on */
bool_t tty_attached;
pthread_t pthread_id;
pthread_mutex_t tty_lock;
pthread_cond_t tty_cv;
ds_tildestate_t tilde_state;
/* following are for exit_string and time_string */
char * xterm_string;
bool_t exit_on_string;
int xterm_string_pos;
time_t start_time;
time_t stop_time;
} ds_state_t;
static void ds_rxfifo_alloc(ds_state_t * dsp);
static void ds_parse_rxfifo_contents(ds_state_t * dsp);
static void ds_reg_init(ds_state_t * dsp, int regnum, int val);
/*
* Internal functions
*/
static void create_term(ds_state_t * dsp);
static void * ds_input_thr(void * ptr);
static void ds_insert_bytes(ds_state_t * dsp, uint8_t * bufp, int len);
static void dump_buf(char * strp, uint8_t * bufp, int len);
static void ds_dump_instruction_counts(ds_state_t * dsp);
static void ds_output(ds_state_t *dsp, char *str);
static void ds_save_state(ds_state_t * dsp);
static void ds_debug_hook_dump(ds_state_t * dsp);
#if !NDEBUG /* { */
/* Nasty hack for TLB debugging - to go away FIXME */
static void ds_dump_tlb(ds_state_t * dsp, bool_t is_itlb);
#endif /* } */
#if ERROR_TRAP_GEN /* { */
static void ds_load_error_file(ds_state_t * dsp);
static void ds_dump_error_active(ds_state_t * dsp);
static void ds_dump_error_supported(ds_state_t * dsp);
#endif /* } */
/*
* Complete the creation and parsing of this specific cpu
*
* .. basically parse and allocate what is necessary.
*/
void ds_parse(config_dev_t * config_devp)
{
ds_state_t * dsp;
lexer_tok_t tok;
DBG( printf("dumbserial_parse: parsing device %d\n", config_devp->device_id); );
dsp = (void*)Xcalloc(1, ds_state_t);
config_devp->devp = (void*)dsp;
dsp->has_registers = false;
dsp->uses_xterm = false; /* default: output but no input */
dsp->term_strp = NULL;
dsp->in.size = -1;
dsp->reg_shift = DS_REG_SHIFT_DEF;
/* setup initial register values */
dsp->line_status = DS_LSTAT_TX_EMPTY | DS_LSTAT_TX_HOLD;
dsp->dlab = false;
dsp->scratch = 0;
dsp->xterm_string = NULL;
dsp->xterm_string_pos = 0;
dsp->exit_on_string = false;
dsp->start_time = 0;
dsp->stop_time = 0;
tok = lex_get_token();
switch (tok) {
case T_S_Colon: return;
case T_L_Brace: break;
default:
unexpected:
lex_fatal("unexpected token");
}
while ( (tok = lex_get_token()) != T_R_Brace) {
if (tok != T_Token)
goto unexpected;
if (streq(lex.strp, "xterm")) {
if (dsp->term_strp != NULL) lex_fatal("xterm directive given more than once");
tok = lex_get_token();
switch(tok) {
case T_S_Colon:
dsp->term_strp = Xstrdup(DEFAULT_XTERM_STR);
break;
case T_String:
dsp->term_strp = Xstrdup(lex.strp);
lex_get(T_S_Colon);
break;
default:
goto unexpected;
}
dsp->uses_xterm = true;
} else
if (streq(lex.strp, "exit_string") || streq(lex.strp, "time_string")) {
if (dsp->xterm_string != NULL)
lex_fatal("exit_string or time_string already declared");
time(&(dsp->start_time));
if (streq(lex.strp, "exit_string"))
dsp->exit_on_string = true;
tok = lex_get_token();
switch (tok) {
case T_S_Colon:
lex_fatal("exit/time_string needs a valid 'string'");
case T_Number:
if ((int)lex.val > 127) {
lex_fatal("time_string must be string or Ascii value <127 ");
}
dsp->xterm_string = Xcalloc(2, char);
dsp->xterm_string[0] = (char)lex.val;
dsp->xterm_string[1] = '\0';
lex_get(T_S_Colon);
break;
case T_String:
dsp->xterm_string = Xstrdup(lex.strp);
if (strlen(dsp->xterm_string) == 0)
lex_fatal("exit/time_string 'string' length is 0");
lex_get(T_S_Colon);
break;
default:
goto unexpected;
}
} else
if (streq(lex.strp, "rxfifosz")) {
EXEC_WARNING(("dumbserial: rxfifosz is a depricated directive. Use rxfifo instead."));
if (dsp->in.bufp != NULL) lex_fatal("fxfifosz or rxfifo already declared");
lex_get(T_Number);
if ((int)lex.val < 0) {
lex_fatal("rxfifosz not positive");
}
dsp->in.size = (int)lex.val;
lex_get(T_S_Colon);
ds_rxfifo_alloc(dsp);
} else
if (streq(lex.strp, "rxfifo")) {
if (dsp->in.bufp != NULL) lex_fatal("fxfifosz or rxfifo already declared");
lex_get(T_Number);
if ((int)lex.val < 0) {
lex_fatal("rxfifosz not positive");
}
dsp->in.size = (int)lex.val;
tok = lex_get_token();
switch (tok) {
case T_L_Brace:
ds_parse_rxfifo_contents(dsp);
break;
case T_S_Colon:
break;
default:
goto unexpected;
}
} else
if (streq(lex.strp, "regshift")) {
tok = lex_get_token();
switch (tok) {
case T_Number:
if ((int)lex.val < 0 || (int)lex.val>3) {
lex_fatal("regshift must be an integer from 0 to 3");
}
dsp->reg_shift = (int)lex.val;
lex_get(T_S_Colon);
break;
default:
goto unexpected;
}
} else
if (streq(lex.strp, "reg")) {
int regnum;
lex_get(T_Number);
regnum = (int)lex.val;
lex_get(T_Number);
ds_reg_init(dsp, regnum, (int)lex.val);
lex_get(T_S_Colon);
} else
goto unexpected;
}
if ((config_devp->addrp->range >> dsp->reg_shift) < 0x8)
fatal("dumbserial: dumbserial requires address space size of 0x%x", 0x8<<dsp->reg_shift);
/* If xterm requested make sure we have an input buffer */
if (dsp->uses_xterm && dsp->in.size == -1) {
dsp->in.size = DEFAULT_RXFIFOSZ;
ds_rxfifo_alloc(dsp);
}
/* If we have an input buffer but no xterm .. fail */
if (dsp->in.bufp != NULL && !dsp->uses_xterm) lex_fatal("receive fifo has been specified, but no xterm directive for input delivery");
}
/*
* Basic allocation of fifo space
*/
static void ds_rxfifo_alloc(ds_state_t * dsp)
{
dsp->in.count = 0;
dsp->in.head = 0;
dsp->in.tail = 0;
dsp->in.bufp = Xcalloc(dsp->in.size, uint8_t);
}
static void ds_parse_rxfifo_contents(ds_state_t * dsp)
{
lexer_tok_t tok;
ds_rxfifo_alloc(dsp);
while ( dsp->in.tail<dsp->in.size ) {
lex_get(T_Number);
if (lex.val<0 || lex.val >255) lex_fatal("Illegal rxfifo contents");
dsp->in.bufp[dsp->in.tail] = (int)lex.val;
dsp->in.tail++;
tok = lex_get_token();
switch (tok) {
case T_Comma:
break;
case T_R_Brace:
goto done;
default:
lex_fatal("Unexpected token parsing rxfifo contents");
}
}
lex_fatal("Too many initialisers in rxfifo directive\n");
abort();
done:;
dsp->in.count = dsp->in.tail;
}
/*
* Initialise the device after parsing is complete
*/
void ds_init(config_dev_t * config_devp)
{
ds_state_t * dsp;
dsp = (ds_state_t*)config_devp->devp;
/* OK register values are now live */
dsp->has_registers = true;
/* if input fifo required it was created in parse phase */
ASSERT(!dsp->uses_xterm || (dsp->uses_xterm && dsp->in.bufp!=NULL));
/* if TTY to be created then do so ... */
if (dsp->uses_xterm) create_term(dsp);
dsp->config_devp = config_devp; /* back pointer */
}
/*
* Dumb serial configuration dump
*/
void ds_dump(config_dev_t * config_devp)
{
ds_state_t * dsp;
dsp = (ds_state_t*)config_devp->devp;
dumpinfo.indent++;
pi("regshift %d ;\n", dsp->reg_shift);
if (dsp->uses_xterm) {
if (strcmp(dsp->term_strp, DEFAULT_XTERM_STR)==0) {
pi("xterm ;\n");
} else {
pi("xterm \"%s\" ;\n", dsp->term_strp);
}
if (dsp->in.bufp != NULL) {
ASSERT( dsp->in.count <= dsp->in.size );
if (dsp->in.count>0) {
int idx, cnt;
pi("rxfifo %d { 0x%02x", dsp->in.size, dsp->in.bufp[dsp->in.head]);
for (cnt=1, idx=dsp->in.head; cnt!=dsp->in.count; cnt++) {
idx++;
if (idx>=dsp->in.size) idx=0;
fprintf(dumpinfo.outp, ", 0x%02x", dsp->in.bufp[idx]);
}
fprintf(dumpinfo.outp, " }\n");
} else {
pi("rxfifo %d ;\n", dsp->in.size);
}
}
}
if (dsp->has_registers) {
/* Dump live register contents here ... */
pi("reg 0x%x 0x%x ;\n", (int)DS_LineStatus, dsp->line_status);
pi("reg 0x%x 0x%x ;\n", (int)DS_LineCtrl, dsp->dlab ? DS_LCR_DLAB : 0);
pi("reg 0x%x 0x%x ;\n", (int)DS_Scratch, dsp->scratch);
}
dumpinfo.indent--;
}
static void ds_reg_init(ds_state_t * dsp, int regnum, int val)
{
switch (regnum) {
case DS_LineStatus:
dsp->line_status = val;
break;
case DS_LineCtrl:
dsp->dlab = (val & DS_LCR_DLAB)!=0 ? true : false;
break;
case DS_Scratch:
dsp->scratch = val;
break;
}
dsp->has_registers = true;
}
/*
* Returns true if device access completes successfully
*/
bool_t
ds_cpu_access(simcpu_t *sp, config_addr_t * cap, tpaddr_t offset, maccess_t op, uint64_t * regp)
{
ds_reg_t reg;
ds_state_t * dsp = (ds_state_t*)cap->config_devp->devp;
uint64_t val;
int size, acc_size;
char timebuf[32];
char buf[2];
/*
* Unique identifier to tag guest/hv console output with legion time
* dump. This works when you have multiple dumbserial devices (one
* for hv, another for guest)as each line of console output on
* either serial device gets a unique tag.
*/
static int output_tag = 0;
size = op & MA_Size_Mask;
op &= MA_Op_Mask;
acc_size = (1<<dsp->reg_shift);
if ((offset & (((tpaddr_t)acc_size)-1))!=0) {
EXEC_WARNING(("dumbserial: Illegal device access - offset "
"(0x%llx) not %d byte aligned",
(uint64_t)offset, acc_size));
return false;
}
if (MA_Size8 != size) {
EXEC_WARNING(("dumbserial: Illegal device access - op size "
"(%d) not byte", size));
return false;
}
reg = offset >> dsp->reg_shift;
if (reg >= DS_NREGS) {
EXEC_WARNING(("dumbserial: Illegal device access - no reg at "
"offset 0x%llx", offset));
return false;
}
if (dsp->dlab && (reg == DS_Input || reg == DS_IntEnable))
reg += DS_DivLo;
switch (op) {
case MA_Ld:
case MA_LdSigned:
val = 0;
switch (reg) {
case DS_Input:
pthread_mutex_lock(&dsp->tty_lock);
if (dsp->in.count == 0) {
val = 0;
dsp->line_status &= ~DS_LSTAT_DATA_READY;
} else {
val = dsp->in.bufp[dsp->in.head];
dsp->in.head ++;
if (dsp->in.head >= dsp->in.size) dsp->in.head = 0;
dsp->in.count --;
if (dsp->in.count == 0) {
dsp->line_status &= ~DS_LSTAT_DATA_READY;
}
}
pthread_mutex_unlock(&dsp->tty_lock);
break;
case DS_IntEnable:
IMPL_WARNING(("dumbserial: Interrupt enable not yet supported"));
break;
case DS_IntIdent:
IMPL_WARNING(("dumbserial: FIFO control not yet supported"));
break;
case DS_LineCtrl:
IMPL_WARNING(("dumbserial: Line control not yet supported"));
if (dsp->dlab) val |= DS_LCR_DLAB;
break;
case DS_ModemCtrl:
IMPL_WARNING(("dumbserial: Modem control not yet supported"));
break;
case DS_LineStatus:
/* FIXME - more to do here */
/* TX always empty for the moment */
pthread_mutex_lock(&dsp->tty_lock);
val = dsp->line_status;
pthread_mutex_unlock(&dsp->tty_lock);
break;
case DS_ModemStatus:
IMPL_WARNING(("dumbserial: Modem status not yet supported"));
break;
case DS_Scratch:
val = dsp->scratch;
break;
case DS_DivLo:
IMPL_WARNING(("dumbserial: Divisor (low) not yet supported"));
break;
case DS_DivHi:
IMPL_WARNING(("dumbserial: Divisor (high) not yet supported"));
break;
default:
EXEC_WARNING(("dumbserial: illegal register accessed - offset 0x%x", offset));
return false;
}
if (MA_LdSigned == op) val = (uint64_t)(sint64_t)(sint8_t)val;
*regp = val;
break;
case MA_St:
val = *regp & 0xff; /* byte acces only */
switch (reg) {
case DS_Output:
if (dsp->xterm_string != NULL) {
/*
* Catch the case where it's just a single char (or an ascii char)
* that we are looking for
*/
if (strlen(dsp->xterm_string) == 1) {
if ((int)dsp->xterm_string[0] == val) {
/* print instn counts on legion console */
time(&(dsp->stop_time));
printf("(T-%d) (%d sec) ->", output_tag,
((dsp->stop_time) - (dsp->start_time)));
ds_dump_instruction_counts(dsp);
/* print time tag on guest console */
sprintf(timebuf, "->(T-%d)", output_tag++);
ds_output(dsp, timebuf);
if (dsp->exit_on_string == true)
fatal("exit_string enabled exit\n");
goto write_output;
}
}
if (val == (dsp->xterm_string[dsp->xterm_string_pos]))
dsp->xterm_string_pos += 1;
else
if (val == dsp->xterm_string[0])
dsp->xterm_string_pos = 1;
else
dsp->xterm_string_pos = 0;
if (dsp->xterm_string[dsp->xterm_string_pos] == '\0') {
time(&(dsp->stop_time));
printf("\nTime from sim start to '%s' = %d seconds\n",\
dsp->xterm_string, (dsp->stop_time) - (dsp->start_time));
ds_dump_instruction_counts(dsp);
if (dsp->exit_on_string == true)
fatal("exit_string enabled exit\n");
}
}
write_output:
buf[0] = val;
buf[1] = '\0';
ds_output(dsp, buf);
break;
case DS_IntEnable:
IMPL_WARNING(("dumbserial: Interrupt enable not yet supported"));
break;
case DS_FIFOCtrl:
IMPL_WARNING(("dumbserial: FIFO control not yet supported"));
break;
case DS_LineCtrl:
IMPL_WARNING(("dumbserial: Line control not yet supported"));
dsp->dlab = ((val & DS_LCR_DLAB) != 0);
break;
case DS_ModemCtrl:
IMPL_WARNING(("dumbserial: Modem control not yet supported"));
break;
case DS_LineStatus:
/* FIXME - more to do here */
/* TX always empty for the moment */
pthread_mutex_lock(&dsp->tty_lock);
dsp->line_status = (val & 0xff) | DS_LSTAT_TX_EMPTY | DS_LSTAT_TX_HOLD;
pthread_mutex_unlock(&dsp->tty_lock);
break;
case DS_ModemStatus:
IMPL_WARNING(("dumbserial: Modem status not yet supported"));
break;
case DS_Scratch:
dsp->scratch = val;
break;
case DS_DivLo:
IMPL_WARNING(("dumbserial: Divisor (low) not yet supported"));
break;
case DS_DivHi:
IMPL_WARNING(("dumbserial: Divisor (high) not yet supported"));
break;
default:
EXEC_WARNING(("dumbserial: illegal register accessed - offset 0x%x", offset));
return false;
}
break;
default:
fatal("ds_cpu_access: Internal error unexpected op=0x%x\n", op);
}
return true;
}
/*
* Support routines ...
*/
/*
* Create term ... during initialisation, create the thread to run the
* terminal.
*/
void create_term(ds_state_t * dsp)
{
dsp->tty_skt = -1;
dsp->tty_attached = false;
/* dsp->tilde_state = TE_Normal; */
dsp->tilde_state = TE_SawCR; /* shouldn't have to CR to use ~ as first input char */
pthread_mutex_init(&dsp->tty_lock, NULL);
pthread_cond_init(&dsp->tty_cv, NULL);
/*
* OK first we create the management thread for this console's input
* - then wait for it to tell us that it is happily up and running
* before we return to the simulator to continue initialisation.
*/
pthread_mutex_lock(&dsp->tty_lock);
create_thread(ds_input_thr, (void*)dsp, &(dsp->pthread_id) );
/* should probably timeout here incase child doesn't start */
while (!dsp->tty_attached) pthread_cond_wait(&dsp->tty_cv, &dsp->tty_lock);
pthread_mutex_unlock(&dsp->tty_lock);
#if 0 /* { */
/*
* Dummy test code ...
*/
do {
static int count = 0;
pthread_mutex_lock(&dsp->tty_lock);
if (dsp->tty_attached) {
struct pollfd fds;
extern void tcparams(int fd);
char temp[MAXPATHLEN];
fds.fd = dsp->tty_skt;
fds.events = POLLOUT;
fds.revents = 0;
res = poll(&fds, 1, -1);
printf("out: ");
ptest(POLLIN); ptest(POLLRDNORM); ptest(POLLRDBAND); ptest(POLLOUT); ptest(POLLWRNORM); ptest(POLLWRBAND); ptest(POLLERR); ptest(POLLHUP); ptest(POLLNVAL);
printf("\n");
fflush(stdout);
sprintf(temp, "test connect %d\n\r", count);
write(dsp->tty_skt, temp, strlen(temp));
}
pthread_mutex_unlock(&dsp->tty_lock);
count ++;
sleep(1);
} while (1);
#endif /* } */
}
/*
* Thread to manage external connections to this serial port.
*
* By default it creates an xterm with a telnet connection to
* the appropriate i/o port.
*/
void * ds_input_thr(void * ptr)
{
ds_state_t * dsp;
uint8_t buf[1024];
#define MAXHOSTNAME 256
char myhostname[MAXHOSTNAME];
int ds_sv_skt;
struct hostent * hp;
int on, length;
struct sockaddr_in ds_server, from;
char * froms;
dsp = (ds_state_t*)ptr;
/*
* Create our socket to listen to
*/
ds_sv_skt = socket(AF_INET, SOCK_STREAM, 0);
if (ds_sv_skt < 0) fatal("opening stream socket");
/* enable the reuse of this socket if this process dies */
if (setsockopt(ds_sv_skt, SOL_SOCKET, SO_REUSEADDR, (uint8_t*)&on, sizeof(on))<0)
fatal("turning on REUSEADDR");
/* bind it */
retry:;
ds_server.sin_family = AF_INET;
ds_server.sin_addr.s_addr = INADDR_ANY;
ds_server.sin_port = htons(0); /* bind to an OS selected local port */
if (bind(ds_sv_skt, (struct sockaddr *)&ds_server, sizeof(ds_server)) < 0) {
switch (errno) {
case EAGAIN:
goto retry;
case EADDRINUSE:
fatal("Port is already in use\n");
default:
fatal("binding tcp stream socket");
}
}
length = sizeof(ds_server);
if (getsockname(ds_sv_skt, (struct sockaddr *) &ds_server, &length)==-1)
fatal("getting socket name");
listen(ds_sv_skt, 1);
/*
* Create the client xterm etc
*/
gethostname(myhostname, MAXHOSTNAME);
sprintf((char*)buf, dsp->term_strp, myhostname, ntohs(ds_server.sin_port));
system((char*)buf);
/*
* OK main loop for processing connections and data traffic
*/
do {
if (!dsp->tty_attached) {
PRINTF(("\ndumbserial: Waiting for connection to : %s:%d\n",
myhostname, ntohs(ds_server.sin_port)));
length = sizeof(from);
dsp->tty_skt = accept(ds_sv_skt, (struct sockaddr *)&from, (int*)&length);
hp = gethostbyaddr((char *)&from.sin_addr, 4, AF_INET);
if (hp == (struct hostent *)0) {
froms = inet_ntoa(from.sin_addr);
fprintf(stderr,"cant resolve hostname for %s\n", froms);
} else {
froms = hp->h_name;
}
PRINTF(("dumbserial: Accepted connection on %s:%d from %s:%d\n",
myhostname, ntohs(ds_server.sin_port),
froms, ntohs(from.sin_port)));
pthread_mutex_lock(&dsp->tty_lock);
dsp->tty_attached = true;
pthread_mutex_unlock(&dsp->tty_lock);
pthread_cond_signal(&dsp->tty_cv);
} else {
int res;
struct pollfd fds;
#define POLL_TIMEOUT -1 /* wait forever ? FIXME ? */
fds.fd = dsp->tty_skt;
fds.events = POLLIN|POLLPRI;
#if HOST_OS_SOLARIS9 /* { */
fds.events |= POLLRDNORM|POLLRDBAND;
#endif /* } */
fds.revents = 0;
res = poll(&fds, 1, POLL_TIMEOUT);
DBGX( printf("tty: ");
ptest(POLLIN);
ptest(POLLOUT);
ptest(POLLERR);
ptest(POLLHUP);
ptest(POLLNVAL);
printf("\n");
fflush(stdout); );
#if HOST_OS_SOLARIS9 /* { */
DBGX( printf("\t");
ptest(POLLRDNORM);
ptest(POLLRDBAND);
ptest(POLLWRNORM);
ptest(POLLWRBAND);
printf("\n");
fflush(stdout); );
#endif /* } */
if (fds.revents & POLLIN) {
res = read(dsp->tty_skt, buf, sizeof (buf));
if (res == 0) {
/* a read of 0 bytes is an EOF */
pthread_mutex_lock(&dsp->tty_lock);
dsp->tty_attached = false;
pthread_mutex_unlock(&dsp->tty_lock);
pthread_cond_signal(&dsp->tty_cv);
close(dsp->tty_skt);
SANITY(dsp->tty_skt = -1;);
} else if (res<0) {
perror("read");
} else {
DBGX( dump_buf("input bytes:", buf, res); );
ds_insert_bytes(dsp, buf, res);
}
}
}
} while (1);
/*NOTREACHED*/
}
/* emits string and newline */
void
ds_output(ds_state_t *dsp, char *str)
{
if (dsp->uses_xterm) {
if (dsp->tty_attached) {
(void) write(dsp->tty_skt, str, strlen(str));
}
/* drop char if tty is not attached */
} else {
fputs(str, stdout);
fflush(stdout);
}
}
/* FIXME: This compile option
* enables the console to force an external chip reset of any procs
* attached to the same domain.
* This is somewhat of a hack, and should be handled else where ...
* ... which is why it is not a device option in the config file
*/
#if CONSOLE_RESET /* { */
void
ds_domain_reset(ds_state_t * dsp)
{
domain_t * dp;
int i;
/*
* FIXME: this is a hack ...
* walk the procs in my domain, and reset them all
*/
dp = dsp->config_devp->domainp;
for (i=0; i<dp->procs.count; i++) {
config_proc_t * cp;
cp = LIST_ENTRY(dp->procs, i);
cp->proc_typep->ext_signal(cp, ES_XIR, NULL);
}
}
#endif /* } */
void
ds_insert_bytes(ds_state_t *dsp, uint8_t *bufp, int len)
{
int i;
pthread_mutex_lock(&dsp->tty_lock);
/*
* process all characters in buffer, don't check for overrun
* right away
*/
for (i = 0; i < len; i++) {
int c = bufp[i];
DBGX( printf("State %d : Char typed 0x%x [%c]\n", (int)dsp->tilde_state, c, c>=' ' && c<127 ? c : '.'); fflush(stdout); );
switch (dsp->tilde_state) {
case TE_Normal: /* waiting for CR */
if (c == '\n')
dsp->tilde_state = TE_SawCR;
break;
case TE_SawCR: /* waiting for tilde */
if (c == '~') {
dsp->tilde_state = TE_SawTilde;
goto skip;
}
if (c != '\n') dsp->tilde_state = TE_Normal;
break;
case TE_SawTilde: /* tilde command */
dsp->tilde_state = TE_SawCR;
/* Flush logging output. */
log_flush();
switch (c) {
case '~': /* emit single tilde */
dsp->tilde_state = TE_Normal;
break;
case '#': /* BREAK */
dsp->line_status |= DS_LSTAT_BREAK;
ds_output(dsp, "BREAK\n");
/* allow another ~ cmd without CR */
goto skip;
case '?': /* help */
ds_output(dsp, "dumb_serial tilde escapes:\n");
ds_output(dsp, "\t# BREAK\n");
ds_output(dsp, "\t~ generate tilde\n");
ds_output(dsp, "\tz exit simulator\n");
#if ERROR_TRAP_GEN /* { */
ds_output(dsp, "\ter reload (append) error config file into running legion\n");
ds_output(dsp, "\t error config filename specified in " \
"legion config \n\t file used at startup using " \
"error_reload_file_name directive\n");
ds_output(dsp, "\ted dump current error_event and error_asi lists\n");
ds_output(dsp, "\tes dump supported (built-in) error types\n");
#endif /* } */
#if CONSOLE_RESET /* { */
ds_output(dsp, "\tx external reset for CPUs in same domain\n");
#endif /* } */
#if !NDEBUG /* { */
ds_output(dsp, "\tj generate JBus interrupt\n");
ds_output(dsp, "\ts generate SSI interrupt\n");
ds_output(dsp, "\tp generate PCIe interrupt\n");
ds_output(dsp, "\tr generate FPGA RX intr\n");
ds_output(dsp, "\tt generate FPGA TX intr\n");
#endif /* } */
#if !NDEBUG /* { */
ds_output(dsp, "\ti dump I-TLB contents for CPUs in same domain\n");
ds_output(dsp, "\td dump D-TLB contents for CPUs in same domain\n");
#endif /* } */
#if !NDEBUG /* { */
ds_output(dsp, "\tb toggle the debug output enable bits\n");
#endif /* } */
ds_output(dsp, "\tl dump data for the debug_hook"\
" function you are using\n\t\t(coverage, tracing, "\
"debug_log, lockstep)\n");
ds_output(dsp, "\tn dump the current and delta instruction count for each CPU\n");
ds_output(dsp, "\t? this message\n");
/* allow another ~ cmd without CR */
goto skip;
case 'z':
fatal("dumbserial requested exit");
goto skip;
#if CONSOLE_RESET /* { */
case 'x': /* Hack to enable extern chip reset easily */
ds_output(dsp, "Resetting cpu(s) in domain\n");
ds_domain_reset(dsp);
goto skip; /* swallow 'x' character after sending reset */
#endif /* } */
#if !NDEBUG /* { */
case 'j': {
config_proc_t * cpp;
jbus_mondo_t mondo;
ds_output(dsp, "JBUS intr\n");
mondo.adr.source = 0;
mondo.adr.target = 0;
mondo.adr.type = 0x14; /* INT */
mondo.data0 = 0xaabbccdd;
mondo.data1 = 0x11223344;
cpp = LIST_ENTRY(
dsp->config_devp->domainp->procs, 0);
cpp->proc_typep->ext_signal(cpp, ES_JBUS, &mondo);
goto skip;
}
case 'p': {
config_proc_t * cpp;
pcie_mondo_t *mondo = (pcie_mondo_t *)Xcalloc(1, pcie_mondo_t);
ds_output(dsp, "PCIE mondo intr\n");
mondo->thread_id = 0;
mondo->data[0] = 0xaabbccdd;
mondo->data[1] = 0x11223344;
cpp = LIST_ENTRY(
dsp->config_devp->domainp->procs, 0);
cpp->proc_typep->ext_signal(cpp, ES_PCIE, mondo);
goto skip;
}
case 's': {
config_proc_t * cpp;
ds_output(dsp, "SSI intr\n");
cpp = LIST_ENTRY(
dsp->config_devp->domainp->procs, 0);
cpp->proc_typep->ext_signal(cpp, ES_SSI, NULL);
goto skip;
}
case 'r': {
extern void (*fpga_intr)(int a);
if (fpga_intr != NULL) {
config_proc_t * cpp;
ds_output(dsp, "fpga RX intr\n");
cpp = LIST_ENTRY(
dsp->config_devp->domainp->procs,
0);
cpp->proc_typep->ext_signal(cpp,
ES_SSI, NULL);
(fpga_intr)(1);
} else {
ds_output(dsp,
"no fpga intr attached\n");
}
goto skip;
}
case 't': {
extern void (*fpga_intr)(int a);
if (fpga_intr != NULL) {
config_proc_t * cpp;
ds_output(dsp, "fpga TX intr\n");
cpp = LIST_ENTRY(
dsp->config_devp->domainp->procs,
0);
cpp->proc_typep->ext_signal(cpp,
ES_SSI, NULL);
(fpga_intr)(2);
} else {
ds_output(dsp,
"no fpga intr attached\n");
}
goto skip;
}
#if INTERNAL_BUILD /* { */
case '%': {
printf("Initiating Simulator state save\n");
ds_save_state(dsp);
goto skip;
}
#endif /* INTERNAL_BUILD } */
case 'i': {
ds_dump_tlb(dsp, false);
goto skip;
}
case 'd': {
ds_dump_tlb(dsp, true);
goto skip;
}
case 'b': {
simcore_update_debug_bits(debug_bits ^ debug_bits_xor);
log_flush();
goto skip;
}
#endif /* } */
case 'n':
ds_dump_instruction_counts(dsp);
goto skip;
case 'l':
ds_debug_hook_dump(dsp);
goto skip;
#if ERROR_TRAP_GEN /* { */
case 'e':
dsp->tilde_state = TE_SawTildeE;
goto skip;
#endif /* ERROR_TRAP_GEN } */
default: /* eat current char */
/*
* FIXME could emit ~ and current char
* but it's harder
*/
ds_output(dsp, "~? for tilde help\n");
goto skip;
}
break;
#if ERROR_TRAP_GEN /* { */
case TE_SawTildeE: /* tilde e command */
dsp->tilde_state = TE_SawCR;
switch (c) {
case 'r':
ds_load_error_file(dsp);
goto skip;
case 'd':
ds_dump_error_active(dsp);
goto skip;
case 's':
ds_dump_error_supported(dsp);
goto skip;
}
break;
#endif /* } ERROR_TRAP_GEN */
}
if (dsp->in.count < dsp->in.size) {
dsp->in.bufp[dsp->in.tail] = bufp[i];
dsp->in.tail++;
if (dsp->in.tail >= dsp->in.size)
dsp->in.tail = 0;
dsp->in.count++;
dsp->line_status |= DS_LSTAT_DATA_READY;
} else {
if (!(dsp->line_status & DS_LSTAT_OVERRUN))
warning("dumbserial: buffer overrun");
dsp->line_status |= DS_LSTAT_OVERRUN;
}
skip:;
}
pthread_mutex_unlock(&dsp->tty_lock);
}
void dump_buf(char * strp, uint8_t * bufp, int len)
{
int i;
printf("%s read %d bytes: ", strp, len);
for(i=0; i<len; i++) printf("0x%02x [%c]",bufp[i],bufp[i]>=32 && bufp[i]<127 ? bufp[i] : '.');
printf("\n");
}
static void ds_save_state(ds_state_t * dsp)
{
domain_t * dp = dsp->config_devp->domainp;
int i;
for (i=0; i<dp->procs.count; i++) {
config_proc_t * cp;
cp = LIST_ENTRY(dp->procs, i);
cp->proc_typep->ext_signal(cp, ES_LEGION_SAVE_STATE, NULL);
}
#if !NDEBUG /* { */
log_flush();
#endif /* } */
}
static void ds_dump_instruction_counts(ds_state_t * dsp)
{
domain_t * dp = dsp->config_devp->domainp;
int i;
for (i=0; i<dp->procs.count; i++) {
config_proc_t * cp;
cp = LIST_ENTRY(dp->procs, i);
cp->proc_typep->instn_cnt_dump(cp);
}
log_flush();
}
static void ds_debug_hook_dump(ds_state_t * dsp)
{
domain_t *dp = dsp->config_devp->domainp;
int i;
void (*debug_hook_dumpp)(void);
bool_t did_dump = false;
log_flush();
fflush(stdout);
for (i=0; i<dp->procs.count; i++) {
config_proc_t * cp;
cp = LIST_ENTRY(dp->procs, i);
debug_hook_dumpp =
(void(*)(void))cp->proc_typep->debug_hook_dumpp;
if (debug_hook_dumpp != NULL) {
if (!did_dump) {
printf("\n");
did_dump = true;
}
debug_hook_dumpp();
}
}
if (did_dump)
printf("\n");
else
printf("\nNo debug_hook dump configured.\n");
log_flush();
fflush(stdout);
}
#if !NDEBUG /* { */
/* Nasty hack for TLB debugging - to go away FIXME */
/* First find all the cpus, then dump their TLBs ... */
static void ds_dump_tlb(ds_state_t * dsp, bool_t is_dtlb)
{
domain_t * dp = dsp->config_devp->domainp;
int i;
/*
* FIXME: this is a hack ...
* walk the procs in my domain, and dump the tlb contents
*/
for (i=0; i<dp->procs.count; i++) {
config_proc_t * cp;
cp = LIST_ENTRY(dp->procs, i);
cp->proc_typep->tlb_dump(cp, is_dtlb);
}
log_flush();
}
#endif /* } */
#if ERROR_TRAP_GEN /* { */
static void ds_load_error_file(ds_state_t * dsp)
{
domain_t * dp = dsp->config_devp->domainp;
config_proc_t * cp;
cp = LIST_ENTRY(dp->procs, 0);
cp->proc_typep->load_error_file(cp);
log_flush();
}
static void ds_dump_error_active(ds_state_t * dsp)
{
domain_t * dp = dsp->config_devp->domainp;
config_proc_t * cp;
cp = LIST_ENTRY(dp->procs, 0);
cp->proc_typep->dump_error_active(cp);
log_flush();
}
static void ds_dump_error_supported(ds_state_t * dsp)
{
domain_t * dp = dsp->config_devp->domainp;
config_proc_t * cp;
cp = LIST_ENTRY(dp->procs, 0);
cp->proc_typep->dump_error_supported(cp);
log_flush();
}
#endif /* } ERROR_TRAP_GEN */