[PATCH] modify lguest console to support multiple hvc's

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

 



From: Eric Van Hensbergen <[email protected]>

This was a quick modification I did of lguest to be able to support multiple
HVC channels for some experiements I was doing.  I'm not sure if this is more
generally useful, so I'm posting it to the list in case someone else has a
need for it.

Signed-off-by: Eric Van Hensbergen <[email protected]>
---
 Documentation/lguest/lguest.c |  161 ++++++++++++++++++++++++-----------------
 drivers/char/hvc_lguest.c     |   57 +++++++++------
 2 files changed, 129 insertions(+), 89 deletions(-)

diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c
index f791840..c6a3e4d 100644
--- a/Documentation/lguest/lguest.c
+++ b/Documentation/lguest/lguest.c
@@ -690,12 +690,14 @@ static void restore_term(void)
 }
 
 /* We associate some data with the console for our exit hack. */
-struct console_abort
+struct console_priv
 {
+	/* which console we are */
+	int index;
 	/* How many times have they hit ^C? */
-	int count;
+	int a_count;
 	/* When did they start? */
-	struct timeval start;
+	struct timeval a_start;
 };
 
 /* This is the routine which handles console input (ie. stdin). */
@@ -705,11 +707,12 @@ static bool handle_console_input(int fd, struct device *dev)
 	int len;
 	unsigned int num;
 	struct iovec iov[LGUEST_MAX_DMA_SECTIONS];
-	struct console_abort *abort = dev->priv;
+	struct console_priv *cons = dev->priv;
 
 	/* First we get the console buffer from the Guest.  The key is dev->mem
-	 * which was set to 0 in setup_console(). */
-	lenp = get_dma_buffer(fd, dev->mem, iov, &num, &irq);
+	 * plus the console index adjusted to be a multiple of 4 because lguest
+	 * wants keys to be a multiple of 4 */
+	lenp = get_dma_buffer(fd, dev->mem+(cons->index*4), iov, &num, &irq);
 	if (!lenp) {
 		/* If it's not ready for input, warn and set up to discard. */
 		warn("console: no dma buffer!");
@@ -734,39 +737,44 @@ static bool handle_console_input(int fd, struct device *dev)
 		trigger_irq(fd, irq);
 	}
 
