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