VirtualBox

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

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

IOM: I/O port statistics. bugref:9218

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