VirtualBox

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

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

IOM,RTC,PCI: Make the port parameter in the I/O port callbacks relative to the start of the mapping rather than absolute. For absolute port numbers, use the IOM_IOPORT_F_ABS flag. bugref:9218

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