1 /* VirtualBox driver - by D.C. van Moolenbroek */
2 #include <minix/drivers.h>
3 #include <minix/vboxtype.h>
4 #include <minix/vboxif.h>
10 #define MAX_CONNS 4 /* maximum number of HGCM connections */
11 #define MAX_REQS 2 /* number of concurrent requests per conn. */
12 #define MAX_PARAMS 8 /* maximum number of parameters per request */
14 /* HGCM connection states. */
22 /* HGCM connection information. */
24 int state
; /* connection state */
25 endpoint_t endpt
; /* caller endpoint */
26 u32_t client_id
; /* VMMDev-given client ID */
28 int busy
; /* is this request ongoing? */
29 struct VMMDevHGCMHeader
*ptr
; /* request buffer */
30 phys_bytes addr
; /* buffer's physical address */
32 int status
; /* IPC status of request */
33 long id
; /* request ID */
35 cp_grant_id_t grant
; /* grant for parameters */
36 int count
; /* number of parameters */
37 vbox_param_t param
[MAX_PARAMS
]; /* local copy of parameters */
38 } req
[MAX_REQS
]; /* concurrent requests */
39 } hgcm_conn
[MAX_CONNS
];
41 /*===========================================================================*
43 *===========================================================================*/
44 static int convert_result(int res
)
46 /* Convert a VirtualBox result code to a POSIX error code.
49 /* HGCM transport error codes. */
51 case VMMDEV_ERR_HGCM_NOT_FOUND
: return ESRCH
;
52 case VMMDEV_ERR_HGCM_DENIED
: return EPERM
;
53 case VMMDEV_ERR_HGCM_INVALID_ADDR
: return EFAULT
;
54 case VMMDEV_ERR_HGCM_ASYNC_EXEC
: return EDONTREPLY
;
55 case VMMDEV_ERR_HGCM_INTERNAL
: return EGENERIC
;
56 case VMMDEV_ERR_HGCM_INVALID_ID
: return EINVAL
;
59 /* Positive codes are success codes. */
63 /* Unsupported negative codes are translated to EGENERIC; it is up to
64 * the caller to check the actual VirtualBox result code in that case.
66 return convert_err(res
);
69 /*===========================================================================*
71 *===========================================================================*/
72 static void send_reply(endpoint_t endpt
, int ipc_status
, int result
, int code
,
75 /* Send a reply to an earlier request. */
79 memset(&m
, 0, sizeof(m
));
80 m
.m_type
= VBOX_REPLY
;
81 m
.VBOX_RESULT
= result
;
85 if (IPC_STATUS_CALL(ipc_status
) == SENDREC
)
86 r
= sendnb(endpt
, &m
);
88 r
= asynsend3(endpt
, &m
, AMF_NOREPLY
);
91 printf("VBOX: unable to send reply to %d: %d\n", endpt
, r
);
94 /*===========================================================================*
96 *===========================================================================*/
97 static int alloc_req(int conn
)
99 /* Allocate a request for the given connection. Allocate memory as
100 * necessary. Do not mark the request as busy, as it may end up not
107 for (req
= 0; req
< MAX_REQS
; req
++)
108 if (!hgcm_conn
[conn
].req
[req
].busy
)
114 if (hgcm_conn
[conn
].req
[req
].ptr
== NULL
) {
115 if ((ptr
= alloc_contig(VMMDEV_BUF_SIZE
, 0, &addr
)) == NULL
)
118 hgcm_conn
[conn
].req
[req
].ptr
= (struct VMMDevHGCMHeader
*) ptr
;
119 hgcm_conn
[conn
].req
[req
].addr
= addr
;
125 /*===========================================================================*
127 *===========================================================================*/
128 static void free_conn(int conn
)
130 /* Free the memory for all requests of the given connections, and mark
131 * the connection as free.
136 for (req
= 0; req
< MAX_REQS
; req
++) {
137 if ((ptr
= (void *) hgcm_conn
[conn
].req
[req
].ptr
) != NULL
) {
138 assert(!hgcm_conn
[conn
].req
[req
].busy
);
140 free_contig(ptr
, VMMDEV_BUF_SIZE
);
142 hgcm_conn
[conn
].req
[req
].ptr
= NULL
;
146 hgcm_conn
[conn
].state
= STATE_FREE
;
149 /*===========================================================================*
151 *===========================================================================*/
152 static int start_req(int conn
, int req
, int type
, size_t size
, int ipc_status
,
155 /* Start a request. */
158 hgcm_conn
[conn
].req
[req
].ptr
->flags
= 0;
159 hgcm_conn
[conn
].req
[req
].ptr
->result
= VMMDEV_ERR_GENERIC
;
161 *code
= res
= vbox_request(&hgcm_conn
[conn
].req
[req
].ptr
->header
,
162 hgcm_conn
[conn
].req
[req
].addr
, type
, size
);
164 r
= convert_result(res
);
166 if (r
!= OK
&& r
!= EDONTREPLY
)
169 /* The request may be processed either immediately or asynchronously.
170 * The caller of this function must be able to cope with both
171 * situations. In either case, mark the current request as ongoing.
173 hgcm_conn
[conn
].req
[req
].busy
= TRUE
;
174 hgcm_conn
[conn
].req
[req
].status
= ipc_status
;
175 hgcm_conn
[conn
].req
[req
].id
= id
;
180 /*===========================================================================*
182 *===========================================================================*/
183 static void cancel_req(int conn
, int req
)
185 /* Cancel an ongoing request. */
187 assert(hgcm_conn
[conn
].req
[req
].ptr
!= NULL
);
189 /* The cancel request consists only of the HGCM header. The physical
190 * location determines the request to cancel. Note that request
191 * cancellation this is full of race conditions, so we simply ignore
192 * the return value and assumed all went well.
194 hgcm_conn
[conn
].req
[req
].ptr
->flags
= 0;
195 hgcm_conn
[conn
].req
[req
].ptr
->result
= VMMDEV_ERR_GENERIC
;
197 vbox_request(&hgcm_conn
[conn
].req
[req
].ptr
->header
,
198 hgcm_conn
[conn
].req
[req
].addr
, VMMDEV_REQ_HGCMCANCEL
,
199 sizeof(struct VMMDevHGCMCancel
));
201 hgcm_conn
[conn
].req
[req
].busy
= FALSE
;
204 /*===========================================================================*
206 *===========================================================================*/
207 static int finish_req(int conn
, int req
, int *code
)
209 /* The given request has finished. Take the appropriate action.
211 struct VMMDevHGCMConnect
*connreq
;
212 struct VMMDevHGCMCall
*callreq
;
213 struct VMMDevHGCMParam
*inp
;
215 int i
, count
, res
, r
= OK
;
217 hgcm_conn
[conn
].req
[req
].busy
= FALSE
;
219 *code
= res
= hgcm_conn
[conn
].req
[req
].ptr
->result
;
221 r
= convert_result(res
);
223 /* The request has finished, so it cannot still be in progress. */
227 switch (hgcm_conn
[conn
].state
) {
235 connreq
= (struct VMMDevHGCMConnect
*)
236 hgcm_conn
[conn
].req
[req
].ptr
;
237 hgcm_conn
[conn
].client_id
= connreq
->client_id
;
238 hgcm_conn
[conn
].state
= STATE_OPEN
;
248 /* Neither we nor the caller can do anything with failures. */
250 printf("VBOX: disconnection failure #2 (%d)\n", res
);
259 /* On success, extract and copy back parameters to the caller.
262 callreq
= (struct VMMDevHGCMCall
*)
263 hgcm_conn
[conn
].req
[req
].ptr
;
264 inp
= (struct VMMDevHGCMParam
*) &callreq
[1];
265 outp
= &hgcm_conn
[conn
].req
[req
].param
[0];
266 count
= hgcm_conn
[conn
].req
[req
].count
;
268 for (i
= 0; i
< count
; i
++) {
269 switch (outp
->type
) {
271 outp
->u32
= inp
->u32
;
275 outp
->u64
= inp
->u64
;
287 r
= sys_safecopyto(hgcm_conn
[conn
].endpt
,
288 hgcm_conn
[conn
].req
[req
].grant
, 0,
290 hgcm_conn
[conn
].req
[req
].param
,
291 count
* sizeof(vbox_param_t
));
301 /*===========================================================================*
303 *===========================================================================*/
304 static void check_conn(int conn
)
306 /* Check all requests for the given connection for completion. */
309 for (req
= 0; req
< MAX_REQS
; req
++) {
310 if (!hgcm_conn
[conn
].req
[req
].busy
) continue;
312 if (!(hgcm_conn
[conn
].req
[req
].ptr
->flags
&
313 VMMDEV_HGCM_REQ_DONE
))
316 r
= finish_req(conn
, req
, &code
);
318 assert(r
!= EDONTREPLY
);
320 send_reply(hgcm_conn
[conn
].endpt
,
321 hgcm_conn
[conn
].req
[req
].status
, r
, code
,
322 hgcm_conn
[conn
].req
[req
].id
);
326 /*===========================================================================*
328 *===========================================================================*/
329 static int do_open(message
*m_ptr
, int ipc_status
, int *code
)
331 /* Process a connection request. */
332 struct VMMDevHGCMConnect
*connreq
;
333 int i
, r
, conn
, count
;
335 if (m_ptr
->VBOX_COUNT
< 0 || m_ptr
->VBOX_COUNT
> VMMDEV_HGCM_NAME_SIZE
)
338 /* Find a free connection slot. Make sure the sending endpoint is not
339 * already using up half of the connection slots.
343 for (i
= 0; i
< MAX_CONNS
; i
++) {
344 if (conn
< 0 && hgcm_conn
[i
].state
== STATE_FREE
)
346 if (hgcm_conn
[i
].endpt
== m_ptr
->m_source
)
350 if (count
>= MAX(MAX_CONNS
/ 2, 2))
356 /* Initialize the connection and request structures. */
357 hgcm_conn
[conn
].state
= STATE_OPENING
;
358 hgcm_conn
[conn
].endpt
= m_ptr
->m_source
;
360 for (i
= 0; i
< MAX_REQS
; i
++) {
361 hgcm_conn
[conn
].req
[i
].busy
= FALSE
;
362 hgcm_conn
[conn
].req
[i
].ptr
= NULL
;
365 /* Set up and start the connection request. */
372 connreq
= (struct VMMDevHGCMConnect
*) hgcm_conn
[conn
].req
[0].ptr
;
373 connreq
->type
= VMMDEV_HGCM_SVCLOC_LOCALHOST_EXISTING
;
374 if ((r
= sys_safecopyfrom(m_ptr
->m_source
, m_ptr
->VBOX_GRANT
, 0,
375 (vir_bytes
) connreq
->name
, m_ptr
->VBOX_COUNT
)) !=
381 connreq
->name
[VMMDEV_HGCM_NAME_SIZE
-1] = 0;
383 r
= start_req(conn
, 0, VMMDEV_REQ_HGCMCONNECT
, sizeof(*connreq
),
384 ipc_status
, m_ptr
->VBOX_ID
, code
);
386 if (r
!= OK
&& r
!= EDONTREPLY
) {
392 return (r
== OK
) ? finish_req(conn
, 0, code
) : r
;
395 /*===========================================================================*
397 *===========================================================================*/
398 static int do_close(message
*m_ptr
, int ipc_status
, int *code
)
400 /* Process a disconnection request. */
401 struct VMMDevHGCMDisconnect
*discreq
;
404 conn
= m_ptr
->VBOX_CONN
;
407 if (conn
< 0 || conn
>= MAX_CONNS
)
409 if (hgcm_conn
[conn
].endpt
!= m_ptr
->m_source
||
410 hgcm_conn
[conn
].state
!= STATE_OPEN
)
413 /* Cancel any ongoing requests. */
414 for (req
= 0; req
< MAX_REQS
; req
++)
415 if (hgcm_conn
[conn
].req
[req
].busy
)
416 cancel_req(conn
, req
);
418 assert(hgcm_conn
[conn
].req
[0].ptr
!= NULL
);
420 discreq
= (struct VMMDevHGCMDisconnect
*) hgcm_conn
[conn
].req
[0].ptr
;
421 discreq
->client_id
= hgcm_conn
[conn
].client_id
;
423 r
= start_req(conn
, 0, VMMDEV_REQ_HGCMDISCONNECT
, sizeof(*discreq
),
424 ipc_status
, m_ptr
->VBOX_ID
, code
);
426 if (r
!= OK
&& r
!= EDONTREPLY
) {
427 /* Neither we nor the caller can do anything with failures. */
428 printf("VBOX: disconnection failure #1 (%d)\n", r
);
435 hgcm_conn
[conn
].state
= STATE_CLOSING
;
437 return (r
== OK
) ? finish_req(conn
, 0, code
) : r
;
440 /*===========================================================================*
442 *===========================================================================*/
443 static int store_pages(int conn
, int req
, vbox_param_t
*inp
, size_t *offp
)
445 /* Create a page list of physical pages that make up the provided
448 struct vumap_vir vvec
;
449 struct vumap_phys pvec
[MAPVEC_NR
];
450 struct VMMDevHGCMPageList
*pagelist
;
451 size_t offset
, size
, skip
;
452 int i
, j
, r
, first
, access
, count
, pages
;
454 /* Empty strings are allowed. */
455 if (inp
->ptr
.size
== 0)
458 pagelist
= (struct VMMDevHGCMPageList
*)
459 (((u8_t
*) hgcm_conn
[conn
].req
[req
].ptr
) + *offp
);
462 if (inp
->ptr
.dir
& VBOX_DIR_IN
)
463 pagelist
->flags
|= VMMDEV_HGCM_FLAG_FROM_HOST
;
464 if (inp
->ptr
.dir
& VBOX_DIR_OUT
)
465 pagelist
->flags
|= VMMDEV_HGCM_FLAG_TO_HOST
;
468 /* Make sure there is room for the header (but no actual pages yet). */
469 *offp
+= sizeof(*pagelist
) - sizeof(pagelist
->addr
[0]);
470 if (*offp
> VMMDEV_BUF_SIZE
)
474 if (inp
->ptr
.dir
& VBOX_DIR_IN
) access
|= VUA_WRITE
;
475 if (inp
->ptr
.dir
& VBOX_DIR_OUT
) access
|= VUA_READ
;
480 /* If the caller gives us a huge buffer, we might need multiple
481 * calls to sys_vumap(). Note that the caller currently has no
482 * reliable way to know whether such a buffer will fit in our
483 * request page. In the future, we may dynamically reallocate
484 * the request area to make more room as necessary; for now we
485 * just return an ENOMEM error in such cases.
487 vvec
.vv_grant
= inp
->ptr
.grant
;
488 vvec
.vv_size
= inp
->ptr
.off
+ inp
->ptr
.size
;
490 if ((r
= sys_vumap(hgcm_conn
[conn
].endpt
, &vvec
, 1,
491 inp
->ptr
.off
+ offset
, access
, pvec
,
495 /* First get the number of bytes processed, before (possibly)
496 * adjusting the size of the first element.
498 for (i
= size
= 0; i
< count
; i
++)
499 size
+= pvec
[i
].vp_size
;
501 /* VirtualBox wants aligned page addresses only, and an offset
502 * into the first page. All other pages except the last are
503 * full pages, and the last page is cut off using the size.
507 skip
= pvec
[0].vp_addr
& (PAGE_SIZE
- 1);
508 pvec
[0].vp_addr
-= skip
;
509 pvec
[0].vp_size
+= skip
;
510 pagelist
->offset
= skip
;
514 /* How many pages were mapped? */
515 pages
= (skip
+ size
+ PAGE_SIZE
- 1) / PAGE_SIZE
;
517 /* Make sure there is room to store this many extra pages. */
518 *offp
+= sizeof(pagelist
->addr
[0]) * pages
;
519 if (*offp
> VMMDEV_BUF_SIZE
)
522 /* Actually store the pages in the page list. */
523 for (i
= j
= 0; i
< pages
; i
++) {
524 assert(!(pvec
[j
].vp_addr
& (PAGE_SIZE
- 1)));
526 pagelist
->addr
[pagelist
->count
++] =
527 cvul64(pvec
[j
].vp_addr
);
529 if (pvec
[j
].vp_size
> PAGE_SIZE
) {
530 pvec
[j
].vp_addr
+= PAGE_SIZE
;
531 pvec
[j
].vp_size
-= PAGE_SIZE
;
538 } while (offset
< inp
->ptr
.size
);
540 assert(offset
== inp
->ptr
.size
);
545 /*===========================================================================*
547 *===========================================================================*/
548 static int do_call(message
*m_ptr
, int ipc_status
, int *code
)
550 /* Perform a HGCM call. */
552 struct VMMDevHGCMParam
*outp
;
553 struct VMMDevHGCMCall
*callreq
;
555 int i
, r
, conn
, req
, count
;
557 conn
= m_ptr
->VBOX_CONN
;
558 count
= m_ptr
->VBOX_COUNT
;
561 if (conn
< 0 || conn
>= MAX_CONNS
)
563 if (hgcm_conn
[conn
].endpt
!= m_ptr
->m_source
||
564 hgcm_conn
[conn
].state
!= STATE_OPEN
)
567 /* Allocate a request, and copy in the parameters. */
568 req
= alloc_req(conn
);
573 hgcm_conn
[conn
].req
[req
].grant
= m_ptr
->VBOX_GRANT
;
574 hgcm_conn
[conn
].req
[req
].count
= count
;
577 if ((r
= sys_safecopyfrom(m_ptr
->m_source
, m_ptr
->VBOX_GRANT
,
578 0, (vir_bytes
) hgcm_conn
[conn
].req
[req
].param
,
579 count
* sizeof(vbox_param_t
))) != OK
)
583 /* Set up the basic request. */
584 callreq
= (struct VMMDevHGCMCall
*) hgcm_conn
[conn
].req
[req
].ptr
;
585 callreq
->client_id
= hgcm_conn
[conn
].client_id
;
586 callreq
->function
= m_ptr
->VBOX_FUNCTION
;
587 callreq
->count
= count
;
589 /* Rewrite and convert the parameters. */
590 inp
= &hgcm_conn
[conn
].req
[req
].param
[0];
591 outp
= (struct VMMDevHGCMParam
*) &callreq
[1];
593 size
= sizeof(*callreq
) + sizeof(*outp
) * count
;
594 assert(size
< VMMDEV_BUF_SIZE
);
596 for (i
= 0; i
< count
; i
++) {
599 outp
->type
= VMMDEV_HGCM_PARAM_U32
;
600 outp
->u32
= inp
->u32
;
604 outp
->type
= VMMDEV_HGCM_PARAM_U64
;
605 outp
->u64
= inp
->u64
;
609 outp
->type
= VMMDEV_HGCM_PARAM_PAGELIST
;
610 outp
->pagelist
.offset
= size
;
611 outp
->pagelist
.size
= inp
->ptr
.size
;
613 if ((r
= store_pages(conn
, req
, inp
, &size
)) != OK
)
626 /* Start the request. */
627 r
= start_req(conn
, req
, VMMDEV_REQ_HGCMCALL
, size
, ipc_status
,
628 m_ptr
->VBOX_ID
, code
);
630 if (r
!= OK
&& r
!= EDONTREPLY
)
633 return (r
== OK
) ? finish_req(conn
, req
, code
) : r
;
636 /*===========================================================================*
638 *===========================================================================*/
639 static int do_cancel(message
*m_ptr
, int ipc_status
)
641 /* Cancel an ongoing call. */
644 conn
= m_ptr
->VBOX_CONN
;
646 /* Sanity checks. Note that connection and disconnection requests
647 * cannot be cancelled.
649 if (conn
< 0 || conn
>= MAX_CONNS
)
651 if (hgcm_conn
[conn
].endpt
!= m_ptr
->m_source
||
652 hgcm_conn
[conn
].state
!= STATE_OPEN
)
655 /* Find the request. */
656 for (req
= 0; req
< MAX_REQS
; req
++) {
657 if (hgcm_conn
[conn
].req
[req
].busy
&&
658 hgcm_conn
[conn
].req
[req
].id
== m_ptr
->VBOX_ID
)
662 /* If no such request was ongoing, then our behavior depends on the
663 * way the request was made: we do not want to send two asynchronous
664 * replies for one request, but if the caller used SENDREC, we have to
665 * reply with something or the caller would deadlock.
667 if (req
== MAX_REQS
) {
668 if (IPC_STATUS_CALL(ipc_status
) == SENDREC
)
674 /* Actually cancel the request, and send a reply. */
675 cancel_req(conn
, req
);
680 /*===========================================================================*
682 *===========================================================================*/
683 void hgcm_message(message
*m_ptr
, int ipc_status
)
685 /* Process a request message. */
686 int r
, code
= VMMDEV_ERR_GENERIC
;
688 switch (m_ptr
->m_type
) {
689 case VBOX_OPEN
: r
= do_open(m_ptr
, ipc_status
, &code
); break;
690 case VBOX_CLOSE
: r
= do_close(m_ptr
, ipc_status
, &code
); break;
691 case VBOX_CALL
: r
= do_call(m_ptr
, ipc_status
, &code
); break;
692 case VBOX_CANCEL
: r
= do_cancel(m_ptr
, ipc_status
); break;
693 default: r
= ENOSYS
; break;
697 send_reply(m_ptr
->m_source
, ipc_status
, r
, code
,
701 /*===========================================================================*
703 *===========================================================================*/
706 /* We received an HGCM event. Check ongoing requests for completion. */
709 for (conn
= 0; conn
< MAX_CONNS
; conn
++)
710 if (hgcm_conn
[conn
].state
!= STATE_FREE
)