IOMAll.cpp revision 96dce0123cc032388e78766d08f9ee5a66b80fac
/* $Id$ */
/** @file
* IOM - Input / Output Monitor - Any Context.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_IOM
#include "IOMInternal.h"
/**
*
* @retval VINF_SUCCESS on success (always in ring-3).
* @retval VERR_SEM_BUSY in RC and R0 if the semaphore is busy.
*
* @param pVM VM handle.
*/
{
return rc;
}
/**
*
* @retval VINF_SUCCESS on success.
* @retval VERR_SEM_BUSY if busy.
*
* @param pVM VM handle.
*/
{
return rc;
}
/**
*
* @param pVM VM handle.
*/
{
}
/**
* Check if this VCPU currently owns the IOM lock.
*
* @param pVM The VM to operate on.
*/
{
}
/**
* Returns the contents of register or immediate data of instruction's parameter.
*
* @returns true on success.
*
* @todo Get rid of this code. Use DISQueryParamVal instead
*
* @param pCpu Pointer to current disassembler context.
* @param pParam Pointer to parameter of instruction to proccess.
* @param pRegFrame Pointer to CPUMCTXCORE guest structure.
* @param pu64Data Where to store retrieved data.
* @param pcbSize Where to store the size of data (1, 2, 4, 8).
*/
bool iomGetRegImmData(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint64_t *pu64Data, unsigned *pcbSize)
{
if (pParam->flags & (USE_BASE | USE_INDEX | USE_SCALE | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32))
{
*pcbSize = 0;
*pu64Data = 0;
return false;
}
/* divide and conquer */
{
{
*pcbSize = 4;
return true;
}
{
*pcbSize = 2;
return true;
}
{
*pcbSize = 1;
return true;
}
*pcbSize = 8;
return true;
}
else
{
{
*pcbSize = 8;
return true;
}
{
*pcbSize = 4;
return true;
}
{
*pcbSize = 2;
return true;
}
{
*pcbSize = 1;
return true;
}
{
*pcbSize = 2;
return true;
} /* Else - error. */
AssertFailed();
*pcbSize = 0;
*pu64Data = 0;
return false;
}
}
/**
* Saves data to 8/16/32 general purpose or segment register defined by
* instruction's parameter.
*
* @returns true on success.
* @param pCpu Pointer to current disassembler context.
* @param pParam Pointer to parameter of instruction to proccess.
* @param pRegFrame Pointer to CPUMCTXCORE guest structure.
* @param u64Data 8/16/32/64 bit data to store.
*/
bool iomSaveDataToReg(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint64_t u64Data)
{
if (pParam->flags & (USE_BASE | USE_INDEX | USE_SCALE | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32 | USE_DISPLACEMENT64 | USE_IMMEDIATE8 | USE_IMMEDIATE16 | USE_IMMEDIATE32 | USE_IMMEDIATE32_SX8 | USE_IMMEDIATE16_SX8))
{
return false;
}
{
return true;
}
{
return true;
}
{
return true;
}
{
return true;
}
{
return true;
}
/* Else - error. */
return false;
}
//#undef LOG_GROUP
//#define LOG_GROUP LOG_GROUP_IOM_IOPORT
/**
* Reads an I/O port register.
*
* @returns Strict VBox status code. Informational status codes other than the one documented
* here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
* @retval VINF_SUCCESS Success.
* @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
* status code must be passed on to EM.
*
* @param pVM VM handle.
* @param Port The port to read.
* @param pu32Value Where to store the value read.
* @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
*/
{
/* Take the IOM lock before performing any device I/O. */
#ifndef IN_RING3
if (rc2 == VERR_SEM_BUSY)
return VINF_IOM_HC_IOPORT_READ;
#endif
#ifdef VBOX_WITH_STATISTICS
/*
* Get the statistics record.
*/
{
if (pStats)
}
#endif
/*
* Get handler for current context.
*/
if ( !pRange
{
if (pRange)
}
if (pRange)
{
/*
* Found a range, get the data in case we leave the IOM lock.
*/
#ifndef IN_RING3
if (!pfnInCallback)
{
return VINF_IOM_HC_IOPORT_READ;
}
#endif
/*
* Call the device - 4 variations.
*/
if (pCritSect)
{
if (rcStrict != VINF_SUCCESS)
{
return rcStrict;
}
#ifdef VBOX_WITH_STATISTICS
if (pStats)
{
}
else
#endif
}
else
{
#ifdef VBOX_WITH_STATISTICS
if (pStats)
{
}
else
#endif
}
#ifdef VBOX_WITH_STATISTICS
# ifndef IN_RING3
# endif
#endif
if (rcStrict == VERR_IOM_IOPORT_UNUSED)
{
/* make return value */
switch (cbValue)
{
default:
if (!pCritSect)
return VERR_IOM_INVALID_IOPORT_SIZE;
}
}
Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=%Rrc\n", Port, *pu32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
if (!pCritSect)
return rcStrict;
}
#ifndef IN_RING3
/*
* Handler in ring-3?
*/
if (pRangeR3)
{
# ifdef VBOX_WITH_STATISTICS
if (pStats)
# endif
return VINF_IOM_HC_IOPORT_READ;
}
#endif
/*
* Ok, no handler for this port.
*/
#ifdef VBOX_WITH_STATISTICS
if (pStats)
else
{
# ifndef IN_RING3
/* Ring-3 will have to create the statistics record. */
return VINF_IOM_HC_IOPORT_READ;
# else
if (pStats)
# endif
}
#endif
/* make return value */
switch (cbValue)
{
default:
return VERR_IOM_INVALID_IOPORT_SIZE;
}
Log3(("IOMIOPortRead: Port=%RTiop *pu32=%08RX32 cb=%d rc=VINF_SUCCESS\n", Port, *pu32Value, cbValue));
return VINF_SUCCESS;
}
/**
* Reads the string buffer of an I/O port register.
*
* @returns Strict VBox status code. Informational status codes other than the one documented
* here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
* @retval VINF_SUCCESS Success.
* @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
* status code must be passed on to EM.
*
* @param pVM VM handle.
* @param Port The port to read.
* @param pGCPtrDst Pointer to the destination buffer (GC, incremented appropriately).
* @param pcTransfers Pointer to the number of transfer units to read, on return remaining transfer units.
* @param cb Size of the transfer unit (1, 2 or 4 bytes).
*/
VMMDECL(VBOXSTRICTRC) IOMIOPortReadString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrDst, PRTGCUINTREG pcTransfers, unsigned cb)
{
/* Take the IOM lock before performing any device I/O. */
#ifndef IN_RING3
if (rc2 == VERR_SEM_BUSY)
return VINF_IOM_HC_IOPORT_READ;
#endif
#ifdef LOG_ENABLED
#endif
#ifdef VBOX_WITH_STATISTICS
/*
* Get the statistics record.
*/
{
if (pStats)
}
#endif
/*
* Get handler for current context.
*/
if ( !pRange
{
if (pRange)
}
if (pRange)
{
/*
* Found a range.
*/
#ifndef IN_RING3
if (!pfnInStrCallback)
{
return VINF_IOM_HC_IOPORT_READ;
}
#endif
/*
* Call the device - 4 variations.
*/
if (pCritSect)
{
if (rcStrict != VINF_SUCCESS)
{
return rcStrict;
}
#ifdef VBOX_WITH_STATISTICS
if (pStats)
{
}
else
#endif
}
else
{
#ifdef VBOX_WITH_STATISTICS
if (pStats)
{
}
else
#endif
}
#ifdef VBOX_WITH_STATISTICS
# ifndef IN_RING3
# endif
#endif
Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Rrc\n",
if (!pCritSect)
return rcStrict;
}
#ifndef IN_RING3
/*
* Handler in ring-3?
*/
if (pRangeR3)
{
# ifdef VBOX_WITH_STATISTICS
if (pStats)
# endif
return VINF_IOM_HC_IOPORT_READ;
}
#endif
/*
* Ok, no handler for this port.
*/
#ifdef VBOX_WITH_STATISTICS
if (pStats)
else
{
# ifndef IN_RING3
/* Ring-3 will have to create the statistics record. */
return VINF_IOM_HC_IOPORT_READ;
# else
if (pStats)
# endif
}
#endif
Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
return VINF_SUCCESS;
}
/**
* Writes to an I/O port register.
*
* @returns Strict VBox status code. Informational status codes other than the one documented
* here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
* @retval VINF_SUCCESS Success.
* @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
* status code must be passed on to EM.
*
* @param pVM VM handle.
* @param Port The port to write to.
* @param u32Value The value to write.
* @param cbValue The size of the register to read in bytes. 1, 2 or 4 bytes.
*/
{
/* Take the IOM lock before performing any device I/O. */
#ifndef IN_RING3
if (rc2 == VERR_SEM_BUSY)
return VINF_IOM_HC_IOPORT_WRITE;
#endif
* entries to the ring-3 node. */
#ifdef VBOX_WITH_STATISTICS
/*
* Find the statistics record.
*/
{
if (pStats)
}
#endif
/*
* Get handler for current context.
*/
if ( !pRange
{
if (pRange)
}
if (pRange)
{
/*
* Found a range.
*/
#ifndef IN_RING3
if (!pfnOutCallback)
{
return VINF_IOM_HC_IOPORT_WRITE;
}
#endif
/*
* Call the device - 4 variations.
*/
if (pCritSect)
{
if (rcStrict != VINF_SUCCESS)
{
return rcStrict;
}
#ifdef VBOX_WITH_STATISTICS
if (pStats)
{
}
else
#endif
}
else
{
#ifdef VBOX_WITH_STATISTICS
if (pStats)
{
}
else
#endif
}
#ifdef VBOX_WITH_STATISTICS
# ifndef IN_RING3
# endif
#endif
Log3(("IOMIOPortWrite: Port=%RTiop u32=%08RX32 cb=%d rc=%Rrc\n", Port, u32Value, cbValue, VBOXSTRICTRC_VAL(rcStrict)));
if (!pCritSect)
return rcStrict;
}
#ifndef IN_RING3
/*
* Handler in ring-3?
*/
if (pRangeR3)
{
# ifdef VBOX_WITH_STATISTICS
if (pStats)
# endif
return VINF_IOM_HC_IOPORT_WRITE;
}
#endif
/*
* Ok, no handler for that port.
*/
#ifdef VBOX_WITH_STATISTICS
/* statistics. */
if (pStats)
else
{
# ifndef IN_RING3
/* R3 will have to create the statistics record. */
return VINF_IOM_HC_IOPORT_WRITE;
# else
if (pStats)
# endif
}
#endif
return VINF_SUCCESS;
}
/**
* Writes the string buffer of an I/O port register.
*
* @returns Strict VBox status code. Informational status codes other than the one documented
* here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
* @retval VINF_SUCCESS Success.
* @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
* status code must be passed on to EM.
*
* @param pVM VM handle.
* @param Port The port to write.
* @param pGCPtrSrc Pointer to the source buffer (GC, incremented appropriately).
* @param pcTransfers Pointer to the number of transfer units to write, on return remaining transfer units.
* @param cb Size of the transfer unit (1, 2 or 4 bytes).
* */
VMMDECL(VBOXSTRICTRC) IOMIOPortWriteString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrSrc, PRTGCUINTREG pcTransfers, unsigned cb)
{
/* Take the IOM lock before performing any device I/O. */
#ifndef IN_RING3
if (rc2 == VERR_SEM_BUSY)
return VINF_IOM_HC_IOPORT_WRITE;
#endif
#ifdef LOG_ENABLED
#endif
#ifdef VBOX_WITH_STATISTICS
/*
* Get the statistics record.
*/
{
if (pStats)
}
#endif
/*
* Get handler for current context.
*/
if ( !pRange
{
if (pRange)
}
if (pRange)
{
/*
* Found a range.
*/
#ifndef IN_RING3
if (!pfnOutStrCallback)
{
return VINF_IOM_HC_IOPORT_WRITE;
}
#endif
/*
* Call the device - 4 variations.
*/
if (pCritSect)
{
if (rcStrict != VINF_SUCCESS)
{
return rcStrict;
}
#ifdef VBOX_WITH_STATISTICS
if (pStats)
{
}
else
#endif
}
else
{
#ifdef VBOX_WITH_STATISTICS
if (pStats)
{
}
else
#endif
}
#ifdef VBOX_WITH_STATISTICS
# ifndef IN_RING3
# endif
#endif
Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rcStrict=%Rrc\n",
if (!pCritSect)
return rcStrict;
}
#ifndef IN_RING3
/*
* Handler in ring-3?
*/
if (pRangeR3)
{
# ifdef VBOX_WITH_STATISTICS
if (pStats)
# endif
return VINF_IOM_HC_IOPORT_WRITE;
}
#endif
/*
* Ok, no handler for this port.
*/
#ifdef VBOX_WITH_STATISTICS
if (pStats)
else
{
# ifndef IN_RING3
/* Ring-3 will have to create the statistics record. */
return VINF_IOM_HC_IOPORT_WRITE;
# else
if (pStats)
# endif
}
#endif
Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=VINF_SUCCESS\n",
return VINF_SUCCESS;
}
/**
* Checks that the operation is allowed according to the IOPL
* level and I/O bitmap.
*
* @returns Strict VBox status code. Informational status codes other than the one documented
* here are to be treated as internal failure.
* @retval VINF_SUCCESS Success.
* @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
* @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
* @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
*
* @param pVM VM handle.
* @param pCtxCore Pointer to register frame.
* @param Port The I/O port number.
* @param cb The access size.
*/
VMMDECL(VBOXSTRICTRC) IOMInterpretCheckPortIOAccess(PVM pVM, PCPUMCTXCORE pCtxCore, RTIOPORT Port, unsigned cb)
{
/*
* If this isn't ring-0, we have to check for I/O privileges.
*/
if ( ( cpl > 0
)
{
/*
* Get TSS location and check if there can be a I/O bitmap.
*/
bool fCanHaveIOBitmap;
if (RT_FAILURE(rc2))
{
}
if ( !fCanHaveIOBitmap
|| cbTss <= sizeof(VBOXTSS)) /** @todo r=bird: Should this really include the interrupt redirection bitmap? */
{
Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d cbTss=%#x fCanHaveIOBitmap=%RTbool -> #GP(0)\n",
}
/*
* Fetch the I/O bitmap offset.
*/
VBOXSTRICTRC rcStrict = PGMPhysInterpretedRead(pVCpu, pCtxCore, &offIOPB, GCPtrTss + RT_OFFSETOF(VBOXTSS, offIoBitmap), sizeof(offIOPB));
if (rcStrict != VINF_SUCCESS)
{
Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%RGv %Rrc\n",
return rcStrict;
}
/*
* Check the limit and read the two bitmap bytes.
*/
{
Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x -> #GP(0)\n",
}
if (rcStrict != VINF_SUCCESS)
{
Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%RGv offTss=%#x -> %Rrc\n",
return rcStrict;
}
/*
* All the bits must be clear.
*/
{
Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d u16=%#x -> #GP(0)\n",
}
LogFlow(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x u16=%#x -> OK\n",
}
return VINF_SUCCESS;
}
/**
* IN <AL|AX|EAX>, <DX|imm16>
*
* @returns Strict VBox status code. Informational status codes other than the one documented
* here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
* @retval VINF_SUCCESS Success.
* @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
* status code must be passed on to EM.
* @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
* @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
* @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
*
* @param pVM The virtual machine (GC pointer ofcourse).
* @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
* @param pCpu Disassembler CPU state.
*/
{
#ifdef IN_RC
#endif
/*
* Get port number from second parameter.
* And get the register size from the first parameter.
*/
unsigned cbSize = 0;
if (rcStrict == VINF_SUCCESS)
{
/*
* Attemp to read the port.
*/
if (IOM_SUCCESS(rcStrict))
{
/*
* Store the result in the AL|AX|EAX register.
*/
}
else
AssertMsg(rcStrict == VINF_IOM_HC_IOPORT_READ || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
}
else
AssertMsg(rcStrict == VINF_EM_RAW_GUEST_TRAP || rcStrict == VINF_TRPM_XCPT_DISPATCHED || rcStrict == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
return rcStrict;
}
/**
* OUT <DX|imm16>, <AL|AX|EAX>
*
* @returns Strict VBox status code. Informational status codes other than the one documented
* here are to be treated as internal failure. Use IOM_SUCCESS() to check for success.
* @retval VINF_SUCCESS Success.
* @retval VINF_EM_FIRST-VINF_EM_LAST Success with some exceptions (see IOM_SUCCESS()), the
* status code must be passed on to EM.
* @retval VINF_EM_RAW_GUEST_TRAP The exception was left pending. (TRPMRaiseXcptErr)
* @retval VINF_TRPM_XCPT_DISPATCHED The exception was raised and dispatched for raw-mode execution. (TRPMRaiseXcptErr)
* @retval VINF_EM_RESCHEDULE_REM The exception was dispatched and cannot be executed in raw-mode. (TRPMRaiseXcptErr)
*
* @param pVM The virtual machine (GC pointer ofcourse).
* @param pRegFrame Pointer to CPUMCTXCORE guest registers structure.
* @param pCpu Disassembler CPU state.
*/
{
#ifdef IN_RC
#endif
/*
* Get port number from first parameter.
* And get the register size and value from the second parameter.
*/
unsigned cbSize = 0;
if (rcStrict == VINF_SUCCESS)
{
/*
* Attempt to write to the port.
*/
AssertMsg(rcStrict == VINF_SUCCESS || rcStrict == VINF_IOM_HC_IOPORT_WRITE || (rcStrict >= VINF_EM_FIRST && rcStrict <= VINF_EM_LAST) || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
}
else
AssertMsg(rcStrict == VINF_EM_RAW_GUEST_TRAP || rcStrict == VINF_TRPM_XCPT_DISPATCHED || rcStrict == VINF_TRPM_XCPT_DISPATCHED || RT_FAILURE(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)));
return rcStrict;
}