summaryrefslogtreecommitdiff
path: root/include/VBox/hgcmsvc.h
blob: c3480a73f1307ebbbe39108d84e20df7aa94ecea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
/** @file
 * Host-Guest Communication Manager (HGCM) - Service library definitions.
 */

/*
 * Copyright (C) 2006-2007 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
 * VirtualBox OSE distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 */

#ifndef ___VBox_hgcm_h
#define ___VBox_hgcm_h

#include <iprt/assert.h>
#include <iprt/string.h>
#include <VBox/cdefs.h>
#include <VBox/types.h>
#include <VBox/err.h>
#ifdef VBOX_TEST_HGCM_PARMS
# include <iprt/test.h>
#endif

/** @todo proper comments. */

/**
 * Service interface version.
 *
 * Includes layout of both VBOXHGCMSVCFNTABLE and VBOXHGCMSVCHELPERS.
 *
 * A service can work with these structures if major version
 * is equal and minor version of service is <= version of the
 * structures.
 *
 * For example when a new helper is added at the end of helpers
 * structure, then the minor version will be increased. All older
 * services still can work because they have their old helpers
 * unchanged.
 *
 * Revision history.
 * 1.1->2.1 Because the pfnConnect now also has the pvClient parameter.
 * 2.1->2.2 Because pfnSaveState and pfnLoadState were added
 * 2.2->3.1 Because pfnHostCall is now synchronous, returns rc, and parameters were changed
 * 3.1->3.2 Because pfnRegisterExtension was added
 * 3.2->3.3 Because pfnDisconnectClient helper was added
 * 3.3->4.1 Because the pvService entry and parameter was added
 * 4.1->4.2 Because the VBOX_HGCM_SVC_PARM_CALLBACK parameter type was added
 * 4.2->5.1 Removed the VBOX_HGCM_SVC_PARM_CALLBACK parameter type, as
 *          this problem is already solved by service extension callbacks
 */
#define VBOX_HGCM_SVC_VERSION_MAJOR (0x0005)
#define VBOX_HGCM_SVC_VERSION_MINOR (0x0001)
#define VBOX_HGCM_SVC_VERSION ((VBOX_HGCM_SVC_VERSION_MAJOR << 16) + VBOX_HGCM_SVC_VERSION_MINOR)


/** Typed pointer to distinguish a call to service. */
struct VBOXHGCMCALLHANDLE_TYPEDEF;
typedef struct VBOXHGCMCALLHANDLE_TYPEDEF *VBOXHGCMCALLHANDLE;

/** Service helpers pointers table. */
typedef struct _VBOXHGCMSVCHELPERS
{
    /** The service has processed the Call request. */
    DECLR3CALLBACKMEMBER(void, pfnCallComplete, (VBOXHGCMCALLHANDLE callHandle, int32_t rc));

    void *pvInstance;

    /** The service disconnects the client. */
    DECLR3CALLBACKMEMBER(void, pfnDisconnectClient, (void *pvInstance, uint32_t u32ClientID));
} VBOXHGCMSVCHELPERS;

typedef VBOXHGCMSVCHELPERS *PVBOXHGCMSVCHELPERS;


#define VBOX_HGCM_SVC_PARM_INVALID (0U)
#define VBOX_HGCM_SVC_PARM_32BIT (1U)
#define VBOX_HGCM_SVC_PARM_64BIT (2U)
#define VBOX_HGCM_SVC_PARM_PTR   (3U)

