VirtualBox

source: vbox/trunk/src/VBox/Devices/Bus/MsixCommon.cpp@ 71770

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

Msi: Some R3 prefixes and some clenaup. [build fix]

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.0 KB
Line 
1/* $Id: MsixCommon.cpp 71770 2018-04-09 14:22:03Z vboxsync $ */
2/** @file
3 * MSI-X support routines
4 */
5
6/*
7 * Copyright (C) 2010-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19#define LOG_GROUP LOG_GROUP_DEV_PCI
20#define PDMPCIDEV_INCLUDE_PRIVATE /* Hack to get pdmpcidevint.h included at the right point. */
21#include <VBox/pci.h>
22#include <VBox/msi.h>
23#include <VBox/vmm/pdmdev.h>
24#include <VBox/log.h>
25#include <VBox/vmm/mm.h>
26
27#include <iprt/assert.h>
28
29#include "MsiCommon.h"
30#include "PciInline.h"
31
32typedef struct
33{
34 uint32_t u32MsgAddressLo;
35 uint32_t u32MsgAddressHi;
36 uint32_t u32MsgData;
37 uint32_t u32VectorControl;
38} MsixTableRecord;
39AssertCompileSize(MsixTableRecord, VBOX_MSIX_ENTRY_SIZE);
40
41
42/** @todo use accessors so that raw PCI devices work correctly with MSI-X. */
43DECLINLINE(uint16_t) msixGetMessageControl(PPDMPCIDEV pDev)
44{
45 return PCIDevGetWord(pDev, pDev->Int.s.u8MsixCapOffset + VBOX_MSIX_CAP_MESSAGE_CONTROL);
46}
47
48DECLINLINE(bool) msixIsEnabled(PPDMPCIDEV pDev)
49{
50 return (msixGetMessageControl(pDev) & VBOX_PCI_MSIX_FLAGS_ENABLE) != 0;
51}
52
53DECLINLINE(bool) msixIsMasked(PPDMPCIDEV pDev)
54{
55 return (msixGetMessageControl(pDev) & VBOX_PCI_MSIX_FLAGS_FUNCMASK) != 0;
56}
57
58#ifdef IN_RING3
59DECLINLINE(uint16_t) msixTableSize(PPDMPCIDEV pDev)
60{
61 return (msixGetMessageControl(pDev) & 0x3ff) + 1;
62}
63#endif
64
65DECLINLINE(uint8_t *) msixGetPageOffset(PPDMPCIDEV pDev, uint32_t off)
66{
67 return (uint8_t *)pDev->Int.s.CTX_SUFF(pMsixPage) + off;
68}
69
70DECLINLINE(MsixTableRecord *) msixGetVectorRecord(PPDMPCIDEV pDev, uint32_t iVector)
71{
72 return (MsixTableRecord *)msixGetPageOffset(pDev, iVector * VBOX_MSIX_ENTRY_SIZE);
73}
74
75DECLINLINE(RTGCPHYS) msixGetMsiAddress(PPDMPCIDEV pDev, uint32_t iVector)
76{
77 MsixTableRecord *pRec = msixGetVectorRecord(pDev, iVector);
78 return RT_MAKE_U64(pRec->u32MsgAddressLo & ~UINT32_C(0x3), pRec->u32MsgAddressHi);
79}
80
81DECLINLINE(uint32_t) msixGetMsiData(PPDMPCIDEV pDev, uint32_t iVector)
82{
83 return msixGetVectorRecord(pDev, iVector)->u32MsgData;
84}
85
86DECLINLINE(uint32_t) msixIsVectorMasked(PPDMPCIDEV pDev, uint32_t iVector)
87{
88 return (msixGetVectorRecord(pDev, iVector)->u32VectorControl & 0x1) != 0;
89}
90
91DECLINLINE(uint8_t *) msixPendingByte(PPDMPCIDEV pDev, uint32_t iVector)
92{
93 return msixGetPageOffset(pDev, pDev->Int.s.offMsixPba + iVector / 8);
94}
95
96DECLINLINE(void) msixSetPending(PPDMPCIDEV pDev, uint32_t iVector)
97{
98 *msixPendingByte(pDev, iVector) |= (1 << (iVector & 0x7));
99}
100
101DECLINLINE(void) msixClearPending(PPDMPCIDEV pDev, uint32_t iVector)
102{
103 *msixPendingByte(pDev, iVector) &= ~(1 << (iVector & 0x7));
104}
105
106#ifdef IN_RING3
107
108DECLINLINE(bool) msixR3IsPending(PPDMPCIDEV pDev, uint32_t iVector)
109{
110 return (*msixPendingByte(pDev, iVector) & (1 << (iVector & 0x7))) != 0;
111}
112
113static void msixR3CheckPendingVector(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, uint32_t iVector)
114{
115 if (msixR3IsPending(pDev, iVector) && !msixIsVectorMasked(pDev, iVector))
116 MsixNotify(pDevIns, pPciHlp, pDev, iVector, 1 /* iLevel */, 0 /*uTagSrc*/);
117}
118
119
120PDMBOTHCBDECL(int) msixR3MMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void *pv, unsigned cb)
121{
122 LogFlowFunc(("\n"));
123
124 uint32_t off = (uint32_t)(GCPhysAddr & 0xffff);
125 PPDMPCIDEV pPciDev = (PPDMPCIDEV)pvUser;
126
127 /// @todo qword accesses?
128 RT_NOREF(pDevIns);
129 AssertMsgReturn(cb == 4,
130 ("MSI-X must be accessed with 4-byte reads"),
131 VERR_INTERNAL_ERROR);
132 AssertMsgReturn(off < pPciDev->Int.s.cbMsixRegion,
133 ("Out of bounds access for the MSI-X region\n"),
134 VINF_IOM_MMIO_UNUSED_FF);
135
136 *(uint32_t *)pv = *(uint32_t *)msixGetPageOffset(pPciDev, off);
137 return VINF_SUCCESS;
138}
139
140PDMBOTHCBDECL(int,) msixR3MMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS GCPhysAddr, void const *pv, unsigned cb)
141{
142 LogFlowFunc(("\n"));
143
144 PPDMPCIDEV pPciDev = (PPDMPCIDEV)pvUser;
145 uint32_t off = (uint32_t)(GCPhysAddr & 0xffff);
146
147 /// @todo qword accesses?
148 AssertMsgReturn(cb == 4,
149 ("MSI-X must be accessed with 4-byte reads"),
150 VERR_INTERNAL_ERROR);
151 AssertMsgReturn(off < pPciDev->Int.s.offMsixPba,
152 ("Trying to write to PBA\n"),
153 VINF_IOM_MMIO_UNUSED_FF);
154
155 *(uint32_t *)msixGetPageOffset(pPciDev, off) = *(uint32_t *)pv;
156
157 msixR3CheckPendingVector(pDevIns, (PCPDMPCIHLP)pPciDev->Int.s.pPciBusPtrR3, pPciDev, off / VBOX_MSIX_ENTRY_SIZE);
158 return VINF_SUCCESS;
159}
160
161/**
162 * @callback_method_impl{FNPCIIOREGIONMAP}
163 */
164static DECLCALLBACK(int) msixR3Map(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
165 RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
166{
167 Assert(enmType == PCI_ADDRESS_SPACE_MEM);
168 NOREF(iRegion); NOREF(enmType);
169
170 int rc = PDMDevHlpMMIORegister(pDevIns, GCPhysAddress, cb, pPciDev,
171 IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
172 msixR3MMIOWrite, msixR3MMIORead, "MSI-X tables");
173
174 if (RT_FAILURE(rc))
175 return rc;
176
177 return VINF_SUCCESS;
178}
179
180int MsixR3Init(PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, PPDMMSIREG pMsiReg)
181{
182 if (pMsiReg->cMsixVectors == 0)
183 return VINF_SUCCESS;
184
185 /* We cannot init MSI-X on raw devices yet. */
186 Assert(!pciDevIsPassthrough(pDev));
187
188 uint16_t cVectors = pMsiReg->cMsixVectors;
189 uint8_t iCapOffset = pMsiReg->iMsixCapOffset;
190 uint8_t iNextOffset = pMsiReg->iMsixNextOffset;
191 uint8_t iBar = pMsiReg->iMsixBar;
192
193 AssertMsgReturn(cVectors <= VBOX_MSIX_MAX_ENTRIES,
194 ("Too many MSI-X vectors: %d\n", cVectors),
195 VERR_TOO_MUCH_DATA);
196 AssertMsgReturn(iBar <= 5,
197 ("Using wrong BAR for MSI-X: %d\n", iBar),
198 VERR_INVALID_PARAMETER);
199
200 Assert(iCapOffset != 0 && iCapOffset < 0xff && iNextOffset < 0xff);
201
202 int rc = VINF_SUCCESS;
203 uint16_t cbPba = cVectors / 8;
204 if (cVectors % 8)
205 cbPba++;
206 uint16_t cbMsixRegion = RT_ALIGN_T(cVectors * sizeof(MsixTableRecord) + cbPba, _4K, uint16_t);
207
208 /* If device is passthrough, BAR is registered using common mechanism. */
209 if (!pciDevIsPassthrough(pDev))
210 {
211 rc = PDMDevHlpPCIIORegionRegister(pDev->Int.s.CTX_SUFF(pDevIns), iBar, cbMsixRegion, PCI_ADDRESS_SPACE_MEM, msixR3Map);
212 if (RT_FAILURE (rc))
213 return rc;
214 }
215
216 uint16_t offTable = 0;
217 uint16_t offPBA = cVectors * sizeof(MsixTableRecord);
218
219 pDev->Int.s.u8MsixCapOffset = iCapOffset;
220 pDev->Int.s.u8MsixCapSize = VBOX_MSIX_CAP_SIZE;
221 pDev->Int.s.cbMsixRegion = cbMsixRegion;
222 pDev->Int.s.offMsixPba = offPBA;
223 PVM pVM = PDMDevHlpGetVM(pDev->Int.s.CTX_SUFF(pDevIns));
224
225 pDev->Int.s.pMsixPageR3 = NULL;
226
227 rc = MMHyperAlloc(pVM, cbMsixRegion, 1, MM_TAG_PDM_DEVICE_USER, (void **)&pDev->Int.s.pMsixPageR3);
228 if (RT_FAILURE(rc) || (pDev->Int.s.pMsixPageR3 == NULL))
229 return VERR_NO_VM_MEMORY;
230 RT_BZERO(pDev->Int.s.pMsixPageR3, cbMsixRegion);
231 pDev->Int.s.pMsixPageR0 = MMHyperR3ToR0(pVM, pDev->Int.s.pMsixPageR3);
232 pDev->Int.s.pMsixPageRC = MMHyperR3ToRC(pVM, pDev->Int.s.pMsixPageR3);
233
234 /* R3 PCI helper */
235 pDev->Int.s.pPciBusPtrR3 = pPciHlp;
236
237 PCIDevSetByte(pDev, iCapOffset + 0, VBOX_PCI_CAP_ID_MSIX);
238 PCIDevSetByte(pDev, iCapOffset + 1, iNextOffset); /* next */
239 PCIDevSetWord(pDev, iCapOffset + VBOX_MSIX_CAP_MESSAGE_CONTROL, cVectors - 1);
240
241 PCIDevSetDWord(pDev, iCapOffset + VBOX_MSIX_TABLE_BIROFFSET, offTable | iBar);
242 PCIDevSetDWord(pDev, iCapOffset + VBOX_MSIX_PBA_BIROFFSET, offPBA | iBar);
243
244 pciDevSetMsixCapable(pDev);
245
246 return VINF_SUCCESS;
247}
248
249#endif /* IN_RING3 */
250
251bool MsixIsEnabled(PPDMPCIDEV pDev)
252{
253 return pciDevIsMsixCapable(pDev) && msixIsEnabled(pDev);
254}
255
256void MsixNotify(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, int iVector, int iLevel, uint32_t uTagSrc)
257{
258 AssertMsg(msixIsEnabled(pDev), ("Must be enabled to use that"));
259
260 Assert(pPciHlp->pfnIoApicSendMsi != NULL);
261
262 /* We only trigger MSI-X on level up */
263 if ((iLevel & PDM_IRQ_LEVEL_HIGH) == 0)
264 {
265 return;
266 }
267
268 // if this vector is somehow disabled
269 if (msixIsMasked(pDev) || msixIsVectorMasked(pDev, iVector))
270 {
271 // mark pending bit
272 msixSetPending(pDev, iVector);
273 return;
274 }
275
276 // clear pending bit
277 msixClearPending(pDev, iVector);
278
279 RTGCPHYS GCAddr = msixGetMsiAddress(pDev, iVector);
280 uint32_t u32Value = msixGetMsiData(pDev, iVector);
281
282 pPciHlp->pfnIoApicSendMsi(pDevIns, GCAddr, u32Value, uTagSrc);
283}
284
285#ifdef IN_RING3
286
287DECLINLINE(bool) msixR3BitJustCleared(uint32_t uOldValue, uint32_t uNewValue, uint32_t uMask)
288{
289 return !!(uOldValue & uMask) && !(uNewValue & uMask);
290}
291
292
293static void msixR3CheckPendingVectors(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev)
294{
295 for (uint32_t i = 0; i < msixTableSize(pDev); i++)
296 msixR3CheckPendingVector(pDevIns, pPciHlp, pDev, i);
297}
298
299
300void MsixR3PciConfigWrite(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPDMPCIDEV pDev, uint32_t u32Address, uint32_t val, unsigned len)
301{
302 int32_t iOff = u32Address - pDev->Int.s.u8MsixCapOffset;
303 Assert(iOff >= 0 && (pciDevIsMsixCapable(pDev) && iOff < pDev->Int.s.u8MsixCapSize));
304
305 Log2(("MsixR3PciConfigWrite: %d <- %x (%d)\n", iOff, val, len));
306
307 uint32_t uAddr = u32Address;
308 uint8_t u8NewVal;
309 bool fJustEnabled = false;
310
311 for (uint32_t i = 0; i < len; i++)
312 {
313 uint32_t reg = i + iOff;
314 uint8_t u8Val = (uint8_t)val;
315 switch (reg)
316 {
317 case 0: /* Capability ID, ro */
318 case 1: /* Next pointer, ro */
319 break;
320 case VBOX_MSIX_CAP_MESSAGE_CONTROL:
321 /* don't change read-only bits: 0-7 */
322 break;
323 case VBOX_MSIX_CAP_MESSAGE_CONTROL + 1:
324 {
325 /* don't change read-only bits 8-13 */
326 u8NewVal = (u8Val & UINT8_C(~0x3f)) | (pDev->abConfig[uAddr] & UINT8_C(0x3f));
327 /* If just enabled globally - check pending vectors */
328 fJustEnabled |= msixR3BitJustCleared(pDev->abConfig[uAddr], u8NewVal, VBOX_PCI_MSIX_FLAGS_ENABLE >> 8);
329 fJustEnabled |= msixR3BitJustCleared(pDev->abConfig[uAddr], u8NewVal, VBOX_PCI_MSIX_FLAGS_FUNCMASK >> 8);
330 pDev->abConfig[uAddr] = u8NewVal;
331 break;
332 }
333 default:
334 /* other fields read-only too */
335 break;
336 }
337 uAddr++;
338 val >>= 8;
339 }
340
341 if (fJustEnabled)
342 msixR3CheckPendingVectors(pDevIns, pPciHlp, pDev);
343}
344
345#endif /* IN_RING3 */
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