// ========== Copyright Header Begin ==========================================
// OpenSPARC T2 Processor File: dumbserial.cc
// 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 ============================================
/* dumb serial device for NIOBP */
/* "Leveraged" from Legion dumbserial devices :) */
* 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
* For the moment, we support only a simple status poll, byte
* 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)
* 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
* See 16550 data sheet p 14 - table M
// #include "createthr.h"
static int dumbserial_ld_operation (void *cd
, uint64_t paddr
, uint64_t *buf
, int size
, uint32_t v9cpuid
);
static int dumbserial_st_operation (void *cd
, uint64_t paddr
, uint64_t *buf
, int size
, uint32_t v9cpuid
);
#define DBGX(s) do {} while (0)
DS_Output
= 0x0, /* WO */
DS_IntIdent
= 0x2, /* RO */
DS_FIFOCtrl
= 0x2, /* WO */
#define ptest(_s) if (fds.revents & _s) printf(","#_s)
/* device registers .. */
#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
int reg_shift
; /* register alignment 0 .. 3 (default = 0 = byte align) */
#define DS_REG_SHIFT_DEF 0
/* the following if uses_term is set */
#define DEFAULT_XTERM_STR "/usr/openwin/bin/xterm -T TTY -e /bin/telnet %s %d &"
int tty_skt
; /* socket to send / receive to client on */
pthread_mutex_t tty_lock
;
ds_tildestate_t tilde_state
;
const char * type
; // GUEST or HYPERVISOR
#define NUM_DEVICES 2 // hypervisor and guest consoles
static ds_state_t
*dumbserials
[NUM_DEVICES
];
#define HV_SERIAL 0 // calls xterm
#define G_SERIAL 1 // nothing
#define HV_START 0x9f00000000
#define HV_END 0x9f0000004f
#define HV_START 0xfff0c2c000
#define HV_END 0xfff0c2c04f
#define G_START 0x9f10002000
#define G_END 0x9f1000204f
static void create_term(ds_state_t
* dsp
);
extern "C" 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
);
/* hack for TLB debugging - to go away FIXME */
static void ds_dump_tlb(ds_state_t
* dsp
, bool is_itlb
);
ds_state_t
*get_ds_instance(uint64_t addr
) {
assert(0); // should not be called
if ( addr
>= HV_START
&& addr
< HV_END
) {
return dumbserials
[HV_DEVICE
];
} else if ( addr
>= G_START
&& addr
< G_END
) {
return dumbserials
[G_DEVICE
];
fprintf(stderr
, "Unknown dumbconsole address range!\n");
extern "C" static int dumbserial_physio_access(uint32_t cpuid
, void* obj
, uint64_t paddr
, mmi_bool_t wr
, uint32_t size
, uint64_t* buf
, uint8_t bytemask
)
return dumbserial_st_operation(obj
, paddr
, buf
, size
, cpuid
);
return dumbserial_ld_operation(obj
, paddr
, buf
, size
, cpuid
);
int dumbserial_ld_operation (void *obj
, uint64_t paddr
, uint64_t *buf
, int size
, uint32_t v9cpuid
)
ds_state_t
*dsp
= (ds_state_t
*) obj
;
DBGX (printf("CAUGHT LD to %016llx with size %d\n", paddr
, size
); );
if ((paddr
& (((uint64_t)1<<dsp
->reg_shift
)-1))!=0) {
// use the offset of the address, not the entire address
reg
= (ds_reg_t
) ((paddr
& 0xfff) >> dsp
->reg_shift
);
DBGX(printf("\treg: %d\n", reg
); );
pthread_mutex_lock(&dsp
->tty_lock
);
if (dsp
->in
.count
== 0) {
dsp
->line_status
&= ~DS_LSTAT_DATA_READY
;
val
= dsp
->in
.bufp
[dsp
->in
.head
];
if (dsp
->in
.head
>= dsp
->in
.size
) dsp
->in
.head
= 0;
if (dsp
->in
.count
== 0) {
dsp
->line_status
&= ~DS_LSTAT_DATA_READY
;
pthread_mutex_unlock(&dsp
->tty_lock
);
/* FIXME - more to do here */
/* TX always empty for the moment */
pthread_mutex_lock(&dsp
->tty_lock
);
pthread_mutex_unlock(&dsp
->tty_lock
);
dsp
->scratch
= val
& 0xff;
val
= (uint64_t)(int64_t)(int8_t)val
;
DBGX(printf("\tLD from %016llx returning %016llx\n", paddr
, val
););
int dumbserial_st_operation (void *obj
, uint64_t pa
, uint64_t *buf
, int size
, uint32_t v9cpuid
)
ds_state_t
*dsp
= (ds_state_t
*) obj
;
DBGX(printf("caught st to %016llx with data %c, size %d\n", pa
, (char)*buf
, size
););
if ((pa
& (((uint64_t)1<<dsp
->reg_shift
)-1))!=0) {
// use the offset of the address, not the entire address
reg
= (ds_reg_t
) ((pa
& 0xfff) >> dsp
->reg_shift
);
write(dsp
->tty_skt
, buf
, 1);
/* 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
);
dsp
->scratch
= val
& 0xff;
ds_state_t
*dsp
= (ds_state_t
*)calloc(1, sizeof(ds_state_t
));
dsp
->uses_term
= false; /* default: output but no input */
dsp
->in
.size
= DEFAULT_RXFIFOSZ
;
dsp
->reg_shift
= DS_REG_SHIFT_DEF
;
dsp
->term_strp
= (char *)malloc(256);
switch(temp_ds_counter
) {
strcpy(dsp
->term_strp
, "xterm -l -lf guest1.log -bg black -fg green -geometry 80x20+170+300 -T 'Hypervisor Console' -e ./netcons %s %d &");
dumbserials
[HV_DEVICE
] = dsp
;
strcpy(dsp
->term_strp
, "xterm -l -lf guest1.log -bg black -fg green -geometry 80x35+200+30 -T 'Guest Console' -e ./netcons %s %d &");
dumbserials
[G_DEVICE
] = dsp
;
void ds_init(ds_state_t
* dsp
) {
dsp
->line_status
= DS_LSTAT_TX_EMPTY
| DS_LSTAT_TX_HOLD
;
dsp
->in
.bufp
= (uint8_t *)calloc(1, dsp
->in
.size
);
/* if TTY to be created then do so ... */
if (dsp
->uses_term
) create_term(dsp
);
* Create term ... during initialisation, create the thread to run the
void create_term(ds_state_t
* dsp
)
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
);
pthread_create(&(dsp
->pthread_id
), NULL
, ds_input_thr
, (void *) dsp
);
/* 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
);
* 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
)
char myhostname
[MAXHOSTNAME
];
struct sockaddr_in ds_server
, from
;
* Create our socket to listen to
ds_sv_skt
= socket(AF_INET
, SOCK_STREAM
, 0);
fprintf(stderr
, "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) {
fprintf(stderr
,"turning on REUSEADDR");
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) {
fprintf(stderr
, "Port is already in use\n");
fprintf(stderr
, "binding tcp stream socket");
length
= sizeof(ds_server
);
if (getsockname(ds_sv_skt
, (struct sockaddr
*) &ds_server
, &length
)==-1) {
fprintf(stderr
, "getting socket name");
* Create the client xterm etc
gethostname(myhostname
, MAXHOSTNAME
);
sprintf((char*)buf
, dsp
->term_strp
, myhostname
, ntohs(ds_server
.sin_port
));
* OK main loop for processing connections and data traffic
if (!dsp
->tty_attached
) {
ui
->output("\nWaiting for connection to : %s:%d\n", myhostname
, ntohs(ds_server
.sin_port
));
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
);;
ui
->output("connection from %s:%d\n", 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
);
#define POLL_TIMEOUT -1 /* wait forever ? FIXME ? */
fds
.events
= POLLIN
|POLLPRI
;
#if HOST_OS_SOLARIS9 /* { */
fds
.events
|= POLLRDNORM
|POLLRDBAND
;
res
= poll(&fds
, 1, POLL_TIMEOUT
);
#if HOST_OS_SOLARIS9 /* { */
if (fds
.revents
& POLLIN
) {
res
= read(dsp
->tty_skt
, buf
, sizeof (buf
));
/* 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
);
// assert(dsp->tty_skt = -1);
DBGX( dump_buf("input bytes:", buf
, res
); );
ds_insert_bytes(dsp
, buf
, res
);
return (void*)0; /* compiler joy */
void dump_buf(char * strp
, uint8_t * bufp
, int len
)
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
] : '.');
/* emits string and newline */
ds_output(ds_state_t
*dsp
, const char *str
)
if (dsp
->uses_term
&& dsp
->tty_attached
) {
(void) write(dsp
->tty_skt
, str
, strlen(str
));
(void) write(dsp
->tty_skt
, "\n", 1);
/* 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 /* { */
ds_domain_reset(ds_state_t
* dsp
)
* 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
++) {
cp
= LIST_ENTRY(dp
->procs
, i
);
cp
->proc_typep
->ext_signal(cp
, ES_Reset
);
ds_insert_bytes(ds_state_t
*dsp
, uint8_t *bufp
, int len
)
pthread_mutex_lock(&dsp
->tty_lock
);
* process all characters in buffer, don't check for overrun
for (i
= 0; i
< len
; 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 */
dsp
->tilde_state
= TE_SawCR
;
case TE_SawCR
: /* waiting for tilde */
dsp
->tilde_state
= TE_SawTilde
;
if (c
!= '\n') dsp
->tilde_state
= TE_Normal
;
case TE_SawTilde
: /* tilde command */
dsp
->tilde_state
= TE_SawCR
;
case '~': /* emit single tilde */
dsp
->tilde_state
= TE_Normal
;
dsp
->line_status
|= DS_LSTAT_BREAK
;
/* allow another ~ cmd without CR */
ds_output(dsp
, "dumb_serial tilde escapes:");
ds_output(dsp
, "\t# BREAK");
ds_output(dsp
, "\t~ generate tilde");
#if CONSOLE_RESET /* { */
ds_output(dsp
, "\tx external reset for CPUs in same domain");
ds_output(dsp
, "\ti dump I-TLB contents for CPUs in same domain");
ds_output(dsp
, "\td dump D-TLB contents for CPUs in same domain");
ds_output(dsp
, "\tb toggle the debug output enable bits");
ds_output(dsp
, "\t? this message");
/* allow another ~ cmd without CR */
#if CONSOLE_RESET /* { */
case 'x': /* Hack to enable extern chip reset easily */
ds_output(dsp
, "Resetting cpu(s) in domain");
goto skip
; /* swallow 'x' character after sending reset */
// ds_dump_tlb(dsp, false);
// ds_dump_tlb(dsp, true);
default: /* eat current char */
* FIXME could emit ~ and current char
ds_output(dsp
, "~? for tilde help");
if (dsp
->in
.count
< dsp
->in
.size
) {
dsp
->in
.bufp
[dsp
->in
.tail
] = bufp
[i
];
if (dsp
->in
.tail
>= dsp
->in
.size
)
dsp
->line_status
|= DS_LSTAT_DATA_READY
;
if (!(dsp
->line_status
& DS_LSTAT_OVERRUN
))
fprintf(stderr
, "dumbserial: buffer overrun");
dsp
->line_status
|= DS_LSTAT_OVERRUN
;
pthread_mutex_unlock(&dsp
->tty_lock
);
/* 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 is_dtlb
)
extern void niagara_dump_tlbs(config_proc_t
* cp
, bool is_dtlb
);
* FIXME: this is a hack ...
* walk the procs in my domain, and dump the tlb contents
dp
= dsp
->config_devp
->domainp
;
for (i
=0; i
<dp
->procs
.count
; i
++) {
cp
= LIST_ENTRY(dp
->procs
, i
);
niagara_dump_tlbs(cp
, is_dtlb
); /* FIXME */
extern "C" void dumbserial_create_instance (const char *modname
, const char *instance_name
)
ds_state_t
* dsp
= new ds_state_t
;
dsp
->in
.size
= DEFAULT_RXFIFOSZ
;
dsp
->reg_shift
= DS_REG_SHIFT_DEF
;
dsp
->term_strp
= (char *)malloc(256);
mmi_instance_t instance
= mmi_register_instance(modname
, instance_name
, (void *) dsp
, "dumbserial model");
// parse startpa, endpa, type values for this instance
// syntax: sysconf dumbserial <name> type=<type> startpa=<pa> endpa=<pa>
// where <type> is either HYPERVISOR or GUEST
// syntax example: sysconf dumbserial ds0 type=HYPERVISOR startpa=0xfff0c2c000 endpa=0xfff0c2cfff
int argc
= mmi_argc(instance
);
char * arg
= strdup(mmi_argv(instance
, i
));
char * lv
= strtok_r(arg
, "=", &marker
);
if (strcmp(lv
, "startpa") == 0) {
char * rv
= strtok_r(NULL
, "=", &marker
);
dsp
->startpa
= strtoull(rv
, NULL
, 0);
perror("dumbserial: error parsing startpa");
} else if (strcmp(lv
, "endpa") == 0) {
char * rv
= strtok_r(NULL
, "=", &marker
);
dsp
->endpa
= strtoull(rv
, NULL
, 0);
perror("dumbserial: error parsing startpa");
} else if (strcmp(lv
, "type") == 0) {
char * rv
= strtok_r(NULL
, "=", &marker
);
if (strcmp(dsp
->type
, "HYPERVISOR") == 0) {
strcpy(dsp
->term_strp
, "xterm -l -lf hypervisor1.log -bg black -fg green -geometry 80x20+170+300 -T 'Hypervisor Console' -e ./netcons %s %d &");
// dumbserials[HV_DEVICE] = dsp;
} else if (strcmp(dsp
->type
, "GUEST") == 0) {
strcpy(dsp
->term_strp
, "xterm -l -lf guest1.log -bg black -fg green -geometry 80x35+200+30 -T 'Guest Console' -e ./netcons %s %d &");
fprintf(stderr
, "DUMBSERIAL: invalid type in sysconf directive (%s) - must be HYPERVISOR or GUEST\n", dsp
->type
);
// Parses memory information
// register IO operations
uint64_t sz
= dsp
->endpa
- dsp
->startpa
+ 1;
ui
->output ("dumbserial (%s): register LD/ST access @PA=0x%llx size=0x%llx\n", instance_name
, dsp
->startpa
, sz
);
if (mmi_map_physio(dsp
->startpa
, sz
, (void *) dsp
, dumbserial_physio_access
)) {
fprintf (stderr
, "dumbserial (%s): unable to register IO interceptor @0x%llx size 0x%llx\n", instance_name
, dsp
->startpa
, sz
);
extern "C" void _init () {
if (! mmi_register_instance_creator ("dumbserial", dumbserial_create_instance
)) {
fprintf (stderr
, "Cannot register instance creator for <%s> \n", "dumbserial");
/////////////////////////////////////////////////