VirtualBox

source: vbox/trunk/src/VBox/Additions/solaris/Virtio/VirtioPci-solaris.c

Last change on this file was 98103, checked in by vboxsync, 17 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: 24.6 KB
Line 
1/* $Id: VirtioPci-solaris.c 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * VirtualBox Guest Additions - Virtio Driver for Solaris, PCI Hypervisor Interface.
4 */
5
6/*
7 * Copyright (C) 2010-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 "VirtioPci-solaris.h"
42
43#include <iprt/asm.h>
44#include <iprt/assert.h>
45#include <iprt/mem.h>
46#include <iprt/param.h>
47#include <VBox/log.h>
48
49#include <sys/pci.h>
50#include <sys/param.h>
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56/*
57 * Pci Register offsets.
58 */
59#define VIRTIO_PCI_HOST_FEATURES 0x00
60#define VIRTIO_PCI_GUEST_FEATURES 0x04
61#define VIRTIO_PCI_QUEUE_PFN 0x08
62#define VIRTIO_PCI_QUEUE_NUM 0x0C
63#define VIRTIO_PCI_QUEUE_SEL 0x0E
64#define VIRTIO_PCI_QUEUE_NOTIFY 0x10
65#define VIRTIO_PCI_STATUS 0x12
66#define VIRTIO_PCI_ISR 0x13
67#define VIRTIO_PCI_CONFIG 0x14
68
69#define VIRTIO_PCI_RING_ALIGN PAGE_SIZE
70#define VIRTIO_PCI_QUEUE_ADDR_SHIFT PAGE_SHIFT
71
72/**
73 * virtio_pci_t: Private data per device instance.
74 */
75typedef struct virtio_pci_t
76{
77 ddi_acc_handle_t hIO; /* IO handle */
78 caddr_t addrIOBase; /* IO base address */
79} virtio_pci_t;
80
81/**
82 * virtio_pci_queue_t: Private data per queue instance.
83 */
84typedef struct virtio_pci_queue_t
85{
86 ddi_dma_handle_t hDMA; /* DMA handle. */
87 ddi_acc_handle_t hIO; /* IO handle. */
88 size_t cbBuf; /* Physical address of buffer. */
89 paddr_t physBuf; /* Size of buffer. */
90 pfn_t pageBuf; /* Page frame number of buffer. */
91} virtio_pci_queue_t;
92
93static ddi_device_acc_attr_t g_VirtioPciAccAttrRegs =
94{
95 DDI_DEVICE_ATTR_V0, /* Version */
96 DDI_STRUCTURE_LE_ACC, /* Structural data access in little endian. */
97 DDI_STRICTORDER_ACC, /* Strict ordering. */
98 DDI_DEFAULT_ACC /* Default access, possible panic on errors*/
99};
100
101static ddi_device_acc_attr_t g_VirtioPciAccAttrRing =
102{
103 DDI_DEVICE_ATTR_V0, /* Version. */
104 DDI_NEVERSWAP_ACC, /* Data access with no byte swapping*/
105 DDI_STRICTORDER_ACC, /* Strict ordering. */
106 DDI_DEFAULT_ACC /* Default access, possible panic on errors*/
107};
108
109static ddi_dma_attr_t g_VirtioPciDmaAttrRing =
110{
111 DMA_ATTR_V0, /* Version. */
112 0, /* Lowest usable address. */
113 0xffffffffffffffffULL, /* Highest usable address. */
114 0x7fffffff, /* Maximum DMAable byte count. */
115 VIRTIO_PCI_RING_ALIGN, /* Alignment in bytes. */
116 0x7ff, /* Bitmap of burst sizes */
117 1, /* Minimum transfer. */
118 0xffffffffU, /* Maximum transfer. */
119 0xffffffffffffffffULL, /* Maximum segment length. */
120 1, /* Maximum number of segments. */
121 1, /* Granularity. */
122 0 /* Flags (reserved). */
123};
124
125/** Pointer to the interrupt handle vector */
126static ddi_intr_handle_t *g_pIntr;
127/** Number of actually allocated interrupt handles */
128static size_t g_cIntrAllocated;
129/** The IRQ Mutex */
130static kmutex_t g_IrqMtx;
131
132
133/*********************************************************************************************************************************
134* Internal Functions *
135*********************************************************************************************************************************/
136static void *VirtioPciAlloc(PVIRTIODEVICE pDevice);
137static void VirtioPciFree(PVIRTIODEVICE pDevice);
138static int VirtioPciAttach(PVIRTIODEVICE pDevice);
139static int VirtioPciDetach(PVIRTIODEVICE pDevice);
140static uint32_t VirtioPciGetFeatures(PVIRTIODEVICE pDevice);
141static void VirtioPciSetFeatures(PVIRTIODEVICE pDevice, uint32_t fFeatures);
142static int VirtioPciNotifyQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue);
143static void VirtioPciGet(PVIRTIODEVICE pDevice, off_t off, void *pv, size_t cb);
144static void VirtioPciSet(PVIRTIODEVICE pDevice, off_t off, void *pv, size_t cb);
145static void *VirtioPciGetQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue);
146static void VirtioPciPutQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue);
147static void VirtioPciSetStatus(PVIRTIODEVICE pDevice, uint8_t Status);
148
149static uint_t VirtioPciISR(caddr_t Arg);
150static int VirtioPciSetupIRQ(dev_info_t *pDip);
151static void VirtioPciRemoveIRQ(dev_info_t *pDip);
152
153/**
154 * Hypervisor operations for Virtio Pci.
155 */
156VIRTIOHYPEROPS g_VirtioHyperOpsPci =
157{
158 VirtioPciAlloc,
159 VirtioPciFree,
160 VirtioPciAttach,
161 VirtioPciDetach,
162 VirtioPciGetFeatures,
163 VirtioPciSetFeatures,
164 VirtioPciNotifyQueue,
165 VirtioPciGet,
166 VirtioPciSet,
167 VirtioPciGetQueue,
168 VirtioPciPutQueue,
169 VirtioPciSetStatus
170};
171
172
173/**
174 * Virtio Pci private data allocation routine.
175 *
176 * @param pDevice Pointer to the Virtio device instance.
177 * @return Allocated private data structure which must only be freed by calling
178 * VirtioPciFree().
179 */
180static void *VirtioPciAlloc(PVIRTIODEVICE pDevice)
181{
182 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciAlloc pDevice=%p\n", pDevice));
183 virtio_pci_t *pPciData = RTMemAllocZ(sizeof(virtio_pci_t));
184 return pPciData;
185}
186
187
188/**
189 * Virtio Pci private data deallocation routine.
190 *
191 * @param pDevice Pointer to the Virtio device instance.
192 */
193static void VirtioPciFree(PVIRTIODEVICE pDevice)
194{
195 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciFree pDevice=%p\n", pDevice));
196 virtio_pci_t *pPciData = pDevice->pvHyper;
197 if (pPciData)
198 {
199 RTMemFree(pDevice->pvHyper);
200 pDevice->pvHyper = NULL;
201 }
202}
203
204
205/**
206 * Virtio Pci attach routine, called from driver attach.
207 *
208 * @param pDevice Pointer to the Virtio device instance.
209 *
210 * @return Solaris DDI error code. DDI_SUCCESS or DDI_FAILURE.
211 */
212static int VirtioPciAttach(PVIRTIODEVICE pDevice)
213{
214 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciAttach pDevice=%p\n", pDevice));
215 virtio_pci_t *pPciData = pDevice->pvHyper;
216 AssertReturn(pPciData, DDI_FAILURE);
217
218 int rc = ddi_regs_map_setup(pDevice->pDip,
219 1, /* reg. num */
220 &pPciData->addrIOBase,
221 0, /* offset */
222 0, /* length */
223 &g_VirtioPciAccAttrRegs,
224 &pPciData->hIO);
225 if (rc == DDI_SUCCESS)
226 {
227 /*
228 * Reset the device.
229 */
230 VirtioPciSetStatus(pDevice, 0);
231
232 /*
233 * Add interrupt handler.
234 */
235 VirtioPciSetupIRQ(pDevice->pDip);
236
237 LogFlow((VIRTIOLOGNAME ":VirtioPciAttach: successfully mapped registers.\n"));
238 return DDI_SUCCESS;
239 }
240 else
241 LogRel((VIRTIOLOGNAME ":VirtioPciAttach: ddi_regs_map_setup failed. rc=%d\n", rc));
242 return DDI_FAILURE;
243}
244
245
246/**
247 * Virtio Pci detach routine, called from driver detach.
248 *
249 * @param pDevice Pointer to the Virtio device instance.
250 *
251 * @return Solaris DDI error code. DDI_SUCCESS or DDI_FAILURE.
252 */
253static int VirtioPciDetach(PVIRTIODEVICE pDevice)
254{
255 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciDetach pDevice=%p\n", pDevice));
256 virtio_pci_t *pPciData = pDevice->pvHyper;
257 AssertReturn(pPciData, DDI_FAILURE);
258
259 VirtioPciRemoveIRQ(pDevice->pDip);
260 ddi_regs_map_free(&pPciData->hIO);
261 return DDI_SUCCESS;
262}
263
264
265/**
266 * Get host supported features.
267 *
268 * @param pDevice Pointer to the Virtio device instance.
269 *
270 * @return Mask of host features.
271 */
272static uint32_t VirtioPciGetFeatures(PVIRTIODEVICE pDevice)
273{
274 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciGetFeatures pDevice=%p\n", pDevice));
275 virtio_pci_t *pPciData = pDevice->pvHyper;
276 AssertReturn(pPciData, 0);
277
278 return ddi_get32(pPciData->hIO, (uint32_t *)(pPciData->addrIOBase + VIRTIO_PCI_HOST_FEATURES));
279}
280
281
282/**
283 * Set guest supported features.
284 *
285 * @param pDevice Pointer to the Virtio device instance.
286 * @param u32Features Mask of guest supported features.
287 */
288static void VirtioPciSetFeatures(PVIRTIODEVICE pDevice, uint32_t u32Features)
289{
290 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciSetFeatures pDevice=%p\n", pDevice));
291 virtio_pci_t *pPciData = pDevice->pvHyper;
292 AssertReturnVoid(pPciData);
293
294 ddi_put32(pPciData->hIO, (uint32_t *)(pPciData->addrIOBase + VIRTIO_PCI_GUEST_FEATURES), u32Features);
295}
296
297
298/**
299 * Update the queue, notify the host.
300 *
301 * @param pDevice Pointer to the Virtio device instance.
302 * @param pQueue Pointer to the Queue that is doing the notification.
303 *
304 * @return Solaris DDI error code. DDI_SUCCESS or DDI_FAILURE.
305 */
306static int VirtioPciNotifyQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue)
307{
308 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciNotifyQueue pDevice=%p pQueue=%p\n", pDevice, pQueue));
309 virtio_pci_t *pPciData = pDevice->pvHyper;
310 AssertReturn(pPciData, DDI_FAILURE);
311
312 pQueue->Ring.pRingAvail->Index += pQueue->cBufs;
313 pQueue->cBufs = 0;
314
315 ASMCompilerBarrier();
316
317 ddi_put16(pPciData->hIO, (uint16_t *)(pPciData->addrIOBase + VIRTIO_PCI_QUEUE_NOTIFY), pQueue->QueueIndex);
318 cmn_err(CE_NOTE, "VirtioPciNotifyQueue\n");
319}
320
321
322
323/**
324 * Virtio Pci set (write) routine.
325 *
326 * @param pDevice Pointer to the Virtio device instance.
327 * @param off Offset into the PCI config space.
328 * @param pv Pointer to the buffer to write from.
329 * @param cb Size of the buffer in bytes.
330 */
331static void VirtioPciSet(PVIRTIODEVICE pDevice, off_t off, void *pv, size_t cb)
332{
333 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciSet pDevice=%p\n", pDevice));
334 virtio_pci_t *pPciData = pDevice->pvHyper;
335 AssertReturnVoid(pPciData);
336
337 uint8_t *pb = pv;
338 for (size_t i = 0; i < cb; i++, pb++)
339 ddi_put8(pPciData->hIO, (uint8_t *)(pPciData->addrIOBase + VIRTIO_PCI_CONFIG + off + i), *pb);
340}
341
342
343/**
344 * Virtio Pci get (read) routine.
345 *
346 * @param pDevice Pointer to the Virtio device instance.
347 * @param off Offset into the PCI config space.
348 * @param pv Where to store the read data.
349 * @param cb Size of the buffer in bytes.
350 */
351static void VirtioPciGet(PVIRTIODEVICE pDevice, off_t off, void *pv, size_t cb)
352{
353 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciGet pDevice=%p off=%u pv=%p cb=%u\n", pDevice, off, pv, cb));
354 virtio_pci_t *pPciData = pDevice->pvHyper;
355 AssertReturnVoid(pPciData);
356
357 uint8_t *pb = pv;
358 for (size_t i = 0; i < cb; i++, pb++)
359 *pb = ddi_get8(pPciData->hIO, (uint8_t *)(pPciData->addrIOBase + VIRTIO_PCI_CONFIG + off + i));
360}
361
362
363/**
364 * Virtio Pci put queue routine. Places the queue and frees associated queue.
365 *
366 * @param pDevice Pointer to the Virtio device instance.
367 * @param pQueue Pointer to the queue.
368 */
369static void VirtioPciPutQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue)
370{
371 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciPutQueue pDevice=%p pQueue=%p\n", pDevice, pQueue));
372 AssertReturnVoid(pDevice);
373 AssertReturnVoid(pQueue);
374
375 virtio_pci_t *pPci = pDevice->pvHyper;
376 AssertReturnVoid(pPci);
377 virtio_pci_queue_t *pPciQueue = pQueue->pvData;
378 if (RT_UNLIKELY(!pPciQueue))
379 {
380 LogRel((VIRTIOLOGNAME ":VirtioPciPutQueue missing Pci queue.\n"));
381 return;
382 }
383
384 ddi_put16(pPci->hIO, (uint16_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_SEL), pQueue->QueueIndex);
385 ddi_put32(pPci->hIO, (uint32_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_PFN), 0);
386
387 ddi_dma_unbind_handle(pPciQueue->hDMA);
388 ddi_dma_mem_free(&pPciQueue->hIO);
389 ddi_dma_free_handle(&pPciQueue->hDMA);
390 RTMemFree(pPciQueue);
391}
392
393
394/**
395 * Virtio Pci get queue routine. Allocates a PCI queue and DMA resources.
396 *
397 * @param pDevice Pointer to the Virtio device instance.
398 * @param pQueue Where to store the queue.
399 *
400 * @return An allocated Virtio Pci queue, or NULL in case of errors.
401 */
402static void *VirtioPciGetQueue(PVIRTIODEVICE pDevice, PVIRTIOQUEUE pQueue)
403{
404 LogFlowFunc((VIRTIOLOGNAME ":VirtioPciGetQueue pDevice=%p pQueue=%p\n", pDevice, pQueue));
405 AssertReturn(pDevice, NULL);
406
407 virtio_pci_t *pPci = pDevice->pvHyper;
408 AssertReturn(pPci, NULL);
409
410 /*
411 * Select a Queue.
412 */
413 ddi_put16(pPci->hIO, (uint16_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_SEL), pQueue->QueueIndex);
414
415 /*
416 * Get the currently selected Queue's size.
417 */
418 pQueue->Ring.cDesc = ddi_get16(pPci->hIO, (uint16_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_NUM));
419 if (RT_UNLIKELY(!pQueue->Ring.cDesc))
420 {
421 LogRel((VIRTIOLOGNAME ": VirtioPciGetQueue: Queue[%d] has no descriptors.\n", pQueue->QueueIndex));
422 return NULL;
423 }
424
425 /*
426 * Check if it's already active.
427 */
428 uint32_t QueuePFN = ddi_get32(pPci->hIO, (uint32_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_PFN));
429 if (QueuePFN != 0)
430 {
431 LogRel((VIRTIOLOGNAME ":VirtioPciGetQueue: Queue[%d] is already used.\n", pQueue->QueueIndex));
432 return NULL;
433 }
434
435 LogFlow(("Queue[%d] has %d slots.\n", pQueue->QueueIndex, pQueue->Ring.cDesc));
436
437 /*
438 * Allocate and initialize Pci queue data.
439 */
440 virtio_pci_queue_t *pPciQueue = RTMemAllocZ(sizeof(virtio_pci_queue_t));
441 if (pPciQueue)
442 {
443 /*
444 * Setup DMA.
445 */
446 size_t cbQueue = VirtioRingSize(pQueue->Ring.cDesc, VIRTIO_PCI_RING_ALIGN);
447 int rc = ddi_dma_alloc_handle(pDevice->pDip, &g_VirtioPciDmaAttrRing, DDI_DMA_SLEEP, 0 /* addr */, &pPciQueue->hDMA);
448 if (rc == DDI_SUCCESS)
449 {
450 rc = ddi_dma_mem_alloc(pPciQueue->hDMA, cbQueue, &g_VirtioPciAccAttrRing, DDI_DMA_CONSISTENT,
451 DDI_DMA_SLEEP, 0 /* addr */, &pQueue->pQueue, &pPciQueue->cbBuf,
452 &pPciQueue->hIO);
453 if (rc == DDI_SUCCESS)
454 {
455 AssertRelease(pPciQueue->cbBuf >= cbQueue);
456 ddi_dma_cookie_t DmaCookie;
457 uint_t cCookies;
458 rc = ddi_dma_addr_bind_handle(pPciQueue->hDMA, NULL /* addrspace */, pQueue->pQueue, pPciQueue->cbBuf,
459 DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP,
460 0 /* addr */, &DmaCookie, &cCookies);
461 if (rc == DDI_SUCCESS)
462 {
463 pPciQueue->physBuf = DmaCookie.dmac_laddress;
464 pPciQueue->pageBuf = pPciQueue->physBuf >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
465
466 LogFlow((VIRTIOLOGNAME ":VirtioPciGetQueue: Queue[%d]%p physBuf=%x pfn of Buf %#x\n", pQueue->QueueIndex,
467 pQueue->pQueue, pPciQueue->physBuf, pPciQueue->pageBuf));
468 cmn_err(CE_NOTE, ":VirtioPciGetQueue: Queue[%d]%p physBuf=%x pfn of Buf %x\n", pQueue->QueueIndex,
469 pQueue->pQueue, pPciQueue->physBuf, pPciQueue->pageBuf);
470
471 /*
472 * Activate the queue and initialize a ring for the queue.
473 */
474 memset(pQueue->pQueue, 0, pPciQueue->cbBuf);
475 ddi_put32(pPci->hIO, (uint32_t *)(pPci->addrIOBase + VIRTIO_PCI_QUEUE_PFN), pPciQueue->pageBuf);
476 VirtioRingInit(pQueue, pQueue->Ring.cDesc, pQueue->pQueue, VIRTIO_PCI_RING_ALIGN);
477 return pPciQueue;
478 }
479 else
480 LogRel((VIRTIOLOGNAME ":VirtioPciGetQueue: ddi_dma_addr_bind_handle failed. rc=%d\n", rc));
481
482 ddi_dma_mem_free(&pPciQueue->hIO);
483 }
484 else
485 LogRel((VIRTIOLOGNAME ":VirtioPciGetQueue: ddi_dma_mem_alloc failed for %u bytes rc=%d\n", cbQueue, rc));
486
487 ddi_dma_free_handle(&pPciQueue->hDMA);
488 }
489 else
490 LogRel((VIRTIOLOGNAME ":VirtioPciGetQueue: ddi_dma_alloc_handle failed. rc=%d\n", rc));
491
492 RTMemFree(pPciQueue);
493 }
494 else
495 LogRel((VIRTIOLOGNAME ":VirtioPciGetQueue: failed to alloc %u bytes for Pci Queue data.\n", sizeof(virtio_pci_queue_t)));
496
497 return NULL;
498}
499
500
501/**
502 * Set the Virtio PCI status bit.
503 *
504 * @param pDevice Pointer to the Virtio device instance.
505 * @param Status The status to set.
506 */
507static void VirtioPciSetStatus(PVIRTIODEVICE pDevice, uint8_t Status)
508{
509 virtio_pci_t *pPciData = pDevice->pvHyper;
510 ddi_put8(pPciData->hIO, (uint8_t *)(pPciData->addrIOBase + VIRTIO_PCI_STATUS), Status);
511}
512
513
514/**
515 * Sets up IRQ for Virtio PCI.
516 *
517 * @param pDip Pointer to the device info structure.
518 *
519 * @return Solaris error code.
520 */
521static int VirtioPciSetupIRQ(dev_info_t *pDip)
522{
523 LogFlow((VIRTIOLOGNAME ":VirtioPciSetupIRQ: pDip=%p\n", pDip));
524 cmn_err(CE_NOTE, "VirtioPciSetupIRQ\n");
525
526 int IntrType = 0;
527 int rc = ddi_intr_get_supported_types(pDip, &IntrType);
528 if (rc == DDI_SUCCESS)
529 {
530 /* We won't need to bother about MSIs. */
531 if (IntrType & DDI_INTR_TYPE_FIXED)
532 {
533 int IntrCount = 0;
534 rc = ddi_intr_get_nintrs(pDip, IntrType, &IntrCount);
535 if ( rc == DDI_SUCCESS
536 && IntrCount > 0)
537 {
538 int IntrAvail = 0;
539 rc = ddi_intr_get_navail(pDip, IntrType, &IntrAvail);
540 if ( rc == DDI_SUCCESS
541 && IntrAvail > 0)
542 {
543 /* Allocated kernel memory for the interrupt handles. The allocation size is stored internally. */
544 g_pIntr = RTMemAllocZ(IntrCount * sizeof(ddi_intr_handle_t));
545 if (g_pIntr)
546 {
547 int IntrAllocated;
548 rc = ddi_intr_alloc(pDip, g_pIntr, IntrType, 0, IntrCount, &IntrAllocated, DDI_INTR_ALLOC_NORMAL);
549 if ( rc == DDI_SUCCESS
550 && IntrAllocated > 0)
551 {
552 g_cIntrAllocated = IntrAllocated;
553 uint_t uIntrPriority;
554 rc = ddi_intr_get_pri(g_pIntr[0], &uIntrPriority);
555 if (rc == DDI_SUCCESS)
556 {
557 /* Initialize the mutex. */
558 mutex_init(&g_IrqMtx, NULL, MUTEX_DRIVER, DDI_INTR_PRI(uIntrPriority));
559
560 /* Assign interrupt handler functions and enable interrupts. */
561 for (int i = 0; i < IntrAllocated; i++)
562 {
563 rc = ddi_intr_add_handler(g_pIntr[i], (ddi_intr_handler_t *)VirtioPciISR,
564 NULL /* No Private Data */, NULL);
565 if (rc == DDI_SUCCESS)
566 rc = ddi_intr_enable(g_pIntr[i]);
567 if (rc != DDI_SUCCESS)
568 {
569 /* Changing local IntrAllocated to hold so-far allocated handles for freeing. */
570 IntrAllocated = i;
571 break;
572 }
573 }
574 if (rc == DDI_SUCCESS)
575 {
576 cmn_err(CE_NOTE, "VirtioPciSetupIRQ success\n");
577 return rc;
578 }
579
580 /* Remove any assigned handlers */
581 LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ failed to assign IRQs allocated=%d\n", IntrAllocated));
582 for (int x = 0; x < IntrAllocated; x++)
583 ddi_intr_remove_handler(g_pIntr[x]);
584 }
585 else
586 LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ failed to get priority of interrupt. rc=%d\n", rc));
587
588 /* Remove allocated IRQs, too bad we can free only one handle at a time. */
589 for (int k = 0; k < g_cIntrAllocated; k++)
590 ddi_intr_free(g_pIntr[k]);
591 }
592 else
593 LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
594 RTMemFree(g_pIntr);
595 }
596 else
597 LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: failed to allocated IRQs. count=%d\n", IntrCount));
598 }
599 else
600 {
601 LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: failed to get or insufficient available IRQs. rc=%d IntrAvail=%d\n",
602 rc, IntrAvail));
603 }
604 }
605 else
606 {
607 LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: failed to get or insufficient number of IRQs. rc=%d IntrCount=%d\n", rc,
608 IntrCount));
609 }
610 }
611 else
612 LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: invalid irq type. IntrType=%#x\n", IntrType));
613 }
614 else
615 LogRel((VIRTIOLOGNAME ":VirtioPciSetupIRQ: failed to get supported interrupt types\n"));
616 return rc;
617}
618
619
620/**
621 * Removes IRQ for Virtio PCI device.
622 *
623 * @param pDip Pointer to the device info structure.
624 */
625static void VirtioPciRemoveIRQ(dev_info_t *pDip)
626{
627 LogFlow((VIRTIOLOGNAME ":VirtioPciRemoveIRQ pDip=%p:\n", pDip));
628
629 for (int i = 0; i < g_cIntrAllocated; i++)
630 {
631 int rc = ddi_intr_disable(g_pIntr[i]);
632 if (rc == DDI_SUCCESS)
633 {
634 rc = ddi_intr_remove_handler(g_pIntr[i]);
635 if (rc == DDI_SUCCESS)
636 ddi_intr_free(g_pIntr[i]);
637 }
638 }
639 RTMemFree(g_pIntr);
640 mutex_destroy(&g_IrqMtx);
641}
642
643
644/**
645 * Interrupt Service Routine for Virtio PCI device.
646 *
647 * @param Arg Private data (unused, will be NULL).
648 * @returns DDI_INTR_CLAIMED if it's our interrupt, DDI_INTR_UNCLAIMED if it isn't.
649 */
650static uint_t VirtioPciISR(caddr_t Arg)
651{
652 LogFlow((VIRTIOLOGNAME ":VBoxGuestSolarisISR\n"));
653 cmn_err(CE_NOTE, "VBoxGuestSolarisISRd Arg=%p\n", Arg);
654
655 mutex_enter(&g_IrqMtx);
656 bool fOurIRQ = false;
657 /*
658 * Call the DeviceOps ISR routine somehow which should notify all Virtio queues
659 * on the interrupt.
660 */
661 mutex_exit(&g_IrqMtx);
662
663 return fOurIRQ ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED;
664}
665
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use