VirtualBox

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

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

IOM: Dump new-style I/O ports in the 'ioport' info item. bugref:9218

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