MsiCommon.cpp revision bca1ab31c0e7c1e9d9ebc4faa4c06925a37f5996
ea0316554d23852601ee840c4a856339501411a4vboxsync/* $Id$ */
ea0316554d23852601ee840c4a856339501411a4vboxsync/** @file
ea0316554d23852601ee840c4a856339501411a4vboxsync * MSI support routines
ea0316554d23852601ee840c4a856339501411a4vboxsync */
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync/*
ea0316554d23852601ee840c4a856339501411a4vboxsync * Copyright (C) 2010 Oracle Corporation
ea0316554d23852601ee840c4a856339501411a4vboxsync *
ea0316554d23852601ee840c4a856339501411a4vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
ea0316554d23852601ee840c4a856339501411a4vboxsync * available from http://www.virtualbox.org. This file is free software;
ea0316554d23852601ee840c4a856339501411a4vboxsync * you can redistribute it and/or modify it under the terms of the GNU
ea0316554d23852601ee840c4a856339501411a4vboxsync * General Public License (GPL) as published by the Free Software
ea0316554d23852601ee840c4a856339501411a4vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
ea0316554d23852601ee840c4a856339501411a4vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
ea0316554d23852601ee840c4a856339501411a4vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
ea0316554d23852601ee840c4a856339501411a4vboxsync */
ea0316554d23852601ee840c4a856339501411a4vboxsync#define LOG_GROUP LOG_GROUP_DEV_PCI
ea0316554d23852601ee840c4a856339501411a4vboxsync/* Hack to get PCIDEVICEINT declare at the right point - include "PCIInternal.h". */
ea0316554d23852601ee840c4a856339501411a4vboxsync#define PCI_INCLUDE_PRIVATE
ea0316554d23852601ee840c4a856339501411a4vboxsync#include <VBox/pci.h>
ea0316554d23852601ee840c4a856339501411a4vboxsync#include <VBox/msi.h>
ea0316554d23852601ee840c4a856339501411a4vboxsync#include <VBox/pdmdev.h>
ea0316554d23852601ee840c4a856339501411a4vboxsync#include <VBox/log.h>
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync#include "MsiCommon.h"
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsyncDECLINLINE(uint16_t) msiGetMessageControl(PPCIDEVICE pDev)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
ea0316554d23852601ee840c4a856339501411a4vboxsync return PCIDevGetWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_CONTROL);
ea0316554d23852601ee840c4a856339501411a4vboxsync}
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsyncDECLINLINE(bool) msiIs64Bit(PPCIDEVICE pDev)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
82546d3d9144e70a29430532ac0011ab77b2d5bbvboxsync return (msiGetMessageControl(pDev) & VBOX_PCI_MSI_FLAGS_64BIT) != 0;
ea0316554d23852601ee840c4a856339501411a4vboxsync}
ea0316554d23852601ee840c4a856339501411a4vboxsync
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsyncDECLINLINE(uint32_t*) msiGetMaskBits(PPCIDEVICE pDev)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync{
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync uint8_t iOff = msiIs64Bit(pDev) ? VBOX_MSI_CAP_MASK_BITS_64 : VBOX_MSI_CAP_MASK_BITS_32;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync iOff += pDev->Int.s.u8MsiCapOffset;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync return (uint32_t*)(pDev->config + iOff);
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync}
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsyncDECLINLINE(uint32_t*) msiGetPendingBits(PPCIDEVICE pDev)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync{
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync uint8_t iOff = msiIs64Bit(pDev) ? VBOX_MSI_CAP_PENDING_BITS_64 : VBOX_MSI_CAP_PENDING_BITS_32;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync iOff += pDev->Int.s.u8MsiCapOffset;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync return (uint32_t*)(pDev->config + iOff);
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync}
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsyncDECLINLINE(bool) msiIsEnabled(PPCIDEVICE pDev)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
82546d3d9144e70a29430532ac0011ab77b2d5bbvboxsync return (msiGetMessageControl(pDev) & VBOX_PCI_MSI_FLAGS_ENABLE) != 0;
ea0316554d23852601ee840c4a856339501411a4vboxsync}
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsyncDECLINLINE(bool) msiIsMME(PPCIDEVICE pDev)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
ea0316554d23852601ee840c4a856339501411a4vboxsync return (msiGetMessageControl(pDev) & VBOX_PCI_MSI_FLAGS_QSIZE) != 0;
ea0316554d23852601ee840c4a856339501411a4vboxsync}
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsyncDECLINLINE(RTGCPHYS) msiGetMsiAddress(PPCIDEVICE pDev)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
ea0316554d23852601ee840c4a856339501411a4vboxsync if (msiIs64Bit(pDev))
ea0316554d23852601ee840c4a856339501411a4vboxsync {
ea0316554d23852601ee840c4a856339501411a4vboxsync uint32_t lo = PCIDevGetDWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_ADDRESS_LO);
ea0316554d23852601ee840c4a856339501411a4vboxsync uint32_t hi = PCIDevGetDWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_ADDRESS_HI);
ea0316554d23852601ee840c4a856339501411a4vboxsync return RT_MAKE_U64(lo, hi);
ea0316554d23852601ee840c4a856339501411a4vboxsync }
ea0316554d23852601ee840c4a856339501411a4vboxsync else
ea0316554d23852601ee840c4a856339501411a4vboxsync {
ea0316554d23852601ee840c4a856339501411a4vboxsync return PCIDevGetDWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_ADDRESS_32);
ea0316554d23852601ee840c4a856339501411a4vboxsync }
ea0316554d23852601ee840c4a856339501411a4vboxsync}
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsyncDECLINLINE(uint32_t) msiGetMsiData(PPCIDEVICE pDev, int32_t iVector)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
ea0316554d23852601ee840c4a856339501411a4vboxsync int16_t iOff = msiIs64Bit(pDev) ? VBOX_MSI_CAP_MESSAGE_DATA_64 : VBOX_MSI_CAP_MESSAGE_DATA_32;
ea0316554d23852601ee840c4a856339501411a4vboxsync uint16_t lo = PCIDevGetWord(pDev, pDev->Int.s.u8MsiCapOffset + iOff);
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync /// @todo: vector encoding into lower bits of message data, for Multiple Message Enable
ea0316554d23852601ee840c4a856339501411a4vboxsync Assert(!msiIsMME(pDev));
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync return RT_MAKE_U32(lo, 0);
ea0316554d23852601ee840c4a856339501411a4vboxsync}
ea0316554d23852601ee840c4a856339501411a4vboxsync
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsyncDECLINLINE(bool) msiBitJustCleared(uint32_t uOldValue,
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync uint32_t uNewValue,
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync uint32_t uMask)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync{
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync return (!!(uOldValue & uMask) && !(uNewValue & uMask));
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync}
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsyncDECLINLINE(bool) msiBitJustSet(uint32_t uOldValue,
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync uint32_t uNewValue,
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync uint32_t uMask)
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync{
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync return (!(uOldValue & uMask) && !!(uNewValue & uMask));
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync}
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
f1f55b6ac890efaabca0ff940f58aa8df1dc84c8vboxsyncvoid MsiPciConfigWrite(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPCIDEVICE pDev, uint32_t u32Address, uint32_t val, unsigned len)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
ea0316554d23852601ee840c4a856339501411a4vboxsync int32_t iOff = u32Address - pDev->Int.s.u8MsiCapOffset;
ea0316554d23852601ee840c4a856339501411a4vboxsync Assert(iOff >= 0 && (PCIIsMsiCapable(pDev) && iOff < pDev->Int.s.u8MsiCapSize));
ea0316554d23852601ee840c4a856339501411a4vboxsync
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync Log2(("MsiPciConfigWrite: %d <- %x (%d)\n", iOff, val, len));
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync uint32_t uAddr = u32Address;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync bool f64Bit = msiIs64Bit(pDev);
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync for (uint32_t i = 0; i < len; i++)
ea0316554d23852601ee840c4a856339501411a4vboxsync {
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync uint32_t reg = i + iOff;
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync uint8_t u8Val = (uint8_t)val;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync switch (reg)
ea0316554d23852601ee840c4a856339501411a4vboxsync {
ea0316554d23852601ee840c4a856339501411a4vboxsync case 0: /* Capability ID, ro */
ea0316554d23852601ee840c4a856339501411a4vboxsync case 1: /* Next pointer, ro */
ea0316554d23852601ee840c4a856339501411a4vboxsync break;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync case VBOX_MSI_CAP_MESSAGE_CONTROL:
ea0316554d23852601ee840c4a856339501411a4vboxsync /* don't change read-only bits: 1-3,7 */
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync u8Val &= UINT8_C(~0x8e);
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync pDev->config[uAddr] = u8Val | (pDev->config[uAddr] & UINT8_C(0x8e));
ea0316554d23852601ee840c4a856339501411a4vboxsync break;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync case VBOX_MSI_CAP_MESSAGE_CONTROL + 1:
ea0316554d23852601ee840c4a856339501411a4vboxsync /* don't change read-only bit 8, and reserved 9-15 */
ea0316554d23852601ee840c4a856339501411a4vboxsync break;
ea0316554d23852601ee840c4a856339501411a4vboxsync default:
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync if (pDev->config[uAddr] != u8Val)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync {
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync int32_t maskUpdated = -1;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync /* If we're enabling masked vector, and have pending messages
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync for this vector, we have to send this message now */
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync if ( !f64Bit
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync && (reg >= VBOX_MSI_CAP_MASK_BITS_32)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync && (reg < VBOX_MSI_CAP_MASK_BITS_32 + 4)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync )
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync {
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync maskUpdated = reg - VBOX_MSI_CAP_MASK_BITS_32;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync }
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync if ( f64Bit
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync && (reg >= VBOX_MSI_CAP_MASK_BITS_64)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync && (reg < VBOX_MSI_CAP_MASK_BITS_64 + 4)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync )
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync {
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync maskUpdated = reg - VBOX_MSI_CAP_MASK_BITS_64;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync }
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
b742a29c8c5a06fb5f362e5e6c09cbcaf8087ff6vboxsync if (maskUpdated != -1 && msiIsEnabled(pDev))
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync {
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync uint32_t* puPending = msiGetPendingBits(pDev);
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync for (int iBitNum = 0; iBitNum < 8; iBitNum++)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync {
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync int32_t iBit = 1 << iBitNum;
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync uint32_t uVector = maskUpdated*8 + iBitNum;
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync if (msiBitJustCleared(pDev->config[uAddr], u8Val, iBit))
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync {
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync Log(("msi: mask updated bit %d@%x (%d)\n", iBitNum, uAddr, maskUpdated));
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync /* To ensure that we're no longer masked */
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync pDev->config[uAddr] &= ~iBit;
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync if ((*puPending & (1 << uVector)) != 0)
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync {
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync Log(("msi: notify earlier masked pending vector: %d\n", uVector));
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync MsiNotify(pDevIns, pPciHlp, pDev, uVector, PDM_IRQ_LEVEL_HIGH);
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync }
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync }
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync if (msiBitJustSet(pDev->config[uAddr], u8Val, iBit))
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync {
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync Log(("msi: mask vector: %d\n", uVector));
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync }
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync }
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync }
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync pDev->config[uAddr] = u8Val;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync }
ea0316554d23852601ee840c4a856339501411a4vboxsync }
ea0316554d23852601ee840c4a856339501411a4vboxsync uAddr++;
ea0316554d23852601ee840c4a856339501411a4vboxsync val >>= 8;
ea0316554d23852601ee840c4a856339501411a4vboxsync }
ea0316554d23852601ee840c4a856339501411a4vboxsync}
ea0316554d23852601ee840c4a856339501411a4vboxsync
f1f55b6ac890efaabca0ff940f58aa8df1dc84c8vboxsyncuint32_t MsiPciConfigRead (PPDMDEVINS pDevIns, PPCIDEVICE pDev, uint32_t u32Address, unsigned len)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
ea0316554d23852601ee840c4a856339501411a4vboxsync int32_t iOff = u32Address - pDev->Int.s.u8MsiCapOffset;
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync Assert(iOff >= 0 && (PCIIsMsiCapable(pDev) && iOff < pDev->Int.s.u8MsiCapSize));
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync uint32_t rv = 0;
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync switch (len)
ea0316554d23852601ee840c4a856339501411a4vboxsync {
ea0316554d23852601ee840c4a856339501411a4vboxsync case 1:
f1f55b6ac890efaabca0ff940f58aa8df1dc84c8vboxsync rv = PCIDevGetByte(pDev, u32Address);
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync break;
ea0316554d23852601ee840c4a856339501411a4vboxsync case 2:
f1f55b6ac890efaabca0ff940f58aa8df1dc84c8vboxsync rv = PCIDevGetWord(pDev, u32Address);
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync break;
ea0316554d23852601ee840c4a856339501411a4vboxsync case 4:
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync rv = PCIDevGetDWord(pDev, u32Address);
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync break;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync default:
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync Assert(false);
ea0316554d23852601ee840c4a856339501411a4vboxsync }
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync Log2(("MsiPciConfigRead: %d (%d) -> %x\n", iOff, len, rv));
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync return rv;
ea0316554d23852601ee840c4a856339501411a4vboxsync}
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync
f1f55b6ac890efaabca0ff940f58aa8df1dc84c8vboxsyncint MsiInit(PPCIDEVICE pDev, PPDMMSIREG pMsiReg)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync if (pMsiReg->cMsiVectors == 0)
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync return VINF_SUCCESS;
ea0316554d23852601ee840c4a856339501411a4vboxsync
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync uint16_t cVectors = pMsiReg->cMsiVectors;
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync uint8_t iCapOffset = pMsiReg->iMsiCapOffset;
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync uint8_t iNextOffset = pMsiReg->iMsiNextOffset;
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync uint16_t iFlags = pMsiReg->iMsiFlags;
ea0316554d23852601ee840c4a856339501411a4vboxsync
b742a29c8c5a06fb5f362e5e6c09cbcaf8087ff6vboxsync if (cVectors != 1)
b742a29c8c5a06fb5f362e5e6c09cbcaf8087ff6vboxsync /* We cannot handle multiple vectors yet */
b742a29c8c5a06fb5f362e5e6c09cbcaf8087ff6vboxsync return VERR_TOO_MUCH_DATA;
b742a29c8c5a06fb5f362e5e6c09cbcaf8087ff6vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync if (cVectors > VBOX_MSI_MAX_ENTRIES)
ea0316554d23852601ee840c4a856339501411a4vboxsync return VERR_TOO_MUCH_DATA;
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync Assert(iCapOffset != 0 && iCapOffset < 0xff && iNextOffset < 0xff);
ea0316554d23852601ee840c4a856339501411a4vboxsync
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync bool f64bit = (iFlags & VBOX_PCI_MSI_FLAGS_64BIT) != 0;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync /* We always support per-vector masking */
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync iFlags |= VBOX_PCI_MSI_FLAGS_MASKBIT;
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync pDev->Int.s.u8MsiCapOffset = iCapOffset;
ea0316554d23852601ee840c4a856339501411a4vboxsync pDev->Int.s.u8MsiCapSize = f64bit ? VBOX_MSI_CAP_SIZE_64 : VBOX_MSI_CAP_SIZE_32;
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync PCIDevSetByte(pDev, iCapOffset + 0, VBOX_PCI_CAP_ID_MSI);
ea0316554d23852601ee840c4a856339501411a4vboxsync PCIDevSetByte(pDev, iCapOffset + 1, iNextOffset); /* next */
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync PCIDevSetWord(pDev, iCapOffset + VBOX_MSI_CAP_MESSAGE_CONTROL, iFlags);
ea0316554d23852601ee840c4a856339501411a4vboxsync
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync *msiGetMaskBits(pDev) = 0;
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync *msiGetPendingBits(pDev) = 0;
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync PCISetMsiCapable(pDev);
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync return VINF_SUCCESS;
ea0316554d23852601ee840c4a856339501411a4vboxsync}
ea0316554d23852601ee840c4a856339501411a4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync
f1f55b6ac890efaabca0ff940f58aa8df1dc84c8vboxsyncbool MsiIsEnabled(PPCIDEVICE pDev)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
ea0316554d23852601ee840c4a856339501411a4vboxsync return PCIIsMsiCapable(pDev) && msiIsEnabled(pDev);
ea0316554d23852601ee840c4a856339501411a4vboxsync}
ea0316554d23852601ee840c4a856339501411a4vboxsync
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsyncvoid MsiNotify(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPCIDEVICE pDev, int iVector, int iLevel)
ea0316554d23852601ee840c4a856339501411a4vboxsync{
ea0316554d23852601ee840c4a856339501411a4vboxsync AssertMsg(msiIsEnabled(pDev), ("Must be enabled to use that"));
ea0316554d23852601ee840c4a856339501411a4vboxsync
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync uint32_t uMask = *msiGetMaskBits(pDev);
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync uint32_t* puPending = msiGetPendingBits(pDev);
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync
bca1ab31c0e7c1e9d9ebc4faa4c06925a37f5996vboxsync LogFlow(("MsiNotify: %d pending=%x mask=%x\n", iVector, *puPending, uMask));
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync /* We only trigger MSI on level up */
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync if ((iLevel & PDM_IRQ_LEVEL_HIGH) == 0)
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync {
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync /* @todo: maybe clear pending interrupts on level down? */
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync#if 0
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync *puPending &= ~(1<<iVector);
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync LogFlow(("msi: clear pending %d, now %x\n", iVector, *puPending));
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync#endif
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync return;
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync }
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync if ((uMask & (1<<iVector)) != 0)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync {
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync *puPending |= (1<<iVector);
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync LogFlow(("msi: %d is masked, mark pending, now %x\n", iVector, *puPending));
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync return;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync }
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
ea0316554d23852601ee840c4a856339501411a4vboxsync RTGCPHYS GCAddr = msiGetMsiAddress(pDev);
ea0316554d23852601ee840c4a856339501411a4vboxsync uint32_t u32Value = msiGetMsiData(pDev, iVector);
ea0316554d23852601ee840c4a856339501411a4vboxsync
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync *puPending &= ~(1<<iVector);
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync
f1f55b6ac890efaabca0ff940f58aa8df1dc84c8vboxsync Assert(pPciHlp->pfnIoApicSendMsi != NULL);
f1f55b6ac890efaabca0ff940f58aa8df1dc84c8vboxsync pPciHlp->pfnIoApicSendMsi(pDevIns, GCAddr, u32Value);
ea0316554d23852601ee840c4a856339501411a4vboxsync}