/* * Copyright (c) 2001-2005 The Regents of The University of Michigan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer; * redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution; * neither the name of the copyright holders nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* @file * Implements the user interface to a serial console */ #include #include #include #include #include #include #include #include #include #include #include "base/misc.hh" #include "base/output.hh" #include "base/socket.hh" #include "base/trace.hh" #include "dev/platform.hh" #include "dev/simconsole.hh" #include "dev/uart.hh" #include "sim/builder.hh" using namespace std; //////////////////////////////////////////////////////////////////////// // // SimConsole::Event::Event(SimConsole *c, int fd, int e) : PollEvent(fd, e), cons(c) { } void SimConsole::Event::process(int revent) { if (revent & POLLIN) cons->data(); else if (revent & POLLNVAL) cons->detach(); } SimConsole::SimConsole(const string &name, ostream *os, int num) : SimObject(name), event(NULL), number(num), in_fd(-1), out_fd(-1), listener(NULL), txbuf(16384), rxbuf(16384), outfile(os) #if TRACING_ON == 1 , linebuf(16384) #endif { if (outfile) outfile->setf(ios::unitbuf); } SimConsole::~SimConsole() { close(); } void SimConsole::close() { if (in_fd != -1) ::close(in_fd); if (out_fd != in_fd && out_fd != -1) ::close(out_fd); } void SimConsole::attach(int in, int out, ConsoleListener *l) { in_fd = in; out_fd = out; listener = l; event = new Event(this, in, POLLIN); pollQueue.schedule(event); stringstream stream; ccprintf(stream, "==== m5 slave console: Console %d ====", number); // we need an actual carriage return followed by a newline for the // terminal stream << "\r\n"; write((const uint8_t *)stream.str().c_str(), stream.str().size()); DPRINTFN("attach console %d\n", number); txbuf.readall(out); } void SimConsole::detach() { close(); in_fd = -1; out_fd = -1; pollQueue.remove(event); if (listener) { listener->add(this); listener = NULL; } DPRINTFN("detach console %d\n", number); } void SimConsole::data() { uint8_t buf[1024]; int len; len = read(buf, sizeof(buf)); if (len) { rxbuf.write((char *)buf, len); // Inform the UART there is data available uart->dataAvailable(); } } size_t SimConsole::read(uint8_t *buf, size_t len) { if (in_fd < 0) panic("Console not properly attached.\n"); size_t ret; do { ret = ::read(in_fd, buf, len); } while (ret == -1 && errno == EINTR); if (ret < 0) DPRINTFN("Read failed.\n"); if (ret <= 0) { detach(); return 0; } return ret; } // Console output. size_t SimConsole::write(const uint8_t *buf, size_t len) { if (out_fd < 0) panic("Console not properly attached.\n"); size_t ret; for (;;) { ret = ::write(out_fd, buf, len); if (ret >= 0) break; if (errno != EINTR) detach(); } return ret; } #define MORE_PENDING (ULL(1) << 61) #define RECEIVE_SUCCESS (ULL(0) << 62) #define RECEIVE_NONE (ULL(2) << 62) #define RECEIVE_ERROR (ULL(3) << 62) uint8_t SimConsole::in() { bool empty; uint8_t c; empty = rxbuf.empty(); assert(!empty); rxbuf.read((char *)&c, 1); empty = rxbuf.empty(); DPRINTF(ConsoleVerbose, "in: \'%c\' %#02x more: %d\n", isprint(c) ? c : ' ', c, !empty); return c; } uint64_t SimConsole::console_in() { uint64_t value; if (dataAvailable()) { value = RECEIVE_SUCCESS | in(); if (!rxbuf.empty()) value |= MORE_PENDING; } else { value = RECEIVE_NONE; } DPRINTF(ConsoleVerbose, "console_in: return: %#x\n", value); return value; } void SimConsole::out(char c) { #if TRACING_ON == 1 if (DTRACE(Console)) { static char last = '\0'; if (c != '\n' && c != '\r' || last != '\n' && last != '\r') { if (c == '\n' || c == '\r') { int size = linebuf.size(); char *buffer = new char[size + 1]; linebuf.read(buffer, size); buffer[size] = '\0'; DPRINTF(Console, "%s\n", buffer); delete [] buffer; } else { linebuf.write(c); } } last = c; } #endif txbuf.write(c); if (out_fd >= 0) write(c); if (outfile) outfile->write(&c, 1); DPRINTF(ConsoleVerbose, "out: \'%c\' %#02x\n", isprint(c) ? c : ' ', (int)c); } void SimConsole::serialize(ostream &os) { } void SimConsole::unserialize(Checkpoint *cp, const std::string §ion) { } BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimConsole) SimObjectParam listener; SimObjectParam intr_control; Param output; Param append_name; Param number; END_DECLARE_SIM_OBJECT_PARAMS(SimConsole) BEGIN_INIT_SIM_OBJECT_PARAMS(SimConsole) INIT_PARAM(listener, "console listener"), INIT_PARAM(intr_control, "interrupt controller"), INIT_PARAM(output, "file to dump output to"), INIT_PARAM_DFLT(append_name, "append name() to filename", true), INIT_PARAM_DFLT(number, "console number", 0) END_INIT_SIM_OBJECT_PARAMS(SimConsole) CREATE_SIM_OBJECT(SimConsole) { string filename = output; ostream *stream = NULL; if (!filename.empty()) { if (append_name) filename += "." + getInstanceName(); stream = simout.find(filename); } SimConsole *console = new SimConsole(getInstanceName(), stream, number); ((ConsoleListener *)listener)->add(console); return console; } REGISTER_SIM_OBJECT("SimConsole", SimConsole) //////////////////////////////////////////////////////////////////////// // // ConsoleListener::ConsoleListener(const string &name) : SimObject(name), event(NULL) {} ConsoleListener::~ConsoleListener() { if (event) delete event; } void ConsoleListener::Event::process(int revent) { listener->accept(); } /////////////////////////////////////////////////////////////////////// // socket creation and console attach // void ConsoleListener::listen(int port) { while (!listener.listen(port, true)) { DPRINTF(Console, ": can't bind address console port %d inuse PID %d\n", port, getpid()); port++; } ccprintf(cerr, "Listening for console connection on port %d\n", port); event = new Event(this, listener.getfd(), POLLIN); pollQueue.schedule(event); } void ConsoleListener::add(SimConsole *cons) { ConsoleList.push_back(cons);} void ConsoleListener::accept() { if (!listener.islistening()) panic("%s: cannot accept a connection if not listening!", name()); int sfd = listener.accept(true); if (sfd != -1) { iter_t i = ConsoleList.begin(); iter_t end = ConsoleList.end(); if (i == end) { close(sfd); } else { (*i)->attach(sfd, this); i = ConsoleList.erase(i); } } } BEGIN_DECLARE_SIM_OBJECT_PARAMS(ConsoleListener) Param port; END_DECLARE_SIM_OBJECT_PARAMS(ConsoleListener) BEGIN_INIT_SIM_OBJECT_PARAMS(ConsoleListener) INIT_PARAM_DFLT(port, "listen port", 3456) END_INIT_SIM_OBJECT_PARAMS(ConsoleListener) CREATE_SIM_OBJECT(ConsoleListener) { ConsoleListener *listener = new ConsoleListener(getInstanceName()); listener->listen(port); return listener; } REGISTER_SIM_OBJECT("ConsoleListener", ConsoleListener)