[PATCH] kvm tools: Allow remapping guest TTY into host PTS

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds the '-tty' option to 'kvm run' which allows the user to
remap a guest TTY into a PTS on the host.

Usage:
	'kvm run --tty [id] [other options]'

The tty will be mapped to a pts and will be printed on the screen:
	'  Info: Assigned terminal 1 to pty /dev/pts/X'

At this point, it is possible to communicate with the guest using that pty.

This is useful for debugging guest kernel using KGDB:

1. Run the guest:
	'kvm run -k [vmlinuz] -p "kdbgoc=ttyS1 kdbgwait" --tty 1'

And see which PTY got assigned to ttyS1.

2. Run GDB on the host:
	'gdb [vmlinuz]'

3. Connect to the guest (from within GDB):
	'target remote /dev/pty/X'

4. Start debugging! (enter 'continue' to continue boot).

Cc: David Evensky <evensky@xxxxxxxxxxxxxxxxxxxx>
Signed-off-by: Sasha Levin <levinsasha928@xxxxxxxxx>
---
 tools/kvm/Makefile           |    1 +
 tools/kvm/builtin-run.c      |   12 ++++++++
 tools/kvm/hw/serial.c        |   46 ++++++++++++++++++--------------
 tools/kvm/include/kvm/term.h |   11 ++++---
 tools/kvm/term.c             |   60 +++++++++++++++++++++++++++++++++--------
 tools/kvm/virtio/console.c   |    6 ++--
 6 files changed, 96 insertions(+), 40 deletions(-)

diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index efa032d..fef624d 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -115,6 +115,7 @@ OBJS	+= bios/bios-rom.o
 
 LIBS	+= -lrt
 LIBS	+= -lpthread
+LIBS	+= -lutil
 
 # Additional ARCH settings for x86
 ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
diff --git a/tools/kvm/builtin-run.c b/tools/kvm/builtin-run.c
index 5dafb15..b5c63ca 100644
--- a/tools/kvm/builtin-run.c
+++ b/tools/kvm/builtin-run.c
@@ -172,6 +172,15 @@ static int virtio_9p_rootdir_parser(const struct option *opt, const char *arg, i
 	return 0;
 }
 
