VirtualBox

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

Last change on this file since 77758 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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