VirtualBox

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

Last change on this file since 45300 was 45300, checked in by vboxsync, 12 years ago

Linux: more CONFIG_UIDGID_STRICT_TYPE_CHECKS fixes

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