VirtualBox

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

Last change on this file since 19602 was 19470, checked in by vboxsync, 16 years ago

Added IOM lock

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