VirtualBox

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

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

IOM: oops. bugref:9218

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