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
410
411
412
413
414
415
416
417
418
419
|
/* $Id: Modesetting.cpp $ */
/** @file
* VirtualBox Video driver, common code - HGSMI initialisation and helper
* functions.
*/
/*
* Copyright (C) 2006-2022 Oracle and/or its affiliates.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <VBoxVideoGuest.h>
#include <VBoxVideoVBE.h>
#include <HGSMIChannels.h>
#ifndef VBOX_GUESTR3XF86MOD
# include <VBoxVideoIPRT.h>
#endif
/**
* Gets the count of virtual monitors attached to the guest via an HGSMI
* command
*
* @returns the right count on success or 1 on failure.
* @param pCtx the context containing the heap to use
*/
DECLHIDDEN(uint32_t) VBoxHGSMIGetMonitorCount(PHGSMIGUESTCOMMANDCONTEXT pCtx)
{
/* Query the configured number of displays. */
uint32_t cDisplays = 0;
VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_MONITOR_COUNT, &cDisplays);
// LogFunc(("cDisplays = %d\n", cDisplays));
if (cDisplays == 0 || cDisplays > VBOX_VIDEO_MAX_SCREENS)
/* Host reported some bad value. Continue in the 1 screen mode. */
cDisplays = 1;
return cDisplays;
}
/**
* Query whether the virtual hardware supports VBE_DISPI_ID_CFG
* and set the interface.
*
* @returns Whether the interface is supported.
*/
DECLHIDDEN(bool) VBoxVGACfgAvailable(void)
{
uint16_t DispiId;
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_CFG);
DispiId = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
return (DispiId == VBE_DISPI_ID_CFG);
}
/**
* Query a configuration value from the virtual hardware which supports VBE_DISPI_ID_CFG.
* I.e. use this function only if VBoxVGACfgAvailable returns true.
*
* @returns Whether the value is supported.
* @param u16Id Identifier of the configuration value (VBE_DISPI_CFG_ID_*).
* @param pu32Value Where to store value from the host.
* @param u32DefValue What to assign to *pu32Value if the value is not supported.
*/
DECLHIDDEN(bool) VBoxVGACfgQuery(uint16_t u16Id, uint32_t *pu32Value, uint32_t u32DefValue)
{
uint32_t u32;
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_CFG);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_CFG_MASK_SUPPORT | u16Id);
u32 = VBVO_PORT_READ_U32(VBE_DISPI_IOPORT_DATA);
if (u32)
{
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, u16Id);
*pu32Value = VBVO_PORT_READ_U32(VBE_DISPI_IOPORT_DATA);
return true;
}
*pu32Value = u32DefValue;
return false;
}
/**
* Returns the size of the video RAM in bytes.
*
* @returns the size
*/
DECLHIDDEN(uint32_t) VBoxVideoGetVRAMSize(void)
{
/** @note A 32bit read on this port returns the VRAM size if interface is older than VBE_DISPI_ID_CFG. */
return VBVO_PORT_READ_U32(VBE_DISPI_IOPORT_DATA);
}
/**
* Check whether this hardware allows the display width to have non-multiple-
* of-eight values.
*
* @returns true if any width is allowed, false otherwise.
*/
DECLHIDDEN(bool) VBoxVideoAnyWidthAllowed(void)
{
unsigned DispiId;
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_ANYX);
DispiId = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
return (DispiId == VBE_DISPI_ID_ANYX);
}
/**
* Tell the host about how VRAM is divided up between each screen via an HGSMI
* command. It is acceptable to specifiy identical data for each screen if
* they share a single framebuffer.
*
* @returns iprt status code, either VERR_NO_MEMORY or the status returned by
* @a pfnFill
* @todo What was I thinking of with that callback function? It
* would be much simpler to just pass in a structure in normal
* memory and copy it.
* @param pCtx the context containing the heap to use
* @param u32Count the number of screens we are activating
* @param pfnFill a callback which initialises the VBVAINFOVIEW structures
* for all screens
* @param pvData context data for @a pfnFill
*/
DECLHIDDEN(int) VBoxHGSMISendViewInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
uint32_t u32Count,
PFNHGSMIFILLVIEWINFO pfnFill,
void *pvData)
{
int rc;
/* Issue the screen info command. */
VBVAINFOVIEW RT_UNTRUSTED_VOLATILE_HOST *pInfo =
(VBVAINFOVIEW RT_UNTRUSTED_VOLATILE_HOST *)VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAINFOVIEW) * u32Count,
HGSMI_CH_VBVA, VBVA_INFO_VIEW);
if (pInfo)
{
rc = pfnFill(pvData, (VBVAINFOVIEW *)pInfo /* lazy bird */, u32Count);
if (RT_SUCCESS(rc))
VBoxHGSMIBufferSubmit(pCtx, pInfo);
VBoxHGSMIBufferFree(pCtx, pInfo);
}
else
rc = VERR_NO_MEMORY;
return rc;
}
/**
* Set a video mode using port registers. This must be done for the first
* screen before every HGSMI modeset and also works when HGSM is not enabled.
* @param cWidth the mode width
* @param cHeight the mode height
* @param cVirtWidth the mode pitch
* @param cBPP the colour depth of the mode
* @param fFlags flags for the mode. These will be or-ed with the
* default _ENABLED flag, so unless you are restoring
* a saved mode or have special requirements you can pass
* zero here.
* @param cx the horizontal panning offset
* @param cy the vertical panning offset
*/
DECLHIDDEN(void) VBoxVideoSetModeRegisters(uint16_t cWidth, uint16_t cHeight,
uint16_t cVirtWidth, uint16_t cBPP,
uint16_t fFlags, uint16_t cx,
uint16_t cy)
{
/* set the mode characteristics */
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cWidth);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cHeight);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VIRT_WIDTH);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cVirtWidth);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cBPP);
/* enable the mode */
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, fFlags | VBE_DISPI_ENABLED);
/* Panning registers */
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_X_OFFSET);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cx);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_Y_OFFSET);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cy);
/** @todo read from the port to see if the mode switch was successful */
}
/**
* Get the video mode for the first screen using the port registers. All
* parameters are optional
* @returns true if the VBE mode returned is active, false if we are in VGA
* mode
* @note If anyone else needs additional register values just extend the
* function with additional parameters and fix any existing callers.
* @param pcWidth where to store the mode width
* @param pcHeight where to store the mode height
* @param pcVirtWidth where to store the mode pitch
* @param pcBPP where to store the colour depth of the mode
* @param pfFlags where to store the flags for the mode
*/
DECLHIDDEN(bool) VBoxVideoGetModeRegisters(uint16_t *pcWidth, uint16_t *pcHeight,
uint16_t *pcVirtWidth, uint16_t *pcBPP,
uint16_t *pfFlags)
{
uint16_t fFlags;
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE);
fFlags = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
if (pcWidth)
{
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES);
*pcWidth = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
}
if (pcHeight)
{
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES);
*pcHeight = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
}
if (pcVirtWidth)
{
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_VIRT_WIDTH);
*pcVirtWidth = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
}
if (pcBPP)
{
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP);
*pcBPP = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA);
}
if (pfFlags)
*pfFlags = fFlags;
return RT_BOOL(fFlags & VBE_DISPI_ENABLED);
}
/**
* Disable our extended graphics mode and go back to VGA mode.
*/
DECLHIDDEN(void) VBoxVideoDisableVBE(void)
{
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ENABLE);
VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, 0);
}
/**
* Set a video mode via an HGSMI request. The views must have been
* initialised first using @a VBoxHGSMISendViewInfo and if the mode is being
* set on the first display then it must be set first using registers.
* @param pCtx The context containing the heap to use.
* @param cDisplay the screen number
* @param cOriginX the horizontal displacement relative to the first screen
* @param cOriginY the vertical displacement relative to the first screen
* @param offStart the offset of the visible area of the framebuffer
* relative to the framebuffer start
* @param cbPitch the offset in bytes between the starts of two adjecent
* scan lines in video RAM
* @param cWidth the mode width
* @param cHeight the mode height
* @param cBPP the colour depth of the mode
* @param fFlags flags
*/
DECLHIDDEN(void) VBoxHGSMIProcessDisplayInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx,
uint32_t cDisplay,
int32_t cOriginX,
int32_t cOriginY,
uint32_t offStart,
uint32_t cbPitch,
uint32_t cWidth,
uint32_t cHeight,
uint16_t cBPP,
uint16_t fFlags)
{
/* Issue the screen info command. */
VBVAINFOSCREEN RT_UNTRUSTED_VOLATILE_HOST *pScreen =
(VBVAINFOSCREEN RT_UNTRUSTED_VOLATILE_HOST *)VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAINFOSCREEN),
HGSMI_CH_VBVA, VBVA_INFO_SCREEN);
if (pScreen != NULL)
{
pScreen->u32ViewIndex = cDisplay;
pScreen->i32OriginX = cOriginX;
pScreen->i32OriginY = cOriginY;
pScreen->u32StartOffset = offStart;
pScreen->u32LineSize = cbPitch;
pScreen->u32Width = cWidth;
pScreen->u32Height = cHeight;
pScreen->u16BitsPerPixel = cBPP;
pScreen->u16Flags = fFlags;
VBoxHGSMIBufferSubmit(pCtx, pScreen);
VBoxHGSMIBufferFree(pCtx, pScreen);
}
else
{
// LogFunc(("HGSMIHeapAlloc failed\n"));
}
}
/** Report the rectangle relative to which absolute pointer events should be
* expressed. This information remains valid until the next VBVA resize event
* for any screen, at which time it is reset to the bounding rectangle of all
* virtual screens.
* @param pCtx The context containing the heap to use.
* @param cOriginX Upper left X co-ordinate relative to the first screen.
* @param cOriginY Upper left Y co-ordinate relative to the first screen.
* @param cWidth Rectangle width.
* @param cHeight Rectangle height.
* @returns iprt status code.
* @returns VERR_NO_MEMORY HGSMI heap allocation failed.
*/
DECLHIDDEN(int) VBoxHGSMIUpdateInputMapping(PHGSMIGUESTCOMMANDCONTEXT pCtx, int32_t cOriginX, int32_t cOriginY,
uint32_t cWidth, uint32_t cHeight)
{
int rc;
VBVAREPORTINPUTMAPPING *p;
// Log(("%s: cOriginX=%d, cOriginY=%d, cWidth=%u, cHeight=%u\n", __PRETTY_FUNCTION__, (int)cOriginX, (int)cOriginX,
// (unsigned)cWidth, (unsigned)cHeight));
/* Allocate the IO buffer. */
p = (VBVAREPORTINPUTMAPPING *)VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAREPORTINPUTMAPPING), HGSMI_CH_VBVA,
VBVA_REPORT_INPUT_MAPPING);
if (p)
{
/* Prepare data to be sent to the host. */
p->x = cOriginX;
p->y = cOriginY;
p->cx = cWidth;
p->cy = cHeight;
rc = VBoxHGSMIBufferSubmit(pCtx, p);
/* Free the IO buffer. */
VBoxHGSMIBufferFree(pCtx, p);
}
else
rc = VERR_NO_MEMORY;
// LogFunc(("rc = %d\n", rc));
return rc;
}
/**
* Get most recent video mode hints.
* @param pCtx the context containing the heap to use
* @param cScreens the number of screens to query hints for, starting at 0.
* @param paHints array of VBVAMODEHINT structures for receiving the hints.
* @returns iprt status code
* @returns VERR_NO_MEMORY HGSMI heap allocation failed.
* @returns VERR_NOT_SUPPORTED Host does not support this command.
*/
DECLHIDDEN(int) VBoxHGSMIGetModeHints(PHGSMIGUESTCOMMANDCONTEXT pCtx,
unsigned cScreens, VBVAMODEHINT *paHints)
{
int rc;
VBVAQUERYMODEHINTS RT_UNTRUSTED_VOLATILE_HOST *pQuery;
AssertPtrReturn(paHints, VERR_INVALID_POINTER);
pQuery = (VBVAQUERYMODEHINTS RT_UNTRUSTED_VOLATILE_HOST *)VBoxHGSMIBufferAlloc(pCtx,
sizeof(VBVAQUERYMODEHINTS)
+ cScreens * sizeof(VBVAMODEHINT),
HGSMI_CH_VBVA, VBVA_QUERY_MODE_HINTS);
if (pQuery != NULL)
{
pQuery->cHintsQueried = cScreens;
pQuery->cbHintStructureGuest = sizeof(VBVAMODEHINT);
pQuery->rc = VERR_NOT_SUPPORTED;
VBoxHGSMIBufferSubmit(pCtx, pQuery);
rc = pQuery->rc;
if (RT_SUCCESS(rc))
memcpy(paHints, (void *)(pQuery + 1), cScreens * sizeof(VBVAMODEHINT));
VBoxHGSMIBufferFree(pCtx, pQuery);
}
else
{
// LogFunc(("HGSMIHeapAlloc failed\n"));
rc = VERR_NO_MEMORY;
}
return rc;
}
/**
* Query the supported flags in VBVAINFOSCREEN::u16Flags.
*
* @returns The mask of VBVA_SCREEN_F_* flags or 0 if host does not support the request.
* @param pCtx the context containing the heap to use
*/
DECLHIDDEN(uint16_t) VBoxHGSMIGetScreenFlags(PHGSMIGUESTCOMMANDCONTEXT pCtx)
{
uint32_t u32Flags = 0;
int rc = VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_SCREEN_FLAGS, &u32Flags);
// LogFunc(("u32Flags = 0x%x rc %Rrc\n", u32Flags, rc));
if (RT_FAILURE(rc) || u32Flags > UINT16_MAX)
u32Flags = 0;
return (uint16_t)u32Flags;
}
|