VirtualBox

source: vbox/trunk/src/VBox/VMM/include/IOMInline.h@ 81834

Last change on this file since 81834 was 81333, checked in by vboxsync, 5 years ago

IOM: More MMIO stuff, almost there now... bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: IOMInline.h 81333 2019-10-17 23:49:39Z vboxsync $ */
2/** @file
3 * IOM - Inlined functions.
4 */
5
6/*
7 * Copyright (C) 2006-2019 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#ifndef VMM_INCLUDED_SRC_include_IOMInline_h
19#define VMM_INCLUDED_SRC_include_IOMInline_h
20#ifndef RT_WITHOUT_PRAGMA_ONCE
21# pragma once
22#endif
23
24#include <iprt/errcore.h>
25
26/** @addtogroup grp_iom_int Internals
27 * @internal
28 * @{
29 */
30
31
32/**
33 * Gets the I/O port entry for the specified I/O port in the current context.
34 *
35 * @returns Pointer to I/O port entry.
36 * @returns NULL if no port registered.
37 *
38 * @param pVM The cross context VM structure.
39 * @param uPort The I/O port to lookup.
40 * @param poffPort Where to return the port offset relative to the
41 * start of the I/O port range.
42 * @param pidxLastHint Pointer to IOMCPU::idxIoPortLastRead or
43 * IOMCPU::idxIoPortLastWrite.
44 *
45 * @note In ring-0 it is possible to get an uninitialized entry (pDevIns is
46 * NULL, cPorts is 0), in which case there should be ring-3 handlers
47 * for the entry. Use IOMIOPORTENTRYR0::idxSelf to get the ring-3
48 * entry.
49 *
50 * @note This code is almost identical to iomMmioGetEntry, so keep in sync.
51 */
52DECLINLINE(CTX_SUFF(PIOMIOPORTENTRY)) iomIoPortGetEntry(PVMCC pVM, RTIOPORT uPort, PRTIOPORT poffPort, uint16_t *pidxLastHint)
53{
54 Assert(IOM_IS_SHARED_LOCK_OWNER(pVM));
55
56#ifdef IN_RING0
57 uint32_t iEnd = RT_MIN(pVM->iom.s.cIoPortLookupEntries, pVM->iomr0.s.cIoPortAlloc);
58 PCIOMIOPORTLOOKUPENTRY paLookup = pVM->iomr0.s.paIoPortLookup;
59#else
60 uint32_t iEnd = pVM->iom.s.cIoPortLookupEntries;
61 PCIOMIOPORTLOOKUPENTRY paLookup = pVM->iom.s.paIoPortLookup;
62#endif
63 if (iEnd > 0)
64 {
65 uint32_t iFirst = 0;
66 uint32_t i = *pidxLastHint;
67 if (i < iEnd)
68 { /* likely */ }
69 else
70 i = iEnd / 2;
71 for (;;)
72 {
73 PCIOMIOPORTLOOKUPENTRY pCur = &paLookup[i];
74 if (pCur->uFirstPort > uPort)
75 {
76 if (i > iFirst)
77 iEnd = i;
78 else
79 break;
80 }
81 else if (pCur->uLastPort < uPort)
82 {
83 i += 1;
84 if (i < iEnd)
85 iFirst = i;
86 else
87 break;
88 }
89 else
90 {
91 *pidxLastHint = (uint16_t)i;
92 *poffPort = uPort - pCur->uFirstPort;
93
94 /*
95 * Translate the 'idx' member into a pointer.
96 */
97 size_t const idx = pCur->idx;
98#ifdef IN_RING0
99 AssertMsg(idx < pVM->iom.s.cIoPortRegs && idx < pVM->iomr0.s.cIoPortAlloc,
100 ("%#zx vs %#x/%x (port %#x)\n", idx, pVM->iom.s.cIoPortRegs, pVM->iomr0.s.cIoPortMax, uPort));
101 if (idx < pVM->iomr0.s.cIoPortAlloc)
102 return &pVM->iomr0.s.paIoPortRegs[idx];
103#else
104 if (idx < pVM->iom.s.cIoPortRegs)
105 return &pVM->iom.s.paIoPortRegs[idx];
106 AssertMsgFailed(("%#zx vs %#x (port %#x)\n", idx, pVM->iom.s.cIoPortRegs, uPort));
107#endif
108 break;
109 }
110
111 i = iFirst + (iEnd - iFirst) / 2;
112 }
113 }
114 *poffPort = 0;
115 return NULL;
116}
117
118
119#ifdef VBOX_WITH_STATISTICS
120/**
121 * Gets the statistics entry for an I/O port.
122 *
123 * @returns Pointer to stats. Instead of NULL, a pointer to IoPortDummyStats is
124 * returned, so the caller does not need to check for NULL.
125 *
126 * @param pVM The cross context VM structure.
127 * @param pRegEntry The I/O port entry to get stats for.
128 * @param offPort The offset of the port relative to the start of the
129 * registration entry.
130 */
131DECLINLINE(PIOMIOPORTSTATSENTRY) iomIoPortGetStats(PVMCC pVM, CTX_SUFF(PIOMIOPORTENTRY) pRegEntry, uint16_t offPort)
132{
133 size_t idxStats = pRegEntry->idxStats;
134 idxStats += offPort;
135# ifdef IN_RING0
136 if (idxStats < pVM->iomr0.s.cIoPortStatsAllocation)
137 return &pVM->iomr0.s.paIoPortStats[idxStats];
138# else
139 if (idxStats < pVM->iom.s.cIoPortStats)
140 return &pVM->iom.s.paIoPortStats[idxStats];
141# endif
142 return &pVM->iom.s.IoPortDummyStats;
143}
144#endif
145
146
147/**
148 * Gets the MMIO region entry for the specified address in the current context.
149 *
150 * @returns Pointer to MMIO region entry.
151 * @returns NULL if no MMIO region registered for the given address.
152 *
153 * @param pVM The cross context VM structure.
154 * @param GCPhys The address to lookup.
155 * @param poffRegion Where to return the byte offset into the MMIO
156 * region that corresponds to @a GCPhys.
157 * @param pidxLastHint Pointer to IOMCPU::idxMmioLastRead,
158 * IOMCPU::idxMmioLastWrite, or similar.
159 *
160 * @note In ring-0 it is possible to get an uninitialized entry (pDevIns is
161 * NULL, cbRegion is 0), in which case there should be ring-3 handlers
162 * for the entry. Use IOMMMIOENTRYR0::idxSelf to get the ring-3 entry.
163 *
164 * @note This code is almost identical to iomIoPortGetEntry, so keep in sync.
165 */
166DECLINLINE(CTX_SUFF(PIOMMMIOENTRY)) iomMmioGetEntry(PVMCC pVM, RTGCPHYS GCPhys, PRTGCPHYS poffRegion, uint16_t *pidxLastHint)
167{
168 Assert(IOM_IS_SHARED_LOCK_OWNER(pVM));
169
170#ifdef IN_RING0
171 uint32_t iEnd = RT_MIN(pVM->iom.s.cMmioLookupEntries, pVM->iomr0.s.cMmioAlloc);
172 PCIOMMMIOLOOKUPENTRY paLookup = pVM->iomr0.s.paMmioLookup;
173#else
174 uint32_t iEnd = pVM->iom.s.cMmioLookupEntries;
175 PCIOMMMIOLOOKUPENTRY paLookup = pVM->iom.s.paMmioLookup;
176#endif
177 if (iEnd > 0)
178 {
179 uint32_t iFirst = 0;
180 uint32_t i = *pidxLastHint;
181 if (i < iEnd)
182 { /* likely */ }
183 else
184 i = iEnd / 2;
185 for (;;)
186 {
187 PCIOMMMIOLOOKUPENTRY pCur = &paLookup[i];
188 if (pCur->GCPhysFirst > GCPhys)
189 {
190 if (i > iFirst)
191 iEnd = i;
192 else
193 break;
194 }
195 else if (pCur->GCPhysLast < GCPhys)
196 {
197 i += 1;
198 if (i < iEnd)
199 iFirst = i;
200 else
201 break;
202 }
203 else
204 {
205 *pidxLastHint = (uint16_t)i;
206 *poffRegion = GCPhys - pCur->GCPhysFirst;
207
208 /*
209 * Translate the 'idx' member into a pointer.
210 */
211 size_t const idx = pCur->idx;
212#ifdef IN_RING0
213 AssertMsg(idx < pVM->iom.s.cMmioRegs && idx < pVM->iomr0.s.cMmioAlloc,
214 ("%#zx vs %#x/%x (GCPhys=%RGp)\n", idx, pVM->iom.s.cMmioRegs, pVM->iomr0.s.cMmioMax, GCPhys));
215 if (idx < pVM->iomr0.s.cMmioAlloc)
216 return &pVM->iomr0.s.paMmioRegs[idx];
217#else
218 if (idx < pVM->iom.s.cMmioRegs)
219 return &pVM->iom.s.paMmioRegs[idx];
220 AssertMsgFailed(("%#zx vs %#x (GCPhys=%RGp)\n", idx, pVM->iom.s.cMmioRegs, GCPhys));
221#endif
222 break;
223 }
224
225 i = iFirst + (iEnd - iFirst) / 2;
226 }
227 }
228 *poffRegion = 0;
229 return NULL;
230}
231
232
233#ifdef VBOX_WITH_STATISTICS
234/**
235 * Gets the statistics entry for an MMIO region.
236 *
237 * @returns Pointer to stats. Instead of NULL, a pointer to MmioDummyStats is
238 * returned, so the caller does not need to check for NULL.
239 *
240 * @param pVM The cross context VM structure.
241 * @param pRegEntry The I/O port entry to get stats for.
242 */
243DECLINLINE(PIOMMMIOSTATSENTRY) iomMmioGetStats(PVMCC pVM, CTX_SUFF(PIOMMMIOENTRY) pRegEntry)
244{
245 size_t idxStats = pRegEntry->idxStats;
246# ifdef IN_RING0
247 if (idxStats < pVM->iomr0.s.cMmioStatsAllocation)
248 return &pVM->iomr0.s.paMmioStats[idxStats];
249# else
250 if (idxStats < pVM->iom.s.cMmioStats)
251 return &pVM->iom.s.paMmioStats[idxStats];
252# endif
253 return &pVM->iom.s.MmioDummyStats;
254}
255#endif
256
257
258/**
259 * Gets the I/O port range for the specified I/O port in the current context.
260 *
261 * @returns Pointer to I/O port range.
262 * @returns NULL if no port registered.
263 *
264 * @param pVM The cross context VM structure.
265 * @param Port The I/O port lookup.
266 */
267DECLINLINE(CTX_SUFF(PIOMIOPORTRANGE)) iomIOPortGetRange(PVM pVM, RTIOPORT Port)
268{
269 Assert(IOM_IS_SHARED_LOCK_OWNER(pVM));
270 return (CTX_SUFF(PIOMIOPORTRANGE))RTAvlroIOPortRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->CTX_SUFF(IOPortTree), Port);
271}
272
273
274/**
275 * Gets the I/O port range for the specified I/O port in the HC.
276 *
277 * @returns Pointer to I/O port range.
278 * @returns NULL if no port registered.
279 *
280 * @param pVM The cross context VM structure.
281 * @param Port The I/O port to lookup.
282 */
283DECLINLINE(PIOMIOPORTRANGER3) iomIOPortGetRangeR3(PVM pVM, RTIOPORT Port)
284{
285 Assert(IOM_IS_SHARED_LOCK_OWNER(pVM));
286 return (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortTreeR3, Port);
287}
288
289
290/**
291 * Gets the MMIO range for the specified physical address in the current context.
292 *
293 * @returns Pointer to MMIO range.
294 * @returns NULL if address not in a MMIO range.
295 *
296 * @param pVM The cross context VM structure.
297 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
298 * @param GCPhys Physical address to lookup.
299 */
300DECLINLINE(PIOMMMIORANGE) iomMmioGetRange(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys)
301{
302 Assert(IOM_IS_SHARED_LOCK_OWNER(pVM));
303 PIOMMMIORANGE pRange = pVCpu->iom.s.CTX_SUFF(pMMIORangeLast);
304 if ( !pRange
305 || GCPhys - pRange->GCPhys >= pRange->cb)
306 pVCpu->iom.s.CTX_SUFF(pMMIORangeLast) = pRange
307 = (PIOMMMIORANGE)RTAvlroGCPhysRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->MMIOTree, GCPhys);
308 return pRange;
309}
310
311/**
312 * Retain a MMIO range.
313 *
314 * @param pRange The range to release.
315 */
316DECLINLINE(void) iomMmioRetainRange(PIOMMMIORANGE pRange)
317{
318 uint32_t cRefs = ASMAtomicIncU32(&pRange->cRefs);
319 Assert(cRefs > 1);
320 Assert(cRefs < _1M);
321 NOREF(cRefs);
322}
323
324
325/**
326 * Gets the referenced MMIO range for the specified physical address in the
327 * current context.
328 *
329 * @returns Pointer to MMIO range.
330 * @returns NULL if address not in a MMIO range.
331 *
332 * @param pVM The cross context VM structure.
333 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
334 * @param GCPhys Physical address to lookup.
335 */
336DECLINLINE(PIOMMMIORANGE) iomMmioGetRangeWithRef(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys)
337{
338 int rc = IOM_LOCK_SHARED_EX(pVM, VINF_SUCCESS);
339 AssertRCReturn(rc, NULL);
340
341 PIOMMMIORANGE pRange = pVCpu->iom.s.CTX_SUFF(pMMIORangeLast);
342 if ( !pRange
343 || GCPhys - pRange->GCPhys >= pRange->cb)
344 pVCpu->iom.s.CTX_SUFF(pMMIORangeLast) = pRange
345 = (PIOMMMIORANGE)RTAvlroGCPhysRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->MMIOTree, GCPhys);
346 if (pRange)
347 iomMmioRetainRange(pRange);
348
349 IOM_UNLOCK_SHARED(pVM);
350 return pRange;
351}
352
353
354/**
355 * Releases a MMIO range.
356 *
357 * @param pVM The cross context VM structure.
358 * @param pRange The range to release.
359 */
360DECLINLINE(void) iomMmioReleaseRange(PVMCC pVM, PIOMMMIORANGE pRange)
361{
362 uint32_t cRefs = ASMAtomicDecU32(&pRange->cRefs);
363 if (!cRefs)
364 iomMmioFreeRange(pVM, pRange);
365}
366
367
368#ifdef VBOX_STRICT
369/**
370 * Gets the MMIO range for the specified physical address in the current context.
371 *
372 * @returns Pointer to MMIO range.
373 * @returns NULL if address not in a MMIO range.
374 *
375 * @param pVM The cross context VM structure.
376 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
377 * @param GCPhys Physical address to lookup.
378 */
379DECLINLINE(PIOMMMIORANGE) iomMMIOGetRangeUnsafe(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys)
380{
381 PIOMMMIORANGE pRange = pVCpu->iom.s.CTX_SUFF(pMMIORangeLast);
382 if ( !pRange
383 || GCPhys - pRange->GCPhys >= pRange->cb)
384 pVCpu->iom.s.CTX_SUFF(pMMIORangeLast) = pRange
385 = (PIOMMMIORANGE)RTAvlroGCPhysRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->MMIOTree, GCPhys);
386 return pRange;
387}
388#endif /* VBOX_STRICT */
389
390
391#ifdef VBOX_WITH_STATISTICS
392/**
393 * Gets the MMIO statistics record.
394 *
395 * In ring-3 this will lazily create missing records, while in GC/R0 the caller has to
396 * return the appropriate status to defer the operation to ring-3.
397 *
398 * @returns Pointer to MMIO stats.
399 * @returns NULL if not found (R0/GC), or out of memory (R3).
400 *
401 * @param pVM The cross context VM structure.
402 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
403 * @param GCPhys Physical address to lookup.
404 * @param pRange The MMIO range.
405 *
406 * @remarks The caller holds the IOM critical section with shared access prior
407 * to calling this method. Upon return, the lock has been released!
408 * This is ugly, but it's a necessary evil since we cannot upgrade read
409 * locks to write locks and the whole purpose here is calling
410 * iomR3MMIOStatsCreate.
411 */
412DECLINLINE(PIOMMMIOSTATS) iomMmioGetStats(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, PIOMMMIORANGE pRange)
413{
414 Assert(IOM_IS_SHARED_LOCK_OWNER(pVM));
415
416 /* For large ranges, we'll put everything on the first byte. */
417 if (pRange->cb > PAGE_SIZE)
418 GCPhys = pRange->GCPhys;
419
420 PIOMMMIOSTATS pStats = pVCpu->iom.s.CTX_SUFF(pMMIOStatsLast);
421 if ( !pStats
422 || pStats->Core.Key != GCPhys)
423 {
424 pStats = (PIOMMMIOSTATS)RTAvloGCPhysGet(&pVM->iom.s.CTX_SUFF(pTrees)->MmioStatTree, GCPhys);
425# ifdef IN_RING3
426 if (!pStats)
427 {
428 IOM_UNLOCK_SHARED(pVM);
429 return iomR3MMIOStatsCreate(pVM, GCPhys, pRange->pszDesc);
430 }
431# endif
432 }
433
434 IOM_UNLOCK_SHARED(pVM);
435 return pStats;
436}
437#endif /* VBOX_WITH_STATISTICS */
438
439
440/** @} */
441
442#endif /* !VMM_INCLUDED_SRC_include_IOMInline_h */
443
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