dev: Refactor UART->Terminal interface

The UART models currently assume that they are always wired to a
terminal. While true at the moment, this isn't necessarily a valid
assumption. This change introduces the SerialDevice class that defines
the interface for serial devices. Currently, Terminal is the only
class that implements this interface.

Change-Id: I74fefafbbaf5ac1ec0d4ec0b5a0f4b246fdad305
Signed-off-by: Andreas Sandberg <andreas.sandberg@arm.com>
Reviewed-by: Curtis Dunham <curtis.dunham@arm.com>
Reviewed-on: https://gem5-review.googlesource.com/4289
Reviewed-by: Gabe Black <gabeblack@google.com>
diff --git a/src/dev/SConscript b/src/dev/SConscript
index 9cfda05..b95d360 100644
--- a/src/dev/SConscript
+++ b/src/dev/SConscript
@@ -44,6 +44,7 @@
 
 SimObject('BadDevice.py')
 SimObject('Platform.py')
+SimObject('Serial.py')
 SimObject('Terminal.py')
 SimObject('Uart.py')
 
@@ -53,6 +54,7 @@
 Source('pixelpump.cc')
 Source('platform.cc')
 Source('ps2.cc')
+Source('serial.cc')
 Source('terminal.cc')
 Source('uart.cc')
 Source('uart8250.cc')
diff --git a/src/dev/Serial.py b/src/dev/Serial.py
new file mode 100644
index 0000000..bbc792b
--- /dev/null
+++ b/src/dev/Serial.py
@@ -0,0 +1,44 @@
+# Copyright (c) 2014, 2017 ARM Limited
+# All rights reserved.
+#
+# The license below extends only to copyright in the software and shall
+# not be construed as granting a license to any other intellectual
+# property including but not limited to intellectual property relating
+# to a hardware implementation of the functionality of the software
+# licensed hereunder.  You may use the software subject to the license
+# terms below provided that you ensure that this notice is replicated
+# unmodified and in its entirety in all distributions of the software,
+# modified or unmodified, in source code or in binary form.
+#
+# 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.
+#
+# Authors: Andreas Sandberg
+
+from m5.params import *
+from m5.SimObject import SimObject
+
+class SerialDevice(SimObject):
+    type = 'SerialDevice'
+    abstract = True
+    cxx_header = "dev/serial.hh"
diff --git a/src/dev/Terminal.py b/src/dev/Terminal.py
index 2b54f9d..9b50111 100644
--- a/src/dev/Terminal.py
+++ b/src/dev/Terminal.py
@@ -29,8 +29,9 @@
 from m5.SimObject import SimObject
 from m5.params import *
 from m5.proxy import *
+from Serial import SerialDevice
 
-class Terminal(SimObject):
+class Terminal(SerialDevice):
     type = 'Terminal'
     cxx_header = "dev/terminal.hh"
     intr_control = Param.IntrControl(Parent.any, "interrupt controller")
diff --git a/src/dev/Uart.py b/src/dev/Uart.py
index c3bc9dd..c3bfd1b 100644
--- a/src/dev/Uart.py
+++ b/src/dev/Uart.py
@@ -29,13 +29,14 @@
 from m5.params import *
 from m5.proxy import *
 from Device import BasicPioDevice
+from Serial import SerialDevice
 
 class Uart(BasicPioDevice):
     type = 'Uart'
     abstract = True
     cxx_header = "dev/uart.hh"
     platform = Param.Platform(Parent.any, "Platform this device is part of.")
-    terminal = Param.Terminal(Parent.any, "The terminal")
+    device = Param.SerialDevice(Parent.any, "The terminal")
 
 class Uart8250(Uart):
     type = 'Uart8250'
diff --git a/src/dev/alpha/backdoor.cc b/src/dev/alpha/backdoor.cc
index 5c627ca..5187e5b 100644
--- a/src/dev/alpha/backdoor.cc
+++ b/src/dev/alpha/backdoor.cc
@@ -233,7 +233,7 @@
         break;
 
       case offsetof(AlphaAccess, outputChar):
