VirtualBox

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

Last change on this file since 67954 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
RevLine 
[37798]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/*
[62490]7 * Copyright (C) 2011-2016 Oracle Corporation
[37798]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
[57372]18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
[37798]22#include "the-linux-kernel.h"
23#include "version-generated.h"
[60584]24#include "revision-generated.h"
[37798]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
[39224]38# include <linux/dmar.h>
39# include <linux/intel-iommu.h>
40# include <linux/pci.h>
[39432]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))
[39224]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 */
[37798]55
56
[57372]57/*********************************************************************************************************************************
58* Internal Functions *
59*********************************************************************************************************************************/
[37798]60static int VBoxPciLinuxInit(void);
61static void VBoxPciLinuxUnload(void);
62
[57372]63
64/*********************************************************************************************************************************
65* Global Variables *
66*********************************************************************************************************************************/
[37798]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
[60584]76MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV));
[37798]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)
[49137]86# define PCI_DEV_PUT(x) do { } while (0)
[37798]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
[50782]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
[37798]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 */
[57594]153 mutex_lock(&module_mutex);
[37798]154 g_VBoxPciGlobals.pciStubModule = find_module(PCI_STUB_MODULE_NAME);
[57594]155 mutex_unlock(&module_mutex);
[37798]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
[39224]174 if (IOMMU_PRESENT())
[37798]175 printk(KERN_INFO "vboxpci: IOMMU found\n");
176 else
[37868]177 printk(KERN_INFO "vboxpci: IOMMU not found (not registered)\n");
[37798]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
[58963]211static int vboxPciLinuxDevRegisterWithIommu(PVBOXRAWPCIINS pIns)
[37798]212{
213#ifdef VBOX_WITH_IOMMU
[58963]214 int rc = VINF_SUCCESS;
[50782]215 struct pci_dev *pPciDev = pIns->pPciDev;
[37798]216 PVBOXRAWPCIDRVVM pData = VBOX_DRV_VMDATA(pIns);
[58963]217 IPRT_LINUX_SAVE_EFL_AC();
[37798]218
[58963]219 if (RT_LIKELY(pData))
[37798]220 {
[58963]221 if (RT_LIKELY(pData->pIommuDomain))
222 {
[63564]223 /** @todo KVM checks IOMMU_CAP_CACHE_COHERENCY and sets
[58963]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 }
[37798]245 }
246 else
247 {
[58963]248 vbpci_printk(KERN_DEBUG, pPciDev, "cannot attach to IOMMU, no VM data\n");
249 rc = VERR_INVALID_PARAMETER;
[37798]250 }
251
[58963]252 IPRT_LINUX_RESTORE_EFL_AC();
[37798]253 return rc;
254#else
255 return VERR_NOT_SUPPORTED;
256#endif
257}
258
[58963]259static int vboxPciLinuxDevUnregisterWithIommu(PVBOXRAWPCIINS pIns)
[37798]260{
261#ifdef VBOX_WITH_IOMMU
262 int rc = VINF_SUCCESS;
[50782]263 struct pci_dev *pPciDev = pIns->pPciDev;
[37798]264 PVBOXRAWPCIDRVVM pData = VBOX_DRV_VMDATA(pIns);
[58963]265 IPRT_LINUX_SAVE_EFL_AC();
[37798]266
[58963]267 if (RT_LIKELY(pData))
[37798]268 {
[58963]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 }
[37798]284 }
[58963]285 else
[37798]286 {
[50782]287 vbpci_printk(KERN_DEBUG, pPciDev,
[58963]288 "cannot detach from IOMMU, no VM data\n");
289 rc = VERR_INVALID_PARAMETER;
[37798]290 }
291
[58963]292 IPRT_LINUX_RESTORE_EFL_AC();
[37798]293 return rc;
294#else
295 return VERR_NOT_SUPPORTED;
296#endif
297}
298
[58963]299static int vboxPciLinuxDevReset(PVBOXRAWPCIINS pIns)
[37798]300{
301 int rc = VINF_SUCCESS;
[58963]302 IPRT_LINUX_SAVE_EFL_AC();
[37798]303
[58963]304 if (RT_LIKELY(pIns->pPciDev))
[37798]305 {
306#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)
307 if (pci_reset_function(pIns->pPciDev))
308 {
[50797]309 vbpci_printk(KERN_DEBUG, pIns->pPciDev,
310 "pci_reset_function() failed\n");
[37798]311 rc = VERR_INTERNAL_ERROR;
312 }
313#else
314 rc = VERR_NOT_SUPPORTED;
315#endif
316 }
[58963]317 else
318 rc = VERR_INVALID_PARAMETER;
[37798]319
[58963]320 IPRT_LINUX_RESTORE_EFL_AC();
[37798]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
[58963]368static int vboxPciLinuxDevDetachHostDriver(PVBOXRAWPCIINS pIns)
[37798]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;
[50780]375 bool fDetach = 0;
[37798]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
[50780]402 fDetach = (currentDriver == NULL || (strcmp(currentDriver, PCI_STUB_MODULE) != 0));
[37798]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 }
[63564]415 /** @todo RTStrCopy not exported. */
[37798]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
[45300]453# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
454 pNewCreds->fsuid = GLOBAL_ROOT_UID;
455# else
[37798]456 pNewCreds->fsuid = 0;
[45300]457# endif
[37798]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
[58963]525static int vboxPciLinuxDevReattachHostDriver(PVBOXRAWPCIINS pIns)
[37798]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
[50797]544 vbpci_printk(KERN_DEBUG, pPciDev,
545 "reattaching old host driver %s\n", pIns->szPrevDriver);
[37798]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
[45300]568# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0)
569 pNewCreds->fsuid = GLOBAL_ROOT_UID;
570# else
[37798]571 pNewCreds->fsuid = 0;
[45300]572# endif
[37798]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
[58963]617DECLHIDDEN(int) vboxPciOsDevInit(PVBOXRAWPCIINS pIns, uint32_t fFlags)
[37798]618{
619 struct pci_dev *pPciDev = NULL;
[58963]620 int rc = VINF_SUCCESS;
621 IPRT_LINUX_SAVE_EFL_AC();
[37798]622
623 if (fFlags & PCIRAWDRIVERRFLAG_DETACH_HOST_DRIVER)
624 {
[58963]625 rc = vboxPciLinuxDevDetachHostDriver(pIns);
[37798]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
[58963]633 if (RT_SUCCESS(rc))
634 {
635 pPciDev = PCI_DEV_GET_SLOT((pIns->HostPciAddress) >> 8,
636 (pIns->HostPciAddress) & 0xff);
[37798]637
[58963]638 if (RT_LIKELY(pPciDev))
639 {
640 int rcLnx = pci_enable_device(pPciDev);
[37798]641
[58963]642 if (!rcLnx)
643 {
644 pIns->pPciDev = pPciDev;
645 vbpci_printk(KERN_DEBUG, pPciDev, "%s\n", __func__);
[37798]646
647#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 1)
[58963]648 if (pci_enable_msi(pPciDev) == 0)
649 pIns->fMsiUsed = true;
[37798]650#endif
651
[58963]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 }
[37798]666
[58963]667 IPRT_LINUX_RESTORE_EFL_AC();
668 return rc;
[37798]669}
670
[58963]671DECLHIDDEN(int) vboxPciOsDevDeinit(PVBOXRAWPCIINS pIns, uint32_t fFlags)
[37798]672{
[58963]673 int rc = VINF_SUCCESS;
[50782]674 struct pci_dev *pPciDev = pIns->pPciDev;
[58963]675 IPRT_LINUX_SAVE_EFL_AC();
[37798]676
[50782]677 vbpci_printk(KERN_DEBUG, pPciDev, "%s\n", __func__);
[37798]678
[58963]679 if (RT_LIKELY(pPciDev))
[37798]680 {
[50779]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
[58963]692 vboxPciLinuxDevUnregisterWithIommu(pIns);
[37798]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);
[58963]700 vboxPciLinuxDevReattachHostDriver(pIns);
[37798]701
702 PCI_DEV_PUT(pPciDev);
703 pIns->pPciDev = NULL;
704 }
[58963]705 else
706 rc = VERR_INVALID_PARAMETER;
[37798]707
[58963]708 IPRT_LINUX_RESTORE_EFL_AC();
709 return rc;
[37798]710}
711
[58963]712DECLHIDDEN(int) vboxPciOsDevDestroy(PVBOXRAWPCIINS pIns)
[37798]713{
[58963]714 return VINF_SUCCESS;
[37798]715}
716
[58963]717DECLHIDDEN(int) vboxPciOsDevGetRegionInfo(PVBOXRAWPCIINS pIns,
718 int32_t iRegion,
719 RTHCPHYS *pRegionStart,
720 uint64_t *pu64RegionSize,
721 bool *pfPresent,
722 uint32_t *pfFlags)
[37798]723{
[58963]724 int rc = VINF_SUCCESS;
[37798]725 struct pci_dev *pPciDev = pIns->pPciDev;
[58963]726 IPRT_LINUX_SAVE_EFL_AC();
[37798]727
[58963]728 if (RT_LIKELY(pPciDev))
[37798]729 {
[58963]730 int fFlags = pci_resource_flags(pPciDev, iRegion);
[37798]731
[58963]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;
[37798]742
[58963]743 if (fFlags & IORESOURCE_MEM)
744 fResFlags |= PCIRAW_ADDRESS_SPACE_MEM;
[37798]745
[58963]746 if (fFlags & IORESOURCE_IO)
747 fResFlags |= PCIRAW_ADDRESS_SPACE_IO;
[37798]748
749#ifdef IORESOURCE_MEM_64
[58963]750 if (fFlags & IORESOURCE_MEM_64)
751 fResFlags |= PCIRAW_ADDRESS_SPACE_BAR64;
[37798]752#endif
753
[58963]754 if (fFlags & IORESOURCE_PREFETCH)
755 fResFlags |= PCIRAW_ADDRESS_SPACE_MEM_PREFETCH;
[37798]756
[58963]757 *pfFlags = fResFlags;
758 *pRegionStart = pci_resource_start(pPciDev, iRegion);
759 *pu64RegionSize = pci_resource_len (pPciDev, iRegion);
[37798]760
[58963]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 }
[37798]772
[58963]773 IPRT_LINUX_RESTORE_EFL_AC();
774 return rc;
[37798]775}
776
[58963]777DECLHIDDEN(int) vboxPciOsDevMapRegion(PVBOXRAWPCIINS pIns,
778 int32_t iRegion,
779 RTHCPHYS RegionStart,
780 uint64_t u64RegionSize,
781 uint32_t fFlags,
782 RTR0PTR *pRegionBase)
[37798]783{
[58963]784 int rc = VINF_SUCCESS;
[37798]785 struct pci_dev *pPciDev = pIns->pPciDev;
[58963]786 IPRT_LINUX_SAVE_EFL_AC();
[37798]787
[58963]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
[50797]797 vbpci_printk(KERN_DEBUG, pPciDev, "reg=%d start=%llx size=%lld\n",
798 iRegion, RegionStart, u64RegionSize);
[37798]799
[58963]800 if ( (pci_resource_flags(pPciDev, iRegion) & IORESOURCE_IO)
801 || RegionStart != pci_resource_start(pPciDev, iRegion)
802 || u64RegionSize != pci_resource_len(pPciDev, iRegion))
[37798]803 {
[58963]804 IPRT_LINUX_RESTORE_EFL_AC();
[37798]805 return VERR_INVALID_PARAMETER;
806 }
807
[50779]808 /*
809 * XXX: Current code never calls unmap. To avoid leaking mappings
810 * only request and map resources once.
811 */
[58963]812 if (!pIns->aRegionR0Mapping[iRegion])
[37798]813 {
[58963]814 int rcLnx;
[50779]815 *pRegionBase = pIns->aRegionR0Mapping[iRegion];
[37798]816
[58963]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));
[50779]823
[58963]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;
[37798]835 }
836
[58963]837 if (RT_SUCCESS(rc))
838 *pRegionBase = pIns->aRegionR0Mapping[iRegion];
[37798]839
[58963]840 IPRT_LINUX_RESTORE_EFL_AC();
841 return rc;
[37798]842}
843
[58963]844DECLHIDDEN(int) vboxPciOsDevUnmapRegion(PVBOXRAWPCIINS pIns,
845 int32_t iRegion,
846 RTHCPHYS RegionStart,
847 uint64_t u64RegionSize,
848 RTR0PTR RegionBase)
[37798]849{
[50779]850 /* XXX: Current code never calls unmap. */
851 return VERR_NOT_IMPLEMENTED;
[37798]852}
853
[58963]854DECLHIDDEN(int) vboxPciOsDevPciCfgWrite(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue)
[37798]855{
856 struct pci_dev *pPciDev = pIns->pPciDev;
[58963]857 int rc = VINF_SUCCESS;
858 IPRT_LINUX_SAVE_EFL_AC();
[37798]859
[58963]860 if (RT_LIKELY(pPciDev))
[37798]861 {
[58963]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 }
[37798]874 }
[58963]875 else
876 rc = VERR_INVALID_PARAMETER;
[37798]877
[58963]878 IPRT_LINUX_RESTORE_EFL_AC();
879 return rc;
[37798]880}
881
[58963]882DECLHIDDEN(int) vboxPciOsDevPciCfgRead(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue)
[37798]883{
884 struct pci_dev *pPciDev = pIns->pPciDev;
[58963]885 int rc = VINF_SUCCESS;
[37798]886
[58963]887 if (RT_LIKELY(pPciDev))
888 {
889 IPRT_LINUX_SAVE_EFL_AC();
[37798]890
[58963]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();
[37798]905 }
[58963]906 else
907 rc = VERR_INVALID_PARAMETER;
[37798]908
[58963]909 return rc;
[37798]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.
[58340]919 * @param pRegs Register set. Removed in 2.6.19.
[37798]920 */
[58340]921#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) && !defined(DOXYGEN_RUNNING)
[37798]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
[58963]940DECLHIDDEN(int) vboxPciOsDevRegisterIrqHandler(PVBOXRAWPCIINS pIns, PFNRAWPCIISR pfnHandler, void* pIrqContext, int32_t *piHostIrq)
[37798]941{
942 int rc;
943 int32_t iIrq = pIns->pPciDev->irq;
[58963]944 IPRT_LINUX_SAVE_EFL_AC();
[37798]945
946 if (iIrq == 0)
947 {
[50782]948 vbpci_printk(KERN_NOTICE, pIns->pPciDev, "no irq assigned\n");
[58963]949 IPRT_LINUX_RESTORE_EFL_AC();
[37798]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 */
[55508]966 /* XXX overhaul */
967# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
[37798]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 {
[50782]977 vbpci_printk(KERN_DEBUG, pIns->pPciDev,
978 "could not request irq %d, error %d\n", iIrq, rc);
[58963]979 IPRT_LINUX_RESTORE_EFL_AC();
[37798]980 return VERR_RESOURCE_BUSY;
981 }
982
[50782]983 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "got irq %d\n", iIrq);
[37798]984 *piHostIrq = iIrq;
[58963]985
986 IPRT_LINUX_RESTORE_EFL_AC();
[37798]987 return VINF_SUCCESS;
988}
989
[58963]990DECLHIDDEN(int) vboxPciOsDevUnregisterIrqHandler(PVBOXRAWPCIINS pIns, int32_t iHostIrq)
[37798]991{
[58963]992 IPRT_LINUX_SAVE_EFL_AC();
993
[50782]994 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "freeing irq %d\n", iHostIrq);
[37798]995 free_irq(iHostIrq, pIns);
[58963]996
997 IPRT_LINUX_RESTORE_EFL_AC();
[37798]998 return VINF_SUCCESS;
999}
1000
[58963]1001DECLHIDDEN(int) vboxPciOsDevPowerStateChange(PVBOXRAWPCIINS pIns, PCIRAWPOWERSTATE aState)
[37798]1002{
1003 int rc;
1004
1005 switch (aState)
1006 {
1007 case PCIRAW_POWER_ON:
[50782]1008 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_ON\n");
[37798]1009 /* Reset device, just in case. */
[58963]1010 vboxPciLinuxDevReset(pIns);
[37798]1011 /* register us with IOMMU */
[58963]1012 rc = vboxPciLinuxDevRegisterWithIommu(pIns);
[37798]1013 break;
1014 case PCIRAW_POWER_RESET:
[50782]1015 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_RESET\n");
[58963]1016 rc = vboxPciLinuxDevReset(pIns);
[37798]1017 break;
1018 case PCIRAW_POWER_OFF:
[50782]1019 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_OFF\n");
[37798]1020 /* unregister us from IOMMU */
[58963]1021 rc = vboxPciLinuxDevUnregisterWithIommu(pIns);
[37798]1022 break;
1023 case PCIRAW_POWER_SUSPEND:
[50782]1024 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_SUSPEND\n");
1025 rc = VINF_SUCCESS;
[63564]1026 /// @todo what do we do here?
[50782]1027 break;
[37798]1028 case PCIRAW_POWER_RESUME:
[50782]1029 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_RESUME\n");
[37798]1030 rc = VINF_SUCCESS;
[63564]1031 /// @todo what do we do here?
[37798]1032 break;
1033 default:
[50782]1034 vbpci_printk(KERN_DEBUG, pIns->pPciDev, "unknown power state %u\n", aState);
[37798]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. */
[58963]1046static DECLCALLBACK(int) vboxPciOsContigMemInfo(PRAWPCIPERVM pVmCtx, RTHCPHYS HostStart, RTGCPHYS GuestStart,
1047 uint64_t cMemSize, PCIRAWMEMINFOACTION Action)
[37798]1048{
1049 struct iommu_domain* domain = ((PVBOXRAWPCIDRVVM)(pVmCtx->pDriverData))->pIommuDomain;
1050 int rc = VINF_SUCCESS;
[58963]1051 IPRT_LINUX_SAVE_EFL_AC();
[37798]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;
[63564]1063 /** @todo flags |= IOMMU_CACHE; */
[37798]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
[58963]1090 IPRT_LINUX_RESTORE_EFL_AC();
[37798]1091 return rc;
1092}
1093#endif
1094
[58963]1095DECLHIDDEN(int) vboxPciOsInitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM, PRAWPCIPERVM pVmData)
[37798]1096{
[58963]1097 int rc = VINF_SUCCESS;
1098
[37798]1099#ifdef VBOX_WITH_IOMMU
[58963]1100 IPRT_LINUX_SAVE_EFL_AC();
1101
[39224]1102 if (IOMMU_PRESENT())
[37798]1103 {
[39224]1104 pThis->pIommuDomain = IOMMU_DOMAIN_ALLOC();
[37798]1105 if (!pThis->pIommuDomain)
1106 {
[50782]1107 vbpci_printk(KERN_DEBUG, NULL, "cannot allocate IOMMU domain\n");
[58963]1108 rc = VERR_NO_MEMORY;
[37798]1109 }
[58963]1110 else
1111 {
1112 pVmData->pfnContigMemInfo = vboxPciOsContigMemInfo;
[37798]1113
[58963]1114 vbpci_printk(KERN_DEBUG, NULL, "created IOMMU domain %p\n",
1115 pThis->pIommuDomain);
1116 }
1117 }
[37798]1118
[58963]1119 IPRT_LINUX_RESTORE_EFL_AC();
[37798]1120#endif
[58963]1121 return rc;
[37798]1122}
1123
[58963]1124DECLHIDDEN(void) vboxPciOsDeinitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM)
[37798]1125{
1126#ifdef VBOX_WITH_IOMMU
[58963]1127 IPRT_LINUX_SAVE_EFL_AC();
1128
[37798]1129 if (pThis->pIommuDomain)
1130 {
[50782]1131 vbpci_printk(KERN_DEBUG, NULL, "freeing IOMMU domain %p\n",
1132 pThis->pIommuDomain);
[37798]1133 iommu_domain_free(pThis->pIommuDomain);
1134 pThis->pIommuDomain = NULL;
1135 }
[58963]1136
1137 IPRT_LINUX_RESTORE_EFL_AC();
[37798]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