VirtualBox

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

Last change on this file since 39837 was 39135, checked in by vboxsync, 13 years ago

Changed PDMDevHlpMMIORegister to take flags and drop pfnFill. Added PDMDevHlpMMIORegisterEx for the one user of pfnFill.

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