-        terminal->out((char)(val & 0xff));
+        terminal->writeData((char)(val & 0xff));
         break;
 
       default:
diff --git a/src/dev/arm/pl011.cc b/src/dev/arm/pl011.cc
index 479a28b..b6b41f9 100644
--- a/src/dev/arm/pl011.cc
+++ b/src/dev/arm/pl011.cc
@@ -48,7 +48,6 @@
 #include "debug/Uart.hh"
 #include "dev/arm/amba_device.hh"
 #include "dev/arm/base_gic.hh"
-#include "dev/terminal.hh"
 #include "mem/packet.hh"
 #include "mem/packet_access.hh"
 #include "params/Pl011.hh"
@@ -81,12 +80,12 @@
     switch(daddr) {
       case UART_DR:
         data = 0;
-        if (term->dataAvailable()) {
-            data = term->in();
+        if (device->dataAvailable()) {
+            data = device->readData();
             // Since we don't simulate a FIFO for incoming data, we
             // assume it's empty and clear RXINTR and RTINTR.
             clearInterrupts(UART_RXINTR | UART_RTINTR);
-            if (term->dataAvailable()) {
+            if (device->dataAvailable()) {
                 DPRINTF(Uart, "Re-raising interrupt due to more data "
                         "after UART_DR read\n");
                 dataAvailable();
@@ -97,7 +96,7 @@
         data =
             UART_FR_CTS | // Clear To Send
             // Given we do not simulate a FIFO we are either empty or full.
-            (!term->dataAvailable() ? UART_FR_RXFE : UART_FR_RXFF) |
+            (!device->dataAvailable() ? UART_FR_RXFE : UART_FR_RXFF) |
             UART_FR_TXFE; // TX FIFO empty
 
         DPRINTF(Uart,
@@ -199,7 +198,7 @@
           if ((data & 0xFF) == 0x04 && endOnEOT)
             exitSimLoop("UART received EOT", 0);
 
-        term->out(data & 0xFF);
+        device->writeData(data & 0xFF);
         // We're supposed to clear TXINTR when this register is
         // written to, however. since we're also infinitely fast, we
         // need to immediately raise it again.
@@ -229,7 +228,7 @@
       case UART_ICR:
         DPRINTF(Uart, "Clearing interrupts 0x%x\n", data);
         clearInterrupts(data);
-        if (term->dataAvailable()) {
+        if (device->dataAvailable()) {
             DPRINTF(Uart, "Re-raising interrupt due to more data after "
                     "UART_ICR write\n");
             dataAvailable();
diff --git a/src/dev/serial.cc b/src/dev/serial.cc
new file mode 100644
index 0000000..63e8a38
--- /dev/null
+++ b/src/dev/serial.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014, 2017 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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.
+ *
+ * Authors: Andreas Sandberg
+ */
+
+#include "dev/serial.hh"
+
+#include "base/misc.hh"
+#include "params/SerialDevice.hh"
+
+SerialDevice::SerialDevice(const SerialDeviceParams *p)
+    : SimObject(p), interfaceCallback(nullptr)
+{
+}
+
+SerialDevice::~SerialDevice()
+{
+}
+
+void
+SerialDevice::regInterfaceCallback(Callback *c)
+{
+    // This can happen if the user has connected multiple UARTs to the
+    // same terminal. In that case, each of them tries to register
+    // callbacks.
+    if (interfaceCallback)
+        fatal("A UART has already been associated with this device.\n");
+    interfaceCallback = c;
+}
+
+void
+SerialDevice::notifyInterface()
+{
+    assert(dataAvailable());
+    // Registering a callback is optional.
+    if (interfaceCallback)
+        interfaceCallback->process();
+}
+
diff --git a/src/dev/serial.hh b/src/dev/serial.hh
new file mode 100644
index 0000000..4ea2f5e
--- /dev/null
+++ b/src/dev/serial.hh
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2014, 2017 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * 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.
+ *
+ * Authors: Andreas Sandberg
+ */
+
+#ifndef __DEV_SERIAL_HH__
+#define __DEV_SERIAL_HH__
+
+#include "base/callback.hh"
+#include "sim/sim_object.hh"
+
+struct SerialDeviceParams;
+
+/**
+ * Base class for serial devices such as terminals.
+ *
+ * This class provides a unified interface that all serial (RS232 or
+ * similar) devices must implement. A device can be wired to exactly
+ * one host serial interface (serial port model).
+ *
+ * SerialDevices are passive devices that are <i>driven</i> by the
+ * serial interface using the writeData(c) (the interface sends a
+ * character) and readData() (the interface reads a character)
+ * methods. Serial devices need to override these methods to
+ * communicate with the host interface layer.
+ *
+ * To implement basic flow control, serial devices must implement the
+ * dataAvailable() method. This method returns true if a valid
+ * character can be read using the readData() method. When data
+ * becomes available, the serial device must call the
+ * notifyInterface() method to send a callback to the interface layer.
+ *
+ * To send a character (host to device), the interface layer calls
+ * writeData(char) to send a character to the serial device.
+ *
+ * To read a character (device to host), the interface layer calls
+ * dataAvailable() to determine if there is a character pending. If
+ * there is data available, it immediately calls readData() to get the
+ * character. The receive loop in the serial device typically looks
+ * like this:
+ *
+ * \code{.cc}
+ * while (device.dataAvailable()) {
+ *    printf("%c", (int)device.readData());
+ * }
+ * \endcode
+ *
+ * To avoid polling, the interface layer may register a data available
+ * callback using the regInterfaceCallback() method. The device uses
+ * this callback to notify the interface layer whenever there is new
+ * data pending. Note that devices will normally only notify the
+ * interface layer when there is a state transition in the
+ * device. E.g., the dataAvailable() transitions from false to
+ * true. This means that there can be multiple pending characters when
+ * the interface layer receives the callback.
+ */
+class SerialDevice : public SimObject
+{
+  public:
+    SerialDevice(const SerialDeviceParams *p);
+    ~SerialDevice();
+
+  public: // Serial device API (UART->Device)
+    /**
+     * Register a data available callback into the host interface layer.
+     *
+     * Serial devices need to call the underlying host interface layer
+     * to inform it of state change such as pending data that can be
+     * read from the device by the interface layer using the readData()
+     * method. The interface layer may use this method to register a
+     * callback that informs it of pending data.
+     *
+     * @param c Callback instance from interface layer.
+     */
+    void regInterfaceCallback(Callback *c);
+
+    /**
+     * Check if there is pending data from the serial device.
+     *
+     * @return true if there is data pending that can be read using
+     * the readData() method.
+     */
+    virtual bool dataAvailable() const = 0;
+
+    /**
+     * Transmit a character from the host interface to the device.
+     *
+     * @param c Received data.
+     */
+    virtual void writeData(uint8_t c) = 0;
+
+    /**
+     * Read a character from the device.
+     *
+     * @return Character from the device's output buffer, undefined if
+     * no data is pending.
+     */
+    virtual uint8_t readData() = 0;
+
+  protected:
+    /** Notify the host interface of pending data. */
+    void notifyInterface();
+
+  private:
+    /** Currently regisxtered host interface layer callback */
+    Callback *interfaceCallback;
+};
+
+#endif // __DEV_SERIAL_HH__
diff --git a/src/dev/terminal.cc b/src/dev/terminal.cc
index 9f0ea5e..4f9881d 100644
--- a/src/dev/terminal.cc
+++ b/src/dev/terminal.cc
@@ -107,7 +107,7 @@
  * Terminal code
  */
 Terminal::Terminal(const Params *p)
-    : SimObject(p), termDataAvail(NULL), listenEvent(NULL), dataEvent(NULL),
+    : SerialDevice(p), listenEvent(NULL), dataEvent(NULL),
       number(p->number), data_fd(-1), txbuf(16384), rxbuf(16384),
       outfile(p->output ? simout.findOrCreate(p->name) : NULL)
 #if TRACING_ON == 1
@@ -133,16 +133,6 @@
         delete dataEvent;
 }
 
-void
-Terminal::regDataAvailCallback(Callback *c)
-{
-    // This can happen if the user has connected multiple UARTs to the
-    // same terminal. In that case, each of them tries to register
-    // callbacks.
-    if (termDataAvail)
-        fatal("Terminal already has already been associated with a UART.\n");
-    termDataAvail = c;
-}
 
 ///////////////////////////////////////////////////////////////////////
 // socket creation and terminal attach
@@ -233,9 +223,7 @@
     len = read(buf, sizeof(buf));
     if (len) {
         rxbuf.write((char *)buf, len);
-        // Inform the UART there is data available
-        assert(termDataAvail);
-        termDataAvail->process();
+        notifyInterface();
     }
 }
 
@@ -282,7 +270,7 @@
 #define RECEIVE_ERROR (ULL(3) << 62)
 
 uint8_t
-Terminal::in()
+Terminal::readData()
 {
     uint8_t c;
 
@@ -301,7 +289,7 @@
     uint64_t value;
 
     if (dataAvailable()) {
-        value = RECEIVE_SUCCESS | in();
+        value = RECEIVE_SUCCESS | readData();
         if (!rxbuf.empty())
             value  |= MORE_PENDING;
     } else {
@@ -314,7 +302,7 @@
 }
 
 void
-Terminal::out(char c)
+Terminal::writeData(uint8_t c)
 {
 #if TRACING_ON == 1
     if (DTRACE(Terminal)) {
@@ -343,7 +331,7 @@
         write(c);
 
     if (outfile)
-        outfile->stream()->write(&c, 1);
+        outfile->stream()->put((char)c);
 
     DPRINTF(TerminalVerbose, "out: \'%c\' %#02x\n",
             isprint(c) ? c : ' ', (int)c);
diff --git a/src/dev/terminal.hh b/src/dev/terminal.hh
index 41c2a9e..8b6ec0b 100644
--- a/src/dev/terminal.hh
+++ b/src/dev/terminal.hh
@@ -43,30 +43,15 @@
 #include "base/pollevent.hh"
 #include "base/socket.hh"
 #include "cpu/intr_control.hh"
+#include "dev/serial.hh"
 #include "params/Terminal.hh"
 #include "sim/sim_object.hh"
 
 class OutputStream;
 class TerminalListener;
 
-class Terminal : public SimObject
+class Terminal : public SerialDevice
 {
-  public:
-    /**
-     * Register a data available callback into the transport layer.
-     *
-     * The terminal needs to call the underlying transport layer to
-     * inform it of available data. The transport layer uses this
-     * method to register a callback that informs it of pending data.
-     *
-     * @param c Callback instance from transport layer.
-     */
-    void regDataAvailCallback(Callback *c);
-
-  protected:
-    /** Currently registered transport layer callbacks */
-    Callback *termDataAvail;
-
   protected:
     class ListenEvent : public PollEvent
     {
@@ -129,13 +114,15 @@
     size_t write(const uint8_t *buf, size_t len);
     void detach();
 
+  public: // SerialDevice interface
+    uint8_t readData() override;
+    void writeData(uint8_t c) override;
+    bool dataAvailable() const override { return !rxbuf.empty(); }
+
   public:
     /////////////////
     // OS interface
 
-    // Get a character from the terminal.
-    uint8_t  in();
-
     // get a character from the terminal in the console specific format
     // corresponds to GETC:
     // retval<63:61>
@@ -149,12 +136,6 @@
     //
     // Interrupts are cleared when the buffer is empty.
     uint64_t console_in();
-
-    // Send a character to the terminal
-    void out(char c);
-
-    // Ask the terminal if data is available
-    bool dataAvailable() { return !rxbuf.empty(); }
 };
 
 #endif // __DEV_TERMINAL_HH__
diff --git a/src/dev/uart.cc b/src/dev/uart.cc
index 0190044..ff952cc 100644
--- a/src/dev/uart.cc
+++ b/src/dev/uart.cc
@@ -34,18 +34,17 @@
 
 #include "dev/uart.hh"
 
-#include "dev/platform.hh"
-#include "dev/terminal.hh"
+#include "dev/serial.hh"
 
 using namespace std;
 
 Uart::Uart(const Params *p, Addr pio_size)
     : BasicPioDevice(p, pio_size),
-      platform(p->platform), term(p->terminal),
+      platform(p->platform), device(p->device),
       callbackDataAvail(this)
 {
     status = 0;
 
-    // setup terminal callbacks
-    term->regDataAvailCallback(&callbackDataAvail);
+    // setup serial device callbacks
+    device->regInterfaceCallback(&callbackDataAvail);
 }
diff --git a/src/dev/uart.hh b/src/dev/uart.hh
index 6c9c08e..bf3dcf7 100644
--- a/src/dev/uart.hh
+++ b/src/dev/uart.hh
@@ -35,8 +35,9 @@
 #ifndef __UART_HH__
 #define __UART_HH__
 
+#include "base/callback.hh"
 #include "dev/io_device.hh"
-#include "dev/terminal.hh"
+#include "dev/serial.hh"
 #include "params/Uart.hh"
 
 class Platform;
@@ -49,7 +50,7 @@
   protected:
     int status;
     Platform *platform;
-    Terminal *term;
+    SerialDevice *device;
 
   public:
     typedef UartParams Params;
diff --git a/src/dev/uart8250.cc b/src/dev/uart8250.cc
index 482135c..f99b247 100644
--- a/src/dev/uart8250.cc
+++ b/src/dev/uart8250.cc
@@ -42,7 +42,6 @@
 #include "config/the_isa.hh"
 #include "debug/Uart.hh"
 #include "dev/platform.hh"
-#include "dev/terminal.hh"
 #include "mem/packet.hh"
 #include "mem/packet_access.hh"
 
@@ -108,8 +107,8 @@
     switch (daddr) {
         case 0x0:
             if (!(LCR & 0x80)) { // read byte
-                if (term->dataAvailable())
-                    pkt->set(term->in());
+                if (device->dataAvailable())
+                    pkt->set(device->readData());
                 else {
                     pkt->set((uint8_t)0);
                     // A limited amount of these are ok.
@@ -118,7 +117,7 @@
                 status &= ~RX_INT;
                 platform->clearConsoleInt();
 
-                if (term->dataAvailable() && (IER & UART_IER_RDI))
+                if (device->dataAvailable() && (IER & UART_IER_RDI))
                     scheduleIntr(&rxIntrEvent);
             } else { // dll divisor latch
                ;
@@ -154,7 +153,7 @@
             uint8_t lsr;
             lsr = 0;
             // check if there are any bytes to be read
-            if (term->dataAvailable())
+            if (device->dataAvailable())
                 lsr = UART_LSR_DR;
             lsr |= UART_LSR_TEMT | UART_LSR_THRE;
             pkt->set(lsr);
@@ -190,7 +189,7 @@
     switch (daddr) {
         case 0x0:
             if (!(LCR & 0x80)) { // write byte
-                term->out(pkt->get<uint8_t>());
+                device->writeData(pkt->get<uint8_t>());
                 platform->clearConsoleInt();
                 status &= ~TX_INT;
                 if (UART_IER_THRI & IER)
@@ -225,7 +224,7 @@
                     status &= ~TX_INT;
                 }
 
-                if ((UART_IER_RDI & IER) && term->dataAvailable()) {
+                if ((UART_IER_RDI & IER) && device->dataAvailable()) {
                     DPRINTF(Uart, "IER: IER_RDI set, scheduling RX intrrupt\n");
                     scheduleIntr(&rxIntrEvent);
                 } else {
diff --git a/src/dev/virtio/VirtIOConsole.py b/src/dev/virtio/VirtIOConsole.py
index cd73d65..bce5e1d 100644
--- a/src/dev/virtio/VirtIOConsole.py
+++ b/src/dev/virtio/VirtIOConsole.py
@@ -40,6 +40,7 @@
 from m5.params import *
 from m5.proxy import *
 from VirtIO import VirtIODeviceBase
+from Serial import SerialDevice
 
 class VirtIOConsole(VirtIODeviceBase):
     type = 'VirtIOConsole'
@@ -48,4 +49,4 @@
     qRecvSize = Param.Unsigned(16, "Receive queue size (descriptors)")
     qTransSize = Param.Unsigned(16, "Transmit queue size (descriptors)")
 
-    terminal = Param.Terminal(Parent.any, "The terminal")
+    device = Param.SerialDevice("Serial device attached to this device")
diff --git a/src/dev/virtio/console.cc b/src/dev/virtio/console.cc
index e3428e9..1027925 100644
--- a/src/dev/virtio/console.cc
+++ b/src/dev/virtio/console.cc
@@ -47,7 +47,7 @@
     : VirtIODeviceBase(params, ID_CONSOLE, sizeof(Config), F_SIZE),
       qRecv(params->system->physProxy, params->qRecvSize, *this),
       qTrans(params->system->physProxy, params->qTransSize, *this),
-      term(*params->terminal), callbackDataAvail(qRecv)
+      device(*params->device), callbackDataAvail(qRecv)
 {
     registerQueue(qRecv);
     registerQueue(qTrans);
@@ -55,7 +55,7 @@
     config.cols = 80;
     config.rows = 24;
 
-    term.regDataAvailCallback(&callbackDataAvail);
+    device.regInterfaceCallback(&callbackDataAvail);
 }
 
 
@@ -81,11 +81,11 @@
     // get free descriptors (i.e., there are buffers available to
     // send) from the guest.
     VirtDescriptor *d;
-    while (parent.term.dataAvailable() && (d = consumeDescriptor())) {
+    while (parent.device.dataAvailable() && (d = consumeDescriptor())) {
         DPRINTF(VIOConsole, "Got descriptor (len: %i)\n", d->size());
         size_t len(0);
-        while (parent.term.dataAvailable() && len < d->size()) {
-            uint8_t in(parent.term.in());
+        while (parent.device.dataAvailable() && len < d->size()) {
+            uint8_t in(parent.device.readData());
             d->chainWrite(len, &in, sizeof(uint8_t));
             ++len;
         }
@@ -108,7 +108,7 @@
     uint8_t data[size];
     desc->chainRead(0, data, size);
     for (int i = 0; i < desc->size(); ++i)
-        parent.term.out(data[i]);
+        parent.device.writeData(data[i]);
 
     // Tell the guest that we are done with this descriptor.
     produceDescriptor(desc, 0);
diff --git a/src/dev/virtio/console.hh b/src/dev/virtio/console.hh
index e41ff9a..720b600 100644
--- a/src/dev/virtio/console.hh
+++ b/src/dev/virtio/console.hh
@@ -40,8 +40,8 @@
 #ifndef __DEV_VIRTIO_CONSOLE_HH__
 #define __DEV_VIRTIO_CONSOLE_HH__
 
+#include "dev/serial.hh"
 #include "dev/virtio/base.hh"
-#include "dev/terminal.hh"
 
 struct VirtIOConsoleParams;
 
@@ -147,7 +147,7 @@
     TermTransQueue qTrans;
 
   protected:
-    Terminal &term;
+    SerialDevice &device;
     MakeCallback<VirtIOConsole::TermRecvQueue,
                  &VirtIOConsole::TermRecvQueue::trySend> callbackDataAvail;
 };