]> Zhao Yanbai Git Server - minix.git/commitdiff
VBOX: add host/guest communication interface
authorDavid van Moolenbroek <david@minix3.org>
Mon, 9 Apr 2012 13:56:20 +0000 (15:56 +0200)
committerDavid van Moolenbroek <david@minix3.org>
Mon, 9 Apr 2012 13:56:20 +0000 (15:56 +0200)
This interface can be used by other system processes by means of the
newly provided vbox API in libsys.

15 files changed:
drivers/vbox/Makefile
drivers/vbox/err.c [new file with mode: 0644]
drivers/vbox/hgcm.c [new file with mode: 0644]
drivers/vbox/proto.h [new file with mode: 0644]
drivers/vbox/vbox.c
drivers/vbox/vbox.h [deleted file]
drivers/vbox/vmmdev.h [new file with mode: 0644]
etc/system.conf
include/Makefile.minix.inc
include/minix/com.h
include/minix/vbox.h [new file with mode: 0644]
include/minix/vboxif.h [new file with mode: 0644]
include/minix/vboxtype.h [new file with mode: 0644]
lib/libsys/Makefile
lib/libsys/vbox.c [new file with mode: 0644]

index abf3e773f931ec4bcc39b5492aa28365f750a310..843298bd193ae115b16fd20bf2bbde8750183f5d 100644 (file)
@@ -1,6 +1,6 @@
 # Makefile for the VirtualBox backdoor driver (VBOX)
 PROG=  vbox
-SRCS=  vbox.c
+SRCS=  vbox.c hgcm.c err.c
 
 DPADD+=        ${LIBSYS}
 LDADD+=        -lsys
