VirtualBox

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

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

vboxpci: make vboxPciOsDevInit() a bit less chatty.

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