VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c@ 86607

Last change on this file since 86607 was 85698, checked in by vboxsync, 4 years ago

IPRT,lnx-kmods: Use new linux kernel version checking macros. Moved them to separate wrapper header.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.2 KB
Line 
1/* $Id: VBoxPci-linux.c 85698 2020-08-11 17:05:29Z vboxsync $ */
2/** @file
3 * VBoxPci - PCI Driver (Host), Linux Specific Code.
4 */
5
6/*
7 * Copyright (C) 2011-2020 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 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include "the-linux-kernel.h"
32#include "version-generated.h"
33#include "revision-generated.h"
34#include "product-generated.h"
35
36#define LOG_GROUP LOG_GROUP_DEV_PCI_RAW
37#include <VBox/log.h>
38#include <VBox/err.h>
39#include <iprt/process.h>
40#include <iprt/initterm.h>
41#include <iprt/string.h>
42#include <iprt/mem.h>
43
44#include "../VBoxPciInternal.h"
45
46#ifdef VBOX_WITH_IOMMU
47# include <linux/dmar.h>
48# include <linux/intel-iommu.h>
49# include <linux/pci.h>
50# if RTLNX_VER_MAX(3,1,0) && \
51 (RTLNX_VER_MAX(2,6,41) || RTLNX_VER_MIN(3,0,0))
52# include <asm/amd_iommu.h>
53# else
54# include <linux/amd-iommu.h>
55# endif
56# if RTLNX_VER_MAX(3,2,0)
57# define IOMMU_PRESENT() iommu_found()
58# define IOMMU_DOMAIN_ALLOC() iommu_domain_alloc()
59# else
60# define IOMMU_PRESENT() iommu_present(&pci_bus_type)
61# define IOMMU_DOMAIN_ALLOC() iommu_domain_alloc(&pci_bus_type)
62# endif
63#endif /* VBOX_WITH_IOMMU */
64
65
66/*********************************************************************************************************************************
67* Internal Functions *
68*********************************************************************************************************************************/
69static int __init VBoxPciLinuxInit(void);
70static void __exit VBoxPciLinuxUnload(void);
71
72
73/*********************************************************************************************************************************
74* Global Variables *
75*********************************************************************************************************************************/
76static VBOXRAWPCIGLOBALS g_VBoxPciGlobals;
77
78module_init(VBoxPciLinuxInit);
79module_exit(VBoxPciLinuxUnload);
80
81MODULE_AUTHOR(VBOX_VENDOR);
82MODULE_DESCRIPTION(VBOX_PRODUCT " PCI access Driver");
83MODULE_LICENSE("GPL");
84#ifdef MODULE_VERSION
85MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV));
86#endif
87
88
89#if RTLNX_VER_MIN(2,6,20)
90# define PCI_DEV_GET(v,d,p) pci_get_device(v,d,p)
91# define PCI_DEV_PUT(x) pci_dev_put(x)
92#if RTLNX_VER_MIN(4,17,0)
93/* assume the domain number to be zero - exactly the same assumption of
94 * pci_get_bus_and_slot()
95 */
96# define PCI_DEV_GET_SLOT(bus, devfn) pci_get_domain_bus_and_slot(0, bus, devfn)
97#else
98# define PCI_DEV_GET_SLOT(bus, devfn) pci_get_bus_and_slot(bus, devfn)
99#endif
100#else
101# define PCI_DEV_GET(v,d,p) pci_find_device(v,d,p)
102# define PCI_DEV_PUT(x) do { } while (0)
103# define PCI_DEV_GET_SLOT(bus, devfn) pci_find_slot(bus, devfn)
104#endif
105
106/**
107 * Name of module used to attach to the host PCI device, when
108 * PCI device passthrough is used.
109 */
110#define PCI_STUB_MODULE "pci-stub"
111/* For some reasons my kernel names module for find_module() this way,
112 * while device name seems to be above one.
113 */
114#define PCI_STUB_MODULE_NAME "pci_stub"
115
116/**
117 * Our driver name.
118 */
119#define DRIVER_NAME "vboxpci"
120
121/*
122 * Currently we keep the device bound to pci stub driver, so
123 * dev_printk() &co would report that instead of our name. They also
124 * expect non-NULL dev pointer in older kernels.
125 */
126#define vbpci_printk(level, pdev, format, arg...) \
127 printk(level DRIVER_NAME "%s%s: " format, \
128 pdev ? " " : "", pdev ? pci_name(pdev) : "", \
129 ## arg)
130
131
132/**
133 * Initialize module.
134 *
135 * @returns appropriate status code.
136 */
137static int __init VBoxPciLinuxInit(void)
138{
139 int rc;
140 /*
141 * Initialize IPRT.
142 */
143 rc = RTR0Init(0);
144
145 if (RT_FAILURE(rc))
146 goto error;
147
148
149 LogRel(("VBoxPciLinuxInit\n"));
150
151 RT_ZERO(g_VBoxPciGlobals);
152
153 rc = vboxPciInit(&g_VBoxPciGlobals);
154 if (RT_FAILURE(rc))
155 {
156 LogRel(("cannot do VBoxPciInit: %Rc\n", rc));
157 goto error;
158 }
159
160#if defined(CONFIG_PCI_STUB)
161 /* nothing to do, pci_stub module part of the kernel */
162 g_VBoxPciGlobals.fPciStubModuleAvail = true;
163
164#elif defined(CONFIG_PCI_STUB_MODULE)
165 if (request_module(PCI_STUB_MODULE) == 0)
166 {
167# if RTLNX_VER_MIN(2,6,30)
168 /* find_module() is static before Linux 2.6.30 */
169 mutex_lock(&module_mutex);
170 g_VBoxPciGlobals.pciStubModule = find_module(PCI_STUB_MODULE_NAME);
171 mutex_unlock(&module_mutex);
172 if (g_VBoxPciGlobals.pciStubModule)
173 {
174 if (try_module_get(g_VBoxPciGlobals.pciStubModule))
175 g_VBoxPciGlobals.fPciStubModuleAvail = true;
176 }
177 else
178 printk(KERN_INFO "vboxpci: find_module %s failed\n", PCI_STUB_MODULE);
179# endif
180 }
181 else
182 printk(KERN_INFO "vboxpci: cannot load %s\n", PCI_STUB_MODULE);
183
184#else
185 printk(KERN_INFO "vboxpci: %s module not available, cannot detach PCI devices\n",
186 PCI_STUB_MODULE);
187#endif
188
189#ifdef VBOX_WITH_IOMMU
190 if (IOMMU_PRESENT())
191 printk(KERN_INFO "vboxpci: IOMMU found\n");
192 else
193 printk(KERN_INFO "vboxpci: IOMMU not found (not registered)\n");
194#else
195 printk(KERN_INFO "vboxpci: IOMMU not found (not compiled)\n");
196#endif
197
198 return 0;
199
200 error:
201 return -RTErrConvertToErrno(rc);
202}
203
204/**
205 * Unload the module.
206 */
207static void __exit VBoxPciLinuxUnload(void)
208{
209 LogRel(("VBoxPciLinuxLinuxUnload\n"));
210
211 /*
212 * Undo the work done during start (in reverse order).
213 */
214 vboxPciShutdown(&g_VBoxPciGlobals);
215
216 RTR0Term();
217
218 if (g_VBoxPciGlobals.pciStubModule)
219 {
220 module_put(g_VBoxPciGlobals.pciStubModule);
221 g_VBoxPciGlobals.pciStubModule = NULL;
222 }
223
224 Log(("VBoxPciLinuxUnload - done\n"));
225}
226
227static int vboxPciLinuxDevRegisterWithIommu(PVBOXRAWPCIINS pIns)
228{
229#ifdef VBOX_WITH_IOMMU
230 int rc = VINF_SUCCESS;
231 struct pci_dev *pPciDev = pIns->pPciDev;
232 PVBOXRAWPCIDRVVM pData = VBOX_DRV_VMDATA(pIns);
233 IPRT_LINUX_SAVE_EFL_AC();
234
235 if (RT_LIKELY(pData))
236 {
237 if (RT_LIKELY(pData->pIommuDomain))
238 {
239 /** @todo KVM checks IOMMU_CAP_CACHE_COHERENCY and sets
240 * flag IOMMU_CACHE later used when mapping physical
241 * addresses, which could improve performance.
242 */
243 int rcLnx = iommu_attach_device(pData->pIommuDomain, &pPciDev->dev);
244 if (!rcLnx)
245 {
246 vbpci_printk(KERN_DEBUG, pPciDev, "attached to IOMMU\n");
247 pIns->fIommuUsed = true;
248 rc = VINF_SUCCESS;
249 }
250 else
251 {
252 vbpci_printk(KERN_DEBUG, pPciDev, "failed to attach to IOMMU, error %d\n", rcLnx);
253 rc = VERR_INTERNAL_ERROR;
254 }
255 }
256 else
257 {
258 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "cannot attach to IOMMU, no domain\n");
259 rc = VERR_NOT_FOUND;
260 }
261 }
262 else
263 {
264 vbpci_printk(KERN_DEBUG, pPciDev, "cannot attach to IOMMU, no VM data\n");
265 rc = VERR_INVALID_PARAMETER;
266 }
267
268 IPRT_LINUX_RESTORE_EFL_AC();
269 return rc;
270#else
271 return VERR_NOT_SUPPORTED;
272#endif
273}
274
275static int vboxPciLinuxDevUnregisterWithIommu(PVBOXRAWPCIINS pIns)
276{
277#ifdef VBOX_WITH_IOMMU
278 int rc = VINF_SUCCESS;
279 struct pci_dev *pPciDev = pIns->pPciDev;
280 PVBOXRAWPCIDRVVM pData = VBOX_DRV_VMDATA(pIns);
281 IPRT_LINUX_SAVE_EFL_AC();
282
283 if (RT_LIKELY(pData))
284 {
285 if (RT_LIKELY(pData->pIommuDomain))
286 {
287 if (pIns->fIommuUsed)
288 {
289 iommu_detach_device(pData->pIommuDomain, &pIns->pPciDev->dev);
290 vbpci_printk(KERN_DEBUG, pPciDev, "detached from IOMMU\n");
291 pIns->fIommuUsed = false;
292 }
293 }
294 else
295 {
296 vbpci_printk(KERN_DEBUG, pPciDev,
297 "cannot detach from IOMMU, no domain\n");
298 rc = VERR_NOT_FOUND;
299 }
300 }
301 else
302 {
303 vbpci_printk(KERN_DEBUG, pPciDev,
304 "cannot detach from IOMMU, no VM data\n");
305 rc = VERR_INVALID_PARAMETER;
306 }
307
308 IPRT_LINUX_RESTORE_EFL_AC();
309 return rc;
310#else
311 return VERR_NOT_SUPPORTED;
312#endif
313}
314
315static int vboxPciLinuxDevReset(PVBOXRAWPCIINS pIns)
316{
317 int rc = VINF_SUCCESS;
318 IPRT_LINUX_SAVE_EFL_AC();
319
320 if (RT_LIKELY(pIns->pPciDev))
321 {
322#if RTLNX_VER_MIN(2,6,28)
323 if (pci_reset_function(pIns->pPciDev))
324 {
325 vbpci_printk(KERN_DEBUG, pIns->pPciDev,
326 "pci_reset_function() failed\n");
327 rc = VERR_INTERNAL_ERROR;
328 }
329#else
330 rc = VERR_NOT_SUPPORTED;
331#endif
332 }
333 else
334 rc = VERR_INVALID_PARAMETER;
335
336 IPRT_LINUX_RESTORE_EFL_AC();
337 return rc;
338}
339
340static struct file* vboxPciFileOpen(const char* path, int flags)
341{
342 struct file* filp = NULL;
343 int err = 0;
344
345 filp = filp_open(path, flags, 0);
346
347 if (IS_ERR(filp))
348 {
349 err = PTR_ERR(filp);
350 printk(KERN_DEBUG "vboxPciFileOpen: error %d\n", err);
351 return NULL;
352 }
353
354 if (!filp->f_op || !filp->f_op->write)
355 {
356 printk(KERN_DEBUG "Not writable FS\n");
357 filp_close(filp, NULL);
358 return NULL;
359 }
360
361 return filp;
362}
363
364static void vboxPciFileClose(struct file* file)
365{
366 filp_close(file, NULL);
367}
368
369static int vboxPciFileWrite(struct file* file, unsigned long long offset, unsigned char* data, unsigned int size)
370{
371 int ret;
372 mm_segment_t fs_save;
373
374 fs_save = get_fs();
375 set_fs(KERNEL_DS);
376#if RTLNX_VER_MIN(4,14,0)
377 ret = kernel_write(file, data, size, &offset);
378#else
379 ret = vfs_write(file, data, size, &offset);
380#endif
381 set_fs(fs_save);
382 if (ret < 0)
383 printk(KERN_DEBUG "vboxPciFileWrite: error %d\n", ret);
384
385 return ret;
386}
387
388static int vboxPciLinuxDevDetachHostDriver(PVBOXRAWPCIINS pIns)
389{
390 struct pci_dev *pPciDev = NULL;
391 uint8_t uBus = (pIns->HostPciAddress) >> 8;
392 uint8_t uDevFn = (pIns->HostPciAddress) & 0xff;
393 const char* currentDriver;
394 uint16_t uVendor, uDevice;
395 bool fDetach = 0;
396
397 if (!g_VBoxPciGlobals.fPciStubModuleAvail)
398 {
399 printk(KERN_INFO "vboxpci: stub module %s not detected: cannot detach\n",
400 PCI_STUB_MODULE);
401 return VERR_ACCESS_DENIED;
402 }
403
404 pPciDev = PCI_DEV_GET_SLOT(uBus, uDevFn);
405
406 if (!pPciDev)
407 {
408 printk(KERN_INFO "vboxpci: device at %02x:%02x.%d not found\n",
409 uBus, uDevFn>>3, uDevFn&7);
410 return VERR_NOT_FOUND;
411 }
412
413 uVendor = pPciDev->vendor;
414 uDevice = pPciDev->device;
415
416 currentDriver = pPciDev->driver ? pPciDev->driver->name : NULL;
417
418 printk(KERN_DEBUG "vboxpci: detected device: %04x:%04x at %02x:%02x.%d, driver %s\n",
419 uVendor, uDevice, uBus, uDevFn>>3, uDevFn&7,
420 currentDriver ? currentDriver : "<none>");
421
422 fDetach = (currentDriver == NULL || (strcmp(currentDriver, PCI_STUB_MODULE) != 0));
423
424 /* Init previous driver data. */
425 pIns->szPrevDriver[0] = '\0';
426
427 if (fDetach && currentDriver)
428 {
429 /* Dangerous: if device name for some reasons contains slashes - arbitrary file could be written to. */
430 if (strchr(currentDriver, '/') != 0)
431 {
432 printk(KERN_DEBUG "vboxpci: ERROR: %s contains invalid symbols\n", currentDriver);
433 return VERR_ACCESS_DENIED;
434 }
435 /** @todo RTStrCopy not exported. */
436 strncpy(pIns->szPrevDriver, currentDriver, sizeof(pIns->szPrevDriver) - 1);
437 pIns->szPrevDriver[sizeof(pIns->szPrevDriver) - 1] = '\0';
438 }
439
440 PCI_DEV_PUT(pPciDev);
441 pPciDev = NULL;
442
443 if (fDetach)
444 {
445 char* szCmdBuf;
446 char* szFileBuf;
447 struct file* pFile;
448 int iCmdLen;
449 const int cMaxBuf = 128;
450#if RTLNX_VER_MIN(2,6,29)
451 const struct cred *pOldCreds;
452 struct cred *pNewCreds;
453#endif
454
455 /*
456 * Now perform kernel analog of:
457 *
458 * echo -n "10de 040a" > /sys/bus/pci/drivers/pci-stub/new_id
459 * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/nvidia/unbind
460 * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/pci-stub/bind
461 *
462 * We do this way, as this interface is presumingly more stable than
463 * in-kernel ones.
464 */
465 szCmdBuf = kmalloc(cMaxBuf, GFP_KERNEL);
466 szFileBuf = kmalloc(cMaxBuf, GFP_KERNEL);
467 if (!szCmdBuf || !szFileBuf)
468 goto done;
469
470 /* Somewhat ugly hack - override current credentials */
471#if RTLNX_VER_MIN(2,6,29)
472 pNewCreds = prepare_creds();
473 if (!pNewCreds)
474 goto done;
475
476# if RTLNX_VER_MIN(3,5,0)
477 pNewCreds->fsuid = GLOBAL_ROOT_UID;
478# else
479 pNewCreds->fsuid = 0;
480# endif
481 pOldCreds = override_creds(pNewCreds);
482#endif
483
484 RTStrPrintf(szFileBuf, cMaxBuf,
485 "/sys/bus/pci/drivers/%s/new_id",
486 PCI_STUB_MODULE);
487 pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
488 if (pFile)
489 {
490 iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf,
491 "%04x %04x",
492 uVendor, uDevice);
493 /* Don't write trailing \0 */
494 vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
495 vboxPciFileClose(pFile);
496 }
497 else
498 printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
499
500 iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf,
501 "0000:%02x:%02x.%d",
502 uBus, uDevFn>>3, uDevFn&7);
503
504 /* Unbind if bound to smth */
505 if (pIns->szPrevDriver[0])
506 {
507 RTStrPrintf(szFileBuf, cMaxBuf,
508 "/sys/bus/pci/drivers/%s/unbind",
509 pIns->szPrevDriver);
510 pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
511 if (pFile)
512 {
513
514 /* Don't write trailing \0 */
515 vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
516 vboxPciFileClose(pFile);
517 }
518 else
519 printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
520 }
521
522 RTStrPrintf(szFileBuf, cMaxBuf,
523 "/sys/bus/pci/drivers/%s/bind",
524 PCI_STUB_MODULE);
525 pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
526 if (pFile)
527 {
528 /* Don't write trailing \0 */
529 vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
530 vboxPciFileClose(pFile);
531 }
532 else
533 printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
534
535#if RTLNX_VER_MIN(2,6,29)
536 revert_creds(pOldCreds);
537 put_cred(pNewCreds);
538#endif
539
540 done:
541 kfree(szCmdBuf);
542 kfree(szFileBuf);
543 }
544
545 return 0;
546}
547
548static int vboxPciLinuxDevReattachHostDriver(PVBOXRAWPCIINS pIns)
549{
550 struct pci_dev *pPciDev = pIns->pPciDev;
551
552 if (!pPciDev)
553 return VINF_SUCCESS;
554
555 if (pIns->szPrevDriver[0])
556 {
557 char* szCmdBuf;
558 char* szFileBuf;
559 struct file* pFile;
560 int iCmdLen;
561 const int cMaxBuf = 128;
562#if RTLNX_VER_MIN(2,6,29)
563 const struct cred *pOldCreds;
564 struct cred *pNewCreds;
565#endif
566 uint8_t uBus = (pIns->HostPciAddress) >> 8;
567 uint8_t uDevFn = (pIns->HostPciAddress) & 0xff;
568
569 vbpci_printk(KERN_DEBUG, pPciDev,
570 "reattaching old host driver %s\n", pIns->szPrevDriver);
571 /*
572 * Now perform kernel analog of:
573 *
574 * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/pci-stub/unbind
575 * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/nvidia/bind
576 */
577 szCmdBuf = kmalloc(cMaxBuf, GFP_KERNEL);
578 szFileBuf = kmalloc(cMaxBuf, GFP_KERNEL);
579
580 if (!szCmdBuf || !szFileBuf)
581 goto done;
582
583 iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf,
584 "0000:%02x:%02x.%d",
585 uBus, uDevFn>>3, uDevFn&7);
586
587 /* Somewhat ugly hack - override current credentials */
588#if RTLNX_VER_MIN(2,6,29)
589 pNewCreds = prepare_creds();
590 if (!pNewCreds)
591 goto done;
592
593# if RTLNX_VER_MIN(3,5,0)
594 pNewCreds->fsuid = GLOBAL_ROOT_UID;
595# else
596 pNewCreds->fsuid = 0;
597# endif
598 pOldCreds = override_creds(pNewCreds);
599#endif
600 RTStrPrintf(szFileBuf, cMaxBuf,
601 "/sys/bus/pci/drivers/%s/unbind",
602 PCI_STUB_MODULE);
603 pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
604 if (pFile)
605 {
606
607 /* Don't write trailing \0 */
608 vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
609 vboxPciFileClose(pFile);
610 }
611 else
612 printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
613
614 RTStrPrintf(szFileBuf, cMaxBuf,
615 "/sys/bus/pci/drivers/%s/bind",
616 pIns->szPrevDriver);
617 pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
618 if (pFile)
619 {
620
621 /* Don't write trailing \0 */
622 vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
623 vboxPciFileClose(pFile);
624 pIns->szPrevDriver[0] = '\0';
625 }
626 else
627 printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
628
629#if RTLNX_VER_MIN(2,6,29)
630 revert_creds(pOldCreds);
631 put_cred(pNewCreds);
632#endif
633
634 done:
635 kfree(szCmdBuf);
636 kfree(szFileBuf);
637 }
638
639 return VINF_SUCCESS;
640}
641
642DECLHIDDEN(int) vboxPciOsDevInit(PVBOXRAWPCIINS pIns, uint32_t fFlags)
643{
644 struct pci_dev *pPciDev = NULL;
645 int rc = VINF_SUCCESS;
646 IPRT_LINUX_SAVE_EFL_AC();
647
648 if (fFlags & PCIRAWDRIVERRFLAG_DETACH_HOST_DRIVER)
649 {
650 rc = vboxPciLinuxDevDetachHostDriver(pIns);
651 if (RT_FAILURE(rc))
652 {
653 printk(KERN_DEBUG "Cannot detach host driver for device %x: %d\n",
654 pIns->HostPciAddress, rc);
655 }
656 }
657
658 if (RT_SUCCESS(rc))
659 {
660 pPciDev = PCI_DEV_GET_SLOT((pIns->HostPciAddress) >> 8,
661 (pIns->HostPciAddress) & 0xff);
662
663 if (RT_LIKELY(pPciDev))
664 {
665 int rcLnx = pci_enable_device(pPciDev);
666
667 if (!rcLnx)
668 {
669 pIns->pPciDev = pPciDev;
670 vbpci_printk(KERN_DEBUG, pPciDev, "%s\n", __func__);
671
672#if RTLNX_VER_MIN(2,6,1)
673 if (pci_enable_msi(pPciDev) == 0)
674 pIns->fMsiUsed = true;
675#endif
676
677 /** @todo
678 * pci_enable_msix(pPciDev, entries, nvec)
679 *
680 * In fact, if device uses interrupts, and cannot be forced to use MSI or MSI-X
681 * we have to refuse using it, as we cannot work with shared PCI interrupts (unless we're lucky
682 * to grab unshared PCI interrupt).
683 */
684 }
685 else
686 rc = RTErrConvertFromErrno(RT_ABS(rcLnx));
687 }
688 else
689 rc = VERR_NOT_FOUND;
690 }
691
692 IPRT_LINUX_RESTORE_EFL_AC();
693 return rc;
694}
695
696DECLHIDDEN(int) vboxPciOsDevDeinit(PVBOXRAWPCIINS pIns, uint32_t fFlags)
697{
698 int rc = VINF_SUCCESS;
699 struct pci_dev *pPciDev = pIns->pPciDev;
700 IPRT_LINUX_SAVE_EFL_AC();
701
702 vbpci_printk(KERN_DEBUG, pPciDev, "%s\n", __func__);
703
704 if (RT_LIKELY(pPciDev))
705 {
706 int iRegion;
707 for (iRegion = 0; iRegion < 7; ++iRegion)
708 {
709 if (pIns->aRegionR0Mapping[iRegion])
710 {
711 iounmap(pIns->aRegionR0Mapping[iRegion]);
712 pIns->aRegionR0Mapping[iRegion] = 0;
713 pci_release_region(pPciDev, iRegion);
714 }
715 }
716
717 vboxPciLinuxDevUnregisterWithIommu(pIns);
718
719#if RTLNX_VER_MIN(2,6,1)
720 if (pIns->fMsiUsed)
721 pci_disable_msi(pPciDev);
722#endif
723 // pci_disable_msix(pPciDev);
724 pci_disable_device(pPciDev);
725 vboxPciLinuxDevReattachHostDriver(pIns);
726
727 PCI_DEV_PUT(pPciDev);
728 pIns->pPciDev = NULL;
729 }
730 else
731 rc = VERR_INVALID_PARAMETER;
732
733 IPRT_LINUX_RESTORE_EFL_AC();
734 return rc;
735}
736
737DECLHIDDEN(int) vboxPciOsDevDestroy(PVBOXRAWPCIINS pIns)
738{
739 return VINF_SUCCESS;
740}
741
742DECLHIDDEN(int) vboxPciOsDevGetRegionInfo(PVBOXRAWPCIINS pIns,
743 int32_t iRegion,
744 RTHCPHYS *pRegionStart,
745 uint64_t *pu64RegionSize,
746 bool *pfPresent,
747 uint32_t *pfFlags)
748{
749 int rc = VINF_SUCCESS;
750 struct pci_dev *pPciDev = pIns->pPciDev;
751 IPRT_LINUX_SAVE_EFL_AC();
752
753 if (RT_LIKELY(pPciDev))
754 {
755 int fFlags = pci_resource_flags(pPciDev, iRegion);
756
757 if ( ((fFlags & (IORESOURCE_MEM | IORESOURCE_IO)) == 0)
758 || ((fFlags & IORESOURCE_DISABLED) != 0))
759 {
760 *pfPresent = false;
761 rc = VERR_INVALID_PARAMETER;
762 }
763 else
764 {
765 uint32_t fResFlags = 0;
766 *pfPresent = true;
767
768 if (fFlags & IORESOURCE_MEM)
769 fResFlags |= PCIRAW_ADDRESS_SPACE_MEM;
770
771 if (fFlags & IORESOURCE_IO)
772 fResFlags |= PCIRAW_ADDRESS_SPACE_IO;
773
774#ifdef IORESOURCE_MEM_64
775 if (fFlags & IORESOURCE_MEM_64)
776 fResFlags |= PCIRAW_ADDRESS_SPACE_BAR64;
777#endif
778
779 if (fFlags & IORESOURCE_PREFETCH)
780 fResFlags |= PCIRAW_ADDRESS_SPACE_MEM_PREFETCH;
781
782 *pfFlags = fResFlags;
783 *pRegionStart = pci_resource_start(pPciDev, iRegion);
784 *pu64RegionSize = pci_resource_len (pPciDev, iRegion);
785
786 vbpci_printk(KERN_DEBUG, pPciDev,
787 "region %d: %s %llx+%lld\n",
788 iRegion, (fFlags & IORESOURCE_MEM) ? "mmio" : "pio",
789 *pRegionStart, *pu64RegionSize);
790 }
791 }
792 else
793 {
794 *pfPresent = false;
795 rc = VERR_INVALID_PARAMETER;
796 }
797
798 IPRT_LINUX_RESTORE_EFL_AC();
799 return rc;
800}
801
802DECLHIDDEN(int) vboxPciOsDevMapRegion(PVBOXRAWPCIINS pIns,
803 int32_t iRegion,
804 RTHCPHYS RegionStart,
805 uint64_t u64RegionSize,
806 uint32_t fFlags,
807 RTR0PTR *pRegionBase)
808{
809 int rc = VINF_SUCCESS;
810 struct pci_dev *pPciDev = pIns->pPciDev;
811 IPRT_LINUX_SAVE_EFL_AC();
812
813 if (!pPciDev || iRegion < 0 || iRegion > 0)
814 {
815 if (pPciDev)
816 vbpci_printk(KERN_DEBUG, pPciDev, "invalid region %d\n", iRegion);
817
818 IPRT_LINUX_RESTORE_EFL_AC();
819 return VERR_INVALID_PARAMETER;
820 }
821
822 vbpci_printk(KERN_DEBUG, pPciDev, "reg=%d start=%llx size=%lld\n",
823 iRegion, RegionStart, u64RegionSize);
824
825 if ( (pci_resource_flags(pPciDev, iRegion) & IORESOURCE_IO)
826 || RegionStart != pci_resource_start(pPciDev, iRegion)
827 || u64RegionSize != pci_resource_len(pPciDev, iRegion))
828 {
829 IPRT_LINUX_RESTORE_EFL_AC();
830 return VERR_INVALID_PARAMETER;
831 }
832
833 /*
834 * XXX: Current code never calls unmap. To avoid leaking mappings
835 * only request and map resources once.
836 */
837 if (!pIns->aRegionR0Mapping[iRegion])
838 {
839 int rcLnx;
840 *pRegionBase = pIns->aRegionR0Mapping[iRegion];
841
842 rcLnx = pci_request_region(pPciDev, iRegion, "vboxpci");
843 if (!rcLnx)
844 {
845#if RTLNX_VER_MIN(2,6,25)
846 /*
847 * ioremap() defaults to no caching since the 2.6 kernels.
848 * ioremap_nocache() has been removed finally in 5.6-rc1.
849 */
850 RTR0PTR R0PtrMapping = ioremap(pci_resource_start(pPciDev, iRegion),
851 pci_resource_len(pPciDev, iRegion));
852#else /* KERNEL_VERSION < 2.6.25 */
853 /* For now no caching, try to optimize later. */
854 RTR0PTR R0PtrMapping = ioremap_nocache(pci_resource_start(pPciDev, iRegion),
855 pci_resource_len(pPciDev, iRegion));
856#endif /* KERNEL_VERSION < 2.6.25 */
857 if (R0PtrMapping != NIL_RTR0PTR)
858 pIns->aRegionR0Mapping[iRegion] = R0PtrMapping;
859 else
860 {
861#if RTLNX_VER_MIN(2,6,25)
862 vbpci_printk(KERN_DEBUG, pPciDev, "ioremap() failed\n");
863#else
864 vbpci_printk(KERN_DEBUG, pPciDev, "ioremap_nocache() failed\n");
865#endif
866 pci_release_region(pPciDev, iRegion);
867 rc = VERR_MAP_FAILED;
868 }
869 }
870 else
871 rc = VERR_RESOURCE_BUSY;
872 }
873
874 if (RT_SUCCESS(rc))
875 *pRegionBase = pIns->aRegionR0Mapping[iRegion];
876
877 IPRT_LINUX_RESTORE_EFL_AC();
878 return rc;
879}
880
881DECLHIDDEN(int) vboxPciOsDevUnmapRegion(PVBOXRAWPCIINS pIns,
882 int32_t iRegion,
883 RTHCPHYS RegionStart,
884 uint64_t u64RegionSize,
885 RTR0PTR RegionBase)
886{
887 /* XXX: Current code never calls unmap. */
888 return VERR_NOT_IMPLEMENTED;
889}
890
891DECLHIDDEN(int) vboxPciOsDevPciCfgWrite(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue)
892{
893 struct pci_dev *pPciDev = pIns->pPciDev;
894 int rc = VINF_SUCCESS;
895 IPRT_LINUX_SAVE_EFL_AC();
896
897 if (RT_LIKELY(pPciDev))
898 {
899 switch (pValue->cb)
900 {
901 case 1:
902 pci_write_config_byte(pPciDev, Register, pValue->u.u8);
903 break;
904 case 2:
905 pci_write_config_word(pPciDev, Register, pValue->u.u16);
906 break;
907 case 4:
908 pci_write_config_dword(pPciDev, Register, pValue->u.u32);
909 break;
910 }
911 }
912 else
913 rc = VERR_INVALID_PARAMETER;
914
915 IPRT_LINUX_RESTORE_EFL_AC();
916 return rc;
917}
918
919DECLHIDDEN(int) vboxPciOsDevPciCfgRead(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue)
920{
921 struct pci_dev *pPciDev = pIns->pPciDev;
922 int rc = VINF_SUCCESS;
923
924 if (RT_LIKELY(pPciDev))
925 {
926 IPRT_LINUX_SAVE_EFL_AC();
927
928 switch (pValue->cb)
929 {
930 case 1:
931 pci_read_config_byte(pPciDev, Register, &pValue->u.u8);
932 break;
933 case 2:
934 pci_read_config_word(pPciDev, Register, &pValue->u.u16);
935 break;
936 case 4:
937 pci_read_config_dword(pPciDev, Register, &pValue->u.u32);
938 break;
939 }
940
941 IPRT_LINUX_RESTORE_EFL_AC();
942 }
943 else
944 rc = VERR_INVALID_PARAMETER;
945
946 return rc;
947}
948
949/**
950 * Interrupt service routine.
951 *
952 * @returns In 2.6 we indicate whether we've handled the IRQ or not.
953 *
954 * @param iIrq The IRQ number.
955 * @param pvDevId The device ID, a pointer to PVBOXRAWPCIINS.
956 * @param pRegs Register set. Removed in 2.6.19.
957 */
958#if RTLNX_VER_MIN(2,6,19) && !defined(DOXYGEN_RUNNING)
959static irqreturn_t vboxPciOsIrqHandler(int iIrq, void *pvDevId)
960#else
961static irqreturn_t vboxPciOsIrqHandler(int iIrq, void *pvDevId, struct pt_regs *pRegs)
962#endif
963{
964 PVBOXRAWPCIINS pIns = (PVBOXRAWPCIINS)pvDevId;
965 bool fTaken = true;
966
967 if (pIns && pIns->IrqHandler.pfnIrqHandler)
968 fTaken = pIns->IrqHandler.pfnIrqHandler(pIns->IrqHandler.pIrqContext, iIrq);
969#ifndef VBOX_WITH_SHARED_PCI_INTERRUPTS
970 /* If we don't allow interrupts sharing, we consider all interrupts as non-shared, thus targetted to us. */
971 fTaken = true;
972#endif
973
974 return fTaken;
975}
976
977DECLHIDDEN(int) vboxPciOsDevRegisterIrqHandler(PVBOXRAWPCIINS pIns, PFNRAWPCIISR pfnHandler, void* pIrqContext, int32_t *piHostIrq)
978{
979 int rc;
980 int32_t iIrq = pIns->pPciDev->irq;
981 IPRT_LINUX_SAVE_EFL_AC();
982
983 if (iIrq == 0)
984 {
985 vbpci_printk(KERN_NOTICE, pIns->pPciDev, "no irq assigned\n");
986 IPRT_LINUX_RESTORE_EFL_AC();
987 return VERR_INVALID_PARAMETER;
988 }
989
990 rc = request_irq(iIrq,
991 vboxPciOsIrqHandler,
992#ifdef VBOX_WITH_SHARED_PCI_INTERRUPTS
993 /* Allow interrupts sharing. */
994# if RTLNX_VER_MIN(2,6,20)
995 IRQF_SHARED,
996# else
997 SA_SHIRQ,
998# endif
999
1000#else
1001
1002 /* We don't allow interrupts sharing */
1003 /* XXX overhaul */
1004# if RTLNX_VER_MIN(2,6,20) && RTLNX_VER_MAX(4,1,0)
1005 IRQF_DISABLED, /* keep irqs disabled when calling the action handler */
1006# else
1007 0,
1008# endif
1009#endif
1010 DRIVER_NAME,
1011 pIns);
1012 if (rc)
1013 {
1014 vbpci_printk(KERN_DEBUG, pIns->pPciDev,
1015 "could not request irq %d, error %d\n", iIrq, rc);
1016 IPRT_LINUX_RESTORE_EFL_AC();
1017 return VERR_RESOURCE_BUSY;
1018 }
1019
1020 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "got irq %d\n", iIrq);
1021 *piHostIrq = iIrq;
1022
1023 IPRT_LINUX_RESTORE_EFL_AC();
1024 return VINF_SUCCESS;
1025}
1026
1027DECLHIDDEN(int) vboxPciOsDevUnregisterIrqHandler(PVBOXRAWPCIINS pIns, int32_t iHostIrq)
1028{
1029 IPRT_LINUX_SAVE_EFL_AC();
1030
1031 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "freeing irq %d\n", iHostIrq);
1032 free_irq(iHostIrq, pIns);
1033
1034 IPRT_LINUX_RESTORE_EFL_AC();
1035 return VINF_SUCCESS;
1036}
1037
1038DECLHIDDEN(int) vboxPciOsDevPowerStateChange(PVBOXRAWPCIINS pIns, PCIRAWPOWERSTATE aState)
1039{
1040 int rc;
1041
1042 switch (aState)
1043 {
1044 case PCIRAW_POWER_ON:
1045 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_ON\n");
1046 /* Reset device, just in case. */
1047 vboxPciLinuxDevReset(pIns);
1048 /* register us with IOMMU */
1049 rc = vboxPciLinuxDevRegisterWithIommu(pIns);
1050 break;
1051 case PCIRAW_POWER_RESET:
1052 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_RESET\n");
1053 rc = vboxPciLinuxDevReset(pIns);
1054 break;
1055 case PCIRAW_POWER_OFF:
1056 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_OFF\n");
1057 /* unregister us from IOMMU */
1058 rc = vboxPciLinuxDevUnregisterWithIommu(pIns);
1059 break;
1060 case PCIRAW_POWER_SUSPEND:
1061 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_SUSPEND\n");
1062 rc = VINF_SUCCESS;
1063 /// @todo what do we do here?
1064 break;
1065 case PCIRAW_POWER_RESUME:
1066 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_RESUME\n");
1067 rc = VINF_SUCCESS;
1068 /// @todo what do we do here?
1069 break;
1070 default:
1071 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "unknown power state %u\n", aState);
1072 /* to make compiler happy */
1073 rc = VERR_NOT_SUPPORTED;
1074 break;
1075 }
1076
1077 return rc;
1078}
1079
1080
1081#ifdef VBOX_WITH_IOMMU
1082/** Callback for FNRAWPCICONTIGPHYSMEMINFO. */
1083static DECLCALLBACK(int) vboxPciOsContigMemInfo(PRAWPCIPERVM pVmCtx, RTHCPHYS HostStart, RTGCPHYS GuestStart,
1084 uint64_t cMemSize, PCIRAWMEMINFOACTION Action)
1085{
1086 struct iommu_domain* domain = ((PVBOXRAWPCIDRVVM)(pVmCtx->pDriverData))->pIommuDomain;
1087 int rc = VINF_SUCCESS;
1088 IPRT_LINUX_SAVE_EFL_AC();
1089
1090 switch (Action)
1091 {
1092 case PCIRAW_MEMINFO_MAP:
1093 {
1094 int flags, r;
1095
1096 if (iommu_iova_to_phys(domain, GuestStart))
1097 break;
1098
1099 flags = IOMMU_READ | IOMMU_WRITE;
1100 /** @todo flags |= IOMMU_CACHE; */
1101
1102 r = iommu_map(domain, GuestStart, HostStart, get_order(cMemSize), flags);
1103 if (r)
1104 {
1105 printk(KERN_ERR "vboxPciOsContigMemInfo:"
1106 "iommu failed to map pfn=%llx\n", HostStart);
1107 rc = VERR_GENERAL_FAILURE;
1108 break;
1109 }
1110 rc = VINF_SUCCESS;
1111 break;
1112 }
1113 case PCIRAW_MEMINFO_UNMAP:
1114 {
1115 int order;
1116 order = iommu_unmap(domain, GuestStart, get_order(cMemSize));
1117 NOREF(order);
1118 break;
1119 }
1120
1121 default:
1122 printk(KERN_DEBUG "Unsupported action: %d\n", (int)Action);
1123 rc = VERR_NOT_SUPPORTED;
1124 break;
1125 }
1126
1127 IPRT_LINUX_RESTORE_EFL_AC();
1128 return rc;
1129}
1130#endif
1131
1132DECLHIDDEN(int) vboxPciOsInitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM, PRAWPCIPERVM pVmData)
1133{
1134 int rc = VINF_SUCCESS;
1135
1136#ifdef VBOX_WITH_IOMMU
1137 IPRT_LINUX_SAVE_EFL_AC();
1138
1139 if (IOMMU_PRESENT())
1140 {
1141 pThis->pIommuDomain = IOMMU_DOMAIN_ALLOC();
1142 if (!pThis->pIommuDomain)
1143 {
1144 vbpci_printk(KERN_DEBUG, NULL, "cannot allocate IOMMU domain\n");
1145 rc = VERR_NO_MEMORY;
1146 }
1147 else
1148 {
1149 pVmData->pfnContigMemInfo = vboxPciOsContigMemInfo;
1150
1151 vbpci_printk(KERN_DEBUG, NULL, "created IOMMU domain %p\n",
1152 pThis->pIommuDomain);
1153 }
1154 }
1155
1156 IPRT_LINUX_RESTORE_EFL_AC();
1157#endif
1158 return rc;
1159}
1160
1161DECLHIDDEN(void) vboxPciOsDeinitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM)
1162{
1163#ifdef VBOX_WITH_IOMMU
1164 IPRT_LINUX_SAVE_EFL_AC();
1165
1166 if (pThis->pIommuDomain)
1167 {
1168 vbpci_printk(KERN_DEBUG, NULL, "freeing IOMMU domain %p\n",
1169 pThis->pIommuDomain);
1170 iommu_domain_free(pThis->pIommuDomain);
1171 pThis->pIommuDomain = NULL;
1172 }
1173
1174 IPRT_LINUX_RESTORE_EFL_AC();
1175#endif
1176}
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