IOMAll.cpp revision 14e483cf65160fb363043534151245ae4c215766
/* $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"
/**
* Calculates the size of register parameter.
*
* @returns 1, 2, 4 on success.
* @returns 0 if non-register parameter.
* @param pCpu Pointer to current disassembler context.
* @param pParam Pointer to parameter of instruction to proccess.
*/
{
if (pParam->flags & (USE_BASE | USE_INDEX | USE_SCALE | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32 | USE_IMMEDIATE8 | USE_IMMEDIATE16 | USE_IMMEDIATE32 | USE_IMMEDIATE16_SX8 | USE_IMMEDIATE32_SX8))
return 0;
return 4;
return 2;
return 1;
return 8;
return 2;
return 0;
}
/**
* Returns the contents of register or immediate data of 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 pu32Data Where to store retrieved data.
* @param pcbSize Where to store the size of data (1, 2, 4).
*/
bool iomGetRegImmData(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, uint32_t *pu32Data, unsigned *pcbSize)
{
if (pParam->flags & (USE_BASE | USE_INDEX | USE_SCALE | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32))
{
*pcbSize = 0;
*pu32Data = 0;
return false;
}
{
*pcbSize = 4;
return true;
}
{
*pcbSize = 2;
return true;
}
{
*pcbSize = 1;
return true;
}
{
AssertFailed();
*pcbSize = 8;
///DISFetchReg64(pRegFrame, pParam->base.reg_gen, pu32Data);
return true;
}
{
AssertFailed();
*pcbSize = 8;
return true;
}
{
*pcbSize = 4;
//*pu32Data = (uint32_t)pParam->parval;
return true;
}
{
*pcbSize = 2;
return true;
}
{
*pcbSize = 1;
return true;
}
{
*pcbSize = 2;
return true;
} /* Else - error. */
AssertFailed();
*pcbSize = 0;
*pu32Data = 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 u32Data 8/16/32 bit data to store.
*/
bool iomSaveDataToReg(PDISCPUSTATE pCpu, PCOP_PARAMETER pParam, PCPUMCTXCORE pRegFrame, unsigned u32Data)
{
if (pParam->flags & (USE_BASE | USE_INDEX | USE_SCALE | USE_DISPLACEMENT8 | USE_DISPLACEMENT16 | USE_DISPLACEMENT32 | USE_IMMEDIATE8 | USE_IMMEDIATE16 | USE_IMMEDIATE32 | USE_IMMEDIATE32_SX8 | USE_IMMEDIATE16_SX8))
{
return false;
}
{
return true;
}
{
return true;
}
{
return true;
}
{
return true;
}
/* Else - error. */
return false;
}
/*
* Internal - statistics only.
*/
{
#ifdef VBOX_WITH_STATISTICS
switch (cb)
{
case 1:
break;
case 2:
break;
case 4:
break;
default:
/* No way. */
break;
}
#else
#endif
}
//#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.
*/
{
#ifdef VBOX_WITH_STATISTICS
/*
* Get the statistics record.
*/
{
if (pStats)
}
#endif
/*
* Get handler for current context.
*/
if ( !pRange
{
if (pRange)
}
#ifdef IN_GC
Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
#endif
if (pRange)
{
/*
* Found a range.
*/
#ifndef IN_RING3
if (!pRange->pfnInCallback)
{
# ifdef VBOX_WITH_STATISTICS
if (pStats)
# endif
return VINF_IOM_HC_IOPORT_READ;
}
#endif
/* call the device. */
#ifdef VBOX_WITH_STATISTICS
if (pStats)
#endif
#ifdef VBOX_WITH_STATISTICS
if (pStats)
# ifndef IN_RING3
# endif
#endif
if (rc == VERR_IOM_IOPORT_UNUSED)
{
/* make return value */
rc = VINF_SUCCESS;
switch (cbValue)
{
default:
return VERR_IOM_INVALID_IOPORT_SIZE;
}
}
return rc;
}
#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).
* */
IOMDECL(int) IOMIOPortReadString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrDst, PRTGCUINTREG pcTransfers, unsigned cb)
{
#ifdef LOG_ENABLED
#endif
#ifdef VBOX_WITH_STATISTICS
/*
* Get the statistics record.
*/
{
if (pStats)
}
#endif
/*
* Get handler for current context.
*/
if ( !pRange
{
if (pRange)
}
#ifdef IN_GC
Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
#endif
if (pRange)
{
/*
* Found a range.
*/
#ifndef IN_RING3
if (!pRange->pfnInStrCallback)
{
# ifdef VBOX_WITH_STATISTICS
if (pStats)
# endif
return VINF_IOM_HC_IOPORT_READ;
}
#endif
/* call the device. */
#ifdef VBOX_WITH_STATISTICS
if (pStats)
#endif
int rc = pRange->pfnInStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrDst, pcTransfers, cb);
#ifdef VBOX_WITH_STATISTICS
if (pStats)
# ifndef IN_RING3
# endif
#endif
Log3(("IOMIOPortReadStr: Port=%RTiop pGCPtrDst=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Vrc\n",
return rc;
}
#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.
*/
{
/** @todo bird: When I get time, I'll remove the GC tree and link the GC 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)
}
#ifdef IN_GC
#endif
if (pRange)
{
/*
* Found a range.
*/
#ifndef IN_RING3
if (!pRange->pfnOutCallback)
{
# ifdef VBOX_WITH_STATISTICS
if (pStats)
# endif
return VINF_IOM_HC_IOPORT_WRITE;
}
#endif
/* call the device. */
#ifdef VBOX_WITH_STATISTICS
if (pStats)
#endif
#ifdef VBOX_WITH_STATISTICS
if (pStats)
# ifndef IN_RING3
# endif
#endif
return rc;
}
#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).
* */
IOMDECL(int) IOMIOPortWriteString(PVM pVM, RTIOPORT Port, PRTGCPTR pGCPtrSrc, PRTGCUINTREG pcTransfers, unsigned cb)
{
#ifdef LOG_ENABLED
#endif
#ifdef VBOX_WITH_STATISTICS
/*
* Get the statistics record.
*/
{
if (pStats)
}
#endif
/*
* Get handler for current context.
*/
if ( !pRange
{
if (pRange)
}
#ifdef IN_GC
Assert(!pRange || MMHyperIsInsideArea(pVM, pRange)); /** @todo r=bird: there is a macro for this which skips the #if'ing. */
#endif
if (pRange)
{
/*
* Found a range.
*/
#ifndef IN_RING3
if (!pRange->pfnOutStrCallback)
{
# ifdef VBOX_WITH_STATISTICS
if (pStats)
# endif
return VINF_IOM_HC_IOPORT_WRITE;
}
#endif
/* call the device. */
#ifdef VBOX_WITH_STATISTICS
if (pStats)
#endif
int rc = pRange->pfnOutStrCallback(pRange->pDevIns, pRange->pvUser, Port, pGCPtrSrc, pcTransfers, cb);
#ifdef VBOX_WITH_STATISTICS
if (pStats)
# ifndef IN_RING3
# endif
#endif
Log3(("IOMIOPortWriteStr: Port=%RTiop pGCPtrSrc=%p pcTransfer=%p:{%#x->%#x} cb=%d rc=%Vrc\n",
return rc;
}
#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.
*/
IOMDECL(int) 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 (VBOX_FAILURE(rc))
{
}
if ( !fCanHaveIOBitmap
{
Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d cbTss=%#x fCanHaveIOBitmap=%RTbool -> #GP(0)\n",
}
/*
* Fetch the I/O bitmap offset.
*/
rc = PGMPhysInterpretedRead(pVM, pCtxCore, &offIOPB, GCPtrTss + RT_OFFSETOF(VBOXTSS, offIoBitmap), sizeof(offIOPB));
if (rc != VINF_SUCCESS)
{
Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%VGv %Vrc\n",
return rc;
}
/*
* Check the limit and read the two bitmap bytes.
*/
{
Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d offTss=%#x cbTss=%#x -> #GP(0)\n",
}
if (rc != VINF_SUCCESS)
{
Log(("iomInterpretCheckPortIOAccess: Port=%RTiop cb=%d GCPtrTss=%VGv offTss=%#x -> %Vrc\n",
return rc;
}
/*
* 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_GC
#endif
/*
* Get port number from second parameter.
* And get the register size from the first parameter.
*/
unsigned cbSize = 0;
if (rc == VINF_SUCCESS)
{
/*
* Attemp to read the port.
*/
if (IOM_SUCCESS(rc))
{
/*
* Store the result in the AL|AX|EAX register.
*/
}
else
}
else
AssertMsg(rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED || rc == VINF_TRPM_XCPT_DISPATCHED || VBOX_FAILURE(rc), ("%Vrc\n", rc));
return rc;
}
/**
* 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_GC
#endif
/*
* Get port number from first parameter.
* And get the register size and value from the second parameter.
*/
unsigned cbSize = 0;
if (rc == VINF_SUCCESS)
{
/*
* Attempt to write to the port.
*/
AssertMsg(rc == VINF_SUCCESS || rc == VINF_IOM_HC_IOPORT_WRITE || (rc >= VINF_EM_FIRST && rc <= VINF_EM_LAST) || VBOX_FAILURE(rc), ("%Vrc\n", rc));
}
else
AssertMsg(rc == VINF_EM_RAW_GUEST_TRAP || rc == VINF_TRPM_XCPT_DISPATCHED || rc == VINF_TRPM_XCPT_DISPATCHED || VBOX_FAILURE(rc), ("%Vrc\n", rc));
return rc;
}