typedef struct VBOXHGCMSVCPARM
{
    /** VBOX_HGCM_SVC_PARM_* values. */
    uint32_t type;

    union
    {
        uint32_t uint32;
        uint64_t uint64;
        struct
        {
            uint32_t size;
            void *addr;
        } pointer;
    } u;
#ifdef __cplusplus
    /** Extract a uint32_t value from an HGCM parameter structure */
    int getUInt32 (uint32_t *u32)
    {
        AssertPtrReturn(u32, VERR_INVALID_POINTER);
        int rc = VINF_SUCCESS;
        if (type != VBOX_HGCM_SVC_PARM_32BIT)
            rc = VERR_INVALID_PARAMETER;
        if (RT_SUCCESS(rc))
            *u32 = u.uint32;
        return rc;
    }

    /** Extract a uint64_t value from an HGCM parameter structure */
    int getUInt64 (uint64_t *u64)
    {
        AssertPtrReturn(u64, VERR_INVALID_POINTER);
        int rc = VINF_SUCCESS;
        if (type != VBOX_HGCM_SVC_PARM_64BIT)
            rc = VERR_INVALID_PARAMETER;
        if (RT_SUCCESS(rc))
            *u64 = u.uint64;
        return rc;
    }

    /** Extract a pointer value from an HGCM parameter structure */
    int getPointer (void **ppv, uint32_t *pcb)
    {
        AssertPtrReturn(ppv, VERR_INVALID_POINTER);
        AssertPtrReturn(pcb, VERR_INVALID_POINTER);
        if (type == VBOX_HGCM_SVC_PARM_PTR)
        {
            *ppv = u.pointer.addr;
            *pcb = u.pointer.size;
            return VINF_SUCCESS;
        }

        return VERR_INVALID_PARAMETER;
    }

    /** Extract a constant pointer value from an HGCM parameter structure */
    int getPointer (const void **ppcv, uint32_t *pcb)
    {
        AssertPtrReturn(ppcv, VERR_INVALID_POINTER);
        AssertPtrReturn(pcb, VERR_INVALID_POINTER);
        void *pv;
        int rc = getPointer(&pv, pcb);
        *ppcv = pv;
        return rc;
    }

    /** Extract a pointer value to a non-empty buffer from an HGCM parameter
     * structure */
    int getBuffer (void **ppv, uint32_t *pcb)
    {
        AssertPtrReturn(ppv, VERR_INVALID_POINTER);
        AssertPtrReturn(pcb, VERR_INVALID_POINTER);
        void *pv = NULL;
        uint32_t cb = 0;
        int rc = getPointer(&pv, &cb);
        if (   RT_SUCCESS(rc)
            && VALID_PTR(pv)
            && cb > 0)
        {
            *ppv = pv;
            *pcb = cb;
            return VINF_SUCCESS;
        }

        return VERR_INVALID_PARAMETER;
    }

    /** Extract a pointer value to a non-empty constant buffer from an HGCM
     * parameter structure */
    int getBuffer (const void **ppcv, uint32_t *pcb)
    {
        AssertPtrReturn(ppcv, VERR_INVALID_POINTER);
        AssertPtrReturn(pcb, VERR_INVALID_POINTER);
        void *pcv = NULL;
        int rc = getBuffer(&pcv, pcb);
        *ppcv = pcv;
        return rc;
    }

    /** Extract a string value from an HGCM parameter structure */
    int getString (char **ppch, uint32_t *pcb)
    {
        uint32_t cb = 0;
        char *pch = NULL;
        int rc = getBuffer((void **)&pch, &cb);
        if (RT_FAILURE(rc))
        {
            *ppch = NULL;
            *pcb = 0;
            return rc;
        }
        rc = RTStrValidateEncodingEx(pch, cb,
                                     RTSTR_VALIDATE_ENCODING_ZERO_TERMINATED);
        *ppch = pch;
        *pcb = cb;
        return rc;
    }

    /** Extract a constant string value from an HGCM parameter structure */
    int getString (const char **ppch, uint32_t *pcb)
    {
        char *pch = NULL;
        int rc = getString(&pch, pcb);
        *ppch = pch;
        return rc;
    }

    /** Set a uint32_t value to an HGCM parameter structure */
    void setUInt32(uint32_t u32)
    {
        type = VBOX_HGCM_SVC_PARM_32BIT;
        u.uint32 = u32;
    }

    /** Set a uint64_t value to an HGCM parameter structure */
    void setUInt64(uint64_t u64)
    {
        type = VBOX_HGCM_SVC_PARM_64BIT;
        u.uint64 = u64;
    }

    /** Set a pointer value to an HGCM parameter structure */
    void setPointer(void *pv, uint32_t cb)
    {
        type = VBOX_HGCM_SVC_PARM_PTR;
        u.pointer.addr = pv;
        u.pointer.size = cb;
    }

    /** Set a const string value to an HGCM parameter structure */
    void setString(const char *psz)
    {
        type = VBOX_HGCM_SVC_PARM_PTR;
        u.pointer.addr = (void *)psz;
        u.pointer.size = (uint32_t)strlen(psz) + 1;
    }

#ifdef VBOX_TEST_HGCM_PARMS
    /** Test the getString member function.  Indirectly tests the getPointer
     * and getBuffer APIs.
     * @param  hTest  an running IPRT test
     * @param  aType  the type that the parameter should be set to before
     *                calling getString
     * @param  apcc   the value that the parameter should be set to before
     *                calling getString, and also the address (!) which we
     *                expect getString to return.  Stricter than needed of
     *                course, but I was feeling lazy.
     * @param  acb    the size that the parameter should be set to before
     *                calling getString, and also the size which we expect
     *                getString to return.
     * @param  rcExp  the expected return value of the call to getString.
     */
    void doTestGetString(RTTEST hTest, uint32_t aType, const char *apcc,
                         uint32_t acb, int rcExp)
    {
        /* An RTTest API like this, which would print out an additional line
         * of context if a test failed, would be nice.  This is because the
         * line number alone doesn't help much here, given that this is a
         * subroutine called many times. */
        /*
        RTTestContextF(hTest,
                       ("doTestGetString, aType=%u, apcc=%p, acp=%u, rcExp=%Rrc",
                        aType, apcc, acp, rcExp));
         */
        setPointer((void *)apcc, acb);
        type = aType;  /* in case we don't want VBOX_HGCM_SVC_PARM_PTR */
        const char *pcc = NULL;
        uint32_t cb = 0;
        int rc = getString(&pcc, &cb);
        RTTEST_CHECK_RC(hTest, rc, rcExp);
        if (RT_SUCCESS(rcExp))
        {
            RTTEST_CHECK_MSG_RETV(hTest, (pcc == apcc),
                                  (hTest, "expected %p, got %p", apcc, pcc));
            RTTEST_CHECK_MSG_RETV(hTest, (cb == acb),
                                  (hTest, "expected %u, got %u", acb, cb));
        }
    }

    /** Run some unit tests on the getString method and indirectly test
     * getPointer and getBuffer as well. */
    void testGetString(RTTEST hTest)
    {
        RTTestSub(hTest, "HGCM string parameter handling");
        doTestGetString(hTest, VBOX_HGCM_SVC_PARM_32BIT, "test", 3,
                        VERR_INVALID_PARAMETER);
        doTestGetString(hTest, VBOX_HGCM_SVC_PARM_PTR, "test", 5,
                        VINF_SUCCESS);
        doTestGetString(hTest, VBOX_HGCM_SVC_PARM_PTR, "test", 3,
                        VERR_BUFFER_OVERFLOW);
        doTestGetString(hTest, VBOX_HGCM_SVC_PARM_PTR, "test\xf0", 6,
                        VERR_INVALID_UTF8_ENCODING);
        doTestGetString(hTest, VBOX_HGCM_SVC_PARM_PTR, "test", 0,
                        VERR_INVALID_PARAMETER);
        doTestGetString(hTest, VBOX_HGCM_SVC_PARM_PTR, (const char *)0x1, 5,
                        VERR_INVALID_PARAMETER);
        RTTestSubDone(hTest);
    }
#endif

    VBOXHGCMSVCPARM() : type(VBOX_HGCM_SVC_PARM_INVALID) {}
#endif
} VBOXHGCMSVCPARM;

