terminal.cc revision 12239
112239Sandreas.sandberg@arm.com/* 212239Sandreas.sandberg@arm.com * Copyright (c) 2001-2005 The Regents of The University of Michigan 312239Sandreas.sandberg@arm.com * All rights reserved. 412239Sandreas.sandberg@arm.com * 512239Sandreas.sandberg@arm.com * Redistribution and use in source and binary forms, with or without 612239Sandreas.sandberg@arm.com * modification, are permitted provided that the following conditions are 712239Sandreas.sandberg@arm.com * met: redistributions of source code must retain the above copyright 812239Sandreas.sandberg@arm.com * notice, this list of conditions and the following disclaimer; 912239Sandreas.sandberg@arm.com * redistributions in binary form must reproduce the above copyright 1012239Sandreas.sandberg@arm.com * notice, this list of conditions and the following disclaimer in the 1112239Sandreas.sandberg@arm.com * documentation and/or other materials provided with the distribution; 1212239Sandreas.sandberg@arm.com * neither the name of the copyright holders nor the names of its 1312239Sandreas.sandberg@arm.com * contributors may be used to endorse or promote products derived from 1412239Sandreas.sandberg@arm.com * this software without specific prior written permission. 1512239Sandreas.sandberg@arm.com * 1612239Sandreas.sandberg@arm.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1712239Sandreas.sandberg@arm.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1812239Sandreas.sandberg@arm.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1912239Sandreas.sandberg@arm.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2012239Sandreas.sandberg@arm.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2112239Sandreas.sandberg@arm.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2212239Sandreas.sandberg@arm.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2312239Sandreas.sandberg@arm.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2412239Sandreas.sandberg@arm.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2512239Sandreas.sandberg@arm.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 2612239Sandreas.sandberg@arm.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2712239Sandreas.sandberg@arm.com * 2812239Sandreas.sandberg@arm.com * Authors: Nathan Binkert 2912239Sandreas.sandberg@arm.com * Ali Saidi 3012239Sandreas.sandberg@arm.com */ 3112239Sandreas.sandberg@arm.com 3212239Sandreas.sandberg@arm.com/* @file 3312239Sandreas.sandberg@arm.com * Implements the user interface to a serial terminal 3412239Sandreas.sandberg@arm.com */ 3512239Sandreas.sandberg@arm.com 3612239Sandreas.sandberg@arm.com#include <sys/ioctl.h> 3712239Sandreas.sandberg@arm.com 3812239Sandreas.sandberg@arm.com#if defined(__FreeBSD__) 3912239Sandreas.sandberg@arm.com#include <termios.h> 4012239Sandreas.sandberg@arm.com 4112239Sandreas.sandberg@arm.com#else 4212239Sandreas.sandberg@arm.com#include <sys/termios.h> 4312239Sandreas.sandberg@arm.com 4412239Sandreas.sandberg@arm.com#endif 4512239Sandreas.sandberg@arm.com#include "dev/serial/terminal.hh" 4612239Sandreas.sandberg@arm.com 4712239Sandreas.sandberg@arm.com#include <poll.h> 4812239Sandreas.sandberg@arm.com#include <unistd.h> 4912239Sandreas.sandberg@arm.com 5012239Sandreas.sandberg@arm.com#include <cctype> 5112239Sandreas.sandberg@arm.com#include <cerrno> 5212239Sandreas.sandberg@arm.com#include <fstream> 5312239Sandreas.sandberg@arm.com#include <iostream> 5412239Sandreas.sandberg@arm.com#include <sstream> 5512239Sandreas.sandberg@arm.com#include <string> 5612239Sandreas.sandberg@arm.com 5712239Sandreas.sandberg@arm.com#include "base/atomicio.hh" 5812239Sandreas.sandberg@arm.com#include "base/misc.hh" 5912239Sandreas.sandberg@arm.com#include "base/output.hh" 6012239Sandreas.sandberg@arm.com#include "base/socket.hh" 6112239Sandreas.sandberg@arm.com#include "base/trace.hh" 6212239Sandreas.sandberg@arm.com#include "debug/Terminal.hh" 6312239Sandreas.sandberg@arm.com#include "debug/TerminalVerbose.hh" 6412239Sandreas.sandberg@arm.com#include "dev/platform.hh" 6512239Sandreas.sandberg@arm.com#include "dev/serial/uart.hh" 6612239Sandreas.sandberg@arm.com 6712239Sandreas.sandberg@arm.comusing namespace std; 6812239Sandreas.sandberg@arm.com 6912239Sandreas.sandberg@arm.com 7012239Sandreas.sandberg@arm.com/* 7112239Sandreas.sandberg@arm.com * Poll event for the listen socket 7212239Sandreas.sandberg@arm.com */ 7312239Sandreas.sandberg@arm.comTerminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e) 7412239Sandreas.sandberg@arm.com : PollEvent(fd, e), term(t) 7512239Sandreas.sandberg@arm.com{ 7612239Sandreas.sandberg@arm.com} 7712239Sandreas.sandberg@arm.com 7812239Sandreas.sandberg@arm.comvoid 7912239Sandreas.sandberg@arm.comTerminal::ListenEvent::process(int revent) 8012239Sandreas.sandberg@arm.com{ 8112239Sandreas.sandberg@arm.com term->accept(); 8212239Sandreas.sandberg@arm.com} 8312239Sandreas.sandberg@arm.com 8412239Sandreas.sandberg@arm.com/* 8512239Sandreas.sandberg@arm.com * Poll event for the data socket 8612239Sandreas.sandberg@arm.com */ 8712239Sandreas.sandberg@arm.comTerminal::DataEvent::DataEvent(Terminal *t, int fd, int e) 8812239Sandreas.sandberg@arm.com : PollEvent(fd, e), term(t) 8912239Sandreas.sandberg@arm.com{ 9012239Sandreas.sandberg@arm.com} 9112239Sandreas.sandberg@arm.com 9212239Sandreas.sandberg@arm.comvoid 9312239Sandreas.sandberg@arm.comTerminal::DataEvent::process(int revent) 9412239Sandreas.sandberg@arm.com{ 9512239Sandreas.sandberg@arm.com // As a consequence of being called from the PollQueue, we might 9612239Sandreas.sandberg@arm.com // have been called from a different thread. Migrate to "our" 9712239Sandreas.sandberg@arm.com // thread. 9812239Sandreas.sandberg@arm.com EventQueue::ScopedMigration migrate(term->eventQueue()); 9912239Sandreas.sandberg@arm.com 10012239Sandreas.sandberg@arm.com if (revent & POLLIN) 10112239Sandreas.sandberg@arm.com term->data(); 10212239Sandreas.sandberg@arm.com else if (revent & POLLNVAL) 10312239Sandreas.sandberg@arm.com term->detach(); 10412239Sandreas.sandberg@arm.com} 10512239Sandreas.sandberg@arm.com 10612239Sandreas.sandberg@arm.com/* 10712239Sandreas.sandberg@arm.com * Terminal code 10812239Sandreas.sandberg@arm.com */ 10912239Sandreas.sandberg@arm.comTerminal::Terminal(const Params *p) 11012239Sandreas.sandberg@arm.com : SerialDevice(p), listenEvent(NULL), dataEvent(NULL), 11112239Sandreas.sandberg@arm.com number(p->number), data_fd(-1), txbuf(16384), rxbuf(16384), 11212239Sandreas.sandberg@arm.com outfile(p->output ? simout.findOrCreate(p->name) : NULL) 11312239Sandreas.sandberg@arm.com#if TRACING_ON == 1 11412239Sandreas.sandberg@arm.com , linebuf(16384) 11512239Sandreas.sandberg@arm.com#endif 11612239Sandreas.sandberg@arm.com{ 11712239Sandreas.sandberg@arm.com if (outfile) 11812239Sandreas.sandberg@arm.com outfile->stream()->setf(ios::unitbuf); 11912239Sandreas.sandberg@arm.com 12012239Sandreas.sandberg@arm.com if (p->port) 12112239Sandreas.sandberg@arm.com listen(p->port); 12212239Sandreas.sandberg@arm.com} 12312239Sandreas.sandberg@arm.com 12412239Sandreas.sandberg@arm.comTerminal::~Terminal() 12512239Sandreas.sandberg@arm.com{ 12612239Sandreas.sandberg@arm.com if (data_fd != -1) 12712239Sandreas.sandberg@arm.com ::close(data_fd); 12812239Sandreas.sandberg@arm.com 12912239Sandreas.sandberg@arm.com if (listenEvent) 13012239Sandreas.sandberg@arm.com delete listenEvent; 13112239Sandreas.sandberg@arm.com 13212239Sandreas.sandberg@arm.com if (dataEvent) 13312239Sandreas.sandberg@arm.com delete dataEvent; 13412239Sandreas.sandberg@arm.com} 13512239Sandreas.sandberg@arm.com 13612239Sandreas.sandberg@arm.com 13712239Sandreas.sandberg@arm.com/////////////////////////////////////////////////////////////////////// 13812239Sandreas.sandberg@arm.com// socket creation and terminal attach 13912239Sandreas.sandberg@arm.com// 14012239Sandreas.sandberg@arm.com 14112239Sandreas.sandberg@arm.comvoid 14212239Sandreas.sandberg@arm.comTerminal::listen(int port) 14312239Sandreas.sandberg@arm.com{ 14412239Sandreas.sandberg@arm.com if (ListenSocket::allDisabled()) { 14512239Sandreas.sandberg@arm.com warn_once("Sockets disabled, not accepting terminal connections"); 14612239Sandreas.sandberg@arm.com return; 14712239Sandreas.sandberg@arm.com } 14812239Sandreas.sandberg@arm.com 14912239Sandreas.sandberg@arm.com while (!listener.listen(port, true)) { 15012239Sandreas.sandberg@arm.com DPRINTF(Terminal, 15112239Sandreas.sandberg@arm.com ": can't bind address terminal port %d inuse PID %d\n", 15212239Sandreas.sandberg@arm.com port, getpid()); 15312239Sandreas.sandberg@arm.com port++; 15412239Sandreas.sandberg@arm.com } 15512239Sandreas.sandberg@arm.com 15612239Sandreas.sandberg@arm.com int p1, p2; 15712239Sandreas.sandberg@arm.com p2 = name().rfind('.') - 1; 15812239Sandreas.sandberg@arm.com p1 = name().rfind('.', p2); 15912239Sandreas.sandberg@arm.com ccprintf(cerr, "Listening for %s connection on port %d\n", 16012239Sandreas.sandberg@arm.com name().substr(p1+1,p2-p1), port); 16112239Sandreas.sandberg@arm.com 16212239Sandreas.sandberg@arm.com listenEvent = new ListenEvent(this, listener.getfd(), POLLIN); 16312239Sandreas.sandberg@arm.com pollQueue.schedule(listenEvent); 16412239Sandreas.sandberg@arm.com} 16512239Sandreas.sandberg@arm.com 16612239Sandreas.sandberg@arm.comvoid 16712239Sandreas.sandberg@arm.comTerminal::accept() 16812239Sandreas.sandberg@arm.com{ 16912239Sandreas.sandberg@arm.com if (!listener.islistening()) 17012239Sandreas.sandberg@arm.com panic("%s: cannot accept a connection if not listening!", name()); 17112239Sandreas.sandberg@arm.com 17212239Sandreas.sandberg@arm.com int fd = listener.accept(true); 17312239Sandreas.sandberg@arm.com if (data_fd != -1) { 17412239Sandreas.sandberg@arm.com char message[] = "terminal already attached!\n"; 17512239Sandreas.sandberg@arm.com atomic_write(fd, message, sizeof(message)); 17612239Sandreas.sandberg@arm.com ::close(fd); 17712239Sandreas.sandberg@arm.com return; 17812239Sandreas.sandberg@arm.com } 17912239Sandreas.sandberg@arm.com 18012239Sandreas.sandberg@arm.com data_fd = fd; 18112239Sandreas.sandberg@arm.com dataEvent = new DataEvent(this, data_fd, POLLIN); 18212239Sandreas.sandberg@arm.com pollQueue.schedule(dataEvent); 18312239Sandreas.sandberg@arm.com 18412239Sandreas.sandberg@arm.com stringstream stream; 18512239Sandreas.sandberg@arm.com ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number); 18612239Sandreas.sandberg@arm.com 18712239Sandreas.sandberg@arm.com // we need an actual carriage return followed by a newline for the 18812239Sandreas.sandberg@arm.com // terminal 18912239Sandreas.sandberg@arm.com stream << "\r\n"; 19012239Sandreas.sandberg@arm.com 19112239Sandreas.sandberg@arm.com write((const uint8_t *)stream.str().c_str(), stream.str().size()); 19212239Sandreas.sandberg@arm.com 19312239Sandreas.sandberg@arm.com DPRINTFN("attach terminal %d\n", number); 19412239Sandreas.sandberg@arm.com char buf[1024]; 19512239Sandreas.sandberg@arm.com for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) { 19612239Sandreas.sandberg@arm.com const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf))); 19712239Sandreas.sandberg@arm.com txbuf.peek(buf, i, chunk_len); 19812239Sandreas.sandberg@arm.com write((const uint8_t *)buf, chunk_len); 19912239Sandreas.sandberg@arm.com } 20012239Sandreas.sandberg@arm.com} 20112239Sandreas.sandberg@arm.com 20212239Sandreas.sandberg@arm.comvoid 20312239Sandreas.sandberg@arm.comTerminal::detach() 20412239Sandreas.sandberg@arm.com{ 20512239Sandreas.sandberg@arm.com if (data_fd != -1) { 20612239Sandreas.sandberg@arm.com ::close(data_fd); 20712239Sandreas.sandberg@arm.com data_fd = -1; 20812239Sandreas.sandberg@arm.com } 20912239Sandreas.sandberg@arm.com 21012239Sandreas.sandberg@arm.com pollQueue.remove(dataEvent); 21112239Sandreas.sandberg@arm.com delete dataEvent; 21212239Sandreas.sandberg@arm.com dataEvent = NULL; 21312239Sandreas.sandberg@arm.com 21412239Sandreas.sandberg@arm.com DPRINTFN("detach terminal %d\n", number); 21512239Sandreas.sandberg@arm.com} 21612239Sandreas.sandberg@arm.com 21712239Sandreas.sandberg@arm.comvoid 21812239Sandreas.sandberg@arm.comTerminal::data() 21912239Sandreas.sandberg@arm.com{ 22012239Sandreas.sandberg@arm.com uint8_t buf[1024]; 22112239Sandreas.sandberg@arm.com int len; 22212239Sandreas.sandberg@arm.com 22312239Sandreas.sandberg@arm.com len = read(buf, sizeof(buf)); 22412239Sandreas.sandberg@arm.com if (len) { 22512239Sandreas.sandberg@arm.com rxbuf.write((char *)buf, len); 22612239Sandreas.sandberg@arm.com notifyInterface(); 22712239Sandreas.sandberg@arm.com } 22812239Sandreas.sandberg@arm.com} 22912239Sandreas.sandberg@arm.com 23012239Sandreas.sandberg@arm.comsize_t 23112239Sandreas.sandberg@arm.comTerminal::read(uint8_t *buf, size_t len) 23212239Sandreas.sandberg@arm.com{ 23312239Sandreas.sandberg@arm.com if (data_fd < 0) 23412239Sandreas.sandberg@arm.com panic("Terminal not properly attached.\n"); 23512239Sandreas.sandberg@arm.com 23612239Sandreas.sandberg@arm.com ssize_t ret; 23712239Sandreas.sandberg@arm.com do { 23812239Sandreas.sandberg@arm.com ret = ::read(data_fd, buf, len); 23912239Sandreas.sandberg@arm.com } while (ret == -1 && errno == EINTR); 24012239Sandreas.sandberg@arm.com 24112239Sandreas.sandberg@arm.com 24212239Sandreas.sandberg@arm.com if (ret < 0) 24312239Sandreas.sandberg@arm.com DPRINTFN("Read failed.\n"); 24412239Sandreas.sandberg@arm.com 24512239Sandreas.sandberg@arm.com if (ret <= 0) { 24612239Sandreas.sandberg@arm.com detach(); 24712239Sandreas.sandberg@arm.com return 0; 24812239Sandreas.sandberg@arm.com } 24912239Sandreas.sandberg@arm.com 25012239Sandreas.sandberg@arm.com return ret; 25112239Sandreas.sandberg@arm.com} 25212239Sandreas.sandberg@arm.com 25312239Sandreas.sandberg@arm.com// Terminal output. 25412239Sandreas.sandberg@arm.comsize_t 25512239Sandreas.sandberg@arm.comTerminal::write(const uint8_t *buf, size_t len) 25612239Sandreas.sandberg@arm.com{ 25712239Sandreas.sandberg@arm.com if (data_fd < 0) 25812239Sandreas.sandberg@arm.com panic("Terminal not properly attached.\n"); 25912239Sandreas.sandberg@arm.com 26012239Sandreas.sandberg@arm.com ssize_t ret = atomic_write(data_fd, buf, len); 26112239Sandreas.sandberg@arm.com if (ret < len) 26212239Sandreas.sandberg@arm.com detach(); 26312239Sandreas.sandberg@arm.com 26412239Sandreas.sandberg@arm.com return ret; 26512239Sandreas.sandberg@arm.com} 26612239Sandreas.sandberg@arm.com 26712239Sandreas.sandberg@arm.com#define MORE_PENDING (ULL(1) << 61) 26812239Sandreas.sandberg@arm.com#define RECEIVE_SUCCESS (ULL(0) << 62) 26912239Sandreas.sandberg@arm.com#define RECEIVE_NONE (ULL(2) << 62) 27012239Sandreas.sandberg@arm.com#define RECEIVE_ERROR (ULL(3) << 62) 27112239Sandreas.sandberg@arm.com 27212239Sandreas.sandberg@arm.comuint8_t 27312239Sandreas.sandberg@arm.comTerminal::readData() 27412239Sandreas.sandberg@arm.com{ 27512239Sandreas.sandberg@arm.com uint8_t c; 27612239Sandreas.sandberg@arm.com 27712239Sandreas.sandberg@arm.com assert(!rxbuf.empty()); 27812239Sandreas.sandberg@arm.com rxbuf.read((char *)&c, 1); 27912239Sandreas.sandberg@arm.com 28012239Sandreas.sandberg@arm.com DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n", 28112239Sandreas.sandberg@arm.com isprint(c) ? c : ' ', c, !rxbuf.empty()); 28212239Sandreas.sandberg@arm.com 28312239Sandreas.sandberg@arm.com return c; 28412239Sandreas.sandberg@arm.com} 28512239Sandreas.sandberg@arm.com 28612239Sandreas.sandberg@arm.comuint64_t 28712239Sandreas.sandberg@arm.comTerminal::console_in() 28812239Sandreas.sandberg@arm.com{ 28912239Sandreas.sandberg@arm.com uint64_t value; 29012239Sandreas.sandberg@arm.com 29112239Sandreas.sandberg@arm.com if (dataAvailable()) { 29212239Sandreas.sandberg@arm.com value = RECEIVE_SUCCESS | readData(); 29312239Sandreas.sandberg@arm.com if (!rxbuf.empty()) 29412239Sandreas.sandberg@arm.com value |= MORE_PENDING; 29512239Sandreas.sandberg@arm.com } else { 29612239Sandreas.sandberg@arm.com value = RECEIVE_NONE; 29712239Sandreas.sandberg@arm.com } 29812239Sandreas.sandberg@arm.com 29912239Sandreas.sandberg@arm.com DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value); 30012239Sandreas.sandberg@arm.com 30112239Sandreas.sandberg@arm.com return value; 30212239Sandreas.sandberg@arm.com} 30312239Sandreas.sandberg@arm.com 30412239Sandreas.sandberg@arm.comvoid 30512239Sandreas.sandberg@arm.comTerminal::writeData(uint8_t c) 30612239Sandreas.sandberg@arm.com{ 30712239Sandreas.sandberg@arm.com#if TRACING_ON == 1 30812239Sandreas.sandberg@arm.com if (DTRACE(Terminal)) { 30912239Sandreas.sandberg@arm.com static char last = '\0'; 31012239Sandreas.sandberg@arm.com 31112239Sandreas.sandberg@arm.com if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) { 31212239Sandreas.sandberg@arm.com if (c == '\n' || c == '\r') { 31312239Sandreas.sandberg@arm.com int size = linebuf.size(); 31412239Sandreas.sandberg@arm.com char *buffer = new char[size + 1]; 31512239Sandreas.sandberg@arm.com linebuf.read(buffer, size); 31612239Sandreas.sandberg@arm.com buffer[size] = '\0'; 31712239Sandreas.sandberg@arm.com DPRINTF(Terminal, "%s\n", buffer); 31812239Sandreas.sandberg@arm.com delete [] buffer; 31912239Sandreas.sandberg@arm.com } else { 32012239Sandreas.sandberg@arm.com linebuf.write(&c, 1); 32112239Sandreas.sandberg@arm.com } 32212239Sandreas.sandberg@arm.com } 32312239Sandreas.sandberg@arm.com 32412239Sandreas.sandberg@arm.com last = c; 32512239Sandreas.sandberg@arm.com } 32612239Sandreas.sandberg@arm.com#endif 32712239Sandreas.sandberg@arm.com 32812239Sandreas.sandberg@arm.com txbuf.write(&c, 1); 32912239Sandreas.sandberg@arm.com 33012239Sandreas.sandberg@arm.com if (data_fd >= 0) 33112239Sandreas.sandberg@arm.com write(c); 33212239Sandreas.sandberg@arm.com 33312239Sandreas.sandberg@arm.com if (outfile) 33412239Sandreas.sandberg@arm.com outfile->stream()->put((char)c); 33512239Sandreas.sandberg@arm.com 33612239Sandreas.sandberg@arm.com DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n", 33712239Sandreas.sandberg@arm.com isprint(c) ? c : ' ', (int)c); 33812239Sandreas.sandberg@arm.com 33912239Sandreas.sandberg@arm.com} 34012239Sandreas.sandberg@arm.com 34112239Sandreas.sandberg@arm.comTerminal * 34212239Sandreas.sandberg@arm.comTerminalParams::create() 34312239Sandreas.sandberg@arm.com{ 34412239Sandreas.sandberg@arm.com return new Terminal(this); 34512239Sandreas.sandberg@arm.com} 346