diff --git a/drivers/vbox/err.c b/drivers/vbox/err.c
new file mode 100644 (file)
index 0000000..1c45c5a
--- /dev/null
@@ -0,0 +1,226 @@
+/* VirtualBox driver - by D.C. van Moolenbroek */
+#include <minix/drivers.h>
+#include <errno.h>
+
+#include "vmmdev.h"
+#include "proto.h"
+
+static int codes[] = {
+  OK,                  /*     0: success */
+  EGENERIC,            /*    -1: general failure */
+  EINVAL,              /*    -2: invalid parameter */
+  EINVAL,              /*    -3: invalid magic */
+  EBADF,               /*    -4: invalid handle */
+  ENOLCK,              /*    -5: lock failed */
+  EFAULT,              /*    -6: invalid pointer */
+  EGENERIC,            /*    -7: patching IDT failed */
+  ENOMEM,              /*    -8: memory allocation failed */
+  EEXIST,              /*    -9: already loaded */
+  EPERM,               /*   -10: permission denied */
+  EINVAL,              /*   -11: version mismatch */
+  ENOSYS,              /*   -12: function not implemented */
+  EGENERIC,            /*   -13 */
+  EGENERIC,            /*   -14 */
+  EGENERIC,            /*   -15 */
+  EGENERIC,            /*   -16 */
+  EGENERIC,            /*   -17 */
+  EGENERIC,            /*   -18: not equal */
+  EINVAL,              /*   -19: not a symlink */
+  ENOMEM,              /*   -20: temporary memory allocation failed */
+  EINVAL,              /*   -21: invalid file mode */
+  EINVAL,              /*   -22: incorrect call order */
+  EGENERIC,            /*   -23: no TLS available */
+  EGENERIC,            /*   -24: failed to set TLS */
+  EGENERIC,            /*   -25 */
+  ENOMEM,              /*   -26: contiguous memory allocation failed */
+  ENOMEM,              /*   -27: no memory available for page table */
+  EGENERIC,            /*   -28 */
+  ESRCH,               /*   -29: thread is dead */
+  EINVAL,              /*   -30: thread is not waitable */
+  EGENERIC,            /*   -31: page table not present */
+  EINVAL,              /*   -32: invalid context */
+  EBUSY,               /*   -33: timer is busy */
+  EGENERIC,            /*   -34: address conflict */
+  EGENERIC,            /*   -35: unresolved error */
+  ENOTTY,              /*   -36: invalid function */
+  EINVAL,              /*   -37: not supported */
+  EACCES,              /*   -38: access denied */
+  EINTR,               /*   -39: interrupted */
+  ETIMEDOUT,           /*   -40: timeout */
+  E2BIG,               /*   -41: buffer overflow */
+  E2BIG,               /*   -42: too much data */
+  EAGAIN,              /*   -43: max thread number reached */
+  EAGAIN,              /*   -44: max process number reached */
+  EGENERIC,            /*   -45: signal refused */
+  EBUSY,               /*   -46: signal already pending */
+  EINVAL,              /*   -47: invalid signal */
+  EGENERIC,            /*   -48: state changed */
+  EINVAL,              /*   -49: invalid UUID format */
+  ESRCH,               /*   -50: process not found */
+  OK,                  /*   -51: waited-for process is still running */
+  EAGAIN,              /*   -52: try again */
+  EGENERIC,            /*   -53: generic parse error */
+  ERANGE,              /*   -54: value out of range */
+  EOVERFLOW,           /*   -55: value too big */
+  EGENERIC,            /*   -56: no digits in string */
+  ERANGE,              /*   -57: minus sign in unsigned value */
+  EILSEQ,              /*   -58: character translation failed */
+  EGENERIC,            /*   -59: encountered unicode byte order mark */
+  EGENERIC,            /*   -60: encountered unicode surrogate */
+  EILSEQ,              /*   -61: invalid UTF8 encoding */
+  EILSEQ,              /*   -62: invalid UTF16 encoding */
+  EGENERIC,            /*   -63: no UTF16 for character */
+  ENOMEM,              /*   -64: string memory allocation failed */
+  ENOMEM,              /*   -65: UTF16 string memory allocation failed */
+  ENOMEM,              /*   -66: code point array allocation failed */
+  EBUSY,               /*   -67: cannot free in-use memory */
+  EGENERIC,            /*   -68: timer already active */
+  EGENERIC,            /*   -69: timer already suspended */
+  ECANCELED,           /*   -70: operation cancelled */
+  ENOMEM,              /*   -71: failed to initialize memory object */
+  ENOMEM,              /*   -72: low physical memory allocation failed */
+  ENOMEM,              /*   -73: physical memory allocation failed */
+  EGENERIC,            /*   -74: address too big */
+  EGENERIC,            /*   -75: memory mapping failed */
+  EGENERIC,            /*   -76: trailing characters */
+  EGENERIC,            /*   -77: trailing spaces */
+  ESRCH,               /*   -78: not found */
+  EGENERIC,            /*   -79: invalid state */
+  ENOMEM,              /*   -80: out of resources */
+  ENFILE,              /*   -81: no more handles */
+  EGENERIC,            /*   -82: preemption disabled */
+  EGENERIC,            /*   -83: end of string */
+  EGENERIC,            /*   -84: page count out of range */
+  EGENERIC,            /*   -85: object destroyed */
+  EGENERIC,            /*   -86: dangling objects */
+  EGENERIC,            /*   -87: invalid Base64 encoding */
+  EGENERIC,            /*   -88: return triggered by callback */
+  EGENERIC,            /*   -89: authentication failure */
+  EGENERIC,            /*   -90: not a power of two */
+  EGENERIC,            /*   -91: ignored */
+  EGENERIC,            /*   -92: concurrent access not allowed */
+  EGENERIC,            /*   -93: invalid reference sharing */
+  EGENERIC,            /*   -94 */
+  EGENERIC,            /*   -95: no change */
+  ENOMEM,              /*   -96: executable memory allocation failed */
+  EINVAL,              /*   -97: unsupported alignment */
+  EGENERIC,            /*   -98: duplicate */
+  EGENERIC,            /*   -99: missing */
+  EIO,                 /*  -100: I/O error */
+  ENXIO,               /*  -101: open failed */
+  ENOENT,              /*  -102: file not found */
+  ENOTDIR,             /*  -103: path not found (may also mean ENOENT) */
+  EINVAL,              /*  -104: invalid name */
+  EEXIST,              /*  -105: already exists */
+  ENFILE,              /*  -106: too many open files */
+  EIO,                 /*  -107: seek error */
+  EINVAL,              /*  -108: negative seek */
+  ESPIPE,              /*  -109: seek on device */
+  EGENERIC,            /*  -110: end of file */
+  EIO,                 /*  -111: generic read error */
+  EIO,                 /*  -112: generic write error */
+  EROFS,               /*  -113: write protected */
+  ETXTBSY,             /*  -114: sharing violation */
+  ENOLCK,              /*  -115: file lock failed */
+  EAGAIN,              /*  -116: file lock violation */
+  EIO,                 /*  -117: cannot create file */
+  EIO,                 /*  -118: cannot delete directory */
+  EXDEV,               /*  -119: not the same device */
+  ENAMETOOLONG,                /*  -120: file name too long */
+  ENXIO,               /*  -121: media not present */
+  EIO,                 /*  -122: media not recognized */
+  OK,                  /*  -123: nothing to unlocked */
+  EGENERIC,            /*  -124: lock lost */
+  ENOTEMPTY,           /*  -125: directory not empty */
+  ENOTDIR,             /*  -126: not a directory */
+  EISDIR,              /*  -127: is a directory */
+  EFBIG,               /*  -128: file too big */
+  EGENERIC,            /*  -129: no asynchronous I/O request */
+  EGENERIC,            /*  -130: asynchronous I/O in progress */
+  EGENERIC,            /*  -131: asynchronous I/O completed */
+  EGENERIC,            /*  -132: asynchronous I/O busy */
+  EGENERIC,            /*  -133: asynchronous I/O limit exceeded */
+  EGENERIC,            /*  -134: asynchronous I/O canceled */
+  EGENERIC,            /*  -135: asynchronous I/O not submitted */
+  EGENERIC,            /*  -136: asynchronous I/O not prepared */
+  EGENERIC,            /*  -137: asynchronous I/O out of resources */
+  EBUSY,               /*  -138: device or resource busy */
+  EGENERIC,            /*  -139: not a file */
+  EGENERIC,            /*  -140: is a file */
+  EGENERIC,            /*  -141: unexpected file type */
+  EGENERIC,            /*  -142: missing path root specification */
+  EGENERIC,            /*  -143: path is relative */
+  EGENERIC,            /*  -144: path is not relative */
+  EGENERIC,            /*  -145 */
+  EGENERIC,            /*  -146 */
+  EGENERIC,            /*  -147 */
+  EGENERIC,            /*  -148 */
+  EGENERIC,            /*  -149 */
+  EIO,                 /*  -150: disk I/O error */
+  ENXIO,               /*  -151: invalid drive number */
+  ENOSPC,              /*  -152: disk full */
+  EIO,                 /*  -153: disk changed */
+  EGENERIC,            /*  -154: drive locked */
+  ENXIO,               /*  -155: invalid disk format */
+  ELOOP,               /*  -156: too many symlinks */
+  EOPNOTSUPP,          /*  -157: can not set symlink file times */
+  EOPNOTSUPP,          /*  -158: can not change symlink owner */
+  EGENERIC,            /*  -159 */
+  EGENERIC,            /*  -160 */
+  EGENERIC,            /*  -161 */
+  EGENERIC,            /*  -162 */
+  EGENERIC,            /*  -163 */
+  EGENERIC,            /*  -164 */
+  EGENERIC,            /*  -165 */
+  EGENERIC,            /*  -166 */
+  EGENERIC,            /*  -167 */
+  EGENERIC,            /*  -168 */
+  EGENERIC,            /*  -169 */
+  EGENERIC,            /*  -170 */
+  EGENERIC,            /*  -171 */
+  EGENERIC,            /*  -172 */
+  EGENERIC,            /*  -173 */
+  EGENERIC,            /*  -174 */
+  EGENERIC,            /*  -175 */
+  EGENERIC,            /*  -176 */
+  EGENERIC,            /*  -177 */
+  EGENERIC,            /*  -178 */
+  EGENERIC,            /*  -179 */
+  EGENERIC,            /*  -180 */
+  EGENERIC,            /*  -181 */
+  EGENERIC,            /*  -182 */
+  EGENERIC,            /*  -183 */
+  EGENERIC,            /*  -184 */
+  EGENERIC,            /*  -185 */
+  EGENERIC,            /*  -186 */
+  EGENERIC,            /*  -187 */
+  EGENERIC,            /*  -188 */
+  EGENERIC,            /*  -189 */
+  EGENERIC,            /*  -190 */
+  EGENERIC,            /*  -191 */
+  EGENERIC,            /*  -192 */
+  EGENERIC,            /*  -193 */
+  EGENERIC,            /*  -194 */
+  EGENERIC,            /*  -195 */
+  EGENERIC,            /*  -196 */
+  EGENERIC,            /*  -197 */
+  EGENERIC,            /*  -198 */
+  EGENERIC,            /*  -199 */
+  EGENERIC,            /*  -200: search error */
+  OK,                  /*  -201: no more files */
+  ENFILE,              /*  -202: no more search handles available */
+};
+
+int convert_err(int code)
+{
+/* Return a POSIX error code for the given VirtualBox error code.
+ */
+  unsigned int index;
+
+  index = -code;
+
+  if (index < sizeof(codes) / sizeof(codes[0]))
+       return codes[index];
+
+  return EGENERIC;
+}
diff --git a/drivers/vbox/hgcm.c b/drivers/vbox/hgcm.c
new file mode 100644 (file)
index 0000000..19b0ab6
--- /dev/null
@@ -0,0 +1,712 @@
+/* VirtualBox driver - by D.C. van Moolenbroek */
+#include <minix/drivers.h>
+#include <minix/vboxtype.h>
+#include <minix/vboxif.h>
+#include <assert.h>
+
+#include "vmmdev.h"
+#include "proto.h"
+
+#define MAX_CONNS      4       /* maximum number of HGCM connections */
+#define MAX_REQS       2       /* number of concurrent requests per conn. */
+#define MAX_PARAMS     8       /* maximum number of parameters per request */
+
+/* HGCM connection states. */
+enum {
+  STATE_FREE,
+  STATE_OPENING,
+  STATE_OPEN,
+  STATE_CLOSING
+};
+
+/* HGCM connection information. */
+static struct {
+  int state;                                   /* connection state */
+  endpoint_t endpt;                            /* caller endpoint */
+  u32_t client_id;                             /* VMMDev-given client ID */
+  struct {
+       int busy;                               /* is this request ongoing? */
+       struct VMMDevHGCMHeader *ptr;           /* request buffer */
+       phys_bytes addr;                        /* buffer's physical address */
+
+       int status;                             /* IPC status of request */
+       long id;                                /* request ID */
+
+       cp_grant_id_t grant;                    /* grant for parameters */
+       int count;                              /* number of parameters */
+       vbox_param_t param[MAX_PARAMS];         /* local copy of parameters */
+  } req[MAX_REQS];                             /* concurrent requests */
+} hgcm_conn[MAX_CONNS];
+
+/*===========================================================================*
+ *                             convert_result                               *
+ *===========================================================================*/
+static int convert_result(int res)
+{
+       /* Convert a VirtualBox result code to a POSIX error code.
+        */
+
+       /* HGCM transport error codes. */
+       switch (res) {
+       case VMMDEV_ERR_HGCM_NOT_FOUND:         return ESRCH;
+       case VMMDEV_ERR_HGCM_DENIED:            return EPERM;
+       case VMMDEV_ERR_HGCM_INVALID_ADDR:      return EFAULT;
+       case VMMDEV_ERR_HGCM_ASYNC_EXEC:        return EDONTREPLY;
+       case VMMDEV_ERR_HGCM_INTERNAL:          return EGENERIC;
+       case VMMDEV_ERR_HGCM_INVALID_ID:        return EINVAL;
+       }
+
+       /* Positive codes are success codes. */
+       if (res >= 0)
+               return OK;
+
+       /* Unsupported negative codes are translated to EGENERIC; it is up to
+        * the caller to check the actual VirtualBox result code in that case.
+        */
+       return convert_err(res);
+}
+
+/*===========================================================================*
+ *                             send_reply                                   *
+ *===========================================================================*/
+static void send_reply(endpoint_t endpt, int ipc_status, int result, int code,
+       long id)
+{
+       /* Send a reply to an earlier request. */
+       message m;
+       int r;
+
+       memset(&m, 0, sizeof(m));
+       m.m_type = VBOX_REPLY;
+       m.VBOX_RESULT = result;
+       m.VBOX_CODE = code;
+       m.VBOX_ID = id;
+
+       if (IPC_STATUS_CALL(ipc_status) == SENDREC)
+               r = sendnb(endpt, &m);
+       else
+               r = asynsend3(endpt, &m, AMF_NOREPLY);
+
+       if (r != OK)
+               printf("VBOX: unable to send reply to %d: %d\n", endpt, r);
+}
+
+/*===========================================================================*
+ *                             alloc_req                                    *
+ *===========================================================================*/
+static int alloc_req(int conn)
+{
+       /* Allocate a request for the given connection. Allocate memory as
+        * necessary. Do not mark the request as busy, as it may end up not
+        * being used.
+        */
+       phys_bytes addr;
+       void *ptr;
+       int req;
+
+       for (req = 0; req < MAX_REQS; req++)
+               if (!hgcm_conn[conn].req[req].busy)
+                       break;
+
+       if (req == MAX_REQS)
+               return EMFILE;
+
+       if (hgcm_conn[conn].req[req].ptr == NULL) {
+               if ((ptr = alloc_contig(VMMDEV_BUF_SIZE, 0, &addr)) == NULL)
+                       return ENOMEM;
+
+               hgcm_conn[conn].req[req].ptr = (struct VMMDevHGCMHeader *) ptr;
+               hgcm_conn[conn].req[req].addr = addr;
+       }
+
+       return req;
+}
+
+/*===========================================================================*
+ *                             free_conn                                    *
+ *===========================================================================*/
+static void free_conn(int conn)
+{
+       /* Free the memory for all requests of the given connections, and mark
+        * the connection as free.
+        */
+       void *ptr;
+       int req;
+
+       for (req = 0; req < MAX_REQS; req++) {
+               if ((ptr = (void *) hgcm_conn[conn].req[req].ptr) != NULL) {
+                       assert(!hgcm_conn[conn].req[req].busy);
+
+                       free_contig(ptr, VMMDEV_BUF_SIZE);
+
+                       hgcm_conn[conn].req[req].ptr = NULL;
+               }
+       }
+
+       hgcm_conn[conn].state = STATE_FREE;
+}
+
+/*===========================================================================*
+ *                             start_req                                    *
+ *===========================================================================*/
+static int start_req(int conn, int req, int type, size_t size, int ipc_status,
+       long id, int *code)
+{
+       /* Start a request. */
+       int r, res;
+
+       hgcm_conn[conn].req[req].ptr->flags = 0;
+       hgcm_conn[conn].req[req].ptr->result = VMMDEV_ERR_GENERIC;
+
+       *code = res = vbox_request(&hgcm_conn[conn].req[req].ptr->header,
+               hgcm_conn[conn].req[req].addr, type, size);
+
+       r = convert_result(res);
+
+       if (r != OK && r != EDONTREPLY)
+               return r;
+
+       /* The request may be processed either immediately or asynchronously.
+        * The caller of this function must be able to cope with both
+        * situations. In either case, mark the current request as ongoing.
+        */
+       hgcm_conn[conn].req[req].busy = TRUE;
+       hgcm_conn[conn].req[req].status = ipc_status;
+       hgcm_conn[conn].req[req].id = id;
+
+       return r;
+}
+
+/*===========================================================================*
+ *                             cancel_req                                   *
+ *===========================================================================*/
+static void cancel_req(int conn, int req)
+{
+       /* Cancel an ongoing request. */
+
+       assert(hgcm_conn[conn].req[req].ptr != NULL);
+
+       /* The cancel request consists only of the HGCM header. The physical
+        * location determines the request to cancel. Note that request
+        * cancellation this is full of race conditions, so we simply ignore
+        * the return value and assumed all went well.
+        */
+       hgcm_conn[conn].req[req].ptr->flags = 0;
+       hgcm_conn[conn].req[req].ptr->result = VMMDEV_ERR_GENERIC;
+
+       vbox_request(&hgcm_conn[conn].req[req].ptr->header,
+               hgcm_conn[conn].req[req].addr, VMMDEV_REQ_HGCMCANCEL,
+               sizeof(struct VMMDevHGCMCancel));
+
+       hgcm_conn[conn].req[req].busy = FALSE;
+}
+
+/*===========================================================================*
+ *                             finish_req                                   *
+ *===========================================================================*/
+static int finish_req(int conn, int req, int *code)
+{
+       /* The given request has finished. Take the appropriate action.
+        */
+       struct VMMDevHGCMConnect *connreq;
+       struct VMMDevHGCMCall *callreq;
+       struct VMMDevHGCMParam *inp;
+       vbox_param_t *outp;
+       int i, count, res, r = OK;
+
+       hgcm_conn[conn].req[req].busy = FALSE;
+
+       *code = res = hgcm_conn[conn].req[req].ptr->result;
+
+       r = convert_result(res);
+
+       /* The request has finished, so it cannot still be in progress. */
+       if (r == EDONTREPLY)
+               r = EGENERIC;
+
+       switch (hgcm_conn[conn].state) {
+       case STATE_FREE:
+               assert(0);
+
+               break;
+
+       case STATE_OPENING:
+               if (r == OK) {
+                       connreq = (struct VMMDevHGCMConnect *)
+                               hgcm_conn[conn].req[req].ptr;
+                       hgcm_conn[conn].client_id = connreq->client_id;
+                       hgcm_conn[conn].state = STATE_OPEN;
+
+                       r = conn;
+               } else {
+                       free_conn(conn);
+               }
+
+               break;
+
+       case STATE_CLOSING:
+               /* Neither we nor the caller can do anything with failures. */
+               if (r != OK)
+                       printf("VBOX: disconnection failure #2 (%d)\n", res);
+
+               free_conn(conn);
+
+               r = OK;
+
+               break;
+
+       case STATE_OPEN:
+               /* On success, extract and copy back parameters to the caller.
+                */
+               if (r == OK) {
+                       callreq = (struct VMMDevHGCMCall *)
+                               hgcm_conn[conn].req[req].ptr;
+                       inp = (struct VMMDevHGCMParam *) &callreq[1];
+                       outp = &hgcm_conn[conn].req[req].param[0];
+                       count = hgcm_conn[conn].req[req].count;
+
+                       for (i = 0; i < count; i++) {
+                               switch (outp->type) {
+                               case VBOX_TYPE_U32:
+                                       outp->u32 = inp->u32;
+                                       break;
+
+                               case VBOX_TYPE_U64:
+                                       outp->u64 = inp->u64;
+                                       break;
+
+                               default:
+                                       break;
+                               }
+
+                               inp++;
+                               outp++;
+                       }
+
+                       if (count > 0) {
+                               r = sys_safecopyto(hgcm_conn[conn].endpt,
+                                       hgcm_conn[conn].req[req].grant, 0,
+                                       (vir_bytes)
+                                       hgcm_conn[conn].req[req].param,
+                                       count * sizeof(vbox_param_t), D);
+                       }
+               }
+
+               break;
+       }
+
+       return r;
+}
+
+/*===========================================================================*
+ *                             check_conn                                   *
+ *===========================================================================*/
+static void check_conn(int conn)
+{
+       /* Check all requests for the given connection for completion. */
+       int r, req, code;
+
+       for (req = 0; req < MAX_REQS; req++) {
+               if (!hgcm_conn[conn].req[req].busy) continue;
+
+               if (!(hgcm_conn[conn].req[req].ptr->flags &
+                               VMMDEV_HGCM_REQ_DONE))
+                       continue;
+
+               r = finish_req(conn, req, &code);
+
+               assert(r != EDONTREPLY);
+
+               send_reply(hgcm_conn[conn].endpt,
+                       hgcm_conn[conn].req[req].status, r, code,
+                       hgcm_conn[conn].req[req].id);
+       }
+}
+
+/*===========================================================================*
+ *                             do_open                                      *
+ *===========================================================================*/
+static int do_open(message *m_ptr, int ipc_status, int *code)
+{
+       /* Process a connection request. */
+       struct VMMDevHGCMConnect *connreq;
+       int i, r, conn, count;
+
+       if (m_ptr->VBOX_COUNT < 0 || m_ptr->VBOX_COUNT > VMMDEV_HGCM_NAME_SIZE)
+               return EINVAL;
+
+       /* Find a free connection slot. Make sure the sending endpoint is not
+        * already using up half of the connection slots.
+        */
+       conn = -1;
+       count = 0;
+       for (i = 0; i < MAX_CONNS; i++) {
+               if (conn < 0 && hgcm_conn[i].state == STATE_FREE)
+                       conn = i;
+               if (hgcm_conn[i].endpt == m_ptr->m_source)
+                       count++;
+       }
+
+       if (count >= MAX(MAX_CONNS / 2, 2))
+               return EMFILE;
+
+       if (conn < 0)
+               return ENFILE;
+
+       /* Initialize the connection and request structures. */
+       hgcm_conn[conn].state = STATE_OPENING;
+       hgcm_conn[conn].endpt = m_ptr->m_source;
+
+       for (i = 0; i < MAX_REQS; i++) {
+               hgcm_conn[conn].req[i].busy = FALSE;
+               hgcm_conn[conn].req[i].ptr = NULL;
+       }
+
+       /* Set up and start the connection request. */
+       r = alloc_req(conn);
+
+       if (r < 0)
+               return r;
+       assert(r == 0);
+
+       connreq = (struct VMMDevHGCMConnect *) hgcm_conn[conn].req[0].ptr;
+       connreq->type = VMMDEV_HGCM_SVCLOC_LOCALHOST_EXISTING;
+       if ((r = sys_safecopyfrom(m_ptr->m_source, m_ptr->VBOX_GRANT, 0,
+                       (vir_bytes) connreq->name, m_ptr->VBOX_COUNT, D)) !=
+                       OK) {
+               free_conn(conn);
+
+               return r;
+       }
+       connreq->name[VMMDEV_HGCM_NAME_SIZE-1] = 0;
+
+       r = start_req(conn, 0, VMMDEV_REQ_HGCMCONNECT, sizeof(*connreq),
+               ipc_status, m_ptr->VBOX_ID, code);
+
+       if (r != OK && r != EDONTREPLY) {
+               free_conn(conn);
+
+               return r;
+       }
+
+       return (r == OK) ? finish_req(conn, 0, code) : r;
+}
+
+/*===========================================================================*
+ *                             do_close                                     *
+ *===========================================================================*/
+static int do_close(message *m_ptr, int ipc_status, int *code)
+{
+       /* Process a disconnection request. */
+       struct VMMDevHGCMDisconnect *discreq;
+       int r, conn, req;
+
+       conn = m_ptr->VBOX_CONN;
+
+       /* Sanity checks. */
+       if (conn < 0 || conn >= MAX_CONNS)
+               return EINVAL;
+       if (hgcm_conn[conn].endpt != m_ptr->m_source ||
+                       hgcm_conn[conn].state != STATE_OPEN)
+               return EINVAL;
+
+       /* Cancel any ongoing requests. */
+       for (req = 0; req < MAX_REQS; req++)
+               if (hgcm_conn[conn].req[req].busy)
+                       cancel_req(conn, req);
+
+       assert(hgcm_conn[conn].req[0].ptr != NULL);
+
+       discreq = (struct VMMDevHGCMDisconnect *) hgcm_conn[conn].req[0].ptr;
+       discreq->client_id = hgcm_conn[conn].client_id;
+
+       r = start_req(conn, 0, VMMDEV_REQ_HGCMDISCONNECT, sizeof(*discreq),
+               ipc_status, m_ptr->VBOX_ID, code);
+
+       if (r != OK && r != EDONTREPLY) {
+               /* Neither we nor the caller can do anything with failures. */
+               printf("VBOX: disconnection failure #1 (%d)\n", r);
+
+               free_conn(conn);
+
+               return OK;
+       }
+
+       hgcm_conn[conn].state = STATE_CLOSING;
+
+       return (r == OK) ? finish_req(conn, 0, code) : r;
+}
+
+/*===========================================================================*
+ *                             store_pages                                  *
+ *===========================================================================*/
+static int store_pages(int conn, int req, vbox_param_t *inp, size_t *offp)
+{
+       /* Create a page list of physical pages that make up the provided
+        * buffer area.
+        */
+       struct vumap_vir vvec;
+       struct vumap_phys pvec[MAPVEC_NR];
+       struct VMMDevHGCMPageList *pagelist;
+       size_t offset, size, skip;
+       int i, j, r, first, access, count, pages;
+
+       /* Empty strings are allowed. */
+       if (inp->ptr.size == 0)
+               return OK;
+
+       pagelist = (struct VMMDevHGCMPageList *)
+               (((u8_t *) hgcm_conn[conn].req[req].ptr) + *offp);
+
+       pagelist->flags = 0;
+       if (inp->ptr.dir & VBOX_DIR_IN)
+               pagelist->flags |= VMMDEV_HGCM_FLAG_FROM_HOST;
+       if (inp->ptr.dir & VBOX_DIR_OUT)
+               pagelist->flags |= VMMDEV_HGCM_FLAG_TO_HOST;
+       pagelist->count = 0;
+
+       /* Make sure there is room for the header (but no actual pages yet). */
+       *offp += sizeof(*pagelist) - sizeof(pagelist->addr[0]);
+       if (*offp > VMMDEV_BUF_SIZE)
+               return ENOMEM;
+
+       access = 0;
+       if (inp->ptr.dir & VBOX_DIR_IN) access |= VUA_WRITE;
+       if (inp->ptr.dir & VBOX_DIR_OUT) access |= VUA_READ;
+
+       offset = 0;
+       first = TRUE;
+       do {
+               /* If the caller gives us a huge buffer, we might need multiple
+                * calls to sys_vumap(). Note that the caller currently has no
+                * reliable way to know whether such a buffer will fit in our
+                * request page. In the future, we may dynamically reallocate
+                * the request area to make more room as necessary; for now we
+                * just return an ENOMEM error in such cases.
+                */
+               vvec.vv_grant = inp->ptr.grant;
+               vvec.vv_size = inp->ptr.off + inp->ptr.size;
+               count = MAPVEC_NR;
+               if ((r = sys_vumap(hgcm_conn[conn].endpt, &vvec, 1,
+                               inp->ptr.off + offset, access, pvec,
+                               &count)) != OK)
+                       return r;
+
+               /* First get the number of bytes processed, before (possibly)
+                * adjusting the size of the first element.
+                */
+               for (i = size = 0; i < count; i++)
+                       size += pvec[i].vp_size;
+
+               /* VirtualBox wants aligned page addresses only, and an offset
+                * into the first page. All other pages except the last are
+                * full pages, and the last page is cut off using the size.
+                */
+               skip = 0;
+               if (first) {
+                       skip = pvec[0].vp_addr & (PAGE_SIZE - 1);
+                       pvec[0].vp_addr -= skip;
+                       pvec[0].vp_size += skip;
+                       pagelist->offset = skip;
+                       first = FALSE;
+               }
+
+               /* How many pages were mapped? */
+               pages = (skip + size + PAGE_SIZE - 1) / PAGE_SIZE;
+
+               /* Make sure there is room to store this many extra pages. */
+               *offp += sizeof(pagelist->addr[0]) * pages;
+               if (*offp > VMMDEV_BUF_SIZE)
+                       return ENOMEM;
+
+               /* Actually store the pages in the page list. */
+               for (i = j = 0; i < pages; i++) {
+                       assert(!(pvec[j].vp_addr & (PAGE_SIZE - 1)));
+
+                       pagelist->addr[pagelist->count++] =
+                               cvul64(pvec[j].vp_addr);
+
+                       if (pvec[j].vp_size > PAGE_SIZE) {
+                               pvec[j].vp_addr += PAGE_SIZE;
+                               pvec[j].vp_size -= PAGE_SIZE;
+                       }
+                       else j++;
+               }
+               assert(j == count);
+
+               offset += size;
+       } while (offset < inp->ptr.size);
+
+       assert(offset == inp->ptr.size);
+
+       return OK;
+}
+
+/*===========================================================================*
+ *                             do_call                                      *
+ *===========================================================================*/
+static int do_call(message *m_ptr, int ipc_status, int *code)
+{
+       /* Perform a HGCM call. */
+       vbox_param_t *inp;
+       struct VMMDevHGCMParam *outp;
+       struct VMMDevHGCMCall *callreq;
+       size_t size;
+       int i, r, conn, req, count;
+
+       conn = m_ptr->VBOX_CONN;
+       count = m_ptr->VBOX_COUNT;
+
+       /* Sanity checks. */
+       if (conn < 0 || conn >= MAX_CONNS)
+               return EINVAL;
+       if (hgcm_conn[conn].endpt != m_ptr->m_source ||
+                       hgcm_conn[conn].state != STATE_OPEN)
+               return EINVAL;
+
+       /* Allocate a request, and copy in the parameters. */
+       req = alloc_req(conn);
+
+       if (req < 0)
+               return req;
+
+       hgcm_conn[conn].req[req].grant = m_ptr->VBOX_GRANT;
+       hgcm_conn[conn].req[req].count = count;
+
+       if (count > 0) {
+               if ((r = sys_safecopyfrom(m_ptr->m_source, m_ptr->VBOX_GRANT,
+                               0, (vir_bytes) hgcm_conn[conn].req[req].param,
+                               count * sizeof(vbox_param_t), D)) != OK)
+                       return r;
+       }
+
+       /* Set up the basic request. */
+       callreq = (struct VMMDevHGCMCall *) hgcm_conn[conn].req[req].ptr;
+       callreq->client_id = hgcm_conn[conn].client_id;
+       callreq->function = m_ptr->VBOX_FUNCTION;
+       callreq->count = count;
+
+       /* Rewrite and convert the parameters. */
+       inp = &hgcm_conn[conn].req[req].param[0];
+       outp = (struct VMMDevHGCMParam *) &callreq[1];
+
+       size = sizeof(*callreq) + sizeof(*outp) * count;
+       assert(size < VMMDEV_BUF_SIZE);
+
+       for (i = 0; i < count; i++) {
+               switch (inp->type) {
+               case VBOX_TYPE_U32:
+                       outp->type = VMMDEV_HGCM_PARAM_U32;
+                       outp->u32 = inp->u32;
+                       break;
+
+               case VBOX_TYPE_U64:
+                       outp->type = VMMDEV_HGCM_PARAM_U64;
+                       outp->u64 = inp->u64;
+                       break;
+
+               case VBOX_TYPE_PTR:
+                       outp->type = VMMDEV_HGCM_PARAM_PAGELIST;
+                       outp->pagelist.offset = size;
+                       outp->pagelist.size = inp->ptr.size;
+
+                       if ((r = store_pages(conn, req, inp, &size)) != OK)
+                               return r;
+
+                       break;
+
+               default:
+                       return EINVAL;
+               }
+
+               inp++;
+               outp++;
+       }
+
+       /* Start the request. */
+       r = start_req(conn, req, VMMDEV_REQ_HGCMCALL, size, ipc_status,
+               m_ptr->VBOX_ID, code);
+
+       if (r != OK && r != EDONTREPLY)
+               return r;
+
+       return (r == OK) ? finish_req(conn, req, code) : r;
+}
+
+/*===========================================================================*
+ *                             do_cancel                                    *
+ *===========================================================================*/
+static int do_cancel(message *m_ptr, int ipc_status)
+{
+       /* Cancel an ongoing call. */
+       int conn, req;
+
+       conn = m_ptr->VBOX_CONN;
+
+       /* Sanity checks. Note that connection and disconnection requests
+        * cannot be cancelled.
+        */
+       if (conn < 0 || conn >= MAX_CONNS)
+               return EINVAL;
+       if (hgcm_conn[conn].endpt != m_ptr->m_source ||
+                       hgcm_conn[conn].state != STATE_OPEN)
+               return EINVAL;
+
+       /* Find the request. */
+       for (req = 0; req < MAX_REQS; req++) {
+               if (hgcm_conn[conn].req[req].busy &&
+                               hgcm_conn[conn].req[req].id == m_ptr->VBOX_ID)
+                       break;
+       }
+
+       /* If no such request was ongoing, then our behavior depends on the
+        * way the request was made: we do not want to send two asynchronous
+        * replies for one request, but if the caller used SENDREC, we have to
+        * reply with something or the caller would deadlock.
+        */
+       if (req == MAX_REQS) {
+               if (IPC_STATUS_CALL(ipc_status) == SENDREC)
+                       return EINVAL;
+               else
+                       return EDONTREPLY;
+       }
+
+       /* Actually cancel the request, and send a reply. */
+       cancel_req(conn, req);
+
+       return EINTR;
+}
+
+/*===========================================================================*
+ *                             hgcm_message                                 *
+ *===========================================================================*/
+void hgcm_message(message *m_ptr, int ipc_status)
+{
+       /* Process a request message. */
+       int r, code = VMMDEV_ERR_GENERIC;
+
+       switch (m_ptr->m_type) {
+       case VBOX_OPEN:         r = do_open(m_ptr, ipc_status, &code);  break;
+       case VBOX_CLOSE:        r = do_close(m_ptr, ipc_status, &code); break;
+       case VBOX_CALL:         r = do_call(m_ptr, ipc_status, &code);  break;
+       case VBOX_CANCEL:       r = do_cancel(m_ptr, ipc_status);       break;
+       default:                r = ENOSYS;                             break;
+       }
+
+       if (r != EDONTREPLY)
+               send_reply(m_ptr->m_source, ipc_status, r, code,
+                       m_ptr->VBOX_ID);
+}
+
+/*===========================================================================*
+ *                             hgcm_intr                                    *
+ *===========================================================================*/
+void hgcm_intr(void)
+{
+       /* We received an HGCM event. Check ongoing requests for completion. */
+       int conn;
+
+       for (conn = 0; conn < MAX_CONNS; conn++)
+               if (hgcm_conn[conn].state != STATE_FREE)
+                       check_conn(conn);
+}
diff --git a/drivers/vbox/proto.h b/drivers/vbox/proto.h
new file mode 100644 (file)
index 0000000..77f96f9
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef _VBOX_PROTO_H
+#define _VBOX_PROTO_H
+
+/* err.c */
+extern int convert_err(int code);
+
+/* hgcm.c */
+extern void hgcm_message(message *m_ptr, int ipc_status);
+extern void hgcm_intr(void);
+
+/* vbox.c */
+extern int vbox_request(struct VMMDevRequestHeader *header, phys_bytes addr,
+       int type, size_t size);
+
+#endif /* _VBOX_PROTO_H */
index dd5ffc928674c8612c1cd0c8496add596686acd7..4053515d00881d52955b57fbd4ef707df1d2eb11 100644 (file)
@@ -1,11 +1,17 @@
-/* VirtualBox driver - only does regular time sync - by D.C. van Moolenbroek */
+/* VirtualBox driver - by D.C. van Moolenbroek */
+/*
+ * This driver currently performs two tasks:
+ * - synchronizing to the host system time;
+ * - providing an interface for HGCM communication with the host system.
+ */
 #include <minix/drivers.h>
 #include <minix/driver.h>
 #include <minix/optset.h>
 #include <machine/pci.h>
 #include <sys/time.h>
 
