VirtualBox

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

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

VMM,Devices: Some PDM device model refactoring. bugref:9218

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