typedef VBOXHGCMSVCPARM *PVBOXHGCMSVCPARM;

#ifdef VBOX_WITH_CRHGSMI
typedef void * HGCMCVSHANDLE;

typedef DECLCALLBACK(void) HGCMHOSTFASTCALLCB (int32_t result, uint32_t u32Function, PVBOXHGCMSVCPARM pParam, void *pvContext);
typedef HGCMHOSTFASTCALLCB *PHGCMHOSTFASTCALLCB;
#endif


/** Service specific extension callback.
 *  This callback is called by the service to perform service specific operation.
 *
 * @param pvExtension The extension pointer.
 * @param u32Function What the callback is supposed to do.
 * @param pvParm      The function parameters.
 * @param cbParm      The size of the function parameters.
 */
typedef DECLCALLBACK(int) FNHGCMSVCEXT(void *pvExtension, uint32_t u32Function, void *pvParm, uint32_t cbParms);
typedef FNHGCMSVCEXT *PFNHGCMSVCEXT;

/** The Service DLL entry points.
 *
 *  HGCM will call the DLL "VBoxHGCMSvcLoad"
 *  function and the DLL must fill in the VBOXHGCMSVCFNTABLE
 *  with function pointers.
 */

/* The structure is used in separately compiled binaries so an explicit packing is required. */
#pragma pack(1)
typedef struct _VBOXHGCMSVCFNTABLE
{
    /** Filled by HGCM */

    /** Size of the structure. */
    uint32_t                 cbSize;

    /** Version of the structure, including the helpers. */
    uint32_t                 u32Version;

    PVBOXHGCMSVCHELPERS      pHelpers;

    /** Filled by the service. */

    /** Size of client information the service want to have. */
    uint32_t                 cbClient;
#if ARCH_BITS == 64
    /** Ensure that the following pointers are properly aligned on 64-bit system. */
    uint32_t                 u32Alignment0;
#endif

    /** Uninitialize service */
    DECLR3CALLBACKMEMBER(int, pfnUnload, (void *pvService));

    /** Inform the service about a client connection. */
    DECLR3CALLBACKMEMBER(int, pfnConnect, (void *pvService, uint32_t u32ClientID, void *pvClient));

    /** Inform the service that the client wants to disconnect. */
    DECLR3CALLBACKMEMBER(int, pfnDisconnect, (void *pvService, uint32_t u32ClientID, void *pvClient));

    /** Service entry point.
     *  Return code is passed to pfnCallComplete callback.
     */
    DECLR3CALLBACKMEMBER(void, pfnCall, (void *pvService, VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]));

    /** Host Service entry point meant for privileged features invisible to the guest.
     *  Return code is passed to pfnCallComplete callback.
     */
    DECLR3CALLBACKMEMBER(int, pfnHostCall, (void *pvService, uint32_t function, uint32_t cParms, VBOXHGCMSVCPARM paParms[]));

    /** Inform the service about a VM save operation. */
    DECLR3CALLBACKMEMBER(int, pfnSaveState, (void *pvService, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM));

    /** Inform the service about a VM load operation. */
    DECLR3CALLBACKMEMBER(int, pfnLoadState, (void *pvService, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM));

    /** Register a service extension callback. */
    DECLR3CALLBACKMEMBER(int, pfnRegisterExtension, (void *pvService, PFNHGCMSVCEXT pfnExtension, void *pvExtension));

    /** User/instance data pointer for the service. */
    void *pvService;

} VBOXHGCMSVCFNTABLE;
#pragma pack()


/** Service initialization entry point. */
typedef DECLCALLBACK(int) VBOXHGCMSVCLOAD(VBOXHGCMSVCFNTABLE *ptable);
typedef VBOXHGCMSVCLOAD *PFNVBOXHGCMSVCLOAD;
#define VBOX_HGCM_SVCLOAD_NAME "VBoxHGCMSvcLoad"

#endif