terminal.cc revision 12693:4db8d6442b44
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 ccprintf(cerr, "%s: Listening for connections on port %d\n", 157 name(), port); 158 159 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN); 160 pollQueue.schedule(listenEvent); 161} 162 163void 164Terminal::accept() 165{ 166 if (!listener.islistening()) 167 panic("%s: cannot accept a connection if not listening!", name()); 168 169 int fd = listener.accept(true); 170 if (data_fd != -1) { 171 char message[] = "terminal already attached!\n"; 172 atomic_write(fd, message, sizeof(message)); 173 ::close(fd); 174 return; 175 } 176 177 data_fd = fd; 178 dataEvent = new DataEvent(this, data_fd, POLLIN); 179 pollQueue.schedule(dataEvent); 180 181 stringstream stream; 182 ccprintf(stream, "==== m5 slave terminal: Terminal %d ====", number); 183 184 // we need an actual carriage return followed by a newline for the 185 // terminal 186 stream << "\r\n"; 187 188 write((const uint8_t *)stream.str().c_str(), stream.str().size()); 189 190 DPRINTFN("attach terminal %d\n", number); 191 char buf[1024]; 192 for (size_t i = 0; i < txbuf.size(); i += sizeof(buf)) { 193 const size_t chunk_len(std::min(txbuf.size() - i, sizeof(buf))); 194 txbuf.peek(buf, i, chunk_len); 195 write((const uint8_t *)buf, chunk_len); 196 } 197} 198 199void 200Terminal::detach() 201{ 202 if (data_fd != -1) { 203 ::close(data_fd); 204 data_fd = -1; 205 } 206 207 pollQueue.remove(dataEvent); 208 delete dataEvent; 209 dataEvent = NULL; 210 211 DPRINTFN("detach terminal %d\n", number); 212} 213 214void 215Terminal::data() 216{ 217 uint8_t buf[1024]; 218 int len; 219 220 len = read(buf, sizeof(buf)); 221 if (len) { 222 rxbuf.write((char *)buf, len); 223 notifyInterface(); 224 } 225} 226 227size_t 228Terminal::read(uint8_t *buf, size_t len) 229{ 230 if (data_fd < 0) 231 panic("Terminal not properly attached.\n"); 232 233 ssize_t ret; 234 do { 235 ret = ::read(data_fd, buf, len); 236 } while (ret == -1 && errno == EINTR); 237 238 239 if (ret < 0) 240 DPRINTFN("Read failed.\n"); 241 242 if (ret <= 0) { 243 detach(); 244 return 0; 245 } 246 247 return ret; 248} 249 250// Terminal output. 251size_t 252Terminal::write(const uint8_t *buf, size_t len) 253{ 254 if (data_fd < 0) 255 panic("Terminal not properly attached.\n"); 256 257 ssize_t ret = atomic_write(data_fd, buf, len); 258 if (ret < len) 259 detach(); 260 261 return ret; 262} 263 264#define MORE_PENDING (ULL(1) << 61) 265#define RECEIVE_SUCCESS (ULL(0) << 62) 266#define RECEIVE_NONE (ULL(2) << 62) 267#define RECEIVE_ERROR (ULL(3) << 62) 268 269uint8_t 270Terminal::readData() 271{ 272 uint8_t c; 273 274 assert(!rxbuf.empty()); 275 rxbuf.read((char *)&c, 1); 276 277 DPRINTF(TerminalVerbose, "in: \'%c\' %#02x more: %d\n", 278 isprint(c) ? c : ' ', c, !rxbuf.empty()); 279 280 return c; 281} 282 283uint64_t 284Terminal::console_in() 285{ 286 uint64_t value; 287 288 if (dataAvailable()) { 289 value = RECEIVE_SUCCESS | readData(); 290 if (!rxbuf.empty()) 291 value |= MORE_PENDING; 292 } else { 293 value = RECEIVE_NONE; 294 } 295 296 DPRINTF(TerminalVerbose, "console_in: return: %#x\n", value); 297 298 return value; 299} 300 301void 302Terminal::writeData(uint8_t c) 303{ 304#if TRACING_ON == 1 305 if (DTRACE(Terminal)) { 306 static char last = '\0'; 307 308 if ((c != '\n' && c != '\r') || (last != '\n' && last != '\r')) { 309 if (c == '\n' || c == '\r') { 310 int size = linebuf.size(); 311 char *buffer = new char[size + 1]; 312 linebuf.read(buffer, size); 313 buffer[size] = '\0'; 314 DPRINTF(Terminal, "%s\n", buffer); 315 delete [] buffer; 316 } else { 317 linebuf.write(&c, 1); 318 } 319 } 320 321 last = c; 322 } 323#endif 324 325 txbuf.write(&c, 1); 326 327 if (data_fd >= 0) 328 write(c); 329 330 if (outfile) 331 outfile->stream()->put((char)c); 332 333 DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n", 334 isprint(c) ? c : ' ', (int)c); 335 336} 337 338Terminal * 339TerminalParams::create() 340{ 341 return new Terminal(this); 342} 343