-#include "vbox.h"
+#include "vmmdev.h"
+#include "proto.h"
 
 #define DEFAULT_INTERVAL       1       /* check host time every second */
 #define DEFAULT_DRIFT          2       /* update time if delta is >= 2 secs */
@@ -17,6 +23,9 @@ static u32_t ticks;
 static int interval;
 static int drift;
 
+static unsigned int irq;
+static int hook_id;
+
 static struct optset optset_table[] = {
        { "interval",   OPT_INT,        &interval,      10              },
        { "drift",      OPT_INT,        &drift,         10              },
@@ -26,22 +35,21 @@ static struct optset optset_table[] = {
 /*===========================================================================*
  *                             vbox_request                                 *
  *===========================================================================*/
-static int vbox_request(int req_nr, size_t size)
+int vbox_request(struct VMMDevRequestHeader *header, phys_bytes addr,
+       int type, size_t size)
 {
        /* Perform a VirtualBox backdoor request. */
-       struct VMMDevRequestHeader *hdr;
        int r;
 
-       hdr = (struct VMMDevRequestHeader *) vir_ptr;
-       hdr->size = size;
-       hdr->version = VMMDEV_BACKDOOR_VERSION;
-       hdr->type = req_nr;
-       hdr->rc = VMMDEV_ERR_PERM;
+       header->size = size;
+       header->version = VMMDEV_BACKDOOR_VERSION;
+       header->type = type;
+       header->result = VMMDEV_ERR_GENERIC;
 
-       if ((r = sys_outl(port, phys_ptr)) != OK)
+       if ((r = sys_outl(port, addr)) != OK)
                panic("device I/O failed: %d", r);
 
-       return hdr->rc;
+       return header->result;
 }
 
 /*===========================================================================*
@@ -69,7 +77,7 @@ static int vbox_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
                if (r != 1)
                        panic("backdoor device not found");
 
-               if (vid == VBOX_PCI_VID && did == VBOX_PCI_DID)
+               if (vid == VMMDEV_PCI_VID && did == VMMDEV_PCI_DID)
                        break;
 
                r = pci_next_dev(&devind, &vid, &did);
@@ -79,14 +87,23 @@ static int vbox_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
 
        port = pci_attr_r32(devind, PCI_BAR) & PCI_BAR_IO_MASK;
 
+       irq = pci_attr_r8(devind, PCI_ILR);
+
+       if ((r = sys_irqsetpolicy(irq, 0 /* IRQ_REENABLE */, &hook_id)) != OK)
+               panic("unable to register IRQ: %d", r);
+
+       if ((r = sys_irqenable(&hook_id)) != OK)
+               panic("unable to enable IRQ: %d", r);
+
        if ((vir_ptr = alloc_contig(VMMDEV_BUF_SIZE, 0, &phys_ptr)) == NULL)
                panic("unable to allocate memory");
 
        req = (struct VMMDevReportGuestInfo *) vir_ptr;
