VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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