VirtualBox

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

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

IOM,PDMDevHlp: Kicked out the old I/O port code. bugref:9218

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.2 KB
Line 
1/* $Id: IOMInline.h 82311 2019-12-01 01:45:02Z 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 MMIO range for the specified physical address in the current context.
260 *
261 * @returns Pointer to MMIO range.
262 * @returns NULL if address not in a MMIO range.
263 *
264 * @param pVM The cross context VM structure.
265 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
266 * @param GCPhys Physical address to lookup.
267 */
268DECLINLINE(PIOMMMIORANGE) iomMmioGetRange(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys)
269{
270 Assert(IOM_IS_SHARED_LOCK_OWNER(pVM));
271 PIOMMMIORANGE pRange = pVCpu->iom.s.CTX_SUFF(pMMIORangeLast);
272 if ( !pRange
273 || GCPhys - pRange->GCPhys >= pRange->cb)
274 pVCpu->iom.s.CTX_SUFF(pMMIORangeLast) = pRange
275 = (PIOMMMIORANGE)RTAvlroGCPhysRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->MMIOTree, GCPhys);
276 return pRange;
277}
278
279/**
280 * Retain a MMIO range.
281 *
282 * @param pRange The range to release.
283 */
284DECLINLINE(void) iomMmioRetainRange(PIOMMMIORANGE pRange)
285{
286 uint32_t cRefs = ASMAtomicIncU32(&pRange->cRefs);
287 Assert(cRefs > 1);
288 Assert(cRefs < _1M);
289 NOREF(cRefs);
290}
291
292
293/**
294 * Gets the referenced MMIO range for the specified physical address in the
295 * current context.
296 *
297 * @returns Pointer to MMIO range.
298 * @returns NULL if address not in a MMIO range.
299 *
300 * @param pVM The cross context VM structure.
301 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
302 * @param GCPhys Physical address to lookup.
303 */
304DECLINLINE(PIOMMMIORANGE) iomMmioGetRangeWithRef(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys)
305{
306 int rc = IOM_LOCK_SHARED_EX(pVM, VINF_SUCCESS);
307 AssertRCReturn(rc, NULL);
308
309 PIOMMMIORANGE pRange = pVCpu->iom.s.CTX_SUFF(pMMIORangeLast);
310 if ( !pRange
311 || GCPhys - pRange->GCPhys >= pRange->cb)
312 pVCpu->iom.s.CTX_SUFF(pMMIORangeLast) = pRange
313 = (PIOMMMIORANGE)RTAvlroGCPhysRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->MMIOTree, GCPhys);
314 if (pRange)
315 iomMmioRetainRange(pRange);
316
317 IOM_UNLOCK_SHARED(pVM);
318 return pRange;
319}
320
321
322/**
323 * Releases a MMIO range.
324 *
325 * @param pVM The cross context VM structure.
326 * @param pRange The range to release.
327 */
328DECLINLINE(void) iomMmioReleaseRange(PVMCC pVM, PIOMMMIORANGE pRange)
329{
330 uint32_t cRefs = ASMAtomicDecU32(&pRange->cRefs);
331 if (!cRefs)
332 iomMmioFreeRange(pVM, pRange);
333}
334
335
336#ifdef VBOX_STRICT
337/**
338 * Gets the MMIO range for the specified physical address in the current context.
339 *
340 * @returns Pointer to MMIO range.
341 * @returns NULL if address not in a MMIO range.
342 *
343 * @param pVM The cross context VM structure.
344 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
345 * @param GCPhys Physical address to lookup.
346 */
347DECLINLINE(PIOMMMIORANGE) iomMMIOGetRangeUnsafe(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys)
348{
349 PIOMMMIORANGE pRange = pVCpu->iom.s.CTX_SUFF(pMMIORangeLast);
350 if ( !pRange
351 || GCPhys - pRange->GCPhys >= pRange->cb)
352 pVCpu->iom.s.CTX_SUFF(pMMIORangeLast) = pRange
353 = (PIOMMMIORANGE)RTAvlroGCPhysRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->MMIOTree, GCPhys);
354 return pRange;
355}
356#endif /* VBOX_STRICT */
357
358
359#ifdef VBOX_WITH_STATISTICS
360/**
361 * Gets the MMIO statistics record.
362 *
363 * In ring-3 this will lazily create missing records, while in GC/R0 the caller has to
364 * return the appropriate status to defer the operation to ring-3.
365 *
366 * @returns Pointer to MMIO stats.
367 * @returns NULL if not found (R0/GC), or out of memory (R3).
368 *
369 * @param pVM The cross context VM structure.
370 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
371 * @param GCPhys Physical address to lookup.
372 * @param pRange The MMIO range.
373 *
374 * @remarks The caller holds the IOM critical section with shared access prior
375 * to calling this method. Upon return, the lock has been released!
376 * This is ugly, but it's a necessary evil since we cannot upgrade read
377 * locks to write locks and the whole purpose here is calling
378 * iomR3MMIOStatsCreate.
379 */
380DECLINLINE(PIOMMMIOSTATS) iomMmioGetStats(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhys, PIOMMMIORANGE pRange)
381{
382 Assert(IOM_IS_SHARED_LOCK_OWNER(pVM));
383
384 /* For large ranges, we'll put everything on the first byte. */
385 if (pRange->cb > PAGE_SIZE)
386 GCPhys = pRange->GCPhys;
387
388 PIOMMMIOSTATS pStats = pVCpu->iom.s.CTX_SUFF(pMMIOStatsLast);
389 if ( !pStats
390 || pStats->Core.Key != GCPhys)
391 {
392 pStats = (PIOMMMIOSTATS)RTAvloGCPhysGet(&pVM->iom.s.CTX_SUFF(pTrees)->MmioStatTree, GCPhys);
393# ifdef IN_RING3
394 if (!pStats)
395 {
396 IOM_UNLOCK_SHARED(pVM);
397 return iomR3MMIOStatsCreate(pVM, GCPhys, pRange->pszDesc);
398 }
399# endif
400 }
401
402 IOM_UNLOCK_SHARED(pVM);
403 return pStats;
404}
405#endif /* VBOX_WITH_STATISTICS */
406
407
408/** @} */
409
410#endif /* !VMM_INCLUDED_SRC_include_IOMInline_h */
411
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