-	/* Three ^C within one second?  Exit.
-	 *
-	 * This is such a hack, but works surprisingly well.  Each ^C has to be
-	 * in a buffer by itself, so they can't be too fast.  But we check that
-	 * we get three within about a second, so they can't be too slow. */
-	if (len == 1 && ((char *)iov[0].iov_base)[0] == 3) {
-		if (!abort->count++)
-			gettimeofday(&abort->start, NULL);
-		else if (abort->count == 3) {
-			struct timeval now;
-			gettimeofday(&now, NULL);
-			if (now.tv_sec <= abort->start.tv_sec+1) {
-				u32 args[] = { LHREQ_BREAK, 0 };
-				/* Close the fd so Waker will know it has to
-				 * exit. */
-				close(waker_fd);
-				/* Just in case waker is blocked in BREAK, send
-				 * unbreak now. */
-				write(fd, args, sizeof(args));
-				exit(2);
+	/* Only do interrupt hack and restore_term() on initial console */
+	if (cons->index == 0) {
+		/* Three ^C within one second?  Exit.
+		 *
+		 * This is such a hack, but works surprisingly well.  Each ^C
+		 * has to be in a buffer by itself, so they can't be too fast.
+		 * But we check that we get three within about a second, so
+		 * they can't be too slow. */
+		if (len == 1 && ((char *)iov[0].iov_base)[0] == 3) {
+			if (!cons->a_count++)
+				gettimeofday(&cons->a_start, NULL);
+			else if (cons->a_count == 3) {
+				struct timeval now;
+				gettimeofday(&now, NULL);
+				if (now.tv_sec <= cons->a_start.tv_sec+1) {
+					u32 args[] = { LHREQ_BREAK, 0 };
+					/* Close the fd so Waker will know it
+					 * has to exit. */
+					close(waker_fd);
+					/* Just in case waker is blocked in
+					 * BREAK, send unbreak now. */
+					write(fd, args, sizeof(args));
+					exit(2);
+				}
+				cons->a_count = 0;
 			}
-			abort->count = 0;
+		} else
+			/* Any other key resets the abort counter. */
+			cons->a_count = 0;
+
+		/* Now, if we didn't read anything, put the input terminal
+		 * back and return failure (meaning, don't call us again). */
+		if (!len) {
+			restore_term();
+			return false;
 		}
-	} else
-		/* Any other key resets the abort counter. */
-		abort->count = 0;
-
-	/* Now, if we didn't read anything, put the input terminal back and
-	 * return failure (meaning, don't call us again). */
-	if (!len) {
-		restore_term();
-		return false;
 	}
+
 	/* Everything went OK! */
 	return true;
 }
@@ -777,7 +785,7 @@ static u32 handle_console_output(int fd, const struct iovec *iov,
 {
 	/* Whatever the Guest sends, write it to standard output.  Return the
 	 * number of bytes written. */
-	return writev(STDOUT_FILENO, iov, num);
+	return writev(dev->fd, iov, num);
 }
 
 /* Guest->Host network output is also pretty easy. */
@@ -1044,34 +1052,56 @@ static struct device *new_device(struct device_list *devices,
 	return dev;
 }
 
+static u32 str2ip(const char *ipaddr)
+{
+	unsigned int byte[4];
+
+	sscanf(ipaddr, "%u.%u.%u.%u", &byte[0], &byte[1], &byte[2], &byte[3]);
+	return (byte[0] << 24) | (byte[1] << 16) | (byte[2] << 8) | byte[3];
+}
+
 /* Our first setup routine is the console.  It's a fairly simple device, but
  * UNIX tty handling makes it uglier than it could be. */
-static void setup_console(struct device_list *devices)
+static void setup_console(char *arg, struct device_list *devices)
 {
 	struct device *dev;
-
-	/* If we can save the initial standard input settings... */
-	if (tcgetattr(STDIN_FILENO, &orig_term) == 0) {
-		struct termios term = orig_term;
-		/* Then we turn off echo, line buffering and ^C etc.  We want a
-		 * raw input stream to the Guest. */
-		term.c_lflag &= ~(ISIG|ICANON|ECHO);
-		tcsetattr(STDIN_FILENO, TCSANOW, &term);
-		/* If we exit gracefully, the original settings will be
-		 * restored so the user can see what they're typing. */
-		atexit(restore_term);
+	int fd;
+	static int hvcs_index;
+	struct console_priv *cons;
+
+	if (arg == NULL) {	/* default console - do the tty stuff */
+		/* If we can save the initial standard input settings... */
+		if (tcgetattr(STDIN_FILENO, &orig_term) == 0) {
+			struct termios term = orig_term;
+			/* Then we turn off echo, line buffering and ^C etc.
+			 * We want a raw input stream to the Guest. */
+			term.c_lflag &= ~(ISIG|ICANON|ECHO);
+			tcsetattr(STDIN_FILENO, TCSANOW, &term);
+			/* If we exit gracefully, the original settings will be
+			 * restored so the user can see what they're typing. */
+			atexit(restore_term);
+		}
+		fd = STDIN_FILENO;
+	} else {
+		/* create or open an exisitng fifo */
+		mkfifo(arg, S_IWUSR | S_IRUSR);
+		fd = open_or_die(arg, O_RDWR);
 	}
 
 	/* We don't currently require any memory for the console, so we ask for
 	 * 0 pages. */
 	dev = new_device(devices, LGUEST_DEVICE_T_CONSOLE, 0, 0,
-			 STDIN_FILENO, handle_console_input,
-			 LGUEST_CONSOLE_DMA_KEY, handle_console_output);
-	/* We store the console state in dev->priv, and initialize it. */
-	dev->priv = malloc(sizeof(struct console_abort));
-	((struct console_abort *)dev->priv)->count = 0;
-	verbose("device %p: console\n",
-		(void *)(dev->desc->pfn * getpagesize()));
+			 fd, handle_console_input,
+			 LGUEST_CONSOLE_DMA_KEY+(hvcs_index*4),
+			 handle_console_output);
+	dev->fd = fd;
+	/* We store the console state in dev->priv, and initialize */
+	dev->priv = malloc(sizeof(struct console_priv));
+	cons = dev->priv;
+	cons->a_count = 0;
+	cons->index = hvcs_index++;
+	verbose("device %p: console %d\n",
+		(void *)(dev->desc->pfn * getpagesize()), cons->index);
 }
 
 /* Setting up a block file is also fairly straightforward. */
@@ -1184,14 +1214,6 @@ static void setup_net_file(const char *filename,
 }
 /*:*/
 
-static u32 str2ip(const char *ipaddr)
-{
-	unsigned int byte[4];
-
-	sscanf(ipaddr, "%u.%u.%u.%u", &byte[0], &byte[1], &byte[2], &byte[3]);
-	return (byte[0] << 24) | (byte[1] << 16) | (byte[2] << 8) | byte[3];
-}
-
 /* This code is "adapted" from libbridge: it attaches the Host end of the
  * network device to the bridge device specified by the command line.
  *
@@ -1369,12 +1391,14 @@ static struct option opts[] = {
 	{ "tunnet", 1, NULL, 't' },
 	{ "block", 1, NULL, 'b' },
 	{ "initrd", 1, NULL, 'i' },
+	{ "console", 1, NULL, 'c' },
 	{ NULL },
 };
 static void usage(void)
 {
 	errx(1, "Usage: lguest [--verbose] "
 	     "[--sharenet=<filename>|--tunnet=(<ipaddr>|bridge:<bridgename>)\n"
+	     "|--console=<fifopath>...\n"
 	     "|--block=<filename>|--initrd=<filename>]...\n"
 	     "<mem-in-mb> vmlinux [args...]");
 }
@@ -1431,6 +1455,9 @@ int main(int argc, char *argv[])
 		}
 	}
 
+	/* We always have at least one console device */
+	setup_console(NULL, &device_list);
+
 	/* The options are fairly straight-forward */
 	while ((c = getopt_long(argc, argv, "v", opts, NULL)) != EOF) {
 		switch (c) {
@@ -1446,6 +1473,9 @@ int main(int argc, char *argv[])
 		case 'b':
 			setup_block_file(optarg, &device_list);
 			break;
+		case 'c':
+			setup_console(optarg, &device_list);
+			break;
 		case 'i':
 			initrd_name = optarg;
 			break;
@@ -1459,9 +1489,6 @@ int main(int argc, char *argv[])
 	if (optind + 2 > argc)
 		usage();
 
-	/* We always have a console device */
-	setup_console(&device_list);
-
 	/* We start by mapping anonymous pages over all of guest-physical
 	 * memory range.  This fills it with 0, and ensures that the Guest
 	 * won't be killed when it tries to access it. */
diff --git a/drivers/char/hvc_lguest.c b/drivers/char/hvc_lguest.c
index 3d6bd0b..b8b026a 100644
--- a/drivers/char/hvc_lguest.c
+++ b/drivers/char/hvc_lguest.c
@@ -38,14 +38,15 @@
 #include <asm/paravirt.h>
 #include "hvc_console.h"
 
-/*D:340 This is our single console input buffer, with associated "struct
- * lguest_dma" referring to it.  Note the 0-terminated length array, and the
- * use of physical address for the buffer itself. */
-static char inbuf[256];
-static struct lguest_dma cons_input = { .used_len = 0,
-					.addr[0] = __pa(inbuf),
-					.len[0] = sizeof(inbuf),
-					.len[1] = 0 };
+#define MAX_LGUEST_CONS 2
+
+/*D:340 Each console instance has an input buffer and associated offset
+associated with it.  Uck -- still static, can't we fix this? */
+static struct lguest_console {
+	struct lguest_dma cons_input;
+	int cons_offset;
+	void *inbuf;
+} lguest_con[MAX_LGUEST_CONS];
 
 /*D:310 The put_chars() callback is pretty straightforward.
  *
@@ -54,7 +55,8 @@ static struct lguest_dma cons_input = { .used_len = 0,
  * the data to (Host) buffers attached to the console key.  Usually a device's
  * key is a physical address within the device's memory, but because the
  * console device doesn't have any associated physical memory, we use the
- * LGUEST_CONSOLE_DMA_KEY constant (aka 0). */
+ * LGUEST_CONSOLE_DMA_KEY constant (aka 0) plus the console index multiplied
+ * by 4 (because keys must be aligned along 32-bit addresses). */
 static int put_chars(u32 vtermno, const char *buf, int count)
 {
 	struct lguest_dma dma;
@@ -66,7 +68,7 @@ static int put_chars(u32 vtermno, const char *buf, int count)
 	dma.len[1] = 0;
 	dma.addr[0] = __pa(buf);
 
-	lguest_send_dma(LGUEST_CONSOLE_DMA_KEY, &dma);
+	lguest_send_dma(LGUEST_CONSOLE_DMA_KEY+(vtermno*4), &dma);
 	/* We're expected to return the amount of data we wrote: all of it. */
 	return count;
 }
@@ -80,26 +82,27 @@ static int put_chars(u32 vtermno, const char *buf, int count)
  * partially-read buffers. */
 static int get_chars(u32 vtermno, char *buf, int count)
 {
-	static int cons_offset;
+	struct lguest_console *lgc = &lguest_con[vtermno];
 
 	/* Nothing left to see here... */
-	if (!cons_input.used_len)
+	if (!lgc->cons_input.used_len)
 		return 0;
 
 	/* You want more than we have to give?  Well, try wanting less! */
-	if (cons_input.used_len - cons_offset < count)
-		count = cons_input.used_len - cons_offset;
+	if (lgc->cons_input.used_len - lgc->cons_offset < count)
+		count = lgc->cons_input.used_len - lgc->cons_offset;
 
 	/* Copy across to their buffer and increment offset. */
-	memcpy(buf, inbuf + cons_offset, count);
-	cons_offset += count;
+	memcpy(buf, lgc->inbuf + lgc->cons_offset, count);
+	lgc->cons_offset += count;
 
 	/* Finished?  Zero offset, and reset cons_input so Host will use it
 	 * again. */
-	if (cons_offset == cons_input.used_len) {
-		cons_offset = 0;
-		cons_input.used_len = 0;
+	if (lgc->cons_offset == lgc->cons_input.used_len) {
+		lgc->cons_offset = 0;
+		lgc->cons_input.used_len = 0;
 	}
+
 	return count;
 }
 /*:*/
@@ -132,6 +135,14 @@ console_initcall(cons_init);
 static int lguestcons_probe(struct lguest_device *lgdev)
 {
 	int err;
+	static int index;
+	struct lguest_console *lgc = &lguest_con[index];
+
+	lgc->inbuf = (void *) get_zeroed_page(GFP_KERNEL);
+	lgc->cons_input.used_len = 0;
+	lgc->cons_input.addr[0] = (unsigned long) __pa(lgc->inbuf);
+	lgc->cons_input.len[0] = PAGE_SIZE;
+	lgc->cons_input.len[1] = 0;
 
 	/* The first argument of hvc_alloc() is the virtual console number, so
 	 * we use zero.  The second argument is the interrupt number.
@@ -140,17 +151,19 @@ static int lguestcons_probe(struct lguest_device *lgdev)
 	 * and get_chars() pointers.  The final argument is the output buffer
 	 * size: we use 256 and expect the Host to have room for us to send
 	 * that much. */
-	lgdev->private = hvc_alloc(0, lgdev_irq(lgdev), &lguest_cons, 256);
+	lgdev->private = hvc_alloc(index, lgdev_irq(lgdev), &lguest_cons,
+		PAGE_SIZE);
 	if (IS_ERR(lgdev->private))
 		return PTR_ERR(lgdev->private);
 
 	/* We bind a single DMA buffer at key LGUEST_CONSOLE_DMA_KEY.
 	 * "cons_input" is that statically-initialized global DMA buffer we saw
 	 * above, and we also give the interrupt we want. */
-	err = lguest_bind_dma(LGUEST_CONSOLE_DMA_KEY, &cons_input, 1,
-			      lgdev_irq(lgdev));
+	err = lguest_bind_dma(LGUEST_CONSOLE_DMA_KEY+(index*4),
+			      &lgc->cons_input, 1, lgdev_irq(lgdev));
 	if (err)
 		printk("lguest console: failed to bind buffer.\n");
+	index++;
 	return err;
 }
 /* Note the use of lgdev_irq() for the interrupt number.  We tell hvc_alloc()
-- 
1.5.0.gddff-dirty

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux