VirtualBox

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

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

fix OSE

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