+static int tty_parser(const struct option *opt, const char *arg, int unset)
+{
+	int tty = atoi(arg);
+
+	term_set_tty(tty);
+
+	return 0;
+}
+
 static int shmem_parser(const struct option *opt, const char *arg, int unset)
 {
 	const u64 default_size = SHMEM_DEFAULT_SIZE;
@@ -316,6 +325,9 @@ static const struct option options[] = {
 	OPT_STRING('\0', "console", &console, "serial or virtio",
 			"Console to use"),
 	OPT_STRING('\0', "dev", &dev, "device_file", "KVM device file"),
+	OPT_CALLBACK('\0', "tty", NULL, "tty id",
+		     "Remap guest TTY into a pty on the host",
+		     tty_parser),
 
 	OPT_GROUP("Kernel options:"),
 	OPT_STRING('k', "kernel", &kernel_filename, "kernel",
diff --git a/tools/kvm/hw/serial.c b/tools/kvm/hw/serial.c
index b3b233f..11fa5d4 100644
--- a/tools/kvm/hw/serial.c
+++ b/tools/kvm/hw/serial.c
@@ -14,6 +14,7 @@
 
 struct serial8250_device {
 	pthread_mutex_t		mutex;
+	u8			id;
 
 	u16			iobase;
 	u8			irq;
@@ -42,6 +43,7 @@ static struct serial8250_device devices[] = {
 	[0]	= {
 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
 
+		.id			= 0,
 		.iobase			= 0x3f8,
 		.irq			= 4,
 
@@ -51,6 +53,7 @@ static struct serial8250_device devices[] = {
 	[1]	= {
 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
 
+		.id			= 1,
 		.iobase			= 0x2f8,
 		.irq			= 3,
 
@@ -60,6 +63,7 @@ static struct serial8250_device devices[] = {
 	[2]	= {
 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
 
+		.id			= 2,
 		.iobase			= 0x3e8,
 		.irq			= 4,
 
@@ -69,6 +73,7 @@ static struct serial8250_device devices[] = {
 	[3]	= {
 		.mutex			= PTHREAD_MUTEX_INITIALIZER,
 
+		.id			= 3,
 		.iobase			= 0x2e8,
 		.irq			= 3,
 
@@ -111,10 +116,10 @@ static void serial8250__receive(struct kvm *kvm, struct serial8250_device *dev)
 		return;
 	}
 
-	if (!term_readable(CONSOLE_8250))
+	if (!term_readable(CONSOLE_8250, dev->id))
 		return;
 
-	c		= term_getc(CONSOLE_8250);
+	c = term_getc(CONSOLE_8250, dev->id);
 
 	if (c < 0)
 		return;
@@ -123,30 +128,31 @@ static void serial8250__receive(struct kvm *kvm, struct serial8250_device *dev)
 	dev->lsr	|= UART_LSR_DR;
 }
 
-/*
- * Interrupts are injected for ttyS0 only.
- */
 void serial8250__inject_interrupt(struct kvm *kvm)
 {
-	struct serial8250_device *dev = &devices[0];
+	int i;
 
-	mutex_lock(&dev->mutex);
+	for (i = 0; i < 4; i++) {
+		struct serial8250_device *dev = &devices[i];
 
-	serial8250__receive(kvm, dev);
+		mutex_lock(&dev->mutex);
 
-	if (dev->ier & UART_IER_RDI && dev->lsr & UART_LSR_DR)
-		dev->iir		= UART_IIR_RDI;
-	else if (dev->ier & UART_IER_THRI)
-		dev->iir		= UART_IIR_THRI;
-	else
-		dev->iir		= UART_IIR_NO_INT;
+		serial8250__receive(kvm, dev);
 
-	if (dev->iir != UART_IIR_NO_INT) {
-		kvm__irq_line(kvm, dev->irq, 0);
-		kvm__irq_line(kvm, dev->irq, 1);
-	}
+		if (dev->ier & UART_IER_RDI && dev->lsr & UART_LSR_DR)
+			dev->iir		= UART_IIR_RDI;
+		else if (dev->ier & UART_IER_THRI)
+			dev->iir		= UART_IIR_THRI;
+		else
+			dev->iir		= UART_IIR_NO_INT;
 
-	mutex_unlock(&dev->mutex);
+		if (dev->iir != UART_IIR_NO_INT) {
+			kvm__irq_line(kvm, dev->irq, 0);
+			kvm__irq_line(kvm, dev->irq, 1);
+		}
+
+		mutex_unlock(&dev->mutex);
+	}
 }
 
 void serial8250__inject_sysrq(struct kvm *kvm)
@@ -217,7 +223,7 @@ static bool serial8250_out(struct ioport *ioport, struct kvm *kvm, u16 port, voi
 			char *addr = data;
 
 			if (!(dev->mcr & UART_MCR_LOOP))
-				term_putc(CONSOLE_8250, addr, size);
+				term_putc(CONSOLE_8250, addr, size, dev->id);
 
 			dev->iir		= UART_IIR_NO_INT;
 			break;
diff --git a/tools/kvm/include/kvm/term.h b/tools/kvm/include/kvm/term.h
index 4d580e1..37ec731 100644
--- a/tools/kvm/include/kvm/term.h
+++ b/tools/kvm/include/kvm/term.h
@@ -6,12 +6,13 @@
 #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);
+int term_putc_iov(int who, struct iovec *iov, int iovcnt, int term);
+int term_getc_iov(int who, struct iovec *iov, int iovcnt, int term);
+int term_putc(int who, char *addr, int cnt, int term);
+int term_getc(int who, int term);
 
-bool term_readable(int who);
+bool term_readable(int who, int term);
+void term_set_tty(int term);
 void term_init(void);
 
 #endif /* KVM__TERM_H */
diff --git a/tools/kvm/term.c b/tools/kvm/term.c
index fa4382d..45e8bab 100644
--- a/tools/kvm/term.c
+++ b/tools/kvm/term.c
@@ -5,6 +5,8 @@
 #include <unistd.h>
 #include <sys/uio.h>
 #include <signal.h>
+#include <pty.h>
+#include <utmp.h>
 
 #include "kvm/read-write.h"
 #include "kvm/term.h"
@@ -20,14 +22,16 @@ bool term_got_escape	= false;
 
 int active_console;
 
-int term_getc(int who)
+int term_fds[4][2];
+
+int term_getc(int who, int term)
 {
 	int c;
 
 	if (who != active_console)
 		return -1;
 
-	if (read_in_full(STDIN_FILENO, &c, 1) < 0)
+	if (read_in_full(term_fds[term][0], &c, 1) < 0)
 		return -1;
 
 	c &= 0xff;
@@ -48,26 +52,27 @@ int term_getc(int who)
 	return c;
 }
 
-int term_putc(int who, char *addr, int cnt)
+int term_putc(int who, char *addr, int cnt, int term)
 {
+	int ret;
+
 	if (who != active_console)
 		return -1;
 
 	while (cnt--)
-		fprintf(stdout, "%c", *addr++);
+		ret = write(term_fds[term][1], addr++, 1);
 
-	fflush(stdout);
 	return cnt;
 }
 
-int term_getc_iov(int who, struct iovec *iov, int iovcnt)
+int term_getc_iov(int who, struct iovec *iov, int iovcnt, int term)
 {
 	int c;
 
 	if (who != active_console)
 		return 0;
 
-	c = term_getc(who);
+	c = term_getc(who, term);
 
 	if (c < 0)
 		return 0;
@@ -77,18 +82,18 @@ int term_getc_iov(int who, struct iovec *iov, int iovcnt)
 	return sizeof(char);
 }
 
-int term_putc_iov(int who, struct iovec *iov, int iovcnt)
+int term_putc_iov(int who, struct iovec *iov, int iovcnt, int term)
 {
 	if (who != active_console)
 		return 0;
 
-	return writev(STDOUT_FILENO, iov, iovcnt);
+	return writev(term_fds[term][1], iov, iovcnt);
 }
 
-bool term_readable(int who)
+bool term_readable(int who, int term)
 {
 	struct pollfd pollfd = (struct pollfd) {
-		.fd	= STDIN_FILENO,
+		.fd	= term_fds[term][0],
 		.events	= POLLIN,
 		.revents = 0,
 	};
@@ -101,7 +106,10 @@ bool term_readable(int who)
 
 static void term_cleanup(void)
 {
-	tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
+	int i;
+
+	for (i = 0; i < 4; i++)
+		tcsetattr(term_fds[i][0], TCSANOW, &orig_term);
 }
 
 static void term_sig_cleanup(int sig)
@@ -111,9 +119,31 @@ static void term_sig_cleanup(int sig)
 	raise(sig);
 }
 
+void term_set_tty(int term)
+{
+	struct termios orig_term;
+	int master, slave;
+	char new_pty[PATH_MAX];
+
+	if (tcgetattr(STDIN_FILENO, &orig_term) < 0)
+		die("unable to save initial standard input settings");
+
+	orig_term.c_lflag &= ~(ICANON | ECHO | ISIG);
+
+	if (openpty(&master, &slave, new_pty, &orig_term, NULL) < 0)
+		return;
+
+	close(slave);
+
+	pr_info("Assigned terminal %d to pty %s\n", term, new_pty);
+
+	term_fds[term][0] = term_fds[term][1] = master;
+}
+
 void term_init(void)
 {
 	struct termios term;
+	int i;
 
 	if (tcgetattr(STDIN_FILENO, &orig_term) < 0)
 		die("unable to save initial standard input settings");
@@ -122,6 +152,12 @@ void term_init(void)
 	term.c_lflag &= ~(ICANON | ECHO | ISIG);
 	tcsetattr(STDIN_FILENO, TCSANOW, &term);
 
+	for (i = 0; i < 4; i++)
+		if (term_fds[i][0] == 0) {
+			term_fds[i][0] = STDIN_FILENO;
+			term_fds[i][1] = STDOUT_FILENO;
+		}
+
 	signal(SIGTERM, term_sig_cleanup);
 	atexit(term_cleanup);
 }
diff --git a/tools/kvm/virtio/console.c b/tools/kvm/virtio/console.c
index c0ccd6c..b880162 100644
--- a/tools/kvm/virtio/console.c
+++ b/tools/kvm/virtio/console.c
@@ -67,9 +67,9 @@ static void virtio_console__inject_interrupt_callback(struct kvm *kvm, void *par
 
 	vq = param;
 
-	if (term_readable(CONSOLE_VIRTIO) && virt_queue__available(vq)) {
+	if (term_readable(CONSOLE_VIRTIO, 0) && virt_queue__available(vq)) {
 		head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
-		len = term_getc_iov(CONSOLE_VIRTIO, iov, in);
+		len = term_getc_iov(CONSOLE_VIRTIO, iov, in, 0);
 		virt_queue__set_used_elem(vq, head, len);
 		virtio_pci__signal_vq(kvm, &cdev.vpci, vq - cdev.vqs);
 	}
@@ -100,7 +100,7 @@ static void virtio_console_handle_callback(struct kvm *kvm, void *param)
 
 	while (virt_queue__available(vq)) {
 		head = virt_queue__get_iov(vq, iov, &out, &in, kvm);
-		len = term_putc_iov(CONSOLE_VIRTIO, iov, out);
+		len = term_putc_iov(CONSOLE_VIRTIO, iov, out, 0);
 		virt_queue__set_used_elem(vq, head, len);
 	}
 
-- 
1.7.6.1

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux