VirtualBox

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

Last change on this file since 66257 was 63564, checked in by vboxsync, 8 years ago

scm: cleaning up todos

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