VirtualBox

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

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

VBoxPci/linux: adapt to new location/name of header file in Linux 3.1. Contributed by Larry Finger.

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