VirtualBox

source: vbox/trunk/src/VBox/Devices/Graphics/HGSMI/HGSMIHost.cpp@ 68637

Last change on this file since 68637 was 66537, checked in by vboxsync, 8 years ago

bugref:8524: Additions/linux: play nicely with distribution-installed Additions
[PATCH] HGSMIChSetup.h: Stop including HGSMI.h

The plan is for the Linux vboxvideo code to stop using the shared HGSMI
code and thus also HGSMI.h, but we do want to keep HGSMIChSetup.h, so
it needs to stop depending on HGSMI.h .

All files including HGSMIChSetup.h but 1, already include HGSMI.h first,
fix that one file and drop the include from HGSMIChSetup.

Signed-off-by: Hans de Goede <hdegoede@…>

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.5 KB
Line 
1/* $Id: HGSMIHost.cpp 66537 2017-04-12 14:42:47Z vboxsync $ */
2/** @file
3 *
4 * VBox Host Guest Shared Memory Interface (HGSMI).
5 * Host part:
6 * - virtual hardware IO handlers;
7 * - channel management;
8 * - low level interface for buffer transfer.
9 */
10
11/*
12 * Copyright (C) 2006-2016 Oracle Corporation
13 *
14 * This file is part of VirtualBox Open Source Edition (OSE), as
15 * available from http://www.virtualbox.org. This file is free software;
16 * you can redistribute it and/or modify it under the terms of the GNU
17 * General Public License (GPL) as published by the Free Software
18 * Foundation, in version 2 as it comes in the "COPYING" file of the
19 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
20 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
21 */
22
23
24/*
25 * Async host->guest calls. Completion by an IO write from the guest or a timer timeout.
26 *
27 * Sync guest->host calls. Initiated by an IO write from the guest.
28 *
29 * Guest->Host
30 * ___________
31 *
32 * Synchronous for the guest, an async result can be also reported later by a host->guest call:
33 *
34 * G: Alloc shared memory, fill the structure, issue an IO write (HGSMI_IO_GUEST) with the memory offset.
35 * H: Verify the shared memory and call the handler.
36 * G: Continue after the IO completion.
37 *
38 *
39 * Host->Guest
40 * __________
41 *
42 * H: Alloc shared memory, fill in the info.
43 * Register in the FIFO with a callback, issue IRQ (on EMT).
44 * Wait on a sem with timeout if necessary.
45 * G: Read FIFO from HGSMI_IO_HOST_COMMAND.
46 * H(EMT): Get the shared memory offset from FIFO to return to the guest.
47 * G: Get offset, process command, issue IO write to HGSMI_IO_HOST_COMMAND.
48 * H(EMT): Find registered shared mem, run callback, which could post the sem.
49 * H: Get results and free shared mem (could be freed automatically on EMT too).
50 *
51 *
52 * Implementation notes:
53 *
54 * Host->Guest
55 *
56 * * Shared memory allocation using a critsect.
57 * * FIFO manipulation with a critsect.
58 *
59 */
60
61#include <iprt/alloc.h>
62#include <iprt/critsect.h>
63#include <iprt/heap.h>
64#include <iprt/list.h>
65#include <iprt/semaphore.h>
66#include <iprt/string.h>
67
68#include <VBox/err.h>
69#define LOG_GROUP LOG_GROUP_HGSMI
70#include <VBox/log.h>
71#include <VBox/vmm/ssm.h>
72
73#include "HGSMIHost.h"
74#include <HGSMIChannels.h>
75
76#include "../DevVGASavedState.h"
77
78#ifdef DEBUG_sunlover
79#define HGSMI_STRICT 1
80#endif /* !DEBUG_sunlover */
81
82#ifdef DEBUG_misha
83//# define VBOXHGSMI_STATE_DEBUG
84#endif
85
86#ifdef VBOXHGSMI_STATE_DEBUG
87#define VBOXHGSMI_STATE_START_MAGIC 0x12345678
88#define VBOXHGSMI_STATE_STOP_MAGIC 0x87654321
89#define VBOXHGSMI_STATE_FIFOSTART_MAGIC 0x9abcdef1
90#define VBOXHGSMI_STATE_FIFOSTOP_MAGIC 0x1fedcba9
91
92#define VBOXHGSMI_SAVE_START(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_START_MAGIC); AssertRC(rc2); }while(0)
93#define VBOXHGSMI_SAVE_STOP(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_STOP_MAGIC); AssertRC(rc2); }while(0)
94#define VBOXHGSMI_SAVE_FIFOSTART(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_FIFOSTART_MAGIC); AssertRC(rc2); }while(0)
95#define VBOXHGSMI_SAVE_FIFOSTOP(_pSSM) do{ int rc2 = SSMR3PutU32(_pSSM, VBOXHGSMI_STATE_FIFOSTOP_MAGIC); AssertRC(rc2); }while(0)
96
97#define VBOXHGSMI_LOAD_CHECK(_pSSM, _v) \
98 do{ \
99 uint32_t u32; \
100 int rc2 = SSMR3GetU32(_pSSM, &u32); AssertRC(rc2); \
101 Assert(u32 == (_v)); \
102 }while(0)
103
104#define VBOXHGSMI_LOAD_START(_pSSM) VBOXHGSMI_LOAD_CHECK(_pSSM, VBOXHGSMI_STATE_START_MAGIC)
105#define VBOXHGSMI_LOAD_FIFOSTART(_pSSM) VBOXHGSMI_LOAD_CHECK(_pSSM, VBOXHGSMI_STATE_FIFOSTART_MAGIC)
106#define VBOXHGSMI_LOAD_FIFOSTOP(_pSSM) VBOXHGSMI_LOAD_CHECK(_pSSM, VBOXHGSMI_STATE_FIFOSTOP_MAGIC)
107#define VBOXHGSMI_LOAD_STOP(_pSSM) VBOXHGSMI_LOAD_CHECK(_pSSM, VBOXHGSMI_STATE_STOP_MAGIC)
108#else
109#define VBOXHGSMI_SAVE_START(_pSSM) do{ }while(0)
110#define VBOXHGSMI_SAVE_STOP(_pSSM) do{ }while(0)
111#define VBOXHGSMI_SAVE_FIFOSTART(_pSSM) do{ }while(0)
112#define VBOXHGSMI_SAVE_FIFOSTOP(_pSSM) do{ }while(0)
113
114
115#define VBOXHGSMI_LOAD_START(_pSSM) do{ }while(0)
116#define VBOXHGSMI_LOAD_FIFOSTART(_pSSM) do{ }while(0)
117#define VBOXHGSMI_LOAD_FIFOSTOP(_pSSM) do{ }while(0)
118#define VBOXHGSMI_LOAD_STOP(_pSSM) do{ }while(0)
119
120#endif
121
122/* Assertions for situations which could happen and normally must be processed properly
123 * but must be investigated during development: guest misbehaving, etc.
124 */
125#ifdef HGSMI_STRICT
126#define HGSMI_STRICT_ASSERT_FAILED() AssertFailed()
127#define HGSMI_STRICT_ASSERT(expr) Assert(expr)
128#else
129#define HGSMI_STRICT_ASSERT_FAILED() do {} while (0)
130#define HGSMI_STRICT_ASSERT(expr) do {} while (0)
131#endif /* !HGSMI_STRICT */
132
133
134/* Host heap types. */
135#define HGSMI_HEAP_TYPE_NULL 0 /* Heap not initialized. */
136#define HGSMI_HEAP_TYPE_POINTER 1 /* Deprecated, used only for old saved states. RTHEAPSIMPLE. */
137#define HGSMI_HEAP_TYPE_OFFSET 2 /* Deprecated, used only for old saved states. RTHEAPOFFSET. */
138#define HGSMI_HEAP_TYPE_MA 3 /* Memory allocator. */
139
140typedef struct HGSMIHOSTHEAP
141{
142 uint32_t u32HeapType; /* HGSMI_HEAP_TYPE_* */
143 int32_t volatile cRefs; /* How many blocks allocated. */
144 HGSMIAREA area; /* Host heap location. */
145 union
146 {
147 HGSMIMADATA ma; /* Memory allocator for the default host heap implementation. */
148 struct /* Legacy heap implementations. For old saved states. */
149 {
150 union
151 {
152 RTHEAPSIMPLE hPtr; /* Pointer based heap. */
153 RTHEAPOFFSET hOff; /* Offset based heap. */
154 } u;
155 } legacy;
156 } u;
157} HGSMIHOSTHEAP;
158
159typedef struct HGSMIINSTANCE
160{
161 PVM pVM; /* The VM. */
162
163 const char *pszName; /* A name for the instance. Mostyl used in the log. */
164
165 RTCRITSECT instanceCritSect; /* For updating the instance data: FIFO's, channels. */
166
167 HGSMIAREA area; /* The shared memory description. */
168 HGSMIHOSTHEAP hostHeap; /* Host heap instance. */
169 RTCRITSECT hostHeapCritSect; /* Heap serialization lock. */
170
171 RTLISTANCHOR hostFIFO; /* Pending host buffers. */
172 RTLISTANCHOR hostFIFORead; /* Host buffers read by the guest. */
173 RTLISTANCHOR hostFIFOProcessed; /* Processed by the guest. */
174 RTLISTANCHOR hostFIFOFree; /* Buffers for reuse. */
175#ifdef VBOX_WITH_WDDM
176 RTLISTANCHOR guestCmdCompleted; /* list of completed guest commands to be returned to the guest*/
177#endif
178 RTCRITSECT hostFIFOCritSect; /* FIFO serialization lock. */
179
180 PFNHGSMINOTIFYGUEST pfnNotifyGuest; /* Guest notification callback. */
181 void *pvNotifyGuest; /* Guest notification callback context. */
182
183 volatile HGSMIHOSTFLAGS *pHGFlags;
184
185 HGSMICHANNELINFO channelInfo; /* Channel handlers indexed by the channel id.
186 * The array is accessed under the instance lock.
187 */
188} HGSMIINSTANCE;
189
190
191typedef DECLCALLBACK(void) FNHGSMIHOSTFIFOCALLBACK(void *pvCallback);
192typedef FNHGSMIHOSTFIFOCALLBACK *PFNHGSMIHOSTFIFOCALLBACK;
193
194typedef struct HGSMIHOSTFIFOENTRY
195{
196 RTLISTNODE nodeEntry;
197
198 HGSMIINSTANCE *pIns; /* Backlink to the HGSMI instance. */
199
200 volatile uint32_t fl; /* Status flags of the entry. */
201
202 HGSMIOFFSET offBuffer; /* Offset of the HGSMI buffer header in the HGSMI host heap:
203 * [pIns->hostHeap.area.offBase .. offLast]. */
204} HGSMIHOSTFIFOENTRY;
205
206
207#define HGSMI_F_HOST_FIFO_ALLOCATED 0x0001
208#define HGSMI_F_HOST_FIFO_QUEUED 0x0002
209#define HGSMI_F_HOST_FIFO_READ 0x0004
210#define HGSMI_F_HOST_FIFO_PROCESSED 0x0008
211#define HGSMI_F_HOST_FIFO_FREE 0x0010
212#define HGSMI_F_HOST_FIFO_CANCELED 0x0020
213
214static DECLCALLBACK(void) hgsmiHostCommandFreeCallback (void *pvCallback);
215
216#ifdef VBOX_WITH_WDDM
217
218typedef struct HGSMIGUESTCOMPLENTRY
219{
220 RTLISTNODE nodeEntry;
221 HGSMIOFFSET offBuffer; /* Offset of the guest command buffer. */
222} HGSMIGUESTCOMPLENTRY;
223
224
225static void hgsmiGuestCompletionFIFOFree (HGSMIINSTANCE *pIns, HGSMIGUESTCOMPLENTRY *pEntry)
226{
227 NOREF (pIns);
228 RTMemFree (pEntry);
229}
230
231static int hgsmiGuestCompletionFIFOAlloc (HGSMIINSTANCE *pIns, HGSMIGUESTCOMPLENTRY **ppEntry)
232{
233 int rc = VINF_SUCCESS;
234
235 NOREF (pIns);
236
237 HGSMIGUESTCOMPLENTRY *pEntry = (HGSMIGUESTCOMPLENTRY *)RTMemAllocZ (sizeof (HGSMIGUESTCOMPLENTRY));
238
239 if (pEntry)
240 *ppEntry = pEntry;
241 else
242 rc = VERR_NO_MEMORY;
243
244 return rc;
245}
246
247#endif
248
249static int hgsmiLock (HGSMIINSTANCE *pIns)
250{
251 int rc = RTCritSectEnter (&pIns->instanceCritSect);
252 AssertRC (rc);
253 return rc;
254}
255
256static void hgsmiUnlock (HGSMIINSTANCE *pIns)
257{
258 int rc = RTCritSectLeave (&pIns->instanceCritSect);
259 AssertRC (rc);
260}
261
262static int hgsmiFIFOLock (HGSMIINSTANCE *pIns)
263{
264 int rc = RTCritSectEnter (&pIns->hostFIFOCritSect);
265 AssertRC (rc);
266 return rc;
267}
268
269static void hgsmiFIFOUnlock (HGSMIINSTANCE *pIns)
270{
271 int rc = RTCritSectLeave (&pIns->hostFIFOCritSect);
272 AssertRC (rc);
273}
274
275/*
276 * Virtual hardware IO handlers.
277 */
278
279/* The guest submits a new buffer to the host.
280 * Called from the HGSMI_IO_GUEST write handler.
281 * @thread EMT
282 */
283void HGSMIGuestWrite (PHGSMIINSTANCE pIns,
284 HGSMIOFFSET offBuffer)
285{
286 HGSMIBufferProcess (&pIns->area, &pIns->channelInfo, offBuffer);
287}
288
289#ifdef VBOX_WITH_WDDM
290static HGSMIOFFSET hgsmiProcessGuestCmdCompletion(HGSMIINSTANCE *pIns)
291{
292 HGSMIOFFSET offCmd = HGSMIOFFSET_VOID;
293 int rc = hgsmiFIFOLock(pIns);
294 AssertRC(rc);
295 if (RT_SUCCESS(rc))
296 {
297 HGSMIGUESTCOMPLENTRY *pEntry = RTListGetFirst(&pIns->guestCmdCompleted, HGSMIGUESTCOMPLENTRY, nodeEntry);
298 if (pEntry)
299 {
300 RTListNodeRemove(&pEntry->nodeEntry);
301 }
302
303 if (RTListIsEmpty(&pIns->guestCmdCompleted))
304 {
305 if (pIns->pHGFlags)
306 ASMAtomicAndU32(&pIns->pHGFlags->u32HostFlags, ~HGSMIHOSTFLAGS_GCOMMAND_COMPLETED);
307 }
308
309 hgsmiFIFOUnlock(pIns);
310
311 if (pEntry)
312 {
313 offCmd = pEntry->offBuffer;
314
315 LogFlowFunc(("host FIFO head %p.\n", pEntry));
316
317 hgsmiGuestCompletionFIFOFree(pIns, pEntry);
318 }
319 }
320 return offCmd;
321}
322#endif
323
324
325/* Called from HGSMI_IO_GUEST read handler. */
326HGSMIOFFSET HGSMIGuestRead (PHGSMIINSTANCE pIns)
327{
328 LogFlowFunc(("pIns %p\n", pIns));
329
330 AssertPtr(pIns);
331
332 VM_ASSERT_EMT(pIns->pVM);
333
334#ifndef VBOX_WITH_WDDM
335 /* Currently there is no functionality here. */
336 NOREF(pIns);
337
338 return HGSMIOFFSET_VOID;
339#else
340 /* use this to speedup guest cmd completion
341 * this mechanism is alternative to submitting H->G command for notification */
342 HGSMIOFFSET offCmd = hgsmiProcessGuestCmdCompletion(pIns);
343 return offCmd;
344#endif
345}
346
347static bool hgsmiProcessHostCmdCompletion(HGSMIINSTANCE *pIns,
348 HGSMIOFFSET offBuffer,
349 bool bCompleteFirst)
350{
351 VM_ASSERT_EMT(pIns->pVM);
352
353 int rc = hgsmiFIFOLock(pIns);
354 if (RT_SUCCESS(rc))
355 {
356 /* Search the Read list for the given buffer offset. */
357 HGSMIHOSTFIFOENTRY *pEntry = NULL;
358
359 HGSMIHOSTFIFOENTRY *pIter;
360 RTListForEach(&pIns->hostFIFORead, pIter, HGSMIHOSTFIFOENTRY, nodeEntry)
361 {
362 Assert(pIter->fl == (HGSMI_F_HOST_FIFO_ALLOCATED | HGSMI_F_HOST_FIFO_READ));
363 if (bCompleteFirst || pIter->offBuffer == offBuffer)
364 {
365 pEntry = pIter;
366 break;
367 }
368 }
369
370 LogFlowFunc(("read list entry: %p.\n", pEntry));
371
372 Assert(pEntry || bCompleteFirst);
373
374 if (pEntry)
375 {
376 RTListNodeRemove(&pEntry->nodeEntry);
377
378 pEntry->fl &= ~HGSMI_F_HOST_FIFO_READ;
379 pEntry->fl |= HGSMI_F_HOST_FIFO_PROCESSED;
380
381 RTListAppend(&pIns->hostFIFOProcessed, &pEntry->nodeEntry);
382
383 hgsmiFIFOUnlock(pIns);
384
385 hgsmiHostCommandFreeCallback(pEntry);
386
387 return true;
388 }
389
390 hgsmiFIFOUnlock(pIns);
391 if (!bCompleteFirst)
392 LogRel(("HGSMI[%s]: ignored invalid write to the host FIFO: 0x%08X!!!\n", pIns->pszName, offBuffer));
393 }
394 return false;
395}
396
397/* The guest has finished processing of a buffer previously submitted by the host.
398 * Called from HGSMI_IO_HOST write handler.
399 * @thread EMT
400 */
401void HGSMIHostWrite (HGSMIINSTANCE *pIns,
402 HGSMIOFFSET offBuffer)
403{
404 LogFlowFunc(("pIns %p offBuffer 0x%x\n", pIns, offBuffer));
405
406 hgsmiProcessHostCmdCompletion (pIns, offBuffer, false);
407}
408
409/* The guest reads a new host buffer to be processed.
410 * Called from the HGSMI_IO_HOST read handler.
411 * @thread EMT
412 */
413HGSMIOFFSET HGSMIHostRead (HGSMIINSTANCE *pIns)
414{
415 LogFlowFunc(("pIns %p\n", pIns));
416
417 VM_ASSERT_EMT(pIns->pVM);
418
419 AssertPtrReturn(pIns->pHGFlags, HGSMIOFFSET_VOID);
420 int rc = hgsmiFIFOLock(pIns);
421 AssertRC(rc);
422 if (RT_SUCCESS(rc))
423 {
424 /* Get the host FIFO head entry. */
425 HGSMIHOSTFIFOENTRY *pEntry = RTListGetFirst(&pIns->hostFIFO, HGSMIHOSTFIFOENTRY, nodeEntry);
426
427 LogFlowFunc(("host FIFO head %p.\n", pEntry));
428
429 if (pEntry != NULL)
430 {
431 Assert(pEntry->fl == (HGSMI_F_HOST_FIFO_ALLOCATED | HGSMI_F_HOST_FIFO_QUEUED));
432
433 /*
434 * Move the entry to the Read list.
435 */
436 RTListNodeRemove(&pEntry->nodeEntry);
437
438 if (RTListIsEmpty(&pIns->hostFIFO))
439 {
440 ASMAtomicAndU32(&pIns->pHGFlags->u32HostFlags, (~HGSMIHOSTFLAGS_COMMANDS_PENDING));
441 }
442
443 pEntry->fl &= ~HGSMI_F_HOST_FIFO_QUEUED;
444 pEntry->fl |= HGSMI_F_HOST_FIFO_READ;
445
446 RTListAppend(&pIns->hostFIFORead, &pEntry->nodeEntry);
447
448 hgsmiFIFOUnlock(pIns);
449
450 /* Return the buffer offset of the host FIFO head. */
451 return pEntry->offBuffer;
452 }
453
454 hgsmiFIFOUnlock(pIns);
455 }
456 /* Special value that means there is no host buffers to be processed. */
457 return HGSMIOFFSET_VOID;
458}
459
460
461/* Tells the guest that a new buffer to be processed is available from the host. */
462static void hgsmiNotifyGuest (HGSMIINSTANCE *pIns)
463{
464 if (pIns->pfnNotifyGuest)
465 {
466 pIns->pfnNotifyGuest (pIns->pvNotifyGuest);
467 }
468}
469
470void HGSMISetHostGuestFlags(HGSMIINSTANCE *pIns, uint32_t flags)
471{
472 AssertPtrReturnVoid(pIns->pHGFlags);
473 ASMAtomicOrU32(&pIns->pHGFlags->u32HostFlags, flags);
474}
475
476uint32_t HGSMIGetHostGuestFlags(HGSMIINSTANCE *pIns)
477{
478 return pIns->pHGFlags? ASMAtomicReadU32(&pIns->pHGFlags->u32HostFlags) : 0;
479}
480
481void HGSMIClearHostGuestFlags(HGSMIINSTANCE *pIns, uint32_t flags)
482{
483 AssertPtrReturnVoid(pIns->pHGFlags);
484 ASMAtomicAndU32(&pIns->pHGFlags->u32HostFlags, (~flags));
485}
486
487/*
488 * The host heap.
489 *
490 * Uses the RTHeap implementation.
491 *
492 */
493static int hgsmiHostHeapLock (HGSMIINSTANCE *pIns)
494{
495 int rc = RTCritSectEnter (&pIns->hostHeapCritSect);
496 AssertRC (rc);
497 return rc;
498}
499
500static void hgsmiHostHeapUnlock (HGSMIINSTANCE *pIns)
501{
502 int rc = RTCritSectLeave (&pIns->hostHeapCritSect);
503 AssertRC (rc);
504}
505
506static HGSMIOFFSET hgsmiHostHeapOffset(HGSMIHOSTHEAP *pHeap)
507{
508 return pHeap->area.offBase;
509}
510
511static HGSMISIZE hgsmiHostHeapSize(HGSMIHOSTHEAP *pHeap)
512{
513 return pHeap->area.cbArea;
514}
515
516static void *hgsmiHostHeapBufferAlloc(HGSMIHOSTHEAP *pHeap,
517 HGSMISIZE cbBuffer)
518{
519 void *pvBuf = NULL;
520
521 if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_MA)
522 {
523 pvBuf = HGSMIMAAlloc(&pHeap->u.ma, cbBuffer);
524 }
525 else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_POINTER)
526 {
527 pvBuf = RTHeapSimpleAlloc(pHeap->u.legacy.u.hPtr, cbBuffer, 0);
528 }
529 else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
530 {
531 pvBuf = RTHeapOffsetAlloc(pHeap->u.legacy.u.hOff, cbBuffer, 0);
532 }
533
534 if (pvBuf)
535 {
536 ++pHeap->cRefs;
537 }
538
539 return pvBuf;
540}
541
542static void hgsmiHostHeapBufferFree(HGSMIHOSTHEAP *pHeap,
543 void *pvBuf)
544{
545 if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_MA)
546 {
547 HGSMIMAFree(&pHeap->u.ma, pvBuf);
548 }
549 else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_POINTER)
550 {
551 RTHeapSimpleFree(pHeap->u.legacy.u.hPtr, pvBuf);
552 }
553 else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
554 {
555 RTHeapOffsetFree(pHeap->u.legacy.u.hOff, pvBuf);
556 }
557 --pHeap->cRefs;
558}
559
560static void *hgsmiHostHeapDataAlloc(HGSMIHOSTHEAP *pHeap,
561 HGSMISIZE cbData,
562 uint8_t u8Channel,
563 uint16_t u16ChannelInfo)
564{
565 HGSMISIZE cbAlloc = HGSMIBufferRequiredSize(cbData);
566 HGSMIBUFFERHEADER *pHeader = (HGSMIBUFFERHEADER *)hgsmiHostHeapBufferAlloc(pHeap, cbAlloc);
567 if (!pHeader)
568 return NULL;
569
570 HGSMIBufferInitializeSingle(&pHeap->area, pHeader, cbAlloc, u8Channel, u16ChannelInfo);
571
572 return HGSMIBufferDataFromPtr(pHeader);
573}
574
575static void hgsmiHostHeapDataFree(HGSMIHOSTHEAP *pHeap,
576 void *pvData)
577{
578 if ( pvData
579 && pHeap->u32HeapType != HGSMI_HEAP_TYPE_NULL)
580 {
581 HGSMIBUFFERHEADER *pHeader = HGSMIBufferHeaderFromData(pvData);
582 hgsmiHostHeapBufferFree(pHeap, pHeader);
583 }
584}
585
586/* Needed for heap relocation: offset of the heap handle relative to the start of heap area. */
587static HGSMIOFFSET hgsmiHostHeapHandleLocationOffset(HGSMIHOSTHEAP *pHeap)
588{
589 HGSMIOFFSET offHeapHandle;
590 if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_POINTER)
591 {
592 offHeapHandle = (HGSMIOFFSET)((uintptr_t)pHeap->u.legacy.u.hPtr - (uintptr_t)pHeap->area.pu8Base);
593 }
594 else if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
595 {
596 offHeapHandle = (HGSMIOFFSET)((uintptr_t)pHeap->u.legacy.u.hOff - (uintptr_t)pHeap->area.pu8Base);
597 }
598 else
599 {
600 offHeapHandle = HGSMIOFFSET_VOID;
601 }
602 return offHeapHandle;
603}
604
605static int hgsmiHostHeapRelocate(HGSMIHOSTHEAP *pHeap,
606 uint32_t u32HeapType,
607 void *pvBase,
608 uint32_t offHeapHandle,
609 uintptr_t offDelta,
610 HGSMISIZE cbArea,
611 HGSMIOFFSET offBase)
612{
613 int rc = HGSMIAreaInitialize(&pHeap->area, pvBase, cbArea, offBase);
614 if (RT_SUCCESS(rc))
615 {
616 if (u32HeapType == HGSMI_HEAP_TYPE_OFFSET)
617 {
618 pHeap->u.legacy.u.hOff = (RTHEAPOFFSET)((uint8_t *)pvBase + offHeapHandle);
619 }
620 else if (u32HeapType == HGSMI_HEAP_TYPE_POINTER)
621 {
622 pHeap->u.legacy.u.hPtr = (RTHEAPSIMPLE)((uint8_t *)pvBase + offHeapHandle);
623 rc = RTHeapSimpleRelocate(pHeap->u.legacy.u.hPtr, offDelta); AssertRC(rc);
624 }
625 else
626 {
627 /* HGSMI_HEAP_TYPE_MA does not need the relocation. */
628 rc = VERR_NOT_SUPPORTED;
629 }
630
631 if (RT_SUCCESS(rc))
632 {
633 pHeap->u32HeapType = u32HeapType;
634 }
635 else
636 {
637 HGSMIAreaClear(&pHeap->area);
638 }
639 }
640
641 return rc;
642}
643
644static int hgsmiHostHeapRestoreMA(HGSMIHOSTHEAP *pHeap,
645 void *pvBase,
646 HGSMISIZE cbArea,
647 HGSMIOFFSET offBase,
648 uint32_t cBlocks,
649 HGSMIOFFSET *paDescriptors,
650 HGSMISIZE cbMaxBlock,
651 HGSMIENV *pEnv)
652{
653 int rc = HGSMIAreaInitialize(&pHeap->area, pvBase, cbArea, offBase);
654 if (RT_SUCCESS(rc))
655 {
656 rc = HGSMIMAInit(&pHeap->u.ma, &pHeap->area, paDescriptors, cBlocks, cbMaxBlock, pEnv);
657
658 if (RT_SUCCESS(rc))
659 {
660 pHeap->u32HeapType = HGSMI_HEAP_TYPE_MA;
661 }
662 else
663 {
664 HGSMIAreaClear(&pHeap->area);
665 }
666 }
667
668 return rc;
669}
670
671static void hgsmiHostHeapSetupUninitialized(HGSMIHOSTHEAP *pHeap)
672{
673 RT_ZERO(*pHeap);
674 pHeap->u32HeapType = HGSMI_HEAP_TYPE_NULL;
675}
676
677static void hgsmiHostHeapDestroy(HGSMIHOSTHEAP *pHeap)
678{
679 if (pHeap->u32HeapType == HGSMI_HEAP_TYPE_MA)
680 {
681 HGSMIMAUninit(&pHeap->u.ma);
682 }
683 hgsmiHostHeapSetupUninitialized(pHeap);
684}
685
686static int hgsmiHostFIFOAlloc (HGSMIINSTANCE *pIns, HGSMIHOSTFIFOENTRY **ppEntry)
687{
688 int rc = VINF_SUCCESS;
689
690 NOREF (pIns);
691
692 HGSMIHOSTFIFOENTRY *pEntry = (HGSMIHOSTFIFOENTRY *)RTMemAllocZ (sizeof (HGSMIHOSTFIFOENTRY));
693
694 if (pEntry)
695 {
696 pEntry->fl = HGSMI_F_HOST_FIFO_ALLOCATED;
697 }
698 else
699 {
700 rc = VERR_NO_MEMORY;
701 }
702
703 if (RT_SUCCESS (rc))
704 {
705 *ppEntry = pEntry;
706 }
707
708 return rc;
709}
710
711static void hgsmiHostFIFOFree (HGSMIINSTANCE *pIns, HGSMIHOSTFIFOENTRY *pEntry)
712{
713 NOREF (pIns);
714 RTMemFree (pEntry);
715}
716
717static int hgsmiHostCommandFreeByEntry (HGSMIHOSTFIFOENTRY *pEntry)
718{
719 LogFlowFunc(("offBuffer 0x%08X\n", pEntry->offBuffer));
720
721 HGSMIINSTANCE *pIns = pEntry->pIns;
722 int rc = hgsmiFIFOLock (pIns);
723 if (RT_SUCCESS(rc))
724 {
725 RTListNodeRemove(&pEntry->nodeEntry);
726 hgsmiFIFOUnlock (pIns);
727
728 void *pvData = HGSMIBufferDataFromOffset(&pIns->hostHeap.area, pEntry->offBuffer);
729
730 rc = hgsmiHostHeapLock (pIns);
731 if (RT_SUCCESS(rc))
732 {
733 /* Deallocate the host heap memory. */
734 hgsmiHostHeapDataFree(&pIns->hostHeap, pvData);
735
736 hgsmiHostHeapUnlock(pIns);
737 }
738
739 hgsmiHostFIFOFree (pIns, pEntry);
740 }
741
742 LogFlowFunc(("%Rrc\n", rc));
743 return rc;
744}
745
746static int hgsmiHostCommandFree(HGSMIINSTANCE *pIns,
747 void *pvData)
748{
749 HGSMIOFFSET offBuffer = HGSMIBufferOffsetFromData(&pIns->hostHeap.area, pvData);
750 HGSMIHOSTFIFOENTRY *pEntry = NULL;
751
752 int rc = hgsmiFIFOLock(pIns);
753 if (RT_SUCCESS(rc))
754 {
755 /* Search the Processed list for the given offBuffer. */
756 HGSMIHOSTFIFOENTRY *pIter;
757 RTListForEach(&pIns->hostFIFOProcessed, pIter, HGSMIHOSTFIFOENTRY, nodeEntry)
758 {
759 Assert(pIter->fl == (HGSMI_F_HOST_FIFO_ALLOCATED | HGSMI_F_HOST_FIFO_PROCESSED));
760
761 if (pIter->offBuffer == offBuffer)
762 {
763 pEntry = pIter;
764 break;
765 }
766 }
767
768 if (pEntry)
769 {
770 RTListNodeRemove(&pEntry->nodeEntry);
771 }
772 else
773 {
774 AssertLogRelMsgFailed(("HGSMI[%s]: the host frees unprocessed FIFO entry: 0x%08X\n",
775 pIns->pszName, offBuffer));
776 }
777
778 hgsmiFIFOUnlock (pIns);
779
780 rc = hgsmiHostHeapLock (pIns);
781 if (RT_SUCCESS(rc))
782 {
783 /* Deallocate the host heap memory. */
784 hgsmiHostHeapDataFree(&pIns->hostHeap, pvData);
785
786 hgsmiHostHeapUnlock(pIns);
787 }
788
789 if (pEntry)
790 {
791 /* Deallocate the entry. */
792 hgsmiHostFIFOFree(pIns, pEntry);
793 }
794 }
795
796 return rc;
797}
798
799static DECLCALLBACK(void) hgsmiHostCommandFreeCallback (void *pvCallback)
800{
801 /* Guest has processed the command. */
802 HGSMIHOSTFIFOENTRY *pEntry = (HGSMIHOSTFIFOENTRY *)pvCallback;
803
804 Assert(pEntry->fl == (HGSMI_F_HOST_FIFO_ALLOCATED | HGSMI_F_HOST_FIFO_PROCESSED));
805
806 /* This is a simple callback, just signal the event. */
807 hgsmiHostCommandFreeByEntry (pEntry);
808}
809
810static int hgsmiHostCommandWrite(HGSMIINSTANCE *pIns,
811 HGSMIOFFSET offBuffer)
812{
813 AssertPtrReturn(pIns->pHGFlags, VERR_WRONG_ORDER);
814
815 HGSMIHOSTFIFOENTRY *pEntry;
816 int rc = hgsmiHostFIFOAlloc(pIns, &pEntry);
817
818 if (RT_SUCCESS(rc))
819 {
820 /* Initialize the new entry and add it to the FIFO. */
821 pEntry->fl |= HGSMI_F_HOST_FIFO_QUEUED;
822
823 pEntry->pIns = pIns;
824 pEntry->offBuffer = offBuffer;
825
826 rc = hgsmiFIFOLock(pIns);
827 if (RT_SUCCESS(rc))
828 {
829 ASMAtomicOrU32(&pIns->pHGFlags->u32HostFlags, HGSMIHOSTFLAGS_COMMANDS_PENDING);
830 RTListAppend(&pIns->hostFIFO, &pEntry->nodeEntry);
831
832 hgsmiFIFOUnlock(pIns);
833 }
834 else
835 {
836 hgsmiHostFIFOFree(pIns, pEntry);
837 }
838 }
839
840 return rc;
841}
842
843
844/**
845 * Append the shared memory block to the FIFO, inform the guest.
846 *
847 * @param pIns Pointer to HGSMI instance.
848 * @param pvData The shared memory block data pointer.
849 * @param fDoIrq Whether the guest interrupt should be generated, i.e. if the command is not
850 * urgent (e.g. some guest command completion notification that does not require
851 * post-processing) the command could be submitted without raising an irq.
852 * @thread EMT
853 */
854static int hgsmiHostCommandSubmit(HGSMIINSTANCE *pIns,
855 void *pvData,
856 bool fDoIrq)
857{
858 /* Append the command to FIFO. */
859 HGSMIOFFSET offBuffer = HGSMIBufferOffsetFromData(&pIns->hostHeap.area, pvData);
860 int rc = hgsmiHostCommandWrite(pIns, offBuffer);
861 if (RT_SUCCESS(rc))
862 {
863 if (fDoIrq)
864 {
865 /* Now guest can read the FIFO, the notification is informational. */
866 hgsmiNotifyGuest(pIns);
867 }
868 }
869
870 return rc;
871}
872
873/**
874 * Allocate a shared memory buffer. The host can write command/data to the memory.
875 * The allocated buffer contains the 'header', 'data' and the 'tail', but *ppvData
876 * will point to the 'data'.
877 *
878 * @return VBox status code. Pointer to the payload data in *ppvData.
879 * @param pIns HGSMI instance,
880 * @param ppvData Where to store the allocated memory pointer to data.
881 * @param cbData How many bytes of data to allocate.
882 * @param u8Channel HGSMI channel.
883 * @param u16ChannelInfo Command parameter.
884 */
885int HGSMIHostCommandAlloc(HGSMIINSTANCE *pIns,
886 void **ppvData,
887 HGSMISIZE cbData,
888 uint8_t u8Channel,
889 uint16_t u16ChannelInfo)
890{
891 LogFlowFunc(("pIns = %p, cbData = %d, u8Channel %d, u16ChannelInfo 0x%04X\n",
892 pIns, cbData, u8Channel, u16ChannelInfo));
893
894 int rc = hgsmiHostHeapLock(pIns);
895 if (RT_SUCCESS(rc))
896 {
897 void *pvData = hgsmiHostHeapDataAlloc(&pIns->hostHeap,
898 cbData,
899 u8Channel,
900 u16ChannelInfo);
901 hgsmiHostHeapUnlock(pIns);
902
903 if (pvData)
904 {
905 *ppvData = pvData;
906 }
907 else
908 {
909 LogRel(("HGSMI[%s]: host heap allocation failed %d bytes\n", pIns->pszName, cbData));
910 rc = VERR_NO_MEMORY;
911 }
912 }
913
914 LogFlowFunc(("%Rrc, pvData = %p\n", rc, *ppvData));
915 return rc;
916}
917
918/**
919 * Convenience function that allows posting the host command asynchronously
920 * and make it freed on completion.
921 * The caller does not get notified in any way on command completion,
922 * on successful return the pvData buffer can not be used after being passed to this function.
923 *
924 * @param pIns HGSMI instance,
925 * @param pvData The pointer returned by 'HGSMIHostCommandAlloc'.
926 * @param fDoIrq Specifies whether the guest interrupt should be generated.
927 * In case the command is not urgent (e.g. some guest command
928 * completion notification that does not require post-processing)
929 * the command could be posted without raising an irq.
930 */
931int HGSMIHostCommandSubmitAndFreeAsynch(PHGSMIINSTANCE pIns,
932 void *pvData,
933 bool fDoIrq)
934{
935 LogFlowFunc(("pIns = %p, pvData = %p, fDoIrq = %d\n", pIns, pvData, fDoIrq));
936
937 int rc;
938 if (HGSMIAreaContainsPointer(&pIns->hostHeap.area, pvData))
939 {
940 rc = hgsmiHostCommandSubmit(pIns, pvData, fDoIrq);
941 }
942 else
943 {
944 AssertLogRelMsgFailed(("HGSMI[%s]: host submits invalid command %p/%p\n",
945 pIns->pszName, pvData, pIns->hostHeap.area.pu8Base));
946 rc = VERR_INVALID_POINTER;
947 }
948
949 LogFlowFunc(("rc = %Rrc\n", rc));
950 return rc;
951}
952
953/**
954 * Free the shared memory block.
955 *
956 * @param pIns Pointer to HGSMI instance,
957 * @param pvData The pointer returned by 'HGSMIHostCommandAlloc'.
958 */
959int HGSMIHostCommandFree(HGSMIINSTANCE *pIns,
960 void *pvData)
961{
962 LogFlowFunc(("pIns = %p, pvData = %p\n", pIns, pvData));
963
964 int rc;
965 if (HGSMIAreaContainsPointer(&pIns->hostHeap.area, pvData))
966 {
967 rc = hgsmiHostCommandFree(pIns, pvData);
968 }
969 else
970 {
971 AssertLogRelMsgFailed(("HGSMI[%s]: the host frees invalid FIFO entry %p/%p\n",
972 pIns->pszName, pvData, pIns->hostHeap.area.pu8Base));
973 rc = VERR_INVALID_POINTER;
974 }
975
976 LogFlowFunc(("rc = %Rrc\n", rc));
977 return rc;
978}
979
980static DECLCALLBACK(void *) hgsmiEnvAlloc(void *pvEnv, HGSMISIZE cb)
981{
982 NOREF(pvEnv);
983 return RTMemAlloc(cb);
984}
985
986static DECLCALLBACK(void) hgsmiEnvFree(void *pvEnv, void *pv)
987{
988 NOREF(pvEnv);
989 RTMemFree(pv);
990}
991
992static HGSMIENV g_hgsmiEnv =
993{
994 NULL,
995 hgsmiEnvAlloc,
996 hgsmiEnvFree
997};
998
999int HGSMIHostHeapSetup(PHGSMIINSTANCE pIns,
1000 HGSMIOFFSET offHeap,
1001 HGSMISIZE cbHeap)
1002{
1003 LogFlowFunc(("pIns %p, offHeap 0x%08X, cbHeap = 0x%08X\n", pIns, offHeap, cbHeap));
1004
1005 int rc = VINF_SUCCESS;
1006
1007 AssertPtrReturn(pIns, VERR_INVALID_PARAMETER);
1008
1009 if ( offHeap >= pIns->area.cbArea
1010 || cbHeap > pIns->area.cbArea
1011 || offHeap > pIns->area.cbArea - cbHeap)
1012 {
1013 AssertLogRelMsgFailed(("offHeap 0x%08X, cbHeap = 0x%08X, pIns->area.cbArea 0x%08X\n",
1014 offHeap, cbHeap, pIns->area.cbArea));
1015 rc = VERR_INVALID_PARAMETER;
1016 }
1017 else
1018 {
1019 rc = hgsmiHostHeapLock (pIns);
1020
1021 if (RT_SUCCESS (rc))
1022 {
1023 if (pIns->hostHeap.cRefs)
1024 {
1025 AssertLogRelMsgFailed(("HGSMI[%s]: host heap setup ignored. %d allocated.\n",
1026 pIns->pszName, pIns->hostHeap.cRefs));
1027 /* It is possible to change the heap only if there is no pending allocations. */
1028 rc = VERR_ACCESS_DENIED;
1029 }
1030 else
1031 {
1032 rc = HGSMIAreaInitialize(&pIns->hostHeap.area, pIns->area.pu8Base + offHeap, cbHeap, offHeap);
1033 if (RT_SUCCESS(rc))
1034 {
1035 rc = HGSMIMAInit(&pIns->hostHeap.u.ma, &pIns->hostHeap.area, NULL, 0, 0, &g_hgsmiEnv);
1036 }
1037
1038 if (RT_SUCCESS(rc))
1039 {
1040 pIns->hostHeap.u32HeapType = HGSMI_HEAP_TYPE_MA;
1041 }
1042 else
1043 {
1044 HGSMIAreaClear(&pIns->hostHeap.area);
1045 }
1046 }
1047
1048 hgsmiHostHeapUnlock (pIns);
1049 }
1050 }
1051
1052 LogFlowFunc(("rc = %Rrc\n", rc));
1053
1054 return rc;
1055}
1056
1057static int hgsmiHostSaveFifoLocked(RTLISTANCHOR *pList, PSSMHANDLE pSSM)
1058{
1059 VBOXHGSMI_SAVE_FIFOSTART(pSSM);
1060
1061 HGSMIHOSTFIFOENTRY *pIter;
1062
1063 uint32_t cEntries = 0;
1064 RTListForEach(pList, pIter, HGSMIHOSTFIFOENTRY, nodeEntry)
1065 {
1066 ++cEntries;
1067 }
1068
1069 int rc = SSMR3PutU32(pSSM, cEntries);
1070 if (RT_SUCCESS(rc))
1071 {
1072 RTListForEach(pList, pIter, HGSMIHOSTFIFOENTRY, nodeEntry)
1073 {
1074 SSMR3PutU32(pSSM, pIter->fl);
1075 rc = SSMR3PutU32(pSSM, pIter->offBuffer);
1076 if (RT_FAILURE(rc))
1077 {
1078 break;
1079 }
1080 }
1081 }
1082
1083 VBOXHGSMI_SAVE_FIFOSTOP(pSSM);
1084
1085 return rc;
1086}
1087
1088static int hgsmiHostSaveGuestCmdCompletedFifoLocked(RTLISTANCHOR *pList, PSSMHANDLE pSSM)
1089{
1090 VBOXHGSMI_SAVE_FIFOSTART(pSSM);
1091
1092 HGSMIGUESTCOMPLENTRY *pIter;
1093
1094 uint32_t cEntries = 0;
1095 RTListForEach(pList, pIter, HGSMIGUESTCOMPLENTRY, nodeEntry)
1096 {
1097 ++cEntries;
1098 }
1099 int rc = SSMR3PutU32(pSSM, cEntries);
1100 if (RT_SUCCESS(rc))
1101 {
1102 RTListForEach(pList, pIter, HGSMIGUESTCOMPLENTRY, nodeEntry)
1103 {
1104 rc = SSMR3PutU32(pSSM, pIter->offBuffer);
1105 if (RT_FAILURE(rc))
1106 {
1107 break;
1108 }
1109 }
1110 }
1111
1112 VBOXHGSMI_SAVE_FIFOSTOP(pSSM);
1113
1114 return rc;
1115}
1116
1117static int hgsmiHostLoadFifoEntryLocked (PHGSMIINSTANCE pIns, HGSMIHOSTFIFOENTRY **ppEntry, PSSMHANDLE pSSM)
1118{
1119 HGSMIHOSTFIFOENTRY *pEntry;
1120 int rc = hgsmiHostFIFOAlloc (pIns, &pEntry); AssertRC(rc);
1121 if (RT_SUCCESS (rc))
1122 {
1123 uint32_t u32;
1124 pEntry->pIns = pIns;
1125 rc = SSMR3GetU32 (pSSM, &u32); AssertRC(rc);
1126 pEntry->fl = u32;
1127 rc = SSMR3GetU32 (pSSM, &pEntry->offBuffer); AssertRC(rc);
1128 if (RT_SUCCESS (rc))
1129 *ppEntry = pEntry;
1130 else
1131 hgsmiHostFIFOFree (pIns, pEntry);
1132 }
1133
1134 return rc;
1135}
1136
1137static int hgsmiHostLoadFifoLocked(PHGSMIINSTANCE pIns, RTLISTANCHOR *pList, PSSMHANDLE pSSM)
1138{
1139 VBOXHGSMI_LOAD_FIFOSTART(pSSM);
1140
1141 uint32_t cEntries = 0;
1142 int rc = SSMR3GetU32(pSSM, &cEntries);
1143 if (RT_SUCCESS(rc) && cEntries)
1144 {
1145 uint32_t i;
1146 for (i = 0; i < cEntries; ++i)
1147 {
1148 HGSMIHOSTFIFOENTRY *pEntry = NULL;
1149 rc = hgsmiHostLoadFifoEntryLocked(pIns, &pEntry, pSSM);
1150 AssertRCBreak(rc);
1151
1152 RTListAppend(pList, &pEntry->nodeEntry);
1153 }
1154 }
1155
1156 VBOXHGSMI_LOAD_FIFOSTOP(pSSM);
1157
1158 return rc;
1159}
1160
1161static int hgsmiHostLoadGuestCmdCompletedFifoEntryLocked (PHGSMIINSTANCE pIns, HGSMIGUESTCOMPLENTRY **ppEntry, PSSMHANDLE pSSM)
1162{
1163 HGSMIGUESTCOMPLENTRY *pEntry;
1164 int rc = hgsmiGuestCompletionFIFOAlloc (pIns, &pEntry); AssertRC(rc);
1165 if (RT_SUCCESS (rc))
1166 {
1167 rc = SSMR3GetU32 (pSSM, &pEntry->offBuffer); AssertRC(rc);
1168 if (RT_SUCCESS (rc))
1169 *ppEntry = pEntry;
1170 else
1171 hgsmiGuestCompletionFIFOFree (pIns, pEntry);
1172 }
1173 return rc;
1174}
1175
1176static int hgsmiHostLoadGuestCmdCompletedFifoLocked(PHGSMIINSTANCE pIns, RTLISTANCHOR *pList, PSSMHANDLE pSSM, uint32_t u32Version)
1177{
1178 VBOXHGSMI_LOAD_FIFOSTART(pSSM);
1179
1180 uint32_t i;
1181
1182 uint32_t cEntries = 0;
1183 int rc = SSMR3GetU32(pSSM, &cEntries);
1184 if (RT_SUCCESS(rc) && cEntries)
1185 {
1186 if (u32Version > VGA_SAVEDSTATE_VERSION_INV_GCMDFIFO)
1187 {
1188 for (i = 0; i < cEntries; ++i)
1189 {
1190 HGSMIGUESTCOMPLENTRY *pEntry = NULL;
1191 rc = hgsmiHostLoadGuestCmdCompletedFifoEntryLocked(pIns, &pEntry, pSSM);
1192 AssertRCBreak(rc);
1193
1194 RTListAppend(pList, &pEntry->nodeEntry);
1195 }
1196 }
1197 else
1198 {
1199 LogRel(("WARNING: the current saved state version has some 3D support data missing, "
1200 "which may lead to some guest applications function improperly"));
1201
1202 /* Just read out all invalid data and discard it. */
1203 for (i = 0; i < cEntries; ++i)
1204 {
1205 HGSMIHOSTFIFOENTRY *pEntry = NULL;
1206 rc = hgsmiHostLoadFifoEntryLocked(pIns, &pEntry, pSSM);
1207 AssertRCBreak(rc);
1208
1209 hgsmiHostFIFOFree(pIns, pEntry);
1210 }
1211 }
1212 }
1213
1214 VBOXHGSMI_LOAD_FIFOSTOP(pSSM);
1215
1216 return rc;
1217}
1218
1219static int hgsmiHostSaveMA(PSSMHANDLE pSSM, HGSMIMADATA *pMA)
1220{
1221 int rc = SSMR3PutU32(pSSM, pMA->cBlocks);
1222 if (RT_SUCCESS(rc))
1223 {
1224 HGSMIMABLOCK *pIter;
1225 RTListForEach(&pMA->listBlocks, pIter, HGSMIMABLOCK, nodeBlock)
1226 {
1227 SSMR3PutU32(pSSM, pIter->descriptor);
1228 }
1229
1230 rc = SSMR3PutU32(pSSM, pMA->cbMaxBlock);
1231 }
1232
1233 return rc;
1234}
1235
1236static int hgsmiHostLoadMA(PSSMHANDLE pSSM, uint32_t *pcBlocks, HGSMIOFFSET **ppaDescriptors, HGSMISIZE *pcbMaxBlock)
1237{
1238 int rc = SSMR3GetU32(pSSM, pcBlocks);
1239 if (RT_SUCCESS(rc))
1240 {
1241 HGSMIOFFSET *paDescriptors = NULL;
1242 if (*pcBlocks > 0)
1243 {
1244 paDescriptors = (HGSMIOFFSET *)RTMemAlloc(*pcBlocks * sizeof(HGSMIOFFSET));
1245 if (paDescriptors)
1246 {
1247 uint32_t i;
1248 for (i = 0; i < *pcBlocks; ++i)
1249 {
1250 SSMR3GetU32(pSSM, &paDescriptors[i]);
1251 }
1252 }
1253 else
1254 {
1255 rc = VERR_NO_MEMORY;
1256 }
1257 }
1258
1259 if (RT_SUCCESS(rc))
1260 {
1261 rc = SSMR3GetU32(pSSM, pcbMaxBlock);
1262 }
1263
1264 if (RT_SUCCESS(rc))
1265 {
1266 *ppaDescriptors = paDescriptors;
1267 }
1268 else
1269 {
1270 RTMemFree(paDescriptors);
1271 }
1272 }
1273
1274 return rc;
1275}
1276
1277int HGSMIHostSaveStateExec (PHGSMIINSTANCE pIns, PSSMHANDLE pSSM)
1278{
1279 VBOXHGSMI_SAVE_START(pSSM);
1280
1281 int rc;
1282
1283 SSMR3PutU32(pSSM, pIns->hostHeap.u32HeapType);
1284
1285 HGSMIOFFSET off = pIns->pHGFlags ? HGSMIPointerToOffset(&pIns->area, (const HGSMIBUFFERHEADER *)pIns->pHGFlags) : HGSMIOFFSET_VOID;
1286 SSMR3PutU32 (pSSM, off);
1287
1288 off = pIns->hostHeap.u32HeapType == HGSMI_HEAP_TYPE_MA?
1289 0:
1290 hgsmiHostHeapHandleLocationOffset(&pIns->hostHeap);
1291 rc = SSMR3PutU32 (pSSM, off);
1292 if (off != HGSMIOFFSET_VOID)
1293 {
1294 SSMR3PutU32 (pSSM, hgsmiHostHeapOffset(&pIns->hostHeap));
1295 SSMR3PutU32 (pSSM, hgsmiHostHeapSize(&pIns->hostHeap));
1296 /* need save mem pointer to calculate offset on restore */
1297 SSMR3PutU64 (pSSM, (uint64_t)(uintptr_t)pIns->area.pu8Base);
1298 rc = hgsmiFIFOLock (pIns);
1299 if (RT_SUCCESS(rc))
1300 {
1301 rc = hgsmiHostSaveFifoLocked (&pIns->hostFIFO, pSSM); AssertRC(rc);
1302 rc = hgsmiHostSaveFifoLocked (&pIns->hostFIFORead, pSSM); AssertRC(rc);
1303 rc = hgsmiHostSaveFifoLocked (&pIns->hostFIFOProcessed, pSSM); AssertRC(rc);
1304#ifdef VBOX_WITH_WDDM
1305 rc = hgsmiHostSaveGuestCmdCompletedFifoLocked (&pIns->guestCmdCompleted, pSSM); AssertRC(rc);
1306#endif
1307
1308 hgsmiFIFOUnlock (pIns);
1309 }
1310
1311 if (RT_SUCCESS(rc))
1312 {
1313 if (pIns->hostHeap.u32HeapType == HGSMI_HEAP_TYPE_MA)
1314 {
1315 rc = hgsmiHostSaveMA(pSSM, &pIns->hostHeap.u.ma);
1316 }
1317 }
1318 }
1319
1320 VBOXHGSMI_SAVE_STOP(pSSM);
1321
1322 return rc;
1323}
1324
1325int HGSMIHostLoadStateExec (PHGSMIINSTANCE pIns, PSSMHANDLE pSSM, uint32_t u32Version)
1326{
1327 if (u32Version < VGA_SAVEDSTATE_VERSION_HGSMI)
1328 return VINF_SUCCESS;
1329
1330 VBOXHGSMI_LOAD_START(pSSM);
1331
1332 int rc;
1333 HGSMIOFFSET off;
1334 uint32_t u32HeapType = HGSMI_HEAP_TYPE_NULL;
1335
1336 if (u32Version >= VGA_SAVEDSTATE_VERSION_HGSMIMA)
1337 {
1338 rc = SSMR3GetU32(pSSM, &u32HeapType);
1339 AssertRCReturn(rc, rc);
1340 }
1341
1342 rc = SSMR3GetU32(pSSM, &off);
1343 AssertLogRelRCReturn(rc, rc);
1344 pIns->pHGFlags = (off != HGSMIOFFSET_VOID) ? (HGSMIHOSTFLAGS*)HGSMIOffsetToPointer (&pIns->area, off) : NULL;
1345
1346 rc = SSMR3GetU32(pSSM, &off);
1347 AssertLogRelRCReturn(rc, rc);
1348 if (off != HGSMIOFFSET_VOID)
1349 {
1350 /* There is a saved heap. */
1351 if (u32HeapType == HGSMI_HEAP_TYPE_NULL)
1352 {
1353 u32HeapType = u32Version > VGA_SAVEDSTATE_VERSION_HOST_HEAP?
1354 HGSMI_HEAP_TYPE_OFFSET:
1355 HGSMI_HEAP_TYPE_POINTER;
1356 }
1357
1358 HGSMIOFFSET offHeap;
1359 SSMR3GetU32(pSSM, &offHeap);
1360 uint32_t cbHeap;
1361 SSMR3GetU32(pSSM, &cbHeap);
1362 uint64_t oldMem;
1363 rc = SSMR3GetU64(pSSM, &oldMem);
1364 AssertLogRelRCReturn(rc, rc);
1365
1366 if (RT_SUCCESS(rc))
1367 {
1368 rc = hgsmiFIFOLock (pIns);
1369 if (RT_SUCCESS(rc))
1370 {
1371 rc = hgsmiHostLoadFifoLocked (pIns, &pIns->hostFIFO, pSSM);
1372 if (RT_SUCCESS(rc))
1373 rc = hgsmiHostLoadFifoLocked (pIns, &pIns->hostFIFORead, pSSM);
1374 if (RT_SUCCESS(rc))
1375 rc = hgsmiHostLoadFifoLocked (pIns, &pIns->hostFIFOProcessed, pSSM);
1376#ifdef VBOX_WITH_WDDM
1377 if (RT_SUCCESS(rc) && u32Version > VGA_SAVEDSTATE_VERSION_PRE_WDDM)
1378 rc = hgsmiHostLoadGuestCmdCompletedFifoLocked (pIns, &pIns->guestCmdCompleted, pSSM, u32Version);
1379#endif
1380
1381 hgsmiFIFOUnlock (pIns);
1382 }
1383 }
1384
1385 if (RT_SUCCESS(rc))
1386 {
1387 if (u32HeapType == HGSMI_HEAP_TYPE_MA)
1388 {
1389 uint32_t cBlocks = 0;
1390 HGSMISIZE cbMaxBlock = 0;
1391 HGSMIOFFSET *paDescriptors = NULL;
1392 rc = hgsmiHostLoadMA(pSSM, &cBlocks, &paDescriptors, &cbMaxBlock);
1393 if (RT_SUCCESS(rc))
1394 {
1395 rc = hgsmiHostHeapRestoreMA(&pIns->hostHeap,
1396 pIns->area.pu8Base+offHeap,
1397 cbHeap,
1398 offHeap,
1399 cBlocks,
1400 paDescriptors,
1401 cbMaxBlock,
1402 &g_hgsmiEnv);
1403
1404 RTMemFree(paDescriptors);
1405 }
1406 }
1407 else if ( u32HeapType == HGSMI_HEAP_TYPE_OFFSET
1408 || u32HeapType == HGSMI_HEAP_TYPE_POINTER)
1409 {
1410 rc = hgsmiHostHeapLock (pIns);
1411 if (RT_SUCCESS (rc))
1412 {
1413 Assert(!pIns->hostHeap.cRefs);
1414 pIns->hostHeap.cRefs = 0;
1415
1416 rc = hgsmiHostHeapRelocate(&pIns->hostHeap,
1417 u32HeapType,
1418 pIns->area.pu8Base+offHeap,
1419 off,
1420 uintptr_t(pIns->area.pu8Base) - uintptr_t(oldMem),
1421 cbHeap,
1422 offHeap);
1423
1424 hgsmiHostHeapUnlock (pIns);
1425 }
1426 }
1427 }
1428 }
1429
1430 VBOXHGSMI_LOAD_STOP(pSSM);
1431
1432 return rc;
1433}
1434
1435/*
1436 * Channels management.
1437 */
1438
1439static int hgsmiChannelMapCreate(PHGSMIINSTANCE pIns, const char *pszChannel, uint8_t *pu8Channel)
1440{
1441 RT_NOREF(pIns, pszChannel, pu8Channel);
1442 /** @todo later */
1443 return VERR_NOT_SUPPORTED;
1444}
1445
1446/* Register a new HGSMI channel by a predefined index.
1447 */
1448int HGSMIHostChannelRegister(PHGSMIINSTANCE pIns,
1449 uint8_t u8Channel,
1450 PFNHGSMICHANNELHANDLER pfnChannelHandler,
1451 void *pvChannelHandler)
1452{
1453 LogFlowFunc(("pIns %p, u8Channel %x, pfnChannelHandler %p, pvChannelHandler %p\n",
1454 pIns, u8Channel, pfnChannelHandler, pvChannelHandler));
1455
1456 AssertReturn(!HGSMI_IS_DYNAMIC_CHANNEL(u8Channel), VERR_INVALID_PARAMETER);
1457 AssertPtrReturn(pIns, VERR_INVALID_PARAMETER);
1458 AssertPtrReturn(pfnChannelHandler, VERR_INVALID_PARAMETER);
1459
1460 int rc = hgsmiLock (pIns);
1461
1462 if (RT_SUCCESS (rc))
1463 {
1464 rc = HGSMIChannelRegister (&pIns->channelInfo, u8Channel, NULL, pfnChannelHandler, pvChannelHandler);
1465
1466 hgsmiUnlock (pIns);
1467 }
1468
1469 LogFlowFunc(("leave rc = %Rrc\n", rc));
1470
1471 return rc;
1472}
1473
1474/* Register a new HGSMI channel by name.
1475 */
1476int HGSMIChannelRegisterName(PHGSMIINSTANCE pIns,
1477 const char *pszChannel,
1478 PFNHGSMICHANNELHANDLER pfnChannelHandler,
1479 void *pvChannelHandler,
1480 uint8_t *pu8Channel)
1481{
1482 LogFlowFunc(("pIns %p, pszChannel %s, pfnChannelHandler %p, pvChannelHandler %p, pu8Channel %p\n",
1483 pIns, pszChannel, pfnChannelHandler, pvChannelHandler, pu8Channel));
1484
1485 AssertPtrReturn(pIns, VERR_INVALID_PARAMETER);
1486 AssertPtrReturn(pszChannel, VERR_INVALID_PARAMETER);
1487 AssertPtrReturn(pu8Channel, VERR_INVALID_PARAMETER);
1488 AssertPtrReturn(pfnChannelHandler, VERR_INVALID_PARAMETER);
1489
1490 int rc;
1491
1492 /* The pointer to the copy will be saved in the channel description. */
1493 char *pszName = RTStrDup (pszChannel);
1494
1495 if (pszName)
1496 {
1497 rc = hgsmiLock (pIns);
1498
1499 if (RT_SUCCESS (rc))
1500 {
1501 rc = hgsmiChannelMapCreate (pIns, pszName, pu8Channel);
1502
1503 if (RT_SUCCESS (rc))
1504 {
1505 rc = HGSMIChannelRegister (&pIns->channelInfo, *pu8Channel, pszName, pfnChannelHandler, pvChannelHandler);
1506 }
1507
1508 hgsmiUnlock (pIns);
1509 }
1510
1511 if (RT_FAILURE (rc))
1512 {
1513 RTStrFree (pszName);
1514 }
1515 }
1516 else
1517 {
1518 rc = VERR_NO_MEMORY;
1519 }
1520
1521 LogFlowFunc(("leave rc = %Rrc\n", rc));
1522
1523 return rc;
1524}
1525
1526void *HGSMIOffsetToPointerHost (PHGSMIINSTANCE pIns,
1527 HGSMIOFFSET offBuffer)
1528{
1529 const HGSMIAREA *pArea = &pIns->area;
1530
1531 if ( offBuffer < pArea->offBase
1532 || offBuffer > pArea->offLast)
1533 {
1534 LogFunc(("offset 0x%x is outside the area [0x%x;0x%x]!!!\n", offBuffer, pArea->offBase, pArea->offLast));
1535 return NULL;
1536 }
1537
1538 return HGSMIOffsetToPointer (pArea, offBuffer);
1539}
1540
1541
1542HGSMIOFFSET HGSMIPointerToOffsetHost (PHGSMIINSTANCE pIns,
1543 const void *pv)
1544{
1545 const HGSMIAREA *pArea = &pIns->area;
1546
1547 uintptr_t pBegin = (uintptr_t)pArea->pu8Base;
1548 uintptr_t pEnd = (uintptr_t)pArea->pu8Base + (pArea->cbArea - 1);
1549 uintptr_t p = (uintptr_t)pv;
1550
1551 if ( p < pBegin
1552 || p > pEnd)
1553 {
1554 LogFunc(("pointer %p is outside the area [%p;%p]!!!\n", pv, pBegin, pEnd));
1555 return HGSMIOFFSET_VOID;
1556 }
1557
1558 return HGSMIPointerToOffset (pArea, (HGSMIBUFFERHEADER *)pv);
1559}
1560
1561
1562void *HGSMIContext (PHGSMIINSTANCE pIns)
1563{
1564 uint8_t *p = (uint8_t *)pIns;
1565 return p + sizeof (HGSMIINSTANCE);
1566}
1567
1568/* The guest submitted a buffer. */
1569static DECLCALLBACK(int) hgsmiChannelHandler (void *pvHandler, uint16_t u16ChannelInfo, void *pvBuffer, HGSMISIZE cbBuffer)
1570{
1571 int rc = VINF_SUCCESS;
1572
1573 LogFlowFunc(("pvHandler %p, u16ChannelInfo %d, pvBuffer %p, cbBuffer %u\n",
1574 pvHandler, u16ChannelInfo, pvBuffer, cbBuffer));
1575
1576 PHGSMIINSTANCE pIns = (PHGSMIINSTANCE)pvHandler;
1577
1578 switch (u16ChannelInfo)
1579 {
1580 case HGSMI_CC_HOST_FLAGS_LOCATION:
1581 {
1582 if (cbBuffer < sizeof (HGSMIBUFFERLOCATION))
1583 {
1584 rc = VERR_INVALID_PARAMETER;
1585 break;
1586 }
1587
1588 HGSMIBUFFERLOCATION *pLoc = (HGSMIBUFFERLOCATION *)pvBuffer;
1589 if (pLoc->cbLocation != sizeof(HGSMIHOSTFLAGS))
1590 {
1591 rc = VERR_INVALID_PARAMETER;
1592 break;
1593 }
1594
1595 pIns->pHGFlags = (HGSMIHOSTFLAGS*)HGSMIOffsetToPointer (&pIns->area, pLoc->offLocation);
1596 } break;
1597
1598 default:
1599 Log(("Unsupported HGSMI guest command %d!!!\n",
1600 u16ChannelInfo));
1601 break;
1602 }
1603
1604 return rc;
1605}
1606
1607int HGSMICreate (PHGSMIINSTANCE *ppIns,
1608 PVM pVM,
1609 const char *pszName,
1610 HGSMIOFFSET offBase,
1611 uint8_t *pu8MemBase,
1612 HGSMISIZE cbMem,
1613 PFNHGSMINOTIFYGUEST pfnNotifyGuest,
1614 void *pvNotifyGuest,
1615 size_t cbContext)
1616{
1617 LogFlowFunc(("ppIns = %p, pVM = %p, pszName = [%s], offBase = 0x%08X, pu8MemBase = %p, cbMem = 0x%08X, "
1618 "pfnNotifyGuest = %p, pvNotifyGuest = %p, cbContext = %d\n",
1619 ppIns,
1620 pVM,
1621 pszName,
1622 offBase,
1623 pu8MemBase,
1624 cbMem,
1625 pfnNotifyGuest,
1626 pvNotifyGuest,
1627 cbContext
1628 ));
1629
1630 AssertPtrReturn(ppIns, VERR_INVALID_PARAMETER);
1631 AssertPtrReturn(pVM, VERR_INVALID_PARAMETER);
1632 AssertPtrReturn(pu8MemBase, VERR_INVALID_PARAMETER);
1633
1634 int rc = VINF_SUCCESS;
1635
1636 PHGSMIINSTANCE pIns = (PHGSMIINSTANCE)RTMemAllocZ (sizeof (HGSMIINSTANCE) + cbContext);
1637
1638 if (!pIns)
1639 {
1640 rc = VERR_NO_MEMORY;
1641 }
1642
1643 if (RT_SUCCESS (rc))
1644 {
1645 rc = HGSMIAreaInitialize (&pIns->area, pu8MemBase, cbMem, offBase);
1646 }
1647
1648 if (RT_SUCCESS (rc))
1649 {
1650 rc = RTCritSectInit (&pIns->instanceCritSect);
1651 }
1652
1653 if (RT_SUCCESS (rc))
1654 {
1655 rc = RTCritSectInit (&pIns->hostHeapCritSect);
1656 }
1657
1658 if (RT_SUCCESS (rc))
1659 {
1660 rc = RTCritSectInit (&pIns->hostFIFOCritSect);
1661 }
1662
1663 if (RT_SUCCESS (rc))
1664 {
1665 pIns->pVM = pVM;
1666
1667 pIns->pszName = VALID_PTR(pszName)? pszName: "";
1668
1669 hgsmiHostHeapSetupUninitialized(&pIns->hostHeap);
1670
1671 pIns->pfnNotifyGuest = pfnNotifyGuest;
1672 pIns->pvNotifyGuest = pvNotifyGuest;
1673
1674 RTListInit(&pIns->hostFIFO);
1675 RTListInit(&pIns->hostFIFORead);
1676 RTListInit(&pIns->hostFIFOProcessed);
1677 RTListInit(&pIns->hostFIFOFree);
1678 RTListInit(&pIns->guestCmdCompleted);
1679 }
1680
1681 rc = HGSMIHostChannelRegister (pIns,
1682 HGSMI_CH_HGSMI,
1683 hgsmiChannelHandler,
1684 pIns);
1685
1686 if (RT_SUCCESS (rc))
1687 {
1688 *ppIns = pIns;
1689 }
1690 else
1691 {
1692 HGSMIDestroy (pIns);
1693 }
1694
1695 LogFlowFunc(("leave rc = %Rrc, pIns = %p\n", rc, pIns));
1696
1697 return rc;
1698}
1699
1700uint32_t HGSMIReset (PHGSMIINSTANCE pIns)
1701{
1702 uint32_t flags = 0;
1703 if (pIns->pHGFlags)
1704 {
1705 /* treat the abandoned commands as read.. */
1706 while (HGSMIHostRead(pIns) != HGSMIOFFSET_VOID)
1707 {}
1708 flags = pIns->pHGFlags->u32HostFlags;
1709 pIns->pHGFlags->u32HostFlags = 0;
1710 }
1711
1712 /* .. and complete them */
1713 while (hgsmiProcessHostCmdCompletion(pIns, 0, true))
1714 {}
1715
1716#ifdef VBOX_WITH_WDDM
1717 while(hgsmiProcessGuestCmdCompletion(pIns) != HGSMIOFFSET_VOID)
1718 {}
1719#endif
1720
1721 hgsmiHostHeapDestroy(&pIns->hostHeap);
1722
1723 return flags;
1724}
1725
1726void HGSMIDestroy (PHGSMIINSTANCE pIns)
1727{
1728 LogFlowFunc(("pIns = %p\n", pIns));
1729
1730 if (pIns)
1731 {
1732 hgsmiHostHeapDestroy(&pIns->hostHeap);
1733
1734 if (RTCritSectIsInitialized (&pIns->hostHeapCritSect))
1735 {
1736 RTCritSectDelete (&pIns->hostHeapCritSect);
1737 }
1738
1739 if (RTCritSectIsInitialized (&pIns->instanceCritSect))
1740 {
1741 RTCritSectDelete (&pIns->instanceCritSect);
1742 }
1743
1744 if (RTCritSectIsInitialized (&pIns->hostFIFOCritSect))
1745 {
1746 RTCritSectDelete (&pIns->hostFIFOCritSect);
1747 }
1748
1749 memset (pIns, 0, sizeof (HGSMIINSTANCE));
1750
1751 RTMemFree (pIns);
1752 }
1753
1754 LogFlowFunc(("leave\n"));
1755}
1756
1757#ifdef VBOX_WITH_WDDM
1758
1759static int hgsmiGuestCommandComplete (HGSMIINSTANCE *pIns, HGSMIOFFSET offMem)
1760{
1761 HGSMIGUESTCOMPLENTRY *pEntry = NULL;
1762
1763 AssertPtrReturn(pIns->pHGFlags, VERR_WRONG_ORDER);
1764 int rc = hgsmiGuestCompletionFIFOAlloc (pIns, &pEntry);
1765 AssertRC(rc);
1766 if (RT_SUCCESS (rc))
1767 {
1768 pEntry->offBuffer = offMem;
1769
1770 rc = hgsmiFIFOLock(pIns);
1771 AssertRC(rc);
1772 if (RT_SUCCESS (rc))
1773 {
1774 RTListAppend(&pIns->guestCmdCompleted, &pEntry->nodeEntry);
1775 ASMAtomicOrU32(&pIns->pHGFlags->u32HostFlags, HGSMIHOSTFLAGS_GCOMMAND_COMPLETED);
1776
1777 hgsmiFIFOUnlock(pIns);
1778 }
1779 else
1780 {
1781 hgsmiGuestCompletionFIFOFree(pIns, pEntry);
1782 }
1783 }
1784
1785 return rc;
1786}
1787
1788int hgsmiCompleteGuestCommand(PHGSMIINSTANCE pIns,
1789 HGSMIOFFSET offBuffer,
1790 bool bDoIrq)
1791{
1792 int rc = hgsmiGuestCommandComplete (pIns, offBuffer);
1793 if (RT_SUCCESS (rc))
1794 {
1795 if (bDoIrq)
1796 {
1797 /* Now guest can read the FIFO, the notification is informational. */
1798 hgsmiNotifyGuest (pIns);
1799 }
1800#ifdef DEBUG_misha
1801 else
1802 {
1803 Assert(0);
1804 }
1805#endif
1806 }
1807 return rc;
1808}
1809
1810int HGSMICompleteGuestCommand(PHGSMIINSTANCE pIns,
1811 void *pvMem,
1812 bool bDoIrq)
1813{
1814 LogFlowFunc(("pIns = %p, pvMem = %p\n", pIns, pvMem));
1815
1816 int rc = VINF_SUCCESS;
1817
1818 HGSMIBUFFERHEADER *pHeader = HGSMIBufferHeaderFromData(pvMem);
1819 HGSMIOFFSET offBuffer = HGSMIPointerToOffset(&pIns->area, pHeader);
1820
1821 Assert(offBuffer != HGSMIOFFSET_VOID);
1822 if (offBuffer != HGSMIOFFSET_VOID)
1823 {
1824 rc = hgsmiCompleteGuestCommand (pIns, offBuffer, bDoIrq);
1825 AssertRC (rc);
1826 }
1827 else
1828 {
1829 LogRel(("invalid cmd offset \n"));
1830 rc = VERR_INVALID_PARAMETER;
1831 }
1832
1833 LogFlowFunc(("rc = %Rrc\n", rc));
1834
1835 return rc;
1836}
1837#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette