VirtualBox

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

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette