VirtualBox

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

Last change on this file since 63100 was 62529, checked in by vboxsync, 8 years ago

(C) 2016

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