Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / common / include / console.h
/*
* ========== Copyright Header Begin ==========================================
*
* OpenSPARC T2 Processor File: console.h
* 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 ============================================
*/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stropts.h>
#include <sys/time.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/select.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
#include <map>
#include <libgen.h>
extern "C"{
void * callTermThread(void * );
}
class consoleInterface{
public:
typedef void (*inputCallbk)(void * data, unsigned char *c, int portNum);
// registerd call back function, called whenever there is an i/p avaliable on terminal
// 'data' is the callback data registered by a client.
// 'c' points to a null terminated string of i/p characters
// 'portNum' is the unique handle associated with the terminal (return value of getTerminal()).
virtual int getTerminal(char * title, int numScrolLines, inputCallbk cbFun, void * cbData, char ** tty_name, bool pop_win = true) = 0;
// get a new terminal window if pop_win is true with
// title 'title'
// scroll lines = 'numScrolLines'
// if pop_win is false its assume the user will call 'tip' itself.
// Registers a callback function 'cbFun' which is called whenever there is an input
// available on the terminal. 'data' is any user supplied callback data.
// The call returns a unique handle(portNumber) with which, a client can identify a terminal.
// The string representation is returned in tty_name (space for string is allocated).
virtual void writeTerminal(int portNumber, unsigned char *c, int length = 0) = 0;
// write null terminated string 'c' on terminal with handle 'portNumber'.
// if the string is not null terminated then the length arguement can be provided.
// no error checking is done for the validity of portNumber. Client
// must make sure, portNumber is valid and obtained through an earlier
// call to getTerminal.
virtual void log(int portNumber, bool enable, char * fileName = 0, bool raw = false, bool append = true) = 0;
// log console i/p, o/p for console associated with portNumber in
// fileName. If file exist, open in append mode,
// else create it. The file is expected to be written in current
// directory.
// if 'enable' is false, disable logging
// 'raw' determines if the log file o/p is generated w/o any '\b'
// processing and is unbuffered. The default is buffered.
// 'append' if true causes file to open in append mode, else the file is
// truncated if it exists
// dump/restore the contents of console in/from a file
virtual void dump(int portNumber, FILE *fp) = 0;
virtual void restore(int portNumber, FILE * fp) = 0;
virtual void setId(const char * id) = 0;
// set a string 'id' to display in debug/error messages
// default 'id' is "console"
// set font, background/foreground color of xterm window
virtual void setFont(const char *) = 0;
virtual void setBg(const char *) = 0;
virtual void setFg(const char *) = 0;
// set command to execute in xterm (/bin/tip is for example)
virtual void setExec(const char *) = 0;
// open an xterm window for handle 'portNum' if not already opened
virtual bool pop_term(int portNum) = 0;
// kill the xterm window for handle 'portNum' if opened
virtual void kill_term(int portNum) = 0;
};
// terminal class. typical usage
// The system would declare a global console object.
// Clients can call getTerminal() method of this object to
// obtain 1 or more terminals. Each terminal will have a
// unique handle. On receiving input on any terminal, a user
// defined client function is called with the i/p characters.
// The client can then decide what to do with the data. The
// client uses function writeTerminal() to write characters
// to a terminal with handle portNumber.
extern "C"{
void * callTermThread(void *);
}
using namespace std;
class console:public consoleInterface{
private:
struct term {
int port_fd;
int mport_fd;
char * tty_name;
inputCallbk cbFun;
void * cbData;
int logFd;
const char * logFile;
bool logEnable;
const char * marker;
bool raw;
int xterm_pid;
// 64 kb circular buffer to dump the contents of console
static const int dumpBufSize = 64 * 1024;
uint8_t dumpBuf[dumpBufSize];
int dumpIndex;
int dumpSize;
term(){
logFd = -1;
logFile = 0;
logEnable = false;
dumpIndex = 0;
dumpSize = 0;
xterm_pid = -1;
marker = strdup("\n\
*************************************************************\n\
********************START NEW CONSOLE LOG********************\n\
*************************************************************\n");
bufferIndex = 0;
raw = false;
}
static const int maxLineSize = 1024;
uint8_t lineBuffer[maxLineSize];
int bufferIndex;
bool pop_term(char * display, char * fn, char * bg, char * fg, \
char * title, int numScrolLines, char * exec ){
int wait_stat;
if( (xterm_pid != -1) && \
(waitpid(xterm_pid, &wait_stat, WNOHANG) == xterm_pid) ){
// the xterm window no longer exists
xterm_pid = -1;
}
if(xterm_pid != -1){
fprintf(stderr," warning: xterm already opened \n");
return false;
}
char numBuf[128];
sprintf(numBuf,"%d",numScrolLines);
if ((xterm_pid = vfork()) == 0) {
const char* xterm = "/usr/openwin/bin/xterm";
const char* sam = getexecname();
// Do we expect getexecname() to fail ... geeesh that would be
// a disaster if $0 is blank. In case it is just assume sam.
if (!sam) sam = "./sam";
// Expect that sam was executed from our release. So expect
// $sam_dir/bin/sam and grab $sam_dir. If not then things have
// been moved around ... well though.
char* origin = dirname(strdup(sam));
char* sam_dir = dirname(strdup(origin));
// Check the exec argument and sub $SAM with the sam_dir variable.
if (strncmp(exec,"$SAM/",5) == 0)
{
char* new_exec = (char*)malloc(strlen(&exec[4]) + 1 + strlen(sam_dir));
sprintf(new_exec,"%s%s",sam_dir,&exec[4]);
free(exec);
exec = new_exec;
}
execl(xterm, xterm, "-display", display,\
"-fn", fn, "-bg", bg, "-fg", fg, "-rw","-vb", \
"-T", title, "-sb", "-sl", numBuf, "-e", exec, tty_name, (char*)0);
perror("execl /usr/openwin/bin/xterm:");
exit(1);
}else if(xterm_pid == -1)
return false;
else{
sleep(1);
return true;
}
}
void kill_term(){
int wait_stat;
if( (xterm_pid != -1) && \
(waitpid(xterm_pid, &wait_stat, WNOHANG) == xterm_pid) ){
// the xterm window no longer exists
xterm_pid = -1;
}
if(xterm_pid != -1){
kill(xterm_pid, SIGKILL);
xterm_pid = -1;
}else
fprintf(stderr," warning: xterm window already closed\n");
}
void log2File(uint8_t *c, int length){
if(!logEnable)
return;
if(*c == 0xd || *c == '\0')
return;
// unbuffered, at times gross file logging
if(raw){
if(write(logFd,c,length) != length)
goto error;
return;
}
// pretty file logging
for(int i = 0; i < length; i++){
if(c[i] == '\n'){
lineBuffer[bufferIndex++] = c[i];
if(write(logFd,lineBuffer,bufferIndex) != bufferIndex)
goto error;
bufferIndex = 0;
}else if(c[i] == '\b'){
if(bufferIndex)
bufferIndex--;
}else if(isprint(c[i])){
lineBuffer[bufferIndex++] = c[i];
if(bufferIndex == maxLineSize){
if(write(logFd,lineBuffer,bufferIndex) != bufferIndex)
goto error;
bufferIndex = 0;
}
}
}
return;
error:
bufferIndex = 0;
logEnable = false;
fprintf(stderr,"write failed for log file %s\n",logFile);
perror(logFile);
return;
}
void log(bool enable, char * fileName, bool raw,bool append){
if(!enable){
logEnable = false;
return;
}
if(logFd != -1)
close(logFd);
if(append)
logFd = open(fileName,O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
else
logFd = open(fileName,O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(logFd == -1){
fprintf(stderr,"logfile %s open failed\n",fileName);
perror(fileName);
return;
}
lseek(logFd,0,SEEK_END);
logEnable = true;
logFile = strdup(fileName);
write(logFd,marker,strlen(marker));
this->raw = raw;
return;
}
};
map<int , term*> termMap;
map<int , term*>::iterator termMapIter;
int currentPortNum;
pthread_mutex_t termMutex;
pthread_t selectThread;
char * display;
void term_read_ports(fd_set readfds);
bool get_pty(char **name, int *sfd, int *mfd);
char * id;
char * fn; // font
char * bg; // back ground color
char * fg; // foreground color
char * exec; // xterm -e <exec> <pty>
char * title;
int term_lines;
public:
console() : title(NULL) {
char * dis = getenv("DISPLAY");
if(dis)
display = strdup(dis);
else{
display = strdup("unknown");
}
currentPortNum = 0;
pthread_mutex_init(&termMutex,0);
assert(pthread_create(&selectThread,0, callTermThread,(void*)this) == 0);
id = strdup("console");
fn = strdup("-dec-terminal-medium-r-normal-*-14-140-*-75-c-80-iso8859-1");
bg = strdup("grey90");
fg = strdup("black");
exec = strdup("/bin/tip");
}
//public functions for console class
void writeTerminal(int portNumber, unsigned char *c, int length){
//no error checking done here.
term * t = termMap[portNumber];
if(!length)
write(t->mport_fd,c, strlen((char*)c));
else
write(t->mport_fd,c, length);
t->log2File(c,length == 0 ? strlen((char*)c):length);
t->dumpBuf[t->dumpIndex++] = *c;
t->dumpIndex %= term::dumpBufSize;
t->dumpSize++;
return;
}
int getTerminal(char * title, int numScrolLines, inputCallbk cbFun, void * cbData, char ** tty_name, bool pop_win);
void term_select();
void log(int portNum, bool enable, char * fileName, bool raw = false, bool append = true){
assert(portNum < currentPortNum);
if(fileName == 0){
char buf[1024];
sprintf(buf,"console_log_%d",portNum);
fileName = strdup(buf);
}
termMap[portNum]->log(enable,fileName,raw,append);
return;
}
void setId(const char * n){
free(id);
id = strdup(n);
}
void setTitle(const char * s){
free(title);
title = strdup(s);
}
void setFont(const char * s){
free(fn);
fn = strdup(s);
}
void setBg(const char * s){
free(bg);
bg = strdup(s);
}
void setFg(const char * s){
free(fg);
fg = strdup(s);
}
void setExec(const char * s){
free(exec);
exec = strdup(s);
}
void dump(int portNumber, FILE * fp){
bool wrap = false;
struct term * t = termMap[portNumber];
if(t->dumpSize >= t->dumpBufSize)
wrap = true;
if(wrap)
fwrite(&(t->dumpBuf[t->dumpIndex]), t->dumpBufSize - t->dumpIndex, 1, fp);
fwrite(t->dumpBuf, t->dumpIndex, 1, fp );
fflush(fp);
}
void restore(int portNumber, FILE * fp){
int k;
if(termMap[portNumber]->xterm_pid == -1){
fprintf(stderr,"no xterm window popped, console not restored\n");
return;
}
bool is_logging = termMap[portNumber]->logEnable;
termMap[portNumber]->logEnable = false;
while( (k = fgetc(fp)) != EOF){
uint8_t c = k;
writeTerminal(portNumber,&c,1);
}
termMap[portNumber]->logEnable = is_logging;
}
bool pop_term(int portNum){
if(!termMap[portNum]->pop_term(display, fn, bg, fg, title,
term_lines,exec)){
fprintf(stderr," error: failed to open xterm\n");
return false;
}
bool wrap = false;
struct term * t = termMap[portNum];
timespec ts;
ts.tv_sec = 0; ts.tv_nsec=0;
if(t->dumpSize >= t->dumpBufSize)
wrap = true;
if(wrap)
for(int i = 0; i < (t->dumpBufSize - t->dumpIndex); i++ ){
write(t->mport_fd,t->dumpBuf + t->dumpIndex + i, 1);
nanosleep(&ts,0);
}
for(int i = 0; i < t->dumpIndex; i++){
write(t->mport_fd,t->dumpBuf + i, 1);
nanosleep(&ts,0);
}
return true;
}
void kill_term(int portNum){
termMap[portNum]->kill_term();
return;
}
};