VBOX: add host/guest communication interface

This interface can be used by other system processes by means of the
newly provided vbox API in libsys.
This commit is contained in:
David van Moolenbroek 2012-04-09 15:56:20 +02:00
parent ef01931f76
commit 6aa61efd09
15 changed files with 1510 additions and 66 deletions

View 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

226
drivers/vbox/err.c Normal file
View file

@ -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;
}

712
drivers/vbox/hgcm.c Normal file
View file

@ -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);
}

15
drivers/vbox/proto.h Normal file
View file

@ -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 */

View 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;

View file

@ -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 */

126
drivers/vbox/vmmdev.h Normal file
View file

@ -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 */

View file

@ -537,6 +537,8 @@ service vbox
{
system
UMAP # 14
VUMAP # 18
IRQCTL # 19
DEVIO # 21
;
pci device 80ee/cafe;

View 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 \

View 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.
*/

27
include/minix/vbox.h Normal file
View file

@ -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 */

33
include/minix/vboxif.h Normal file
View file

@ -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 */

36
include/minix/vboxtype.h Normal file
View file

@ -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 */

View 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 \

256
lib/libsys/vbox.c Normal file
View file

@ -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;
}