IOM.cpp revision 323b78bf4831666c95416edf3b6e54657a769e5d
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * IOM - Input / Output Monitor.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * available from http://www.virtualbox.org. This file is free software;
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * you can redistribute it and/or modify it under the terms of the GNU
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * General Public License (GPL) as published by the Free Software
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * additional information or have any questions.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync/** @page pg_iom IOM - The Input / Output Monitor
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * The input/output monitor will handle I/O exceptions routing them to the
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * appropriate device. It implements an API to register and deregister virtual
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * I/0 port handlers and memory mapped I/O handlers. A handler is PDM devices
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * and a set of callback functions.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @section sec_iom_rawmode Raw-Mode
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * In raw-mode I/O port access is trapped (\#GP(0)) by ensuring that the actual
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * IOPL is 0 regardless of what the guest IOPL is. The \#GP handler use the
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * dissassembler (DIS) to figure which instruction caused it (there are a number
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * of instructions in addition to the I/O ones) and if it's an I/O port access
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * it will hand it to IOMGCIOPortHandler (via EMInterpretPortIO).
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * IOMGCIOPortHandler will lookup the port in the AVL tree of registered
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * handlers. If found, the handler will be called otherwise default action is
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * taken. (Default action is to write into the void and read all set bits.)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Memory Mapped I/O (MMIO) is implemented as a sligtly special case of PGM
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * access handlers. An MMIO range is registered with IOM which then registers it
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * with the PGM access handler sub-system. The access handler catches all
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * access and will be called in the context of a \#PF handler. In RC and R0 this
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * handler is IOMMMIOHandler while in ring-3 it's IOMR3MMIOHandler (althought in
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * ring-3 there can be alternative ways). IOMMMIOHandler will attempt to emulate
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * the instruction that is doing the access and pass the corresponding reads /
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * writes to the device.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Emulating I/O port access is less complex and should be sligtly faster than
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * emulating MMIO, so in most cases we should encourage the OS to use port I/O.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Devices which are freqently accessed should register GC handlers to speed up
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * execution.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @section sec_iom_hwaccm Hardware Assisted Virtualization Mode
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * When running in hardware assisted virtualization mode we'll be doing much the
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * same things as in raw-mode. The main difference is that we're running in the
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * host ring-0 context and that we don't get faults (\#GP(0) and \#PG) but
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @section sec_iom_rem Recompiled Execution Mode
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * When running in the recompiler things are different. I/O port access is
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * handled by calling IOMIOPortRead and IOMIOPortWrite directly. While MMIO can
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * be handled in one of two ways. The normal way is that we have a registered a
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * special RAM range with the recompiler and in the three callbacks (for byte,
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * word and dword access) we call IOMMMIORead and IOMMMIOWrite directly. The
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * alternative ways that the physical memory access which goes via PGM will take
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * care of it by calling IOMR3MMIOHandler via the PGM access handler machinery
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * - this shouldn't happen but it is an alternative...
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @section sec_iom_other Other Accesses
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * I/O ports aren't really exposed in any other way, unless you count the
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * instruction interpreter in EM, but that's just what we're doing in the
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * raw-mode \#GP(0) case really. Now it's possible to call IOMIOPortRead and
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * IOMIOPortWrite directly to talk to a device, but this is really bad behavior
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * and should only be done as temporary hacks (the PC BIOS device used to
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * setup the CMOS this way back in the dark ages).
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * MMIO has similar direct routes as the I/O ports and these shouldn't be used
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * for the same reasons and with the same restrictions. OTOH since MMIO is
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * mapped into the physical memory address space, it can be accessed in a number
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * of ways thru PGM.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync/*******************************************************************************
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync* Header Files *
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync*******************************************************************************/
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync/*******************************************************************************
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync* Internal Functions *
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync*******************************************************************************/
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic DECLCALLBACK(int) iomR3RelocateIOPortCallback(PAVLROIOPORTNODECORE pNode, void *pvUser);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic DECLCALLBACK(int) iomR3RelocateMMIOCallback(PAVLROGCPHYSNODECORE pNode, void *pvUser);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic DECLCALLBACK(void) iomR3IOPortInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic DECLCALLBACK(void) iomR3MMIOInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic DECLCALLBACK(int) iomR3IOPortDummyIn(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic DECLCALLBACK(int) iomR3IOPortDummyOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic DECLCALLBACK(int) iomR3IOPortDummyInStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic DECLCALLBACK(int) iomR3IOPortDummyOutStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic const char *iomR3IOPortGetStandardName(RTIOPORT Port);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Initializes the IOM.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @returns VBox status code.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM The VM to operate on.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Assert alignment and sizes.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertRelease(sizeof(pVM->iom.s) <= sizeof(pVM->iom.padding));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Setup any fixed pointers and offsets.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Allocate the trees structure.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync int rc = MMHyperAlloc(pVM, sizeof(*pVM->iom.s.pTreesR3), 0, MM_TAG_IOM, (void **)&pVM->iom.s.pTreesR3);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync pVM->iom.s.pTreesRC = MMHyperR3ToRC(pVM, pVM->iom.s.pTreesR3);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync pVM->iom.s.pTreesR0 = MMHyperR3ToR0(pVM, pVM->iom.s.pTreesR3);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync DBGFR3InfoRegisterInternal(pVM, "ioport", "Dumps all IOPort ranges. No arguments.", &iomR3IOPortInfo);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync DBGFR3InfoRegisterInternal(pVM, "mmio", "Dumps all MMIO ranges. No arguments.", &iomR3MMIOInfo);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Statistics.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZMMIOHandler, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler", STAMUNIT_TICKS_PER_CALL, "Profiling of the IOMMMIOHandler() body, only success calls.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZMMIO1Byte, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Access1", STAMUNIT_OCCURENCES, "MMIO access by 1 byte counter.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZMMIO2Bytes, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Access2", STAMUNIT_OCCURENCES, "MMIO access by 2 bytes counter.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZMMIO4Bytes, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Access4", STAMUNIT_OCCURENCES, "MMIO access by 4 bytes counter.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZMMIO8Bytes, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Access8", STAMUNIT_OCCURENCES, "MMIO access by 8 bytes counter.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZMMIOFailures, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/MMIOFailures", STAMUNIT_OCCURENCES, "Number of times IOMMMIOHandler() didn't service the request.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstMov, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/MOV", STAMUNIT_TICKS_PER_CALL, "Profiling of the MOV instruction emulation.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstCmp, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/CMP", STAMUNIT_TICKS_PER_CALL, "Profiling of the CMP instruction emulation.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstAnd, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/AND", STAMUNIT_TICKS_PER_CALL, "Profiling of the AND instruction emulation.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstOr, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/OR", STAMUNIT_TICKS_PER_CALL, "Profiling of the OR instruction emulation.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstXor, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/XOR", STAMUNIT_TICKS_PER_CALL, "Profiling of the XOR instruction emulation.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstBt, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/BT", STAMUNIT_TICKS_PER_CALL, "Profiling of the BT instruction emulation.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstTest, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/TEST", STAMUNIT_TICKS_PER_CALL, "Profiling of the TEST instruction emulation.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstXchg, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/XCHG", STAMUNIT_TICKS_PER_CALL, "Profiling of the XCHG instruction emulation.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstStos, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/STOS", STAMUNIT_TICKS_PER_CALL, "Profiling of the STOS instruction emulation.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstLods, STAMTYPE_PROFILE, "/IOM/RZ-MMIOHandler/Inst/LODS", STAMUNIT_TICKS_PER_CALL, "Profiling of the LODS instruction emulation.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync 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.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync 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.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync 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.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync 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.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatRZInstOther, STAMTYPE_COUNTER, "/IOM/RZ-MMIOHandler/Inst/Other", STAMUNIT_OCCURENCES, "Other instructions counter.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatR3MMIOHandler, STAMTYPE_COUNTER, "/IOM/R3-MMIOHandler", STAMUNIT_OCCURENCES, "Number of calls to IOMR3MMIOHandler.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatInstIn, STAMTYPE_COUNTER, "/IOM/IOWork/In", STAMUNIT_OCCURENCES, "Counter of any IN instructions.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatInstOut, STAMTYPE_COUNTER, "/IOM/IOWork/Out", STAMUNIT_OCCURENCES, "Counter of any OUT instructions.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatInstIns, STAMTYPE_COUNTER, "/IOM/IOWork/Ins", STAMUNIT_OCCURENCES, "Counter of any INS instructions.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync STAM_REG(pVM, &pVM->iom.s.StatInstOuts, STAMTYPE_COUNTER, "/IOM/IOWork/Outs", STAMUNIT_OCCURENCES, "Counter of any OUTS instructions.");
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* Redundant, but just in case we change something in the future */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Flushes the IOM port & statistics lookup cache
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM The VM.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Caching of port and statistics (saves some time in rep outs/ins instruction emulation)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * The VM is being reset.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM VM handle.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Applies relocations to data and code managed by this
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * component. This function will be called at init and
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * whenever the VMM need to relocate it self inside the GC.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * The IOM will update the addresses used by the switcher.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM The VM.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param offDelta Relocation delta relative to old location.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncIOMR3DECL(void) IOMR3Relocate(PVM pVM, RTGCINTPTR offDelta)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync LogFlow(("IOMR3Relocate: offDelta=%d\n", offDelta));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Apply relocations to the GC callbacks.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync pVM->iom.s.pTreesRC = MMHyperR3ToRC(pVM, pVM->iom.s.pTreesR3);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync RTAvlroIOPortDoWithAll(&pVM->iom.s.pTreesR3->IOPortTreeRC, true, iomR3RelocateIOPortCallback, &offDelta);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync RTAvlroGCPhysDoWithAll(&pVM->iom.s.pTreesR3->MMIOTree, true, iomR3RelocateMMIOCallback, &offDelta);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Apply relocations to the cached GC handlers
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Callback function for relocating a I/O port range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @returns 0 (continue enum)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pNode Pointer to a IOMIOPORTRANGERC node.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pvUser Pointer to the offDelta. This is a pointer to the delta since we're
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * not certain the delta will fit in a void pointer for all possible configs.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic DECLCALLBACK(int) iomR3RelocateIOPortCallback(PAVLROIOPORTNODECORE pNode, void *pvUser)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync PIOMIOPORTRANGERC pRange = (PIOMIOPORTRANGERC)pNode;
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Callback function for relocating a MMIO range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @returns 0 (continue enum)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pNode Pointer to a IOMMMIORANGE node.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pvUser Pointer to the offDelta. This is a pointer to the delta since we're
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * not certain the delta will fit in a void pointer for all possible configs.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncstatic DECLCALLBACK(int) iomR3RelocateMMIOCallback(PAVLROGCPHYSNODECORE pNode, void *pvUser)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Terminates the IOM.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Termination means cleaning up and freeing all resources,
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * the VM it self is at this point powered off or suspended.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @returns VBox status code.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM The VM to operate on.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * IOM is not owning anything but automatically freed resources,
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * so there's nothing to do here.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Create the statistics node for an I/O port.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @returns Pointer to new stats node.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM VM handle.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param Port Port.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pszDesc Description.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncPIOMIOPORTSTATS iomR3IOPortStatsCreate(PVM pVM, RTIOPORT Port, const char *pszDesc)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* check if it already exists. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync PIOMIOPORTSTATS pPort = (PIOMIOPORTSTATS)RTAvloIOPortGet(&pVM->iom.s.pTreesR3->IOPortStatTree, Port);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* allocate stats node. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync int rc = MMHyperAlloc(pVM, sizeof(*pPort), 0, MM_TAG_IOM_STATS, (void **)&pPort);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* insert into the tree. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if (RTAvloIOPortInsert(&pVM->iom.s.pTreesR3->IOPortStatTree, &pPort->Core))
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* put a name on common ports. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* register the statistics counters. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pPort->InR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-In-R3", Port); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pPort->OutR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-Out-R3", Port); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pPort->InRZ, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-In-RZ", Port); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pPort->OutRZ, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-Out-RZ", Port); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pPort->InRZToR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-In-RZ-2-R3", Port); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pPort->OutRZToR3,STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/Ports/%04x-Out-RZ-2-R3", Port); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* Profiling */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pPort->ProfInR3, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc,"/IOM/Ports/%04x-In-R3/Prof", Port); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pPort->ProfOutR3,STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc,"/IOM/Ports/%04x-Out-R3/Prof", Port); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pPort->ProfInRZ, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc,"/IOM/Ports/%04x-In-RZ/Prof", Port); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pPort->ProfOutRZ,STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc,"/IOM/Ports/%04x-Out-RZ/Prof", Port); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Create the statistics node for an MMIO address.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @returns Pointer to new stats node.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM VM handle.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param GCPhys The address.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pszDesc Description.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncPIOMMMIOSTATS iomR3MMIOStatsCreate(PVM pVM, RTGCPHYS GCPhys, const char *pszDesc)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* check if it already exists. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync PIOMMMIOSTATS pStats = (PIOMMMIOSTATS)RTAvloGCPhysGet(&pVM->iom.s.pTreesR3->MMIOStatTree, GCPhys);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* allocate stats node. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync int rc = MMHyperAlloc(pVM, sizeof(*pStats), 0, MM_TAG_IOM_STATS, (void **)&pStats);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* insert into the tree. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if (RTAvloGCPhysInsert(&pVM->iom.s.pTreesR3->MMIOStatTree, &pStats->Core))
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* register the statistics counters. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pStats->ReadR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/MMIO/%RGp-Read-R3", GCPhys); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pStats->WriteR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/MMIO/%RGp-Write-R3", GCPhys); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pStats->ReadRZ, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/MMIO/%RGp-Read-RZ", GCPhys); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pStats->WriteRZ, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/MMIO/%RGp-Write-RZ", GCPhys); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pStats->ReadRZToR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/MMIO/%RGp-Read-RZ-2-R3", GCPhys); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pStats->WriteRZToR3, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES, pszDesc, "/IOM/MMIO/%RGp-Write-RZ-2-R3", GCPhys); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* Profiling */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pStats->ProfReadR3, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc, "/IOM/MMIO/%RGp-Read-R3/Prof", GCPhys); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pStats->ProfWriteR3, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc, "/IOM/MMIO/%RGp-Write-R3/Prof", GCPhys); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pStats->ProfReadRZ, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc, "/IOM/MMIO/%RGp-Read-RZ/Prof", GCPhys); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync rc = STAMR3RegisterF(pVM, &pStats->ProfWriteRZ, STAMTYPE_PROFILE, STAMVISIBILITY_USED, STAMUNIT_TICKS_PER_CALL, pszDesc, "/IOM/MMIO/%RGp-Write-RZ/Prof", GCPhys); AssertRC(rc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync#endif /* VBOX_WITH_STATISTICS */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Registers a I/O port ring-3 handler.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * This API is called by PDM on behalf of a device. Devices must first register
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * ring-3 ranges before any GC and R0 ranges can be registerd using IOMR3IOPortRegisterRC()
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * and IOMR3IOPortRegisterR0().
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @returns VBox status code.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM VM handle.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pDevIns PDM device instance owning the port range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param PortStart First port number in the range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param cPorts Number of ports to register.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pvUser User argument for the callbacks.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnOutCallback Pointer to function which is gonna handle OUT operations in R3.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnInCallback Pointer to function which is gonna handle IN operations in R3.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnOutStrCallback Pointer to function which is gonna handle string OUT operations in R3.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnInStrCallback Pointer to function which is gonna handle string IN operations in R3.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pszDesc Pointer to description string. This must not be freed.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncIOMR3DECL(int) IOMR3IOPortRegisterR3(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTHCPTR pvUser,
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync R3PTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, R3PTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync R3PTRTYPE(PFNIOMIOPORTOUTSTRING) pfnOutStrCallback, R3PTRTYPE(PFNIOMIOPORTINSTRING) pfnInStrCallback, const char *pszDesc)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync LogFlow(("IOMR3IOPortRegisterR3: pDevIns=%p PortStart=%#x cPorts=%#x pvUser=%VHv pfnOutCallback=%#x pfnInCallback=%#x pfnOutStrCallback=%#x pfnInStrCallback=%#x pszDesc=%s\n",
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync pDevIns, PortStart, cPorts, pvUser, pfnOutCallback, pfnInCallback, pszDesc, pfnOutStrCallback, pfnInStrCallback));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Validate input.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if ( (RTUINT)PortStart + cPorts <= (RTUINT)PortStart
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Invalid port range %#x-%#x (inclusive)! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("no handlers specfied for %#x-%#x (inclusive)! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* Flush the IO port lookup cache */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Allocate new range record and initialize it.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Try Insert it.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if (RTAvlroIOPortInsert(&pVM->iom.s.pTreesR3->IOPortTreeR3, &pRange->Core))
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync iomR3IOPortStatsCreate(pVM, PortStart + iPort, pszDesc);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* conflict. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Port range %#x-%#x (%s) conflicts with existing range(s)!\n", PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Registers a I/O port RC handler.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * using IOMIOPortRegisterR3() before calling this function.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @returns VBox status code.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM VM handle.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pDevIns PDM device instance owning the port range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param PortStart First port number in the range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param cPorts Number of ports to register.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pvUser User argument for the callbacks.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnOutCallback Pointer to function which is gonna handle OUT operations in GC.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnInCallback Pointer to function which is gonna handle IN operations in GC.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnOutStrCallback Pointer to function which is gonna handle string OUT operations in GC.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnInStrCallback Pointer to function which is gonna handle string IN operations in GC.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pszDesc Pointer to description string. This must not be freed.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncIOMR3DECL(int) IOMR3IOPortRegisterRC(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTRCPTR pvUser,
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync RCPTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, RCPTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync RCPTRTYPE(PFNIOMIOPORTOUTSTRING) pfnOutStrCallback, RCPTRTYPE(PFNIOMIOPORTINSTRING) pfnInStrCallback, const char *pszDesc)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync LogFlow(("IOMR3IOPortRegisterRC: pDevIns=%p PortStart=%#x cPorts=%#x pvUser=%VRv pfnOutCallback=%VGv pfnInCallback=%VRv pfnOutStrCallback=%VRv pfnInStrCallback=%VRv pszDesc=%s\n",
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync pDevIns, PortStart, cPorts, pvUser, pfnOutCallback, pfnInCallback, pfnOutStrCallback, pfnInStrCallback, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Validate input.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if ( (RTUINT)PortStart + cPorts <= (RTUINT)PortStart
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Invalid port range %#x-%#x! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Invalid port range %#x-%#x! No callbacks! (%s)\n", PortStart, PortLast, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Validate that there are ring-3 ranges for the ports.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortTreeR3, Port);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("No R3! Port=#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if (pRange->pDevIns != MMHyperRCToCC(pVM, pDevIns))
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Not owner! Port=%#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* Flush the IO port lookup cache */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Allocate new range record and initialize it.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Insert it.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if (RTAvlroIOPortInsert(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortTreeRC, &pRange->Core))
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* conflict. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Port range %#x-%#x (%s) conflicts with existing range(s)!\n", PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Registers a Port IO R0 handler.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * This API is called by PDM on behalf of a device. Devices must first register ring-3 ranges
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * using IOMR3IOPortRegisterR3() before calling this function.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @returns VBox status code.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM VM handle.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pDevIns PDM device instance owning the port range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param PortStart First port number in the range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param cPorts Number of ports to register.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pvUser User argument for the callbacks.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnOutCallback Pointer to function which is gonna handle OUT operations in GC.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnInCallback Pointer to function which is gonna handle IN operations in GC.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnOutStrCallback Pointer to function which is gonna handle OUT operations in GC.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pfnInStrCallback Pointer to function which is gonna handle IN operations in GC.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pszDesc Pointer to description string. This must not be freed.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncIOMR3DECL(int) IOMR3IOPortRegisterR0(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts, RTR0PTR pvUser,
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync R0PTRTYPE(PFNIOMIOPORTOUT) pfnOutCallback, R0PTRTYPE(PFNIOMIOPORTIN) pfnInCallback,
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync R0PTRTYPE(PFNIOMIOPORTOUTSTRING) pfnOutStrCallback, R0PTRTYPE(PFNIOMIOPORTINSTRING) pfnInStrCallback,
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync const char *pszDesc)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync LogFlow(("IOMR3IOPortRegisterR0: pDevIns=%p PortStart=%#x cPorts=%#x pvUser=%VHv pfnOutCallback=%VHv pfnInCallback=%VHv pfnOutStrCallback=%VHv pfnInStrCallback=%VHv pszDesc=%s\n",
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync pDevIns, PortStart, cPorts, pvUser, pfnOutCallback, pfnInCallback, pfnOutStrCallback, pfnInStrCallback, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Validate input.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if ( (RTUINT)PortStart + cPorts <= (RTUINT)PortStart
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Invalid port range %#x-%#x! (%s)\n", PortStart, (RTUINT)PortStart + (cPorts - 1), pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Invalid port range %#x-%#x! No callbacks! (%s)\n", PortStart, PortLast, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Validate that there are ring-3 ranges for the ports.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortTreeR3, Port);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("No R3! Port=#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if (pRange->pDevIns != MMHyperRCToCC(pVM, pDevIns))
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Not owner! Port=%#x %#x-%#x! (%s)\n", Port, PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* Flush the IO port lookup cache */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Allocate new range record and initialize it.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync int rc = MMHyperAlloc(pVM, sizeof(*pRange), 0, MM_TAG_IOM, (void **)&pRange);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Insert it.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if (RTAvlroIOPortInsert(&pVM->iom.s.CTX_SUFF(pTrees)->IOPortTreeR0, &pRange->Core))
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* conflict. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Port range %#x-%#x (%s) conflicts with existing range(s)!\n", PortStart, (unsigned)PortStart + cPorts - 1, pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Deregisters a I/O Port range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * The specified range must be registered using IOMR3IOPortRegister previous to
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * this call. The range does can be a smaller part of the range specified to
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * IOMR3IOPortRegister, but it can never be larger.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * This function will remove GC, R0 and R3 context port handlers for this range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @returns VBox status code.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pVM The virtual machine.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param pDevIns The device instance associated with the range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param PortStart First port number in the range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @param cPorts Number of ports to remove starting at PortStart.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * @remark This function mainly for PCI PnP Config and will not do
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * all the checks you might expect it to do.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsyncIOMR3DECL(int) IOMR3IOPortDeregister(PVM pVM, PPDMDEVINS pDevIns, RTIOPORT PortStart, RTUINT cPorts)
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync LogFlow(("IOMR3IOPortDeregister: pDevIns=%p PortStart=%#x cPorts=%#x\n", pDevIns, PortStart, cPorts));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Validate input.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if ( (RTUINT)PortStart + cPorts < (RTUINT)PortStart
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Invalid port range %#x-%#x!\n", PortStart, (unsigned)PortStart + cPorts - 1));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* Flush the IO port lookup cache */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Check ownership.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.pTreesR3->IOPortTreeR3, Port);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync AssertMsgFailed(("Removal of ports in range %#x-%#x rejected because not owner of %#x-%#x (%s)\n",
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync PortStart, PortLast, pRange->Core.Key, pRange->Core.KeyLast, pRange->pszDesc));
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync#endif /* !IOM_NO_PDMINS_CHECKS */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Remove any RC ranges first.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Try find range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync PIOMIOPORTRANGERC pRange = (PIOMIOPORTRANGERC)RTAvlroIOPortRangeGet(&pVM->iom.s.pTreesR3->IOPortTreeRC, Port);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Kick out the entire range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync void *pv = RTAvlroIOPortRemove(&pVM->iom.s.pTreesR3->IOPortTreeRC, Port);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Cut of the head of the range, done.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Just cut of the tail.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Split the range, done.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync Assert(pRange->Core.KeyLast > PortLast && pRange->Core.Key < Port);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* create tail. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync int rc = MMHyperAlloc(pVM, sizeof(*pRangeNew), 0, MM_TAG_IOM, (void **)&pRangeNew);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync pRangeNew->cPorts = pRangeNew->Core.KeyLast - PortLast + 1;
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* adjust head */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync /* insert */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync if (!RTAvlroIOPortInsert(&pVM->iom.s.pTreesR3->IOPortTreeRC, &pRangeNew->Core))
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync else /* next port */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync } /* for all ports - RC. */
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Remove any R0 ranges first.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Try find range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync PIOMIOPORTRANGER0 pRange = (PIOMIOPORTRANGER0)RTAvlroIOPortRangeGet(&pVM->iom.s.pTreesR3->IOPortTreeR0, Port);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Kick out the entire range.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync void *pv = RTAvlroIOPortRemove(&pVM->iom.s.pTreesR3->IOPortTreeR0, Port);
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Cut of the head of the range, done.
b8e299dddd091ae24e0c08c45d91b8f937bd14d2vboxsync * Just cut of the tail.
Port += c;
return rc;
Port++;
PIOMIOPORTRANGER3 pRange = (PIOMIOPORTRANGER3)RTAvlroIOPortRangeGet(&pVM->iom.s.pTreesR3->IOPortTreeR3, Port);
if (pRange)
Port += c;
return rc;
Port++;
return rc;
static DECLCALLBACK(int) iomR3IOPortDummyIn(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
switch (cb)
return VERR_INTERNAL_ERROR;
return VINF_SUCCESS;
* @param pcTransfer Pointer to the number of transfer units to read, on return remaining transfer units.
static DECLCALLBACK(int) iomR3IOPortDummyInStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrDst, PRTGCUINTREG pcTransfer, unsigned cb)
return VINF_SUCCESS;
static DECLCALLBACK(int) iomR3IOPortDummyOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
return VINF_SUCCESS;
* @param pcTransfer Pointer to the number of transfer units to write, on return remaining transfer units.
static DECLCALLBACK(int) iomR3IOPortDummyOutStr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, RTGCPTR *pGCPtrSrc, PRTGCUINTREG pcTransfer, unsigned cb)
return VINF_SUCCESS;
pVM,
RTAvlroIOPortDoWithAll(&pVM->iom.s.pTreesR3->IOPortTreeR3, true, iomR3IOPortInfoOneR3, (void *)pHlp);
pVM,
RTAvlroIOPortDoWithAll(&pVM->iom.s.pTreesR3->IOPortTreeR0, true, iomR3IOPortInfoOneR3, (void *)pHlp);
pVM,
RTAvlroIOPortDoWithAll(&pVM->iom.s.pTreesR3->IOPortTreeRC, true, iomR3IOPortInfoOneRC, (void *)pHlp);
* before any GC and R0 ranges can be registered using IOMR3MMIORegisterRC() and IOMR3MMIORegisterR0().
IOMR3DECL(int) IOMR3MMIORegisterR3(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTUINT cbRange, RTHCPTR pvUser,
LogFlow(("IOMR3MMIORegisterR3: pDevIns=%p GCPhysStart=%VGp cbRange=%#x pvUser=%VHv pfnWriteCallback=%#x pfnReadCallback=%#x pfnFillCallback=%#x pszDesc=%s\n",
pDevIns, GCPhysStart, cbRange, pvUser, pfnWriteCallback, pfnReadCallback, pfnFillCallback, pszDesc));
int rc;
return VERR_IOM_INVALID_MMIO_RANGE;
return VINF_SUCCESS;
return rc;
IOMR3DECL(int) IOMR3MMIORegisterRC(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTUINT cbRange, RTGCPTR pvUser,
LogFlow(("IOMR3MMIORegisterRC: pDevIns=%p GCPhysStart=%VGp cbRange=%#x pvUser=%VGv pfnWriteCallback=%#x pfnReadCallback=%#x pfnFillCallback=%#x\n",
return VERR_INVALID_PARAMETER;
return VINF_SUCCESS;
IOMR3DECL(int) IOMR3MMIORegisterR0(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTUINT cbRange, RTR0PTR pvUser,
LogFlow(("IOMR3MMIORegisterR0: pDevIns=%p GCPhysStart=%VGp cbRange=%#x pvUser=%VHv pfnWriteCallback=%#x pfnReadCallback=%#x pfnFillCallback=%#x\n",
return VERR_INVALID_PARAMETER;
return VINF_SUCCESS;
IOMR3DECL(int) IOMR3MMIODeregister(PVM pVM, PPDMDEVINS pDevIns, RTGCPHYS GCPhysStart, RTUINT cbRange)
LogFlow(("IOMR3MMIODeregister: pDevIns=%p GCPhysStart=%VGp cbRange=%#x\n", pDevIns, GCPhysStart, cbRange));
return VERR_IOM_INVALID_MMIO_RANGE;
if (!pRange)
return VERR_IOM_MMIO_RANGE_NOT_FOUND;
("Incomplete R3 range! GCPhys=%VGp %VGp LB%#x %s\n", GCPhys, GCPhysStart, cbRange, pRange->pszDesc),
return VINF_SUCCESS;
pVM,
#ifdef VBOX_WITH_STATISTICS
switch (Port)
return NULL;