1/*
2 * Copyright (C) 2006-2017 Oracle Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23#include <HGSMIBase.h>
24#include <VBoxVideoIPRT.h>
25#include <VBoxVideoGuest.h>
26#include <VBoxVideoVBE.h>
27#include <HGSMIChannels.h>
28#include <HGSMIChSetup.h>
29
30/** Detect whether HGSMI is supported by the host. */
31DECLHIDDEN(bool) VBoxHGSMIIsSupported(void)
32{
33    uint16_t DispiId;
34
35    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID);
36    VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_HGSMI);
37
38    DispiId = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
39
40    return (DispiId == VBE_DISPI_ID_HGSMI);
41}
42
43
44/**
45 * Inform the host of the location of the host flags in VRAM via an HGSMI command.
46 * @returns  IPRT status value.
47 * @returns  VERR_NOT_IMPLEMENTED  if the host does not support the command.
48 * @returns  VERR_NO_MEMORY        if a heap allocation fails.
49 * @param    pCtx                  the context of the guest heap to use.
50 * @param    offLocation           the offset chosen for the flags within guest VRAM.
51 */
52DECLHIDDEN(int) VBoxHGSMIReportFlagsLocation(PHGSMIGUESTCOMMANDCONTEXT pCtx, HGSMIOFFSET offLocation)
53{
54    HGSMIBUFFERLOCATION *p;
55
56    /* Allocate the IO buffer. */
57    p = (HGSMIBUFFERLOCATION *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_HGSMI,
58                                                    HGSMI_CC_HOST_FLAGS_LOCATION);
59    if (!p)
60        return VERR_NO_MEMORY;
61
62    /* Prepare data to be sent to the host. */
63    p->offLocation = offLocation;
64    p->cbLocation  = sizeof(HGSMIHOSTFLAGS);
65    /* No need to check that the buffer is valid as we have just allocated it. */
66    VBoxHGSMIBufferSubmit(pCtx, p);
67    /* Free the IO buffer. */
68    VBoxHGSMIBufferFree(pCtx, p);
69
70    return VINF_SUCCESS;
71}
72
73
74/**
75 * Notify the host of HGSMI-related guest capabilities via an HGSMI command.
76 * @returns  IPRT status value.
77 * @returns  VERR_NOT_IMPLEMENTED  if the host does not support the command.
78 * @returns  VERR_NO_MEMORY        if a heap allocation fails.
79 * @param    pCtx                  the context of the guest heap to use.
80 * @param    fCaps                 the capabilities to report, see VBVACAPS.
81 */
82DECLHIDDEN(int) VBoxHGSMISendCapsInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, uint32_t fCaps)
83{
84    VBVACAPS *p;
85
86    /* Allocate the IO buffer. */
87    p = (VBVACAPS *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_VBVA, VBVA_INFO_CAPS);
88
89    if (!p)
90        return VERR_NO_MEMORY;
91
92    /* Prepare data to be sent to the host. */
93    p->rc    = VERR_NOT_IMPLEMENTED;
94    p->fCaps = fCaps;
95    /* No need to check that the buffer is valid as we have just allocated it. */
96    VBoxHGSMIBufferSubmit(pCtx, p);
97
98    AssertRC(p->rc);
99    /* Free the IO buffer. */
100    VBoxHGSMIBufferFree(pCtx, p);
101    return p->rc;
102}
103
104
105/**
106 * Get the information needed to map the basic communication structures in
107 * device memory into our address space.  All pointer parameters are optional.
108 *
109 * @param  cbVRAM               how much video RAM is allocated to the device
110 * @param  poffVRAMBaseMapping  where to save the offset from the start of the
111 *                              device VRAM of the whole area to map
112 * @param  pcbMapping           where to save the mapping size
113 * @param  poffGuestHeapMemory  where to save the offset into the mapped area
114 *                              of the guest heap backing memory
115 * @param  pcbGuestHeapMemory   where to save the size of the guest heap
116 *                              backing memory
117 * @param  poffHostFlags        where to save the offset into the mapped area
118 *                              of the host flags
119 */
120DECLHIDDEN(void) VBoxHGSMIGetBaseMappingInfo(uint32_t cbVRAM,
121                                             uint32_t *poffVRAMBaseMapping,
122                                             uint32_t *pcbMapping,
123                                             uint32_t *poffGuestHeapMemory,
124                                             uint32_t *pcbGuestHeapMemory,
125                                             uint32_t *poffHostFlags)
126{
127    AssertPtrNullReturnVoid(poffVRAMBaseMapping);
128    AssertPtrNullReturnVoid(pcbMapping);
129    AssertPtrNullReturnVoid(poffGuestHeapMemory);
130    AssertPtrNullReturnVoid(pcbGuestHeapMemory);
131    AssertPtrNullReturnVoid(poffHostFlags);
132    if (poffVRAMBaseMapping)
133        *poffVRAMBaseMapping = cbVRAM - VBVA_ADAPTER_INFORMATION_SIZE;
134    if (pcbMapping)
135        *pcbMapping = VBVA_ADAPTER_INFORMATION_SIZE;
136    if (poffGuestHeapMemory)
137        *poffGuestHeapMemory = 0;
138    if (pcbGuestHeapMemory)
139        *pcbGuestHeapMemory =   VBVA_ADAPTER_INFORMATION_SIZE
140                              - sizeof(HGSMIHOSTFLAGS);
141    if (poffHostFlags)
142        *poffHostFlags =   VBVA_ADAPTER_INFORMATION_SIZE
143                         - sizeof(HGSMIHOSTFLAGS);
144}
145
146/**
147 * Query the host for an HGSMI configuration parameter via an HGSMI command.
148 * @returns iprt status value
149 * @param  pCtx      the context containing the heap used
150 * @param  u32Index  the index of the parameter to query,
151 *                   @see VBVACONF32::u32Index
152 * @param  pulValue  where to store the value of the parameter on success
153 */
154DECLHIDDEN(int) VBoxQueryConfHGSMI(PHGSMIGUESTCOMMANDCONTEXT pCtx, uint32_t u32Index, uint32_t *pulValue)
155{
156    VBVACONF32 *p;
157
158    /* Allocate the IO buffer. */
159    p = (VBVACONF32 *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_VBVA,
160                                           VBVA_QUERY_CONF32);
161    if (!p)
162        return VERR_NO_MEMORY;
163
164    /* Prepare data to be sent to the host. */
165    p->u32Index = u32Index;
166    p->u32Value = UINT32_MAX;
167    /* No need to check that the buffer is valid as we have just allocated it. */
168    VBoxHGSMIBufferSubmit(pCtx, p);
169    *pulValue = p->u32Value;
170    /* Free the IO buffer. */
171    VBoxHGSMIBufferFree(pCtx, p);
172    return VINF_SUCCESS;
173}
174
175/**
176 * Pass the host a new mouse pointer shape via an HGSMI command.
177 *
178 * @returns  success or failure
179 * @param  pCtx      the context containing the heap to be used
180 * @param  fFlags    cursor flags, @see VMMDevReqMousePointer::fFlags
181 * @param  cHotX     horizontal position of the hot spot
182 * @param  cHotY     vertical position of the hot spot
183 * @param  cWidth    width in pixels of the cursor
184 * @param  cHeight   height in pixels of the cursor
185 * @param  pPixels   pixel data, @see VMMDevReqMousePointer for the format
186 * @param  cbLength  size in bytes of the pixel data
187 */
188DECLHIDDEN(int)  VBoxHGSMIUpdatePointerShape(PHGSMIGUESTCOMMANDCONTEXT pCtx, uint32_t fFlags,
189                                             uint32_t cHotX, uint32_t cHotY, uint32_t cWidth, uint32_t cHeight,
190                                             uint8_t *pPixels, uint32_t cbLength)
191{
192    VBVAMOUSEPOINTERSHAPE *p;
193    uint32_t cbPixels = 0;
194    int rc;
195
196    if (fFlags & VBOX_MOUSE_POINTER_SHAPE)
197    {
198        /*
199         * Size of the pointer data:
200         * sizeof (AND mask) + sizeof (XOR_MASK)
201         */
202        cbPixels = ((((cWidth + 7) / 8) * cHeight + 3) & ~3)
203                 + cWidth * 4 * cHeight;
204        if (cbPixels > cbLength)
205            return VERR_INVALID_PARAMETER;
206        /*
207         * If shape is supplied, then always create the pointer visible.
208         * See comments in 'vboxUpdatePointerShape'
209         */
210        fFlags |= VBOX_MOUSE_POINTER_VISIBLE;
211    }
212    /* Allocate the IO buffer. */
213    p = (VBVAMOUSEPOINTERSHAPE *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p) + cbPixels, HGSMI_CH_VBVA,
214                                                      VBVA_MOUSE_POINTER_SHAPE);
215    if (!p)
216        return VERR_NO_MEMORY;
217    /* Prepare data to be sent to the host. */
218    /* Will be updated by the host. */
219    p->i32Result = VINF_SUCCESS;
220    /* We have our custom flags in the field */
221    p->fu32Flags = fFlags;
222    p->u32HotX   = cHotX;
223    p->u32HotY   = cHotY;
224    p->u32Width  = cWidth;
225    p->u32Height = cHeight;
226    if (cbPixels)
227        /* Copy the actual pointer data. */
228        memcpy (p->au8Data, pPixels, cbPixels);
229    /* No need to check that the buffer is valid as we have just allocated it. */
230    VBoxHGSMIBufferSubmit(pCtx, p);
231    rc = p->i32Result;
232    /* Free the IO buffer. */
233    VBoxHGSMIBufferFree(pCtx, p);
234    return rc;
235}
236
237
238/**
239 * Report the guest cursor position.  The host may wish to use this information
240 * to re-position its own cursor (though this is currently unlikely).  The
241 * current host cursor position is returned.
242 * @param  pCtx             The context containing the heap used.
243 * @param  fReportPosition  Are we reporting a position?
244 * @param  x                Guest cursor X position.
245 * @param  y                Guest cursor Y position.
246 * @param  pxHost           Host cursor X position is stored here.  Optional.
247 * @param  pyHost           Host cursor Y position is stored here.  Optional.
248 * @returns  iprt status code.
249 * @returns  VERR_NO_MEMORY      HGSMI heap allocation failed.
250 */
251DECLHIDDEN(int) VBoxHGSMICursorPosition(PHGSMIGUESTCOMMANDCONTEXT pCtx, bool fReportPosition,
252                                        uint32_t x, uint32_t y, uint32_t *pxHost, uint32_t *pyHost)
253{
254    VBVACURSORPOSITION *p;
255
256    /* Allocate the IO buffer. */
257    p = (VBVACURSORPOSITION *)VBoxHGSMIBufferAlloc(pCtx, sizeof(*p), HGSMI_CH_VBVA,
258                                                   VBVA_CURSOR_POSITION);
259    if (!p)
260        return VERR_NO_MEMORY;
261    /* Prepare data to be sent to the host. */
262    p->fReportPosition = fReportPosition;
263    p->x = x;
264    p->y = y;
265    /* No need to check that the buffer is valid as we have just allocated it. */
266    VBoxHGSMIBufferSubmit(pCtx, p);
267    if (pxHost)
268        *pxHost = p->x;
269    if (pyHost)
270        *pyHost = p->y;
271    /* Free the IO buffer. */
272    VBoxHGSMIBufferFree(pCtx, p);
273    return VINF_SUCCESS;
274}
275
276
277/**
278 * @todo Mouse pointer position to be read from VMMDev memory, address of the
279 * memory region can be queried from VMMDev via an IOCTL. This VMMDev memory
280 * region will contain host information which is needed by the guest.
281 *
282 * Reading will not cause a switch to the host.
283 *
284 * Have to take into account:
285 *  * synchronization: host must write to the memory only from EMT,
286 *    large structures must be read under flag, which tells the host
287 *    that the guest is currently reading the memory (OWNER flag?).
288 *  * guest writes: may be allocate a page for the host info and make
289 *    the page readonly for the guest.
290 *  * the information should be available only for additions drivers.
291 *  * VMMDev additions driver will inform the host which version of the info
292 *    it expects, host must support all versions.
293 */
294