VirtualBox

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

Last change on this file since 50781 was 50781, checked in by vboxsync, 11 years ago

One semicolon is enough.

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