-       req->guest_info.add_version = VMMDEV_GUEST_VERSION;
-       req->guest_info.os_type = VMMDEV_GUEST_OS_OTHER;
+       req->add_version = VMMDEV_GUEST_VERSION;
+       req->os_type = VMMDEV_GUEST_OS_OTHER;
 
-       if ((r = vbox_request(VMMDEV_REQ_REPORTGUESTINFO, sizeof(*req))) !=
+       if ((r = vbox_request(&req->header, phys_ptr,
+                       VMMDEV_REQ_REPORTGUESTINFO, sizeof(*req))) !=
                        VMMDEV_ERR_OK)
                panic("backdoor device not functioning");
 
@@ -97,6 +114,37 @@ static int vbox_init(int UNUSED(type), sef_init_info_t *UNUSED(info))
        return OK;
 }
 
+/*===========================================================================*
+ *                             vbox_intr                                    *
+ *===========================================================================*/
+static void vbox_intr(void)
+{
+       /* Process an interrupt. */
+       struct VMMDevEvents *req;
+       int r;
+
+       req = (struct VMMDevEvents *) vir_ptr;
+       req->events = 0;
+
+       /* If we cannot retrieve the events mask, we cannot do anything with
+        * this or any future interrupt either, so return without reenabling
+        * interrupts.
+        */
+       if ((r = vbox_request(&req->header, phys_ptr,
+                       VMMDEV_REQ_ACKNOWLEDGEEVENTS, sizeof(*req))) !=
+                       VMMDEV_ERR_OK) {
+               printf("VBOX: unable to retrieve event mask (%d)\n", r);
+
+               return;
+       }
+
+       if (req->events & VMMDEV_EVENT_HGCM)
+               hgcm_intr();
+
+       if ((r = sys_irqenable(&hook_id)) != OK)
+               panic("unable to reenable IRQ: %d", r);
+}
+
 /*===========================================================================*
  *                             vbox_update_time                             *
  *===========================================================================*/
