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