VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/drm/vbox_main.c@ 59256

Last change on this file since 59256 was 59240, checked in by vboxsync, 9 years ago

bugref:8087: Additions/x11: support non-root X server: add host flags location reporting to the kernel graphics driver in preparation for adding IRQ handling. Among other things, the host uses the flags, which it places at the location reported/requested by the guest in video RAM, to tell the guest what event the IRQ was sent in response to.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.0 KB
Line 
1/* $Id: vbox_main.c 59240 2016-01-01 20:20:34Z vboxsync $ */
2/** @file
3 * VirtualBox Additions Linux kernel video driver
4 */
5
6/*
7 * Copyright (C) 2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 * --------------------------------------------------------------------
17 *
18 * This code is based on
19 * ast_main.c
20 * with the following copyright and permission notice:
21 *
22 * Copyright 2012 Red Hat Inc.
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a
25 * copy of this software and associated documentation files (the
26 * "Software"), to deal in the Software without restriction, including
27 * without limitation the rights to use, copy, modify, merge, publish,
28 * distribute, sub license, and/or sell copies of the Software, and to
29 * permit persons to whom the Software is furnished to do so, subject to
30 * the following conditions:
31 *
32 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
35 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
36 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
37 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
38 * USE OR OTHER DEALINGS IN THE SOFTWARE.
39 *
40 * The above copyright notice and this permission notice (including the
41 * next paragraph) shall be included in all copies or substantial portions
42 * of the Software.
43 *
44 */
45/*
46 * Authors: Dave Airlie <[email protected]>
47 */
48#include "vbox_drv.h"
49
50#include <VBox/VBoxVideoGuest.h>
51#include <VBox/VBoxVideo.h>
52
53#include <drm/drm_fb_helper.h>
54#include <drm/drm_crtc_helper.h>
55
56static void vbox_user_framebuffer_destroy(struct drm_framebuffer *fb)
57{
58 struct vbox_framebuffer *vbox_fb = to_vbox_framebuffer(fb);
59 if (vbox_fb->obj)
60 drm_gem_object_unreference_unlocked(vbox_fb->obj);
61
62 LogFunc(("vboxvideo: %d: vbox_fb=%p, vbox_fb->obj=%p\n", __LINE__,
63 vbox_fb, vbox_fb->obj));
64 drm_framebuffer_cleanup(fb);
65 kfree(fb);
66}
67
68static int vbox_user_framebuffer_create_handle(struct drm_framebuffer *fb,
69 struct drm_file *file,
70 unsigned int *handle)
71{
72 return -EINVAL;
73}
74
75/** Send information about dirty rectangles to VBVA. If necessary we enable
76 * VBVA first, as this is normally disabled after a mode set in case a user
77 * takes over the console that is not aware of VBVA (i.e. the VESA BIOS). */
78void vbox_framebuffer_dirty_rectangles(struct drm_framebuffer *fb,
79 struct drm_clip_rect *pRects,
80 unsigned cRects)
81{
82 struct vbox_private *vbox = fb->dev->dev_private;
83 unsigned i;
84 unsigned long flags;
85
86 LogFunc(("vboxvideo: %d: fb=%p, cRects=%u, vbox=%p\n", __LINE__, fb,
87 cRects, vbox));
88 spin_lock_irqsave(&vbox->dev_lock, flags);
89 for (i = 0; i < cRects; ++i)
90 {
91 struct drm_crtc *crtc;
92 list_for_each_entry(crtc, &fb->dev->mode_config.crtc_list, head)
93 {
94 unsigned iCrtc = to_vbox_crtc(crtc)->crtc_id;
95 struct VBVABUFFER *pVBVA = vbox->paVBVACtx[iCrtc].pVBVA;
96 VBVACMDHDR cmdHdr;
97
98 if (!pVBVA)
99 {
100 pVBVA = (struct VBVABUFFER *) ( ((uint8_t *)vbox->vram)
101 + vbox->vram_size
102 + iCrtc * VBVA_MIN_BUFFER_SIZE);
103 if (!VBoxVBVAEnable(&vbox->paVBVACtx[iCrtc], &vbox->Ctx, pVBVA, iCrtc))
104 AssertReleaseMsgFailed(("VBoxVBVAEnable failed - heap allocation error, very old host or driver error.\n"));
105 /* Assume that if the user knows to send dirty rectangle information
106 * they can also handle hot-plug events. */
107 VBoxHGSMISendCapsInfo(&vbox->Ctx, VBVACAPS_VIDEO_MODE_HINTS | VBVACAPS_DISABLE_CURSOR_INTEGRATION);
108 }
109 if ( CRTC_FB(crtc) != fb
110 || pRects[i].x1 > crtc->x
111 + crtc->hwmode.hdisplay
112 || pRects[i].y1 > crtc->y
113 + crtc->hwmode.vdisplay
114 || pRects[i].x2 < crtc->x
115 || pRects[i].y2 < crtc->y)
116 continue;
117 cmdHdr.x = (int16_t)pRects[i].x1;
118 cmdHdr.y = (int16_t)pRects[i].y1;
119 cmdHdr.w = (uint16_t)pRects[i].x2 - pRects[i].x1;
120 cmdHdr.h = (uint16_t)pRects[i].y2 - pRects[i].y1;
121 if (VBoxVBVABufferBeginUpdate(&vbox->paVBVACtx[iCrtc],
122 &vbox->Ctx))
123 {
124 VBoxVBVAWrite(&vbox->paVBVACtx[iCrtc], &vbox->Ctx, &cmdHdr,
125 sizeof(cmdHdr));
126 VBoxVBVABufferEndUpdate(&vbox->paVBVACtx[iCrtc]);
127 }
128 }
129 }
130 spin_unlock_irqrestore(&vbox->dev_lock, flags);
131 LogFunc(("vboxvideo: %d\n", __LINE__));
132}
133
134static int vbox_user_framebuffer_dirty(struct drm_framebuffer *fb,
135 struct drm_file *file_priv,
136 unsigned flags, unsigned color,
137 struct drm_clip_rect *pRects,
138 unsigned cRects)
139{
140 LogFunc(("vboxvideo: %d, flags=%u\n", __LINE__, flags));
141 vbox_framebuffer_dirty_rectangles(fb, pRects, cRects);
142 return 0;
143}
144
145static const struct drm_framebuffer_funcs vbox_fb_funcs =
146{
147 .destroy = vbox_user_framebuffer_destroy,
148 .create_handle = vbox_user_framebuffer_create_handle,
149 .dirty = vbox_user_framebuffer_dirty,
150};
151
152
153int vbox_framebuffer_init(struct drm_device *dev,
154 struct vbox_framebuffer *vbox_fb,
155 struct DRM_MODE_FB_CMD *mode_cmd,
156 struct drm_gem_object *obj)
157{
158 int ret;
159
160 LogFunc(("vboxvideo: %d: dev=%p, vbox_fb=%p, obj=%p\n", __LINE__, dev,
161 vbox_fb, obj));
162 drm_helper_mode_fill_fb_struct(&vbox_fb->base, mode_cmd);
163 vbox_fb->obj = obj;
164 ret = drm_framebuffer_init(dev, &vbox_fb->base, &vbox_fb_funcs);
165 if (ret)
166 {
167 DRM_ERROR("framebuffer init failed %d\n", ret);
168 LogFunc(("vboxvideo: %d\n", __LINE__));
169 return ret;
170 }
171 LogFunc(("vboxvideo: %d\n", __LINE__));
172 return 0;
173}
174
175static struct drm_framebuffer *
176vbox_user_framebuffer_create(struct drm_device *dev,
177 struct drm_file *filp,
178 struct drm_mode_fb_cmd2 *mode_cmd)
179{
180 struct drm_gem_object *obj;
181 struct vbox_framebuffer *vbox_fb;
182 int ret;
183
184 LogFunc(("vboxvideo: %d\n", __LINE__));
185 obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]);
186 if (obj == NULL)
187 return ERR_PTR(-ENOENT);
188
189 vbox_fb = kzalloc(sizeof(*vbox_fb), GFP_KERNEL);
190 if (!vbox_fb)
191 {
192 drm_gem_object_unreference_unlocked(obj);
193 return ERR_PTR(-ENOMEM);
194 }
195
196 ret = vbox_framebuffer_init(dev, vbox_fb, mode_cmd, obj);
197 if (ret)
198 {
199 drm_gem_object_unreference_unlocked(obj);
200 kfree(vbox_fb);
201 return ERR_PTR(ret);
202 }
203 LogFunc(("vboxvideo: %d\n", __LINE__));
204 return &vbox_fb->base;
205}
206
207static const struct drm_mode_config_funcs vbox_mode_funcs =
208{
209 .fb_create = vbox_user_framebuffer_create,
210};
211
212static void disableVBVA(struct vbox_private *pVBox)
213{
214 unsigned i;
215
216 if (pVBox->paVBVACtx)
217 {
218 for (i = 0; i < pVBox->cCrtcs; ++i)
219 VBoxVBVADisable(&pVBox->paVBVACtx[i], &pVBox->Ctx, i);
220 kfree(pVBox->paVBVACtx);
221 pVBox->paVBVACtx = NULL;
222 }
223}
224
225static int vbox_vbva_init(struct vbox_private *vbox)
226{
227 unsigned i;
228 bool fRC = true;
229 LogFunc(("vboxvideo: %d: vbox=%p, vbox->cCrtcs=%u, vbox->paVBVACtx=%p\n",
230 __LINE__, vbox, (unsigned)vbox->cCrtcs, vbox->paVBVACtx));
231 if (!vbox->paVBVACtx)
232 {
233 vbox->paVBVACtx = kzalloc( sizeof(struct VBVABUFFERCONTEXT)
234 * vbox->cCrtcs,
235 GFP_KERNEL);
236 if (!vbox->paVBVACtx)
237 return -ENOMEM;
238 }
239 /* Take a command buffer for each screen from the end of usable VRAM. */
240 vbox->vram_size -= vbox->cCrtcs * VBVA_MIN_BUFFER_SIZE;
241 for (i = 0; i < vbox->cCrtcs; ++i)
242 VBoxVBVASetupBufferContext(&vbox->paVBVACtx[i],
243 vbox->vram_size + i * VBVA_MIN_BUFFER_SIZE,
244 VBVA_MIN_BUFFER_SIZE);
245 LogFunc(("vboxvideo: %d: vbox->paVBVACtx=%p, vbox->vram_size=%u\n",
246 __LINE__, vbox->paVBVACtx, (unsigned)vbox->vram_size));
247 return 0;
248}
249
250
251/** Allocation function for the HGSMI heap and data. */
252static DECLCALLBACK(void *) hgsmiEnvAlloc(void *pvEnv, HGSMISIZE cb)
253{
254 NOREF(pvEnv);
255 return kmalloc(cb, GFP_KERNEL);
256}
257
258
259/** Free function for the HGSMI heap and data. */
260static DECLCALLBACK(void) hgsmiEnvFree(void *pvEnv, void *pv)
261{
262 NOREF(pvEnv);
263 kfree(pv);
264}
265
266
267/** Pointers to the HGSMI heap and data manipulation functions. */
268static HGSMIENV g_hgsmiEnv =
269{
270 NULL,
271 hgsmiEnvAlloc,
272 hgsmiEnvFree
273};
274
275
276/** Do we support the 4.3 plus mode hint reporting interface? */
277static bool haveHGSMIModeHintAndCursorReportingInterface(struct vbox_private *pVBox)
278{
279 uint32_t fModeHintReporting, fCursorReporting;
280
281 return RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->Ctx, VBOX_VBVA_CONF32_MODE_HINT_REPORTING, &fModeHintReporting))
282 && RT_SUCCESS(VBoxQueryConfHGSMI(&pVBox->Ctx, VBOX_VBVA_CONF32_GUEST_CURSOR_REPORTING, &fCursorReporting))
283 && fModeHintReporting == VINF_SUCCESS
284 && fCursorReporting == VINF_SUCCESS;
285}
286
287
288/** Set up our heaps and data exchange buffers in VRAM before handing the rest
289 * to the memory manager. */
290static int setupAcceleration(struct vbox_private *pVBox)
291{
292 uint32_t offBase, offGuestHeap, cbGuestHeap, offHostFlags;
293 void *pvGuestHeap;
294
295 VBoxHGSMIGetBaseMappingInfo(pVBox->full_vram_size, &offBase, NULL,
296 &offGuestHeap, &cbGuestHeap, &offHostFlags);
297 pvGuestHeap = ((uint8_t *)pVBox->vram) + offBase + offGuestHeap;
298 pVBox->offHostFlags = offBase + offHostFlags;
299 if (RT_FAILURE(VBoxHGSMISetupGuestContext(&pVBox->Ctx, pvGuestHeap,
300 cbGuestHeap,
301 offBase + offGuestHeap,
302 &g_hgsmiEnv)))
303 return -ENOMEM;
304 /* Reduce available VRAM size to reflect the guest heap. */
305 pVBox->vram_size = offBase;
306 /* Linux drm represents monitors as a 32-bit array. */
307 pVBox->cCrtcs = RT_MIN(VBoxHGSMIGetMonitorCount(&pVBox->Ctx), 32);
308 if (!haveHGSMIModeHintAndCursorReportingInterface(pVBox))
309 return -ENOTSUPP;
310 pVBox->paVBVAModeHints = kzalloc(sizeof(VBVAMODEHINT) * pVBox->cCrtcs, GFP_KERNEL);
311 if (!pVBox->paVBVAModeHints)
312 return -ENOMEM;
313 return vbox_vbva_init(pVBox);
314}
315
316
317int vbox_driver_load(struct drm_device *dev, unsigned long flags)
318{
319 struct vbox_private *vbox;
320 int ret = 0;
321
322 LogFunc(("vboxvideo: %d: dev=%p\n", __LINE__, dev));
323 if (!VBoxHGSMIIsSupported())
324 return -ENODEV;
325 vbox = kzalloc(sizeof(struct vbox_private), GFP_KERNEL);
326 if (!vbox)
327 return -ENOMEM;
328
329 dev->dev_private = vbox;
330 vbox->dev = dev;
331
332 spin_lock_init(&vbox->dev_lock);
333 /* I hope this won't interfere with the memory manager. */
334 vbox->vram = pci_iomap(dev->pdev, 0, 0);
335 if (!vbox->vram)
336 {
337 ret = -EIO;
338 goto out_free;
339 }
340 vbox->full_vram_size = VBoxVideoGetVRAMSize();
341 vbox->fAnyX = VBoxVideoAnyWidthAllowed();
342 DRM_INFO("VRAM %08x\n", vbox->full_vram_size);
343
344 ret = setupAcceleration(vbox);
345 if (ret)
346 goto out_free;
347
348 ret = vbox_mm_init(vbox);
349 if (ret)
350 goto out_free;
351
352 drm_mode_config_init(dev);
353
354 dev->mode_config.funcs = (void *)&vbox_mode_funcs;
355 dev->mode_config.min_width = 64;
356 dev->mode_config.min_height = 64;
357 dev->mode_config.preferred_depth = 24;
358 dev->mode_config.max_width = VBE_DISPI_MAX_XRES;
359 dev->mode_config.max_height = VBE_DISPI_MAX_YRES;
360
361 ret = vbox_mode_init(dev);
362 if (ret)
363 goto out_free;
364
365 ret = vbox_fbdev_init(dev);
366 if (ret)
367 goto out_free;
368 LogFunc(("vboxvideo: %d: vbox=%p, vbox->vram=%p, vbox->full_vram_size=%u\n",
369 __LINE__, vbox, vbox->vram, (unsigned)vbox->full_vram_size));
370 return 0;
371out_free:
372 if (vbox->vram)
373 pci_iounmap(dev->pdev, vbox->vram);
374 kfree(vbox);
375 dev->dev_private = NULL;
376 LogFunc(("vboxvideo: %d: ret=%d\n", __LINE__, ret));
377 return ret;
378}
379
380int vbox_driver_unload(struct drm_device *dev)
381{
382 struct vbox_private *vbox = dev->dev_private;
383
384 LogFunc(("vboxvideo: %d\n", __LINE__));
385 vbox_mode_fini(dev);
386 vbox_fbdev_fini(dev);
387 drm_mode_config_cleanup(dev);
388
389 disableVBVA(vbox);
390 vbox_mm_fini(vbox);
391 pci_iounmap(dev->pdev, vbox->vram);
392 kfree(vbox);
393 LogFunc(("vboxvideo: %d\n", __LINE__));
394 return 0;
395}
396
397
398#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
399static bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper
400 *pHelper)
401{
402 bool rc;
403
404 drm_modeset_lock_all(pHelper->dev);
405 rc = drm_fb_helper_restore_fbdev_mode(pHelper);
406 drm_modeset_unlock_all(pHelper->dev);
407 return rc;
408}
409#endif
410
411
412void vbox_driver_lastclose(struct drm_device *pDev)
413{
414 struct vbox_private *pVBox = pDev->dev_private;
415
416 if (pVBox->fbdev)
417 drm_fb_helper_restore_fbdev_mode_unlocked(&pVBox->fbdev->helper);
418}
419
420
421int vbox_gem_create(struct drm_device *dev,
422 u32 size, bool iskernel,
423 struct drm_gem_object **obj)
424{
425 struct vbox_bo *vboxbo;
426 int ret;
427
428 LogFunc(("vboxvideo: %d: dev=%p, size=%u, iskernel=%u\n", __LINE__,
429 dev, (unsigned)size, (unsigned)iskernel));
430 *obj = NULL;
431
432 size = roundup(size, PAGE_SIZE);
433 if (size == 0)
434 return -EINVAL;
435
436 ret = vbox_bo_create(dev, size, 0, 0, &vboxbo);
437 if (ret)
438 {
439 if (ret != -ERESTARTSYS)
440 DRM_ERROR("failed to allocate GEM object\n");
441 return ret;
442 }
443 *obj = &vboxbo->gem;
444 LogFunc(("vboxvideo: %d: obj=%p\n", __LINE__, obj));
445 return 0;
446}
447
448int vbox_dumb_create(struct drm_file *file,
449 struct drm_device *dev,
450 struct drm_mode_create_dumb *args)
451{
452 int ret;
453 struct drm_gem_object *gobj;
454 u32 handle;
455
456 LogFunc(("vboxvideo: %d: args->width=%u, args->height=%u, args->bpp=%u\n",
457 __LINE__, (unsigned)args->width, (unsigned)args->height,
458 (unsigned)args->bpp));
459 args->pitch = args->width * ((args->bpp + 7) / 8);
460 args->size = args->pitch * args->height;
461
462 ret = vbox_gem_create(dev, args->size, false,
463 &gobj);
464 if (ret)
465 return ret;
466
467 ret = drm_gem_handle_create(file, gobj, &handle);
468 drm_gem_object_unreference_unlocked(gobj);
469 if (ret)
470 return ret;
471
472 args->handle = handle;
473 LogFunc(("vboxvideo: %d: args->handle=%u\n", __LINE__,
474 (unsigned)args->handle));
475 return 0;
476}
477
478int vbox_dumb_destroy(struct drm_file *file,
479 struct drm_device *dev,
480 uint32_t handle)
481{
482 LogFunc(("vboxvideo: %d: dev=%p, handle=%u\n", __LINE__, dev,
483 (unsigned)handle));
484 return drm_gem_handle_delete(file, handle);
485}
486
487void vbox_bo_unref(struct vbox_bo **bo)
488{
489 struct ttm_buffer_object *tbo;
490
491 if ((*bo) == NULL)
492 return;
493
494 LogFunc(("vboxvideo: %d: bo=%p\n", __LINE__, bo));
495 tbo = &((*bo)->bo);
496 ttm_bo_unref(&tbo);
497 if (tbo == NULL)
498 *bo = NULL;
499
500}
501void vbox_gem_free_object(struct drm_gem_object *obj)
502{
503 struct vbox_bo *vbox_bo = gem_to_vbox_bo(obj);
504
505 LogFunc(("vboxvideo: %d: vbox_bo=%p\n", __LINE__, vbox_bo));
506 if (!vbox_bo)
507 return;
508 vbox_bo_unref(&vbox_bo);
509}
510
511
512static inline u64 vbox_bo_mmap_offset(struct vbox_bo *bo)
513{
514#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0)
515 return bo->bo.addr_space_offset;
516#else
517 return drm_vma_node_offset_addr(&bo->bo.vma_node);
518#endif
519}
520int
521vbox_dumb_mmap_offset(struct drm_file *file,
522 struct drm_device *dev,
523 uint32_t handle,
524 uint64_t *offset)
525{
526 struct drm_gem_object *obj;
527 int ret;
528 struct vbox_bo *bo = NULL;
529
530 LogFunc(("vboxvideo: %d: dev=%p, handle=%u\n", __LINE__,
531 dev, (unsigned)handle));
532 mutex_lock(&dev->struct_mutex);
533 obj = drm_gem_object_lookup(dev, file, handle);
534 if (obj == NULL)
535 {
536 ret = -ENOENT;
537 goto out_unlock;
538 }
539
540 bo = gem_to_vbox_bo(obj);
541 *offset = vbox_bo_mmap_offset(bo);
542
543 drm_gem_object_unreference(obj);
544 ret = 0;
545out_unlock:
546 mutex_unlock(&dev->struct_mutex);
547 LogFunc(("vboxvideo: %d: bo=%p, offset=%llu\n", __LINE__,
548 bo, (unsigned long long)*offset));
549 return ret;
550
551}
Note: See TracBrowser for help on using the repository browser.

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