VirtualBox

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

Last change on this file since 41244 was 39432, checked in by vboxsync, 13 years ago

VBoxPci: Fedora 2.6.41 exception

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.9 KB
Line 
1/* $Id: VBoxPci-linux.c 39432 2011-11-28 11:20:27Z 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 int 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)) ? 1 : 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 pNewCreds->fsuid = 0;
433 pOldCreds = override_creds(pNewCreds);
434#endif
435
436 RTStrPrintf(szFileBuf, cMaxBuf,
437 "/sys/bus/pci/drivers/%s/new_id",
438 PCI_STUB_MODULE);
439 pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
440 if (pFile)
441 {
442 iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf,
443 "%04x %04x",
444 uVendor, uDevice);
445 /* Don't write trailing \0 */
446 vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
447 vboxPciFileClose(pFile);
448 }
449 else
450 printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
451
452 iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf,
453 "0000:%02x:%02x.%d",
454 uBus, uDevFn>>3, uDevFn&7);
455
456 /* Unbind if bound to smth */
457 if (pIns->szPrevDriver[0])
458 {
459 RTStrPrintf(szFileBuf, cMaxBuf,
460 "/sys/bus/pci/drivers/%s/unbind",
461 pIns->szPrevDriver);
462 pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
463 if (pFile)
464 {
465
466 /* Don't write trailing \0 */
467 vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
468 vboxPciFileClose(pFile);
469 }
470 else
471 printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
472 }
473
474 RTStrPrintf(szFileBuf, cMaxBuf,
475 "/sys/bus/pci/drivers/%s/bind",
476 PCI_STUB_MODULE);
477 pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
478 if (pFile)
479 {
480 /* Don't write trailing \0 */
481 vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
482 vboxPciFileClose(pFile);
483 }
484 else
485 printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
486
487#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
488 revert_creds(pOldCreds);
489 put_cred(pNewCreds);
490#endif
491
492 done:
493 kfree(szCmdBuf);
494 kfree(szFileBuf);
495 }
496
497 return 0;
498}
499
500int vboxPciOsDevReattachHostDriver(PVBOXRAWPCIINS pIns)
501{
502 struct pci_dev *pPciDev = pIns->pPciDev;
503
504 if (!pPciDev)
505 return VINF_SUCCESS;
506
507 if (pIns->szPrevDriver[0])
508 {
509 char* szCmdBuf;
510 char* szFileBuf;
511 struct file* pFile;
512 int iCmdLen;
513 const int cMaxBuf = 128;
514 const struct cred *pOldCreds;
515 struct cred *pNewCreds;
516 uint8_t uBus = (pIns->HostPciAddress) >> 8;
517 uint8_t uDevFn = (pIns->HostPciAddress) & 0xff;
518
519 printk(KERN_DEBUG "vboxpci: reattaching old host driver %s\n", pIns->szPrevDriver);
520 /*
521 * Now perform kernel analog of:
522 *
523 * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/pci-stub/unbind
524 * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/nvidia/bind
525 */
526 szCmdBuf = kmalloc(cMaxBuf, GFP_KERNEL);
527 szFileBuf = kmalloc(cMaxBuf, GFP_KERNEL);
528
529 if (!szCmdBuf || !szFileBuf)
530 goto done;
531
532 iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf,
533 "0000:%02x:%02x.%d",
534 uBus, uDevFn>>3, uDevFn&7);
535
536 /* Somewhat ugly hack - override current credentials */
537#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
538 pNewCreds = prepare_creds();
539 if (!pNewCreds)
540 goto done;
541
542 pNewCreds->fsuid = 0;
543 pOldCreds = override_creds(pNewCreds);
544#endif
545 RTStrPrintf(szFileBuf, cMaxBuf,
546 "/sys/bus/pci/drivers/%s/unbind",
547 PCI_STUB_MODULE);
548 pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
549 if (pFile)
550 {
551
552 /* Don't write trailing \0 */
553 vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
554 vboxPciFileClose(pFile);
555 }
556 else
557 printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
558
559 RTStrPrintf(szFileBuf, cMaxBuf,
560 "/sys/bus/pci/drivers/%s/bind",
561 pIns->szPrevDriver);
562 pFile = vboxPciFileOpen(szFileBuf, O_WRONLY);
563 if (pFile)
564 {
565
566 /* Don't write trailing \0 */
567 vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen);
568 vboxPciFileClose(pFile);
569 pIns->szPrevDriver[0] = '\0';
570 }
571 else
572 printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf);
573
574#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
575 revert_creds(pOldCreds);
576 put_cred(pNewCreds);
577#endif
578
579 done:
580 kfree(szCmdBuf);
581 kfree(szFileBuf);
582 }
583
584 return VINF_SUCCESS;
585}
586
587int vboxPciOsDevInit(PVBOXRAWPCIINS pIns, uint32_t fFlags)
588{
589 struct pci_dev *pPciDev = NULL;
590 int rc;
591
592 printk(KERN_DEBUG "vboxpci: vboxPciOsDevInit: dev=%x\n", pIns->HostPciAddress);
593
594 if (fFlags & PCIRAWDRIVERRFLAG_DETACH_HOST_DRIVER)
595 {
596 rc = vboxPciOsDevDetachHostDriver(pIns);
597 if (RT_FAILURE(rc))
598 {
599 printk(KERN_DEBUG "Cannot detach host driver for device %x: %d\n",
600 pIns->HostPciAddress, rc);
601 return VERR_ACCESS_DENIED;
602 }
603 }
604
605
606 pPciDev = PCI_DEV_GET_SLOT((pIns->HostPciAddress) >> 8,
607 (pIns->HostPciAddress) & 0xff);
608
609 printk(KERN_DEBUG "vboxpci: vboxPciOsDevInit: dev=%x pdev=%p\n",
610 pIns->HostPciAddress, pPciDev);
611
612 if (!pPciDev)
613 return 0;
614
615 pIns->pPciDev = pPciDev;
616
617 rc = pci_enable_device(pPciDev);
618
619#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 1)
620 if (pci_enable_msi(pPciDev) == 0)
621 {
622 printk(KERN_DEBUG "vboxpci: enabled MSI\n");
623 pIns->fMsiUsed = true;
624 }
625#endif
626
627 // pci_enable_msix(pPciDev, entries, nvec)
628
629 /* In fact, if device uses interrupts, and cannot be forced to use MSI or MSI-X
630 we have to refuse using it, as we cannot work with shared PCI interrupts (unless we're lucky
631 to grab unshared PCI interrupt). */
632
633 return VINF_SUCCESS;
634}
635
636int vboxPciOsDevDeinit(PVBOXRAWPCIINS pIns, uint32_t fFlags)
637{
638 struct pci_dev *pPciDev = NULL;
639
640 printk(KERN_DEBUG "vboxpci: vboxPciOsDevDeinit: dev=%x\n", pIns->HostPciAddress);
641
642 pPciDev = pIns->pPciDev;
643
644 if (pPciDev)
645 {
646 vboxPciOsDevUnregisterWithIommu(pIns);
647
648#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 1)
649 if (pIns->fMsiUsed)
650 pci_disable_msi(pPciDev);
651#endif
652 // pci_disable_msix(pPciDev);
653 pci_disable_device(pPciDev);
654 vboxPciOsDevReattachHostDriver(pIns);
655
656 PCI_DEV_PUT(pPciDev);
657 pIns->pPciDev = NULL;
658 }
659
660 return 0;
661}
662
663int vboxPciOsDevDestroy(PVBOXRAWPCIINS pIns)
664{
665 return 0;
666}
667
668int vboxPciOsDevGetRegionInfo(PVBOXRAWPCIINS pIns,
669 int32_t iRegion,
670 RTHCPHYS *pRegionStart,
671 uint64_t *pu64RegionSize,
672 bool *pfPresent,
673 uint32_t *pfFlags)
674{
675 int flags;
676 struct pci_dev *pPciDev = pIns->pPciDev;
677 uint32_t fResFlags;
678
679 if (!pPciDev)
680 {
681 *pfPresent = false;
682 return 0;
683 }
684
685 printk(KERN_DEBUG "%x: linux vboxPciOsDevGetRegionInfo: reg=%d\n",
686 pIns->HostPciAddress, iRegion);
687
688 flags = pci_resource_flags(pPciDev, iRegion);
689 if (((flags & (IORESOURCE_MEM | IORESOURCE_IO)) == 0)
690 ||
691 ((flags & IORESOURCE_DISABLED) != 0))
692 {
693 *pfPresent = false;
694 return 0;
695 }
696
697 *pfPresent = true;
698 fResFlags = 0;
699
700 if (flags & IORESOURCE_MEM)
701 fResFlags |= PCIRAW_ADDRESS_SPACE_MEM;
702
703 if (flags & IORESOURCE_IO)
704 fResFlags |= PCIRAW_ADDRESS_SPACE_IO;
705
706#ifdef IORESOURCE_MEM_64
707 if (flags & IORESOURCE_MEM_64)
708 fResFlags |= PCIRAW_ADDRESS_SPACE_BAR64;
709#endif
710
711 if (flags & IORESOURCE_PREFETCH)
712 fResFlags |= PCIRAW_ADDRESS_SPACE_MEM_PREFETCH;
713
714 *pfFlags = fResFlags;
715 *pRegionStart = pci_resource_start(pPciDev, iRegion);
716 *pu64RegionSize = pci_resource_len (pPciDev, iRegion);
717
718 printk(KERN_DEBUG "got %s region: %llx:%lld\n",
719 (flags & IORESOURCE_MEM) ? "mmio" : "pio", *pRegionStart, *pu64RegionSize);
720
721 return 0;
722}
723
724int vboxPciOsDevMapRegion(PVBOXRAWPCIINS pIns,
725 int32_t iRegion,
726 RTHCPHYS RegionStart,
727 uint64_t u64RegionSize,
728 uint32_t fFlags,
729 RTR0PTR *pRegionBase)
730{
731 struct pci_dev *pPciDev = pIns->pPciDev;
732 struct resource *pRegion;
733 RTR0PTR result = 0;
734
735 printk(KERN_DEBUG "linux vboxPciOsDevMapRegion: reg=%d start=%llx size=%lld\n", iRegion, RegionStart, u64RegionSize);
736
737 if (!pPciDev)
738 return 0;
739
740 if (iRegion < 0 || iRegion > 6)
741 {
742 printk(KERN_DEBUG "vboxPciOsDevMapRegion: invalid region: %d\n", iRegion);
743 return VERR_INVALID_PARAMETER;
744 }
745
746 pRegion = request_mem_region(RegionStart, u64RegionSize, "vboxpci");
747 if (!pRegion)
748 {
749 /** @todo: need to make sure if thise error indeed can be ignored. */
750 printk(KERN_DEBUG "request_mem_region() failed, don't care\n");
751 }
752
753 /* For now no caching, try to optimize later. */
754 result = ioremap_nocache(RegionStart, u64RegionSize);
755
756 if (!result)
757 {
758 printk(KERN_DEBUG "cannot ioremap_nocache\n");
759 if (pRegion)
760 release_mem_region(RegionStart, u64RegionSize);
761 return 0;
762 }
763
764 *pRegionBase = result;
765
766 return 0;
767}
768
769int vboxPciOsDevUnmapRegion(PVBOXRAWPCIINS pIns,
770 int32_t iRegion,
771 RTHCPHYS RegionStart,
772 uint64_t u64RegionSize,
773 RTR0PTR RegionBase)
774{
775
776 iounmap(RegionBase);
777 release_mem_region(RegionStart, u64RegionSize);
778
779 return VINF_SUCCESS;
780}
781
782int vboxPciOsDevPciCfgWrite(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue)
783{
784 struct pci_dev *pPciDev = pIns->pPciDev;
785
786 if (!pPciDev)
787 return VINF_SUCCESS;
788
789 switch (pValue->cb)
790 {
791 case 1:
792 pci_write_config_byte(pPciDev, Register, pValue->u.u8);
793 break;
794 case 2:
795 pci_write_config_word(pPciDev, Register, pValue->u.u16);
796 break;
797 case 4:
798 pci_write_config_dword(pPciDev, Register, pValue->u.u32);
799 break;
800 }
801
802 return VINF_SUCCESS;
803}
804
805int vboxPciOsDevPciCfgRead (PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue)
806{
807 struct pci_dev *pPciDev = pIns->pPciDev;
808
809 if (!pPciDev)
810 return VINF_SUCCESS;
811
812 switch (pValue->cb)
813 {
814 case 1:
815 pci_read_config_byte(pPciDev, Register, &pValue->u.u8);
816 break;
817 case 2:
818 pci_read_config_word(pPciDev, Register, &pValue->u.u16);
819 break;
820 case 4:
821 pci_read_config_dword(pPciDev, Register, &pValue->u.u32);
822 break;
823 }
824
825 return VINF_SUCCESS;
826}
827
828/**
829 * Interrupt service routine.
830 *
831 * @returns In 2.6 we indicate whether we've handled the IRQ or not.
832 *
833 * @param iIrq The IRQ number.
834 * @param pvDevId The device ID, a pointer to PVBOXRAWPCIINS.
835 * @param pvRegs Register set. Removed in 2.6.19.
836 */
837#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
838static irqreturn_t vboxPciOsIrqHandler(int iIrq, void *pvDevId)
839#else
840static irqreturn_t vboxPciOsIrqHandler(int iIrq, void *pvDevId, struct pt_regs *pRegs)
841#endif
842{
843 PVBOXRAWPCIINS pIns = (PVBOXRAWPCIINS)pvDevId;
844 bool fTaken = true;
845
846 if (pIns && pIns->IrqHandler.pfnIrqHandler)
847 fTaken = pIns->IrqHandler.pfnIrqHandler(pIns->IrqHandler.pIrqContext, iIrq);
848#ifndef VBOX_WITH_SHARED_PCI_INTERRUPTS
849 /* If we don't allow interrupts sharing, we consider all interrupts as non-shared, thus targetted to us. */
850 fTaken = true;
851#endif
852
853 return fTaken;
854}
855
856int vboxPciOsDevRegisterIrqHandler(PVBOXRAWPCIINS pIns, PFNRAWPCIISR pfnHandler, void* pIrqContext, int32_t *piHostIrq)
857{
858 int rc;
859 int32_t iIrq = pIns->pPciDev->irq;
860
861 if (iIrq == 0)
862 {
863 printk(KERN_DEBUG "device not assigned host interrupt\n");
864 return VERR_INVALID_PARAMETER;
865 }
866
867 rc = request_irq(iIrq,
868 vboxPciOsIrqHandler,
869#ifdef VBOX_WITH_SHARED_PCI_INTERRUPTS
870 /* Allow interrupts sharing. */
871# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
872 IRQF_SHARED,
873# else
874 SA_SHIRQ,
875# endif
876
877#else
878
879 /* We don't allow interrupts sharing */
880# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
881 IRQF_DISABLED, /* keep irqs disabled when calling the action handler */
882# else
883 0,
884# endif
885#endif
886 DRIVER_NAME,
887 pIns);
888 if (rc)
889 {
890 printk(KERN_DEBUG "could not request IRQ %d: err=%d\n", iIrq, rc);
891 return VERR_RESOURCE_BUSY;
892 }
893
894 printk(KERN_DEBUG "got PCI IRQ: %d\n", iIrq);
895 *piHostIrq = iIrq;
896 return VINF_SUCCESS;
897}
898
899int vboxPciOsDevUnregisterIrqHandler(PVBOXRAWPCIINS pIns, int32_t iHostIrq)
900{
901 printk(KERN_DEBUG "free PCI IRQ: %d\n", iHostIrq);
902 free_irq(iHostIrq, pIns);
903 return VINF_SUCCESS;
904}
905
906int vboxPciOsDevPowerStateChange(PVBOXRAWPCIINS pIns, PCIRAWPOWERSTATE aState)
907{
908 int rc;
909
910 printk(KERN_DEBUG "power state: %d\n", (int)aState);
911
912 switch (aState)
913 {
914 case PCIRAW_POWER_ON:
915 /* Reset device, just in case. */
916 vboxPciOsDevReset(pIns);
917 /* register us with IOMMU */
918 rc = vboxPciOsDevRegisterWithIommu(pIns);
919 break;
920 case PCIRAW_POWER_RESET:
921 rc = vboxPciOsDevReset(pIns);
922 break;
923 case PCIRAW_POWER_OFF:
924 /* unregister us from IOMMU */
925 rc = vboxPciOsDevUnregisterWithIommu(pIns);
926 break;
927 case PCIRAW_POWER_SUSPEND:
928 case PCIRAW_POWER_RESUME:
929 rc = VINF_SUCCESS;
930 /// @todo: what do we do here?
931 break;
932 default:
933 /* to make compiler happy */
934 rc = VERR_NOT_SUPPORTED;
935 break;
936 }
937
938 return rc;
939}
940
941
942#ifdef VBOX_WITH_IOMMU
943/** Callback for FNRAWPCICONTIGPHYSMEMINFO. */
944static int vboxPciOsContigMemInfo(PRAWPCIPERVM pVmCtx, RTHCPHYS HostStart, RTGCPHYS GuestStart, uint64_t cMemSize, PCIRAWMEMINFOACTION Action)
945{
946 struct iommu_domain* domain = ((PVBOXRAWPCIDRVVM)(pVmCtx->pDriverData))->pIommuDomain;
947 int rc = VINF_SUCCESS;
948
949 switch (Action)
950 {
951 case PCIRAW_MEMINFO_MAP:
952 {
953 int flags, r;
954
955 if (iommu_iova_to_phys(domain, GuestStart))
956 break;
957
958 flags = IOMMU_READ | IOMMU_WRITE;
959 /* @todo: flags |= IOMMU_CACHE; */
960
961 r = iommu_map(domain, GuestStart, HostStart, get_order(cMemSize), flags);
962 if (r)
963 {
964 printk(KERN_ERR "vboxPciOsContigMemInfo:"
965 "iommu failed to map pfn=%llx\n", HostStart);
966 rc = VERR_GENERAL_FAILURE;
967 break;
968 }
969 rc = VINF_SUCCESS;
970 break;
971 }
972 case PCIRAW_MEMINFO_UNMAP:
973 {
974 int order;
975 order = iommu_unmap(domain, GuestStart, get_order(cMemSize));
976 NOREF(order);
977 break;
978 }
979
980 default:
981 printk(KERN_DEBUG "Unsupported action: %d\n", (int)Action);
982 rc = VERR_NOT_SUPPORTED;
983 break;
984 }
985
986 return rc;
987}
988#endif
989
990int vboxPciOsInitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM, PRAWPCIPERVM pVmData)
991{
992#ifdef DEBUG
993 printk(KERN_DEBUG "vboxPciOsInitVm: %p\n", pThis);
994#endif
995#ifdef VBOX_WITH_IOMMU
996 if (IOMMU_PRESENT())
997 {
998 pThis->pIommuDomain = IOMMU_DOMAIN_ALLOC();
999 if (!pThis->pIommuDomain)
1000 {
1001 printk(KERN_DEBUG "cannot allocate IOMMU domain\n");
1002 return VERR_NO_MEMORY;
1003 }
1004
1005 pVmData->pfnContigMemInfo = vboxPciOsContigMemInfo;
1006
1007 printk(KERN_DEBUG "created IOMMU domain %p\n", pThis->pIommuDomain);
1008 }
1009#endif
1010 return VINF_SUCCESS;
1011}
1012
1013void vboxPciOsDeinitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM)
1014{
1015#ifdef DEBUG
1016 printk(KERN_DEBUG "vboxPciOsDeinitVm: %p\n", pThis);
1017#endif
1018#ifdef VBOX_WITH_IOMMU
1019 if (pThis->pIommuDomain)
1020 {
1021 iommu_domain_free(pThis->pIommuDomain);
1022 pThis->pIommuDomain = NULL;
1023 }
1024#endif
1025}
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