terminal.cc revision 12334
1/* 2 * Copyright (c) 2001-2005 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Authors: Nathan Binkert 29 * Ali Saidi 30 */ 31 32/* @file 33 * Implements the user interface to a serial terminal 34 */ 35 36#include <sys/ioctl.h> 37 38#if defined(__FreeBSD__) 39#include <termios.h> 40 41#else 42#include <sys/termios.h> 43 44#endif 45#include "dev/serial/terminal.hh" 46 47#include <poll.h> 48#include <unistd.h> 49 50#include <cctype> 51#include <cerrno> 52#include <fstream> 53#include <iostream> 54#include <sstream> 55#include <string> 56 57#include "base/atomicio.hh" 58#include "base/logging.hh" 59#include "base/output.hh" 60#include "base/socket.hh" 61#include "base/trace.hh" 62#include "debug/Terminal.hh" 63#include "debug/TerminalVerbose.hh" 64#include "dev/platform.hh" 65#include "dev/serial/uart.hh" 66 67using namespace std; 68 69 70/* 71 * Poll event for the listen socket 72 */ 73Terminal::ListenEvent::ListenEvent(Terminal *t, int fd, int e) 74 : PollEvent(fd, e), term(t) 75{ 76} 77 78void 79Terminal::ListenEvent::process(int revent) 80{ 81 term->accept(); 82} 83 84/* 85 * Poll event for the data socket 86 */ 87Terminal::DataEvent::DataEvent(Terminal *t, int fd, int e) 88 : PollEvent(fd, e), term(t) 89{ 90} 91 92void 93Terminal::DataEvent::process(int revent) 94{ 95 // As a consequence of being called from the PollQueue, we might 96 // have been called from a different thread. Migrate to "our" 97 // thread. 98 EventQueue::ScopedMigration migrate(term->eventQueue()); 99 100 if (revent & POLLIN) 101 term->data(); 102 else if (revent & POLLNVAL) 103 term->detach(); 104} 105 106/* 107 * Terminal code 108 */ 109Terminal::Terminal(const Params *p) 110 : SerialDevice(p), listenEvent(NULL), dataEvent(NULL), 111 number(p->number), data_fd(-1), txbuf(16384), rxbuf(16384), 112 outfile(p->output ? simout.findOrCreate(p->name) : NULL) 113#if TRACING_ON == 1 114 , linebuf(16384) 115#endif 116{ 117 if (outfile) 118 outfile->stream()->setf(ios::unitbuf); 119 120 if (p->port) 121 listen(p->port); 122} 123 124Terminal::~Terminal() 125{ 126 if (data_fd != -1) 127 ::close(data_fd); 128 129 if (listenEvent) 130 delete listenEvent; 131 132 if (dataEvent) 133 delete dataEvent; 134} 135 136 137/////////////////////////////////////////////////////////////////////// 138// socket creation and terminal attach 139// 140 141void 142Terminal::listen(int port) 143{ 144 if (ListenSocket::allDisabled()) { 145 warn_once("Sockets disabled, not accepting terminal connections"); 146 return; 147 } 148 149 while (!listener.listen(port, true)) { 150 DPRINTF(Terminal, 151 ": can't bind address terminal port %d inuse PID %d\n", 152 port, getpid()); 153 port++; 154 } 155 156 int p1, p2; 157 p2 = name().rfind('.') - 1; 158 p1 = name().rfind('.', p2); 159 ccprintf(cerr, "Listening for %s connection on port %d\n", 160 name().substr(p1+1,p2-p1), port); 161 162 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN); 163 pollQueue.schedule(listenEvent); 164} 165 166void 167Terminal::accept() 168{ 169 if (!listener.islistening()) 170 panic("%s: cannot accept a connection if not listening!", name()); 171 172 int fd = listener.accept(true); 173 if (data_fd != -1) { 174 char message[] = "terminal already attached!\n"; 175 atomic_write(fd, message, sizeof(message)); 176 ::close(fd); 177 return; 178 } 179 180 data_fd = fd; 181 dataEvent = new DataEvent(this, data_fd, POLLIN); 182 pollQueue.schedule(dataEvent); 183 184 stringstream stream; 185 ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number); 186 187 // we need an actual carriage return followed by a newline for the 188 // terminal 189 stream << "\r\n"; 190 191 write((const uint8_t *)stream.str().c_str(), stream.str().size()); 192 193 DPRINTFN("attach terminal %d\n", number); 194 char buf[1024]; 195 for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) { 196 const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf))); 197 txbuf.peek(buf, i, chunk_len); 198 write((const uint8_t *)buf, chunk_len); 199 } 200} 201 202void 203Terminal::detach() 204{ 205 if (data_fd != -1) { 206 ::close(data_fd); 207 data_fd = -1; 208 } 209 210 pollQueue.remove(dataEvent); 211 delete dataEvent; 212 dataEvent = NULL; 213 214 DPRINTFN("detach terminal %d\n", number); 215} 216 217void 218Terminal::data() 219{ 220 uint8_t buf[1024]; 221 int len; 222 223 len = read(buf, sizeof(buf)); 224 if (len) { 225 rxbuf.write((char *)buf, len); 226 notifyInterface(); 227 } 228} 229 230size_t 231Terminal::read(uint8_t *buf, size_t len) 232{ 233 if (data_fd < 0) 234 panic("Terminal not properly attached.\n"); 235 236 ssize_t ret; 237 do { 238 ret = ::read(data_fd, buf, len); 239 } while (ret == -1 && errno == EINTR); 240 241 242 if (ret < 0) 243 DPRINTFN("Read failed.\n"); 244 245 if (ret <= 0) { 246 detach(); 247 return 0; 248 } 249 250 return ret; 251} 252 253// Terminal output. 254size_t 255Terminal::write(const uint8_t *buf, size_t len) 256{ 257 if (data_fd < 0) 258 panic("Terminal not properly attached.\n"); 259 260 ssize_t ret = atomic_write(data_fd, buf, len); 261 if (ret < len) 262 detach(); 263 264 return ret; 265} 266 267#define MORE_PENDING (ULL(1) << 61) 268#define RECEIVE_SUCCESS (ULL(0) << 62) 269#define RECEIVE_NONE (ULL(2) << 62) 270#define RECEIVE_ERROR (ULL(3) << 62) 271 272uint8_t 273Terminal::readData() 274{ 275 uint8_t c; 276 277 assert(!rxbuf.empty()); 278 rxbuf.read((char *)&c, 1); 279 280 DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n", 281 isprint(c) ? c : ' ', c, !rxbuf.empty()); 282 283 return c; 284} 285 286uint64_t 287Terminal::console_in() 288{ 289 uint64_t value; 290 291 if (dataAvailable()) { 292 value = RECEIVE_SUCCESS | readData(); 293 if (!rxbuf.empty()) 294 value |= MORE_PENDING; 295 } else { 296 value = RECEIVE_NONE; 297 } 298 299 DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value); 300 301 return value; 302} 303 304void 305Terminal::writeData(uint8_t c) 306{ 307#if TRACING_ON == 1 308 if (DTRACE(Terminal)) { 309 static char last = '\0'; 310 311 if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) { 312 if (c == '\n' || c == '\r') { 313 int size = linebuf.size(); 314 char *buffer = new char[size + 1]; 315 linebuf.read(buffer, size); 316 buffer[size] = '\0'; 317 DPRINTF(Terminal, "%s\n", buffer); 318 delete [] buffer; 319 } else { 320 linebuf.write(&c, 1); 321 } 322 } 323 324 last = c; 325 } 326#endif 327 328 txbuf.write(&c, 1); 329 330 if (data_fd >= 0) 331 write(c); 332 333 if (outfile) 334 outfile->stream()->put((char)c); 335 336 DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n", 337 isprint(c) ? c : ' ', (int)c); 338 339} 340 341Terminal * 342TerminalParams::create() 343{ 344 return new Terminal(this); 345} 346