VirtualBox

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

Last change on this file since 39317 was 39224, checked in by vboxsync, 14 years ago

Linux 3.2-rc1 compile fixes

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette