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 <VBoxVideoGuest.h> 24#include <VBoxVideoVBE.h> 25#include <HGSMIChannels.h> 26 27#ifndef VBOX_GUESTR3XF86MOD 28# include <VBoxVideoIPRT.h> 29#endif 30 31/** 32 * Gets the count of virtual monitors attached to the guest via an HGSMI 33 * command 34 * 35 * @returns the right count on success or 1 on failure. 36 * @param pCtx the context containing the heap to use 37 */ 38DECLHIDDEN(uint32_t) VBoxHGSMIGetMonitorCount(PHGSMIGUESTCOMMANDCONTEXT pCtx) 39{ 40 /* Query the configured number of displays. */ 41 uint32_t cDisplays = 0; 42 VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_MONITOR_COUNT, &cDisplays); 43 // LogFunc(("cDisplays = %d\n", cDisplays)); 44 if (cDisplays == 0 || cDisplays > VBOX_VIDEO_MAX_SCREENS) 45 /* Host reported some bad value. Continue in the 1 screen mode. */ 46 cDisplays = 1; 47 return cDisplays; 48} 49 50 51/** 52 * Returns the size of the video RAM in bytes. 53 * 54 * @returns the size 55 */ 56DECLHIDDEN(uint32_t) VBoxVideoGetVRAMSize(void) 57{ 58 /** @note A 32bit read on this port returns the VRAM size. */ 59 return VBVO_PORT_READ_U32(VBE_DISPI_IOPORT_DATA); 60} 61 62 63/** 64 * Check whether this hardware allows the display width to have non-multiple- 65 * of-eight values. 66 * 67 * @returns true if any width is allowed, false otherwise. 68 */ 69DECLHIDDEN(bool) VBoxVideoAnyWidthAllowed(void) 70{ 71 unsigned DispiId; 72 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_ID); 73 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, VBE_DISPI_ID_ANYX); 74 DispiId = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); 75 return (DispiId == VBE_DISPI_ID_ANYX); 76} 77 78 79/** 80 * Tell the host about how VRAM is divided up between each screen via an HGSMI 81 * command. It is acceptable to specify identical data for each screen if 82 * they share a single framebuffer. 83 * 84 * @returns iprt status code, either VERR_NO_MEMORY or the status returned by 85 * @a pfnFill 86 * @todo What was I thinking of with that callback function? It 87 * would be much simpler to just pass in a structure in normal 88 * memory and copy it. 89 * @param pCtx the context containing the heap to use 90 * @param u32Count the number of screens we are activating 91 * @param pfnFill a callback which initialises the VBVAINFOVIEW structures 92 * for all screens 93 * @param pvData context data for @a pfnFill 94 */ 95DECLHIDDEN(int) VBoxHGSMISendViewInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, 96 uint32_t u32Count, 97 PFNHGSMIFILLVIEWINFO pfnFill, 98 void *pvData) 99{ 100 int rc; 101 /* Issue the screen info command. */ 102 void *p = VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAINFOVIEW) * u32Count, 103 HGSMI_CH_VBVA, VBVA_INFO_VIEW); 104 if (p) 105 { 106 VBVAINFOVIEW *pInfo = (VBVAINFOVIEW *)p; 107 rc = pfnFill(pvData, pInfo, u32Count); 108 if (RT_SUCCESS(rc)) 109 VBoxHGSMIBufferSubmit (pCtx, p); 110 VBoxHGSMIBufferFree(pCtx, p); 111 } 112 else 113 rc = VERR_NO_MEMORY; 114 return rc; 115} 116 117 118/** 119 * Set a video mode using port registers. This must be done for the first 120 * screen before every HGSMI modeset and also works when HGSM is not enabled. 121 * @param cWidth the mode width 122 * @param cHeight the mode height 123 * @param cVirtWidth the mode pitch 124 * @param cBPP the colour depth of the mode 125 * @param fFlags flags for the mode. These will be or-ed with the 126 * default _ENABLED flag, so unless you are restoring 127 * a saved mode or have special requirements you can pass 128 * zero here. 129 * @param cx the horizontal panning offset 130 * @param cy the vertical panning offset 131 */ 132DECLHIDDEN(void) VBoxVideoSetModeRegisters(uint16_t cWidth, uint16_t cHeight, 133 uint16_t cVirtWidth, uint16_t cBPP, 134 uint16_t fFlags, uint16_t cx, 135 uint16_t cy) 136{ 137 /* set the mode characteristics */ 138 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_XRES); 139 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cWidth); 140 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_YRES); 141 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cHeight); 142 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, 143 VBE_DISPI_INDEX_VIRT_WIDTH); 144 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cVirtWidth); 145 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, VBE_DISPI_INDEX_BPP); 146 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cBPP); 147 /* enable the mode */ 148 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, 149 VBE_DISPI_INDEX_ENABLE); 150 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, 151 fFlags | VBE_DISPI_ENABLED); 152 /* Panning registers */ 153 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, 154 VBE_DISPI_INDEX_X_OFFSET); 155 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cx); 156 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, 157 VBE_DISPI_INDEX_Y_OFFSET); 158 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, cy); 159 /** @todo read from the port to see if the mode switch was successful */ 160} 161 162 163/** 164 * Get the video mode for the first screen using the port registers. All 165 * parameters are optional 166 * @returns true if the VBE mode returned is active, false if we are in VGA 167 * mode 168 * @note If anyone else needs additional register values just extend the 169 * function with additional parameters and fix any existing callers. 170 * @param pcWidth where to store the mode width 171 * @param pcHeight where to store the mode height 172 * @param pcVirtWidth where to store the mode pitch 173 * @param pcBPP where to store the colour depth of the mode 174 * @param pfFlags where to store the flags for the mode 175 */ 176DECLHIDDEN(bool) VBoxVideoGetModeRegisters(uint16_t *pcWidth, uint16_t *pcHeight, 177 uint16_t *pcVirtWidth, uint16_t *pcBPP, 178 uint16_t *pfFlags) 179{ 180 uint16_t fFlags; 181 182 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, 183 VBE_DISPI_INDEX_ENABLE); 184 fFlags = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); 185 if (pcWidth) 186 { 187 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, 188 VBE_DISPI_INDEX_XRES); 189 *pcWidth = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); 190 } 191 if (pcHeight) 192 { 193 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, 194 VBE_DISPI_INDEX_YRES); 195 *pcHeight = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); 196 } 197 if (pcVirtWidth) 198 { 199 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, 200 VBE_DISPI_INDEX_VIRT_WIDTH); 201 *pcVirtWidth = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); 202 } 203 if (pcBPP) 204 { 205 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, 206 VBE_DISPI_INDEX_BPP); 207 *pcBPP = VBVO_PORT_READ_U16(VBE_DISPI_IOPORT_DATA); 208 } 209 if (pfFlags) 210 *pfFlags = fFlags; 211 return !!(fFlags & VBE_DISPI_ENABLED); 212} 213 214 215/** 216 * Disable our extended graphics mode and go back to VGA mode. 217 */ 218DECLHIDDEN(void) VBoxVideoDisableVBE(void) 219{ 220 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_INDEX, 221 VBE_DISPI_INDEX_ENABLE); 222 VBVO_PORT_WRITE_U16(VBE_DISPI_IOPORT_DATA, 0); 223} 224 225 226/** 227 * Set a video mode via an HGSMI request. The views must have been 228 * initialised first using @a VBoxHGSMISendViewInfo and if the mode is being 229 * set on the first display then it must be set first using registers. 230 * @param pCtx The context containing the heap to use. 231 * @param cDisplay the screen number 232 * @param cOriginX the horizontal displacement relative to the first screen 233 * @param cOriginY the vertical displacement relative to the first screen 234 * @param offStart the offset of the visible area of the framebuffer 235 * relative to the framebuffer start 236 * @param cbPitch the offset in bytes between the starts of two adjecent 237 * scan lines in video RAM 238 * @param cWidth the mode width 239 * @param cHeight the mode height 240 * @param cBPP the colour depth of the mode 241 * @param fFlags flags 242 */ 243DECLHIDDEN(void) VBoxHGSMIProcessDisplayInfo(PHGSMIGUESTCOMMANDCONTEXT pCtx, 244 uint32_t cDisplay, 245 int32_t cOriginX, 246 int32_t cOriginY, 247 uint32_t offStart, 248 uint32_t cbPitch, 249 uint32_t cWidth, 250 uint32_t cHeight, 251 uint16_t cBPP, 252 uint16_t fFlags) 253{ 254 /* Issue the screen info command. */ 255 void *p = VBoxHGSMIBufferAlloc(pCtx, 256 sizeof (VBVAINFOSCREEN), 257 HGSMI_CH_VBVA, 258 VBVA_INFO_SCREEN); 259 if (!p) 260 { 261 // LogFunc(("HGSMIHeapAlloc failed\n")); 262 } 263 else 264 { 265 VBVAINFOSCREEN *pScreen = (VBVAINFOSCREEN *)p; 266 267 pScreen->u32ViewIndex = cDisplay; 268 pScreen->i32OriginX = cOriginX; 269 pScreen->i32OriginY = cOriginY; 270 pScreen->u32StartOffset = offStart; 271 pScreen->u32LineSize = cbPitch; 272 pScreen->u32Width = cWidth; 273 pScreen->u32Height = cHeight; 274 pScreen->u16BitsPerPixel = cBPP; 275 pScreen->u16Flags = fFlags; 276 277 VBoxHGSMIBufferSubmit(pCtx, p); 278 279 VBoxHGSMIBufferFree(pCtx, p); 280 } 281} 282 283 284/** Report the rectangle relative to which absolute pointer events should be 285 * expressed. This information remains valid until the next VBVA resize event 286 * for any screen, at which time it is reset to the bounding rectangle of all 287 * virtual screens. 288 * @param pCtx The context containing the heap to use. 289 * @param cOriginX Upper left X coordinate relative to the first screen. 290 * @param cOriginY Upper left Y coordinate relative to the first screen. 291 * @param cWidth Rectangle width. 292 * @param cHeight Rectangle height. 293 * @returns iprt status code. 294 * @returns VERR_NO_MEMORY HGSMI heap allocation failed. 295 */ 296DECLHIDDEN(int) VBoxHGSMIUpdateInputMapping(PHGSMIGUESTCOMMANDCONTEXT pCtx, int32_t cOriginX, int32_t cOriginY, 297 uint32_t cWidth, uint32_t cHeight) 298{ 299 int rc = VINF_SUCCESS; 300 VBVAREPORTINPUTMAPPING *p; 301 // Log(("%s: cOriginX=%d, cOriginY=%d, cWidth=%u, cHeight=%u\n", __PRETTY_FUNCTION__, (int)cOriginX, (int)cOriginX, 302 // (unsigned)cWidth, (unsigned)cHeight)); 303 304 /* Allocate the IO buffer. */ 305 p = (VBVAREPORTINPUTMAPPING *)VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAREPORTINPUTMAPPING), HGSMI_CH_VBVA, 306 VBVA_REPORT_INPUT_MAPPING); 307 if (p) 308 { 309 /* Prepare data to be sent to the host. */ 310 p->x = cOriginX; 311 p->y = cOriginY; 312 p->cx = cWidth; 313 p->cy = cHeight; 314 rc = VBoxHGSMIBufferSubmit(pCtx, p); 315 /* Free the IO buffer. */ 316 VBoxHGSMIBufferFree(pCtx, p); 317 } 318 else 319 rc = VERR_NO_MEMORY; 320 // LogFunc(("rc = %d\n", rc)); 321 return rc; 322} 323 324 325/** 326 * Get most recent video mode hints. 327 * @param pCtx the context containing the heap to use 328 * @param cScreens the number of screens to query hints for, starting at 0. 329 * @param paHints array of VBVAMODEHINT structures for receiving the hints. 330 * @returns iprt status code 331 * @returns VERR_NO_MEMORY HGSMI heap allocation failed. 332 * @returns VERR_NOT_SUPPORTED Host does not support this command. 333 */ 334DECLHIDDEN(int) VBoxHGSMIGetModeHints(PHGSMIGUESTCOMMANDCONTEXT pCtx, 335 unsigned cScreens, VBVAMODEHINT *paHints) 336{ 337 int rc; 338 void *p; 339 340 AssertPtr(paHints); 341 if (!paHints) 342 return VERR_INVALID_POINTER; 343 344 p = VBoxHGSMIBufferAlloc(pCtx, sizeof(VBVAQUERYMODEHINTS) 345 + cScreens * sizeof(VBVAMODEHINT), 346 HGSMI_CH_VBVA, VBVA_QUERY_MODE_HINTS); 347 if (!p) 348 { 349 // LogFunc(("HGSMIHeapAlloc failed\n")); 350 return VERR_NO_MEMORY; 351 } 352 else 353 { 354 VBVAQUERYMODEHINTS *pQuery = (VBVAQUERYMODEHINTS *)p; 355 356 pQuery->cHintsQueried = cScreens; 357 pQuery->cbHintStructureGuest = sizeof(VBVAMODEHINT); 358 pQuery->rc = VERR_NOT_SUPPORTED; 359 360 VBoxHGSMIBufferSubmit(pCtx, p); 361 rc = pQuery->rc; 362 if (RT_SUCCESS(rc)) 363 memcpy(paHints, ((uint8_t *)p) + sizeof(VBVAQUERYMODEHINTS), 364 cScreens * sizeof(VBVAMODEHINT)); 365 366 VBoxHGSMIBufferFree(pCtx, p); 367 } 368 return rc; 369} 370 371 372/** 373 * Query the supported flags in VBVAINFOSCREEN::u16Flags. 374 * 375 * @returns The mask of VBVA_SCREEN_F_* flags or 0 if host does not support the request. 376 * @param pCtx the context containing the heap to use 377 */ 378DECLHIDDEN(uint16_t) VBoxHGSMIGetScreenFlags(PHGSMIGUESTCOMMANDCONTEXT pCtx) 379{ 380 uint32_t u32Flags = 0; 381 int rc = VBoxQueryConfHGSMI(pCtx, VBOX_VBVA_CONF32_SCREEN_FLAGS, &u32Flags); 382 // LogFunc(("u32Flags = 0x%x rc %Rrc\n", u32Flags, rc)); 383 if (RT_FAILURE(rc) || u32Flags > UINT16_MAX) 384 u32Flags = 0; 385 return (uint16_t)u32Flags; 386} 387