VirtualBox

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

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

HostDrivers/VBoxPci: enclose find_module() calls by mutex_lock/unlock as required by Linux 4.2

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