summaryrefslogtreecommitdiff
path: root/src/dev/simconsole.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/dev/simconsole.cc')
-rw-r--r--src/dev/simconsole.cc414
1 files changed, 414 insertions, 0 deletions
diff --git a/src/dev/simconsole.cc b/src/dev/simconsole.cc
new file mode 100644
index 000000000..c3e4f554a
--- /dev/null
+++ b/src/dev/simconsole.cc
@@ -0,0 +1,414 @@
+/*
+ * 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 <sys/ioctl.h>
+#include <sys/termios.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+
+#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)
+
+bool
+SimConsole::in(uint8_t &c)
+{
+ bool empty, ret;
+
+ empty = rxbuf.empty();
+ ret = !empty;
+ if (!empty) {
+ rxbuf.read((char *)&c, 1);
+ empty = rxbuf.empty();
+ }
+
+ DPRINTF(ConsoleVerbose, "in: \'%c\' %#02x more: %d, return: %d\n",
+ isprint(c) ? c : ' ', c, !empty, ret);
+
+ return ret;
+}
+
+uint64_t
+SimConsole::console_in()
+{
+ uint8_t c;
+ uint64_t value;
+
+ if (in(c)) {
+ value = RECEIVE_SUCCESS | c;
+ 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 &section)
+{
+}
+
+
+BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimConsole)
+
+ SimObjectParam<ConsoleListener *> listener;
+ SimObjectParam<IntrControl *> intr_control;
+ Param<string> output;
+ Param<bool> append_name;
+ Param<int> 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<int> 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)