terminal.cc revision 12693
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" 5812334Sgabeblack@google.com#include "base/logging.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 15612693Sandreas.sandberg@arm.com ccprintf(cerr, "%s: Listening for connections on port %d\n", 15712693Sandreas.sandberg@arm.com name(), port); 15812239Sandreas.sandberg@arm.com 15912239Sandreas.sandberg@arm.com listenEvent = new ListenEvent(this, listener.getfd(), POLLIN); 16012239Sandreas.sandberg@arm.com pollQueue.schedule(listenEvent); 16112239Sandreas.sandberg@arm.com} 16212239Sandreas.sandberg@arm.com 16312239Sandreas.sandberg@arm.comvoid 16412239Sandreas.sandberg@arm.comTerminal::accept() 16512239Sandreas.sandberg@arm.com{ 16612239Sandreas.sandberg@arm.com if (!listener.islistening()) 16712239Sandreas.sandberg@arm.com panic("%s: cannot accept a connection if not listening!", name()); 16812239Sandreas.sandberg@arm.com 16912239Sandreas.sandberg@arm.com int fd = listener.accept(true); 17012239Sandreas.sandberg@arm.com if (data_fd != -1) { 17112239Sandreas.sandberg@arm.com char message[] = "terminal already attached!\n"; 17212239Sandreas.sandberg@arm.com atomic_write(fd, message, sizeof(message)); 17312239Sandreas.sandberg@arm.com ::close(fd); 17412239Sandreas.sandberg@arm.com return; 17512239Sandreas.sandberg@arm.com } 17612239Sandreas.sandberg@arm.com 17712239Sandreas.sandberg@arm.com data_fd = fd; 17812239Sandreas.sandberg@arm.com dataEvent = new DataEvent(this, data_fd, POLLIN); 17912239Sandreas.sandberg@arm.com pollQueue.schedule(dataEvent); 18012239Sandreas.sandberg@arm.com 18112239Sandreas.sandberg@arm.com stringstream stream; 18212239Sandreas.sandberg@arm.com ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number); 18312239Sandreas.sandberg@arm.com 18412239Sandreas.sandberg@arm.com // we need an actual carriage return followed by a newline for the 18512239Sandreas.sandberg@arm.com // terminal 18612239Sandreas.sandberg@arm.com stream << "\r\n"; 18712239Sandreas.sandberg@arm.com 18812239Sandreas.sandberg@arm.com write((const uint8_t *)stream.str().c_str(), stream.str().size()); 18912239Sandreas.sandberg@arm.com 19012239Sandreas.sandberg@arm.com DPRINTFN("attach terminal %d\n", number); 19112239Sandreas.sandberg@arm.com char buf[1024]; 19212239Sandreas.sandberg@arm.com for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) { 19312239Sandreas.sandberg@arm.com const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf))); 19412239Sandreas.sandberg@arm.com txbuf.peek(buf, i, chunk_len); 19512239Sandreas.sandberg@arm.com write((const uint8_t *)buf, chunk_len); 19612239Sandreas.sandberg@arm.com } 19712239Sandreas.sandberg@arm.com} 19812239Sandreas.sandberg@arm.com 19912239Sandreas.sandberg@arm.comvoid 20012239Sandreas.sandberg@arm.comTerminal::detach() 20112239Sandreas.sandberg@arm.com{ 20212239Sandreas.sandberg@arm.com if (data_fd != -1) { 20312239Sandreas.sandberg@arm.com ::close(data_fd); 20412239Sandreas.sandberg@arm.com data_fd = -1; 20512239Sandreas.sandberg@arm.com } 20612239Sandreas.sandberg@arm.com 20712239Sandreas.sandberg@arm.com pollQueue.remove(dataEvent); 20812239Sandreas.sandberg@arm.com delete dataEvent; 20912239Sandreas.sandberg@arm.com dataEvent = NULL; 21012239Sandreas.sandberg@arm.com 21112239Sandreas.sandberg@arm.com DPRINTFN("detach terminal %d\n", number); 21212239Sandreas.sandberg@arm.com} 21312239Sandreas.sandberg@arm.com 21412239Sandreas.sandberg@arm.comvoid 21512239Sandreas.sandberg@arm.comTerminal::data() 21612239Sandreas.sandberg@arm.com{ 21712239Sandreas.sandberg@arm.com uint8_t buf[1024]; 21812239Sandreas.sandberg@arm.com int len; 21912239Sandreas.sandberg@arm.com 22012239Sandreas.sandberg@arm.com len = read(buf, sizeof(buf)); 22112239Sandreas.sandberg@arm.com if (len) { 22212239Sandreas.sandberg@arm.com rxbuf.write((char *)buf, len); 22312239Sandreas.sandberg@arm.com notifyInterface(); 22412239Sandreas.sandberg@arm.com } 22512239Sandreas.sandberg@arm.com} 22612239Sandreas.sandberg@arm.com 22712239Sandreas.sandberg@arm.comsize_t 22812239Sandreas.sandberg@arm.comTerminal::read(uint8_t *buf, size_t len) 22912239Sandreas.sandberg@arm.com{ 23012239Sandreas.sandberg@arm.com if (data_fd < 0) 23112239Sandreas.sandberg@arm.com panic("Terminal not properly attached.\n"); 23212239Sandreas.sandberg@arm.com 23312239Sandreas.sandberg@arm.com ssize_t ret; 23412239Sandreas.sandberg@arm.com do { 23512239Sandreas.sandberg@arm.com ret = ::read(data_fd, buf, len); 23612239Sandreas.sandberg@arm.com } while (ret == -1 && errno == EINTR); 23712239Sandreas.sandberg@arm.com 23812239Sandreas.sandberg@arm.com 23912239Sandreas.sandberg@arm.com if (ret < 0) 24012239Sandreas.sandberg@arm.com DPRINTFN("Read failed.\n"); 24112239Sandreas.sandberg@arm.com 24212239Sandreas.sandberg@arm.com if (ret <= 0) { 24312239Sandreas.sandberg@arm.com detach(); 24412239Sandreas.sandberg@arm.com return 0; 24512239Sandreas.sandberg@arm.com } 24612239Sandreas.sandberg@arm.com 24712239Sandreas.sandberg@arm.com return ret; 24812239Sandreas.sandberg@arm.com} 24912239Sandreas.sandberg@arm.com 25012239Sandreas.sandberg@arm.com// Terminal output. 25112239Sandreas.sandberg@arm.comsize_t 25212239Sandreas.sandberg@arm.comTerminal::write(const uint8_t *buf, size_t len) 25312239Sandreas.sandberg@arm.com{ 25412239Sandreas.sandberg@arm.com if (data_fd < 0) 25512239Sandreas.sandberg@arm.com panic("Terminal not properly attached.\n"); 25612239Sandreas.sandberg@arm.com 25712239Sandreas.sandberg@arm.com ssize_t ret = atomic_write(data_fd, buf, len); 25812239Sandreas.sandberg@arm.com if (ret < len) 25912239Sandreas.sandberg@arm.com detach(); 26012239Sandreas.sandberg@arm.com 26112239Sandreas.sandberg@arm.com return ret; 26212239Sandreas.sandberg@arm.com} 26312239Sandreas.sandberg@arm.com 26412239Sandreas.sandberg@arm.com#define MORE_PENDING (ULL(1) << 61) 26512239Sandreas.sandberg@arm.com#define RECEIVE_SUCCESS (ULL(0) << 62) 26612239Sandreas.sandberg@arm.com#define RECEIVE_NONE (ULL(2) << 62) 26712239Sandreas.sandberg@arm.com#define RECEIVE_ERROR (ULL(3) << 62) 26812239Sandreas.sandberg@arm.com 26912239Sandreas.sandberg@arm.comuint8_t 27012239Sandreas.sandberg@arm.comTerminal::readData() 27112239Sandreas.sandberg@arm.com{ 27212239Sandreas.sandberg@arm.com uint8_t c; 27312239Sandreas.sandberg@arm.com 27412239Sandreas.sandberg@arm.com assert(!rxbuf.empty()); 27512239Sandreas.sandberg@arm.com rxbuf.read((char *)&c, 1); 27612239Sandreas.sandberg@arm.com 27712239Sandreas.sandberg@arm.com DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n", 27812239Sandreas.sandberg@arm.com isprint(c) ? c : ' ', c, !rxbuf.empty()); 27912239Sandreas.sandberg@arm.com 28012239Sandreas.sandberg@arm.com return c; 28112239Sandreas.sandberg@arm.com} 28212239Sandreas.sandberg@arm.com 28312239Sandreas.sandberg@arm.comuint64_t 28412239Sandreas.sandberg@arm.comTerminal::console_in() 28512239Sandreas.sandberg@arm.com{ 28612239Sandreas.sandberg@arm.com uint64_t value; 28712239Sandreas.sandberg@arm.com 28812239Sandreas.sandberg@arm.com if (dataAvailable()) { 28912239Sandreas.sandberg@arm.com value = RECEIVE_SUCCESS | readData(); 29012239Sandreas.sandberg@arm.com if (!rxbuf.empty()) 29112239Sandreas.sandberg@arm.com value |= MORE_PENDING; 29212239Sandreas.sandberg@arm.com } else { 29312239Sandreas.sandberg@arm.com value = RECEIVE_NONE; 29412239Sandreas.sandberg@arm.com } 29512239Sandreas.sandberg@arm.com 29612239Sandreas.sandberg@arm.com DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value); 29712239Sandreas.sandberg@arm.com 29812239Sandreas.sandberg@arm.com return value; 29912239Sandreas.sandberg@arm.com} 30012239Sandreas.sandberg@arm.com 30112239Sandreas.sandberg@arm.comvoid 30212239Sandreas.sandberg@arm.comTerminal::writeData(uint8_t c) 30312239Sandreas.sandberg@arm.com{ 30412239Sandreas.sandberg@arm.com#if TRACING_ON == 1 30512239Sandreas.sandberg@arm.com if (DTRACE(Terminal)) { 30612239Sandreas.sandberg@arm.com static char last = '\0'; 30712239Sandreas.sandberg@arm.com 30812239Sandreas.sandberg@arm.com if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) { 30912239Sandreas.sandberg@arm.com if (c == '\n' || c == '\r') { 31012239Sandreas.sandberg@arm.com int size = linebuf.size(); 31112239Sandreas.sandberg@arm.com char *buffer = new char[size + 1]; 31212239Sandreas.sandberg@arm.com linebuf.read(buffer, size); 31312239Sandreas.sandberg@arm.com buffer[size] = '\0'; 31412239Sandreas.sandberg@arm.com DPRINTF(Terminal, "%s\n", buffer); 31512239Sandreas.sandberg@arm.com delete [] buffer; 31612239Sandreas.sandberg@arm.com } else { 31712239Sandreas.sandberg@arm.com linebuf.write(&c, 1); 31812239Sandreas.sandberg@arm.com } 31912239Sandreas.sandberg@arm.com } 32012239Sandreas.sandberg@arm.com 32112239Sandreas.sandberg@arm.com last = c; 32212239Sandreas.sandberg@arm.com } 32312239Sandreas.sandberg@arm.com#endif 32412239Sandreas.sandberg@arm.com 32512239Sandreas.sandberg@arm.com txbuf.write(&c, 1); 32612239Sandreas.sandberg@arm.com 32712239Sandreas.sandberg@arm.com if (data_fd >= 0) 32812239Sandreas.sandberg@arm.com write(c); 32912239Sandreas.sandberg@arm.com 33012239Sandreas.sandberg@arm.com if (outfile) 33112239Sandreas.sandberg@arm.com outfile->stream()->put((char)c); 33212239Sandreas.sandberg@arm.com 33312239Sandreas.sandberg@arm.com DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n", 33412239Sandreas.sandberg@arm.com isprint(c) ? c : ' ', (int)c); 33512239Sandreas.sandberg@arm.com 33612239Sandreas.sandberg@arm.com} 33712239Sandreas.sandberg@arm.com 33812239Sandreas.sandberg@arm.comTerminal * 33912239Sandreas.sandberg@arm.comTerminalParams::create() 34012239Sandreas.sandberg@arm.com{ 34112239Sandreas.sandberg@arm.com return new Terminal(this); 34212239Sandreas.sandberg@arm.com} 343