VirtualBox

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

Last change on this file since 69250 was 69250, checked in by vboxsync, 7 years ago

HostDrivers: scm updates

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