VirtualBox

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

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

IOM,PDMDevHlp: Started implementing new MMIO registration APIs. Splitting up IOM.cpp into I/O port and MMIO source files. bugref:9218

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