ea0316554d23852601ee840c4a856339501411a4vboxsync * MSI support routines
c7814cf6e1240a519cbec0441e033d0e2470ed00vboxsync * Copyright (C) 2010-2012 Oracle Corporation
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/* Hack to get PCIDEVICEINT declare at the right point - include "PCIInternal.h". */
5870e01d9070d2730543e4e811f6e84827439d39vboxsync/** @todo: use accessors so that raw PCI devices work correctly with MSI. */
ea0316554d23852601ee840c4a856339501411a4vboxsyncDECLINLINE(uint16_t) msiGetMessageControl(PPCIDEVICE pDev)
ea0316554d23852601ee840c4a856339501411a4vboxsync return PCIDevGetWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_CONTROL);
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsyncDECLINLINE(uint32_t*) msiGetMaskBits(PPCIDEVICE pDev)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync uint8_t iOff = msiIs64Bit(pDev) ? VBOX_MSI_CAP_MASK_BITS_64 : VBOX_MSI_CAP_MASK_BITS_32;
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsyncDECLINLINE(uint32_t*) msiGetPendingBits(PPCIDEVICE pDev)
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync uint8_t iOff = msiIs64Bit(pDev) ? VBOX_MSI_CAP_PENDING_BITS_64 : VBOX_MSI_CAP_PENDING_BITS_32;
82546d3d9144e70a29430532ac0011ab77b2d5bbvboxsync return (msiGetMessageControl(pDev) & VBOX_PCI_MSI_FLAGS_ENABLE) != 0;
48a5358f2f99d054c9b6a1a0f063c11c9addfe14vboxsync return (msiGetMessageControl(pDev) & VBOX_PCI_MSI_FLAGS_QSIZE) >> 4;
ea0316554d23852601ee840c4a856339501411a4vboxsyncDECLINLINE(RTGCPHYS) msiGetMsiAddress(PPCIDEVICE pDev)
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 PCIDevGetDWord(pDev, pDev->Int.s.u8MsiCapOffset + VBOX_MSI_CAP_MESSAGE_ADDRESS_32);
ea0316554d23852601ee840c4a856339501411a4vboxsyncDECLINLINE(uint32_t) msiGetMsiData(PPCIDEVICE pDev, int32_t iVector)
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);
48a5358f2f99d054c9b6a1a0f063c11c9addfe14vboxsync // vector encoding into lower bits of message data
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsyncDECLINLINE(bool) msiBitJustCleared(uint32_t uOldValue,
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync return (!!(uOldValue & uMask) && !(uNewValue & uMask));
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync return (!(uOldValue & uMask) && !!(uNewValue & uMask));
377f1df8d6ec248927bcdf0efabf87ab55c4a615vboxsyncvoid MsiPciConfigWrite(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPCIDEVICE pDev,
ea0316554d23852601ee840c4a856339501411a4vboxsync int32_t iOff = u32Address - pDev->Int.s.u8MsiCapOffset;
c4159ccabc1c97ff09d2d128e6e3a2b578b31af4vboxsync Assert(iOff >= 0 && (pciDevIsMsiCapable(pDev) && iOff < pDev->Int.s.u8MsiCapSize));
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync Log2(("MsiPciConfigWrite: %d <- %x (%d)\n", iOff, val, len));
ea0316554d23852601ee840c4a856339501411a4vboxsync case 0: /* Capability ID, ro */
ea0316554d23852601ee840c4a856339501411a4vboxsync /* don't change read-only bits: 1-3,7 */
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync pDev->config[uAddr] = u8Val | (pDev->config[uAddr] & UINT8_C(0x8e));
ea0316554d23852601ee840c4a856339501411a4vboxsync /* don't change read-only bit 8, and reserved 9-15 */
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync /* If we're enabling masked vector, and have pending messages
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync for this vector, we have to send this message now */
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync if (msiBitJustCleared(pDev->config[uAddr], u8Val, iBit))
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync Log(("msi: mask updated bit %d@%x (%d)\n", iBitNum, uAddr, maskUpdated));
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync /* To ensure that we're no longer masked */
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync Log(("msi: notify earlier masked pending vector: %d\n", uVector));
377f1df8d6ec248927bcdf0efabf87ab55c4a615vboxsync MsiNotify(pDevIns, pPciHlp, pDev, uVector, PDM_IRQ_LEVEL_HIGH, 0 /*uTagSrc*/);
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync if (msiBitJustSet(pDev->config[uAddr], u8Val, iBit))
f1f55b6ac890efaabca0ff940f58aa8df1dc84c8vboxsyncuint32_t MsiPciConfigRead (PPDMDEVINS pDevIns, PPCIDEVICE pDev, uint32_t u32Address, unsigned len)
ea0316554d23852601ee840c4a856339501411a4vboxsync int32_t iOff = u32Address - pDev->Int.s.u8MsiCapOffset;
c4159ccabc1c97ff09d2d128e6e3a2b578b31af4vboxsync Assert(iOff >= 0 && (pciDevIsMsiCapable(pDev) && iOff < pDev->Int.s.u8MsiCapSize));
0f1ee7c0c041f17c1c692fc7f09af458a62ceaeevboxsync Log2(("MsiPciConfigRead: %d (%d) -> %x\n", iOff, len, rv));
e792133826a035b6af26ba1577d38bf259680d2avboxsync /* We cannot init MSI on raw devices yet. */
48a5358f2f99d054c9b6a1a0f063c11c9addfe14vboxsync /* Compute multiple-message capable bitfield */
48a5358f2f99d054c9b6a1a0f063c11c9addfe14vboxsync if ((cVectors > VBOX_MSI_MAX_ENTRIES) || (1 << iMmc) < cVectors)
ea0316554d23852601ee840c4a856339501411a4vboxsync Assert(iCapOffset != 0 && iCapOffset < 0xff && iNextOffset < 0xff);
0eb8851309a7678e8ccee719ee3eead40651b5d4vboxsync /* We always support per-vector masking */
48a5358f2f99d054c9b6a1a0f063c11c9addfe14vboxsync /* How many vectors we're capable of */
ea0316554d23852601ee840c4a856339501411a4vboxsync pDev->Int.s.u8MsiCapSize = f64bit ? VBOX_MSI_CAP_SIZE_64 : VBOX_MSI_CAP_SIZE_32;
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);
e792133826a035b6af26ba1577d38bf259680d2avboxsync#endif /* IN_RING3 */
c4159ccabc1c97ff09d2d128e6e3a2b578b31af4vboxsync return pciDevIsMsiCapable(pDev) && msiIsEnabled(pDev);
377f1df8d6ec248927bcdf0efabf87ab55c4a615vboxsyncvoid MsiNotify(PPDMDEVINS pDevIns, PCPDMPCIHLP pPciHlp, PPCIDEVICE pDev, int iVector, int iLevel, uint32_t uTagSrc)
ea0316554d23852601ee840c4a856339501411a4vboxsync AssertMsg(msiIsEnabled(pDev), ("Must be enabled to use that"));
bca1ab31c0e7c1e9d9ebc4faa4c06925a37f5996vboxsync LogFlow(("MsiNotify: %d pending=%x mask=%x\n", iVector, *puPending, uMask));
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync /* We only trigger MSI on level up */
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync /* @todo: maybe clear pending interrupts on level down? */
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync LogFlow(("msi: clear pending %d, now %x\n", iVector, *puPending));
c9c1b52d5c13d75ba8fc3d86278f2b0c6b3e8c2avboxsync LogFlow(("msi: %d is masked, mark pending, now %x\n", iVector, *puPending));