@@ -108,7 +156,8 @@ static void vbox_update_time(void)
 
        req = (struct VMMDevReqHostTime *) vir_ptr;
 
-       if (vbox_request(VMMDEV_REQ_HOSTTIME, sizeof(*req)) == VMMDEV_ERR_OK) {
+       if (vbox_request(&req->header, phys_ptr, VMMDEV_REQ_HOSTTIME,
+                       sizeof(*req)) == VMMDEV_ERR_OK) {
                time(&otime);                           /* old time */
 
                ntime = div64u(req->time, 1000);        /* new time */
@@ -168,6 +217,11 @@ int main(int argc, char **argv)
 
                if (is_ipc_notify(ipc_status)) {
                        switch (m.m_source) {
+                       case HARDWARE:
+                               vbox_intr();
+
+                               break;
+
                        case CLOCK:
                                vbox_update_time();
 
@@ -181,8 +235,7 @@ int main(int argc, char **argv)
                        continue;
                }
 
-               printf("VBOX: received message %d from %d\n",
-                       m.m_type, m.m_source);
+               hgcm_message(&m, ipc_status);
        }
 
        return 0;
diff --git a/drivers/vbox/vbox.h b/drivers/vbox/vbox.h
deleted file mode 100644 (file)
index 4e43673..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef _VBOX_H
-#define _VBOX_H
-
-#define VBOX_PCI_VID 0x80ee
-#define VBOX_PCI_DID 0xcafe
-
-struct VMMDevRequestHeader {
-       u32_t size;
-       u32_t version;
-       u32_t type;
-       i32_t rc;
-       u32_t reserved[2];
-};
-
-struct VBoxGuestInfo {
-       u32_t add_version;
-       u32_t os_type;
-};
-
-struct VMMDevReportGuestInfo {
-       struct VMMDevRequestHeader header;
-       struct VBoxGuestInfo guest_info;
-};
-
-struct VMMDevReqHostTime {
-       struct VMMDevRequestHeader header;
-       u64_t time;
-};
-
-#define VMMDEV_MAKEWORD(m,n)   (((m) << 16) | (n))
-
-#define VMMDEV_BACKDOOR_VERSION        VMMDEV_MAKEWORD(1, 1)
-#define VMMDEV_GUEST_VERSION   VMMDEV_MAKEWORD(1, 4)
-#define VMMDEV_GUEST_OS_OTHER  0x90000         /* this is L4 - close enough */
-
-#define VMMDEV_REQ_REPORTGUESTINFO     50
-#define VMMDEV_REQ_HOSTTIME            10
-
-#define VMMDEV_ERR_OK          0
-#define VMMDEV_ERR_PERM                (-10)
-
-#define VMMDEV_BUF_SIZE                4096            /* just one page */
-
-#endif /* _VBOX_H */
diff --git a/drivers/vbox/vmmdev.h b/drivers/vbox/vmmdev.h
new file mode 100644 (file)
index 0000000..0047bae
--- /dev/null
@@ -0,0 +1,126 @@
+#ifndef _VBOX_VMMDEV_H
+#define _VBOX_VMMDEV_H
+
+#define VMMDEV_PCI_VID 0x80ee
+#define VMMDEV_PCI_DID 0xcafe
+
+#define VMMDEV_REQ_HOSTTIME            10
+#define VMMDEV_REQ_ACKNOWLEDGEEVENTS   41
+#define VMMDEV_REQ_REPORTGUESTINFO     50
+#define VMMDEV_REQ_HGCMCONNECT         60
+#define VMMDEV_REQ_HGCMDISCONNECT      61
+#define VMMDEV_REQ_HGCMCALL            62
+#define VMMDEV_REQ_HGCMCANCEL          64
+
+#define VMMDEV_ERR_OK                  0               /* success */
+#define VMMDEV_ERR_GENERIC             (-1)            /* general failure */
+#define VMMDEV_ERR_HGCM_NOT_FOUND      (-2900)         /* service not found */
+#define VMMDEV_ERR_HGCM_DENIED         2901            /* client rejected */
+#define VMMDEV_ERR_HGCM_INVALID_ADDR   (-2902)         /* invalid address */
+#define VMMDEV_ERR_HGCM_ASYNC_EXEC     2903            /* call in progress */
+#define VMMDEV_ERR_HGCM_INTERNAL       (-2904)         /* internal error */
+#define VMMDEV_ERR_HGCM_INVALID_ID     (-2905)         /* invalid client ID */
+
+#define VMMDEV_MAKEWORD(m,n)   (((m) << 16) | (n))
+
+#define VMMDEV_BACKDOOR_VERSION        VMMDEV_MAKEWORD(1, 1)
+#define VMMDEV_GUEST_VERSION   VMMDEV_MAKEWORD(1, 4)
+#define VMMDEV_GUEST_OS_OTHER  0x90000         /* this is L4 - close enough */
+
+struct VMMDevRequestHeader {
+       u32_t size;
+       u32_t version;
+       u32_t type;
+       i32_t result;
+       u32_t reserved[2];
+};
+
+struct VMMDevReportGuestInfo {
+       struct VMMDevRequestHeader header;
+       u32_t add_version;
+       u32_t os_type;
+};
+
+struct VMMDevReqHostTime {
+       struct VMMDevRequestHeader header;
+       u64_t time;
+};
+
+#define VMMDEV_EVENT_HGCM      (1 << 1)
+
+struct VMMDevEvents {
+       struct VMMDevRequestHeader header;
+       u32_t events;
+};
+
+#define VMMDEV_HGCM_REQ_DONE   (1 << 0)
+
+struct VMMDevHGCMHeader {
+       struct VMMDevRequestHeader header;
+       u32_t flags;
+       i32_t result;
+};
+
+#define VMMDEV_HGCM_SVCLOC_LOCALHOST_EXISTING 2
+
+#define VMMDEV_HGCM_NAME_SIZE 128
+
+struct VMMDevHGCMConnect {
+       struct VMMDevHGCMHeader header;
+       u32_t type;
+       char name[VMMDEV_HGCM_NAME_SIZE];
+       u32_t client_id;
+};
+
+struct VMMDevHGCMDisconnect {
+       struct VMMDevHGCMHeader header;
+       u32_t client_id;
+};
+
+#define VMMDEV_HGCM_FLAG_TO_HOST       0x01
+#define VMMDEV_HGCM_FLAG_FROM_HOST     0x02
+
+struct VMMDevHGCMPageList {
+       u32_t flags;
+       u16_t offset;
+       u16_t count;
+       u64_t addr[1];
+};
+
+#define VMMDEV_HGCM_PARAM_U32          1
+#define VMMDEV_HGCM_PARAM_U64          2
+#define VMMDEV_HGCM_PARAM_PAGELIST     10
+
+struct VMMDevHGCMParam {
+       u32_t type;
+       union {
+               u32_t u32;
+               u64_t u64;
+               struct {
+                       u32_t size;
+                       union {
+                               u32_t phys;
+                               void *vir;
+                       } addr;
+               } ptr;
+               struct {
+                       u32_t size;
+                       u32_t offset;
+               } pagelist;
+       };
+};
+
+struct VMMDevHGCMCall {
+       struct VMMDevHGCMHeader header;
+       u32_t client_id;
+       u32_t function;
+       u32_t count;
+};
+
+struct VMMDevHGCMCancel {
+       struct VMMDevHGCMHeader header;
+};
+
+#define VMMDEV_BUF_SIZE                4096            /* just one page */
+
+#endif /* _VBOX_VMMDEV_H */
index f45f2a04e68dbf47d5c0ba021691c97757206ead..d77112d4a895a0cb2502b8c9b1e6f697140d80ff 100644 (file)
@@ -537,6 +537,8 @@ service vbox
 {
        system
                UMAP            # 14
+               VUMAP           # 18
+               IRQCTL          # 19
                DEVIO           # 21
        ;
        pci device      80ee/cafe;
index 49a1dfcba0151889035a79700fd6143a5bced746..7d540865eeebadd9696d101b9e1a773349485627 100644 (file)
@@ -17,9 +17,9 @@ INCS+=        minix/acpi.h minix/audio_fw.h minix/bitmap.h \
        minix/rs.h minix/safecopies.h minix/sched.h minix/sef.h \
        minix/sound.h minix/spin.h minix/sys_config.h minix/sysinfo.h \
        minix/syslib.h minix/sysutil.h minix/timers.h minix/type.h \
-       minix/tty.h minix/u64.h minix/usb.h minix/usb_ch9.h minix/vm.h \
-       minix/vfsif.h minix/vtreefs.h minix/libminixfs.h \
-       minix/netsock.h
+       minix/tty.h minix/u64.h minix/usb.h minix/usb_ch9.h minix/vbox.h \
+       minix/vboxif.h minix/vboxtype.h minix/vm.h \
+       minix/vfsif.h minix/vtreefs.h minix/libminixfs.h minix/netsock.h
 
 INCS+= net/gen/arp_io.h net/gen/dhcp.h net/gen/ether.h \
        net/gen/eth_hdr.h net/gen/eth_io.h net/gen/icmp.h \
index 76dc34f2344fd90c4ef3a951823c55cb23f2dd08..d995b5a8f8ea1377c4ad93986d51f034ddb501b5 100644 (file)
@@ -26,6 +26,7 @@
  *   0x1300 - 0x13FF    TTY Input
  *   0x1400 - 0x14FF   VFS-FS transaction IDs
  *   0x1500 - 0x15FF   Block device requests and responses
+ *   0x1600 - 0x16FF   VirtualBox (VBOX) requests (see vboxif.h)
  *
  * Zero and negative values are widely used for OK and error responses.
  */
diff --git a/include/minix/vbox.h b/include/minix/vbox.h
new file mode 100644 (file)
index 0000000..0c0f166
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef _MINIX_VBOX_H
+#define _MINIX_VBOX_H
+
+#include <minix/vboxtype.h>
+
+typedef int vbox_conn_t;
+
+extern int vbox_init(void);
+
+extern vbox_conn_t vbox_open(char *name);
+extern int vbox_close(vbox_conn_t conn);
+extern int vbox_call(vbox_conn_t conn, u32_t function, vbox_param_t *param,
+       int count, int *code);
+
+extern void vbox_set_u32(vbox_param_t *param, u32_t value);
+extern void vbox_set_u64(vbox_param_t *param, u64_t value);
+extern void vbox_set_ptr(vbox_param_t *param, void *ptr, size_t size,
+       unsigned int dir);
+extern void vbox_set_grant(vbox_param_t *param, endpoint_t endpt,
+       cp_grant_id_t grant, size_t off, size_t size, unsigned int dir);
+
+extern u32_t vbox_get_u32(vbox_param_t *param);
+extern u64_t vbox_get_u64(vbox_param_t *param);
+
+extern void vbox_put(vbox_param_t *param, int count);
+
+#endif /* _MINIX_VBOX_H */
diff --git a/include/minix/vboxif.h b/include/minix/vboxif.h
new file mode 100644 (file)
index 0000000..1b8c04e
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef _MINIX_VBOXIF_H
+#define _MINIX_VBOXIF_H
+
+/*===========================================================================*
+ *                     Messages for VBOX device                             *
+ *===========================================================================*/
+
+/* Base type for VBOX requests and responses. */
+#define VBOX_RQ_BASE   0x1600
+#define VBOX_RS_BASE   0x1680
+
+#define IS_VBOX_RQ(type) (((type) & ~0x7f) == VBOX_RQ_BASE)
+#define IS_VBOX_RS(type) (((type) & ~0x7f) == VBOX_RS_BASE)
+
+/* Message types for VBOX requests. */
+#define VBOX_OPEN      (VBOX_RQ_BASE + 0)      /* open a connection */
+#define VBOX_CLOSE     (VBOX_RQ_BASE + 1)      /* close a connection */
+#define VBOX_CALL      (VBOX_RQ_BASE + 2)      /* perform a call */
+#define VBOX_CANCEL    (VBOX_RQ_BASE + 3)      /* cancel an ongoing call */
+
+/* Message types for VBOX responses. */
+#define VBOX_REPLY     (VBOX_RS_BASE + 0)      /* general reply code */
+
+/* Field names for VBOX messages. */
+#define VBOX_CONN      m2_i1   /* connection identifier */
+#define VBOX_GRANT     m2_i2   /* grant ID of buffer or name */
+#define VBOX_COUNT     m2_i3   /* number of bytes or elements */
+#define VBOX_RESULT    m2_i1   /* result or error code */
+#define VBOX_CODE      m2_i2   /* VirtualBox result code */
+#define VBOX_FUNCTION  m2_l1   /* function call number */
+#define VBOX_ID                m2_l2   /* opaque request ID */
+
+#endif /* _MINIX_VBOXIF_H */
diff --git a/include/minix/vboxtype.h b/include/minix/vboxtype.h
new file mode 100644 (file)
index 0000000..cada0f3
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef _MINIX_VBOXTYPE_H
+#define _MINIX_VBOXTYPE_H
+
+/* This header declares the type definitions shared between VBOX driver, the
+ * interface in libsys, and any caller of those interface functions.
+ */
+
+/* Call parameter type. */
+typedef enum {
+  VBOX_TYPE_INVALID,           /* invalid type */
+  VBOX_TYPE_U32,               /* 32-bit value */
+  VBOX_TYPE_U64,               /* 64-bit value */
+  VBOX_TYPE_PTR                        /* pointer to granted memory area */
+} vbox_type_t;
+
+/* Call parameter transfer direction. */
+#define VBOX_DIR_IN    0x01    /* from host to guest */
+#define VBOX_DIR_OUT   0x02    /* from guest to host */
+#define VBOX_DIR_INOUT (VBOX_DIR_IN | VBOX_DIR_OUT)
+
+/* Call parameter. */
+typedef struct {
+  vbox_type_t type;
+  union {
+       u32_t u32;
+       u64_t u64;
+       struct {
+               cp_grant_id_t grant;
+               size_t off;
+               size_t size;
+               unsigned int dir;
+       } ptr;
+  };
+} vbox_param_t;
+
+#endif /*_MINIX_VBOXTYPE_H */
index d644b218b06c712d38d759f994381dcbcf0e7ca7..9201b0bcd956dc762172dc679a30166b63eab5bb 100644 (file)
@@ -115,6 +115,7 @@ SRCS=  \
        timers.c \
        timing.c \
        tsc_util.c \
+       vbox.c \
        vm_brk.c \
        vm_dmacalls.c \
        vm_exec_newmem.c \
diff --git a/lib/libsys/vbox.c b/lib/libsys/vbox.c
new file mode 100644 (file)
index 0000000..c0a26dd
--- /dev/null
@@ -0,0 +1,256 @@
+/* VBOX driver interface - synchronous version - by D.C. van Moolenbroek */
+
+#include <minix/drivers.h>
+#include <minix/vboxtype.h>
+#include <minix/vboxif.h>
+#include <minix/vbox.h>
+#include <minix/ds.h>
+#include <assert.h>
+
+static endpoint_t vbox_endpt = NONE;
+
+int vbox_init(void)
+{
+/* Initialize the library, by resolving the VBOX driver endpoint.
+ */
+  int r;
+
+  if ((r = ds_retrieve_label_endpt("vbox", &vbox_endpt)) != OK) {
+       printf("libvbox: unable to obtain VBOX endpoint (%d)\n", r);
+
+       return EINVAL;
+  }
+
+  return OK;
+}
+
+vbox_conn_t vbox_open(char *name)
+{
+/* Open a VirtualBox HGCM connection.
+ */
+  message m;
+  cp_grant_id_t grant;
+  size_t len;
+  int r;
+
+  if (vbox_endpt == NONE)
+       return EDEADSRCDST;
+
+  len = strlen(name) + 1;
+  grant = cpf_grant_direct(vbox_endpt, (vir_bytes) name, len, CPF_READ);
+  if (!GRANT_VALID(grant))
+       return ENOMEM;
+
+  memset(&m, 0, sizeof(m));
+  m.m_type = VBOX_OPEN;
+  m.VBOX_GRANT = grant;
+  m.VBOX_COUNT = len;
+  m.VBOX_ID = 0;
+
+  r = sendrec(vbox_endpt, &m);
+
+  cpf_revoke(grant);
+
+  if (r != OK)
+       return r;
+
+  if (m.VBOX_ID != 0)
+       return EINVAL;
+
+  return m.VBOX_RESULT;
+}
+
+int vbox_close(vbox_conn_t conn)
+{
+/* Close a VirtualBox HGCM connection.
+ */
+  message m;
+  int r;
+
+  if (vbox_endpt == NONE)
+       return EDEADSRCDST;
+
+  memset(&m, 0, sizeof(m));
+  m.m_type = VBOX_CLOSE;
+  m.VBOX_CONN = conn;
+  m.VBOX_ID = 0;
+
+  r = sendrec(vbox_endpt, &m);
+
+  if (r != OK)
+       return r;
+
+  if (m.VBOX_ID != 0)
+       return EINVAL;
+
+  return m.VBOX_RESULT;
+}
+
+int vbox_call(vbox_conn_t conn, u32_t function, vbox_param_t *param, int count,
+  int *code)
+{
+/* Call a VirtualBox HGCM function. The caller must set up all buffer grants.
+ */
+  cp_grant_id_t grant = GRANT_INVALID;
+  message m;
+  int i, r;
+
+  if (vbox_endpt == NONE) {
+       vbox_put(param, count);
+
+       return EDEADSRCDST;
+  }
+
+  /* Check whether all parameters are initialized correctly. */
+  for (i = 0; i < count; i++) {
+       switch (param[i].type) {
+       case VBOX_TYPE_U32:
+       case VBOX_TYPE_U64:
+       case VBOX_TYPE_PTR:
+               break;
+
+       default:
+               vbox_put(param, count);
+
+               return ENOMEM;
+       }
+  }
+
+  if (count > 0) {
+       grant = cpf_grant_direct(vbox_endpt, (vir_bytes) param,
+               sizeof(param[0]) * count, CPF_READ | CPF_WRITE);
+       if (!GRANT_VALID(grant)) {
+               vbox_put(param, count);
+
+               return ENOMEM;
+       }
+  }
+
+  memset(&m, 0, sizeof(m));
+  m.m_type = VBOX_CALL;
+  m.VBOX_CONN = conn;
+  m.VBOX_GRANT = grant;
+  m.VBOX_COUNT = count;
+  m.VBOX_ID = 0;
+  m.VBOX_FUNCTION = function;
+
+  r = sendrec(vbox_endpt, &m);
+
+  if (GRANT_VALID(grant))
+       cpf_revoke(grant);
+
+  vbox_put(param, count);
+
+  if (r != OK)
+       return r;
+
+  if (m.VBOX_ID != 0)
+       return EINVAL;
+
+  if (code != NULL)
+       *code = m.VBOX_CODE;
+
+  return m.VBOX_RESULT;
+}
+
+void vbox_set_u32(vbox_param_t *param, u32_t value)
+{
+/* Set the given parameter to a 32-bit value.
+ */
+
+  param->type = VBOX_TYPE_U32;
+  param->u32 = value;
+}
+
+void vbox_set_u64(vbox_param_t *param, u64_t value)
+{
+/* Set the given parameter to a 32-bit value.
+ */
+
+  param->type = VBOX_TYPE_U64;
+  param->u64 = value;
+}
+
+void vbox_set_ptr(vbox_param_t *param, void *ptr, size_t size,
+  unsigned int dir)
+{
+/* Set the given parameter to a grant for the given local pointer.
+ */
+  cp_grant_id_t grant = GRANT_INVALID;
+  int flags;
+
+  flags = 0;
+  if (dir & VBOX_DIR_IN) flags |= CPF_WRITE;
+  if (dir & VBOX_DIR_OUT) flags |= CPF_READ;
+
+  if (size > 0) {
+       grant = cpf_grant_direct(vbox_endpt, (vir_bytes) ptr, size, flags);
+       if (!GRANT_VALID(grant)) {
+               param->type = VBOX_TYPE_INVALID;
+
+               return;
+       }
+  }
+
+  param->type = VBOX_TYPE_PTR;
+  param->ptr.grant = grant;
+  param->ptr.off = 0;
+  param->ptr.size = size;
+  param->ptr.dir = dir;
+}
+
+void vbox_set_grant(vbox_param_t *param, endpoint_t endpt, cp_grant_id_t grant,
+  size_t off, size_t size, unsigned int dir)
+{
+/* Set the given parameter to an indirect grant for the given grant.
+ */
+  cp_grant_id_t indir_grant;
+
+  /* Unfortunately, the current implementation of indirect grants does not
+   * support making smaller subgrants out of larger original grants. Therefore,
+   * we are forced to grant more access than we would like.
+   */
+  indir_grant = cpf_grant_indirect(vbox_endpt, endpt, grant);
+  if (!GRANT_VALID(indir_grant)) {
+       param->type = VBOX_TYPE_INVALID;
+
+       return;
+  }
+
+  param->type = VBOX_TYPE_PTR;
+  param->ptr.grant = indir_grant;
+  param->ptr.off = off;
+  param->ptr.size = size;
+  param->ptr.dir = dir;
+}
+
+void vbox_put(vbox_param_t *param, int count)
+{
+/* Free all resources used for the given parameters.
+ */
+
+  while (count--) {
+       if (param->type == VBOX_TYPE_PTR && GRANT_VALID(param->ptr.grant))
+               cpf_revoke(param->ptr.grant);
+
+       param++;
+  }
+}
+
+u32_t vbox_get_u32(vbox_param_t *param)
+{
+/* Retrieve the 32-bit value from the given parameter.
+ */
+
+  assert(param->type == VBOX_TYPE_U32);
+  return param->u32;
+}
+
+u64_t vbox_get_u64(vbox_param_t *param)
+{
+/* Retrieve the 64-bit value from the given parameter.
+ */
+
+  assert(param->type == VBOX_TYPE_U64);
+  return param->u64;
+}