VirtualBox

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

Last change on this file was 107565, checked in by vboxsync, 5 days ago

Additions: Linux: vboxvideo: Fix issue when DRM device open() was failing on 6.12.x kernels, bugref:10782.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.7 KB
Line 
1/* $Id: vbox_drv.c 107565 2025-01-09 09:10:59Z 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 <airlied@redhat.com>
32 * Michael Thayer <michael.thayer@oracle.com,
33 * Hans de Goede <hdegoede@redhat.com>
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_RANGE(5,14,0, 6,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
47# include <drm/drm_aperture.h>
48#endif
49
50#include "version-generated.h"
51#include "revision-generated.h"
52
53/** Detect whether kernel mode setting is OFF. */
54#if defined(CONFIG_VGA_CONSOLE)
55# 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)
56# define VBOX_VIDEO_NOMODESET() drm_firmware_drivers_only() && vbox_modeset == -1
57# elif RTLNX_VER_MIN(4,7,0)
58# define VBOX_VIDEO_NOMODESET() vgacon_text_force() && vbox_modeset == -1
59# else /* < 4.7.0 */
60# define VBOX_VIDEO_NOMODESET() 0
61# endif /* < 4.7.0 */
62#else /* !CONFIG_VGA_CONSOLE */
63# define VBOX_VIDEO_NOMODESET() 0
64#endif /* !CONFIG_VGA_CONSOLE */
65
66#include <VBox/VBoxLnxModInline.h>
67
68static int vbox_modeset = -1;
69
70MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
71module_param_named(modeset, vbox_modeset, int, 0400);
72
73static struct drm_driver driver;
74
75static const struct pci_device_id pciidlist[] = {
76 { 0x80ee, 0xbeef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
77 { 0, 0, 0},
78};
79MODULE_DEVICE_TABLE(pci, pciidlist);
80
81static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
82{
83#if RTLNX_VER_MIN(4,19,0) || RTLNX_RHEL_MIN(8,3)
84 struct drm_device *dev = NULL;
85 int ret = 0;
86
87# if RTLNX_VER_RANGE(5,14,0, 6,13,0) || RTLNX_RHEL_RANGE(8,6, 8,99)
88# 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)
89 ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &driver);
90# else
91 ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, "vboxvideofb");
92# endif
93 if (ret)
94 {
95 printk("unable to remove conflicting framebuffer devices\n");
96 return ret;
97 }
98# endif /* >= 5.14. */
99
100 dev = drm_dev_alloc(&driver, &pdev->dev);
101 if (IS_ERR(dev)) {
102 ret = PTR_ERR(dev);
103 goto err_drv_alloc;
104 }
105# if RTLNX_VER_MAX(5,14,0) && !RTLNX_RHEL_RANGE(8,6, 8,99)
106 dev->pdev = pdev;
107# endif
108 pci_set_drvdata(pdev, dev);
109
110 ret = vbox_driver_load(dev);
111 if (ret)
112 goto err_vbox_driver_load;
113
114 ret = drm_dev_register(dev, 0);
115 if (ret)
116 goto err_drv_dev_register;
117 return ret;
118
119err_drv_dev_register:
120 vbox_driver_unload(dev);
121err_vbox_driver_load:
122 drm_dev_put(dev);
123err_drv_alloc:
124 return ret;
125#else /* < 4.19.0 || RHEL < 8.3 */
126 return drm_get_pci_dev(pdev, ent, &driver);
127#endif
128}
129
130static void vbox_pci_remove(struct pci_dev *pdev)
131{
132 struct drm_device *dev = pci_get_drvdata(pdev);
133
134#if RTLNX_VER_MAX(4,19,0)
135 drm_put_dev(dev);
136#else
137 drm_dev_unregister(dev);
138 vbox_driver_unload(dev);
139 drm_dev_put(dev);
140#endif
141}
142
143#if RTLNX_VER_MAX(4,9,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
144static void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
145 bool suspend)
146{
147 if (!fb_helper || !fb_helper->fbdev)
148 return;
149
150 console_lock();
151 fb_set_suspend(fb_helper->fbdev, suspend);
152 console_unlock();
153}
154#endif
155
156static int vbox_drm_freeze(struct drm_device *dev)
157{
158 struct vbox_private *vbox = dev->dev_private;
159
160 drm_kms_helper_poll_disable(dev);
161
162 pci_save_state(VBOX_DRM_TO_PCI_DEV(dev));
163
164 drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, true);
165
166 return 0;
167}
168
169static int vbox_drm_thaw(struct drm_device *dev)
170{
171 struct vbox_private *vbox = dev->dev_private;
172
173 drm_mode_config_reset(dev);
174 drm_helper_resume_force_mode(dev);
175 drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, false);
176
177 return 0;
178}
179
180static int vbox_drm_resume(struct drm_device *dev)
181{
182 int ret;
183
184 if (pci_enable_device(VBOX_DRM_TO_PCI_DEV(dev)))
185 return -EIO;
186
187 ret = vbox_drm_thaw(dev);
188 if (ret)
189 return ret;
190
191 drm_kms_helper_poll_enable(dev);
192
193 return 0;
194}
195
196static int vbox_pm_suspend(struct device *dev)
197{
198 struct pci_dev *pdev = to_pci_dev(dev);
199 struct drm_device *ddev = pci_get_drvdata(pdev);
200 int error;
201
202 error = vbox_drm_freeze(ddev);
203 if (error)
204 return error;
205
206 pci_disable_device(pdev);
207 pci_set_power_state(pdev, PCI_D3hot);
208
209 return 0;
210}
211
212static int vbox_pm_resume(struct device *dev)
213{
214 struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
215
216 return vbox_drm_resume(ddev);
217}
218
219static int vbox_pm_freeze(struct device *dev)
220{
221 struct pci_dev *pdev = to_pci_dev(dev);
222 struct drm_device *ddev = pci_get_drvdata(pdev);
223
224 if (!ddev || !ddev->dev_private)
225 return -ENODEV;
226
227 return vbox_drm_freeze(ddev);
228}
229
230static int vbox_pm_thaw(struct device *dev)
231{
232 struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
233
234 return vbox_drm_thaw(ddev);
235}
236
237static int vbox_pm_poweroff(struct device *dev)
238{
239 struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
240
241 return vbox_drm_freeze(ddev);
242}
243
244static const struct dev_pm_ops vbox_pm_ops = {
245 .suspend = vbox_pm_suspend,
246 .resume = vbox_pm_resume,
247 .freeze = vbox_pm_freeze,
248 .thaw = vbox_pm_thaw,
249 .poweroff = vbox_pm_poweroff,
250 .restore = vbox_pm_resume,
251};
252
253static struct pci_driver vbox_pci_driver = {
254 .name = DRIVER_NAME,
255 .id_table = pciidlist,
256 .probe = vbox_pci_probe,
257 .remove = vbox_pci_remove,
258 .driver.pm = &vbox_pm_ops,
259};
260
261#if RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
262/* This works around a bug in X servers prior to 1.18.4, which sometimes
263 * submit more dirty rectangles than the kernel is willing to handle and
264 * then disable dirty rectangle handling altogether when they see the
265 * EINVAL error. I do not want the code to hang around forever, which is
266 * why I am limiting it to certain kernel versions. We can increase the
267 * limit if some distributions uses old X servers with new kernels. */
268long vbox_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
269{
270 long rc = drm_ioctl(filp, cmd, arg);
271
272 if (cmd == DRM_IOCTL_MODE_DIRTYFB && rc == -EINVAL)
273 return -EOVERFLOW;
274
275 return rc;
276}
277#endif /* RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4) */
278
279static const struct file_operations vbox_fops = {
280 .owner = THIS_MODULE,
281 .open = drm_open,
282 .release = drm_release,
283#if RTLNX_VER_MAX(4,7,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
284 .unlocked_ioctl = vbox_ioctl,
285#else
286 .unlocked_ioctl = drm_ioctl,
287#endif
288 .mmap = vbox_mmap,
289 .poll = drm_poll,
290#if RTLNX_VER_MAX(3,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,0)
291 .fasync = drm_fasync,
292#endif
293#ifdef CONFIG_COMPAT
294 .compat_ioctl = drm_compat_ioctl,
295#endif
296 .read = drm_read,
297#if RTLNX_VER_MIN(6,12,0)
298 .fop_flags = FOP_UNSIGNED_OFFSET,
299#endif
300};
301
302#if RTLNX_VER_MIN(5,9,0) || RTLNX_RHEL_MIN(8,4) || RTLNX_SUSE_MAJ_PREREQ(15,3)
303static void
304#else
305static int
306#endif
307vbox_master_set(struct drm_device *dev,
308 struct drm_file *file_priv, bool from_open)
309{
310 struct vbox_private *vbox = dev->dev_private;
311
312 /*
313 * We do not yet know whether the new owner can handle hotplug, so we
314 * do not advertise dynamic modes on the first query and send a
315 * tentative hotplug notification after that to see if they query again.
316 */
317 vbox->initial_mode_queried = false;
318
319 mutex_lock(&vbox->hw_mutex);
320 /* Start the refresh timer in case the user does not provide dirty
321 * rectangles. */
322 vbox->need_refresh_timer = true;
323 schedule_delayed_work(&vbox->refresh_work, VBOX_REFRESH_PERIOD);
324 mutex_unlock(&vbox->hw_mutex);
325
326#if RTLNX_VER_MAX(5,9,0) && !RTLNX_RHEL_MAJ_PREREQ(8,4) && !RTLNX_SUSE_MAJ_PREREQ(15,3)
327 return 0;
328#endif
329}
330
331#if RTLNX_VER_MAX(4,8,0) && !RTLNX_RHEL_MAJ_PREREQ(7,4)
332static void vbox_master_drop(struct drm_device *dev,
333 struct drm_file *file_priv, bool from_release)
334#else
335static void vbox_master_drop(struct drm_device *dev, struct drm_file *file_priv)
336#endif
337{
338 struct vbox_private *vbox = dev->dev_private;
339
340 /* See vbox_master_set() */
341 vbox->initial_mode_queried = false;
342 vbox_report_caps(vbox);
343
344 mutex_lock(&vbox->hw_mutex);
345 vbox->need_refresh_timer = false;
346 mutex_unlock(&vbox->hw_mutex);
347}
348
349static struct drm_driver driver = {
350#if RTLNX_VER_MAX(5,4,0) && !RTLNX_RHEL_MAJ_PREREQ(8,3) && !RTLNX_SUSE_MAJ_PREREQ(15,3)
351 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ |
352# if RTLNX_VER_MAX(5,1,0) && !RTLNX_RHEL_MAJ_PREREQ(8,1)
353 DRIVER_IRQ_SHARED |
354# endif
355 DRIVER_PRIME,
356#else /* >= 5.4.0 && RHEL >= 8.3 && SLES >= 15-SP3 */
357 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ,
358#endif /* < 5.4.0 */
359
360#if RTLNX_VER_MAX(4,19,0) && !RTLNX_RHEL_MAJ_PREREQ(8,3)
361 /* Legacy hooks, but still supported. */
362 .load = vbox_driver_load,
363 .unload = vbox_driver_unload,
364#endif
365#if RTLNX_VER_MAX(6,12,0)
366 .lastclose = vbox_driver_lastclose,
367#endif
368 .master_set = vbox_master_set,
369 .master_drop = vbox_master_drop,
370#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,2)
371# 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)
372 .set_busid = drm_pci_set_busid,
373# endif
374#endif
375
376 .fops = &vbox_fops,
377#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)
378 .irq_handler = vbox_irq_handler,
379#endif
380 .name = DRIVER_NAME,
381 .desc = DRIVER_DESC,
382 .date = DRIVER_DATE,
383 .major = DRIVER_MAJOR,
384 .minor = DRIVER_MINOR,
385 .patchlevel = DRIVER_PATCHLEVEL,
386
387#if RTLNX_VER_MAX(4,7,0)
388 .gem_free_object = vbox_gem_free_object,
389#endif
390 .dumb_create = vbox_dumb_create,
391 .dumb_map_offset = vbox_dumb_mmap_offset,
392#if RTLNX_VER_MAX(3,12,0) && !RTLNX_RHEL_MAJ_PREREQ(7,3)
393 .dumb_destroy = vbox_dumb_destroy,
394#elif RTLNX_VER_MAX(5,12,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
395 .dumb_destroy = drm_gem_dumb_destroy,
396#endif
397#if RTLNX_VER_MAX(6,6,0) && !RTLNX_RHEL_RANGE(9,4, 9,99)
398 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
399 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
400#endif
401 .gem_prime_import = drm_gem_prime_import,
402 .gem_prime_import_sg_table = vbox_gem_prime_import_sg_table,
403#if RTLNX_VER_MAX(6,6,0) && !RTLNX_RHEL_RANGE(9,4, 9,99) && !RTLNX_SUSE_MAJ_PREREQ(15, 6)
404 .gem_prime_mmap = vbox_gem_prime_mmap,
405#endif
406
407#if RTLNX_VER_MAX(5,11,0) && !RTLNX_RHEL_MAJ_PREREQ(8,5)
408 .dev_priv_size = 0,
409# if RTLNX_VER_MIN(4,7,0)
410 .gem_free_object_unlocked = vbox_gem_free_object,
411# endif
412 .gem_prime_export = drm_gem_prime_export,
413 .gem_prime_pin = vbox_gem_prime_pin,
414 .gem_prime_unpin = vbox_gem_prime_unpin,
415 .gem_prime_get_sg_table = vbox_gem_prime_get_sg_table,
416 .gem_prime_vmap = vbox_gem_prime_vmap,
417 .gem_prime_vunmap = vbox_gem_prime_vunmap,
418#endif
419};
420
421static int __init vbox_init(void)
422{
423 /* Check if modue loading was disabled. */
424 if (!vbox_mod_should_load())
425 return -EINVAL;
426
427 printk("vboxvideo: loading version " VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV) "\n");
428 if (VBOX_VIDEO_NOMODESET())
429 {
430 printk("vboxvideo: kernel is running with *nomodeset* parameter,\n");
431 printk("vboxvideo: please consider either to remove it or load driver\n");
432 printk("vboxvideo: with parameter modeset=1, unloading\n");
433 return -EINVAL;
434 }
435
436 if (vbox_modeset == 0)
437 {
438 printk("vboxvideo: driver loaded with modeset=0 parameter, unloading\n");
439 return -EINVAL;
440 }
441
442#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,3)
443 return pci_register_driver(&vbox_pci_driver);
444#else
445 return drm_pci_init(&driver, &vbox_pci_driver);
446#endif
447}
448
449static void __exit vbox_exit(void)
450{
451#if RTLNX_VER_MIN(3,18,0) || RTLNX_RHEL_MAJ_PREREQ(7,3)
452 pci_unregister_driver(&vbox_pci_driver);
453#else
454 drm_pci_exit(&driver, &vbox_pci_driver);
455#endif
456}
457
458module_init(vbox_init);
459module_exit(vbox_exit);
460
461MODULE_AUTHOR(DRIVER_AUTHOR);
462MODULE_DESCRIPTION(DRIVER_DESC);
463MODULE_LICENSE("GPL and additional rights");
464#ifdef MODULE_VERSION
465MODULE_VERSION(VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV));
466#endif
Note: See TracBrowser for help on using the repository browser.

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