VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/IOM.cpp@ 80090

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

VMM: Kicking out raw-mode - IOM. bugref:9517

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 94.7 KB
Line 
1/* $Id: IOM.cpp 80090 2019-07-31 21:20:57Z vboxsync $ */
2/** @file
3 * IOM - Input / Output Monitor.
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
19/** @page pg_iom IOM - The Input / Output Monitor
20 *
21 * The input/output monitor will handle I/O exceptions routing them to the
22 * appropriate device. It implements an API to register and deregister virtual
23 * I/0 port handlers and memory mapped I/O handlers. A handler is PDM devices
24 * and a set of callback functions.
25 *
26 * @see grp_iom
27 *
28 *
29 * @section sec_iom_rawmode Raw-Mode
30 *
31 * In raw-mode I/O port access is trapped (\#GP(0)) by ensuring that the actual
32 * IOPL is 0 regardless of what the guest IOPL is. The \#GP handler use the
33 * disassembler (DIS) to figure which instruction caused it (there are a number
34 * of instructions in addition to the I/O ones) and if it's an I/O port access
35 * it will hand it to IOMRCIOPortHandler (via EMInterpretPortIO).
36 * IOMRCIOPortHandler will lookup the port in the AVL tree of registered
37 * handlers. If found, the handler will be called otherwise default action is
38 * taken. (Default action is to write into the void and read all set bits.)
39 *
40 * Memory Mapped I/O (MMIO) is implemented as a slightly special case of PGM
41 * access handlers. An MMIO range is registered with IOM which then registers it
42 * with the PGM access handler sub-system. The access handler catches all
43 * access and will be called in the context of a \#PF handler. In RC and R0 this
44 * handler is iomMmioPfHandler while in ring-3 it's iomR3MmioHandler (although
45 * in ring-3 there can be alternative ways). iomMmioPfHandler will attempt to
46 * emulate the instruction that is doing the access and pass the corresponding
47 * reads / writes to the device.
48 *
49 * Emulating I/O port access is less complex and should be slightly faster than
50 * emulating MMIO, so in most cases we should encourage the OS to use port I/O.
51 * Devices which are frequently accessed should register GC handlers to speed up
52 * execution.
53 *
54 *
55 * @section sec_iom_hm Hardware Assisted Virtualization Mode
56 *
57 * When running in hardware assisted virtualization mode we'll be doing much the
58 * same things as in raw-mode. The main difference is that we're running in the
59 * host ring-0 context and that we don't get faults (\#GP(0) and \#PG) but
60 * exits.
61 *
62 *
63 * @section sec_iom_rem Recompiled Execution Mode
64 *
65 * When running in the recompiler things are different. I/O port access is
66 * handled by calling IOMIOPortRead and IOMIOPortWrite directly. While MMIO can
67 * be handled in one of two ways. The normal way is that we have a registered a
68 * special RAM range with the recompiler and in the three callbacks (for byte,
69 * word and dword access) we call IOMMMIORead and IOMMMIOWrite directly. The
70 * alternative ways that the physical memory access which goes via PGM will take
71 * care of it by calling iomR3MmioHandler via the PGM access handler machinery
72 * - this shouldn't happen but it is an alternative...
73 *
74 *
75 * @section sec_iom_other Other Accesses
76 *
77 * I/O ports aren't really exposed in any other way, unless you count the
78 * instruction interpreter in EM, but that's just what we're doing in the
79 * raw-mode \#GP(0) case really. Now, it's possible to call IOMIOPortRead and
80 * IOMIOPortWrite directly to talk to a device, but this is really bad behavior
81 * and should only be done as temporary hacks (the PC BIOS device used to setup
82 * the CMOS this way back in the dark ages).
83 *
84 * MMIO has similar direct routes as the I/O ports and these shouldn't be used
85 * for the same reasons and with the same restrictions. OTOH since MMIO is
86 * mapped into the physical memory address space, it can be accessed in a number
87 * of ways thru PGM.
88 *
89 *
90 * @section sec_iom_logging Logging Levels
91 *
92 * Following assignments:
93 * - Level 5 is used for defering I/O port and MMIO writes to ring-3.
94 *
95 */
96
97/** @todo MMIO - simplifying the device end.
98 * - Add a return status for doing DBGFSTOP on access where there are no known
99 * registers.
100 * -
101 *
102 * */
103
104
105/*********************************************************************************************************************************
106* Header Files *
107*********************************************************************************************************************************/
108#define LOG_GROUP LOG_GROUP_IOM
109#include <VBox/vmm/iom.h>
110#include <VBox/vmm/cpum.h>
111#include <VBox/vmm/pgm.h>
112#include <VBox/sup.h>
113#include <VBox/vmm/hm.h>
114#include <VBox/vmm/mm.h>
115#include <VBox/vmm/stam.h>
116#include <VBox/vmm/dbgf.h>
117#include <VBox/vmm/pdmapi.h>
118#include <VBox/vmm/pdmdev.h>
119#include "IOMInternal.h"
120#include <VBox/vmm/vm.h>
121
122#include <VBox/param.h>
123#include <iprt/assert.h>
124#include <iprt/alloc.h>
125#include <iprt/string.h>
126#include <VBox/log.h>
127#include <VBox/err.h>
128
129#include "IOMInline.h"
130
131
132/*********************************************************************************************************************************
133* Internal Functions *
134*********************************************************************************************************************************/
135static void iomR3FlushCache(PVM pVM);
136static DECLCALLBACK(int) iomR3RelocateIOPortCallback(PAVLROIOPORTNODECORE pNode, void *pvUser);
137static DECLCALLBACK(int) iomR3RelocateMMIOCallback(PAVLROGCPHYSNODECORE pNode, void *pvUser);
138static DECLCALLBACK(void) iomR3IOPortInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
139static DECLCALLBACK(void) iomR3MMIOInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
140static FNIOMIOPORTIN iomR3IOPortDummyIn;
141static FNIOMIOPORTOUT iomR3IOPortDummyOut;
142static FNIOMIOPORTINSTRING iomR3IOPortDummyInStr;
143static FNIOMIOPORTOUTSTRING iomR3IOPortDummyOutStr;
144
145#ifdef VBOX_WITH_STATISTICS
146static const char *iomR3IOPortGetStandardName(RTIOPORT Port);
147#endif
148
149
150/**
151 * Initializes the IOM.
152 *
153 * @returns VBox status code.
154 * @param pVM The cross context VM structure.
155 */
156VMMR3_INT_DECL(int) IOMR3Init(PVM pVM)
157{
158 LogFlow(("IOMR3Init:\n"));
159
160 /*
161 * Assert alignment and sizes.
162 */
163 AssertCompileMemberAlignment(VM, iom.s, 32);
164 AssertCompile(sizeof(pVM->iom.s) <= sizeof(pVM->iom.padding));
165 AssertCompileMemberAlignment(IOM, CritSect, sizeof(uintptr_t));
166
167 /*
168 * Initialize the REM critical section.
169 */
170#ifdef IOM_WITH_CRIT_SECT_RW
171 int rc = PDMR3CritSectRwInit(pVM, &pVM->iom.s.CritSect, RT_SRC_POS, "IOM Lock");
172#else
173 int rc = PDMR3CritSectInit(pVM, &pVM->iom.s.CritSect, RT_SRC_POS, "IOM Lock");
174#endif
175 AssertRCReturn(rc, rc);
176
177 /*
178 * Allocate the trees structure.
179 */
180 rc = MMHyperAlloc(pVM, sizeof(*pVM->iom.s.pTreesR3), 0, MM_TAG_IOM, (void **)&pVM->iom.s.pTreesR3);
181 if (RT_SUCCESS(rc))
182 {
183 pVM->iom.s.pTreesR0 = MMHyperR3ToR0(pVM, pVM->iom.s.pTreesR3);
184
185 /*
186 * Register the MMIO access handler type.
187 */
188 rc = PGMR3HandlerPhysicalTypeRegister(pVM, PGMPHYSHANDLERKIND_MMIO,
189 iomMmioHandler,
190 NULL, "iomMmioHandler", "iomMmioPfHandler",
191 NULL, "iomMmioHandler", "iomMmioPfHandler",
192 "MMIO", &pVM->iom.s.hMmioHandlerType);
193 AssertRC(rc);
194 if (RT_SUCCESS(rc))
195 {
196
197 /*
198 * Info.
199 */
200 DBGFR3InfoRegisterInternal(pVM, "ioport", "Dumps all IOPort ranges. No arguments.", &iomR3IOPortInfo);
201 DBGFR3InfoRegisterInternal(pVM, "mmio", "Dumps all MMIO ranges. No arguments.", &iomR3MMIOInfo);
202
203 /*
204 * Statistics.
205 */
206 STAM_REG(pVM, &pVM->iom.s.StatRZMMIOHandler, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler", STAMUNIT_TICKS_PER_CALL, "Profiling of the iomMmioPfHandler() body, only success calls.");
207 STAM_REG(pVM, &pVM->iom.s.StatRZMMIO1Byte, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Access1", STAMUNIT_OCCURENCES, "MMIO access by 1 byte counter.");
208 STAM_REG(pVM, &pVM->iom.s.StatRZMMIO2Bytes, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Access2", STAMUNIT_OCCURENCES, "MMIO access by 2 bytes counter.");
209 STAM_REG(pVM, &pVM->iom.s.StatRZMMIO4Bytes, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Access4", STAMUNIT_OCCURENCES, "MMIO access by 4 bytes counter.");
210 STAM_REG(pVM, &pVM->iom.s.StatRZMMIO8Bytes, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Access8", STAMUNIT_OCCURENCES, "MMIO access by 8 bytes counter.");
211 STAM_REG(pVM, &pVM->iom.s.StatRZMMIOFailures, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/MMIOFailures", STAMUNIT_OCCURENCES, "Number of times iomMmioPfHandler() didn't service the request.");
212 STAM_REG(pVM, &pVM->iom.s.StatRZInstMov, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/MOV", STAMUNIT_TICKS_PER_CALL, "Profiling of the MOV instruction emulation.");
213 STAM_REG(pVM, &pVM->iom.s.StatRZInstCmp, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/CMP", STAMUNIT_TICKS_PER_CALL, "Profiling of the CMP instruction emulation.");
214 STAM_REG(pVM, &pVM->iom.s.StatRZInstAnd, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/AND", STAMUNIT_TICKS_PER_CALL, "Profiling of the AND instruction emulation.");
215 STAM_REG(pVM, &pVM->iom.s.StatRZInstOr, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/OR", STAMUNIT_TICKS_PER_CALL, "Profiling of the OR instruction emulation.");
216 STAM_REG(pVM, &pVM->iom.s.StatRZInstXor, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/XOR", STAMUNIT_TICKS_PER_CALL, "Profiling of the XOR instruction emulation.");
217 STAM_REG(pVM, &pVM->iom.s.StatRZInstBt, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/BT", STAMUNIT_TICKS_PER_CALL, "Profiling of the BT instruction emulation.");
218 STAM_REG(pVM, &pVM->iom.s.StatRZInstTest, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/TEST", STAMUNIT_TICKS_PER_CALL, "Profiling of the TEST instruction emulation.");
219 STAM_REG(pVM, &pVM->iom.s.StatRZInstXchg, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/XCHG", STAMUNIT_TICKS_PER_CALL, "Profiling of the XCHG instruction emulation.");
220 STAM_REG(pVM, &pVM->iom.s.StatRZInstStos, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/STOS", STAMUNIT_TICKS_PER_CALL, "Profiling of the STOS instruction emulation.");
221 STAM_REG(pVM, &pVM->iom.s.StatRZInstLods, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/LODS", STAMUNIT_TICKS_PER_CALL, "Profiling of the LODS instruction emulation.");
222#ifdef IOM_WITH_MOVS_SUPPORT
223 STAM_REG(pVM, &pVM->iom.s.StatRZInstMovs, STAMTYPE_PROFILE_ADV, "/IOM/RZ-MMIOHandler/Inst/MOVS", STAMUNIT_TICKS_PER_CALL, "Profiling of the MOVS instruction emulation.");
224 STAM_REG(pVM, &pVM->iom.s.StatRZInstMovsToMMIO, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/MOVS/ToMMIO", STAMUNIT_TICKS_PER_CALL, "Profiling of the MOVS instruction emulation - Mem2MMIO.");
225 STAM_REG(pVM, &pVM->iom.s.StatRZInstMovsFromMMIO, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/MOVS/FromMMIO", STAMUNIT_TICKS_PER_CALL, "Profiling of the MOVS instruction emulation - MMIO2Mem.");
226 STAM_REG(pVM, &pVM->iom.s.StatRZInstMovsMMIO, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/MOVS/MMIO2MMIO", STAMUNIT_TICKS_PER_CALL, "Profiling of the MOVS instruction emulation - MMIO2MMIO.");
227#endif
228 STAM_REG(pVM, &pVM->iom.s.StatRZInstOther, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Inst/Other", STAMUNIT_OCCURENCES, "Other instructions counter.");
229 STAM_REG(pVM, &pVM->iom.s.StatR3MMIOHandler, STAMTYPE_COUNTER, "/IOM/R3-MMIOHandler", STAMUNIT_OCCURENCES, "Number of calls to iomR3MmioHandler.");
230 STAM_REG(pVM, &pVM->iom.s.StatInstIn, STAMTYPE_COUNTER, "/IOM/IOWork/In", STAMUNIT_OCCURENCES, "Counter of any IN instructions.");
231 STAM_REG(pVM, &pVM->iom.s.StatInstOut, STAMTYPE_COUNTER, "/IOM/IOWork/Out", STAMUNIT_OCCURENCES, "Counter of any OUT instructions.");
232 STAM_REG(pVM, &pVM->iom.s.StatInstIns, STAMTYPE_COUNTER, "/IOM/IOWork/Ins", STAMUNIT_OCCURENCES, "Counter of any INS instructions.");
233 STAM_REG(pVM, &pVM->iom.s.StatInstOuts, STAMTYPE_COUNTER, "/IOM/IOWork/Outs", STAMUNIT_OCCURENCES, "Counter of any OUTS instructions.");
234 }
235 }
236
237 /* Redundant, but just in case we change something in the future */
238 iomR3FlushCache(pVM);
239
240 LogFlow(("IOMR3Init: returns %Rrc\n", rc));
241 return rc;
242}
243
244
245/**
246 * Flushes the IOM port & statistics lookup cache
247 *
248 * @param pVM The cross context VM structure.
249 */
250static void iomR3FlushCache(PVM pVM)
251{
252 /*
253 * Since all relevant (1) cache use requires at least read access to the
254 * critical section, we can exclude all other EMTs by grabbing exclusive
255 * access to the critical section and then safely update the caches of
256 * other EMTs.
257 * (1) The irrelvant access not holding the lock is in assertion code.
258 */
259 IOM_LOCK_EXCL(pVM);
260 VMCPUID iCpu = pVM->cCpus;
261 while (iCpu-- > 0)
262 {
263 PVMCPU pVCpu = &pVM->aCpus[iCpu];
264 pVCpu->iom.s.pRangeLastReadR0 = NIL_RTR0PTR;
265 pVCpu->iom.s.pRangeLastWriteR0 = NIL_RTR0PTR;
266 pVCpu->iom.s.pStatsLastReadR0 = NIL_RTR0PTR;
267 pVCpu->iom.s.pStatsLastWriteR0 = NIL_RTR0PTR;
268 pVCpu->iom.s.pMMIORangeLastR0 = NIL_RTR0PTR;
269 pVCpu->iom.s.pMMIOStatsLastR0 = NIL_RTR0PTR;
270
271 pVCpu->iom.s.pRangeLastReadR3 = NULL;
272 pVCpu->iom.s.pRangeLastWriteR3 = NULL;
273 pVCpu->iom.s.pStatsLastReadR3 = NULL;
274 pVCpu->iom.s.pStatsLastWriteR3 = NULL;
275 pVCpu->iom.s.pMMIORangeLastR3 = NULL;
276 pVCpu->iom.s.pMMIOStatsLastR3 = NULL;
277 }
278
279 IOM_UNLOCK_EXCL(pVM);
280}
281
282
283/**
284 * The VM is being reset.
285 *
286 * @param pVM The cross context VM structure.
287 */
288VMMR3_INT_DECL(void) IOMR3Reset(PVM pVM)
289{
290 iomR3FlushCache(pVM);
291}
292
293
294/**
295 * Applies relocations to data and code managed by this
296 * component. This function will be called at init and
297 * whenever the VMM need to relocate it self inside the GC.
298 *
299 * The IOM will update the addresses used by the switcher.
300 *
301 * @param pVM The cross context VM structure.
302 * @param offDelta Relocation delta relative to old location.
303 */
304VMMR3_INT_DECL(void) IOMR3Relocate(PVM pVM, RTGCINTPTR offDelta)
305{
306#if 0
307 LogFlow(("IOMR3Relocate: offDelta=%d\n", offDelta));
308
309 /*
310 * Apply relocations to the GC callbacks.
311 */
312 pVM->iom.s.pTreesRC = MMHyperR3ToRC(pVM, pVM->iom.s.pTreesR3);
313 RTAvlroIOPortDoWithAll(&pVM->iom.s.pTreesR3->IOPortTreeRC, true, iomR3RelocateIOPortCallback, &offDelta);
314 RTAvlroGCPhysDoWithAll(&pVM->iom.s.pTreesR3->MMIOTree, true, iomR3RelocateMMIOCallback, &offDelta);
315
316 /*
317 * Reset the raw-mode cache (don't bother relocating it).
318 */
319 VMCPUID iCpu = pVM->cCpus;
320 while (iCpu-- > 0)
321 {
322 PVMCPU pVCpu = &pVM->aCpus[iCpu];
323 pVCpu->iom.s.pRangeLastReadRC = NIL_RTRCPTR;
324 pVCpu->iom.s.pRangeLastWriteRC = NIL_RTRCPTR;
325 pVCpu->iom.s.pStatsLastReadRC = NIL_RTRCPTR;
326 pVCpu->iom.s.pStatsLastWriteRC = NIL_RTRCPTR;
327 pVCpu->iom.s.pMMIORangeLastRC = NIL_RTRCPTR;
328 pVCpu->iom.s.pMMIOStatsLastRC = NIL_RTRCPTR;
329 }
330#else
331 RT_NOREF(pVM, offDelta);
332#endif
333}
334
335
336/**
337 * Callback function for relocating a I/O port range.
338 *
339 * @returns 0 (continue enum)
340 * @param pNode Pointer to a IOMIOPORTRANGERC node.
341 * @param pvUser Pointer to the offDelta. This is a pointer to the delta since we're
342 * not certain the delta will fit in a void pointer for all possible configs.
343 */
344static DECLCALLBACK(int) iomR3RelocateIOPortCallback(PAVLROIOPORTNODECORE pNode, void *pvUser)
345{
346 PIOMIOPORTRANGERC pRange = (PIOMIOPORTRANGERC)pNode;
347 RTGCINTPTR offDelta = *(PRTGCINTPTR)pvUser;
348
349 Assert(pRange->pDevIns);
350 pRange->pDevIns += offDelta;
351 if (pRange->pfnOutCallback)
352 pRange->pfnOutCallback += offDelta;
353 if (pRange->pfnInCallback)
354 pRange->pfnInCallback += offDelta;
355 if (pRange->pfnOutStrCallback)
356 pRange->pfnOutStrCallback += offDelta;
357 if (pRange->pfnInStrCallback)
358 pRange->pfnInStrCallback += offDelta;
359 if (pRange->pvUser > _64K)
360 pRange->pvUser += offDelta;
361 return 0;
362}
363
364
365#if 0
366/**
367 * Callback function for relocating a MMIO range.
368 *
369 * @returns 0 (continue enum)
370 * @param pNode Pointer to a IOMMMIORANGE node.
371 * @param pvUser Pointer to the offDelta. This is a pointer to the delta since we're
372 * not certain the delta will fit in a void pointer for all possible configs.
373 */
374static DECLCALLBACK(int) iomR3RelocateMMIOCallback(PAVLROGCPHYSNODECORE pNode, void *pvUser)
375{
376 PIOMMMIORANGE pRange = (PIOMMMIORANGE)pNode;
377 RTGCINTPTR offDelta = *(PRTGCINTPTR)pvUser;
378
379 if (pRange->pDevInsRC)
380 pRange->pDevInsRC += offDelta;
381 if (pRange->pfnWriteCallbackRC)
382 pRange->pfnWriteCallbackRC += offDelta;
383 if (pRange->pfnReadCallbackRC)
384 pRange->pfnReadCallbackRC += offDelta;
385 if (pRange->pfnFillCallbackRC)
386 pRange->pfnFillCallbackRC += offDelta;
387 if (pRange->pvUserRC > _64K)
388 pRange->pvUserRC += offDelta;
389
390 return 0;
391}
392#endif
393
394
395/**
396 * Terminates the IOM.
397 *
398 * Termination means cleaning up and freeing all resources,
399 * the VM it self is at this point powered off or suspended.
400 *
401 * @returns VBox status code.
402 * @param pVM The cross context VM structure.
403 */
404VMMR3_INT_DECL(int) IOMR3Term(PVM pVM)
405{
406 /*
407 * IOM is not owning anything but automatically freed resources,
408 * so there's nothing to do here.
409 */
410 NOREF(pVM);
411 return VINF_SUCCESS;
412}
413
414#ifdef VBOX_WITH_STATISTICS
415
416/**
417 * Create the statistics node for an I/O port.
418 *
419 * @returns Pointer to new stats node.
420 *
421 * @param pVM The cross context VM structure.
422 * @param Port Port.
423 * @param pszDesc Description.
424 */
425static PIOMIOPORTSTATS iomR3IOPortStatsCreate(PVM pVM, RTIOPORT Port, const char *pszDesc)
426{
427 IOM_LOCK_EXCL(pVM);
428
429 /* check if it already exists. */
430 PIOMIOPORTSTATS pPort = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.pTreesR3->IOPortStatTree, Port);
431 if (pPort)
432 {
433 IOM_UNLOCK_EXCL(pVM);
434 return pPort;
435 }
436
437 /* allocate stats node. */
438 int rc = MMHyperAlloc(pVM, sizeof(*pPort), 0, MM_TAG_IOM_STATS, (void **)&pPort);
439 AssertRC(rc);
440 if (RT_SUCCESS(rc))
441 {
442 /* insert into the tree. */
443 pPort->Core.Key = Port;
444 if (RTAvloIOPortInsert(&pVM->iom.s.pTreesR3->IOPortStatTree, &pPort->Core))
445 {
446 IOM_UNLOCK_EXCL(pVM);
447
448 /* put a name on common ports. */
449 if (!pszDesc)
450 pszDesc = iomR3IOPortGetStandardName(Port);
451
452 /* register the statistics counters. */
453 rc = STAMR3RegisterF(pVM, &pPort->InR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-In-R3", Port); AssertRC(rc);
454 rc = STAMR3RegisterF(pVM, &pPort->OutR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-Out-R3", Port); AssertRC(rc);
455 rc = STAMR3RegisterF(pVM, &pPort->InRZ, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-In-RZ", Port); AssertRC(rc);
456 rc = STAMR3RegisterF(pVM, &pPort->OutRZ, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-Out-RZ", Port); AssertRC(rc);
457 rc = STAMR3RegisterF(pVM, &pPort->InRZToR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-In-RZtoR3", Port); AssertRC(rc);
458 rc = STAMR3RegisterF(pVM, &pPort->OutRZToR3,STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-Out-RZtoR3", Port); AssertRC(rc);
459
460 /* Profiling */
461 rc = STAMR3RegisterF(pVM, &pPort->ProfInR3, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc,"/IOM/Ports/%04x-In-R3/Prof", Port); AssertRC(rc);
462 rc = STAMR3RegisterF(pVM, &pPort->ProfOutR3,STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc,"/IOM/Ports/%04x-Out-R3/Prof", Port); AssertRC(rc);
463 rc = STAMR3RegisterF(pVM, &pPort->ProfInRZ, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc,"/IOM/Ports/%04x-In-RZ/Prof", Port); AssertRC(rc);
464 rc = STAMR3RegisterF(pVM, &pPort->ProfOutRZ,STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc,"/IOM/Ports/%04x-Out-RZ/Prof", Port); AssertRC(rc);
465
466 return pPort;
467 }
468
469 AssertMsgFailed(("what! Port=%d\n", Port));
470 MMHyperFree(pVM, pPort);
471 }
472 IOM_UNLOCK_EXCL(pVM);
473 return NULL;
474}
475
476
477/**
478 * Create the statistics node for an MMIO address.
479 *
480 * @returns Pointer to new stats node.
481 *
482 * @param pVM The cross context VM structure.
483 * @param GCPhys The address.
484 * @param pszDesc Description.
485 */
486PIOMMMIOSTATS iomR3MMIOStatsCreate(PVM pVM, RTGCPHYS GCPhys, const char *pszDesc)
487{
488 IOM_LOCK_EXCL(pVM);
489
490 /* check if it already exists. */
491 PIOMMMIOSTATS pStats = (PIOMMMIOSTATS)RTAvloGCPhysGet(&pVM->iom.s.pTreesR3->MmioStatTree, GCPhys);
492 if (pStats)
493 {
494 IOM_UNLOCK_EXCL(pVM);
495 return pStats;
496 }
497
498 /* allocate stats node. */
499 int rc = MMHyperAlloc(pVM, sizeof(*pStats), 0, MM_TAG_IOM_STATS, (void **)&pStats);
500 AssertRC(rc);
501 if (RT_SUCCESS(rc))
502 {
503 /* insert into the tree. */
504 pStats->Core.Key = GCPhys;
505 if (RTAvloGCPhysInsert(&pVM->iom.s.pTreesR3->MmioStatTree, &pStats->Core))
506 {
507 IOM_UNLOCK_EXCL(pVM);
508
509 rc = STAMR3RegisterF(pVM, &pStats->Accesses, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/MMIO/%RGp", GCPhys); AssertRC(rc);
510 rc = STAMR3RegisterF(pVM, &pStats->ProfReadR3, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc, "/IOM/MMIO/%RGp/Read-R3", GCPhys); AssertRC(rc);
511 rc = STAMR3RegisterF(pVM, &pStats->ProfWriteR3, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc, "/IOM/MMIO/%RGp/Write-R3", GCPhys); AssertRC(rc);
512 rc = STAMR3RegisterF(pVM, &pStats->ProfReadRZ, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc, "/IOM/MMIO/%RGp/Read-RZ", GCPhys); AssertRC(rc);
513 rc = STAMR3RegisterF(pVM, &pStats->ProfWriteRZ, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc, "/IOM/MMIO/%RGp/Write-RZ", GCPhys); AssertRC(rc);
514 rc = STAMR3RegisterF(pVM, &pStats->ReadRZToR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/MMIO/%RGp/Read-RZtoR3", GCPhys); AssertRC(rc);
515 rc = STAMR3RegisterF(pVM, &pStats->WriteRZToR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/MMIO/%RGp/Write-RZtoR3", GCPhys); AssertRC(rc);
516
517 return pStats;
518 }
519 AssertMsgFailed(("what! GCPhys=%RGp\n", GCPhys));
520 MMHyperFree(pVM, pStats);
521 }
522 IOM_UNLOCK_EXCL(pVM);
523 return NULL;
524}
525
526#endif /* VBOX_WITH_STATISTICS */
527
528/**
529 * Registers a I/O port ring-3 handler.
530 *
531 * This API is called by PDM on behalf of a device. Devices must first register
532 * ring-3 ranges before any GC and R0 ranges can be registered using IOMR3IOPortRegisterRC()
533 * and IOMR3IOPortRegisterR0().
534 *
535 *
536 * @returns VBox status code.
537 *
538 * @param pVM The cross context VM structure.
539 * @param pDevIns PDM device instance owning the port range.
540 * @param PortStart First port number in the range.
541 * @param cPorts Number of ports to register.
542 * @param pvUser User argument for the callbacks.
543 * @param pfnOutCallback Pointer to function which is gonna handle OUT operations in R3.
544 * @param pfnInCallback Pointer to function which is gonna handle IN operations in R3.
545 * @param pfnOutStrCallback Pointer to function which is gonna handle string OUT operations in R3.
546 * @param pfnInStrCallback Pointer to function which is gonna handle string IN operations in R3.
547 * @param pszDesc Pointer to description string. This must not be freed.
548 */
549VMMR3_INT_DECL(int) IOMR3IOPortRegisterR3(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTHCPTR pvUser,
550 R3PTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, R3PTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
551 R3PTRTYPE(PFNIOMIOPORTOUTSTRING) pfnOutStrCallback, R3PTRTYPE(PFNIOMIOPORTINSTRING) pfnInStrCallback, const char *pszDesc)
552{
553 LogFlow(("IOMR3IOPortRegisterR3: pDevIns=%p PortStart=%#x cPorts=%#x pvUser=%RHv pfnOutCallback=%#x pfnInCallback=%#x pfnOutStrCallback=%#x pfnInStrCallback=%#x pszDesc=%s\n",
554 pDevIns, PortStart, cPorts, pvUser, pfnOutCallback, pfnInCallback, pfnOutStrCallback, pfnInStrCallback, pszDesc));
555
556 /*
557 * Validate input.
558 */
559 if ( (RTUINT)PortStart + cPorts <= (RTUINT)PortStart
560 || (RTUINT)PortStart + cPorts > 0x10000)
561 {
562 AssertMsgFailed(("Invalid port range %#x-%#x (inclusive)! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
563 return VERR_IOM_INVALID_IOPORT_RANGE;
564 }
565 if (!pfnOutCallback && !pfnInCallback)
566 {
567 AssertMsgFailed(("no handlers specfied for %#x-%#x (inclusive)! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
568 return VERR_INVALID_PARAMETER;
569 }
570 if (!pfnOutCallback)
571 pfnOutCallback = iomR3IOPortDummyOut;
572 if (!pfnInCallback)
573 pfnInCallback = iomR3IOPortDummyIn;
574 if (!pfnOutStrCallback)
575 pfnOutStrCallback = iomR3IOPortDummyOutStr;
576 if (!pfnInStrCallback)
577 pfnInStrCallback = iomR3IOPortDummyInStr;
578
579 /* Flush the IO port lookup cache */
580 iomR3FlushCache(pVM);
581
582 /*
583 * Allocate new range record and initialize it.
584 */
585 PIOMIOPORTRANGER3 pRange;
586 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
587 if (RT_SUCCESS(rc))
588 {
589 pRange->Core.Key = PortStart;
590 pRange->Core.KeyLast = PortStart + (cPorts - 1);
591 pRange->Port = PortStart;
592 pRange->cPorts = cPorts;
593 pRange->pvUser = pvUser;
594 pRange->pDevIns = pDevIns;
595 pRange->pfnOutCallback = pfnOutCallback;
596 pRange->pfnInCallback = pfnInCallback;
597 pRange->pfnOutStrCallback = pfnOutStrCallback;
598 pRange->pfnInStrCallback = pfnInStrCallback;
599 pRange->pszDesc = pszDesc;
600
601 /*
602 * Try Insert it.
603 */
604 IOM_LOCK_EXCL(pVM);
605 if (RTAvlroIOPortInsert(&pVM->iom.s.pTreesR3->IOPortTreeR3, &pRange->Core))
606 {
607#ifdef VBOX_WITH_STATISTICS
608 for (unsigned iPort = 0; iPort < cPorts; iPort++)
609 iomR3IOPortStatsCreate(pVM, PortStart + iPort, pszDesc);
610#endif
611 IOM_UNLOCK_EXCL(pVM);
612 return VINF_SUCCESS;
613 }
614 IOM_UNLOCK_EXCL(pVM);
615
616 /* conflict. */
617 DBGFR3Info(pVM->pUVM, "ioport", NULL, NULL);
618 AssertMsgFailed(("Port range %#x-%#x (%s) conflicts with existing range(s)!\n", PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
619 MMHyperFree(pVM, pRange);
620 rc = VERR_IOM_IOPORT_RANGE_CONFLICT;
621 }
622
623 return rc;
624}
625
626
627#if 0
628/**
629 * Registers a I/O port RC handler.
630 *
631 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
632 * using IOMIOPortRegisterR3() before calling this function.
633 *
634 *
635 * @returns VBox status code.
636 *
637 * @param pVM The cross context VM structure.
638 * @param pDevIns PDM device instance owning the port range.
639 * @param PortStart First port number in the range.
640 * @param cPorts Number of ports to register.
641 * @param pvUser User argument for the callbacks.
642 * @param pfnOutCallback Pointer to function which is gonna handle OUT operations in GC.
643 * @param pfnInCallback Pointer to function which is gonna handle IN operations in GC.
644 * @param pfnOutStrCallback Pointer to function which is gonna handle string OUT operations in GC.
645 * @param pfnInStrCallback Pointer to function which is gonna handle string IN operations in GC.
646 * @param pszDesc Pointer to description string. This must not be freed.
647 */
648VMMR3_INT_DECL(int) IOMR3IOPortRegisterRC(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTRCPTR pvUser,
649 RCPTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, RCPTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
650 RCPTRTYPE(PFNIOMIOPORTOUTSTRING) pfnOutStrCallback, RCPTRTYPE(PFNIOMIOPORTINSTRING) pfnInStrCallback, const char *pszDesc)
651{
652 LogFlow(("IOMR3IOPortRegisterRC: pDevIns=%p PortStart=%#x cPorts=%#x pvUser=%RRv pfnOutCallback=%RRv pfnInCallback=%RRv pfnOutStrCallback=%RRv pfnInStrCallback=%RRv pszDesc=%s\n",
653 pDevIns, PortStart, cPorts, pvUser, pfnOutCallback, pfnInCallback, pfnOutStrCallback, pfnInStrCallback, pszDesc));
654 AssertReturn(VM_IS_RAW_MODE_ENABLED(pVM), VERR_IOM_HM_IPE);
655
656 /*
657 * Validate input.
658 */
659 if ( (RTUINT)PortStart + cPorts <= (RTUINT)PortStart
660 || (RTUINT)PortStart + cPorts > 0x10000)
661 {
662 AssertMsgFailed(("Invalid port range %#x-%#x! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
663 return VERR_IOM_INVALID_IOPORT_RANGE;
664 }
665 RTIOPORT PortLast = PortStart + (cPorts - 1);
666 if (!pfnOutCallback && !pfnInCallback)
667 {
668 AssertMsgFailed(("Invalid port range %#x-%#x! No callbacks! (%s)\n", PortStart, PortLast, pszDesc));
669 return VERR_INVALID_PARAMETER;
670 }
671
672 IOM_LOCK_EXCL(pVM);
673
674 /*
675 * Validate that there are ring-3 ranges for the ports.
676 */
677 RTIOPORT Port = PortStart;
678 while (Port <= PortLast && Port >= PortStart)
679 {
680 PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortTreeR3, Port);
681 if (!pRange)
682 {
683 AssertMsgFailed(("No R3! Port=%#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
684 IOM_UNLOCK_EXCL(pVM);
685 return VERR_IOM_NO_R3_IOPORT_RANGE;
686 }
687#ifndef IOM_NO_PDMINS_CHECKS
688# ifndef IN_RC
689 if (pRange->pDevIns != pDevIns)
690# else
691 if (pRange->pDevIns != MMHyperRCToCC(pVM, pDevIns))
692# endif
693 {
694 AssertMsgFailed(("Not owner! Port=%#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
695 IOM_UNLOCK_EXCL(pVM);
696 return VERR_IOM_NOT_IOPORT_RANGE_OWNER;
697 }
698#endif
699 Port = pRange->Core.KeyLast + 1;
700 }
701
702 /* Flush the IO port lookup cache */
703 iomR3FlushCache(pVM);
704
705 /*
706 * Allocate new range record and initialize it.
707 */
708 PIOMIOPORTRANGERC pRange;
709 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
710 if (RT_SUCCESS(rc))
711 {
712 pRange->Core.Key = PortStart;
713 pRange->Core.KeyLast = PortLast;
714 pRange->Port = PortStart;
715 pRange->cPorts = cPorts;
716 pRange->pvUser = pvUser;
717 pRange->pfnOutCallback = pfnOutCallback;
718 pRange->pfnInCallback = pfnInCallback;
719 pRange->pfnOutStrCallback = pfnOutStrCallback;
720 pRange->pfnInStrCallback = pfnInStrCallback;
721 pRange->pDevIns = MMHyperCCToRC(pVM, pDevIns);
722 pRange->pszDesc = pszDesc;
723
724 /*
725 * Insert it.
726 */
727 if (RTAvlroIOPortInsert(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortTreeRC, &pRange->Core))
728 {
729 IOM_UNLOCK_EXCL(pVM);
730 return VINF_SUCCESS;
731 }
732
733 /* conflict. */
734 AssertMsgFailed(("Port range %#x-%#x (%s) conflicts with existing range(s)!\n", PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
735 MMHyperFree(pVM, pRange);
736 rc = VERR_IOM_IOPORT_RANGE_CONFLICT;
737 }
738 IOM_UNLOCK_EXCL(pVM);
739 return rc;
740}
741#endif
742
743
744/**
745 * Registers a Port IO R0 handler.
746 *
747 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
748 * using IOMR3IOPortRegisterR3() before calling this function.
749 *
750 *
751 * @returns VBox status code.
752 *
753 * @param pVM The cross context VM structure.
754 * @param pDevIns PDM device instance owning the port range.
755 * @param PortStart First port number in the range.
756 * @param cPorts Number of ports to register.
757 * @param pvUser User argument for the callbacks.
758 * @param pfnOutCallback Pointer to function which is gonna handle OUT operations in GC.
759 * @param pfnInCallback Pointer to function which is gonna handle IN operations in GC.
760 * @param pfnOutStrCallback Pointer to function which is gonna handle OUT operations in GC.
761 * @param pfnInStrCallback Pointer to function which is gonna handle IN operations in GC.
762 * @param pszDesc Pointer to description string. This must not be freed.
763 */
764VMMR3_INT_DECL(int) IOMR3IOPortRegisterR0(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTR0PTR pvUser,
765 R0PTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, R0PTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
766 R0PTRTYPE(PFNIOMIOPORTOUTSTRING) pfnOutStrCallback, R0PTRTYPE(PFNIOMIOPORTINSTRING) pfnInStrCallback,
767 const char *pszDesc)
768{
769 LogFlow(("IOMR3IOPortRegisterR0: pDevIns=%p PortStart=%#x cPorts=%#x pvUser=%RHv pfnOutCallback=%RHv pfnInCallback=%RHv pfnOutStrCallback=%RHv pfnInStrCallback=%RHv pszDesc=%s\n",
770 pDevIns, PortStart, cPorts, pvUser, pfnOutCallback, pfnInCallback, pfnOutStrCallback, pfnInStrCallback, pszDesc));
771
772 /*
773 * Validate input.
774 */
775 if ( (RTUINT)PortStart + cPorts <= (RTUINT)PortStart
776 || (RTUINT)PortStart + cPorts > 0x10000)
777 {
778 AssertMsgFailed(("Invalid port range %#x-%#x! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
779 return VERR_IOM_INVALID_IOPORT_RANGE;
780 }
781 RTIOPORT PortLast = PortStart + (cPorts - 1);
782 if (!pfnOutCallback && !pfnInCallback)
783 {
784 AssertMsgFailed(("Invalid port range %#x-%#x! No callbacks! (%s)\n", PortStart, PortLast, pszDesc));
785 return VERR_INVALID_PARAMETER;
786 }
787
788 IOM_LOCK_EXCL(pVM);
789
790 /*
791 * Validate that there are ring-3 ranges for the ports.
792 */
793 RTIOPORT Port = PortStart;
794 while (Port <= PortLast && Port >= PortStart)
795 {
796 PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortTreeR3, Port);
797 if (!pRange)
798 {
799 AssertMsgFailed(("No R3! Port=%#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
800 IOM_UNLOCK_EXCL(pVM);
801 return VERR_IOM_NO_R3_IOPORT_RANGE;
802 }
803#ifndef IOM_NO_PDMINS_CHECKS
804# ifndef IN_RC
805 if (pRange->pDevIns != pDevIns)
806# else
807 if (pRange->pDevIns != MMHyperRCToCC(pVM, pDevIns))
808# endif
809 {
810 AssertMsgFailed(("Not owner! Port=%#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
811 IOM_UNLOCK_EXCL(pVM);
812 return VERR_IOM_NOT_IOPORT_RANGE_OWNER;
813 }
814#endif
815 Port = pRange->Core.KeyLast + 1;
816 }
817
818 /* Flush the IO port lookup cache */
819 iomR3FlushCache(pVM);
820
821 /*
822 * Allocate new range record and initialize it.
823 */
824 PIOMIOPORTRANGER0 pRange;
825 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
826 if (RT_SUCCESS(rc))
827 {
828 pRange->Core.Key = PortStart;
829 pRange->Core.KeyLast = PortLast;
830 pRange->Port = PortStart;
831 pRange->cPorts = cPorts;
832 pRange->pvUser = pvUser;
833 pRange->pfnOutCallback = pfnOutCallback;
834 pRange->pfnInCallback = pfnInCallback;
835 pRange->pfnOutStrCallback = pfnOutStrCallback;
836 pRange->pfnInStrCallback = pfnInStrCallback;
837 pRange->pDevIns = MMHyperR3ToR0(pVM, pDevIns);
838 pRange->pszDesc = pszDesc;
839
840 /*
841 * Insert it.
842 */
843 if (RTAvlroIOPortInsert(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortTreeR0, &pRange->Core))
844 {
845 IOM_UNLOCK_EXCL(pVM);
846 return VINF_SUCCESS;
847 }
848
849 /* conflict. */
850 AssertMsgFailed(("Port range %#x-%#x (%s) conflicts with existing range(s)!\n", PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
851 MMHyperFree(pVM, pRange);
852 rc = VERR_IOM_IOPORT_RANGE_CONFLICT;
853 }
854 IOM_UNLOCK_EXCL(pVM);
855 return rc;
856}
857
858
859/**
860 * Deregisters a I/O Port range.
861 *
862 * The specified range must be registered using IOMR3IOPortRegister previous to
863 * this call. The range does can be a smaller part of the range specified to
864 * IOMR3IOPortRegister, but it can never be larger.
865 *
866 * This function will remove GC, R0 and R3 context port handlers for this range.
867 *
868 * @returns VBox status code.
869 *
870 * @param pVM The cross context VM structure.
871 * @param pDevIns The device instance associated with the range.
872 * @param PortStart First port number in the range.
873 * @param cPorts Number of ports to remove starting at PortStart.
874 *
875 * @remark This function mainly for PCI PnP Config and will not do
876 * all the checks you might expect it to do.
877 */
878VMMR3_INT_DECL(int) IOMR3IOPortDeregister(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts)
879{
880 LogFlow(("IOMR3IOPortDeregister: pDevIns=%p PortStart=%#x cPorts=%#x\n", pDevIns, PortStart, cPorts));
881
882 /*
883 * Validate input.
884 */
885 if ( (RTUINT)PortStart + cPorts < (RTUINT)PortStart
886 || (RTUINT)PortStart + cPorts > 0x10000)
887 {
888 AssertMsgFailed(("Invalid port range %#x-%#x!\n", PortStart, (unsigned)PortStart + cPorts - 1));
889 return VERR_IOM_INVALID_IOPORT_RANGE;
890 }
891
892 IOM_LOCK_EXCL(pVM);
893
894 /* Flush the IO port lookup cache */
895 iomR3FlushCache(pVM);
896
897 /*
898 * Check ownership.
899 */
900 RTIOPORT PortLast = PortStart + (cPorts - 1);
901 RTIOPORT Port = PortStart;
902 while (Port <= PortLast && Port >= PortStart)
903 {
904 PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.pTreesR3->IOPortTreeR3, Port);
905 if (pRange)
906 {
907 Assert(Port <= pRange->Core.KeyLast);
908#ifndef IOM_NO_PDMINS_CHECKS
909 if (pRange->pDevIns != pDevIns)
910 {
911 AssertMsgFailed(("Removal of ports in range %#x-%#x rejected because not owner of %#x-%#x (%s)\n",
912 PortStart, PortLast, pRange->Core.Key, pRange->Core.KeyLast, pRange->pszDesc));
913 IOM_UNLOCK_EXCL(pVM);
914 return VERR_IOM_NOT_IOPORT_RANGE_OWNER;
915 }
916#else /* IOM_NO_PDMINS_CHECKS */
917 RT_NOREF_PV(pDevIns);
918#endif /* IOM_NO_PDMINS_CHECKS */
919 Port = pRange->Core.KeyLast;
920 }
921 Port++;
922 }
923
924#if 0
925 /*
926 * Remove any RC ranges first.
927 */
928 int rc = VINF_SUCCESS;
929 Port = PortStart;
930 while (Port <= PortLast && Port >= PortStart)
931 {
932 /*
933 * Try find range.
934 */
935 PIOMIOPORTRANGERC pRange = (PIOMIOPORTRANGERC)RTAvlroIOPortRangeGet(&pVM->iom.s.pTreesR3->IOPortTreeRC, Port);
936 if (pRange)
937 {
938 if ( pRange->Core.Key == Port
939 && pRange->Core.KeyLast <= PortLast)
940 {
941 /*
942 * Kick out the entire range.
943 */
944 void *pv = RTAvlroIOPortRemove(&pVM->iom.s.pTreesR3->IOPortTreeRC, Port);
945 Assert(pv == (void *)pRange); NOREF(pv);
946 Port += pRange->cPorts;
947 MMHyperFree(pVM, pRange);
948 }
949 else if (pRange->Core.Key == Port)
950 {
951 /*
952 * Cut of the head of the range, done.
953 */
954 pRange->cPorts -= Port - pRange->Port;
955 pRange->Core.Key = Port;
956 pRange->Port = Port;
957 break;
958 }
959 else if (pRange->Core.KeyLast <= PortLast)
960 {
961 /*
962 * Just cut of the tail.
963 */
964 unsigned c = pRange->Core.KeyLast - Port + 1;
965 pRange->Core.KeyLast -= c;
966 pRange->cPorts -= c;
967 Port += c;
968 }
969 else
970 {
971 /*
972 * Split the range, done.
973 */
974 Assert(pRange->Core.KeyLast > PortLast && pRange->Core.Key < Port);
975 /* create tail. */
976 PIOMIOPORTRANGERC pRangeNew;
977 int rc2 = MMHyperAlloc(pVM, sizeof(*pRangeNew), 0, MM_TAG_IOM, (void **)&pRangeNew);
978 if (RT_FAILURE(rc2))
979 {
980 IOM_UNLOCK_EXCL(pVM);
981 return rc2;
982 }
983 *pRangeNew = *pRange;
984 pRangeNew->Core.Key = PortLast;
985 pRangeNew->Port = PortLast;
986 pRangeNew->cPorts = pRangeNew->Core.KeyLast - PortLast + 1;
987
988 LogFlow(("IOMR3IOPortDeregister (rc): split the range; new %x\n", pRangeNew->Core.Key));
989
990 /* adjust head */
991 pRange->Core.KeyLast = Port - 1;
992 pRange->cPorts = Port - pRange->Port;
993
994 /* insert */
995 if (!RTAvlroIOPortInsert(&pVM->iom.s.pTreesR3->IOPortTreeRC, &pRangeNew->Core))
996 {
997 AssertMsgFailed(("This cannot happen!\n"));
998 MMHyperFree(pVM, pRangeNew);
999 rc = VERR_IOM_IOPORT_IPE_1;
1000 }
1001 break;
1002 }
1003 }
1004 else /* next port */
1005 Port++;
1006 } /* for all ports - RC. */
1007#else
1008 int rc = VINF_SUCCESS;
1009#endif
1010
1011 /*
1012 * Remove any R0 ranges.
1013 */
1014 Port = PortStart;
1015 while (Port <= PortLast && Port >= PortStart)
1016 {
1017 /*
1018 * Try find range.
1019 */
1020 PIOMIOPORTRANGER0 pRange = (PIOMIOPORTRANGER0)RTAvlroIOPortRangeGet(&pVM->iom.s.pTreesR3->IOPortTreeR0, Port);
1021 if (pRange)
1022 {
1023 if ( pRange->Core.Key == Port
1024 && pRange->Core.KeyLast <= PortLast)
1025 {
1026 /*
1027 * Kick out the entire range.
1028 */
1029 void *pv = RTAvlroIOPortRemove(&pVM->iom.s.pTreesR3->IOPortTreeR0, Port);
1030 Assert(pv == (void *)pRange); NOREF(pv);
1031 Port += pRange->cPorts;
1032 MMHyperFree(pVM, pRange);
1033 }
1034 else if (pRange->Core.Key == Port)
1035 {
1036 /*
1037 * Cut of the head of the range, done.
1038 */
1039 pRange->cPorts -= Port - pRange->Port;
1040 pRange->Core.Key = Port;
1041 pRange->Port = Port;
1042 break;
1043 }
1044 else if (pRange->Core.KeyLast <= PortLast)
1045 {
1046 /*
1047 * Just cut of the tail.
1048 */
1049 unsigned c = pRange->Core.KeyLast - Port + 1;
1050 pRange->Core.KeyLast -= c;
1051 pRange->cPorts -= c;
1052 Port += c;
1053 }
1054 else
1055 {
1056 /*
1057 * Split the range, done.
1058 */
1059 Assert(pRange->Core.KeyLast > PortLast && pRange->Core.Key < Port);
1060 /* create tail. */
1061 PIOMIOPORTRANGER0 pRangeNew;
1062 int rc2 = MMHyperAlloc(pVM, sizeof(*pRangeNew), 0, MM_TAG_IOM, (void **)&pRangeNew);
1063 if (RT_FAILURE(rc2))
1064 {
1065 IOM_UNLOCK_EXCL(pVM);
1066 return rc2;
1067 }
1068 *pRangeNew = *pRange;
1069 pRangeNew->Core.Key = PortLast;
1070 pRangeNew->Port = PortLast;
1071 pRangeNew->cPorts = pRangeNew->Core.KeyLast - PortLast + 1;
1072
1073 LogFlow(("IOMR3IOPortDeregister (r0): split the range; new %x\n", pRangeNew->Core.Key));
1074
1075 /* adjust head */
1076 pRange->Core.KeyLast = Port - 1;
1077 pRange->cPorts = Port - pRange->Port;
1078
1079 /* insert */
1080 if (!RTAvlroIOPortInsert(&pVM->iom.s.pTreesR3->IOPortTreeR0, &pRangeNew->Core))
1081 {
1082 AssertMsgFailed(("This cannot happen!\n"));
1083 MMHyperFree(pVM, pRangeNew);
1084 rc = VERR_IOM_IOPORT_IPE_1;
1085 }
1086 break;
1087 }
1088 }
1089 else /* next port */
1090 Port++;
1091 } /* for all ports - R0. */
1092
1093 /*
1094 * And the same procedure for ring-3 ranges.
1095 */
1096 Port = PortStart;
1097 while (Port <= PortLast && Port >= PortStart)
1098 {
1099 /*
1100 * Try find range.
1101 */
1102 PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.pTreesR3->IOPortTreeR3, Port);
1103 if (pRange)
1104 {
1105 if ( pRange->Core.Key == Port
1106 && pRange->Core.KeyLast <= PortLast)
1107 {
1108 /*
1109 * Kick out the entire range.
1110 */
1111 void *pv = RTAvlroIOPortRemove(&pVM->iom.s.pTreesR3->IOPortTreeR3, Port);
1112 Assert(pv == (void *)pRange); NOREF(pv);
1113 Port += pRange->cPorts;
1114 MMHyperFree(pVM, pRange);
1115 }
1116 else if (pRange->Core.Key == Port)
1117 {
1118 /*
1119 * Cut of the head of the range, done.
1120 */
1121 pRange->cPorts -= Port - pRange->Port;
1122 pRange->Core.Key = Port;
1123 pRange->Port = Port;
1124 break;
1125 }
1126 else if (pRange->Core.KeyLast <= PortLast)
1127 {
1128 /*
1129 * Just cut of the tail.
1130 */
1131 unsigned c = pRange->Core.KeyLast - Port + 1;
1132 pRange->Core.KeyLast -= c;
1133 pRange->cPorts -= c;
1134 Port += c;
1135 }
1136 else
1137 {
1138 /*
1139 * Split the range, done.
1140 */
1141 Assert(pRange->Core.KeyLast > PortLast && pRange->Core.Key < Port);
1142 /* create tail. */
1143 PIOMIOPORTRANGER3 pRangeNew;
1144 int rc2 = MMHyperAlloc(pVM, sizeof(*pRangeNew), 0, MM_TAG_IOM, (void **)&pRangeNew);
1145 if (RT_FAILURE(rc2))
1146 {
1147 IOM_UNLOCK_EXCL(pVM);
1148 return rc2;
1149 }
1150 *pRangeNew = *pRange;
1151 pRangeNew->Core.Key = PortLast;
1152 pRangeNew->Port = PortLast;
1153 pRangeNew->cPorts = pRangeNew->Core.KeyLast - PortLast + 1;
1154
1155 LogFlow(("IOMR3IOPortDeregister (r3): split the range; new %x\n", pRangeNew->Core.Key));
1156
1157 /* adjust head */
1158 pRange->Core.KeyLast = Port - 1;
1159 pRange->cPorts = Port - pRange->Port;
1160
1161 /* insert */
1162 if (!RTAvlroIOPortInsert(&pVM->iom.s.pTreesR3->IOPortTreeR3, &pRangeNew->Core))
1163 {
1164 AssertMsgFailed(("This cannot happen!\n"));
1165 MMHyperFree(pVM, pRangeNew);
1166 rc = VERR_IOM_IOPORT_IPE_1;
1167 }
1168 break;
1169 }
1170 }
1171 else /* next port */
1172 Port++;
1173 } /* for all ports - ring-3. */
1174
1175 /* done */
1176 IOM_UNLOCK_EXCL(pVM);
1177 return rc;
1178}
1179
1180
1181/**
1182 * Dummy Port I/O Handler for IN operations.
1183 *
1184 * @returns VBox status code.
1185 *
1186 * @param pDevIns The device instance.
1187 * @param pvUser User argument.
1188 * @param Port Port number used for the IN operation.
1189 * @param pu32 Where to store the result.
1190 * @param cb Number of bytes read.
1191 */
1192static DECLCALLBACK(int) iomR3IOPortDummyIn(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
1193{
1194 NOREF(pDevIns); NOREF(pvUser); NOREF(Port);
1195 switch (cb)
1196 {
1197 case 1: *pu32 = 0xff; break;
1198 case 2: *pu32 = 0xffff; break;
1199 case 4: *pu32 = UINT32_C(0xffffffff); break;
1200 default:
1201 AssertReleaseMsgFailed(("cb=%d\n", cb));
1202 return VERR_IOM_IOPORT_IPE_2;
1203 }
1204 return VINF_SUCCESS;
1205}
1206
1207
1208/**
1209 * @callback_method_impl{FNIOMIOPORTINSTRING,
1210 * Dummy Port I/O Handler for string IN operations.}
1211 */
1212static DECLCALLBACK(int) iomR3IOPortDummyInStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint8_t *pbDst,
1213 uint32_t *pcTransfer, unsigned cb)
1214{
1215 NOREF(pDevIns); NOREF(pvUser); NOREF(Port); NOREF(pbDst); NOREF(pcTransfer); NOREF(cb);
1216 return VINF_SUCCESS;
1217}
1218
1219
1220/**
1221 * Dummy Port I/O Handler for OUT operations.
1222 *
1223 * @returns VBox status code.
1224 *
1225 * @param pDevIns The device instance.
1226 * @param pvUser User argument.
1227 * @param Port Port number used for the OUT operation.
1228 * @param u32 The value to output.
1229 * @param cb The value size in bytes.
1230 */
1231static DECLCALLBACK(int) iomR3IOPortDummyOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
1232{
1233 NOREF(pDevIns); NOREF(pvUser); NOREF(Port); NOREF(u32); NOREF(cb);
1234 return VINF_SUCCESS;
1235}
1236
1237
1238/**
1239 * @callback_method_impl{FNIOMIOPORTOUTSTRING,
1240 * Dummy Port I/O Handler for string OUT operations.}
1241 */
1242static DECLCALLBACK(int) iomR3IOPortDummyOutStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint8_t const *pbSrc,
1243 uint32_t *pcTransfer, unsigned cb)
1244{
1245 NOREF(pDevIns); NOREF(pvUser); NOREF(Port); NOREF(pbSrc); NOREF(pcTransfer); NOREF(cb);
1246 return VINF_SUCCESS;
1247}
1248
1249
1250/**
1251 * Display a single I/O port ring-3 range.
1252 *
1253 * @returns 0
1254 * @param pNode Pointer to I/O port HC range.
1255 * @param pvUser Pointer to info output callback structure.
1256 */
1257static DECLCALLBACK(int) iomR3IOPortInfoOneR3(PAVLROIOPORTNODECORE pNode, void *pvUser)
1258{
1259 PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)pNode;
1260 PCDBGFINFOHLP pHlp = (PCDBGFINFOHLP)pvUser;
1261 pHlp->pfnPrintf(pHlp,
1262 "%04x-%04x %p %p %p %p %s\n",
1263 pRange->Core.Key,
1264 pRange->Core.KeyLast,
1265 pRange->pDevIns,
1266 pRange->pfnInCallback,
1267 pRange->pfnOutCallback,
1268 pRange->pvUser,
1269 pRange->pszDesc);
1270 return 0;
1271}
1272
1273
1274#if 0
1275/**
1276 * Display a single I/O port GC range.
1277 *
1278 * @returns 0
1279 * @param pNode Pointer to IOPORT GC range.
1280 * @param pvUser Pointer to info output callback structure.
1281 */
1282static DECLCALLBACK(int) iomR3IOPortInfoOneRC(PAVLROIOPORTNODECORE pNode, void *pvUser)
1283{
1284 PIOMIOPORTRANGERC pRange = (PIOMIOPORTRANGERC)pNode;
1285 PCDBGFINFOHLP pHlp = (PCDBGFINFOHLP)pvUser;
1286 pHlp->pfnPrintf(pHlp,
1287 "%04x-%04x %RRv %RRv %RRv %RRv %s\n",
1288 pRange->Core.Key,
1289 pRange->Core.KeyLast,
1290 pRange->pDevIns,
1291 pRange->pfnInCallback,
1292 pRange->pfnOutCallback,
1293 pRange->pvUser,
1294 pRange->pszDesc);
1295 return 0;
1296}
1297#endif
1298
1299
1300/**
1301 * Display all registered I/O port ranges.
1302 *
1303 * @param pVM The cross context VM structure.
1304 * @param pHlp The info helpers.
1305 * @param pszArgs Arguments, ignored.
1306 */
1307static DECLCALLBACK(void) iomR3IOPortInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
1308{
1309 NOREF(pszArgs);
1310 pHlp->pfnPrintf(pHlp,
1311 "I/O Port R3 ranges (pVM=%p)\n"
1312 "Range %.*s %.*s %.*s %.*s Description\n",
1313 pVM,
1314 sizeof(RTHCPTR) * 2, "pDevIns ",
1315 sizeof(RTHCPTR) * 2, "In ",
1316 sizeof(RTHCPTR) * 2, "Out ",
1317 sizeof(RTHCPTR) * 2, "pvUser ");
1318 RTAvlroIOPortDoWithAll(&pVM->iom.s.pTreesR3->IOPortTreeR3, true, iomR3IOPortInfoOneR3, (void *)pHlp);
1319
1320 pHlp->pfnPrintf(pHlp,
1321 "I/O Port R0 ranges (pVM=%p)\n"
1322 "Range %.*s %.*s %.*s %.*s Description\n",
1323 pVM,
1324 sizeof(RTHCPTR) * 2, "pDevIns ",
1325 sizeof(RTHCPTR) * 2, "In ",
1326 sizeof(RTHCPTR) * 2, "Out ",
1327 sizeof(RTHCPTR) * 2, "pvUser ");
1328 RTAvlroIOPortDoWithAll(&pVM->iom.s.pTreesR3->IOPortTreeR0, true, iomR3IOPortInfoOneR3, (void *)pHlp);
1329
1330#if 0
1331 pHlp->pfnPrintf(pHlp,
1332 "I/O Port GC ranges (pVM=%p)\n"
1333 "Range %.*s %.*s %.*s %.*s Description\n",
1334 pVM,
1335 sizeof(RTRCPTR) * 2, "pDevIns ",
1336 sizeof(RTRCPTR) * 2, "In ",
1337 sizeof(RTRCPTR) * 2, "Out ",
1338 sizeof(RTRCPTR) * 2, "pvUser ");
1339 RTAvlroIOPortDoWithAll(&pVM->iom.s.pTreesR3->IOPortTreeRC, true, iomR3IOPortInfoOneRC, (void *)pHlp);
1340#endif
1341}
1342
1343
1344/**
1345 * Registers a Memory Mapped I/O R3 handler.
1346 *
1347 * This API is called by PDM on behalf of a device. Devices must register ring-3 ranges
1348 * before any GC and R0 ranges can be registered using IOMR3MMIORegisterRC() and IOMR3MMIORegisterR0().
1349 *
1350 * @returns VBox status code.
1351 *
1352 * @param pVM The cross context VM structure.
1353 * @param pDevIns PDM device instance owning the MMIO range.
1354 * @param GCPhysStart First physical address in the range.
1355 * @param cbRange The size of the range (in bytes).
1356 * @param pvUser User argument for the callbacks.
1357 * @param pfnWriteCallback Pointer to function which is gonna handle Write operations.
1358 * @param pfnReadCallback Pointer to function which is gonna handle Read operations.
1359 * @param pfnFillCallback Pointer to function which is gonna handle Fill/memset operations.
1360 * @param fFlags Flags, see IOMMMIO_FLAGS_XXX.
1361 * @param pszDesc Pointer to description string. This must not be freed.
1362 */
1363VMMR3_INT_DECL(int)
1364IOMR3MmioRegisterR3(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTGCPHYS cbRange, RTHCPTR pvUser,
1365 R3PTRTYPE(PFNIOMMMIOWRITE) pfnWriteCallback, R3PTRTYPE(PFNIOMMMIOREAD) pfnReadCallback,
1366 R3PTRTYPE(PFNIOMMMIOFILL) pfnFillCallback, uint32_t fFlags, const char *pszDesc)
1367{
1368 LogFlow(("IOMR3MmioRegisterR3: pDevIns=%p GCPhysStart=%RGp cbRange=%RGp pvUser=%RHv pfnWriteCallback=%#x pfnReadCallback=%#x pfnFillCallback=%#x fFlags=%#x pszDesc=%s\n",
1369 pDevIns, GCPhysStart, cbRange, pvUser, pfnWriteCallback, pfnReadCallback, pfnFillCallback, fFlags, pszDesc));
1370 int rc;
1371
1372 /*
1373 * Validate input.
1374 */
1375 AssertMsgReturn(GCPhysStart + (cbRange - 1) >= GCPhysStart,("Wrapped! %RGp LB %RGp\n", GCPhysStart, cbRange),
1376 VERR_IOM_INVALID_MMIO_RANGE);
1377 AssertMsgReturn( !(fFlags & ~IOMMMIO_FLAGS_VALID_MASK)
1378 && (fFlags & IOMMMIO_FLAGS_READ_MODE) <= IOMMMIO_FLAGS_READ_DWORD_QWORD
1379 && (fFlags & IOMMMIO_FLAGS_WRITE_MODE) <= IOMMMIO_FLAGS_WRITE_ONLY_DWORD_QWORD,
1380 ("%#x\n", fFlags),
1381 VERR_INVALID_PARAMETER);
1382
1383 /*
1384 * Allocate new range record and initialize it.
1385 */
1386 PIOMMMIORANGE pRange;
1387 rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
1388 if (RT_SUCCESS(rc))
1389 {
1390 pRange->Core.Key = GCPhysStart;
1391 pRange->Core.KeyLast = GCPhysStart + (cbRange - 1);
1392 pRange->GCPhys = GCPhysStart;
1393 pRange->cb = cbRange;
1394 pRange->cRefs = 1; /* The tree reference. */
1395 pRange->pszDesc = pszDesc;
1396
1397 //pRange->pvUserR0 = NIL_RTR0PTR;
1398 //pRange->pDevInsR0 = NIL_RTR0PTR;
1399 //pRange->pfnReadCallbackR0 = NIL_RTR0PTR;
1400 //pRange->pfnWriteCallbackR0 = NIL_RTR0PTR;
1401 //pRange->pfnFillCallbackR0 = NIL_RTR0PTR;
1402
1403 //pRange->pvUserRC = NIL_RTRCPTR;
1404 //pRange->pDevInsRC = NIL_RTRCPTR;
1405 //pRange->pfnReadCallbackRC = NIL_RTRCPTR;
1406 //pRange->pfnWriteCallbackRC = NIL_RTRCPTR;
1407 //pRange->pfnFillCallbackRC = NIL_RTRCPTR;
1408
1409 pRange->fFlags = fFlags;
1410
1411 pRange->pvUserR3 = pvUser;
1412 pRange->pDevInsR3 = pDevIns;
1413 pRange->pfnReadCallbackR3 = pfnReadCallback;
1414 pRange->pfnWriteCallbackR3 = pfnWriteCallback;
1415 pRange->pfnFillCallbackR3 = pfnFillCallback;
1416
1417 /*
1418 * Try register it with PGM and then insert it into the tree.
1419 */
1420 rc = PGMR3PhysMMIORegister(pVM, GCPhysStart, cbRange, pVM->iom.s.hMmioHandlerType,
1421 pRange, MMHyperR3ToR0(pVM, pRange), MMHyperR3ToRC(pVM, pRange), pszDesc);
1422 if (RT_SUCCESS(rc))
1423 {
1424 IOM_LOCK_EXCL(pVM);
1425 if (RTAvlroGCPhysInsert(&pVM->iom.s.pTreesR3->MMIOTree, &pRange->Core))
1426 {
1427 iomR3FlushCache(pVM);
1428 IOM_UNLOCK_EXCL(pVM);
1429 return VINF_SUCCESS;
1430 }
1431
1432 /* bail out */
1433 IOM_UNLOCK_EXCL(pVM);
1434 DBGFR3Info(pVM->pUVM, "mmio", NULL, NULL);
1435 AssertMsgFailed(("This cannot happen!\n"));
1436 rc = VERR_IOM_IOPORT_IPE_3;
1437 }
1438
1439 MMHyperFree(pVM, pRange);
1440 }
1441 if (pDevIns->iInstance > 0)
1442 MMR3HeapFree((void *)pszDesc);
1443 return rc;
1444}
1445
1446
1447#if 0
1448/**
1449 * Registers a Memory Mapped I/O RC handler range.
1450 *
1451 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
1452 * using IOMMMIORegisterR3() before calling this function.
1453 *
1454 *
1455 * @returns VBox status code.
1456 *
1457 * @param pVM The cross context VM structure.
1458 * @param pDevIns PDM device instance owning the MMIO range.
1459 * @param GCPhysStart First physical address in the range.
1460 * @param cbRange The size of the range (in bytes).
1461 * @param pvUser User argument for the callbacks.
1462 * @param pfnWriteCallback Pointer to function which is gonna handle Write operations.
1463 * @param pfnReadCallback Pointer to function which is gonna handle Read operations.
1464 * @param pfnFillCallback Pointer to function which is gonna handle Fill/memset operations.
1465 * @thread EMT
1466 */
1467VMMR3_INT_DECL(int)
1468IOMR3MmioRegisterRC(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTGCPHYS cbRange, RTGCPTR pvUser,
1469 RCPTRTYPE(PFNIOMMMIOWRITE) pfnWriteCallback, RCPTRTYPE(PFNIOMMMIOREAD) pfnReadCallback,
1470 RCPTRTYPE(PFNIOMMMIOFILL) pfnFillCallback)
1471{
1472 LogFlow(("IOMR3MmioRegisterRC: pDevIns=%p GCPhysStart=%RGp cbRange=%RGp pvUser=%RGv pfnWriteCallback=%#x pfnReadCallback=%#x pfnFillCallback=%#x\n",
1473 pDevIns, GCPhysStart, cbRange, pvUser, pfnWriteCallback, pfnReadCallback, pfnFillCallback));
1474 AssertReturn(VM_IS_RAW_MODE_ENABLED(pVM), VERR_IOM_HM_IPE);
1475
1476 /*
1477 * Validate input.
1478 */
1479 if (!pfnWriteCallback && !pfnReadCallback)
1480 {
1481 AssertMsgFailed(("No callbacks! %RGp LB %RGp\n", GCPhysStart, cbRange));
1482 return VERR_INVALID_PARAMETER;
1483 }
1484 PVMCPU pVCpu = VMMGetCpu(pVM); Assert(pVCpu);
1485
1486 /*
1487 * Find the MMIO range and check that the input matches.
1488 */
1489 IOM_LOCK_EXCL(pVM);
1490 PIOMMMIORANGE pRange = iomMmioGetRange(pVM, pVCpu, GCPhysStart);
1491 AssertReturnStmt(pRange, IOM_UNLOCK_EXCL(pVM), VERR_IOM_MMIO_RANGE_NOT_FOUND);
1492 AssertReturnStmt(pRange->pDevInsR3 == pDevIns, IOM_UNLOCK_EXCL(pVM), VERR_IOM_NOT_MMIO_RANGE_OWNER);
1493 AssertReturnStmt(pRange->GCPhys == GCPhysStart, IOM_UNLOCK_EXCL(pVM), VERR_IOM_INVALID_MMIO_RANGE);
1494 AssertReturnStmt(pRange->cb == cbRange, IOM_UNLOCK_EXCL(pVM), VERR_IOM_INVALID_MMIO_RANGE);
1495
1496 pRange->pvUserRC = pvUser;
1497 pRange->pfnReadCallbackRC = pfnReadCallback;
1498 pRange->pfnWriteCallbackRC= pfnWriteCallback;
1499 pRange->pfnFillCallbackRC = pfnFillCallback;
1500 pRange->pDevInsRC = MMHyperCCToRC(pVM, pDevIns);
1501 IOM_UNLOCK_EXCL(pVM);
1502
1503 return VINF_SUCCESS;
1504}
1505#endif
1506
1507
1508/**
1509 * Registers a Memory Mapped I/O R0 handler range.
1510 *
1511 * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
1512 * using IOMMR3MIORegisterHC() before calling this function.
1513 *
1514 *
1515 * @returns VBox status code.
1516 *
1517 * @param pVM The cross context VM structure.
1518 * @param pDevIns PDM device instance owning the MMIO range.
1519 * @param GCPhysStart First physical address in the range.
1520 * @param cbRange The size of the range (in bytes).
1521 * @param pvUser User argument for the callbacks.
1522 * @param pfnWriteCallback Pointer to function which is gonna handle Write operations.
1523 * @param pfnReadCallback Pointer to function which is gonna handle Read operations.
1524 * @param pfnFillCallback Pointer to function which is gonna handle Fill/memset operations.
1525 * @thread EMT
1526 */
1527VMMR3_INT_DECL(int)
1528IOMR3MmioRegisterR0(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTGCPHYS cbRange, RTR0PTR pvUser,
1529 R0PTRTYPE(PFNIOMMMIOWRITE) pfnWriteCallback,
1530 R0PTRTYPE(PFNIOMMMIOREAD) pfnReadCallback,
1531 R0PTRTYPE(PFNIOMMMIOFILL) pfnFillCallback)
1532{
1533 LogFlow(("IOMR3MmioRegisterR0: pDevIns=%p GCPhysStart=%RGp cbRange=%RGp pvUser=%RHv pfnWriteCallback=%#x pfnReadCallback=%#x pfnFillCallback=%#x\n",
1534 pDevIns, GCPhysStart, cbRange, pvUser, pfnWriteCallback, pfnReadCallback, pfnFillCallback));
1535
1536 /*
1537 * Validate input.
1538 */
1539 if (!pfnWriteCallback && !pfnReadCallback)
1540 {
1541 AssertMsgFailed(("No callbacks! %RGp LB %RGp\n", GCPhysStart, cbRange));
1542 return VERR_INVALID_PARAMETER;
1543 }
1544 PVMCPU pVCpu = VMMGetCpu(pVM); Assert(pVCpu);
1545
1546 /*
1547 * Find the MMIO range and check that the input matches.
1548 */
1549 IOM_LOCK_EXCL(pVM);
1550 PIOMMMIORANGE pRange = iomMmioGetRange(pVM, pVCpu, GCPhysStart);
1551 AssertReturnStmt(pRange, IOM_UNLOCK_EXCL(pVM), VERR_IOM_MMIO_RANGE_NOT_FOUND);
1552 AssertReturnStmt(pRange->pDevInsR3 == pDevIns, IOM_UNLOCK_EXCL(pVM), VERR_IOM_NOT_MMIO_RANGE_OWNER);
1553 AssertReturnStmt(pRange->GCPhys == GCPhysStart, IOM_UNLOCK_EXCL(pVM), VERR_IOM_INVALID_MMIO_RANGE);
1554 AssertReturnStmt(pRange->cb == cbRange, IOM_UNLOCK_EXCL(pVM), VERR_IOM_INVALID_MMIO_RANGE);
1555
1556 pRange->pvUserR0 = pvUser;
1557 pRange->pfnReadCallbackR0 = pfnReadCallback;
1558 pRange->pfnWriteCallbackR0= pfnWriteCallback;
1559 pRange->pfnFillCallbackR0 = pfnFillCallback;
1560 pRange->pDevInsR0 = MMHyperCCToR0(pVM, pDevIns);
1561 IOM_UNLOCK_EXCL(pVM);
1562
1563 return VINF_SUCCESS;
1564}
1565
1566
1567/**
1568 * Deregisters a Memory Mapped I/O handler range.
1569 *
1570 * Registered GC, R0, and R3 ranges are affected.
1571 *
1572 * @returns VBox status code.
1573 *
1574 * @param pVM The cross context VM structure.
1575 * @param pDevIns Device instance which the MMIO region is registered.
1576 * @param GCPhysStart First physical address (GC) in the range.
1577 * @param cbRange Number of bytes to deregister.
1578 *
1579 * @remark This function mainly for PCI PnP Config and will not do
1580 * all the checks you might expect it to do.
1581 */
1582VMMR3_INT_DECL(int) IOMR3MmioDeregister(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTGCPHYS cbRange)
1583{
1584 LogFlow(("IOMR3MmioDeregister: pDevIns=%p GCPhysStart=%RGp cbRange=%RGp\n", pDevIns, GCPhysStart, cbRange));
1585
1586 /*
1587 * Validate input.
1588 */
1589 RTGCPHYS GCPhysLast = GCPhysStart + (cbRange - 1);
1590 if (GCPhysLast < GCPhysStart)
1591 {
1592 AssertMsgFailed(("Wrapped! %#x LB %RGp\n", GCPhysStart, cbRange));
1593 return VERR_IOM_INVALID_MMIO_RANGE;
1594 }
1595 PVMCPU pVCpu = VMMGetCpu(pVM); Assert(pVCpu);
1596
1597 IOM_LOCK_EXCL(pVM);
1598
1599 /*
1600 * Check ownership and such for the entire area.
1601 */
1602 RTGCPHYS GCPhys = GCPhysStart;
1603 while (GCPhys <= GCPhysLast && GCPhys >= GCPhysStart)
1604 {
1605 PIOMMMIORANGE pRange = iomMmioGetRange(pVM, pVCpu, GCPhys);
1606 if (!pRange)
1607 {
1608 IOM_UNLOCK_EXCL(pVM);
1609 return VERR_IOM_MMIO_RANGE_NOT_FOUND;
1610 }
1611 AssertMsgReturnStmt(pRange->pDevInsR3 == pDevIns,
1612 ("Not owner! GCPhys=%RGp %RGp LB %RGp %s\n", GCPhys, GCPhysStart, cbRange, pRange->pszDesc),
1613 IOM_UNLOCK_EXCL(pVM),
1614 VERR_IOM_NOT_MMIO_RANGE_OWNER);
1615 AssertMsgReturnStmt(pRange->Core.KeyLast <= GCPhysLast,
1616 ("Incomplete R3 range! GCPhys=%RGp %RGp LB %RGp %s\n", GCPhys, GCPhysStart, cbRange, pRange->pszDesc),
1617 IOM_UNLOCK_EXCL(pVM),
1618 VERR_IOM_INCOMPLETE_MMIO_RANGE);
1619
1620 /* next */
1621 Assert(GCPhys <= pRange->Core.KeyLast);
1622 GCPhys = pRange->Core.KeyLast + 1;
1623 }
1624
1625 /*
1626 * Do the actual removing of the MMIO ranges.
1627 */
1628 GCPhys = GCPhysStart;
1629 while (GCPhys <= GCPhysLast && GCPhys >= GCPhysStart)
1630 {
1631 iomR3FlushCache(pVM);
1632
1633 PIOMMMIORANGE pRange = (PIOMMMIORANGE)RTAvlroGCPhysRemove(&pVM->iom.s.pTreesR3->MMIOTree, GCPhys);
1634 Assert(pRange);
1635 Assert(pRange->Core.Key == GCPhys && pRange->Core.KeyLast <= GCPhysLast);
1636 IOM_UNLOCK_EXCL(pVM); /* Lock order fun. */
1637
1638 /* remove it from PGM */
1639 int rc = PGMR3PhysMMIODeregister(pVM, GCPhys, pRange->cb);
1640 AssertRC(rc);
1641
1642 IOM_LOCK_EXCL(pVM);
1643
1644 /* advance and free. */
1645 GCPhys = pRange->Core.KeyLast + 1;
1646 if (pDevIns->iInstance > 0)
1647 {
1648 void *pvDesc = ASMAtomicXchgPtr((void * volatile *)&pRange->pszDesc, NULL);
1649 MMR3HeapFree(pvDesc);
1650 }
1651 iomMmioReleaseRange(pVM, pRange);
1652 }
1653
1654 IOM_UNLOCK_EXCL(pVM);
1655 return VINF_SUCCESS;
1656}
1657
1658
1659/**
1660 * Pre-Registers a MMIO region.
1661 *
1662 * The rest of of the manipulation of this region goes thru the PGMPhysMMIOEx*
1663 * APIs: PGMR3PhysMMIOExMap, PGMR3PhysMMIOExUnmap, PGMR3PhysMMIOExDeregister
1664 *
1665 * @returns VBox status code.
1666 * @param pVM Pointer to the cross context VM structure.
1667 * @param pDevIns The device.
1668 * @param iSubDev The sub-device number.
1669 * @param iRegion The region number.
1670 * @param cbRegion The size of the MMIO region. Must be a multiple
1671 * of X86_PAGE_SIZE
1672 * @param fFlags Flags, see IOMMMIO_FLAGS_XXX.
1673 * @param pszDesc Pointer to description string. This must not be
1674 * freed.
1675 * @param pvUserR3 Ring-3 user pointer.
1676 * @param pfnWriteCallbackR3 Callback for handling writes, ring-3. Mandatory.
1677 * @param pfnReadCallbackR3 Callback for handling reads, ring-3. Mandatory.
1678 * @param pfnFillCallbackR3 Callback for handling fills, ring-3. Optional.
1679 * @param pvUserR0 Ring-0 user pointer.
1680 * @param pfnWriteCallbackR0 Callback for handling writes, ring-0. Optional.
1681 * @param pfnReadCallbackR0 Callback for handling reads, ring-0. Optional.
1682 * @param pfnFillCallbackR0 Callback for handling fills, ring-0. Optional.
1683 * @param pvUserRC Raw-mode context user pointer. This will be
1684 * relocated with the hypervisor guest mapping if
1685 * the unsigned integer value is 0x10000 or above.
1686 * @param pfnWriteCallbackRC Callback for handling writes, RC. Optional.
1687 * @param pfnReadCallbackRC Callback for handling reads, RC. Optional.
1688 * @param pfnFillCallbackRC Callback for handling fills, RC. Optional.
1689 */
1690VMMR3_INT_DECL(int) IOMR3MmioExPreRegister(PVM pVM, PPDMDEVINS pDevIns, uint32_t iSubDev, uint32_t iRegion, RTGCPHYS cbRegion,
1691 uint32_t fFlags, const char *pszDesc,
1692 RTR3PTR pvUserR3,
1693 R3PTRTYPE(PFNIOMMMIOWRITE) pfnWriteCallbackR3,
1694 R3PTRTYPE(PFNIOMMMIOREAD) pfnReadCallbackR3,
1695 R3PTRTYPE(PFNIOMMMIOFILL) pfnFillCallbackR3,
1696 RTR0PTR pvUserR0,
1697 R0PTRTYPE(PFNIOMMMIOWRITE) pfnWriteCallbackR0,
1698 R0PTRTYPE(PFNIOMMMIOREAD) pfnReadCallbackR0,
1699 R0PTRTYPE(PFNIOMMMIOFILL) pfnFillCallbackR0,
1700 RTRCPTR pvUserRC,
1701 RCPTRTYPE(PFNIOMMMIOWRITE) pfnWriteCallbackRC,
1702 RCPTRTYPE(PFNIOMMMIOREAD) pfnReadCallbackRC,
1703 RCPTRTYPE(PFNIOMMMIOFILL) pfnFillCallbackRC)
1704{
1705 LogFlow(("IOMR3MmioExPreRegister: pDevIns=%p iSubDev=%u iRegion=%u cbRegion=%RGp fFlags=%#x pszDesc=%s\n"
1706 " pvUserR3=%RHv pfnWriteCallbackR3=%RHv pfnReadCallbackR3=%RHv pfnFillCallbackR3=%RHv\n"
1707 " pvUserR0=%RHv pfnWriteCallbackR0=%RHv pfnReadCallbackR0=%RHv pfnFillCallbackR0=%RHv\n"
1708 " pvUserRC=%RRv pfnWriteCallbackRC=%RRv pfnReadCallbackRC=%RRv pfnFillCallbackRC=%RRv\n",
1709 pDevIns, iSubDev, iRegion, cbRegion, fFlags, pszDesc,
1710 pvUserR3, pfnWriteCallbackR3, pfnReadCallbackR3, pfnFillCallbackR3,
1711 pvUserR0, pfnWriteCallbackR0, pfnReadCallbackR0, pfnFillCallbackR0,
1712 pvUserRC, pfnWriteCallbackRC, pfnReadCallbackRC, pfnFillCallbackRC));
1713
1714 /*
1715 * Validate input.
1716 */
1717 AssertReturn(cbRegion > 0, VERR_INVALID_PARAMETER);
1718 AssertReturn(RT_ALIGN_T(cbRegion, X86_PAGE_SIZE, RTGCPHYS), VERR_INVALID_PARAMETER);
1719 AssertMsgReturn( !(fFlags & ~IOMMMIO_FLAGS_VALID_MASK)
1720 && (fFlags & IOMMMIO_FLAGS_READ_MODE) <= IOMMMIO_FLAGS_READ_DWORD_QWORD
1721 && (fFlags & IOMMMIO_FLAGS_WRITE_MODE) <= IOMMMIO_FLAGS_WRITE_ONLY_DWORD_QWORD,
1722 ("%#x\n", fFlags),
1723 VERR_INVALID_PARAMETER);
1724 AssertPtrReturn(pfnWriteCallbackR3, VERR_INVALID_POINTER);
1725 AssertPtrReturn(pfnReadCallbackR3, VERR_INVALID_POINTER);
1726
1727 /*
1728 * Allocate new range record and initialize it.
1729 */
1730 PIOMMMIORANGE pRange;
1731 int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
1732 if (RT_SUCCESS(rc))
1733 {
1734 pRange->Core.Key = NIL_RTGCPHYS;
1735 pRange->Core.KeyLast = NIL_RTGCPHYS;
1736 pRange->GCPhys = NIL_RTGCPHYS;
1737 pRange->cb = cbRegion;
1738 pRange->cRefs = 1; /* The PGM reference. */
1739 pRange->fFlags = fFlags;
1740
1741 pRange->pvUserR3 = pvUserR3;
1742 pRange->pDevInsR3 = pDevIns;
1743 pRange->pfnReadCallbackR3 = pfnReadCallbackR3;
1744 pRange->pfnWriteCallbackR3 = pfnWriteCallbackR3;
1745 pRange->pfnFillCallbackR3 = pfnFillCallbackR3;
1746 pRange->pszDesc = pszDesc;
1747
1748 if (pfnReadCallbackR0 || pfnWriteCallbackR0 || pfnFillCallbackR0)
1749 {
1750 pRange->pvUserR0 = pvUserR0;
1751 pRange->pDevInsR0 = MMHyperCCToR0(pVM, pDevIns);
1752 pRange->pfnReadCallbackR0 = pfnReadCallbackR0;
1753 pRange->pfnWriteCallbackR0 = pfnWriteCallbackR0;
1754 pRange->pfnFillCallbackR0 = pfnFillCallbackR0;
1755 }
1756
1757#if 0
1758 if (pfnReadCallbackRC || pfnWriteCallbackRC || pfnFillCallbackRC)
1759 {
1760 pRange->pvUserRC = pvUserRC;
1761 pRange->pDevInsRC = MMHyperCCToRC(pVM, pDevIns);
1762 pRange->pfnReadCallbackRC = pfnReadCallbackRC;
1763 pRange->pfnWriteCallbackRC = pfnWriteCallbackRC;
1764 pRange->pfnFillCallbackRC = pfnFillCallbackRC;
1765 }
1766#else
1767 RT_NOREF(pfnReadCallbackRC, pfnWriteCallbackRC, pfnFillCallbackRC, pvUserRC);
1768#endif
1769
1770 /*
1771 * Try register it with PGM. PGM will call us back when it's mapped in
1772 * and out of the guest address space, and once it's destroyed.
1773 */
1774 rc = PGMR3PhysMMIOExPreRegister(pVM, pDevIns, iSubDev, iRegion, cbRegion, pVM->iom.s.hMmioHandlerType,
1775 pRange, MMHyperR3ToR0(pVM, pRange), MMHyperR3ToRC(pVM, pRange), pszDesc);
1776 if (RT_SUCCESS(rc))
1777 return VINF_SUCCESS;
1778
1779 MMHyperFree(pVM, pRange);
1780 }
1781 if (pDevIns->iInstance > 0)
1782 MMR3HeapFree((void *)pszDesc);
1783 return rc;
1784
1785}
1786
1787
1788/**
1789 * Notfication from PGM that the pre-registered MMIO region has been mapped into
1790 * user address space.
1791 *
1792 * @returns VBox status code.
1793 * @param pVM Pointer to the cross context VM structure.
1794 * @param pvUser The pvUserR3 argument of PGMR3PhysMMIOExPreRegister.
1795 * @param GCPhys The mapping address.
1796 * @remarks Called while owning the PGM lock.
1797 */
1798VMMR3_INT_DECL(int) IOMR3MmioExNotifyMapped(PVM pVM, void *pvUser, RTGCPHYS GCPhys)
1799{
1800 PIOMMMIORANGE pRange = (PIOMMMIORANGE)pvUser;
1801 AssertReturn(pRange->GCPhys == NIL_RTGCPHYS, VERR_IOM_MMIO_IPE_1);
1802
1803 IOM_LOCK_EXCL(pVM);
1804 Assert(pRange->GCPhys == NIL_RTGCPHYS);
1805 pRange->GCPhys = GCPhys;
1806 pRange->Core.Key = GCPhys;
1807 pRange->Core.KeyLast = GCPhys + pRange->cb - 1;
1808 if (RTAvlroGCPhysInsert(&pVM->iom.s.pTreesR3->MMIOTree, &pRange->Core))
1809 {
1810 iomR3FlushCache(pVM);
1811 IOM_UNLOCK_EXCL(pVM);
1812 return VINF_SUCCESS;
1813 }
1814 IOM_UNLOCK_EXCL(pVM);
1815
1816 AssertLogRelMsgFailed(("RTAvlroGCPhysInsert failed on %RGp..%RGp - %s\n", pRange->Core.Key, pRange->Core.KeyLast, pRange->pszDesc));
1817 pRange->GCPhys = NIL_RTGCPHYS;
1818 pRange->Core.Key = NIL_RTGCPHYS;
1819 pRange->Core.KeyLast = NIL_RTGCPHYS;
1820 return VERR_IOM_MMIO_IPE_2;
1821}
1822
1823
1824/**
1825 * Notfication from PGM that the pre-registered MMIO region has been unmapped
1826 * from user address space.
1827 *
1828 * @param pVM Pointer to the cross context VM structure.
1829 * @param pvUser The pvUserR3 argument of PGMR3PhysMMIOExPreRegister.
1830 * @param GCPhys The mapping address.
1831 * @remarks Called while owning the PGM lock.
1832 */
1833VMMR3_INT_DECL(void) IOMR3MmioExNotifyUnmapped(PVM pVM, void *pvUser, RTGCPHYS GCPhys)
1834{
1835 PIOMMMIORANGE pRange = (PIOMMMIORANGE)pvUser;
1836 AssertLogRelReturnVoid(pRange->GCPhys == GCPhys);
1837
1838 IOM_LOCK_EXCL(pVM);
1839 Assert(pRange->GCPhys == GCPhys);
1840 PIOMMMIORANGE pRemoved = (PIOMMMIORANGE)RTAvlroGCPhysRemove(&pVM->iom.s.pTreesR3->MMIOTree, GCPhys);
1841 if (pRemoved == pRange)
1842 {
1843 pRange->GCPhys = NIL_RTGCPHYS;
1844 pRange->Core.Key = NIL_RTGCPHYS;
1845 pRange->Core.KeyLast = NIL_RTGCPHYS;
1846 iomR3FlushCache(pVM);
1847 IOM_UNLOCK_EXCL(pVM);
1848 }
1849 else
1850 {
1851 if (pRemoved)
1852 RTAvlroGCPhysInsert(&pVM->iom.s.pTreesR3->MMIOTree, &pRemoved->Core);
1853 IOM_UNLOCK_EXCL(pVM);
1854 AssertLogRelMsgFailed(("RTAvlroGCPhysRemove returned %p instead of %p for %RGp (%s)\n",
1855 pRemoved, pRange, GCPhys, pRange->pszDesc));
1856 }
1857}
1858
1859
1860/**
1861 * Notfication from PGM that the pre-registered MMIO region has been mapped into
1862 * user address space.
1863 *
1864 * @param pVM Pointer to the cross context VM structure.
1865 * @param pvUser The pvUserR3 argument of PGMR3PhysMMIOExPreRegister.
1866 * @remarks Called while owning the PGM lock.
1867 */
1868VMMR3_INT_DECL(void) IOMR3MmioExNotifyDeregistered(PVM pVM, void *pvUser)
1869{
1870 PIOMMMIORANGE pRange = (PIOMMMIORANGE)pvUser;
1871 AssertLogRelReturnVoid(pRange->GCPhys == NIL_RTGCPHYS);
1872 iomMmioReleaseRange(pVM, pRange);
1873}
1874
1875
1876/**
1877 * Handles the unlikely and probably fatal merge cases.
1878 *
1879 * @returns Merged status code.
1880 * @param rcStrict Current EM status code.
1881 * @param rcStrictCommit The IOM I/O or MMIO write commit status to merge
1882 * with @a rcStrict.
1883 * @param rcIom For logging purposes only.
1884 * @param pVCpu The cross context virtual CPU structure of the
1885 * calling EMT. For logging purposes.
1886 */
1887DECL_NO_INLINE(static, VBOXSTRICTRC) iomR3MergeStatusSlow(VBOXSTRICTRC rcStrict, VBOXSTRICTRC rcStrictCommit,
1888 int rcIom, PVMCPU pVCpu)
1889{
1890 if (RT_FAILURE_NP(rcStrict))
1891 return rcStrict;
1892
1893 if (RT_FAILURE_NP(rcStrictCommit))
1894 return rcStrictCommit;
1895
1896 if (rcStrict == rcStrictCommit)
1897 return rcStrictCommit;
1898
1899 AssertLogRelMsgFailed(("rcStrictCommit=%Rrc rcStrict=%Rrc IOPort={%#06x<-%#xx/%u} MMIO={%RGp<-%.*Rhxs} (rcIom=%Rrc)\n",
1900 VBOXSTRICTRC_VAL(rcStrictCommit), VBOXSTRICTRC_VAL(rcStrict),
1901 pVCpu->iom.s.PendingIOPortWrite.IOPort,
1902 pVCpu->iom.s.PendingIOPortWrite.u32Value, pVCpu->iom.s.PendingIOPortWrite.cbValue,
1903 pVCpu->iom.s.PendingMmioWrite.GCPhys,
1904 pVCpu->iom.s.PendingMmioWrite.cbValue, &pVCpu->iom.s.PendingMmioWrite.abValue[0], rcIom));
1905 return VERR_IOM_FF_STATUS_IPE;
1906}
1907
1908
1909/**
1910 * Helper for IOMR3ProcessForceFlag.
1911 *
1912 * @returns Merged status code.
1913 * @param rcStrict Current EM status code.
1914 * @param rcStrictCommit The IOM I/O or MMIO write commit status to merge
1915 * with @a rcStrict.
1916 * @param rcIom Either VINF_IOM_R3_IOPORT_COMMIT_WRITE or
1917 * VINF_IOM_R3_MMIO_COMMIT_WRITE.
1918 * @param pVCpu The cross context virtual CPU structure of the
1919 * calling EMT.
1920 */
1921DECLINLINE(VBOXSTRICTRC) iomR3MergeStatus(VBOXSTRICTRC rcStrict, VBOXSTRICTRC rcStrictCommit, int rcIom, PVMCPU pVCpu)
1922{
1923 /* Simple. */
1924 if (RT_LIKELY(rcStrict == rcIom || rcStrict == VINF_EM_RAW_TO_R3 || rcStrict == VINF_SUCCESS))
1925 return rcStrictCommit;
1926
1927 if (RT_LIKELY(rcStrictCommit == VINF_SUCCESS))
1928 return rcStrict;
1929
1930 /* EM scheduling status codes. */
1931 if (RT_LIKELY( rcStrict >= VINF_EM_FIRST
1932 && rcStrict <= VINF_EM_LAST))
1933 {
1934 if (RT_LIKELY( rcStrictCommit >= VINF_EM_FIRST
1935 && rcStrictCommit <= VINF_EM_LAST))
1936 return rcStrict < rcStrictCommit ? rcStrict : rcStrictCommit;
1937 }
1938
1939 /* Unlikely */
1940 return iomR3MergeStatusSlow(rcStrict, rcStrictCommit, rcIom, pVCpu);
1941}
1942
1943
1944/**
1945 * Called by force-flag handling code when VMCPU_FF_IOM is set.
1946 *
1947 * @returns Merge between @a rcStrict and what the commit operation returned.
1948 * @param pVM The cross context VM structure.
1949 * @param pVCpu The cross context virtual CPU structure of the calling EMT.
1950 * @param rcStrict The status code returned by ring-0 or raw-mode.
1951 * @thread EMT(pVCpu)
1952 *
1953 * @remarks The VMCPU_FF_IOM flag is handled before the status codes by EM, so
1954 * we're very likely to see @a rcStrict set to
1955 * VINF_IOM_R3_IOPORT_COMMIT_WRITE and VINF_IOM_R3_MMIO_COMMIT_WRITE
1956 * here.
1957 */
1958VMMR3_INT_DECL(VBOXSTRICTRC) IOMR3ProcessForceFlag(PVM pVM, PVMCPU pVCpu, VBOXSTRICTRC rcStrict)
1959{
1960 VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_IOM);
1961 Assert(pVCpu->iom.s.PendingIOPortWrite.cbValue || pVCpu->iom.s.PendingMmioWrite.cbValue);
1962
1963 if (pVCpu->iom.s.PendingIOPortWrite.cbValue)
1964 {
1965 Log5(("IOM: Dispatching pending I/O port write: %#x LB %u -> %RTiop\n", pVCpu->iom.s.PendingIOPortWrite.u32Value,
1966 pVCpu->iom.s.PendingIOPortWrite.cbValue, pVCpu->iom.s.PendingIOPortWrite.IOPort));
1967 VBOXSTRICTRC rcStrictCommit = IOMIOPortWrite(pVM, pVCpu, pVCpu->iom.s.PendingIOPortWrite.IOPort,
1968 pVCpu->iom.s.PendingIOPortWrite.u32Value,
1969 pVCpu->iom.s.PendingIOPortWrite.cbValue);
1970 pVCpu->iom.s.PendingIOPortWrite.cbValue = 0;
1971 rcStrict = iomR3MergeStatus(rcStrict, rcStrictCommit, VINF_IOM_R3_IOPORT_COMMIT_WRITE, pVCpu);
1972 }
1973
1974
1975 if (pVCpu->iom.s.PendingMmioWrite.cbValue)
1976 {
1977 Log5(("IOM: Dispatching pending MMIO write: %RGp LB %#x\n",
1978 pVCpu->iom.s.PendingMmioWrite.GCPhys, pVCpu->iom.s.PendingMmioWrite.cbValue));
1979 /** @todo Try optimize this some day? Currently easier and correcter to
1980 * involve PGM here since we never know if the MMIO area is still mapped
1981 * to the same location as when we wrote to it in RC/R0 context. */
1982 VBOXSTRICTRC rcStrictCommit = PGMPhysWrite(pVM, pVCpu->iom.s.PendingMmioWrite.GCPhys,
1983 pVCpu->iom.s.PendingMmioWrite.abValue, pVCpu->iom.s.PendingMmioWrite.cbValue,
1984 PGMACCESSORIGIN_IOM);
1985 pVCpu->iom.s.PendingMmioWrite.cbValue = 0;
1986 rcStrict = iomR3MergeStatus(rcStrict, rcStrictCommit, VINF_IOM_R3_MMIO_COMMIT_WRITE, pVCpu);
1987 }
1988
1989 return rcStrict;
1990}
1991
1992
1993/**
1994 * Notification from DBGF that the number of active I/O port or MMIO
1995 * breakpoints has change.
1996 *
1997 * For performance reasons, IOM will only call DBGF before doing I/O and MMIO
1998 * accesses where there are armed breakpoints.
1999 *
2000 * @param pVM The cross context VM structure.
2001 * @param fPortIo True if there are armed I/O port breakpoints.
2002 * @param fMmio True if there are armed MMIO breakpoints.
2003 */
2004VMMR3_INT_DECL(void) IOMR3NotifyBreakpointCountChange(PVM pVM, bool fPortIo, bool fMmio)
2005{
2006 /** @todo I/O breakpoints. */
2007 RT_NOREF3(pVM, fPortIo, fMmio);
2008}
2009
2010
2011/**
2012 * Notification from DBGF that an event has been enabled or disabled.
2013 *
2014 * For performance reasons, IOM may cache the state of events it implements.
2015 *
2016 * @param pVM The cross context VM structure.
2017 * @param enmEvent The event.
2018 * @param fEnabled The new state.
2019 */
2020VMMR3_INT_DECL(void) IOMR3NotifyDebugEventChange(PVM pVM, DBGFEVENT enmEvent, bool fEnabled)
2021{
2022 /** @todo IOM debug events. */
2023 RT_NOREF3(pVM, enmEvent, fEnabled);
2024}
2025
2026
2027/**
2028 * Display a single MMIO range.
2029 *
2030 * @returns 0
2031 * @param pNode Pointer to MMIO R3 range.
2032 * @param pvUser Pointer to info output callback structure.
2033 */
2034static DECLCALLBACK(int) iomR3MMIOInfoOne(PAVLROGCPHYSNODECORE pNode, void *pvUser)
2035{
2036 PIOMMMIORANGE pRange = (PIOMMMIORANGE)pNode;
2037 PCDBGFINFOHLP pHlp = (PCDBGFINFOHLP)pvUser;
2038 pHlp->pfnPrintf(pHlp,
2039 "%RGp-%RGp %RHv %RHv %RHv %RHv %RHv %s\n",
2040 pRange->Core.Key,
2041 pRange->Core.KeyLast,
2042 pRange->pDevInsR3,
2043 pRange->pfnReadCallbackR3,
2044 pRange->pfnWriteCallbackR3,
2045 pRange->pfnFillCallbackR3,
2046 pRange->pvUserR3,
2047 pRange->pszDesc);
2048 pHlp->pfnPrintf(pHlp,
2049 "%*s %RHv %RHv %RHv %RHv %RHv\n",
2050 sizeof(RTGCPHYS) * 2 * 2 + 1, "R0",
2051 pRange->pDevInsR0,
2052 pRange->pfnReadCallbackR0,
2053 pRange->pfnWriteCallbackR0,
2054 pRange->pfnFillCallbackR0,
2055 pRange->pvUserR0);
2056#if 0
2057 pHlp->pfnPrintf(pHlp,
2058 "%*s %RRv %RRv %RRv %RRv %RRv\n",
2059 sizeof(RTGCPHYS) * 2 * 2 + 1, "RC",
2060 pRange->pDevInsRC,
2061 pRange->pfnReadCallbackRC,
2062 pRange->pfnWriteCallbackRC,
2063 pRange->pfnFillCallbackRC,
2064 pRange->pvUserRC);
2065#endif
2066 return 0;
2067}
2068
2069
2070/**
2071 * Display registered MMIO ranges to the log.
2072 *
2073 * @param pVM The cross context VM structure.
2074 * @param pHlp The info helpers.
2075 * @param pszArgs Arguments, ignored.
2076 */
2077static DECLCALLBACK(void) iomR3MMIOInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
2078{
2079 NOREF(pszArgs);
2080 pHlp->pfnPrintf(pHlp,
2081 "MMIO ranges (pVM=%p)\n"
2082 "%.*s %.*s %.*s %.*s %.*s %.*s %s\n",
2083 pVM,
2084 sizeof(RTGCPHYS) * 4 + 1, "GC Phys Range ",
2085 sizeof(RTHCPTR) * 2, "pDevIns ",
2086 sizeof(RTHCPTR) * 2, "Read ",
2087 sizeof(RTHCPTR) * 2, "Write ",
2088 sizeof(RTHCPTR) * 2, "Fill ",
2089 sizeof(RTHCPTR) * 2, "pvUser ",
2090 "Description");
2091 RTAvlroGCPhysDoWithAll(&pVM->iom.s.pTreesR3->MMIOTree, true, iomR3MMIOInfoOne, (void *)pHlp);
2092}
2093
2094
2095#ifdef VBOX_WITH_STATISTICS
2096/**
2097 * Tries to come up with the standard name for a port.
2098 *
2099 * @returns Pointer to readonly string if known.
2100 * @returns NULL if unknown port number.
2101 *
2102 * @param Port The port to name.
2103 */
2104static const char *iomR3IOPortGetStandardName(RTIOPORT Port)
2105{
2106 switch (Port)
2107 {
2108 case 0x00: case 0x10: case 0x20: case 0x30: case 0x40: case 0x50: case 0x70:
2109 case 0x01: case 0x11: case 0x21: case 0x31: case 0x41: case 0x51: case 0x61: case 0x71:
2110 case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: case 0x62: case 0x72:
2111 case 0x03: case 0x13: case 0x23: case 0x33: case 0x43: case 0x53: case 0x63: case 0x73:
2112 case 0x04: case 0x14: case 0x24: case 0x34: case 0x44: case 0x54: case 0x74:
2113 case 0x05: case 0x15: case 0x25: case 0x35: case 0x45: case 0x55: case 0x65: case 0x75:
2114 case 0x06: case 0x16: case 0x26: case 0x36: case 0x46: case 0x56: case 0x66: case 0x76:
2115 case 0x07: case 0x17: case 0x27: case 0x37: case 0x47: case 0x57: case 0x67: case 0x77:
2116 case 0x08: case 0x18: case 0x28: case 0x38: case 0x48: case 0x58: case 0x68: case 0x78:
2117 case 0x09: case 0x19: case 0x29: case 0x39: case 0x49: case 0x59: case 0x69: case 0x79:
2118 case 0x0a: case 0x1a: case 0x2a: case 0x3a: case 0x4a: case 0x5a: case 0x6a: case 0x7a:
2119 case 0x0b: case 0x1b: case 0x2b: case 0x3b: case 0x4b: case 0x5b: case 0x6b: case 0x7b:
2120 case 0x0c: case 0x1c: case 0x2c: case 0x3c: case 0x4c: case 0x5c: case 0x6c: case 0x7c:
2121 case 0x0d: case 0x1d: case 0x2d: case 0x3d: case 0x4d: case 0x5d: case 0x6d: case 0x7d:
2122 case 0x0e: case 0x1e: case 0x2e: case 0x3e: case 0x4e: case 0x5e: case 0x6e: case 0x7e:
2123 case 0x0f: case 0x1f: case 0x2f: case 0x3f: case 0x4f: case 0x5f: case 0x6f: case 0x7f:
2124
2125 case 0x80: case 0x90: case 0xa0: case 0xb0: case 0xc0: case 0xd0: case 0xe0: case 0xf0:
2126 case 0x81: case 0x91: case 0xa1: case 0xb1: case 0xc1: case 0xd1: case 0xe1: case 0xf1:
2127 case 0x82: case 0x92: case 0xa2: case 0xb2: case 0xc2: case 0xd2: case 0xe2: case 0xf2:
2128 case 0x83: case 0x93: case 0xa3: case 0xb3: case 0xc3: case 0xd3: case 0xe3: case 0xf3:
2129 case 0x84: case 0x94: case 0xa4: case 0xb4: case 0xc4: case 0xd4: case 0xe4: case 0xf4:
2130 case 0x85: case 0x95: case 0xa5: case 0xb5: case 0xc5: case 0xd5: case 0xe5: case 0xf5:
2131 case 0x86: case 0x96: case 0xa6: case 0xb6: case 0xc6: case 0xd6: case 0xe6: case 0xf6:
2132 case 0x87: case 0x97: case 0xa7: case 0xb7: case 0xc7: case 0xd7: case 0xe7: case 0xf7:
2133 case 0x88: case 0x98: case 0xa8: case 0xb8: case 0xc8: case 0xd8: case 0xe8: case 0xf8:
2134 case 0x89: case 0x99: case 0xa9: case 0xb9: case 0xc9: case 0xd9: case 0xe9: case 0xf9:
2135 case 0x8a: case 0x9a: case 0xaa: case 0xba: case 0xca: case 0xda: case 0xea: case 0xfa:
2136 case 0x8b: case 0x9b: case 0xab: case 0xbb: case 0xcb: case 0xdb: case 0xeb: case 0xfb:
2137 case 0x8c: case 0x9c: case 0xac: case 0xbc: case 0xcc: case 0xdc: case 0xec: case 0xfc:
2138 case 0x8d: case 0x9d: case 0xad: case 0xbd: case 0xcd: case 0xdd: case 0xed: case 0xfd:
2139 case 0x8e: case 0x9e: case 0xae: case 0xbe: case 0xce: case 0xde: case 0xee: case 0xfe:
2140 case 0x8f: case 0x9f: case 0xaf: case 0xbf: case 0xcf: case 0xdf: case 0xef: case 0xff:
2141 return "System Reserved";
2142
2143 case 0x60:
2144 case 0x64:
2145 return "Keyboard & Mouse";
2146
2147 case 0x378:
2148 case 0x379:
2149 case 0x37a:
2150 case 0x37b:
2151 case 0x37c:
2152 case 0x37d:
2153 case 0x37e:
2154 case 0x37f:
2155 case 0x3bc:
2156 case 0x3bd:
2157 case 0x3be:
2158 case 0x3bf:
2159 case 0x278:
2160 case 0x279:
2161 case 0x27a:
2162 case 0x27b:
2163 case 0x27c:
2164 case 0x27d:
2165 case 0x27e:
2166 case 0x27f:
2167 return "LPT1/2/3";
2168
2169 case 0x3f8:
2170 case 0x3f9:
2171 case 0x3fa:
2172 case 0x3fb:
2173 case 0x3fc:
2174 case 0x3fd:
2175 case 0x3fe:
2176 case 0x3ff:
2177 return "COM1";
2178
2179 case 0x2f8:
2180 case 0x2f9:
2181 case 0x2fa:
2182 case 0x2fb:
2183 case 0x2fc:
2184 case 0x2fd:
2185 case 0x2fe:
2186 case 0x2ff:
2187 return "COM2";
2188
2189 case 0x3e8:
2190 case 0x3e9:
2191 case 0x3ea:
2192 case 0x3eb:
2193 case 0x3ec:
2194 case 0x3ed:
2195 case 0x3ee:
2196 case 0x3ef:
2197 return "COM3";
2198
2199 case 0x2e8:
2200 case 0x2e9:
2201 case 0x2ea:
2202 case 0x2eb:
2203 case 0x2ec:
2204 case 0x2ed:
2205 case 0x2ee:
2206 case 0x2ef:
2207 return "COM4";
2208
2209 case 0x200:
2210 case 0x201:
2211 case 0x202:
2212 case 0x203:
2213 case 0x204:
2214 case 0x205:
2215 case 0x206:
2216 case 0x207:
2217 return "Joystick";
2218
2219 case 0x3f0:
2220 case 0x3f1:
2221 case 0x3f2:
2222 case 0x3f3:
2223 case 0x3f4:
2224 case 0x3f5:
2225 case 0x3f6:
2226 case 0x3f7:
2227 return "Floppy";
2228
2229 case 0x1f0:
2230 case 0x1f1:
2231 case 0x1f2:
2232 case 0x1f3:
2233 case 0x1f4:
2234 case 0x1f5:
2235 case 0x1f6:
2236 case 0x1f7:
2237 //case 0x3f6:
2238 //case 0x3f7:
2239 return "IDE 1st";
2240
2241 case 0x170:
2242 case 0x171:
2243 case 0x172:
2244 case 0x173:
2245 case 0x174:
2246 case 0x175:
2247 case 0x176:
2248 case 0x177:
2249 case 0x376:
2250 case 0x377:
2251 return "IDE 2nd";
2252
2253 case 0x1e0:
2254 case 0x1e1:
2255 case 0x1e2:
2256 case 0x1e3:
2257 case 0x1e4:
2258 case 0x1e5:
2259 case 0x1e6:
2260 case 0x1e7:
2261 case 0x3e6:
2262 case 0x3e7:
2263 return "IDE 3rd";
2264
2265 case 0x160:
2266 case 0x161:
2267 case 0x162:
2268 case 0x163:
2269 case 0x164:
2270 case 0x165:
2271 case 0x166:
2272 case 0x167:
2273 case 0x366:
2274 case 0x367:
2275 return "IDE 4th";
2276
2277 case 0x130: case 0x140: case 0x150:
2278 case 0x131: case 0x141: case 0x151:
2279 case 0x132: case 0x142: case 0x152:
2280 case 0x133: case 0x143: case 0x153:
2281 case 0x134: case 0x144: case 0x154:
2282 case 0x135: case 0x145: case 0x155:
2283 case 0x136: case 0x146: case 0x156:
2284 case 0x137: case 0x147: case 0x157:
2285 case 0x138: case 0x148: case 0x158:
2286 case 0x139: case 0x149: case 0x159:
2287 case 0x13a: case 0x14a: case 0x15a:
2288 case 0x13b: case 0x14b: case 0x15b:
2289 case 0x13c: case 0x14c: case 0x15c:
2290 case 0x13d: case 0x14d: case 0x15d:
2291 case 0x13e: case 0x14e: case 0x15e:
2292 case 0x13f: case 0x14f: case 0x15f:
2293 case 0x220: case 0x230:
2294 case 0x221: case 0x231:
2295 case 0x222: case 0x232:
2296 case 0x223: case 0x233:
2297 case 0x224: case 0x234:
2298 case 0x225: case 0x235:
2299 case 0x226: case 0x236:
2300 case 0x227: case 0x237:
2301 case 0x228: case 0x238:
2302 case 0x229: case 0x239:
2303 case 0x22a: case 0x23a:
2304 case 0x22b: case 0x23b:
2305 case 0x22c: case 0x23c:
2306 case 0x22d: case 0x23d:
2307 case 0x22e: case 0x23e:
2308 case 0x22f: case 0x23f:
2309 case 0x330: case 0x340: case 0x350:
2310 case 0x331: case 0x341: case 0x351:
2311 case 0x332: case 0x342: case 0x352:
2312 case 0x333: case 0x343: case 0x353:
2313 case 0x334: case 0x344: case 0x354:
2314 case 0x335: case 0x345: case 0x355:
2315 case 0x336: case 0x346: case 0x356:
2316 case 0x337: case 0x347: case 0x357:
2317 case 0x338: case 0x348: case 0x358:
2318 case 0x339: case 0x349: case 0x359:
2319 case 0x33a: case 0x34a: case 0x35a:
2320 case 0x33b: case 0x34b: case 0x35b:
2321 case 0x33c: case 0x34c: case 0x35c:
2322 case 0x33d: case 0x34d: case 0x35d:
2323 case 0x33e: case 0x34e: case 0x35e:
2324 case 0x33f: case 0x34f: case 0x35f:
2325 return "SCSI (typically)";
2326
2327 case 0x320:
2328 case 0x321:
2329 case 0x322:
2330 case 0x323:
2331 case 0x324:
2332 case 0x325:
2333 case 0x326:
2334 case 0x327:
2335 return "XT HD";
2336
2337 case 0x3b0:
2338 case 0x3b1:
2339 case 0x3b2:
2340 case 0x3b3:
2341 case 0x3b4:
2342 case 0x3b5:
2343 case 0x3b6:
2344 case 0x3b7:
2345 case 0x3b8:
2346 case 0x3b9:
2347 case 0x3ba:
2348 case 0x3bb:
2349 return "VGA";
2350
2351 case 0x3c0: case 0x3d0:
2352 case 0x3c1: case 0x3d1:
2353 case 0x3c2: case 0x3d2:
2354 case 0x3c3: case 0x3d3:
2355 case 0x3c4: case 0x3d4:
2356 case 0x3c5: case 0x3d5:
2357 case 0x3c6: case 0x3d6:
2358 case 0x3c7: case 0x3d7:
2359 case 0x3c8: case 0x3d8:
2360 case 0x3c9: case 0x3d9:
2361 case 0x3ca: case 0x3da:
2362 case 0x3cb: case 0x3db:
2363 case 0x3cc: case 0x3dc:
2364 case 0x3cd: case 0x3dd:
2365 case 0x3ce: case 0x3de:
2366 case 0x3cf: case 0x3df:
2367 return "VGA/EGA";
2368
2369 case 0x240: case 0x260: case 0x280:
2370 case 0x241: case 0x261: case 0x281:
2371 case 0x242: case 0x262: case 0x282:
2372 case 0x243: case 0x263: case 0x283:
2373 case 0x244: case 0x264: case 0x284:
2374 case 0x245: case 0x265: case 0x285:
2375 case 0x246: case 0x266: case 0x286:
2376 case 0x247: case 0x267: case 0x287:
2377 case 0x248: case 0x268: case 0x288:
2378 case 0x249: case 0x269: case 0x289:
2379 case 0x24a: case 0x26a: case 0x28a:
2380 case 0x24b: case 0x26b: case 0x28b:
2381 case 0x24c: case 0x26c: case 0x28c:
2382 case 0x24d: case 0x26d: case 0x28d:
2383 case 0x24e: case 0x26e: case 0x28e:
2384 case 0x24f: case 0x26f: case 0x28f:
2385 case 0x300:
2386 case 0x301:
2387 case 0x388:
2388 case 0x389:
2389 case 0x38a:
2390 case 0x38b:
2391 return "Sound Card (typically)";
2392
2393 default:
2394 return NULL;
2395 }
2396}
2397#endif /* VBOX_WITH_STATISTICS */
2398
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