VirtualBox

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

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

PDM,PGM: Added handled based MMIO2 interface. Made some adjustments to the PCI I/O region registrations. (Preps for VMMDev.) bugref:9218

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