VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/drm/vbox_drv.c@ 109111

Last change on this file since 109111 was 109111, checked in by vboxsync, 13 days ago

Additions: Linux: vboxvideo: Additional fixes for kernel 6.15, bugref:10891.

  • Add missing aperture_remove_conflicting_pci_devices() call to replace simpledrm after initial boot,
  • Do not provide dummy .gem_prime_import_sg_table interface to prevent our self-coocked warning from being triggered.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
1/* $Id: vbox_drv.c 109111 2025-04-29 17:00:16Z vboxsync $ */
2/** @file
3 * VirtualBox Additions Linux kernel video driver
4 */
5
6/*
7 * Copyright (C) 2013-2024 Oracle and/or its affiliates.
8 * This file is based on ast_drv.c
9 * Copyright 2012 Red Hat Inc.
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the
13 * "Software"), to deal in the Software without restriction, including
14 * without limitation the rights to use, copy, modify, merge, publish,
15 * distribute, sub license, and/or sell copies of the Software, and to
16 * permit persons to whom the Software is furnished to do so, subject to
17 * the following conditions:
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * The above copyright notice and this permission notice (including the
28 * next paragraph) shall be included in all copies or substantial portions
29 * of the Software.
30 *
31 * Authors: Dave Airlie <[email protected]>
32 * Michael Thayer <[email protected],
33 * Hans de Goede <[email protected]>
34 */
35#include <linux/module.h>
36#include <linux/console.h>
37#include <linux/vt_kern.h>
38
39#include "vbox_drv.h"
40
41#include <drm/drm_crtc_helper.h>
42#if RTLNX_VER_MIN(5,1,0) || RTLNX_RHEL_MAJ_PREREQ(8,1)
43# include <drm/drm_probe_helper.h>
44#endif
45
46#if RTLNX_VER_MIN(6,13,0) && defined(CONFIG_APERTURE_HELPERS)
47# include <linux/aperture.h>
48#endif
49
50#if RTLNX_VER_RANGE(5,14,0, 6,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
51# include <drm/drm_aperture.h>
52#endif
53
54#include "version-generated.h"
55#include "revision-generated.h"
56
57/** Detect whether kernel mode setting is OFF. */
58#if defined(CONFIG_VGA_CONSOLE)
59# if RTLNX_VER_MIN(5,17,0) || RTLNX_RHEL_RANGE(8,7, 8,99) || RTLNX_RHEL_MIN(9,1) || RTLNX_SUSE_MAJ_PREREQ(15,5)
60# define VBOX_VIDEO_NOMODESET() drm_firmware_drivers_only() && vbox_modeset == -1
61# elif RTLNX_VER_MIN(4,7,0)
62# define VBOX_VIDEO_NOMODESET() vgacon_text_force() && vbox_modeset == -1
63# else /* < 4.7.0 */
64# define VBOX_VIDEO_NOMODESET() 0
65# endif /* < 4.7.0 */
66#else /* !CONFIG_VGA_CONSOLE */
67# define VBOX_VIDEO_NOMODESET() 0
68#endif /* !CONFIG_VGA_CONSOLE */
69
70#include <VBox/VBoxLnxModInline.h>
71
72static int vbox_modeset = -1;
73
74MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
75module_param_named(modeset, vbox_modeset, int, 0400);
76
77static struct drm_driver driver;
78
79static const struct pci_device_id pciidlist[] = {
80 { 0x80ee, 0xbeef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
81 { 0, 0, 0},
82};
83MODULE_DEVICE_TABLE(pci, pciidlist);
84
85static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
86{
87#if RTLNX_VER_MIN(4,19,0) || RTLNX_RHEL_MIN(8,3)
88 struct drm_device *dev = NULL;
89 int ret = 0;
90
91#if RTLNX_VER_MIN(6,0,0)
92 static bool fWarned = false;
93 if (!fWarned)
94 {
95 printk(KERN_ERR "vboxvideo: VM is using legacy graphics controller, "
96 "please consider to configure this guest to use VMSVGA instead\n");
97 fWarned = true;
98 }
99#endif
100
101# if RTLNX_VER_MIN(6,13,0) && defined(CONFIG_APERTURE_HELPERS)
102 ret = aperture_remove_conflicting_pci_devices(pdev, driver.name);
103# elif RTLNX_VER_RANGE(5,14,0, 6,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
104# if RTLNX_VER_MIN(5,15,0) || RTLNX_RHEL_RANGE(8,7, 8,99) || RTLNX_RHEL_MIN(9,1) || RTLNX_SUSE_MAJ_PREREQ(15,4)
105 ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &driver);
106# else
107 ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, "vboxvideofb");
108# endif
109 if (ret)
110 {
111 printk("unable to remove conflicting framebuffer devices\n");
112 return ret;
113 }
114# endif /* >= 5.14. */
115
116 dev = drm_dev_alloc(&driver, &pdev->dev);
117 if (IS_ERR(dev)) {
118 ret = PTR_ERR(dev);
119 goto err_drv_alloc;
120 }
121# if RTLNX_VER_MAX(5,14,0) && !RTLNX_RHEL_RANGE(8,6, 8,99)
122 dev->pdev = pdev;
123# endif
124 pci_set_drvdata(pdev, dev);
125
126 ret = vbox_driver_load(dev);
127 if (ret)
128 goto err_vbox_driver_load;
129
130 ret = drm_dev_register(dev, 0);
131 if (ret)
132 goto err_drv_dev_register;
133 return ret;
134
135err_drv_dev_register:
136 vbox_driver_unload(dev);
137err_vbox_driver_load:
138 drm_dev_put(dev);
139err_drv_alloc:
140 return ret;
141#else /* < 4.19.0 || RHEL < 8.3 */
142 return drm_get_pci_dev(pdev, ent, &driver);
143#endif
144}
145
146static void vbox_pci_remove(struct pci_dev *pdev)
147{
148 struct drm_device *dev = pci_get_drvdata(pdev);
149
150#if RTLNX_VER_MAX(4,19,0)
151 drm_put_dev(dev);
152#else
153 drm_dev_unregister(dev);
154 vbox_driver_unload(dev);
155 drm_dev_put(dev);
156#endif
157}
158
159#if RTLNX_VER_MAX(4,9,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
160static void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
161 bool suspend)
162{
163 if (!fb_helper || !fb_helper->fbdev)
164 return;
165
166 console_lock();
167 fb_set_suspend(fb_helper->fbdev, suspend);
168 console_unlock();
169}
170#endif
171
172static int vbox_drm_freeze(struct drm_device *dev)
173{
174 struct vbox_private *vbox = dev->dev_private;
175
176 drm_kms_helper_poll_disable(dev);
177
178 pci_save_state(VBOX_DRM_TO_PCI_DEV(dev));
179
180 drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, true);
181
182 return 0;
183}
184
185static int vbox_drm_thaw(struct drm_device *dev)
186{
187 struct vbox_private *vbox = dev->dev_private;
188
189 drm_mode_config_reset(dev);
190 drm_helper_resume_force_mode(dev);
191 drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, false);
192
193 return 0;
194}
195
196static int vbox_drm_resume(struct drm_device *dev)
197{
198 int ret;
199
200 if (pci_enable_device(VBOX_DRM_TO_PCI_DEV(dev)))
201 return -EIO;
202
203 ret = vbox_drm_thaw(dev);
204 if (ret)
205 return ret;
206
207 drm_kms_helper_poll_enable(dev);
208
209 return 0;
210}
211
212static int vbox_pm_suspend(struct device *dev)
213{
214 struct pci_dev *pdev = to_pci_dev(dev);
215 struct drm_device *ddev = pci_get_drvdata(pdev);
216 int error;
217
218 error = vbox_drm_freeze(ddev);
219 if (error)
220 return error;
221
222 pci_disable_device(pdev);
223 pci_set_power_state(pdev, PCI_D3hot);
224
225 return 0;
226}
227
228static int vbox_pm_resume(struct device *dev)
229{
230 struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
231
232 return vbox_drm_resume(ddev);
233}
234
235static int vbox_pm_freeze(struct device *dev)
236{
237 struct pci_dev *pdev = to_pci_dev(dev);
238 struct drm_device *ddev = pci_get_drvdata(pdev);
239
240 if (!ddev || !ddev->dev_private)
241 return -ENODEV;
242
243 return vbox_drm_freeze(ddev);
244}
245
246static int vbox_pm_thaw(struct device *dev)
247{
248 struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
249
250 return vbox_drm_thaw(ddev);
251}
252
253static int vbox_pm_poweroff(struct device *dev)
254{
255 struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
256
257 return vbox_drm_freeze(ddev);
258}
259
260static const struct dev_pm_ops vbox_pm_ops = {
261 .suspend = vbox_pm_suspend,
262 .resume = vbox_pm_resume,
263 .freeze = vbox_pm_freeze,
264 .thaw = vbox_pm_thaw,
265 .poweroff = vbox_pm_poweroff,
266 .restore = vbox_pm_resume,
267};
268
269static struct pci_driver vbox_pci_driver = {
270 .name = DRIVER_NAME,
271 .id_table = pciidlist,
272 .probe = vbox_pci_probe,
273 .remove = vbox_pci_remove,
274 .driver.pm = &vbox_pm_ops,
275};
276
277#if RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
278/* This works around a bug in X servers prior to 1.18.4, which sometimes
279 * submit more dirty rectangles than the kernel is willing to handle and
280 * then disable dirty rectangle handling altogether when they see the
281 * EINVAL error. I do not want the code to hang around forever, which is
282 * why I am limiting it to certain kernel versions. We can increase the
283 * limit if some distributions uses old X servers with new kernels. */
284long vbox_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
285{
286 long rc = drm_ioctl(filp, cmd, arg);
287
288 if (cmd == DRM_IOCTL_MODE_DIRTYFB && rc == -EINVAL)
289 return -EOVERFLOW;
290
291 return rc;
292}
293#endif /* RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4) */
294
295static const struct file_operations vbox_fops = {
296 .owner = THIS_MODULE,
297 .open = drm_open,
298 .release = drm_release,
299#if RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
300 .unlocked_ioctl = vbox_ioctl,
301#else
302 .unlocked_ioctl = drm_ioctl,
303#endif
304 .mmap = vbox_mmap,
305 .poll = drm_poll,
306#if RTLNX_VER_MAX(3,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,0)
307 .fasync = drm_fasync,
308#endif
309#ifdef CONFIG_COMPAT
310 .compat_ioctl = drm_compat_ioctl,
311#endif
312 .read = drm_read,
313#if RTLNX_VER_MIN(6,12,0)
314 .fop_flags = FOP_UNSIGNED_OFFSET,
315#endif
316};
317
318#if RTLNX_VER_MIN(5,9,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
319static void
320#else
321static int
322#endif
323vbox_master_set(struct drm_device *dev,
324 struct drm_file *file_priv, bool from_open)
325{
326 struct vbox_private *vbox = dev->dev_private;
327
328 /*
329 * We do not yet know whether the new owner can handle hotplug, so we
330 * do not advertise dynamic modes on the first query and send a
331 * tentative hotplug notification after that to see if they query again.
332 */
333 vbox->initial_mode_queried = false;
334
335 mutex_lock(&vbox->hw_mutex);
336 /* Start the refresh timer in case the user does not provide dirty
337 * rectangles. */
338 vbox->need_refresh_timer = true;
339 schedule_delayed_work(&vbox->refresh_work, VBOX_REFRESH_PERIOD);
340 mutex_unlock(&vbox->hw_mutex);
341
342#if RTLNX_VER_MAX(5,9,0) && !RTLNX_RHEL_MAJ_PREREQ(8,4) && !RTLNX_SUSE_MAJ_PREREQ(15,3)
343 return 0;
344#endif
345}
346
347#if RTLNX_VER_MAX(4,8,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
348static void vbox_master_drop(struct drm_device *dev,
349 struct drm_file *file_priv, bool from_release)
350#else
351static void vbox_master_drop(struct drm_device *dev, struct drm_file *file_priv)
352#endif
353{
354 struct vbox_private *vbox = dev->dev_private;
355
356 /* See vbox_master_set() */
357 vbox->initial_mode_queried = false;
358 vbox_report_caps(vbox);
359
360 mutex_lock(&vbox->hw_mutex);
361 vbox->need_refresh_timer = false;
362 mutex_unlock(&vbox->hw_mutex);
363}
364
365static struct drm_driver driver = {
366#if RTLNX_VER_MAX(5,4,0) && !RTLNX_RHEL_MAJ_PREREQ(8,3) && !RTLNX_SUSE_MAJ_PREREQ(15,3)
367 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ |
368# if RTLNX_VER_MAX(5,1,0) && !RTLNX_RHEL_MAJ_PREREQ(8,1)
369 DRIVER_IRQ_SHARED |
370# endif
371 DRIVER_PRIME,
372#else /* >= 5.4.0 && RHEL >= 8.3 && SLES >= 15-SP3 */
373 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ,
374#endif /* < 5.4.0 */
375
376#if RTLNX_VER_MAX(4,19,0) && !RTLNX_RHEL_MAJ_PREREQ(8,3)
377 /* Legacy hooks, but still supported. */
378 .load = vbox_driver_load,
379 .unload = vbox_driver_unload,
380#endif
381#if RTLNX_VER_MAX(6,12,0) && !RTLNX_RHEL_RANGE(9,7, 9,99)
382 .lastclose = vbox_driver_lastclose,
383#endif
384 .master_set = vbox_master_set,
385 .master_drop = vbox_master_drop,
386#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,2)
387# if RTLNX_VER_MAX(4,14,0) && !RTLNX_RHEL_MAJ_PREREQ(7,5) && !RTLNX_SUSE_MAJ_PREREQ(15,1) && !RTLNX_SUSE_MAJ_PREREQ(12,5)
388 .set_busid = drm_pci_set_busid,
389# endif
390#endif
391
392 .fops = &vbox_fops,
393#if RTLNX_VER_MAX(5,15,0) && !RTLNX_RHEL_RANGE(8,7, 8,99) && !RTLNX_RHEL_MAJ_PREREQ(9,1) && !RTLNX_SUSE_MAJ_PREREQ(15,5)
394 .irq_handler = vbox_irq_handler,
395#endif
396 .name = DRIVER_NAME,
397 .desc = DRIVER_DESC,
398#if RTLNX_VER_MAX(6,14,0)
399 .date = DRIVER_DATE,
400#endif
401#if RTLNX_VER_MIN(6,15,0)
402 .fbdev_probe = vboxfb_create,
403#endif
404 .major = DRIVER_MAJOR,
405 .minor = DRIVER_MINOR,
406 .patchlevel = DRIVER_PATCHLEVEL,
407
408#if RTLNX_VER_MAX(4,7,0)
409 .gem_free_object = vbox_gem_free_object,
410#endif
411 .dumb_create = vbox_dumb_create,
412 .dumb_map_offset = vbox_dumb_mmap_offset,
413#if RTLNX_VER_MAX(3,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
414 .dumb_destroy = vbox_dumb_destroy,
415#elif RTLNX_VER_MAX(5,12,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
416 .dumb_destroy = drm_gem_dumb_destroy,
417#endif
418#if RTLNX_VER_MAX(6,6,0) && !RTLNX_RHEL_RANGE(9,4, 9,99)
419 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
420 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
421#endif
422 .gem_prime_import = drm_gem_prime_import,
423#if RTLNX_VER_MAX(6,15,0)
424 .gem_prime_import_sg_table = vbox_gem_prime_import_sg_table,
425#endif
426#if RTLNX_VER_MAX(6,6,0) && !RTLNX_RHEL_RANGE(9,4, 9,99) && !RTLNX_SUSE_MAJ_PREREQ(15, 6)
427 .gem_prime_mmap = vbox_gem_prime_mmap,
428#endif
429
430#if RTLNX_VER_MAX(5,11,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
431 .dev_priv_size = 0,
432# if RTLNX_VER_MIN(4,7,0)
433 .gem_free_object_unlocked = vbox_gem_free_object,
434# endif
435 .gem_prime_export = drm_gem_prime_export,
436 .gem_prime_pin = vbox_gem_prime_pin,
437 .gem_prime_unpin = vbox_gem_prime_unpin,
438 .gem_prime_get_sg_table = vbox_gem_prime_get_sg_table,
439 .gem_prime_vmap = vbox_gem_prime_vmap,
440 .gem_prime_vunmap = vbox_gem_prime_vunmap,
441#endif
442};
443
444static int __init vbox_init(void)
445{
446 /* Check if modue loading was disabled. */
447 if (!vbox_mod_should_load())
448 return -EINVAL;
449
450 printk("vboxvideo: loading version " VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) "\n");
451 if (VBOX_VIDEO_NOMODESET())
452 {
453 printk("vboxvideo: kernel is running with *nomodeset* parameter,\n");
454 printk("vboxvideo: please consider either to remove it or load driver\n");
455 printk("vboxvideo: with parameter modeset=1, unloading\n");
456 return -EINVAL;
457 }
458
459 if (vbox_modeset == 0)
460 {
461 printk("vboxvideo: driver loaded with modeset=0 parameter, unloading\n");
462 return -EINVAL;
463 }
464
465#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,3)
466 return pci_register_driver(&vbox_pci_driver);
467#else
468 return drm_pci_init(&driver, &vbox_pci_driver);
469#endif
470}
471
472static void __exit vbox_exit(void)
473{
474#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,3)
475 pci_unregister_driver(&vbox_pci_driver);
476#else
477 drm_pci_exit(&driver, &vbox_pci_driver);
478#endif
479}
480
481module_init(vbox_init);
482module_exit(vbox_exit);
483
484MODULE_AUTHOR(DRIVER_AUTHOR);
485MODULE_DESCRIPTION(DRIVER_DESC);
486MODULE_LICENSE("GPL and additional rights");
487#ifdef MODULE_VERSION
488MODULE_VERSION(VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV));
489#endif
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