diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index 747aa51..0244f9b 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -24,6 +24,7 @@ OBJS	+= main.o
 OBJS	+= mmio.o
 OBJS	+= pci.o
 OBJS	+= util.o
+OBJS	+= term.o
 
 DEPS	:= $(patsubst %.o,%.d,$(OBJS))
 
diff --git a/tools/kvm/include/kvm/term.h b/tools/kvm/include/kvm/term.h
new file mode 100644
index 0000000..4d580e1
--- /dev/null
+++ b/tools/kvm/include/kvm/term.h
@@ -0,0 +1,17 @@
+#ifndef KVM__TERM_H
+#define KVM__TERM_H
+
+#include <sys/uio.h>
+
+#define CONSOLE_8250	1
+#define CONSOLE_VIRTIO	2
+
+int term_putc_iov(int who, struct iovec *iov, int iovcnt);
+int term_getc_iov(int who, struct iovec *iov, int iovcnt);
+int term_putc(int who, char *addr, int cnt);
+int term_getc(int who);
+
+bool term_readable(int who);
+void term_init(void);
+
+#endif /* KVM__TERM_H */
diff --git a/tools/kvm/main.c b/tools/kvm/main.c
index 7beb6a6..46fb9fd 100644
--- a/tools/kvm/main.c
+++ b/tools/kvm/main.c
@@ -5,9 +5,9 @@
 #include "kvm/disk-image.h"
 #include "kvm/util.h"
 #include "kvm/pci.h"
+#include "kvm/term.h"
 
 #include <inttypes.h>
-#include <termios.h>
 #include <signal.h>
 #include <stdint.h>
 #include <stdlib.h>
@@ -29,32 +29,6 @@ static void usage(char *argv[])
 
 static struct kvm *kvm;
 
-static struct termios orig_term;
-
-static void setup_console(void)
-{
-	struct termios term;
-
-	if (tcgetattr(STDIN_FILENO, &orig_term) < 0)
-		die("unable to save initial standard input settings");
-
-	term			= orig_term;
-
-	term.c_lflag		&= ~(ICANON|ECHO);
-
-	tcsetattr(STDIN_FILENO, TCSANOW, &term);
-}
-
-static void cleanup_console(void)
-{
-	tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
-}
-
-static void shutdown(void)
-{
-	cleanup_console();
-}
-
 static void handle_sigint(int sig)
 {
 	exit(1);
@@ -65,7 +39,6 @@ static void handle_sigquit(int sig)
 	kvm__show_registers(kvm);
 	kvm__show_code(kvm);
 	kvm__show_page_tables(kvm);
-
 	kvm__delete(kvm);
 
 	exit(1);
@@ -92,10 +65,6 @@ int main(int argc, char *argv[])
 	signal(SIGQUIT, handle_sigquit);
 	signal(SIGINT, handle_sigint);
 
-	setup_console();
-
-	atexit(shutdown);
-
 	for (i = 1; i < argc; i++) {
 		if (option_matches(argv[i], "--kernel=")) {
 			kernel_filename	= &argv[i][9];
@@ -138,6 +107,8 @@ int main(int argc, char *argv[])
 	if (!kernel_filename)
 		usage(argv);
 
+	term_init();
+
 	kvm = kvm__init(kvm_dev, ram_size);
 
 	if (image_filename) {
diff --git a/tools/kvm/term.c b/tools/kvm/term.c
new file mode 100644
index 0000000..0ac53c0
--- /dev/null
+++ b/tools/kvm/term.c
@@ -0,0 +1,89 @@
+#include <poll.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/uio.h>
+
+#include "kvm/read-write.h"
+#include "kvm/term.h"
+#include "kvm/util.h"
+
+static struct termios orig_term;
+
+int active_console = CONSOLE_8250;
+
+int term_getc(int who)
+{
+	int c;
+
+	if (who != active_console)
+		return -1;
+
+	if (read_in_full(STDIN_FILENO, &c, 1) < 0)
+		return -1;
+	return c;
+}
+
+int term_putc(int who, char *addr, int cnt)
+{
+	if (who != active_console)
+		return -1;
+
+	while (cnt--) {
+		fprintf(stdout, "%c", *addr++);
+	}
+
+	fflush(stdout);
+	return cnt;
+}
+
+int term_getc_iov(int who, struct iovec *iov, int iovcnt)
+{
+	if (who != active_console)
+		return -1;
+
+	return readv(STDIN_FILENO, iov, iovcnt);
+}
+
+int term_putc_iov(int who, struct iovec *iov, int iovcnt)
+{
+	if (who != active_console)
+		return -1;
+
+	return writev(STDOUT_FILENO, iov, iovcnt);
+}
+
+bool term_readable(int who)
+{
+	struct pollfd pollfd = (struct pollfd) {
+		.fd	= STDIN_FILENO,
+		.events	= POLLIN,
+		.revents = 0,
+	};
+
+	if (who != active_console)
+		return false;
+
+	return poll(&pollfd, 1, 0) > 0;
+}
+
+static void term_cleanup(void)
+{
+	tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
+}
+
+void term_init(void)
+{
+	struct termios term;
+
+	if (tcgetattr(STDIN_FILENO, &orig_term) < 0)
+		die("unable to save initial standard input settings");
+
+	term = orig_term;
+	term.c_lflag &= ~(ICANON |ECHO | ISIG);
+	tcsetattr(STDIN_FILENO, TCSANOW, &term);
+
+	atexit(term_cleanup);
+}
+
