VMM: Added console queue service functions from lguest
authorMichael Taufen <mtaufen@gmail.com>
Wed, 4 May 2016 22:45:03 +0000 (15:45 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 13 May 2016 14:42:56 +0000 (10:42 -0400)
These will act as the basis for our console queue service functions.

Signed-off-by: Michael Taufen <mtaufen@gmail.com>
[minor formatting changes]
Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
user/vmm/virtio_lguest_console.c [new file with mode: 0644]

diff --git a/user/vmm/virtio_lguest_console.c b/user/vmm/virtio_lguest_console.c
new file mode 100644 (file)
index 0000000..dc4a8fb
--- /dev/null
@@ -0,0 +1,111 @@
+/* Virtio helper functions from linux/tools/lguest/lguest.c
+ *
+ * Copyright (C) 1991-2016, the Linux Kernel authors
+ * Copyright (c) 2016 Google Inc.
+ *
+ * Author:
+ *  Rusty Russell <rusty@rustcorp.com.au>
+ *  Michael Taufen <mtaufen@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * The code from lguest.c has been modified for Akaros.
+ *
+ * Original linux/tools/lguest/lguest.c:
+ *   https://github.com/torvalds/linux/blob/v4.5/tools/lguest/lguest.c
+ *   most recent hash on the file as of v4.5 tag:
+ *     e523caa601f4a7c2fa1ecd040db921baf7453798
+ */
+
+/* This is the routine which handles console input (ie. stdin). */
+static void console_input(struct virtqueue *vq)
+{
+       int len;
+       unsigned int head, in_num, out_num;
+       struct console_abort *abort = vq->dev->priv;
+       struct iovec iov[vq->vring.num];
+
+       /* Make sure there's a descriptor available. */
+       head = wait_for_vq_desc(vq, iov, &out_num, &in_num);
+       if (out_num)
+               bad_driver_vq(vq, "Output buffers in console in queue?");
+
+       /* Read into it.  This is where we usually wait. */
+       len = readv(STDIN_FILENO, iov, in_num);
+       if (len <= 0) {
+               /* Ran out of input? */
+               warnx("Failed to get console input, ignoring console.");
+               /*
+                * For simplicity, dying threads kill the whole Launcher.  So
+                * just nap here.
+                */
+               for (;;)
+                       pause();
+       }
+
+       /* Tell the Guest we used a buffer. */
+       add_used_and_trigger(vq, head, len);
+
+       /*
+        * 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) {
+               abort->count = 0;
+               return;
+       }
+
+       abort->count++;
+       if (abort->count == 1)
+               gettimeofday(&abort->start, NULL);
+       else if (abort->count == 3) {
+               struct timeval now;
+
+               gettimeofday(&now, NULL);
+               /* Kill all Launcher processes with SIGINT, like normal ^C */
+               if (now.tv_sec <= abort->start.tv_sec+1)
+                       kill(0, SIGINT);
+               abort->count = 0;
+       }
+}
+
+/* This is the routine which handles console output (ie. stdout). */
+static void console_output(struct virtqueue *vq)
+{
+       unsigned int head, out, in;
+       struct iovec iov[vq->vring.num];
+
+       /* We usually wait in here, for the Guest to give us something. */
+       head = wait_for_vq_desc(vq, iov, &out, &in);
+       if (in)
+               bad_driver_vq(vq, "Input buffers in console output queue?");
+
+       /* writev can return a partial write, so we loop here. */
+       while (!iov_empty(iov, out)) {
+               int len = writev(STDOUT_FILENO, iov, out);
+
+               if (len <= 0) {
+                       warn("Write to stdout gave %i (%d)", len, errno);
+                       break;
+               }
+               iov_consume(vq->dev, iov, out, NULL, len);
+       }
+
+       /*
+        * We're finished with that buffer: if we're going to sleep,
+        * wait_for_vq_desc() will prod the Guest with an interrupt.
+        */
+       add_used(vq, head, 0);
+}