Commit 3e7ee490 authored by Hank Janssen's avatar Hank Janssen Committed by Greg Kroah-Hartman

Staging: hv: add the Hyper-V virtual bus

This is the virtual bus that all of the Linux Hyper-V drivers use.
Signed-off-by: default avatarHank Janssen <hjanssen@microsoft.com>
Signed-off-by: default avatarHaiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent ab057781
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include "osd.h"
#include "logging.h"
#include "VmbusPrivate.h"
//
// Internal routines
//
static int
VmbusChannelCreateGpadlHeader(
PVOID Kbuffer, // must be phys and virt contiguous
UINT32 Size, // page-size multiple
VMBUS_CHANNEL_MSGINFO **msgInfo,
UINT32 *MessageCount
);
static void
DumpVmbusChannel(
VMBUS_CHANNEL *Channel
);
static void
VmbusChannelSetEvent(
VMBUS_CHANNEL *Channel
);
#if 0
static void
DumpMonitorPage(
HV_MONITOR_PAGE *MonitorPage
)
{
int i=0;
int j=0;
DPRINT_DBG(VMBUS, "monitorPage - %p, trigger state - %d", MonitorPage, MonitorPage->TriggerState);
for (i=0; i<4; i++)
{
DPRINT_DBG(VMBUS, "trigger group (%d) - %llx", i, MonitorPage->TriggerGroup[i].AsUINT64);
}
for (i=0; i<4; i++)
{
for (j=0; j<32; j++)
{
DPRINT_DBG(VMBUS, "latency (%d)(%d) - %llx", i, j, MonitorPage->Latency[i][j]);
}
}
for (i=0; i<4; i++)
{
for (j=0; j<32; j++)
{
DPRINT_DBG(VMBUS, "param-conn id (%d)(%d) - %d", i, j, MonitorPage->Parameter[i][j].ConnectionId.AsUINT32);
DPRINT_DBG(VMBUS, "param-flag (%d)(%d) - %d", i, j, MonitorPage->Parameter[i][j].FlagNumber);
}
}
}
#endif
/*++
Name:
VmbusChannelSetEvent()
Description:
Trigger an event notification on the specified channel.
--*/
static void
VmbusChannelSetEvent(
VMBUS_CHANNEL *Channel
)
{
HV_MONITOR_PAGE *monitorPage;
DPRINT_ENTER(VMBUS);
if (Channel->OfferMsg.MonitorAllocated)
{
// Each UINT32 represents 32 channels
BitSet((UINT32*)gVmbusConnection.SendInterruptPage + (Channel->OfferMsg.ChildRelId >> 5), Channel->OfferMsg.ChildRelId & 31);
monitorPage = (HV_MONITOR_PAGE*)gVmbusConnection.MonitorPages;
monitorPage++; // Get the child to parent monitor page
BitSet((UINT32*) &monitorPage->TriggerGroup[Channel->MonitorGroup].Pending, Channel->MonitorBit);
}
else
{
VmbusSetEvent(Channel->OfferMsg.ChildRelId);
}
DPRINT_EXIT(VMBUS);
}
#if 0
static void
VmbusChannelClearEvent(
VMBUS_CHANNEL *Channel
)
{
HV_MONITOR_PAGE *monitorPage;
DPRINT_ENTER(VMBUS);
if (Channel->OfferMsg.MonitorAllocated)
{
// Each UINT32 represents 32 channels
BitClear((UINT32*)gVmbusConnection.SendInterruptPage + (Channel->OfferMsg.ChildRelId >> 5), Channel->OfferMsg.ChildRelId & 31);
monitorPage = (HV_MONITOR_PAGE*)gVmbusConnection.MonitorPages;
monitorPage++; // Get the child to parent monitor page
BitClear((UINT32*) &monitorPage->TriggerGroup[Channel->MonitorGroup].Pending, Channel->MonitorBit);
}
DPRINT_EXIT(VMBUS);
}
#endif
/*++;
Name:
VmbusChannelGetDebugInfo()
Description:
Retrieve various channel debug info
--*/
void
VmbusChannelGetDebugInfo(
VMBUS_CHANNEL *Channel,
VMBUS_CHANNEL_DEBUG_INFO *DebugInfo
)
{
HV_MONITOR_PAGE *monitorPage;
UINT8 monitorGroup = (UINT8)Channel->OfferMsg.MonitorId / 32;
UINT8 monitorOffset = (UINT8)Channel->OfferMsg.MonitorId % 32;
//UINT32 monitorBit = 1 << monitorOffset;
DebugInfo->RelId = Channel->OfferMsg.ChildRelId;
DebugInfo->State = Channel->State;
memcpy(&DebugInfo->InterfaceType, &Channel->OfferMsg.Offer.InterfaceType, sizeof(GUID));
memcpy(&DebugInfo->InterfaceInstance, &Channel->OfferMsg.Offer.InterfaceInstance, sizeof(GUID));
monitorPage = (HV_MONITOR_PAGE*)gVmbusConnection.MonitorPages;
DebugInfo->MonitorId = Channel->OfferMsg.MonitorId;
DebugInfo->ServerMonitorPending = monitorPage->TriggerGroup[monitorGroup].Pending;
DebugInfo->ServerMonitorLatency = monitorPage->Latency[monitorGroup][ monitorOffset];
DebugInfo->ServerMonitorConnectionId = monitorPage->Parameter[monitorGroup][ monitorOffset].ConnectionId.u.Id;
monitorPage++;
DebugInfo->ClientMonitorPending = monitorPage->TriggerGroup[monitorGroup].Pending;
DebugInfo->ClientMonitorLatency = monitorPage->Latency[monitorGroup][ monitorOffset];
DebugInfo->ClientMonitorConnectionId = monitorPage->Parameter[monitorGroup][ monitorOffset].ConnectionId.u.Id;
RingBufferGetDebugInfo(&Channel->Inbound, &DebugInfo->Inbound);
RingBufferGetDebugInfo(&Channel->Outbound, &DebugInfo->Outbound);
}
/*++;
Name:
VmbusChannelOpen()
Description:
Open the specified channel.
--*/
int
VmbusChannelOpen(
VMBUS_CHANNEL *NewChannel,
UINT32 SendRingBufferSize,
UINT32 RecvRingBufferSize,
PVOID UserData,
UINT32 UserDataLen,
PFN_CHANNEL_CALLBACK pfnOnChannelCallback,
PVOID Context
)
{
int ret=0;
VMBUS_CHANNEL_OPEN_CHANNEL* openMsg;
VMBUS_CHANNEL_MSGINFO* openInfo;
void *in, *out;
DPRINT_ENTER(VMBUS);
// Aligned to page size
ASSERT(!(SendRingBufferSize & (PAGE_SIZE -1)));
ASSERT(!(RecvRingBufferSize & (PAGE_SIZE -1)));
NewChannel->OnChannelCallback = pfnOnChannelCallback;
NewChannel->ChannelCallbackContext = Context;
// Allocate the ring buffer
out = PageAlloc((SendRingBufferSize + RecvRingBufferSize) >> PAGE_SHIFT);
//out = MemAllocZeroed(sendRingBufferSize + recvRingBufferSize);
ASSERT(out);
ASSERT(((ULONG_PTR)out & (PAGE_SIZE-1)) == 0);
in = (void*)((ULONG_PTR)out + SendRingBufferSize);
NewChannel->RingBufferPages = out;
NewChannel->RingBufferPageCount = (SendRingBufferSize + RecvRingBufferSize) >> PAGE_SHIFT;
RingBufferInit(&NewChannel->Outbound, out, SendRingBufferSize);
RingBufferInit(&NewChannel->Inbound, in, RecvRingBufferSize);
// Establish the gpadl for the ring buffer
DPRINT_DBG(VMBUS, "Establishing ring buffer's gpadl for channel %p...", NewChannel);
NewChannel->RingBufferGpadlHandle = 0;
ret = VmbusChannelEstablishGpadl(NewChannel,
NewChannel->Outbound.RingBuffer,
SendRingBufferSize + RecvRingBufferSize,
&NewChannel->RingBufferGpadlHandle);
DPRINT_DBG(VMBUS, "channel %p <relid %d gpadl 0x%x send ring %p size %d recv ring %p size %d, downstreamoffset %d>",
NewChannel,
NewChannel->OfferMsg.ChildRelId,
NewChannel->RingBufferGpadlHandle,
NewChannel->Outbound.RingBuffer,
NewChannel->Outbound.RingSize,
NewChannel->Inbound.RingBuffer,
NewChannel->Inbound.RingSize,
SendRingBufferSize);
// Create and init the channel open message
openInfo =
(VMBUS_CHANNEL_MSGINFO*)MemAlloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_OPEN_CHANNEL));
ASSERT(openInfo != NULL);
openInfo->WaitEvent = WaitEventCreate();
openMsg = (VMBUS_CHANNEL_OPEN_CHANNEL*)openInfo->Msg;
openMsg->Header.MessageType = ChannelMessageOpenChannel;
openMsg->OpenId = NewChannel->OfferMsg.ChildRelId; // FIXME
openMsg->ChildRelId = NewChannel->OfferMsg.ChildRelId;
openMsg->RingBufferGpadlHandle = NewChannel->RingBufferGpadlHandle;
ASSERT(openMsg->RingBufferGpadlHandle);
openMsg->DownstreamRingBufferPageOffset = SendRingBufferSize >> PAGE_SHIFT;
openMsg->ServerContextAreaGpadlHandle = 0; // TODO
ASSERT(UserDataLen <= MAX_USER_DEFINED_BYTES);
if (UserDataLen)
{
memcpy(openMsg->UserData, UserData, UserDataLen);
}
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &openInfo->MsgListEntry);
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
DPRINT_DBG(VMBUS, "Sending channel open msg...");
ret = VmbusPostMessage(openMsg, sizeof(VMBUS_CHANNEL_OPEN_CHANNEL));
if (ret != 0)
{
DPRINT_ERR(VMBUS, "unable to open channel - %d", ret);
goto Cleanup;
}
// FIXME: Need to time-out here
WaitEventWait(openInfo->WaitEvent);
if (openInfo->Response.OpenResult.Status == 0)
{
DPRINT_INFO(VMBUS, "channel <%p> open success!!", NewChannel);
}
else
{
DPRINT_INFO(VMBUS, "channel <%p> open failed - %d!!", NewChannel, openInfo->Response.OpenResult.Status);
}
Cleanup:
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
REMOVE_ENTRY_LIST(&openInfo->MsgListEntry);
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
WaitEventClose(openInfo->WaitEvent);
MemFree(openInfo);
DPRINT_EXIT(VMBUS);
return 0;
}
/*++;
Name:
DumpGpadlBody()
Description:
Dump the gpadl body message to the console for debugging purposes.
--*/
static void DumpGpadlBody(
VMBUS_CHANNEL_GPADL_BODY *Gpadl,
UINT32 Len)
{
int i=0;
int pfnCount=0;
pfnCount = (Len - sizeof(VMBUS_CHANNEL_GPADL_BODY))/ sizeof(UINT64);
DPRINT_DBG(VMBUS, "gpadl body - len %d pfn count %d", Len, pfnCount);
for (i=0; i< pfnCount; i++)
{
DPRINT_DBG(VMBUS, "gpadl body - %d) pfn %llu", i, Gpadl->Pfn[i]);
}
}
/*++;
Name:
DumpGpadlHeader()
Description:
Dump the gpadl header message to the console for debugging purposes.
--*/
static void DumpGpadlHeader(
VMBUS_CHANNEL_GPADL_HEADER *Gpadl
)
{
int i=0,j=0;
int pageCount=0;
DPRINT_DBG(VMBUS, "gpadl header - relid %d, range count %d, range buflen %d",
Gpadl->ChildRelId,
Gpadl->RangeCount,
Gpadl->RangeBufLen);
for (i=0; i< Gpadl->RangeCount; i++)
{
pageCount = Gpadl->Range[i].ByteCount >> PAGE_SHIFT;
pageCount = (pageCount > 26)? 26 : pageCount;
DPRINT_DBG(VMBUS, "gpadl range %d - len %d offset %d page count %d",
i, Gpadl->Range[i].ByteCount, Gpadl->Range[i].ByteOffset, pageCount);
for (j=0; j< pageCount; j++)
{
DPRINT_DBG(VMBUS, "%d) pfn %llu", j, Gpadl->Range[i].PfnArray[j]);
}
}
}
/*++;
Name:
VmbusChannelCreateGpadlHeader()
Description:
Creates a gpadl for the specified buffer
--*/
static int
VmbusChannelCreateGpadlHeader(
PVOID Kbuffer, // from kmalloc()
UINT32 Size, // page-size multiple
VMBUS_CHANNEL_MSGINFO **MsgInfo,
UINT32 *MessageCount)
{
int i;
int pageCount;
unsigned long long pfn;
VMBUS_CHANNEL_GPADL_HEADER* gpaHeader;
VMBUS_CHANNEL_GPADL_BODY* gpadlBody;
VMBUS_CHANNEL_MSGINFO* msgHeader;
VMBUS_CHANNEL_MSGINFO* msgBody;
UINT32 msgSize;
int pfnSum, pfnCount, pfnLeft, pfnCurr, pfnSize;
//ASSERT( (kbuffer & (PAGE_SIZE-1)) == 0);
ASSERT( (Size & (PAGE_SIZE-1)) == 0);
pageCount = Size >> PAGE_SHIFT;
pfn = GetPhysicalAddress(Kbuffer) >> PAGE_SHIFT;
// do we need a gpadl body msg
pfnSize = MAX_SIZE_CHANNEL_MESSAGE - sizeof(VMBUS_CHANNEL_GPADL_HEADER) - sizeof(GPA_RANGE);
pfnCount = pfnSize / sizeof(UINT64);
if (pageCount > pfnCount) // we need a gpadl body
{
// fill in the header
msgSize = sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_HEADER) + sizeof(GPA_RANGE) + pfnCount*sizeof(UINT64);
msgHeader = MemAllocZeroed(msgSize);
INITIALIZE_LIST_HEAD(&msgHeader->SubMsgList);
msgHeader->MessageSize=msgSize;
gpaHeader = (VMBUS_CHANNEL_GPADL_HEADER*)msgHeader->Msg;
gpaHeader->RangeCount = 1;
gpaHeader->RangeBufLen = sizeof(GPA_RANGE) + pageCount*sizeof(UINT64);
gpaHeader->Range[0].ByteOffset = 0;
gpaHeader->Range[0].ByteCount = Size;
for (i=0; i<pfnCount; i++)
{
gpaHeader->Range[0].PfnArray[i] = pfn+i;
}
*MsgInfo = msgHeader;
*MessageCount = 1;
pfnSum = pfnCount;
pfnLeft = pageCount - pfnCount;
// how many pfns can we fit
pfnSize = MAX_SIZE_CHANNEL_MESSAGE - sizeof(VMBUS_CHANNEL_GPADL_BODY);
pfnCount = pfnSize / sizeof(UINT64);
// fill in the body
while (pfnLeft)
{
if (pfnLeft > pfnCount)
{
pfnCurr = pfnCount;
}
else
{
pfnCurr = pfnLeft;
}
msgSize = sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_BODY) + pfnCurr*sizeof(UINT64);
msgBody = MemAllocZeroed(msgSize);
ASSERT(msgBody);
msgBody->MessageSize = msgSize;
(*MessageCount)++;
gpadlBody = (VMBUS_CHANNEL_GPADL_BODY*)msgBody->Msg;
// FIXME: Gpadl is UINT32 and we are using a pointer which could be 64-bit
//gpadlBody->Gpadl = kbuffer;
for (i=0; i<pfnCurr; i++)
{
gpadlBody->Pfn[i] = pfn + pfnSum + i;
}
// add to msg header
INSERT_TAIL_LIST(&msgHeader->SubMsgList, &msgBody->MsgListEntry);
pfnSum += pfnCurr;
pfnLeft -= pfnCurr;
}
}
else
{
// everything fits in a header
msgSize = sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_HEADER) + sizeof(GPA_RANGE) + pageCount*sizeof(UINT64);
msgHeader = MemAllocZeroed(msgSize);
msgHeader->MessageSize=msgSize;
gpaHeader = (VMBUS_CHANNEL_GPADL_HEADER*)msgHeader->Msg;
gpaHeader->RangeCount = 1;
gpaHeader->RangeBufLen = sizeof(GPA_RANGE) + pageCount*sizeof(UINT64);
gpaHeader->Range[0].ByteOffset = 0;
gpaHeader->Range[0].ByteCount = Size;
for (i=0; i<pageCount; i++)
{
gpaHeader->Range[0].PfnArray[i] = pfn+i;
}
*MsgInfo = msgHeader;
*MessageCount = 1;
}
return 0;
}
/*++;
Name:
VmbusChannelEstablishGpadl()
Description:
Estabish a GPADL for the specified buffer
--*/
int
VmbusChannelEstablishGpadl(
VMBUS_CHANNEL *Channel,
PVOID Kbuffer, // from kmalloc()
UINT32 Size, // page-size multiple
UINT32 *GpadlHandle
)
{
int ret=0;
VMBUS_CHANNEL_GPADL_HEADER* gpadlMsg;
VMBUS_CHANNEL_GPADL_BODY* gpadlBody;
//VMBUS_CHANNEL_GPADL_CREATED* gpadlCreated;
VMBUS_CHANNEL_MSGINFO *msgInfo;
VMBUS_CHANNEL_MSGINFO *subMsgInfo;
UINT32 msgCount;
LIST_ENTRY* anchor;
LIST_ENTRY* curr;
UINT32 nextGpadlHandle;
DPRINT_ENTER(VMBUS);
nextGpadlHandle = gVmbusConnection.NextGpadlHandle;
InterlockedIncrement((int*)&gVmbusConnection.NextGpadlHandle);
VmbusChannelCreateGpadlHeader(Kbuffer, Size, &msgInfo, &msgCount);
ASSERT(msgInfo != NULL);
ASSERT(msgCount >0);
msgInfo->WaitEvent = WaitEventCreate();
gpadlMsg = (VMBUS_CHANNEL_GPADL_HEADER*)msgInfo->Msg;
gpadlMsg->Header.MessageType = ChannelMessageGpadlHeader;
gpadlMsg->ChildRelId = Channel->OfferMsg.ChildRelId;
gpadlMsg->Gpadl = nextGpadlHandle;
DumpGpadlHeader(gpadlMsg);
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &msgInfo->MsgListEntry);
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
DPRINT_DBG(VMBUS, "buffer %p, size %d msg cnt %d", Kbuffer, Size, msgCount);
DPRINT_DBG(VMBUS, "Sending GPADL Header - len %d", msgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO));
ret = VmbusPostMessage(gpadlMsg, msgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO));
if (ret != 0)
{
DPRINT_ERR(VMBUS, "Unable to open channel - %d", ret);
goto Cleanup;
}
if (msgCount>1)
{
ITERATE_LIST_ENTRIES(anchor, curr, &msgInfo->SubMsgList)
{
subMsgInfo = (VMBUS_CHANNEL_MSGINFO*) curr;
gpadlBody = (VMBUS_CHANNEL_GPADL_BODY*)subMsgInfo->Msg;
gpadlBody->Header.MessageType = ChannelMessageGpadlBody;
gpadlBody->Gpadl = nextGpadlHandle;
DPRINT_DBG(VMBUS, "Sending GPADL Body - len %d", subMsgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO));
DumpGpadlBody(gpadlBody, subMsgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO));
ret = VmbusPostMessage(gpadlBody, subMsgInfo->MessageSize - sizeof(VMBUS_CHANNEL_MSGINFO));
ASSERT(ret == 0);
}
}
WaitEventWait(msgInfo->WaitEvent);
// At this point, we received the gpadl created msg
DPRINT_DBG(VMBUS, "Received GPADL created (relid %d, status %d handle %x)",
Channel->OfferMsg.ChildRelId,
msgInfo->Response.GpadlCreated.CreationStatus,
gpadlMsg->Gpadl);
*GpadlHandle = gpadlMsg->Gpadl;
Cleanup:
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
WaitEventClose(msgInfo->WaitEvent);
MemFree(msgInfo);
DPRINT_EXIT(VMBUS);
return ret;
}
/*++;
Name:
VmbusChannelTeardownGpadl()
Description:
Teardown the specified GPADL handle
--*/
int
VmbusChannelTeardownGpadl(
VMBUS_CHANNEL *Channel,
UINT32 GpadlHandle
)
{
int ret=0;
VMBUS_CHANNEL_GPADL_TEARDOWN *msg;
VMBUS_CHANNEL_MSGINFO* info;
DPRINT_ENTER(VMBUS);
ASSERT(GpadlHandle != 0);
info =
(VMBUS_CHANNEL_MSGINFO*)MemAlloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_GPADL_TEARDOWN));
ASSERT(info != NULL);
info->WaitEvent = WaitEventCreate();
msg = (VMBUS_CHANNEL_GPADL_TEARDOWN*)info->Msg;
msg->Header.MessageType = ChannelMessageGpadlTeardown;
msg->ChildRelId = Channel->OfferMsg.ChildRelId;
msg->Gpadl = GpadlHandle;
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &info->MsgListEntry);
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_GPADL_TEARDOWN));
if (ret != 0)
{
// TODO:
}
WaitEventWait(info->WaitEvent);
// Received a torndown response
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
REMOVE_ENTRY_LIST(&info->MsgListEntry);
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
WaitEventClose(info->WaitEvent);
MemFree(info);
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
VmbusChannelClose()
Description:
Close the specified channel
--*/
VOID
VmbusChannelClose(
VMBUS_CHANNEL *Channel
)
{
int ret=0;
VMBUS_CHANNEL_CLOSE_CHANNEL* msg;
VMBUS_CHANNEL_MSGINFO* info;
DPRINT_ENTER(VMBUS);
// Stop callback and cancel the timer asap
Channel->OnChannelCallback = NULL;
TimerStop(Channel->PollTimer);
// Send a closing message
info =
(VMBUS_CHANNEL_MSGINFO*)MemAlloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_CLOSE_CHANNEL));
ASSERT(info != NULL);
//info->waitEvent = WaitEventCreate();
msg = (VMBUS_CHANNEL_CLOSE_CHANNEL*)info->Msg;
msg->Header.MessageType = ChannelMessageCloseChannel;
msg->ChildRelId = Channel->OfferMsg.ChildRelId;
ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_CLOSE_CHANNEL));
if (ret != 0)
{
// TODO:
}
// Tear down the gpadl for the channel's ring buffer
if (Channel->RingBufferGpadlHandle)
{
VmbusChannelTeardownGpadl(Channel, Channel->RingBufferGpadlHandle);
}
// TODO: Send a msg to release the childRelId
// Cleanup the ring buffers for this channel
RingBufferCleanup(&Channel->Outbound);
RingBufferCleanup(&Channel->Inbound);
PageFree(Channel->RingBufferPages, Channel->RingBufferPageCount);
MemFree(info);
// If we are closing the channel during an error path in opening the channel, don't free the channel
// since the caller will free the channel
if (Channel->State == CHANNEL_OPEN_STATE)
{
SpinlockAcquire(gVmbusConnection.ChannelLock);
REMOVE_ENTRY_LIST(&Channel->ListEntry);
SpinlockRelease(gVmbusConnection.ChannelLock);
FreeVmbusChannel(Channel);
}
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusChannelSendPacket()
Description:
Send the specified buffer on the given channel
--*/
int
VmbusChannelSendPacket(
VMBUS_CHANNEL *Channel,
const PVOID Buffer,
UINT32 BufferLen,
UINT64 RequestId,
VMBUS_PACKET_TYPE Type,
UINT32 Flags
)
{
int ret=0;
VMPACKET_DESCRIPTOR desc;
UINT32 packetLen = sizeof(VMPACKET_DESCRIPTOR) + BufferLen;
UINT32 packetLenAligned = ALIGN_UP(packetLen, sizeof(UINT64));
SG_BUFFER_LIST bufferList[3];
UINT64 alignedData=0;
DPRINT_ENTER(VMBUS);
DPRINT_DBG(VMBUS, "channel %p buffer %p len %d", Channel, Buffer, BufferLen);
DumpVmbusChannel(Channel);
ASSERT((packetLenAligned - packetLen) < sizeof(UINT64));
// Setup the descriptor
desc.Type = Type;//VmbusPacketTypeDataInBand;
desc.Flags = Flags;//VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
desc.DataOffset8 = sizeof(VMPACKET_DESCRIPTOR) >> 3; // in 8-bytes granularity
desc.Length8 = (UINT16)(packetLenAligned >> 3);
desc.TransactionId = RequestId;
bufferList[0].Data = &desc;
bufferList[0].Length = sizeof(VMPACKET_DESCRIPTOR);
bufferList[1].Data = Buffer;
bufferList[1].Length = BufferLen;
bufferList[2].Data = &alignedData;
bufferList[2].Length = packetLenAligned - packetLen;
ret = RingBufferWrite(
&Channel->Outbound,
bufferList,
3);
// TODO: We should determine if this is optional
if (ret == 0 && !GetRingBufferInterruptMask(&Channel->Outbound))
{
VmbusChannelSetEvent(Channel);
}
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
VmbusChannelSendPacketPageBuffer()
Description:
Send a range of single-page buffer packets using a GPADL Direct packet type.
--*/
int
VmbusChannelSendPacketPageBuffer(
VMBUS_CHANNEL *Channel,
PAGE_BUFFER PageBuffers[],
UINT32 PageCount,
PVOID Buffer,
UINT32 BufferLen,
UINT64 RequestId
)
{
int ret=0;
int i=0;
VMBUS_CHANNEL_PACKET_PAGE_BUFFER desc;
UINT32 descSize;
UINT32 packetLen;
UINT32 packetLenAligned;
SG_BUFFER_LIST bufferList[3];
UINT64 alignedData=0;
DPRINT_ENTER(VMBUS);
ASSERT(PageCount <= MAX_PAGE_BUFFER_COUNT);
DumpVmbusChannel(Channel);
// Adjust the size down since VMBUS_CHANNEL_PACKET_PAGE_BUFFER is the largest size we support
descSize = sizeof(VMBUS_CHANNEL_PACKET_PAGE_BUFFER) - ((MAX_PAGE_BUFFER_COUNT - PageCount)*sizeof(PAGE_BUFFER));
packetLen = descSize + BufferLen;
packetLenAligned = ALIGN_UP(packetLen, sizeof(UINT64));
ASSERT((packetLenAligned - packetLen) < sizeof(UINT64));
// Setup the descriptor
desc.Type = VmbusPacketTypeDataUsingGpaDirect;
desc.Flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
desc.DataOffset8 = descSize >> 3; // in 8-bytes grandularity
desc.Length8 = (UINT16)(packetLenAligned >> 3);
desc.TransactionId = RequestId;
desc.RangeCount = PageCount;
for (i=0; i<PageCount; i++)
{
desc.Range[i].Length = PageBuffers[i].Length;
desc.Range[i].Offset = PageBuffers[i].Offset;
desc.Range[i].Pfn = PageBuffers[i].Pfn;
}
bufferList[0].Data = &desc;
bufferList[0].Length = descSize;
bufferList[1].Data = Buffer;
bufferList[1].Length = BufferLen;
bufferList[2].Data = &alignedData;
bufferList[2].Length = packetLenAligned - packetLen;
ret = RingBufferWrite(
&Channel->Outbound,
bufferList,
3);
// TODO: We should determine if this is optional
if (ret == 0 && !GetRingBufferInterruptMask(&Channel->Outbound))
{
VmbusChannelSetEvent(Channel);
}
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
VmbusChannelSendPacketMultiPageBuffer()
Description:
Send a multi-page buffer packet using a GPADL Direct packet type.
--*/
int
VmbusChannelSendPacketMultiPageBuffer(
VMBUS_CHANNEL *Channel,
MULTIPAGE_BUFFER *MultiPageBuffer,
PVOID Buffer,
UINT32 BufferLen,
UINT64 RequestId
)
{
int ret=0;
VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER desc;
UINT32 descSize;
UINT32 packetLen;
UINT32 packetLenAligned;
SG_BUFFER_LIST bufferList[3];
UINT64 alignedData=0;
UINT32 PfnCount = NUM_PAGES_SPANNED(MultiPageBuffer->Offset, MultiPageBuffer->Length);
DPRINT_ENTER(VMBUS);
DumpVmbusChannel(Channel);
DPRINT_DBG(VMBUS, "data buffer - offset %u len %u pfn count %u", MultiPageBuffer->Offset, MultiPageBuffer->Length, PfnCount);
ASSERT(PfnCount > 0);
ASSERT(PfnCount <= MAX_MULTIPAGE_BUFFER_COUNT);
// Adjust the size down since VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER is the largest size we support
descSize = sizeof(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER) - ((MAX_MULTIPAGE_BUFFER_COUNT - PfnCount)*sizeof(UINT64));
packetLen = descSize + BufferLen;
packetLenAligned = ALIGN_UP(packetLen, sizeof(UINT64));
ASSERT((packetLenAligned - packetLen) < sizeof(UINT64));
// Setup the descriptor
desc.Type = VmbusPacketTypeDataUsingGpaDirect;
desc.Flags = VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED;
desc.DataOffset8 = descSize >> 3; // in 8-bytes grandularity
desc.Length8 = (UINT16)(packetLenAligned >> 3);
desc.TransactionId = RequestId;
desc.RangeCount = 1;
desc.Range.Length = MultiPageBuffer->Length;
desc.Range.Offset = MultiPageBuffer->Offset;
memcpy(desc.Range.PfnArray, MultiPageBuffer->PfnArray, PfnCount*sizeof(UINT64));
bufferList[0].Data = &desc;
bufferList[0].Length = descSize;
bufferList[1].Data = Buffer;
bufferList[1].Length = BufferLen;
bufferList[2].Data = &alignedData;
bufferList[2].Length = packetLenAligned - packetLen;
ret = RingBufferWrite(
&Channel->Outbound,
bufferList,
3);
// TODO: We should determine if this is optional
if (ret == 0 && !GetRingBufferInterruptMask(&Channel->Outbound))
{
VmbusChannelSetEvent(Channel);
}
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
VmbusChannelRecvPacket()
Description:
Retrieve the user packet on the specified channel
--*/
// TODO: Do we ever receive a gpa direct packet other than the ones we send ?
int
VmbusChannelRecvPacket(
VMBUS_CHANNEL *Channel,
PVOID Buffer,
UINT32 BufferLen,
UINT32* BufferActualLen,
UINT64* RequestId
)
{
VMPACKET_DESCRIPTOR desc;
UINT32 packetLen;
UINT32 userLen;
int ret;
DPRINT_ENTER(VMBUS);
*BufferActualLen = 0;
*RequestId = 0;
SpinlockAcquire(Channel->InboundLock);
ret = RingBufferPeek(&Channel->Inbound, &desc, sizeof(VMPACKET_DESCRIPTOR));
if (ret != 0)
{
SpinlockRelease(Channel->InboundLock);
//DPRINT_DBG(VMBUS, "nothing to read!!");
DPRINT_EXIT(VMBUS);
return 0;
}
//VmbusChannelClearEvent(Channel);
packetLen = desc.Length8 << 3;
userLen = packetLen - (desc.DataOffset8 << 3);
//ASSERT(userLen > 0);
DPRINT_DBG(VMBUS, "packet received on channel %p relid %d <type %d flag %d tid %llx pktlen %d datalen %d> ",
Channel,
Channel->OfferMsg.ChildRelId,
desc.Type,
desc.Flags,
desc.TransactionId, packetLen, userLen);
*BufferActualLen = userLen;
if (userLen > BufferLen)
{
SpinlockRelease(Channel->InboundLock);
DPRINT_ERR(VMBUS, "buffer too small - got %d needs %d", BufferLen, userLen);
DPRINT_EXIT(VMBUS);
return -1;
}
*RequestId = desc.TransactionId;
// Copy over the packet to the user buffer
ret = RingBufferRead(&Channel->Inbound, Buffer, userLen, (desc.DataOffset8 << 3));
SpinlockRelease(Channel->InboundLock);
DPRINT_EXIT(VMBUS);
return 0;
}
/*++
Name:
VmbusChannelRecvPacketRaw()
Description:
Retrieve the raw packet on the specified channel
--*/
int
VmbusChannelRecvPacketRaw(
VMBUS_CHANNEL *Channel,
PVOID Buffer,
UINT32 BufferLen,
UINT32* BufferActualLen,
UINT64* RequestId
)
{
VMPACKET_DESCRIPTOR desc;
UINT32 packetLen;
UINT32 userLen;
int ret;
DPRINT_ENTER(VMBUS);
*BufferActualLen = 0;
*RequestId = 0;
SpinlockAcquire(Channel->InboundLock);
ret = RingBufferPeek(&Channel->Inbound, &desc, sizeof(VMPACKET_DESCRIPTOR));
if (ret != 0)
{
SpinlockRelease(Channel->InboundLock);
//DPRINT_DBG(VMBUS, "nothing to read!!");
DPRINT_EXIT(VMBUS);
return 0;
}
//VmbusChannelClearEvent(Channel);
packetLen = desc.Length8 << 3;
userLen = packetLen - (desc.DataOffset8 << 3);
DPRINT_DBG(VMBUS, "packet received on channel %p relid %d <type %d flag %d tid %llx pktlen %d datalen %d> ",
Channel,
Channel->OfferMsg.ChildRelId,
desc.Type,
desc.Flags,
desc.TransactionId, packetLen, userLen);
*BufferActualLen = packetLen;
if (packetLen > BufferLen)
{
SpinlockRelease(Channel->InboundLock);
DPRINT_ERR(VMBUS, "buffer too small - needed %d bytes but got space for only %d bytes", packetLen, BufferLen);
DPRINT_EXIT(VMBUS);
return -2;
}
*RequestId = desc.TransactionId;
// Copy over the entire packet to the user buffer
ret = RingBufferRead(&Channel->Inbound, Buffer, packetLen, 0);
SpinlockRelease(Channel->InboundLock);
DPRINT_EXIT(VMBUS);
return 0;
}
/*++
Name:
VmbusChannelOnChannelEvent()
Description:
Channel event callback
--*/
void
VmbusChannelOnChannelEvent(
VMBUS_CHANNEL *Channel
)
{
DumpVmbusChannel(Channel);
ASSERT(Channel->OnChannelCallback);
#ifdef ENABLE_POLLING
TimerStop(Channel->PollTimer);
Channel->OnChannelCallback(Channel->ChannelCallbackContext);
TimerStart(Channel->PollTimer, 100 /* 100us */);
#else
Channel->OnChannelCallback(Channel->ChannelCallbackContext);
#endif
}
/*++
Name:
VmbusChannelOnTimer()
Description:
Timer event callback
--*/
void
VmbusChannelOnTimer(
void *Context
)
{
VMBUS_CHANNEL *channel = (VMBUS_CHANNEL*)Context;
if (channel->OnChannelCallback)
{
channel->OnChannelCallback(channel->ChannelCallbackContext);
#ifdef ENABLE_POLLING
TimerStart(channel->PollTimer, 100 /* 100us */);
#endif
}
}
/*++
Name:
DumpVmbusChannel()
Description:
Dump vmbus channel info to the console
--*/
static void
DumpVmbusChannel(
VMBUS_CHANNEL *Channel
)
{
DPRINT_DBG(VMBUS, "Channel (%d)", Channel->OfferMsg.ChildRelId);
DumpRingInfo(&Channel->Outbound, "Outbound ");
DumpRingInfo(&Channel->Inbound, "Inbound ");
}
// eof
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#ifndef _CHANNEL_H_
#define _CHANNEL_H_
#include "osd.h"
#include "ChannelMgmt.h"
#pragma pack(push,1)
// The format must be the same as VMDATA_GPA_DIRECT
typedef struct _VMBUS_CHANNEL_PACKET_PAGE_BUFFER {
UINT16 Type;
UINT16 DataOffset8;
UINT16 Length8;
UINT16 Flags;
UINT64 TransactionId;
UINT32 Reserved;
UINT32 RangeCount;
PAGE_BUFFER Range[MAX_PAGE_BUFFER_COUNT];
} VMBUS_CHANNEL_PACKET_PAGE_BUFFER;
// The format must be the same as VMDATA_GPA_DIRECT
typedef struct _VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER {
UINT16 Type;
UINT16 DataOffset8;
UINT16 Length8;
UINT16 Flags;
UINT64 TransactionId;
UINT32 Reserved;
UINT32 RangeCount; // Always 1 in this case
MULTIPAGE_BUFFER Range;
} VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER;
#pragma pack(pop)
//
// Routines
//
INTERNAL int
VmbusChannelOpen(
VMBUS_CHANNEL *Channel,
UINT32 SendRingBufferSize,
UINT32 RecvRingBufferSize,
PVOID UserData,
UINT32 UserDataLen,
PFN_CHANNEL_CALLBACK pfnOnChannelCallback,
PVOID Context
);
INTERNAL void
VmbusChannelClose(
VMBUS_CHANNEL *Channel
);
INTERNAL int
VmbusChannelSendPacket(
VMBUS_CHANNEL *Channel,
const PVOID Buffer,
UINT32 BufferLen,
UINT64 RequestId,
VMBUS_PACKET_TYPE Type,
UINT32 Flags
);
INTERNAL int
VmbusChannelSendPacketPageBuffer(
VMBUS_CHANNEL *Channel,
PAGE_BUFFER PageBuffers[],
UINT32 PageCount,
PVOID Buffer,
UINT32 BufferLen,
UINT64 RequestId
);
INTERNAL int
VmbusChannelSendPacketMultiPageBuffer(
VMBUS_CHANNEL *Channel,
MULTIPAGE_BUFFER *MultiPageBuffer,
PVOID Buffer,
UINT32 BufferLen,
UINT64 RequestId
);
INTERNAL int
VmbusChannelEstablishGpadl(
VMBUS_CHANNEL *Channel,
PVOID Kbuffer, // from kmalloc()
UINT32 Size, // page-size multiple
UINT32 *GpadlHandle
);
INTERNAL int
VmbusChannelTeardownGpadl(
VMBUS_CHANNEL *Channel,
UINT32 GpadlHandle
);
INTERNAL int
VmbusChannelRecvPacket(
VMBUS_CHANNEL *Channel,
PVOID Buffer,
UINT32 BufferLen,
UINT32* BufferActualLen,
UINT64* RequestId
);
INTERNAL int
VmbusChannelRecvPacketRaw(
VMBUS_CHANNEL *Channel,
PVOID Buffer,
UINT32 BufferLen,
UINT32* BufferActualLen,
UINT64* RequestId
);
INTERNAL void
VmbusChannelOnChannelEvent(
VMBUS_CHANNEL *Channel
);
INTERNAL void
VmbusChannelGetDebugInfo(
VMBUS_CHANNEL *Channel,
VMBUS_CHANNEL_DEBUG_INFO *DebugInfo
);
INTERNAL void
VmbusChannelOnTimer(
void *Context
);
#endif //_CHANNEL_H_
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include "VmbusPrivate.h"
INTERNAL int
IVmbusChannelOpen(
PDEVICE_OBJECT Device,
UINT32 SendBufferSize,
UINT32 RecvRingBufferSize,
PVOID UserData,
UINT32 UserDataLen,
VMBUS_CHANNEL_CALLBACK ChannelCallback,
PVOID Context
)
{
return VmbusChannelOpen( (VMBUS_CHANNEL*)Device->context,
SendBufferSize,
RecvRingBufferSize,
UserData,
UserDataLen,
ChannelCallback,
Context);
}
INTERNAL void
IVmbusChannelClose(
PDEVICE_OBJECT Device
)
{
VmbusChannelClose((VMBUS_CHANNEL*)Device->context);
}
INTERNAL int
IVmbusChannelSendPacket(
PDEVICE_OBJECT Device,
const PVOID Buffer,
UINT32 BufferLen,
UINT64 RequestId,
UINT32 Type,
UINT32 Flags
)
{
return VmbusChannelSendPacket((VMBUS_CHANNEL*)Device->context,
Buffer,
BufferLen,
RequestId,
Type,
Flags);
}
INTERNAL int
IVmbusChannelSendPacketPageBuffer(
PDEVICE_OBJECT Device,
PAGE_BUFFER PageBuffers[],
UINT32 PageCount,
PVOID Buffer,
UINT32 BufferLen,
UINT64 RequestId
)
{
return VmbusChannelSendPacketPageBuffer((VMBUS_CHANNEL*)Device->context,
PageBuffers,
PageCount,
Buffer,
BufferLen,
RequestId);
}
INTERNAL int
IVmbusChannelSendPacketMultiPageBuffer(
PDEVICE_OBJECT Device,
MULTIPAGE_BUFFER *MultiPageBuffer,
PVOID Buffer,
UINT32 BufferLen,
UINT64 RequestId
)
{
return VmbusChannelSendPacketMultiPageBuffer((VMBUS_CHANNEL*)Device->context,
MultiPageBuffer,
Buffer,
BufferLen,
RequestId);
}
INTERNAL int
IVmbusChannelRecvPacket (
PDEVICE_OBJECT Device,
PVOID Buffer,
UINT32 BufferLen,
UINT32* BufferActualLen,
UINT64* RequestId
)
{
return VmbusChannelRecvPacket((VMBUS_CHANNEL*)Device->context,
Buffer,
BufferLen,
BufferActualLen,
RequestId);
}
INTERNAL int
IVmbusChannelRecvPacketRaw(
PDEVICE_OBJECT Device,
PVOID Buffer,
UINT32 BufferLen,
UINT32* BufferActualLen,
UINT64* RequestId
)
{
return VmbusChannelRecvPacketRaw((VMBUS_CHANNEL*)Device->context,
Buffer,
BufferLen,
BufferActualLen,
RequestId);
}
INTERNAL int
IVmbusChannelEstablishGpadl(
PDEVICE_OBJECT Device,
PVOID Buffer,
UINT32 BufferLen,
UINT32* GpadlHandle
)
{
return VmbusChannelEstablishGpadl((VMBUS_CHANNEL*)Device->context,
Buffer,
BufferLen,
GpadlHandle);
}
INTERNAL int
IVmbusChannelTeardownGpadl(
PDEVICE_OBJECT Device,
UINT32 GpadlHandle
)
{
return VmbusChannelTeardownGpadl((VMBUS_CHANNEL*)Device->context,
GpadlHandle);
}
INTERNAL void
GetChannelInterface(
VMBUS_CHANNEL_INTERFACE *ChannelInterface
)
{
ChannelInterface->Open = IVmbusChannelOpen;
ChannelInterface->Close = IVmbusChannelClose;
ChannelInterface->SendPacket = IVmbusChannelSendPacket;
ChannelInterface->SendPacketPageBuffer = IVmbusChannelSendPacketPageBuffer;
ChannelInterface->SendPacketMultiPageBuffer = IVmbusChannelSendPacketMultiPageBuffer;
ChannelInterface->RecvPacket = IVmbusChannelRecvPacket;
ChannelInterface->RecvPacketRaw = IVmbusChannelRecvPacketRaw;
ChannelInterface->EstablishGpadl = IVmbusChannelEstablishGpadl;
ChannelInterface->TeardownGpadl = IVmbusChannelTeardownGpadl;
ChannelInterface->GetInfo = GetChannelInfo;
}
INTERNAL void
GetChannelInfo(
PDEVICE_OBJECT Device,
DEVICE_INFO *DeviceInfo
)
{
VMBUS_CHANNEL_DEBUG_INFO debugInfo;
if (Device->context)
{
VmbusChannelGetDebugInfo((VMBUS_CHANNEL*)Device->context, &debugInfo);
DeviceInfo->ChannelId = debugInfo.RelId;
DeviceInfo->ChannelState = debugInfo.State;
memcpy(&DeviceInfo->ChannelType, &debugInfo.InterfaceType, sizeof(GUID));
memcpy(&DeviceInfo->ChannelInstance, &debugInfo.InterfaceInstance, sizeof(GUID));
DeviceInfo->MonitorId = debugInfo.MonitorId;
DeviceInfo->ServerMonitorPending = debugInfo.ServerMonitorPending;
DeviceInfo->ServerMonitorLatency = debugInfo.ServerMonitorLatency;
DeviceInfo->ServerMonitorConnectionId = debugInfo.ServerMonitorConnectionId;
DeviceInfo->ClientMonitorPending = debugInfo.ClientMonitorPending;
DeviceInfo->ClientMonitorLatency = debugInfo.ClientMonitorLatency;
DeviceInfo->ClientMonitorConnectionId = debugInfo.ClientMonitorConnectionId;
DeviceInfo->Inbound.InterruptMask = debugInfo.Inbound.CurrentInterruptMask;
DeviceInfo->Inbound.ReadIndex = debugInfo.Inbound.CurrentReadIndex;
DeviceInfo->Inbound.WriteIndex = debugInfo.Inbound.CurrentWriteIndex;
DeviceInfo->Inbound.BytesAvailToRead = debugInfo.Inbound.BytesAvailToRead;
DeviceInfo->Inbound.BytesAvailToWrite = debugInfo.Inbound.BytesAvailToWrite;
DeviceInfo->Outbound.InterruptMask = debugInfo.Outbound.CurrentInterruptMask;
DeviceInfo->Outbound.ReadIndex = debugInfo.Outbound.CurrentReadIndex;
DeviceInfo->Outbound.WriteIndex = debugInfo.Outbound.CurrentWriteIndex;
DeviceInfo->Outbound.BytesAvailToRead = debugInfo.Outbound.BytesAvailToRead;
DeviceInfo->Outbound.BytesAvailToWrite = debugInfo.Outbound.BytesAvailToWrite;
}
}
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#ifndef _CHANNEL_INTERFACE_H_
#define _CHANNEL_INTERFACE_H_
#include "VmbusApi.h"
INTERNAL void
GetChannelInterface(
VMBUS_CHANNEL_INTERFACE *ChannelInterface
);
INTERNAL void
GetChannelInfo(
PDEVICE_OBJECT Device,
DEVICE_INFO *DeviceInfo
);
#endif // _CHANNEL_INTERFACE_H_
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include "osd.h"
#include "logging.h"
#include "VmbusPrivate.h"
//
// Defines
//
//
// Data types
//
typedef void (*PFN_CHANNEL_MESSAGE_HANDLER)(VMBUS_CHANNEL_MESSAGE_HEADER* msg);
typedef struct _VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY {
VMBUS_CHANNEL_MESSAGE_TYPE messageType;
PFN_CHANNEL_MESSAGE_HANDLER messageHandler;
} VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY;
//
// Internal routines
//
static void
VmbusChannelOnOffer(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
);
static void
VmbusChannelOnOpenResult(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
);
static void
VmbusChannelOnOfferRescind(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
);
static void
VmbusChannelOnGpadlCreated(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
);
static void
VmbusChannelOnGpadlTorndown(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
);
static void
VmbusChannelOnOffersDelivered(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
);
static void
VmbusChannelOnVersionResponse(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
);
static void
VmbusChannelProcessOffer(
PVOID context
);
static void
VmbusChannelProcessRescindOffer(
PVOID context
);
//
// Globals
//
#define MAX_NUM_DEVICE_CLASSES_SUPPORTED 4
const GUID gSupportedDeviceClasses[MAX_NUM_DEVICE_CLASSES_SUPPORTED]= {
//{ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}
{.Data = {0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d, 0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f}},// Storage - SCSI
//{F8615163-DF3E-46c5-913F-F2D2F965ED0E}
{.Data = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46, 0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E}}, // Network
//{CFA8B69E-5B4A-4cc0-B98B-8BA1A1F3F95A}
{.Data = {0x9E, 0xB6, 0xA8, 0xCF, 0x4A, 0x5B, 0xc0, 0x4c, 0xB9, 0x8B, 0x8B, 0xA1, 0xA1, 0xF3, 0xF9, 0x5A}}, // Input
//{32412632-86cb-44a2-9b5c-50d1417354f5}
{.Data = {0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5}}, // IDE
};
// Channel message dispatch table
VMBUS_CHANNEL_MESSAGE_TABLE_ENTRY gChannelMessageTable[ChannelMessageCount]= {
{ChannelMessageInvalid, NULL},
{ChannelMessageOfferChannel, VmbusChannelOnOffer},
{ChannelMessageRescindChannelOffer, VmbusChannelOnOfferRescind},
{ChannelMessageRequestOffers, NULL},
{ChannelMessageAllOffersDelivered, VmbusChannelOnOffersDelivered},
{ChannelMessageOpenChannel, NULL},
{ChannelMessageOpenChannelResult, VmbusChannelOnOpenResult},
{ChannelMessageCloseChannel, NULL},
{ChannelMessageGpadlHeader, NULL},
{ChannelMessageGpadlBody, NULL},
{ChannelMessageGpadlCreated, VmbusChannelOnGpadlCreated},
{ChannelMessageGpadlTeardown, NULL},
{ChannelMessageGpadlTorndown, VmbusChannelOnGpadlTorndown},
{ChannelMessageRelIdReleased, NULL},
{ChannelMessageInitiateContact, NULL},
{ChannelMessageVersionResponse, VmbusChannelOnVersionResponse},
{ChannelMessageUnload, NULL},
};
/*++
Name:
AllocVmbusChannel()
Description:
Allocate and initialize a vmbus channel object
--*/
VMBUS_CHANNEL* AllocVmbusChannel(void)
{
VMBUS_CHANNEL* channel;
channel = (VMBUS_CHANNEL*) MemAllocAtomic(sizeof(VMBUS_CHANNEL));
if (!channel)
{
return NULL;
}
memset(channel, 0,sizeof(VMBUS_CHANNEL));
channel->InboundLock = SpinlockCreate();
if (!channel->InboundLock)
{
MemFree(channel);
return NULL;
}
channel->PollTimer = TimerCreate(VmbusChannelOnTimer, channel);
if (!channel->PollTimer)
{
SpinlockClose(channel->InboundLock);
MemFree(channel);
return NULL;
}
//channel->dataWorkQueue = WorkQueueCreate("data");
channel->ControlWQ = WorkQueueCreate("control");
if (!channel->ControlWQ)
{
TimerClose(channel->PollTimer);
SpinlockClose(channel->InboundLock);
MemFree(channel);
return NULL;
}
return channel;
}
/*++
Name:
ReleaseVmbusChannel()
Description:
Release the vmbus channel object itself
--*/
static inline void ReleaseVmbusChannel(void* Context)
{
VMBUS_CHANNEL* channel = (VMBUS_CHANNEL*)Context;
DPRINT_ENTER(VMBUS);
DPRINT_DBG(VMBUS, "releasing channel (%p)", channel);
WorkQueueClose(channel->ControlWQ);
DPRINT_DBG(VMBUS, "channel released (%p)", channel);
MemFree(channel);
DPRINT_EXIT(VMBUS);
}
/*++
Name:
FreeVmbusChannel()
Description:
Release the resources used by the vmbus channel object
--*/
void FreeVmbusChannel(VMBUS_CHANNEL* Channel)
{
SpinlockClose(Channel->InboundLock);
TimerClose(Channel->PollTimer);
// We have to release the channel's workqueue/thread in the vmbus's workqueue/thread context
// ie we can't destroy ourselves.
WorkQueueQueueWorkItem(gVmbusConnection.WorkQueue, ReleaseVmbusChannel, (void*)Channel);
}
/*++
Name:
VmbusChannelProcessOffer()
Description:
Process the offer by creating a channel/device associated with this offer
--*/
static void
VmbusChannelProcessOffer(
PVOID context
)
{
int ret=0;
VMBUS_CHANNEL* newChannel=(VMBUS_CHANNEL*)context;
LIST_ENTRY* anchor;
LIST_ENTRY* curr;
BOOL fNew=TRUE;
VMBUS_CHANNEL* channel;
DPRINT_ENTER(VMBUS);
// Make sure this is a new offer
SpinlockAcquire(gVmbusConnection.ChannelLock);
ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelList)
{
channel = CONTAINING_RECORD(curr, VMBUS_CHANNEL, ListEntry);
if (!memcmp(&channel->OfferMsg.Offer.InterfaceType, &newChannel->OfferMsg.Offer.InterfaceType,sizeof(GUID)) &&
!memcmp(&channel->OfferMsg.Offer.InterfaceInstance, &newChannel->OfferMsg.Offer.InterfaceInstance, sizeof(GUID)))
{
fNew = FALSE;
break;
}
}
if (fNew)
{
INSERT_TAIL_LIST(&gVmbusConnection.ChannelList, &newChannel->ListEntry);
}
SpinlockRelease(gVmbusConnection.ChannelLock);
if (!fNew)
{
DPRINT_DBG(VMBUS, "Ignoring duplicate offer for relid (%d)", newChannel->OfferMsg.ChildRelId);
FreeVmbusChannel(newChannel);
DPRINT_EXIT(VMBUS);
return;
}
// Start the process of binding this offer to the driver
// We need to set the DeviceObject field before calling VmbusChildDeviceAdd()
newChannel->DeviceObject = VmbusChildDeviceCreate(
newChannel->OfferMsg.Offer.InterfaceType,
newChannel->OfferMsg.Offer.InterfaceInstance,
newChannel);
DPRINT_DBG(VMBUS, "child device object allocated - %p", newChannel->DeviceObject);
// Add the new device to the bus. This will kick off device-driver binding
// which eventually invokes the device driver's AddDevice() method.
ret = VmbusChildDeviceAdd(newChannel->DeviceObject);
if (ret != 0)
{
DPRINT_ERR(VMBUS, "unable to add child device object (relid %d)",
newChannel->OfferMsg.ChildRelId);
SpinlockAcquire(gVmbusConnection.ChannelLock);
REMOVE_ENTRY_LIST(&newChannel->ListEntry);
SpinlockRelease(gVmbusConnection.ChannelLock);
FreeVmbusChannel(newChannel);
}
else
{
// This state is used to indicate a successful open so that when we do close the channel normally,
// we can cleanup properly
newChannel->State = CHANNEL_OPEN_STATE;
}
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusChannelProcessRescindOffer()
Description:
Rescind the offer by initiating a device removal
--*/
static void
VmbusChannelProcessRescindOffer(
PVOID context
)
{
VMBUS_CHANNEL* channel=(VMBUS_CHANNEL*)context;
DPRINT_ENTER(VMBUS);
VmbusChildDeviceRemove(channel->DeviceObject);
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusChannelOnOffer()
Description:
Handler for channel offers from vmbus in parent partition. We ignore all offers except
network and storage offers. For each network and storage offers, we create a channel object
and queue a work item to the channel object to process the offer synchronously
--*/
static void
VmbusChannelOnOffer(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
)
{
VMBUS_CHANNEL_OFFER_CHANNEL* offer = (VMBUS_CHANNEL_OFFER_CHANNEL*)hdr;
VMBUS_CHANNEL* newChannel;
GUID *guidType;
GUID *guidInstance;
int i;
int fSupported=0;
DPRINT_ENTER(VMBUS);
for (i=0; i<MAX_NUM_DEVICE_CLASSES_SUPPORTED; i++)
{
if (memcmp(&offer->Offer.InterfaceType, &gSupportedDeviceClasses[i], sizeof(GUID)) == 0)
{
fSupported = 1;
break;
}
}
if (!fSupported)
{
DPRINT_DBG(VMBUS, "Ignoring channel offer notification for child relid %d", offer->ChildRelId);
DPRINT_EXIT(VMBUS);
return;
}
guidType = &offer->Offer.InterfaceType;
guidInstance = &offer->Offer.InterfaceInstance;
DPRINT_INFO(VMBUS, "Channel offer notification - child relid %d monitor id %d allocated %d, "
"type {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x} "
"instance {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
offer->ChildRelId,
offer->MonitorId,
offer->MonitorAllocated,
guidType->Data[3], guidType->Data[2], guidType->Data[1], guidType->Data[0], guidType->Data[5], guidType->Data[4], guidType->Data[7], guidType->Data[6], guidType->Data[8], guidType->Data[9], guidType->Data[10], guidType->Data[11], guidType->Data[12], guidType->Data[13], guidType->Data[14], guidType->Data[15],
guidInstance->Data[3], guidInstance->Data[2], guidInstance->Data[1], guidInstance->Data[0], guidInstance->Data[5], guidInstance->Data[4], guidInstance->Data[7], guidInstance->Data[6], guidInstance->Data[8], guidInstance->Data[9], guidInstance->Data[10], guidInstance->Data[11], guidInstance->Data[12], guidInstance->Data[13], guidInstance->Data[14], guidInstance->Data[15]);
// Allocate the channel object and save this offer.
newChannel = AllocVmbusChannel();
if (!newChannel)
{
DPRINT_ERR(VMBUS, "unable to allocate channel object");
return;
}
DPRINT_DBG(VMBUS, "channel object allocated - %p", newChannel);
memcpy(&newChannel->OfferMsg, offer, sizeof(VMBUS_CHANNEL_OFFER_CHANNEL));
newChannel->MonitorGroup = (UINT8)offer->MonitorId / 32;
newChannel->MonitorBit = (UINT8)offer->MonitorId % 32;
// TODO: Make sure the offer comes from our parent partition
WorkQueueQueueWorkItem(newChannel->ControlWQ, VmbusChannelProcessOffer, newChannel);
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusChannelOnOfferRescind()
Description:
Rescind offer handler. We queue a work item to process this offer
synchronously
--*/
static void
VmbusChannelOnOfferRescind(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
)
{
VMBUS_CHANNEL_RESCIND_OFFER* rescind = (VMBUS_CHANNEL_RESCIND_OFFER*)hdr;
VMBUS_CHANNEL* channel;
DPRINT_ENTER(VMBUS);
channel = GetChannelFromRelId(rescind->ChildRelId);
if (channel == NULL)
{
DPRINT_DBG(VMBUS, "channel not found for relId %d", rescind->ChildRelId);
return;
}
WorkQueueQueueWorkItem(channel->ControlWQ, VmbusChannelProcessRescindOffer, channel);
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusChannelOnOffersDelivered()
Description:
This is invoked when all offers have been delivered.
Nothing to do here.
--*/
static void
VmbusChannelOnOffersDelivered(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
)
{
DPRINT_ENTER(VMBUS);
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusChannelOnOpenResult()
Description:
Open result handler. This is invoked when we received a response
to our channel open request. Find the matching request, copy the
response and signal the requesting thread.
--*/
static void
VmbusChannelOnOpenResult(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
)
{
VMBUS_CHANNEL_OPEN_RESULT* result = (VMBUS_CHANNEL_OPEN_RESULT*)hdr;
LIST_ENTRY* anchor;
LIST_ENTRY* curr;
VMBUS_CHANNEL_MSGINFO* msgInfo;
VMBUS_CHANNEL_MESSAGE_HEADER* requestHeader;
VMBUS_CHANNEL_OPEN_CHANNEL* openMsg;
DPRINT_ENTER(VMBUS);
DPRINT_DBG(VMBUS, "vmbus open result - %d", result->Status);
// Find the open msg, copy the result and signal/unblock the wait event
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelMsgList)
{
msgInfo = (VMBUS_CHANNEL_MSGINFO*) curr;
requestHeader = (VMBUS_CHANNEL_MESSAGE_HEADER*)msgInfo->Msg;
if (requestHeader->MessageType == ChannelMessageOpenChannel)
{
openMsg = (VMBUS_CHANNEL_OPEN_CHANNEL*)msgInfo->Msg;
if (openMsg->ChildRelId == result->ChildRelId &&
openMsg->OpenId == result->OpenId)
{
memcpy(&msgInfo->Response.OpenResult, result, sizeof(VMBUS_CHANNEL_OPEN_RESULT));
WaitEventSet(msgInfo->WaitEvent);
break;
}
}
}
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusChannelOnGpadlCreated()
Description:
GPADL created handler. This is invoked when we received a response
to our gpadl create request. Find the matching request, copy the
response and signal the requesting thread.
--*/
static void
VmbusChannelOnGpadlCreated(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
)
{
VMBUS_CHANNEL_GPADL_CREATED *gpadlCreated = (VMBUS_CHANNEL_GPADL_CREATED*)hdr;
LIST_ENTRY *anchor;
LIST_ENTRY *curr;
VMBUS_CHANNEL_MSGINFO *msgInfo;
VMBUS_CHANNEL_MESSAGE_HEADER *requestHeader;
VMBUS_CHANNEL_GPADL_HEADER *gpadlHeader;
DPRINT_ENTER(VMBUS);
DPRINT_DBG(VMBUS, "vmbus gpadl created result - %d", gpadlCreated->CreationStatus);
// Find the establish msg, copy the result and signal/unblock the wait event
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelMsgList)
{
msgInfo = (VMBUS_CHANNEL_MSGINFO*) curr;
requestHeader = (VMBUS_CHANNEL_MESSAGE_HEADER*)msgInfo->Msg;
if (requestHeader->MessageType == ChannelMessageGpadlHeader)
{
gpadlHeader = (VMBUS_CHANNEL_GPADL_HEADER*)requestHeader;
if ((gpadlCreated->ChildRelId == gpadlHeader->ChildRelId) &&
(gpadlCreated->Gpadl == gpadlHeader->Gpadl))
{
memcpy(&msgInfo->Response.GpadlCreated, gpadlCreated, sizeof(VMBUS_CHANNEL_GPADL_CREATED));
WaitEventSet(msgInfo->WaitEvent);
break;
}
}
}
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusChannelOnGpadlTorndown()
Description:
GPADL torndown handler. This is invoked when we received a response
to our gpadl teardown request. Find the matching request, copy the
response and signal the requesting thread.
--*/
static void
VmbusChannelOnGpadlTorndown(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
)
{
VMBUS_CHANNEL_GPADL_TORNDOWN* gpadlTorndown = (VMBUS_CHANNEL_GPADL_TORNDOWN*)hdr;
LIST_ENTRY* anchor;
LIST_ENTRY* curr;
VMBUS_CHANNEL_MSGINFO* msgInfo;
VMBUS_CHANNEL_MESSAGE_HEADER *requestHeader;
VMBUS_CHANNEL_GPADL_TEARDOWN *gpadlTeardown;
DPRINT_ENTER(VMBUS);
// Find the open msg, copy the result and signal/unblock the wait event
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelMsgList)
{
msgInfo = (VMBUS_CHANNEL_MSGINFO*) curr;
requestHeader = (VMBUS_CHANNEL_MESSAGE_HEADER*)msgInfo->Msg;
if (requestHeader->MessageType == ChannelMessageGpadlTeardown)
{
gpadlTeardown = (VMBUS_CHANNEL_GPADL_TEARDOWN*)requestHeader;
if (gpadlTorndown->Gpadl == gpadlTeardown->Gpadl)
{
memcpy(&msgInfo->Response.GpadlTorndown, gpadlTorndown, sizeof(VMBUS_CHANNEL_GPADL_TORNDOWN));
WaitEventSet(msgInfo->WaitEvent);
break;
}
}
}
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusChannelOnVersionResponse()
Description:
Version response handler. This is invoked when we received a response
to our initiate contact request. Find the matching request, copy the
response and signal the requesting thread.
--*/
static void
VmbusChannelOnVersionResponse(
PVMBUS_CHANNEL_MESSAGE_HEADER hdr
)
{
LIST_ENTRY* anchor;
LIST_ENTRY* curr;
VMBUS_CHANNEL_MSGINFO *msgInfo;
VMBUS_CHANNEL_MESSAGE_HEADER *requestHeader;
VMBUS_CHANNEL_INITIATE_CONTACT *initiate;
VMBUS_CHANNEL_VERSION_RESPONSE *versionResponse = (VMBUS_CHANNEL_VERSION_RESPONSE*)hdr;
DPRINT_ENTER(VMBUS);
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelMsgList)
{
msgInfo = (VMBUS_CHANNEL_MSGINFO*) curr;
requestHeader = (VMBUS_CHANNEL_MESSAGE_HEADER*)msgInfo->Msg;
if (requestHeader->MessageType == ChannelMessageInitiateContact)
{
initiate = (VMBUS_CHANNEL_INITIATE_CONTACT*)requestHeader;
memcpy(&msgInfo->Response.VersionResponse, versionResponse, sizeof(VMBUS_CHANNEL_VERSION_RESPONSE));
WaitEventSet(msgInfo->WaitEvent);
}
}
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusOnChannelMessage()
Description:
Handler for channel protocol messages.
This is invoked in the vmbus worker thread context.
--*/
VOID
VmbusOnChannelMessage(
void *Context
)
{
HV_MESSAGE *msg=(HV_MESSAGE*)Context;
VMBUS_CHANNEL_MESSAGE_HEADER* hdr;
int size;
DPRINT_ENTER(VMBUS);
hdr = (VMBUS_CHANNEL_MESSAGE_HEADER*)msg->u.Payload;
size=msg->Header.PayloadSize;
DPRINT_DBG(VMBUS, "message type %d size %d", hdr->MessageType, size);
if (hdr->MessageType >= ChannelMessageCount)
{
DPRINT_ERR(VMBUS, "Received invalid channel message type %d size %d", hdr->MessageType, size);
PrintBytes((unsigned char *)msg->u.Payload, size);
MemFree(msg);
return;
}
if (gChannelMessageTable[hdr->MessageType].messageHandler)
{
gChannelMessageTable[hdr->MessageType].messageHandler(hdr);
}
else
{
DPRINT_ERR(VMBUS, "Unhandled channel message type %d", hdr->MessageType);
}
// Free the msg that was allocated in VmbusOnMsgDPC()
MemFree(msg);
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusChannelRequestOffers()
Description:
Send a request to get all our pending offers.
--*/
int
VmbusChannelRequestOffers(
VOID
)
{
int ret=0;
VMBUS_CHANNEL_MESSAGE_HEADER* msg;
VMBUS_CHANNEL_MSGINFO* msgInfo;
DPRINT_ENTER(VMBUS);
msgInfo =
(VMBUS_CHANNEL_MSGINFO*)MemAlloc(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_MESSAGE_HEADER));
ASSERT(msgInfo != NULL);
msgInfo->WaitEvent = WaitEventCreate();
msg = (VMBUS_CHANNEL_MESSAGE_HEADER*)msgInfo->Msg;
msg->MessageType = ChannelMessageRequestOffers;
/*SpinlockAcquire(gVmbusConnection.channelMsgLock);
INSERT_TAIL_LIST(&gVmbusConnection.channelMsgList, &msgInfo->msgListEntry);
SpinlockRelease(gVmbusConnection.channelMsgLock);*/
ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_MESSAGE_HEADER));
if (ret != 0)
{
DPRINT_ERR(VMBUS, "Unable to request offers - %d", ret);
/*SpinlockAcquire(gVmbusConnection.channelMsgLock);
REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
SpinlockRelease(gVmbusConnection.channelMsgLock);*/
goto Cleanup;
}
//WaitEventWait(msgInfo->waitEvent);
/*SpinlockAcquire(gVmbusConnection.channelMsgLock);
REMOVE_ENTRY_LIST(&msgInfo->msgListEntry);
SpinlockRelease(gVmbusConnection.channelMsgLock);*/
Cleanup:
if (msgInfo)
{
WaitEventClose(msgInfo->WaitEvent);
MemFree(msgInfo);
}
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
VmbusChannelReleaseUnattachedChannels()
Description:
Release channels that are unattached/unconnected ie (no drivers associated)
--*/
void
VmbusChannelReleaseUnattachedChannels(
VOID
)
{
LIST_ENTRY *entry;
VMBUS_CHANNEL *channel;
VMBUS_CHANNEL *start=NULL;
SpinlockAcquire(gVmbusConnection.ChannelLock);
while (!IsListEmpty(&gVmbusConnection.ChannelList))
{
entry = TOP_LIST_ENTRY(&gVmbusConnection.ChannelList);
channel = CONTAINING_RECORD(entry, VMBUS_CHANNEL, ListEntry);
if (channel == start)
break;
if (!channel->DeviceObject->Driver)
{
REMOVE_ENTRY_LIST(&channel->ListEntry);
DPRINT_INFO(VMBUS, "Releasing unattached device object %p", channel->DeviceObject);
VmbusChildDeviceRemove(channel->DeviceObject);
FreeVmbusChannel(channel);
}
else
{
if (!start)
{
start = channel;
}
}
}
SpinlockRelease(gVmbusConnection.ChannelLock);
}
// eof
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#ifndef _CHANNEL_MGMT_H_
#define _CHANNEL_MGMT_H_
#include "osd.h"
#include "List.h"
#include "RingBuffer.h"
#include "VmbusChannelInterface.h"
#include "ChannelMessages.h"
typedef void (*PFN_CHANNEL_CALLBACK)(PVOID context);
typedef enum {
CHANNEL_OFFER_STATE,
CHANNEL_OPENING_STATE,
CHANNEL_OPEN_STATE,
} VMBUS_CHANNEL_STATE;
typedef struct _VMBUS_CHANNEL {
LIST_ENTRY ListEntry;
DEVICE_OBJECT* DeviceObject;
HANDLE PollTimer; // SA-111 workaround
VMBUS_CHANNEL_STATE State;
VMBUS_CHANNEL_OFFER_CHANNEL OfferMsg;
// These are based on the OfferMsg.MonitorId. Save it here for easy access.
UINT8 MonitorGroup;
UINT8 MonitorBit;
UINT32 RingBufferGpadlHandle;
// Allocated memory for ring buffer
VOID* RingBufferPages;
UINT32 RingBufferPageCount;
RING_BUFFER_INFO Outbound; // send to parent
RING_BUFFER_INFO Inbound; // receive from parent
HANDLE InboundLock;
HANDLE ControlWQ;
// Channel callback are invoked in this workqueue context
//HANDLE dataWorkQueue;
PFN_CHANNEL_CALLBACK OnChannelCallback;
PVOID ChannelCallbackContext;
} VMBUS_CHANNEL;
typedef struct _VMBUS_CHANNEL_DEBUG_INFO {
UINT32 RelId;
VMBUS_CHANNEL_STATE State;
GUID InterfaceType;
GUID InterfaceInstance;
UINT32 MonitorId;
UINT32 ServerMonitorPending;
UINT32 ServerMonitorLatency;
UINT32 ServerMonitorConnectionId;
UINT32 ClientMonitorPending;
UINT32 ClientMonitorLatency;
UINT32 ClientMonitorConnectionId;
RING_BUFFER_DEBUG_INFO Inbound;
RING_BUFFER_DEBUG_INFO Outbound;
} VMBUS_CHANNEL_DEBUG_INFO;
typedef union {
VMBUS_CHANNEL_VERSION_SUPPORTED VersionSupported;
VMBUS_CHANNEL_OPEN_RESULT OpenResult;
VMBUS_CHANNEL_GPADL_TORNDOWN GpadlTorndown;
VMBUS_CHANNEL_GPADL_CREATED GpadlCreated;
VMBUS_CHANNEL_VERSION_RESPONSE VersionResponse;
} VMBUS_CHANNEL_MESSAGE_RESPONSE;
// Represents each channel msg on the vmbus connection
// This is a variable-size data structure depending on
// the msg type itself
typedef struct _VMBUS_CHANNEL_MSGINFO {
// Bookkeeping stuff
LIST_ENTRY MsgListEntry;
// So far, this is only used to handle gpadl body message
LIST_ENTRY SubMsgList;
// Synchronize the request/response if needed
HANDLE WaitEvent;
VMBUS_CHANNEL_MESSAGE_RESPONSE Response;
UINT32 MessageSize;
// The channel message that goes out on the "wire".
// It will contain at minimum the VMBUS_CHANNEL_MESSAGE_HEADER header
unsigned char Msg[0];
} VMBUS_CHANNEL_MSGINFO;
//
// Routines
//
INTERNAL VMBUS_CHANNEL*
AllocVmbusChannel(
void
);
INTERNAL void
FreeVmbusChannel(
VMBUS_CHANNEL *Channel
);
INTERNAL void
VmbusOnChannelMessage(
void *Context
);
INTERNAL int
VmbusChannelRequestOffers(
void
);
INTERNAL void
VmbusChannelReleaseUnattachedChannels(
void
);
#endif //_CHANNEL_MGMT_H_
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include "logging.h"
#include "VmbusPrivate.h"
//
// Globals
//
VMBUS_CONNECTION gVmbusConnection = {
.ConnectState = Disconnected,
.NextGpadlHandle = 0xE1E10,
};
/*++
Name:
VmbusConnect()
Description:
Sends a connect request on the partition service connection
--*/
int
VmbusConnect(
)
{
int ret=0;
VMBUS_CHANNEL_MSGINFO *msgInfo=NULL;
VMBUS_CHANNEL_INITIATE_CONTACT *msg;
DPRINT_ENTER(VMBUS);
// Make sure we are not connecting or connected
if (gVmbusConnection.ConnectState != Disconnected)
return -1;
// Initialize the vmbus connection
gVmbusConnection.ConnectState = Connecting;
gVmbusConnection.WorkQueue = WorkQueueCreate("vmbusQ");
INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelMsgList);
gVmbusConnection.ChannelMsgLock = SpinlockCreate();
INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelList);
gVmbusConnection.ChannelLock = SpinlockCreate();
// Setup the vmbus event connection for channel interrupt abstraction stuff
gVmbusConnection.InterruptPage = PageAlloc(1);
if (gVmbusConnection.InterruptPage == NULL)
{
ret = -1;
goto Cleanup;
}
gVmbusConnection.RecvInterruptPage = gVmbusConnection.InterruptPage;
gVmbusConnection.SendInterruptPage = (void*)((ULONG_PTR)gVmbusConnection.InterruptPage + (PAGE_SIZE >> 1));
// Setup the monitor notification facility. The 1st page for parent->child and the 2nd page for child->parent
gVmbusConnection.MonitorPages = PageAlloc(2);
if (gVmbusConnection.MonitorPages == NULL)
{
ret = -1;
goto Cleanup;
}
msgInfo = (VMBUS_CHANNEL_MSGINFO*)MemAllocZeroed(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_INITIATE_CONTACT));
if (msgInfo == NULL)
{
ret = -1;
goto Cleanup;
}
msgInfo->WaitEvent = WaitEventCreate();
msg = (VMBUS_CHANNEL_INITIATE_CONTACT*)msgInfo->Msg;
msg->Header.MessageType = ChannelMessageInitiateContact;
msg->VMBusVersionRequested = VMBUS_REVISION_NUMBER;
msg->InterruptPage = GetPhysicalAddress(gVmbusConnection.InterruptPage);
msg->MonitorPage1 = GetPhysicalAddress(gVmbusConnection.MonitorPages);
msg->MonitorPage2 = GetPhysicalAddress((PVOID)((ULONG_PTR)gVmbusConnection.MonitorPages + PAGE_SIZE));
// Add to list before we send the request since we may receive the response
// before returning from this routine
SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &msgInfo->MsgListEntry);
SpinlockRelease(gVmbusConnection.ChannelMsgLock);
DPRINT_DBG(VMBUS, "Vmbus connection - interrupt pfn %llx, monitor1 pfn %llx,, monitor2 pfn %llx",
msg->InterruptPage, msg->MonitorPage1, msg->MonitorPage2);
DPRINT_DBG(VMBUS, "Sending channel initiate msg...");
ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_INITIATE_CONTACT));
if (ret != 0)
{
REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
goto Cleanup;
}
// Wait for the connection response
WaitEventWait(msgInfo->WaitEvent);
REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
// Check if successful
if (msgInfo->Response.VersionResponse.VersionSupported)
{
DPRINT_INFO(VMBUS, "Vmbus connected!!");
gVmbusConnection.ConnectState = Connected;
}
else
{
DPRINT_ERR(VMBUS, "Vmbus connection failed!!...current version (%d) not supported", VMBUS_REVISION_NUMBER);
ret = -1;
goto Cleanup;
}
WaitEventClose(msgInfo->WaitEvent);
MemFree(msgInfo);
DPRINT_EXIT(VMBUS);
return 0;
Cleanup:
gVmbusConnection.ConnectState = Disconnected;
WorkQueueClose(gVmbusConnection.WorkQueue);
SpinlockClose(gVmbusConnection.ChannelLock);
SpinlockClose(gVmbusConnection.ChannelMsgLock);
if (gVmbusConnection.InterruptPage)
{
PageFree(gVmbusConnection.InterruptPage, 1);
gVmbusConnection.InterruptPage = NULL;
}
if (gVmbusConnection.MonitorPages)
{
PageFree(gVmbusConnection.MonitorPages, 2);
gVmbusConnection.MonitorPages = NULL;
}
if (msgInfo)
{
if (msgInfo->WaitEvent)
WaitEventClose(msgInfo->WaitEvent);
MemFree(msgInfo);
}
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
VmbusDisconnect()
Description:
Sends a disconnect request on the partition service connection
--*/
int
VmbusDisconnect(
VOID
)
{
int ret=0;
VMBUS_CHANNEL_UNLOAD *msg;
DPRINT_ENTER(VMBUS);
// Make sure we are connected
if (gVmbusConnection.ConnectState != Connected)
return -1;
msg = MemAllocZeroed(sizeof(VMBUS_CHANNEL_UNLOAD));
msg->MessageType = ChannelMessageUnload;
ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_UNLOAD));
if (ret != 0)
{
goto Cleanup;
}
PageFree(gVmbusConnection.InterruptPage, 1);
// TODO: iterate thru the msg list and free up
SpinlockClose(gVmbusConnection.ChannelMsgLock);
WorkQueueClose(gVmbusConnection.WorkQueue);
gVmbusConnection.ConnectState = Disconnected;
DPRINT_INFO(VMBUS, "Vmbus disconnected!!");
Cleanup:
if (msg)
{
MemFree(msg);
}
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
GetChannelFromRelId()
Description:
Get the channel object given its child relative id (ie channel id)
--*/
VMBUS_CHANNEL*
GetChannelFromRelId(
UINT32 relId
)
{
VMBUS_CHANNEL* channel;
VMBUS_CHANNEL* foundChannel=NULL;
LIST_ENTRY* anchor;
LIST_ENTRY* curr;
SpinlockAcquire(gVmbusConnection.ChannelLock);
ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelList)
{
channel = CONTAINING_RECORD(curr, VMBUS_CHANNEL, ListEntry);
if (channel->OfferMsg.ChildRelId == relId)
{
foundChannel = channel;
break;
}
}
SpinlockRelease(gVmbusConnection.ChannelLock);
return foundChannel;
}
/*++
Name:
VmbusProcessChannelEvent()
Description:
Process a channel event notification
--*/
static void
VmbusProcessChannelEvent(
PVOID context
)
{
VMBUS_CHANNEL* channel;
UINT32 relId = (UINT32)(ULONG_PTR)context;
ASSERT(relId > 0);
// Find the channel based on this relid and invokes
// the channel callback to process the event
channel = GetChannelFromRelId(relId);
if (channel)
{
VmbusChannelOnChannelEvent(channel);
//WorkQueueQueueWorkItem(channel->dataWorkQueue, VmbusChannelOnChannelEvent, (void*)channel);
}
else
{
DPRINT_ERR(VMBUS, "channel not found for relid - %d.", relId);
}
}
/*++
Name:
VmbusOnEvents()
Description:
Handler for events
--*/
VOID
VmbusOnEvents(
VOID
)
{
int dword;
//int maxdword = PAGE_SIZE >> 3; // receive size is 1/2 page and divide that by 4 bytes
int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5;
int bit;
int relid;
UINT32* recvInterruptPage = gVmbusConnection.RecvInterruptPage;
//VMBUS_CHANNEL_MESSAGE* receiveMsg;
DPRINT_ENTER(VMBUS);
// Check events
if (recvInterruptPage)
{
for (dword = 0; dword < maxdword; dword++)
{
if (recvInterruptPage[dword])
{
for (bit = 0; bit < 32; bit++)
{
if (BitTestAndClear(&recvInterruptPage[dword], bit))
{
relid = (dword << 5) + bit;
DPRINT_DBG(VMBUS, "event detected for relid - %d", relid);
if (relid == 0) // special case - vmbus channel protocol msg
{
DPRINT_DBG(VMBUS, "invalid relid - %d", relid);
continue; }
else
{
//QueueWorkItem(VmbusProcessEvent, (void*)relid);
//ret = WorkQueueQueueWorkItem(gVmbusConnection.workQueue, VmbusProcessChannelEvent, (void*)relid);
VmbusProcessChannelEvent((void*)(ULONG_PTR)relid);
}
}
}
}
}
}
DPRINT_EXIT(VMBUS);
return;
}
/*++
Name:
VmbusPostMessage()
Description:
Send a msg on the vmbus's message connection
--*/
int
VmbusPostMessage(
PVOID buffer,
SIZE_T bufferLen
)
{
int ret=0;
HV_CONNECTION_ID connId;
connId.AsUINT32 =0;
connId.u.Id = VMBUS_MESSAGE_CONNECTION_ID;
ret = HvPostMessage(
connId,
1,
buffer,
bufferLen);
return ret;
}
/*++
Name:
VmbusSetEvent()
Description:
Send an event notification to the parent
--*/
int
VmbusSetEvent(UINT32 childRelId)
{
int ret=0;
DPRINT_ENTER(VMBUS);
// Each UINT32 represents 32 channels
BitSet((UINT32*)gVmbusConnection.SendInterruptPage + (childRelId >> 5), childRelId & 31);
ret = HvSignalEvent();
DPRINT_EXIT(VMBUS);
return ret;
}
// EOF
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include "logging.h"
#include "VmbusPrivate.h"
//
// Globals
//
// The one and only
HV_CONTEXT gHvContext={
.SynICInitialized = FALSE,
.HypercallPage = NULL,
.SignalEventParam = NULL,
.SignalEventBuffer = NULL,
};
/*++
Name:
HvQueryHypervisorPresence()
Description:
Query the cpuid for presense of windows hypervisor
--*/
static int
HvQueryHypervisorPresence (
void
)
{
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
unsigned int edx;
unsigned int op;
eax = 0;
ebx = 0;
ecx = 0;
edx = 0;
op = HvCpuIdFunctionVersionAndFeatures;
do_cpuid(op, &eax, &ebx, &ecx, &edx);
return (ecx & HV_PRESENT_BIT);
}
/*++
Name:
HvQueryHypervisorInfo()
Description:
Get version info of the windows hypervisor
--*/
static int
HvQueryHypervisorInfo (
void
)
{
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
unsigned int edx;
unsigned int maxLeaf;
unsigned int op;
//
// Its assumed that this is called after confirming that Viridian is present.
// Query id and revision.
//
eax = 0;
ebx = 0;
ecx = 0;
edx = 0;
op = HvCpuIdFunctionHvVendorAndMaxFunction;
do_cpuid(op, &eax, &ebx, &ecx, &edx);
DPRINT_INFO(VMBUS, "Vendor ID: %c%c%c%c%c%c%c%c%c%c%c%c",
(ebx & 0xFF),
((ebx >> 8) & 0xFF),
((ebx >> 16) & 0xFF),
((ebx >> 24) & 0xFF),
(ecx & 0xFF),
((ecx >> 8) & 0xFF),
((ecx >> 16) & 0xFF),
((ecx >> 24) & 0xFF),
(edx & 0xFF),
((edx >> 8) & 0xFF),
((edx >> 16) & 0xFF),
((edx >> 24) & 0xFF));
maxLeaf = eax;
eax = 0;
ebx = 0;
ecx = 0;
edx = 0;
op = HvCpuIdFunctionHvInterface;
do_cpuid(op, &eax, &ebx, &ecx, &edx);
DPRINT_INFO(VMBUS, "Interface ID: %c%c%c%c",
(eax & 0xFF),
((eax >> 8) & 0xFF),
((eax >> 16) & 0xFF),
((eax >> 24) & 0xFF));
if (maxLeaf >= HvCpuIdFunctionMsHvVersion) {
eax = 0;
ebx = 0;
ecx = 0;
edx = 0;
op = HvCpuIdFunctionMsHvVersion;
do_cpuid(op, &eax, &ebx, &ecx, &edx);
DPRINT_INFO(VMBUS, "OS Build:%d-%d.%d-%d-%d.%d",
eax,
ebx >> 16,
ebx & 0xFFFF,
ecx,
edx >> 24,
edx & 0xFFFFFF);
}
return maxLeaf;
}
/*++
Name:
HvDoHypercall()
Description:
Invoke the specified hypercall
--*/
static UINT64
HvDoHypercall (
UINT64 Control,
void* Input,
void* Output
)
{
#ifdef x86_64
UINT64 hvStatus=0;
UINT64 inputAddress = (Input)? GetPhysicalAddress(Input) : 0;
UINT64 outputAddress = (Output)? GetPhysicalAddress(Output) : 0;
volatile void* hypercallPage = gHvContext.HypercallPage;
DPRINT_DBG(VMBUS, "Hypercall <control %llx input phys %llx virt %p output phys %llx virt %p hypercall %p>",
Control,
inputAddress,
Input,
outputAddress,
Output,
hypercallPage);
__asm__ __volatile__ ("mov %0, %%r8" : : "r" (outputAddress): "r8");
__asm__ __volatile__ ("call *%3" : "=a"(hvStatus): "c" (Control), "d" (inputAddress), "m" (hypercallPage));
DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatus);
return hvStatus;
#else
UINT32 controlHi = Control >> 32;
UINT32 controlLo = Control & 0xFFFFFFFF;
UINT32 hvStatusHi = 1;
UINT32 hvStatusLo = 1;
UINT64 inputAddress = (Input) ? GetPhysicalAddress(Input) : 0;
UINT32 inputAddressHi = inputAddress >> 32;
UINT32 inputAddressLo = inputAddress & 0xFFFFFFFF;
UINT64 outputAddress = (Output) ?GetPhysicalAddress(Output) : 0;
UINT32 outputAddressHi = outputAddress >> 32;
UINT32 outputAddressLo = outputAddress & 0xFFFFFFFF;
volatile void* hypercallPage = gHvContext.HypercallPage;
DPRINT_DBG(VMBUS, "Hypercall <control %llx input %p output %p>",
Control,
Input,
Output);
__asm__ __volatile__ ("call *%8" : "=d"(hvStatusHi), "=a"(hvStatusLo) : "d" (controlHi), "a" (controlLo), "b" (inputAddressHi), "c" (inputAddressLo), "D"(outputAddressHi), "S"(outputAddressLo), "m" (hypercallPage));
DPRINT_DBG(VMBUS, "Hypercall <return %llx>", hvStatusLo | ((UINT64)hvStatusHi << 32));
return (hvStatusLo | ((UINT64)hvStatusHi << 32));
#endif // x86_64
}
/*++
Name:
HvInit()
Description:
Main initialization routine. This routine must be called
before any other routines in here are called
--*/
static int
HvInit (
void
)
{
int ret=0;
int maxLeaf;
HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
void* virtAddr=0;
ULONG_PTR physAddr=0;
DPRINT_ENTER(VMBUS);
memset(gHvContext.synICEventPage, 0, sizeof(HANDLE)*MAX_NUM_CPUS);
memset(gHvContext.synICMessagePage, 0, sizeof(HANDLE)*MAX_NUM_CPUS);
if (!HvQueryHypervisorPresence())
{
DPRINT_ERR(VMBUS, "No Windows hypervisor detected!!");
goto Cleanup;
}
DPRINT_INFO(VMBUS, "Windows hypervisor detected! Retrieving more info...");
maxLeaf = HvQueryHypervisorInfo();
//HvQueryHypervisorFeatures(maxLeaf);
// Determine if we are running on xenlinux (ie x2v shim) or native linux
gHvContext.GuestId = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
if (gHvContext.GuestId == 0)
{
// Write our OS info
WriteMsr(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
gHvContext.GuestId = HV_LINUX_GUEST_ID;
}
// See if the hypercall page is already set
hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
{
// Allocate the hypercall page memory
//virtAddr = PageAlloc(1);
virtAddr = VirtualAllocExec(PAGE_SIZE);
if (!virtAddr)
{
DPRINT_ERR(VMBUS, "unable to allocate hypercall page!!");
goto Cleanup;
}
hypercallMsr.Enable = 1;
//hypercallMsr.GuestPhysicalAddress = Logical2PhysicalAddr(virtAddr) >> PAGE_SHIFT;
hypercallMsr.GuestPhysicalAddress = Virtual2Physical(virtAddr) >> PAGE_SHIFT;
WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
// Confirm that hypercall page did get setup.
hypercallMsr.AsUINT64 = 0;
hypercallMsr.AsUINT64 = ReadMsr(HV_X64_MSR_HYPERCALL);
if (!hypercallMsr.Enable)
{
DPRINT_ERR(VMBUS, "unable to set hypercall page!!");
goto Cleanup;
}
gHvContext.HypercallPage = virtAddr;
}
else
{
DPRINT_ERR(VMBUS, "Unknown guest id (0x%llx)!!", gHvContext.GuestId);
goto Cleanup;
}
DPRINT_INFO(VMBUS, "Hypercall page VA=0x%08x, PA=0x%08x",
(unsigned long)gHvContext.HypercallPage,
(unsigned long)hypercallMsr.GuestPhysicalAddress << PAGE_SHIFT);
// Setup the global signal event param for the signal event hypercall
gHvContext.SignalEventBuffer = MemAlloc(sizeof(HV_INPUT_SIGNAL_EVENT_BUFFER));
if (!gHvContext.SignalEventBuffer)
{
goto Cleanup;
}
gHvContext.SignalEventParam = (PHV_INPUT_SIGNAL_EVENT)(ALIGN_UP((ULONG_PTR)gHvContext.SignalEventBuffer, HV_HYPERCALL_PARAM_ALIGN));
gHvContext.SignalEventParam->ConnectionId.AsUINT32 = 0;
gHvContext.SignalEventParam->ConnectionId.u.Id = VMBUS_EVENT_CONNECTION_ID;
gHvContext.SignalEventParam->FlagNumber = 0;
gHvContext.SignalEventParam->RsvdZ = 0;
//DPRINT_DBG(VMBUS, "My id %llu", HvGetCurrentPartitionId());
DPRINT_EXIT(VMBUS);
return ret;
Cleanup:
if (virtAddr)
{
if (hypercallMsr.Enable)
{
hypercallMsr.AsUINT64 = 0;
WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
}
VirtualFree(virtAddr);
}
ret = -1;
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
HvCleanup()
Description:
Cleanup routine. This routine is called normally during driver unloading or exiting.
--*/
void
HvCleanup (
void
)
{
HV_X64_MSR_HYPERCALL_CONTENTS hypercallMsr;
DPRINT_ENTER(VMBUS);
if (gHvContext.SignalEventBuffer)
{
MemFree(gHvContext.SignalEventBuffer);
gHvContext.SignalEventBuffer = NULL;
gHvContext.SignalEventParam = NULL;
}
if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
{
if (gHvContext.HypercallPage)
{
hypercallMsr.AsUINT64 = 0;
WriteMsr(HV_X64_MSR_HYPERCALL, hypercallMsr.AsUINT64);
VirtualFree(gHvContext.HypercallPage);
gHvContext.HypercallPage = NULL;
}
}
DPRINT_EXIT(VMBUS);
}
/*++
Name:
HvPostMessage()
Description:
Post a message using the hypervisor message IPC. This
involves a hypercall.
--*/
HV_STATUS
HvPostMessage(
HV_CONNECTION_ID connectionId,
HV_MESSAGE_TYPE messageType,
PVOID payload,
SIZE_T payloadSize
)
{
struct alignedInput {
UINT64 alignment8;
HV_INPUT_POST_MESSAGE msg;
};
PHV_INPUT_POST_MESSAGE alignedMsg;
HV_STATUS status;
ULONG_PTR addr;
if (payloadSize > HV_MESSAGE_PAYLOAD_BYTE_COUNT)
{
return -1;
}
addr = (ULONG_PTR)MemAllocAtomic(sizeof(struct alignedInput));
if (!addr)
{
return -1;
}
alignedMsg = (PHV_INPUT_POST_MESSAGE)(ALIGN_UP(addr, HV_HYPERCALL_PARAM_ALIGN));
alignedMsg->ConnectionId = connectionId;
alignedMsg->MessageType = messageType;
alignedMsg->PayloadSize = payloadSize;
memcpy((void*)alignedMsg->Payload, payload, payloadSize);
status = HvDoHypercall(HvCallPostMessage, alignedMsg, 0) & 0xFFFF;
MemFree((void*)addr);
return status;
}
/*++
Name:
HvSignalEvent()
Description:
Signal an event on the specified connection using the hypervisor event IPC. This
involves a hypercall.
--*/
HV_STATUS
HvSignalEvent(
)
{
HV_STATUS status;
status = HvDoHypercall(HvCallSignalEvent, gHvContext.SignalEventParam, 0) & 0xFFFF;
return status;
}
/*++
Name:
HvSynicInit()
Description:
Initialize the Synthethic Interrupt Controller. If it is already initialized by
another entity (ie x2v shim), we need to retrieve the initialized message and event pages.
Otherwise, we create and initialize the message and event pages.
--*/
int
HvSynicInit (
UINT32 irqVector
)
{
UINT64 version;
HV_SYNIC_SIMP simp;
HV_SYNIC_SIEFP siefp;
HV_SYNIC_SINT sharedSint;
HV_SYNIC_SCONTROL sctrl;
UINT64 guestID;
int ret=0;
DPRINT_ENTER(VMBUS);
if (!gHvContext.HypercallPage)
{
DPRINT_EXIT(VMBUS);
return ret;
}
// Check the version
version = ReadMsr(HV_X64_MSR_SVERSION);
DPRINT_INFO(VMBUS, "SynIC version: %llx", version);
// TODO: Handle SMP
if (gHvContext.GuestId == HV_XENLINUX_GUEST_ID)
{
DPRINT_INFO(VMBUS, "Skipping SIMP and SIEFP setup since it is already set.");
simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
DPRINT_DBG(VMBUS, "Simp: %llx, Sifep: %llx", simp.AsUINT64, siefp.AsUINT64);
// Determine if we are running on xenlinux (ie x2v shim) or native linux
guestID = ReadMsr(HV_X64_MSR_GUEST_OS_ID);
if (guestID == HV_LINUX_GUEST_ID)
{
gHvContext.synICMessagePage[0] = GetVirtualAddress(simp.BaseSimpGpa << PAGE_SHIFT);
gHvContext.synICEventPage[0] = GetVirtualAddress(siefp.BaseSiefpGpa << PAGE_SHIFT);
}
else
{
DPRINT_ERR(VMBUS, "unknown guest id!!");
goto Cleanup;
}
DPRINT_DBG(VMBUS, "MAPPED: Simp: %p, Sifep: %p", gHvContext.synICMessagePage[0], gHvContext.synICEventPage[0]);
}
else
{
gHvContext.synICMessagePage[0] = PageAlloc(1);
if (gHvContext.synICMessagePage[0] == NULL)
{
DPRINT_ERR(VMBUS, "unable to allocate SYNIC message page!!");
goto Cleanup;
}
gHvContext.synICEventPage[0] = PageAlloc(1);
if (gHvContext.synICEventPage[0] == NULL)
{
DPRINT_ERR(VMBUS, "unable to allocate SYNIC event page!!");
goto Cleanup;
}
//
// Setup the Synic's message page
//
simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
simp.SimpEnabled = 1;
simp.BaseSimpGpa = GetPhysicalAddress(gHvContext.synICMessagePage[0]) >> PAGE_SHIFT;
DPRINT_DBG(VMBUS, "HV_X64_MSR_SIMP msr set to: %llx", simp.AsUINT64);
WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
//
// Setup the Synic's event page
//
siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
siefp.SiefpEnabled = 1;
siefp.BaseSiefpGpa = GetPhysicalAddress(gHvContext.synICEventPage[0]) >> PAGE_SHIFT;
DPRINT_DBG(VMBUS, "HV_X64_MSR_SIEFP msr set to: %llx", siefp.AsUINT64);
WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
}
//
// Setup the interception SINT.
//
//WriteMsr((HV_X64_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX),
// interceptionSint.AsUINT64);
//
// Setup the shared SINT.
//
sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
sharedSint.AsUINT64 = 0;
sharedSint.Vector = irqVector; //HV_SHARED_SINT_IDT_VECTOR + 0x20;
sharedSint.Masked = FALSE;
sharedSint.AutoEoi = TRUE;
DPRINT_DBG(VMBUS, "HV_X64_MSR_SINT1 msr set to: %llx", sharedSint.AsUINT64);
WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
// Enable the global synic bit
sctrl.AsUINT64 = ReadMsr(HV_X64_MSR_SCONTROL);
sctrl.Enable = 1;
WriteMsr(HV_X64_MSR_SCONTROL, sctrl.AsUINT64);
gHvContext.SynICInitialized = TRUE;
DPRINT_EXIT(VMBUS);
return ret;
Cleanup:
ret = -1;
if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
{
if (gHvContext.synICEventPage[0])
{
PageFree(gHvContext.synICEventPage[0],1);
}
if (gHvContext.synICMessagePage[0])
{
PageFree(gHvContext.synICMessagePage[0], 1);
}
}
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
HvSynicCleanup()
Description:
Cleanup routine for HvSynicInit().
--*/
VOID
HvSynicCleanup(
VOID
)
{
HV_SYNIC_SINT sharedSint;
HV_SYNIC_SIMP simp;
HV_SYNIC_SIEFP siefp;
DPRINT_ENTER(VMBUS);
if (!gHvContext.SynICInitialized)
{
DPRINT_EXIT(VMBUS);
return;
}
sharedSint.AsUINT64 = ReadMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT);
sharedSint.Masked = 1;
// Disable the interrupt
WriteMsr(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, sharedSint.AsUINT64);
// Disable and free the resources only if we are running as native linux
// since in xenlinux, we are sharing the resources with the x2v shim
if (gHvContext.GuestId == HV_LINUX_GUEST_ID)
{
simp.AsUINT64 = ReadMsr(HV_X64_MSR_SIMP);
simp.SimpEnabled = 0;
simp.BaseSimpGpa = 0;
WriteMsr(HV_X64_MSR_SIMP, simp.AsUINT64);
siefp.AsUINT64 = ReadMsr(HV_X64_MSR_SIEFP);
siefp.SiefpEnabled = 0;
siefp.BaseSiefpGpa = 0;
WriteMsr(HV_X64_MSR_SIEFP, siefp.AsUINT64);
PageFree(gHvContext.synICMessagePage[0], 1);
PageFree(gHvContext.synICEventPage[0], 1);
}
DPRINT_EXIT(VMBUS);
}
// eof
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#ifndef __HV_H__
#define __HV_H__
#include "osd.h"
#include "HvTypes.h"
#include "HvStatus.h"
//#include "HvVmApi.h"
//#include "HvKeApi.h"
//#include "HvMmApi.h"
//#include "HvCpuApi.h"
#include "HvHalApi.h"
#include "HvVpApi.h"
//#include "HvTrApi.h"
#include "HvSynicApi.h"
//#include "HvAmApi.h"
//#include "HvHkApi.h"
//#include "HvValApi.h"
#include "HvHcApi.h"
#include "HvPtApi.h"
enum
{
VMBUS_MESSAGE_CONNECTION_ID = 1,
VMBUS_MESSAGE_PORT_ID = 1,
VMBUS_EVENT_CONNECTION_ID = 2,
VMBUS_EVENT_PORT_ID = 2,
VMBUS_MONITOR_CONNECTION_ID = 3,
VMBUS_MONITOR_PORT_ID = 3,
VMBUS_MESSAGE_SINT = 2
};
//
// #defines
//
#define HV_PRESENT_BIT 0x80000000
#define HV_XENLINUX_GUEST_ID_LO 0x00000000
#define HV_XENLINUX_GUEST_ID_HI 0x0B00B135
#define HV_XENLINUX_GUEST_ID (((UINT64)HV_XENLINUX_GUEST_ID_HI << 32) | HV_XENLINUX_GUEST_ID_LO)
#define HV_LINUX_GUEST_ID_LO 0x00000000
#define HV_LINUX_GUEST_ID_HI 0xB16B00B5
#define HV_LINUX_GUEST_ID (((UINT64)HV_LINUX_GUEST_ID_HI << 32) | HV_LINUX_GUEST_ID_LO)
#define HV_CPU_POWER_MANAGEMENT (1 << 0)
#define HV_RECOMMENDATIONS_MAX 4
#define HV_X64_MAX 5
#define HV_CAPS_MAX 8
#define HV_HYPERCALL_PARAM_ALIGN sizeof(UINT64)
//
// Service definitions
//
#define HV_SERVICE_PARENT_PORT (0)
#define HV_SERVICE_PARENT_CONNECTION (0)
#define HV_SERVICE_CONNECT_RESPONSE_SUCCESS (0)
#define HV_SERVICE_CONNECT_RESPONSE_INVALID_PARAMETER (1)
#define HV_SERVICE_CONNECT_RESPONSE_UNKNOWN_SERVICE (2)
#define HV_SERVICE_CONNECT_RESPONSE_CONNECTION_REJECTED (3)
#define HV_SERVICE_CONNECT_REQUEST_MESSAGE_ID (1)
#define HV_SERVICE_CONNECT_RESPONSE_MESSAGE_ID (2)
#define HV_SERVICE_DISCONNECT_REQUEST_MESSAGE_ID (3)
#define HV_SERVICE_DISCONNECT_RESPONSE_MESSAGE_ID (4)
#define HV_SERVICE_MAX_MESSAGE_ID (4)
#define HV_SERVICE_PROTOCOL_VERSION (0x0010)
#define HV_CONNECT_PAYLOAD_BYTE_COUNT 64
//#define VMBUS_REVISION_NUMBER 6
//#define VMBUS_PORT_ID 11 // Our local vmbus's port and connection id. Anything >0 is fine
// 628180B8-308D-4c5e-B7DB-1BEB62E62EF4
static const GUID VMBUS_SERVICE_ID = {.Data = {0xb8, 0x80, 0x81, 0x62, 0x8d, 0x30, 0x5e, 0x4c, 0xb7, 0xdb, 0x1b, 0xeb, 0x62, 0xe6, 0x2e, 0xf4} };
#define MAX_NUM_CPUS 1
typedef struct {
UINT64 Align8;
HV_INPUT_SIGNAL_EVENT Event;
} HV_INPUT_SIGNAL_EVENT_BUFFER;
typedef struct {
UINT64 GuestId; // XenLinux or native Linux. If XenLinux, the hypercall and synic pages has already been initialized
void* HypercallPage;
BOOL SynICInitialized;
// This is used as an input param to HvCallSignalEvent hypercall. The input param is immutable
// in our usage and must be dynamic mem (vs stack or global).
HV_INPUT_SIGNAL_EVENT_BUFFER *SignalEventBuffer;
HV_INPUT_SIGNAL_EVENT *SignalEventParam; // 8-bytes aligned of the buffer above
HANDLE synICMessagePage[MAX_NUM_CPUS];
HANDLE synICEventPage[MAX_NUM_CPUS];
} HV_CONTEXT;
extern HV_CONTEXT gHvContext;
//
// Inline routines
//
static inline unsigned long long ReadMsr(int msr)
{
unsigned long long val;
RDMSR(msr, val);
return val;
}
static inline void WriteMsr(int msr, UINT64 val)
{
WRMSR(msr, val);
return;
}
//
// Hv Interface
//
INTERNAL int
HvInit(
VOID
);
INTERNAL VOID
HvCleanup(
VOID
);
INTERNAL HV_STATUS
HvPostMessage(
HV_CONNECTION_ID connectionId,
HV_MESSAGE_TYPE messageType,
PVOID payload,
SIZE_T payloadSize
);
INTERNAL HV_STATUS
HvSignalEvent(
VOID
);
INTERNAL int
HvSynicInit(
UINT32 irqVector
);
INTERNAL VOID
HvSynicCleanup(
VOID
);
#endif // __HV_H__
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include "logging.h"
#include "RingBuffer.h"
//
// #defines
//
// Amount of space to write to
#define BYTES_AVAIL_TO_WRITE(r, w, z) ((w) >= (r))?((z) - ((w) - (r))):((r) - (w))
/*++
Name:
GetRingBufferAvailBytes()
Description:
Get number of bytes available to read and to write to
for the specified ring buffer
--*/
static inline void
GetRingBufferAvailBytes(RING_BUFFER_INFO *rbi, UINT32 *read, UINT32 *write)
{
UINT32 read_loc,write_loc;
// Capture the read/write indices before they changed
read_loc = rbi->RingBuffer->ReadIndex;
write_loc = rbi->RingBuffer->WriteIndex;
*write = BYTES_AVAIL_TO_WRITE(read_loc, write_loc, rbi->RingDataSize);
*read = rbi->RingDataSize - *write;
}
/*++
Name:
GetNextWriteLocation()
Description:
Get the next write location for the specified ring buffer
--*/
static inline UINT32
GetNextWriteLocation(RING_BUFFER_INFO* RingInfo)
{
UINT32 next = RingInfo->RingBuffer->WriteIndex;
ASSERT(next < RingInfo->RingDataSize);
return next;
}
/*++
Name:
SetNextWriteLocation()
Description:
Set the next write location for the specified ring buffer
--*/
static inline void
SetNextWriteLocation(RING_BUFFER_INFO* RingInfo, UINT32 NextWriteLocation)
{
RingInfo->RingBuffer->WriteIndex = NextWriteLocation;
}
/*++
Name:
GetNextReadLocation()
Description:
Get the next read location for the specified ring buffer
--*/
static inline UINT32
GetNextReadLocation(RING_BUFFER_INFO* RingInfo)
{
UINT32 next = RingInfo->RingBuffer->ReadIndex;
ASSERT(next < RingInfo->RingDataSize);
return next;
}
/*++
Name:
GetNextReadLocationWithOffset()
Description:
Get the next read location + offset for the specified ring buffer.
This allows the caller to skip
--*/
static inline UINT32
GetNextReadLocationWithOffset(RING_BUFFER_INFO* RingInfo, UINT32 Offset)
{
UINT32 next = RingInfo->RingBuffer->ReadIndex;
ASSERT(next < RingInfo->RingDataSize);
next += Offset;
next %= RingInfo->RingDataSize;
return next;
}
/*++
Name:
SetNextReadLocation()
Description:
Set the next read location for the specified ring buffer
--*/
static inline void
SetNextReadLocation(RING_BUFFER_INFO* RingInfo, UINT32 NextReadLocation)
{
RingInfo->RingBuffer->ReadIndex = NextReadLocation;
}
/*++
Name:
GetRingBuffer()
Description:
Get the start of the ring buffer
--*/
static inline PVOID
GetRingBuffer(RING_BUFFER_INFO* RingInfo)
{
return (PVOID)RingInfo->RingBuffer->Buffer;
}
/*++
Name:
GetRingBufferSize()
Description:
Get the size of the ring buffer
--*/
static inline UINT32
GetRingBufferSize(RING_BUFFER_INFO* RingInfo)
{
return RingInfo->RingDataSize;
}
/*++
Name:
GetRingBufferIndices()
Description:
Get the read and write indices as UINT64 of the specified ring buffer
--*/
static inline UINT64
GetRingBufferIndices(RING_BUFFER_INFO* RingInfo)
{
return ((UINT64)RingInfo->RingBuffer->WriteIndex << 32) || RingInfo->RingBuffer->ReadIndex;
}
/*++
Name:
DumpRingInfo()
Description:
Dump out to console the ring buffer info
--*/
void
DumpRingInfo(RING_BUFFER_INFO* RingInfo, char *Prefix)
{
UINT32 bytesAvailToWrite;
UINT32 bytesAvailToRead;
GetRingBufferAvailBytes(RingInfo, &bytesAvailToRead, &bytesAvailToWrite);
DPRINT(VMBUS, DEBUG_RING_LVL, "%s <<ringinfo %p buffer %p avail write %u avail read %u read idx %u write idx %u>>",
Prefix,
RingInfo,
RingInfo->RingBuffer->Buffer,
bytesAvailToWrite,
bytesAvailToRead,
RingInfo->RingBuffer->ReadIndex,
RingInfo->RingBuffer->WriteIndex);
}
//
// Internal routines
//
static UINT32
CopyToRingBuffer(
RING_BUFFER_INFO *RingInfo,
UINT32 StartWriteOffset,
PVOID Src,
UINT32 SrcLen);
static UINT32
CopyFromRingBuffer(
RING_BUFFER_INFO *RingInfo,
PVOID Dest,
UINT32 DestLen,
UINT32 StartReadOffset);
/*++
Name:
RingBufferGetDebugInfo()
Description:
Get various debug metrics for the specified ring buffer
--*/
void
RingBufferGetDebugInfo(
RING_BUFFER_INFO *RingInfo,
RING_BUFFER_DEBUG_INFO *DebugInfo
)
{
UINT32 bytesAvailToWrite;
UINT32 bytesAvailToRead;
if (RingInfo->RingBuffer)
{
GetRingBufferAvailBytes(RingInfo, &bytesAvailToRead, &bytesAvailToWrite);
DebugInfo->BytesAvailToRead = bytesAvailToRead;
DebugInfo->BytesAvailToWrite = bytesAvailToWrite;
DebugInfo->CurrentReadIndex = RingInfo->RingBuffer->ReadIndex;
DebugInfo->CurrentWriteIndex = RingInfo->RingBuffer->WriteIndex;
DebugInfo->CurrentInterruptMask = RingInfo->RingBuffer->InterruptMask;
}
}
/*++
Name:
GetRingBufferInterruptMask()
Description:
Get the interrupt mask for the specified ring buffer
--*/
UINT32
GetRingBufferInterruptMask(
RING_BUFFER_INFO *rbi
)
{
return rbi->RingBuffer->InterruptMask;
}
/*++
Name:
RingBufferInit()
Description:
Initialize the ring buffer
--*/
int
RingBufferInit(
RING_BUFFER_INFO *RingInfo,
VOID *Buffer,
UINT32 BufferLen
)
{
ASSERT(sizeof(RING_BUFFER) == PAGE_SIZE);
memset(RingInfo, 0, sizeof(RING_BUFFER_INFO));
RingInfo->RingBuffer = (RING_BUFFER*)Buffer;
RingInfo->RingBuffer->ReadIndex = RingInfo->RingBuffer->WriteIndex = 0;
RingInfo->RingSize = BufferLen;
RingInfo->RingDataSize = BufferLen - sizeof(RING_BUFFER);
RingInfo->RingLock = SpinlockCreate();
return 0;
}
/*++
Name:
RingBufferCleanup()
Description:
Cleanup the ring buffer
--*/
void
RingBufferCleanup(
RING_BUFFER_INFO* RingInfo
)
{
SpinlockClose(RingInfo->RingLock);
}
/*++
Name:
RingBufferWrite()
Description:
Write to the ring buffer
--*/
int
RingBufferWrite(
RING_BUFFER_INFO* OutRingInfo,
SG_BUFFER_LIST SgBuffers[],
UINT32 SgBufferCount
)
{
int i=0;
UINT32 byteAvailToWrite;
UINT32 byteAvailToRead;
UINT32 totalBytesToWrite=0;
volatile UINT32 nextWriteLocation;
UINT64 prevIndices=0;
DPRINT_ENTER(VMBUS);
for (i=0; i < SgBufferCount; i++)
{
totalBytesToWrite += SgBuffers[i].Length;
}
totalBytesToWrite += sizeof(UINT64);
SpinlockAcquire(OutRingInfo->RingLock);
GetRingBufferAvailBytes(OutRingInfo, &byteAvailToRead, &byteAvailToWrite);
DPRINT_DBG(VMBUS, "Writing %u bytes...", totalBytesToWrite);
//DumpRingInfo(OutRingInfo, "BEFORE ");
// If there is only room for the packet, assume it is full. Otherwise, the next time around, we think the ring buffer
// is empty since the read index == write index
if (byteAvailToWrite <= totalBytesToWrite)
{
DPRINT_DBG(VMBUS, "No more space left on outbound ring buffer (needed %u, avail %u)", totalBytesToWrite, byteAvailToWrite);
SpinlockRelease(OutRingInfo->RingLock);
DPRINT_EXIT(VMBUS);
return -1;
}
// Write to the ring buffer
nextWriteLocation = GetNextWriteLocation(OutRingInfo);
for (i=0; i < SgBufferCount; i++)
{
nextWriteLocation = CopyToRingBuffer(OutRingInfo,
nextWriteLocation,
SgBuffers[i].Data,
SgBuffers[i].Length);
}
// Set previous packet start
prevIndices = GetRingBufferIndices(OutRingInfo);
nextWriteLocation = CopyToRingBuffer(OutRingInfo,
nextWriteLocation,
&prevIndices,
sizeof(UINT64));
// Make sure we flush all writes before updating the writeIndex
MemoryFence();
// Now, update the write location
SetNextWriteLocation(OutRingInfo, nextWriteLocation);
//DumpRingInfo(OutRingInfo, "AFTER ");
SpinlockRelease(OutRingInfo->RingLock);
DPRINT_EXIT(VMBUS);
return 0;
}
/*++
Name:
RingBufferPeek()
Description:
Read without advancing the read index
--*/
int
RingBufferPeek(
RING_BUFFER_INFO* InRingInfo,
void* Buffer,
UINT32 BufferLen
)
{
UINT32 bytesAvailToWrite;
UINT32 bytesAvailToRead;
UINT32 nextReadLocation=0;
SpinlockAcquire(InRingInfo->RingLock);
GetRingBufferAvailBytes(InRingInfo, &bytesAvailToRead, &bytesAvailToWrite);
// Make sure there is something to read
if (bytesAvailToRead < BufferLen )
{
//DPRINT_DBG(VMBUS, "got callback but not enough to read <avail to read %d read size %d>!!", bytesAvailToRead, BufferLen);
SpinlockRelease(InRingInfo->RingLock);
return -1;
}
// Convert to byte offset
nextReadLocation = GetNextReadLocation(InRingInfo);
nextReadLocation = CopyFromRingBuffer(InRingInfo,
Buffer,
BufferLen,
nextReadLocation);
SpinlockRelease(InRingInfo->RingLock);
return 0;
}
/*++
Name:
RingBufferRead()
Description:
Read and advance the read index
--*/
int
RingBufferRead(
RING_BUFFER_INFO* InRingInfo,
PVOID Buffer,
UINT32 BufferLen,
UINT32 Offset
)
{
UINT32 bytesAvailToWrite;
UINT32 bytesAvailToRead;
UINT32 nextReadLocation=0;
UINT64 prevIndices=0;
ASSERT(BufferLen > 0);
SpinlockAcquire(InRingInfo->RingLock);
GetRingBufferAvailBytes(InRingInfo, &bytesAvailToRead, &bytesAvailToWrite);
DPRINT_DBG(VMBUS, "Reading %u bytes...", BufferLen);
//DumpRingInfo(InRingInfo, "BEFORE ");
// Make sure there is something to read
if (bytesAvailToRead < BufferLen )
{
DPRINT_DBG(VMBUS, "got callback but not enough to read <avail to read %d read size %d>!!", bytesAvailToRead, BufferLen);
SpinlockRelease(InRingInfo->RingLock);
return -1;
}
nextReadLocation = GetNextReadLocationWithOffset(InRingInfo, Offset);
nextReadLocation = CopyFromRingBuffer(InRingInfo,
Buffer,
BufferLen,
nextReadLocation);
nextReadLocation = CopyFromRingBuffer(InRingInfo,
&prevIndices,
sizeof(UINT64),
nextReadLocation);
// Make sure all reads are done before we update the read index since
// the writer may start writing to the read area once the read index is updated
MemoryFence();
// Update the read index
SetNextReadLocation(InRingInfo, nextReadLocation);
//DumpRingInfo(InRingInfo, "AFTER ");
SpinlockRelease(InRingInfo->RingLock);
return 0;
}
/*++
Name:
CopyToRingBuffer()
Description:
Helper routine to copy from source to ring buffer.
Assume there is enough room. Handles wrap-around in dest case only!!
--*/
UINT32
CopyToRingBuffer(
RING_BUFFER_INFO *RingInfo,
UINT32 StartWriteOffset,
PVOID Src,
UINT32 SrcLen)
{
PVOID ringBuffer=GetRingBuffer(RingInfo);
UINT32 ringBufferSize=GetRingBufferSize(RingInfo);
UINT32 fragLen;
if (SrcLen > ringBufferSize - StartWriteOffset) // wrap-around detected!
{
DPRINT_DBG(VMBUS, "wrap-around detected!");
fragLen = ringBufferSize - StartWriteOffset;
memcpy(ringBuffer + StartWriteOffset, Src, fragLen);
memcpy(ringBuffer, Src + fragLen, SrcLen - fragLen);
}
else
{
memcpy(ringBuffer + StartWriteOffset, Src, SrcLen);
}
StartWriteOffset += SrcLen;
StartWriteOffset %= ringBufferSize;
return StartWriteOffset;
}
/*++
Name:
CopyFromRingBuffer()
Description:
Helper routine to copy to source from ring buffer.
Assume there is enough room. Handles wrap-around in src case only!!
--*/
UINT32
CopyFromRingBuffer(
RING_BUFFER_INFO *RingInfo,
PVOID Dest,
UINT32 DestLen,
UINT32 StartReadOffset)
{
PVOID ringBuffer=GetRingBuffer(RingInfo);
UINT32 ringBufferSize=GetRingBufferSize(RingInfo);
UINT32 fragLen;
if (DestLen > ringBufferSize - StartReadOffset) // wrap-around detected at the src
{
DPRINT_DBG(VMBUS, "src wrap-around detected!");
fragLen = ringBufferSize - StartReadOffset;
memcpy(Dest, ringBuffer + StartReadOffset, fragLen);
memcpy(Dest + fragLen, ringBuffer, DestLen - fragLen);
}
else
{
memcpy(Dest, ringBuffer + StartReadOffset, DestLen);
}
StartReadOffset += DestLen;
StartReadOffset %= ringBufferSize;
return StartReadOffset;
}
// eof
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#ifndef _RING_BUFFER_H_
#define _RING_BUFFER_H_
#include "osd.h"
typedef struct _SG_BUFFER_LIST {
PVOID Data;
UINT32 Length;
} SG_BUFFER_LIST;
typedef struct _RING_BUFFER {
volatile UINT32 WriteIndex; // Offset in bytes from the start of ring data below
volatile UINT32 ReadIndex; // Offset in bytes from the start of ring data below
volatile UINT32 InterruptMask;
UINT8 Reserved[4084]; // Pad it to PAGE_SIZE so that data starts on page boundary
// NOTE: The InterruptMask field is used only for channels but since our vmbus connection
// also uses this data structure and its data starts here, we commented out this field.
// volatile UINT32 InterruptMask;
// Ring data starts here + RingDataStartOffset !!! DO NOT place any fields below this !!!
UINT8 Buffer[0];
} STRUCT_PACKED RING_BUFFER;
typedef struct _RING_BUFFER_INFO {
RING_BUFFER* RingBuffer;
UINT32 RingSize; // Include the shared header
HANDLE RingLock;
UINT32 RingDataSize; // < ringSize
UINT32 RingDataStartOffset;
} RING_BUFFER_INFO;
typedef struct _RING_BUFFER_DEBUG_INFO {
UINT32 CurrentInterruptMask;
UINT32 CurrentReadIndex;
UINT32 CurrentWriteIndex;
UINT32 BytesAvailToRead;
UINT32 BytesAvailToWrite;
}RING_BUFFER_DEBUG_INFO;
//
// Interface
//
INTERNAL int
RingBufferInit(
RING_BUFFER_INFO *RingInfo,
PVOID Buffer,
UINT32 BufferLen
);
INTERNAL void
RingBufferCleanup(
RING_BUFFER_INFO *RingInfo
);
INTERNAL int
RingBufferWrite(
RING_BUFFER_INFO *RingInfo,
SG_BUFFER_LIST SgBuffers[],
UINT32 SgBufferCount
);
INTERNAL int
RingBufferPeek(
RING_BUFFER_INFO *RingInfo,
PVOID Buffer,
UINT32 BufferLen
);
INTERNAL int
RingBufferRead(
RING_BUFFER_INFO *RingInfo,
PVOID Buffer,
UINT32 BufferLen,
UINT32 Offset
);
INTERNAL UINT32
GetRingBufferInterruptMask(
RING_BUFFER_INFO *RingInfo
);
INTERNAL void
DumpRingInfo(
RING_BUFFER_INFO* RingInfo,
char *Prefix
);
INTERNAL void
RingBufferGetDebugInfo(
RING_BUFFER_INFO *RingInfo,
RING_BUFFER_DEBUG_INFO *DebugInfo
);
#endif // _RING_BUFFER_H_
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include "Vmbus.c"
#include "Hv.c"
#include "Connection.c"
#include "Channel.c"
#include "ChannelMgmt.c"
#include "ChannelInterface.c"
#include "RingBuffer.c"
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#pragma once
const char VersionDate[]=__DATE__;
const char VersionTime[]=__TIME__;
const char VersionDesc[]= "Version 2.0";
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include "logging.h"
#include "VersionInfo.h"
#include "VmbusPrivate.h"
//
// Globals
//
static const char* gDriverName="vmbus";
// Windows vmbus does not defined this. We defined this to be consistent with other devices
//{c5295816-f63a-4d5f-8d1a-4daf999ca185}
static const GUID gVmbusDeviceType={
.Data = {0x16, 0x58, 0x29, 0xc5, 0x3a, 0xf6, 0x5f, 0x4d, 0x8d, 0x1a, 0x4d, 0xaf, 0x99, 0x9c, 0xa1, 0x85}
};
//{ac3760fc-9adf-40aa-9427-a70ed6de95c5}
static const GUID gVmbusDeviceId={
.Data = {0xfc, 0x60, 0x37, 0xac, 0xdf, 0x9a, 0xaa, 0x40, 0x94, 0x27, 0xa7, 0x0e, 0xd6, 0xde, 0x95, 0xc5}
};
static DRIVER_OBJECT* gDriver; // vmbus driver object
static DEVICE_OBJECT* gDevice; // vmbus root device
//
// Internal routines
//
static void
VmbusGetChannelInterface(
VMBUS_CHANNEL_INTERFACE *Interface
);
static void
VmbusGetChannelInfo(
DEVICE_OBJECT *DeviceObject,
DEVICE_INFO *DeviceInfo
);
static void
VmbusGetChannelOffers(
void
);
static int
VmbusOnDeviceAdd(
DEVICE_OBJECT *Device,
void *AdditionalInfo
);
static int
VmbusOnDeviceRemove(
DEVICE_OBJECT* dev
);
static void
VmbusOnCleanup(
DRIVER_OBJECT* drv
);
static int
VmbusOnISR(
DRIVER_OBJECT* drv
);
static void
VmbusOnMsgDPC(
DRIVER_OBJECT* drv
);
static void
VmbusOnEventDPC(
DRIVER_OBJECT* drv
);
/*++;
Name:
VmbusInitialize()
Description:
Main entry point
--*/
int
VmbusInitialize(
DRIVER_OBJECT* drv
)
{
VMBUS_DRIVER_OBJECT* driver = (VMBUS_DRIVER_OBJECT*)drv;
int ret=0;
DPRINT_ENTER(VMBUS);
DPRINT_INFO(VMBUS, "+++++++ Build Date=%s %s +++++++", VersionDate, VersionTime);
DPRINT_INFO(VMBUS, "+++++++ Build Description=%s +++++++", VersionDesc);
DPRINT_INFO(VMBUS, "+++++++ Vmbus supported version = %d +++++++", VMBUS_REVISION_NUMBER);
DPRINT_INFO(VMBUS, "+++++++ Vmbus using SINT %d +++++++", VMBUS_MESSAGE_SINT);
DPRINT_DBG(VMBUS, "sizeof(VMBUS_CHANNEL_PACKET_PAGE_BUFFER)=%d, sizeof(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER)=%d",
sizeof(VMBUS_CHANNEL_PACKET_PAGE_BUFFER), sizeof(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER));
drv->name = gDriverName;
memcpy(&drv->deviceType, &gVmbusDeviceType, sizeof(GUID));
// Setup dispatch table
driver->Base.OnDeviceAdd = VmbusOnDeviceAdd;
driver->Base.OnDeviceRemove = VmbusOnDeviceRemove;
driver->Base.OnCleanup = VmbusOnCleanup;
driver->OnIsr = VmbusOnISR;
driver->OnMsgDpc = VmbusOnMsgDPC;
driver->OnEventDpc = VmbusOnEventDPC;
driver->GetChannelOffers = VmbusGetChannelOffers;
driver->GetChannelInterface = VmbusGetChannelInterface;
driver->GetChannelInfo = VmbusGetChannelInfo;
// Hypervisor initialization...setup hypercall page..etc
ret = HvInit();
if (ret != 0)
{
DPRINT_ERR(VMBUS, "Unable to initialize the hypervisor - 0x%x", ret);
}
gDriver = drv;
DPRINT_EXIT(VMBUS);
return ret;
}
/*++;
Name:
VmbusGetChannelOffers()
Description:
Retrieve the channel offers from the parent partition
--*/
static void
VmbusGetChannelOffers(void)
{
DPRINT_ENTER(VMBUS);
VmbusChannelRequestOffers();
DPRINT_EXIT(VMBUS);
}
/*++;
Name:
VmbusGetChannelInterface()
Description:
Get the channel interface
--*/
static void
VmbusGetChannelInterface(
VMBUS_CHANNEL_INTERFACE *Interface
)
{
GetChannelInterface(Interface);
}
/*++;
Name:
VmbusGetChannelInterface()
Description:
Get the device info for the specified device object
--*/
static void
VmbusGetChannelInfo(
DEVICE_OBJECT *DeviceObject,
DEVICE_INFO *DeviceInfo
)
{
GetChannelInfo(DeviceObject, DeviceInfo);
}
/*++
Name:
VmbusCreateChildDevice()
Description:
Creates the child device on the bus that represents the channel offer
--*/
DEVICE_OBJECT*
VmbusChildDeviceCreate(
GUID DeviceType,
GUID DeviceInstance,
void *Context)
{
VMBUS_DRIVER_OBJECT* vmbusDriver = (VMBUS_DRIVER_OBJECT*)gDriver;
return vmbusDriver->OnChildDeviceCreate(
DeviceType,
DeviceInstance,
Context);
}
/*++
Name:
VmbusChildDeviceAdd()
Description:
Registers the child device with the vmbus
--*/
int
VmbusChildDeviceAdd(
DEVICE_OBJECT* ChildDevice)
{
VMBUS_DRIVER_OBJECT* vmbusDriver = (VMBUS_DRIVER_OBJECT*)gDriver;
return vmbusDriver->OnChildDeviceAdd(gDevice, ChildDevice);
}
/*++
Name:
VmbusChildDeviceRemove()
Description:
Unregisters the child device from the vmbus
--*/
void
VmbusChildDeviceRemove(
DEVICE_OBJECT* ChildDevice)
{
VMBUS_DRIVER_OBJECT* vmbusDriver = (VMBUS_DRIVER_OBJECT*)gDriver;
vmbusDriver->OnChildDeviceRemove(ChildDevice);
}
/*++
Name:
VmbusChildDeviceDestroy()
Description:
Release the child device from the vmbus
--*/
//void
//VmbusChildDeviceDestroy(
// DEVICE_OBJECT* ChildDevice
// )
//{
// VMBUS_DRIVER_OBJECT* vmbusDriver = (VMBUS_DRIVER_OBJECT*)gDriver;
//
// vmbusDriver->OnChildDeviceDestroy(ChildDevice);
//}
/*++
Name:
VmbusOnDeviceAdd()
Description:
Callback when the root bus device is added
--*/
static int
VmbusOnDeviceAdd(
DEVICE_OBJECT *dev,
void *AdditionalInfo
)
{
UINT32 *irqvector = (UINT32*) AdditionalInfo;
int ret=0;
DPRINT_ENTER(VMBUS);
gDevice = dev;
memcpy(&gDevice->deviceType, &gVmbusDeviceType, sizeof(GUID));
memcpy(&gDevice->deviceInstance, &gVmbusDeviceId, sizeof(GUID));
//strcpy(dev->name, "vmbus");
// SynIC setup...
ret = HvSynicInit(*irqvector);
// Connect to VMBus in the root partition
ret = VmbusConnect();
//VmbusSendEvent(device->localPortId+1);
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
VmbusOnDeviceRemove()
Description:
Callback when the root bus device is removed
--*/
int VmbusOnDeviceRemove(
DEVICE_OBJECT* dev
)
{
int ret=0;
DPRINT_ENTER(VMBUS);
VmbusChannelReleaseUnattachedChannels();
VmbusDisconnect();
HvSynicCleanup();
DPRINT_EXIT(VMBUS);
return ret;
}
/*++
Name:
VmbusOnCleanup()
Description:
Perform any cleanup when the driver is removed
--*/
void
VmbusOnCleanup(
DRIVER_OBJECT* drv
)
{
//VMBUS_DRIVER_OBJECT* driver = (VMBUS_DRIVER_OBJECT*)drv;
DPRINT_ENTER(VMBUS);
HvCleanup();
DPRINT_EXIT(VMBUS);
}
/*++
Name:
VmbusOnMsgDPC()
Description:
DPC routine to handle messages from the hypervisior
--*/
void
VmbusOnMsgDPC(
DRIVER_OBJECT* drv
)
{
void *page_addr = gHvContext.synICMessagePage[0];
HV_MESSAGE* msg = (HV_MESSAGE*)page_addr + VMBUS_MESSAGE_SINT;
HV_MESSAGE *copied;
while (1)
{
if (msg->Header.MessageType == HvMessageTypeNone) // no msg
{
break;
}
else
{
copied = MemAllocAtomic(sizeof(HV_MESSAGE));
if (copied == NULL)
{
continue;
}
memcpy(copied, msg, sizeof(HV_MESSAGE));
WorkQueueQueueWorkItem(gVmbusConnection.WorkQueue, VmbusOnChannelMessage, (void*)copied);
}
msg->Header.MessageType = HvMessageTypeNone;
// Make sure the write to MessageType (ie set to HvMessageTypeNone) happens
// before we read the MessagePending and EOMing. Otherwise, the EOMing will not deliver
// any more messages since there is no empty slot
MemoryFence();
if (msg->Header.MessageFlags.MessagePending)
{
// This will cause message queue rescan to possibly deliver another msg from the hypervisor
WriteMsr(HV_X64_MSR_EOM, 0);
}
}
}
/*++
Name:
VmbusOnEventDPC()
Description:
DPC routine to handle events from the hypervisior
--*/
void
VmbusOnEventDPC(
DRIVER_OBJECT* drv
)
{
// TODO: Process any events
VmbusOnEvents();
}
/*++
Name:
VmbusOnISR()
Description:
ISR routine
--*/
int
VmbusOnISR(
DRIVER_OBJECT* drv
)
{
//VMBUS_DRIVER_OBJECT* driver = (VMBUS_DRIVER_OBJECT*)drv;
int ret=0;
//struct page* page;
void *page_addr;
HV_MESSAGE* msg;
HV_SYNIC_EVENT_FLAGS* event;
//page = SynICMessagePage[0];
//page_addr = page_address(page);
page_addr = gHvContext.synICMessagePage[0];
msg = (HV_MESSAGE*)page_addr + VMBUS_MESSAGE_SINT;
DPRINT_ENTER(VMBUS);
// Check if there are actual msgs to be process
if (msg->Header.MessageType != HvMessageTypeNone)
{
DPRINT_DBG(VMBUS, "received msg type %d size %d", msg->Header.MessageType, msg->Header.PayloadSize);
ret |= 0x1;
}
// TODO: Check if there are events to be process
page_addr = gHvContext.synICEventPage[0];
event = (HV_SYNIC_EVENT_FLAGS*)page_addr + VMBUS_MESSAGE_SINT;
// Since we are a child, we only need to check bit 0
if (BitTestAndClear(&event->Flags32[0], 0))
{
DPRINT_DBG(VMBUS, "received event %d", event->Flags32[0]);
ret |= 0x2;
}
DPRINT_EXIT(VMBUS);
return ret;
}
// eof
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#ifndef _VMBUS_PRIVATE_H_
#define _VMBUS_PRIVATE_H_
#ifndef INTERNAL
#define INTERNAL static
#endif
#include "Hv.h"
#include "VmbusApi.h"
#include "Channel.h"
#include "ChannelMgmt.h"
#include "ChannelInterface.h"
//#include "ChannelMessages.h"
#include "RingBuffer.h"
//#include "Packet.h"
#include "List.h"
//
// Defines
//
// Maximum channels is determined by the size of the interrupt page which is PAGE_SIZE. 1/2 of PAGE_SIZE is for
// send endpoint interrupt and the other is receive endpoint interrupt
#define MAX_NUM_CHANNELS (PAGE_SIZE >> 1) << 3 // 16348 channels
// The value here must be in multiple of 32
// TODO: Need to make this configurable
#define MAX_NUM_CHANNELS_SUPPORTED 256
//
// Data types
//
typedef enum {
Disconnected,
Connecting,
Connected,
Disconnecting
} VMBUS_CONNECT_STATE;
#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT
typedef struct _VMBUS_CONNECTION {
VMBUS_CONNECT_STATE ConnectState;
UINT32 NextGpadlHandle;
// Represents channel interrupts. Each bit position
// represents a channel.
// When a channel sends an interrupt via VMBUS, it
// finds its bit in the sendInterruptPage, set it and
// calls Hv to generate a port event. The other end
// receives the port event and parse the recvInterruptPage
// to see which bit is set
VOID* InterruptPage;
VOID* SendInterruptPage;
VOID* RecvInterruptPage;
// 2 pages - 1st page for parent->child notification and 2nd is child->parent notification
VOID* MonitorPages;
LIST_ENTRY ChannelMsgList;
HANDLE ChannelMsgLock;
// List of channels
LIST_ENTRY ChannelList;
HANDLE ChannelLock;
HANDLE WorkQueue;
} VMBUS_CONNECTION;
typedef struct _VMBUS_MSGINFO {
// Bookkeeping stuff
LIST_ENTRY MsgListEntry;
// Synchronize the request/response if needed
HANDLE WaitEvent;
// The message itself
unsigned char Msg[0];
} VMBUS_MSGINFO;
//
// Externs
//
extern VMBUS_CONNECTION gVmbusConnection;
//
// General vmbus interface
//
INTERNAL DEVICE_OBJECT*
VmbusChildDeviceCreate(
GUID deviceType,
GUID deviceInstance,
void *context);
INTERNAL int
VmbusChildDeviceAdd(
DEVICE_OBJECT* Device);
INTERNAL void
VmbusChildDeviceRemove(
DEVICE_OBJECT* Device);
//INTERNAL void
//VmbusChildDeviceDestroy(
// DEVICE_OBJECT*);
INTERNAL VMBUS_CHANNEL*
GetChannelFromRelId(
UINT32 relId
);
//
// Connection interface
//
INTERNAL int
VmbusConnect(
VOID
);
INTERNAL int
VmbusDisconnect(
VOID
);
INTERNAL int
VmbusPostMessage(
PVOID buffer,
SIZE_T bufSize
);
INTERNAL int
VmbusSetEvent(
UINT32 childRelId
);
INTERNAL VOID
VmbusOnEvents(
VOID
);
#endif // _VMBUS_PRIVATE_H_
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/highmem.h>
#include <linux/vmalloc.h>
//#include <linux/config.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/kernel.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/time.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/kmap_types.h>
#include <asm/atomic.h>
#include "osd.h"
//
// Data types
//
typedef struct _TIMER {
struct timer_list timer;
PFN_TIMER_CALLBACK callback;
void* context;
}TIMER;
typedef struct _WAITEVENT {
int condition;
wait_queue_head_t event;
} WAITEVENT;
typedef struct _SPINLOCK {
spinlock_t lock;
unsigned long flags;
} SPINLOCK;
typedef struct _WORKQUEUE {
struct workqueue_struct *queue;
} WORKQUEUE;
typedef struct _WORKITEM {
struct work_struct work;
PFN_WORKITEM_CALLBACK callback;
void* context;
} WORKITEM;
//
// Global
//
void LogMsg(const char *fmt, ...)
{
#ifdef KERNEL_2_6_5
char buf[1024];
#endif
va_list args;
va_start(args, fmt);
#ifdef KERNEL_2_6_5
vsnprintf(buf, 1024, fmt, args);
va_end(args);
printk(buf);
#else
vprintk(fmt, args);
va_end(args);
#endif
}
void BitSet(unsigned int* addr, int bit)
{
set_bit(bit, (unsigned long*)addr);
}
int BitTest(unsigned int* addr, int bit)
{
return test_bit(bit, (unsigned long*)addr);
}
void BitClear(unsigned int* addr, int bit)
{
clear_bit(bit, (unsigned long*)addr);
}
int BitTestAndClear(unsigned int* addr, int bit)
{
return test_and_clear_bit(bit, (unsigned long*)addr);
}
int BitTestAndSet(unsigned int* addr, int bit)
{
return test_and_set_bit(bit, (unsigned long*)addr);
}
int InterlockedIncrement(int *val)
{
#ifdef KERNEL_2_6_5
int i;
local_irq_disable();
i = atomic_read((atomic_t*)val);
atomic_set((atomic_t*)val, i+1);
local_irq_enable();
return i+1;
#else
return atomic_inc_return((atomic_t*)val);
#endif
}
int InterlockedDecrement(int *val)
{
#ifdef KERNEL_2_6_5
int i;
local_irq_disable();
i = atomic_read((atomic_t*)val);
atomic_set((atomic_t*)val, i-1);
local_irq_enable();
return i-1;
#else
return atomic_dec_return((atomic_t*)val);
#endif
}
#ifndef atomic_cmpxchg
#define atomic_cmpxchg(v, old, new) ((int)cmpxchg(&((v)->counter), old, new))
#endif
int InterlockedCompareExchange(int *val, int new, int curr)
{
//return ((int)cmpxchg(((atomic_t*)val), curr, new));
return atomic_cmpxchg((atomic_t*)val, curr, new);
}
void Sleep(unsigned long usecs)
{
udelay(usecs);
}
void* VirtualAllocExec(unsigned int size)
{
#ifdef __x86_64__
return __vmalloc(size, GFP_KERNEL, PAGE_KERNEL_EXEC);
#else
return __vmalloc(size, GFP_KERNEL, __pgprot(__PAGE_KERNEL & (~_PAGE_NX)));
#endif
}
void VirtualFree(void* VirtAddr)
{
return vfree(VirtAddr);
}
void* PageAlloc(unsigned int count)
{
void *p;
p = (void *)__get_free_pages(GFP_KERNEL, get_order(count * PAGE_SIZE));
if (p) memset(p, 0, count * PAGE_SIZE);
return p;
//struct page* page = alloc_page(GFP_KERNEL|__GFP_ZERO);
//void *p;
////BUGBUG: We need to use kmap in case we are in HIMEM region
//p = page_address(page);
//if (p) memset(p, 0, PAGE_SIZE);
//return p;
}
void PageFree(void* page, unsigned int count)
{
free_pages((unsigned long)page, get_order(count * PAGE_SIZE));
/*struct page* p = virt_to_page(page);
__free_page(p);*/
}
void* PageMapVirtualAddress(unsigned long Pfn)
{
return kmap_atomic(pfn_to_page(Pfn), KM_IRQ0);
}
void PageUnmapVirtualAddress(void* VirtAddr)
{
kunmap_atomic(VirtAddr, KM_IRQ0);
}
void* MemAlloc(unsigned int size)
{
return kmalloc(size, GFP_KERNEL);
}
void* MemAllocZeroed(unsigned int size)
{
void *p = kmalloc(size, GFP_KERNEL);
if (p) memset(p, 0, size);
return p;
}
void* MemAllocAtomic(unsigned int size)
{
return kmalloc(size, GFP_ATOMIC);
}
void MemFree(void* buf)
{
kfree(buf);
}
void *MemMapIO(unsigned long phys, unsigned long size)
{
#if X2V_LINUX
#ifdef __x86_64__
return (void*)(phys + 0xFFFF83000C000000);
#else // i386
return (void*)(phys + 0xfb000000);
#endif
#else
return (void*)GetVirtualAddress(phys); //return ioremap_nocache(phys, size);
#endif
}
void MemUnmapIO(void *virt)
{
//iounmap(virt);
}
void MemoryFence()
{
mb();
}
void TimerCallback(unsigned long data)
{
TIMER* t = (TIMER*)data;
t->callback(t->context);
}
HANDLE TimerCreate(PFN_TIMER_CALLBACK pfnTimerCB, void* context)
{
TIMER* t = kmalloc(sizeof(TIMER), GFP_KERNEL);
if (!t)
{
return NULL;
}
t->callback = pfnTimerCB;
t->context = context;
init_timer(&t->timer);
t->timer.data = (unsigned long)t;
t->timer.function = TimerCallback;
return t;
}
void TimerStart(HANDLE hTimer, UINT32 expirationInUs)
{
TIMER* t = (TIMER* )hTimer;
t->timer.expires = jiffies + usecs_to_jiffies(expirationInUs);
add_timer(&t->timer);
}
int TimerStop(HANDLE hTimer)
{
TIMER* t = (TIMER* )hTimer;
return del_timer(&t->timer);
}
void TimerClose(HANDLE hTimer)
{
TIMER* t = (TIMER* )hTimer;
del_timer(&t->timer);
kfree(t);
}
SIZE_T GetTickCount(void)
{
return jiffies;
}
signed long long GetTimestamp(void)
{
struct timeval t;
do_gettimeofday(&t);
return timeval_to_ns(&t);
}
HANDLE WaitEventCreate(void)
{
WAITEVENT* wait = kmalloc(sizeof(WAITEVENT), GFP_KERNEL);
if (!wait)
{
return NULL;
}
wait->condition = 0;
init_waitqueue_head(&wait->event);
return wait;
}
void WaitEventClose(HANDLE hWait)
{
WAITEVENT* waitEvent = (WAITEVENT* )hWait;
kfree(waitEvent);
}
void WaitEventSet(HANDLE hWait)
{
WAITEVENT* waitEvent = (WAITEVENT* )hWait;
waitEvent->condition = 1;
wake_up_interruptible(&waitEvent->event);
}
int WaitEventWait(HANDLE hWait)
{
int ret=0;
WAITEVENT* waitEvent = (WAITEVENT* )hWait;
ret= wait_event_interruptible(waitEvent->event,
waitEvent->condition);
waitEvent->condition = 0;
return ret;
}
int WaitEventWaitEx(HANDLE hWait, UINT32 TimeoutInMs)
{
int ret=0;
WAITEVENT* waitEvent = (WAITEVENT* )hWait;
ret= wait_event_interruptible_timeout(waitEvent->event,
waitEvent->condition,
msecs_to_jiffies(TimeoutInMs));
waitEvent->condition = 0;
return ret;
}
HANDLE SpinlockCreate(VOID)
{
SPINLOCK* spin = kmalloc(sizeof(SPINLOCK), GFP_KERNEL);
if (!spin)
{
return NULL;
}
spin_lock_init(&spin->lock);
return spin;
}
VOID SpinlockAcquire(HANDLE hSpin)
{
SPINLOCK* spin = (SPINLOCK* )hSpin;
spin_lock_irqsave(&spin->lock, spin->flags);
}
VOID SpinlockRelease(HANDLE hSpin)
{
SPINLOCK* spin = (SPINLOCK* )hSpin;
spin_unlock_irqrestore(&spin->lock, spin->flags);
}
VOID SpinlockClose(HANDLE hSpin)
{
SPINLOCK* spin = (SPINLOCK* )hSpin;
kfree(spin);
}
void* Physical2LogicalAddr(ULONG_PTR PhysAddr)
{
void* logicalAddr = phys_to_virt(PhysAddr);
BUG_ON(!virt_addr_valid(logicalAddr));
return logicalAddr;
}
ULONG_PTR Logical2PhysicalAddr(PVOID LogicalAddr)
{
BUG_ON(!virt_addr_valid(LogicalAddr));
return virt_to_phys(LogicalAddr);
}
ULONG_PTR Virtual2Physical(PVOID VirtAddr)
{
ULONG_PTR pfn = vmalloc_to_pfn(VirtAddr);
return pfn << PAGE_SHIFT;
}
#ifdef KERNEL_2_6_27
void WorkItemCallback(struct work_struct *work)
#else
void WorkItemCallback(void* work)
#endif
{
WORKITEM* w = (WORKITEM*)work;
w->callback(w->context);
kfree(w);
}
HANDLE WorkQueueCreate(char* name)
{
WORKQUEUE *wq = kmalloc(sizeof(WORKQUEUE), GFP_KERNEL);
if (!wq)
{
return NULL;
}
wq->queue = create_workqueue(name);
return wq;
}
void WorkQueueClose(HANDLE hWorkQueue)
{
WORKQUEUE *wq = (WORKQUEUE *)hWorkQueue;
destroy_workqueue(wq->queue);
return;
}
int WorkQueueQueueWorkItem(HANDLE hWorkQueue, PFN_WORKITEM_CALLBACK workItem, void* context)
{
WORKQUEUE *wq = (WORKQUEUE *)hWorkQueue;
WORKITEM* w = kmalloc(sizeof(WORKITEM), GFP_ATOMIC);
if (!w)
{
return -1;
}
w->callback = workItem,
w->context = context;
#ifdef KERNEL_2_6_27
INIT_WORK(&w->work, WorkItemCallback);
#else
INIT_WORK(&w->work, WorkItemCallback, w);
#endif
return queue_work(wq->queue, &w->work);
}
void QueueWorkItem(PFN_WORKITEM_CALLBACK workItem, void* context)
{
WORKITEM* w = kmalloc(sizeof(WORKITEM), GFP_ATOMIC);
if (!w)
{
return;
}
w->callback = workItem,
w->context = context;
#ifdef KERNEL_2_6_27
INIT_WORK(&w->work, WorkItemCallback);
#else
INIT_WORK(&w->work, WorkItemCallback, w);
#endif
schedule_work(&w->work);
}
/*
*
* Copyright (c) 2009, Microsoft Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
* Authors:
* Haiyang Zhang <haiyangz@microsoft.com>
* Hank Janssen <hjanssen@microsoft.com>
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/sysctl.h>
#include "logging.h"
#include "vmbus.h"
//
// Defines
//
// FIXME! We need to do this dynamically for PIC and APIC system
#define VMBUS_IRQ 0x5
#ifdef KERNEL_2_6_27
#define VMBUS_IRQ_VECTOR IRQ5_VECTOR
#endif
//
// Data types
//
// Main vmbus driver data structure
struct vmbus_driver_context {
// !! These must be the first 2 fields !!
// The driver field is not used in here. Instead, the bus field is
// used to represent the driver
struct driver_context drv_ctx;
VMBUS_DRIVER_OBJECT drv_obj;
struct bus_type bus;
struct tasklet_struct msg_dpc;
struct tasklet_struct event_dpc;
// The bus root device
struct device_context device_ctx;
};
//
// Static decl
//
static int vmbus_match(struct device *device, struct device_driver *driver);
static int vmbus_probe(struct device *device);
static int vmbus_remove(struct device *device);
static void vmbus_shutdown(struct device *device);
#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
#elif defined(KERNEL_2_6_27)
static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env);
#else
static int vmbus_uevent(struct device *device, char **envp, int num_envp, char *buffer, int buffer_size);
#endif
static void vmbus_msg_dpc(unsigned long data);
static void vmbus_event_dpc(unsigned long data);
#ifdef KERNEL_2_6_27
static irqreturn_t vmbus_isr(int irq, void* dev_id);
#else
static int vmbus_isr(int irq, void* dev_id, struct pt_regs *regs);
#endif
static void vmbus_device_release(struct device *device);
static void vmbus_bus_release(struct device *device);
static DEVICE_OBJECT* vmbus_child_device_create(GUID type, GUID instance, void* context);
static void vmbus_child_device_destroy(DEVICE_OBJECT* device_obj);
static int vmbus_child_device_register(DEVICE_OBJECT* root_device_obj, DEVICE_OBJECT* child_device_obj);
static void vmbus_child_device_unregister(DEVICE_OBJECT* child_device_obj);
static void vmbus_child_device_get_info(DEVICE_OBJECT *device_obj, DEVICE_INFO *device_info);
//static ssize_t vmbus_show_class_id(struct device *dev, struct device_attribute *attr, char *buf);
//static ssize_t vmbus_show_device_id(struct device *dev, struct device_attribute *attr, char *buf);
static ssize_t vmbus_show_device_attr(struct device *dev, struct device_attribute *dev_attr, char *buf);
//
// Global
//
// Global logging setting
//unsigned int vmbus_loglevel= (((VMBUS | VMBUS_DRV)<<16) | DEBUG_LVL_ENTEREXIT);
//unsigned int vmbus_loglevel= (ALL_MODULES << 16 | DEBUG_LVL_ENTEREXIT);
unsigned int vmbus_loglevel= (ALL_MODULES << 16 | INFO_LVL);
EXPORT_SYMBOL(vmbus_loglevel);
static int vmbus_irq = VMBUS_IRQ;
// Setup /proc/sys/bus/vmbus/vmbus_loglevel
// Allow usage of sysctl cmd to set the logging level
static struct ctl_table_header *vmbus_ctl_table_hdr;
static ctl_table vmbus_dev_ctl_table[] = {
{ .ctl_name = 8461,
.procname = "vmbus_loglevel",
.data = &vmbus_loglevel,
.maxlen = sizeof(vmbus_loglevel),
.mode = 0644,
.proc_handler = &proc_dointvec },
{ }
};
static ctl_table vmbus_ctl_table[] = {
{ .ctl_name = CTL_DEV,
.procname = "vmbus",
.mode = 0555,
.child = vmbus_dev_ctl_table },
{ }
};
static ctl_table vmus_root_ctl_table[] = {
{ .ctl_name = CTL_BUS,
.procname = "bus",
.mode = 0555,
.child = vmbus_ctl_table },
{ }
};
#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
#else
//
// Set up per device attributes in /sys/bus/vmbus/devices/<bus device>
//
static struct device_attribute vmbus_device_attrs[] = {
__ATTR(id, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(state, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(class_id, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(device_id, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(monitor_id, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(server_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(server_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(server_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(client_monitor_pending, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(client_monitor_latency, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(client_monitor_conn_id, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(out_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(out_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(out_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(out_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(out_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(in_intr_mask, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(in_read_index, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(in_write_index, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(in_read_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR(in_write_bytes_avail, S_IRUGO, vmbus_show_device_attr, NULL),
__ATTR_NULL
};
#endif
// The one and only one
static struct vmbus_driver_context g_vmbus_drv={
.bus.name = "vmbus",
.bus.match = vmbus_match,
#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
#else
.bus.shutdown = vmbus_shutdown,
.bus.remove = vmbus_remove,
.bus.probe = vmbus_probe,
.bus.uevent = vmbus_uevent,
.bus.dev_attrs = vmbus_device_attrs,
#endif
};
//
// Routines
//
/*++
Name: vmbus_show_device_attr()
Desc: Show the device attribute in sysfs. This is invoked when user does a "cat /sys/bus/vmbus/devices/<bus device>/<attr name>"
--*/
static ssize_t vmbus_show_device_attr(struct device *dev, struct device_attribute *dev_attr, char *buf)
{
struct device_context *device_ctx = device_to_device_context(dev);
DEVICE_INFO device_info;
memset(&device_info, 0, sizeof(DEVICE_INFO));
vmbus_child_device_get_info(&device_ctx->device_obj, &device_info);
if (!strcmp(dev_attr->attr.name, "class_id"))
{
return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}\n",
device_info.ChannelType.Data[3], device_info.ChannelType.Data[2], device_info.ChannelType.Data[1], device_info.ChannelType.Data[0],
device_info.ChannelType.Data[5], device_info.ChannelType.Data[4],
device_info.ChannelType.Data[7], device_info.ChannelType.Data[6],
device_info.ChannelType.Data[8], device_info.ChannelType.Data[9], device_info.ChannelType.Data[10], device_info.ChannelType.Data[11], device_info.ChannelType.Data[12], device_info.ChannelType.Data[13], device_info.ChannelType.Data[14], device_info.ChannelType.Data[15]);
}
else if (!strcmp(dev_attr->attr.name, "device_id"))
{
return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}\n",
device_info.ChannelInstance.Data[3], device_info.ChannelInstance.Data[2], device_info.ChannelInstance.Data[1], device_info.ChannelInstance.Data[0],
device_info.ChannelInstance.Data[5], device_info.ChannelInstance.Data[4],
device_info.ChannelInstance.Data[7], device_info.ChannelInstance.Data[6],
device_info.ChannelInstance.Data[8], device_info.ChannelInstance.Data[9], device_info.ChannelInstance.Data[10], device_info.ChannelInstance.Data[11], device_info.ChannelInstance.Data[12], device_info.ChannelInstance.Data[13], device_info.ChannelInstance.Data[14], device_info.ChannelInstance.Data[15]);
}
else if (!strcmp(dev_attr->attr.name, "state"))
{
return sprintf(buf, "%d\n", device_info.ChannelState);
}
else if (!strcmp(dev_attr->attr.name, "id"))
{
return sprintf(buf, "%d\n", device_info.ChannelId);
}
else if (!strcmp(dev_attr->attr.name, "out_intr_mask"))
{
return sprintf(buf, "%d\n", device_info.Outbound.InterruptMask);
}
else if (!strcmp(dev_attr->attr.name, "out_read_index"))
{
return sprintf(buf, "%d\n", device_info.Outbound.ReadIndex);
}
else if (!strcmp(dev_attr->attr.name, "out_write_index"))
{
return sprintf(buf, "%d\n", device_info.Outbound.WriteIndex);
}
else if (!strcmp(dev_attr->attr.name, "out_read_bytes_avail"))
{
return sprintf(buf, "%d\n", device_info.Outbound.BytesAvailToRead);
}
else if (!strcmp(dev_attr->attr.name, "out_write_bytes_avail"))
{
return sprintf(buf, "%d\n", device_info.Outbound.BytesAvailToWrite);
}
else if (!strcmp(dev_attr->attr.name, "in_intr_mask"))
{
return sprintf(buf, "%d\n", device_info.Inbound.InterruptMask);
}
else if (!strcmp(dev_attr->attr.name, "in_read_index"))
{
return sprintf(buf, "%d\n", device_info.Inbound.ReadIndex);
}
else if (!strcmp(dev_attr->attr.name, "in_write_index"))
{
return sprintf(buf, "%d\n", device_info.Inbound.WriteIndex);
}
else if (!strcmp(dev_attr->attr.name, "in_read_bytes_avail"))
{
return sprintf(buf, "%d\n", device_info.Inbound.BytesAvailToRead);
}
else if (!strcmp(dev_attr->attr.name, "in_write_bytes_avail"))
{
return sprintf(buf, "%d\n", device_info.Inbound.BytesAvailToWrite);
}
else if (!strcmp(dev_attr->attr.name, "monitor_id"))
{
return sprintf(buf, "%d\n", device_info.MonitorId);
}
else if (!strcmp(dev_attr->attr.name, "server_monitor_pending"))
{
return sprintf(buf, "%d\n", device_info.ServerMonitorPending);
}
else if (!strcmp(dev_attr->attr.name, "server_monitor_latency"))
{
return sprintf(buf, "%d\n", device_info.ServerMonitorLatency);
}
else if (!strcmp(dev_attr->attr.name, "server_monitor_conn_id"))
{
return sprintf(buf, "%d\n", device_info.ServerMonitorConnectionId);
}
else if (!strcmp(dev_attr->attr.name, "client_monitor_pending"))
{
return sprintf(buf, "%d\n", device_info.ClientMonitorPending);
}
else if (!strcmp(dev_attr->attr.name, "client_monitor_latency"))
{
return sprintf(buf, "%d\n", device_info.ClientMonitorLatency);
}
else if (!strcmp(dev_attr->attr.name, "client_monitor_conn_id"))
{
return sprintf(buf, "%d\n", device_info.ClientMonitorConnectionId);
}
else
{
return 0;
}
}
/*++
Name: vmbus_show_class_id()
Desc: Show the device class id in sysfs
--*/
//static ssize_t vmbus_show_class_id(struct device *dev, struct device_attribute *attr, char *buf)
//{
// struct device_context *device_ctx = device_to_device_context(dev);
// return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}\n",
// device_ctx->class_id[3], device_ctx->class_id[2], device_ctx->class_id[1], device_ctx->class_id[0],
// device_ctx->class_id[5], device_ctx->class_id[4],
// device_ctx->class_id[7], device_ctx->class_id[6],
// device_ctx->class_id[8], device_ctx->class_id[9], device_ctx->class_id[10], device_ctx->class_id[11], device_ctx->class_id[12], device_ctx->class_id[13], device_ctx->class_id[14], device_ctx->class_id[15]);
//}
/*++
Name: vmbus_show_device_id()
Desc: Show the device instance id in sysfs
--*/
//static ssize_t vmbus_show_device_id(struct device *dev, struct device_attribute *attr, char *buf)
//{
// struct device_context *device_ctx = device_to_device_context(dev);
// return sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}\n",
// device_ctx->device_id[3], device_ctx->device_id[2], device_ctx->device_id[1], device_ctx->device_id[0],
// device_ctx->device_id[5], device_ctx->device_id[4],
// device_ctx->device_id[7], device_ctx->device_id[6],
// device_ctx->device_id[8], device_ctx->device_id[9], device_ctx->device_id[10], device_ctx->device_id[11], device_ctx->device_id[12], device_ctx->device_id[13], device_ctx->device_id[14], device_ctx->device_id[15]);
//}
/*++
Name: vmbus_bus_init()
Desc: Main vmbus driver initialization routine. Here, we
- initialize the vmbus driver context
- setup various driver entry points
- invoke the vmbus hv main init routine
- get the irq resource
- invoke the vmbus to add the vmbus root device
- setup the vmbus root device
- retrieve the channel offers
--*/
int vmbus_bus_init(PFN_DRIVERINITIALIZE pfn_drv_init)
{
int ret=0;
unsigned int vector=0;
struct vmbus_driver_context *vmbus_drv_ctx=&g_vmbus_drv;
VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
struct device_context *dev_ctx=&g_vmbus_drv.device_ctx;
DPRINT_ENTER(VMBUS_DRV);
// Set this up to allow lower layer to callback to add/remove child devices on the bus
vmbus_drv_obj->OnChildDeviceCreate = vmbus_child_device_create;
vmbus_drv_obj->OnChildDeviceDestroy = vmbus_child_device_destroy;
vmbus_drv_obj->OnChildDeviceAdd = vmbus_child_device_register;
vmbus_drv_obj->OnChildDeviceRemove = vmbus_child_device_unregister;
// Call to bus driver to initialize
ret = pfn_drv_init(&vmbus_drv_obj->Base);
if (ret != 0)
{
DPRINT_ERR(VMBUS_DRV, "Unable to initialize vmbus (%d)", ret);
goto cleanup;
}
// Sanity checks
if (!vmbus_drv_obj->Base.OnDeviceAdd)
{
DPRINT_ERR(VMBUS_DRV, "OnDeviceAdd() routine not set");
ret = -1;
goto cleanup;
}
vmbus_drv_ctx->bus.name = vmbus_drv_obj->Base.name;
// Initialize the bus context
tasklet_init(&vmbus_drv_ctx->msg_dpc, vmbus_msg_dpc, (unsigned long)vmbus_drv_obj);
tasklet_init(&vmbus_drv_ctx->event_dpc, vmbus_event_dpc, (unsigned long)vmbus_drv_obj);
// Now, register the bus driver with LDM
bus_register(&vmbus_drv_ctx->bus);
// Get the interrupt resource
#ifdef KERNEL_2_6_27
ret = request_irq(vmbus_irq,
vmbus_isr,
IRQF_SAMPLE_RANDOM,
vmbus_drv_obj->Base.name,
NULL);
#else
ret = request_irq(vmbus_irq,
vmbus_isr,
SA_SAMPLE_RANDOM,
vmbus_drv_obj->Base.name,
NULL);
#endif
if (ret != 0)
{
DPRINT_ERR(VMBUS_DRV, "ERROR - Unable to request IRQ %d", vmbus_irq);
bus_unregister(&vmbus_drv_ctx->bus);
ret = -1;
goto cleanup;
}
#ifdef KERNEL_2_6_27
vector = VMBUS_IRQ_VECTOR;
#else
#if X2V_LINUX
vector = vmbus_irq + FIRST_DEVICE_VECTOR - 2;
#else
vector = vmbus_irq + FIRST_EXTERNAL_VECTOR;
#endif
#endif
DPRINT_INFO(VMBUS_DRV, "irq 0x%x vector 0x%x", vmbus_irq, vector);
// Call to bus driver to add the root device
memset(dev_ctx, 0, sizeof(struct device_context));
ret = vmbus_drv_obj->Base.OnDeviceAdd(&dev_ctx->device_obj, &vector);
if (ret != 0)
{
DPRINT_ERR(VMBUS_DRV, "ERROR - Unable to add vmbus root device");
free_irq(vmbus_irq, NULL);
bus_unregister(&vmbus_drv_ctx->bus);
ret = -1;
goto cleanup;
}
//strcpy(dev_ctx->device.bus_id, dev_ctx->device_obj.name);
sprintf(dev_ctx->device.bus_id, "vmbus_0_0");
memcpy(&dev_ctx->class_id, &dev_ctx->device_obj.deviceType, sizeof(GUID));
memcpy(&dev_ctx->device_id, &dev_ctx->device_obj.deviceInstance, sizeof(GUID));
// No need to bind a driver to the root device.
dev_ctx->device.parent = NULL;
dev_ctx->device.bus = &vmbus_drv_ctx->bus; //NULL; // vmbus_remove() does not get invoked
// Setup the device dispatch table
dev_ctx->device.release = vmbus_bus_release;
// Setup the bus as root device
device_register(&dev_ctx->device);
vmbus_drv_obj->GetChannelOffers();
cleanup:
DPRINT_EXIT(VMBUS_DRV);
return ret;
}
/*++
Name: vmbus_bus_exit()
Desc: Terminate the vmbus driver. This routine is opposite of vmbus_bus_init()
--*/
void vmbus_bus_exit(void)
{
VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
struct vmbus_driver_context *vmbus_drv_ctx=&g_vmbus_drv;
struct device_context *dev_ctx=&g_vmbus_drv.device_ctx;
DPRINT_ENTER(VMBUS_DRV);
// Remove the root device
if (vmbus_drv_obj->Base.OnDeviceRemove)
vmbus_drv_obj->Base.OnDeviceRemove(&dev_ctx->device_obj);
if (vmbus_drv_obj->Base.OnCleanup)
vmbus_drv_obj->Base.OnCleanup(&vmbus_drv_obj->Base);
// Unregister the root bus device
device_unregister(&dev_ctx->device);
bus_unregister(&vmbus_drv_ctx->bus);
free_irq(vmbus_irq, NULL);
tasklet_kill(&vmbus_drv_ctx->msg_dpc);
tasklet_kill(&vmbus_drv_ctx->event_dpc);
DPRINT_EXIT(VMBUS_DRV);
return;
}
/*++
Name: vmbus_child_driver_register()
Desc: Register a vmbus's child driver
--*/
void vmbus_child_driver_register(struct driver_context* driver_ctx)
{
VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
DPRINT_ENTER(VMBUS_DRV);
DPRINT_INFO(VMBUS_DRV, "child driver (%p) registering - name %s", driver_ctx, driver_ctx->driver.name);
// The child driver on this vmbus
driver_ctx->driver.bus = &g_vmbus_drv.bus;
driver_register(&driver_ctx->driver);
vmbus_drv_obj->GetChannelOffers();
DPRINT_EXIT(VMBUS_DRV);
}
EXPORT_SYMBOL(vmbus_child_driver_register);
/*++
Name: vmbus_child_driver_unregister()
Desc: Unregister a vmbus's child driver
--*/
void vmbus_child_driver_unregister(struct driver_context* driver_ctx)
{
DPRINT_ENTER(VMBUS_DRV);
DPRINT_INFO(VMBUS_DRV, "child driver (%p) unregistering - name %s", driver_ctx, driver_ctx->driver.name);
driver_unregister(&driver_ctx->driver);
driver_ctx->driver.bus = NULL;
DPRINT_EXIT(VMBUS_DRV);
}
EXPORT_SYMBOL(vmbus_child_driver_unregister);
/*++
Name: vmbus_get_interface()
Desc: Get the vmbus channel interface. This is invoked by child/client driver that sits
above vmbus
--*/
void vmbus_get_interface(VMBUS_CHANNEL_INTERFACE *interface)
{
VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
vmbus_drv_obj->GetChannelInterface(interface);
}
EXPORT_SYMBOL(vmbus_get_interface);
/*++
Name: vmbus_child_device_get_info()
Desc: Get the vmbus child device info. This is invoked to display various device attributes in sysfs.
--*/
static void vmbus_child_device_get_info(DEVICE_OBJECT *device_obj, DEVICE_INFO *device_info)
{
VMBUS_DRIVER_OBJECT *vmbus_drv_obj=&g_vmbus_drv.drv_obj;
vmbus_drv_obj->GetChannelInfo(device_obj, device_info);
}
/*++
Name: vmbus_child_device_create()
Desc: Creates and registers a new child device on the vmbus.
--*/
static DEVICE_OBJECT* vmbus_child_device_create(GUID type, GUID instance, void* context)
{
struct device_context *child_device_ctx;
DEVICE_OBJECT* child_device_obj;
DPRINT_ENTER(VMBUS_DRV);
// Allocate the new child device
child_device_ctx = kzalloc(sizeof(struct device_context), GFP_KERNEL);
if (!child_device_ctx)
{
DPRINT_ERR(VMBUS_DRV, "unable to allocate device_context for child device");
DPRINT_EXIT(VMBUS_DRV);
return NULL;
}
DPRINT_DBG(VMBUS_DRV, "child device (%p) allocated - "
"type {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x},"
"id {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
&child_device_ctx->device,
type.Data[3], type.Data[2], type.Data[1], type.Data[0], type.Data[5], type.Data[4], type.Data[7], type.Data[6], type.Data[8], type.Data[9], type.Data[10], type.Data[11], type.Data[12], type.Data[13], type.Data[14], type.Data[15],
instance.Data[3], instance.Data[2], instance.Data[1], instance.Data[0], instance.Data[5], instance.Data[4], instance.Data[7], instance.Data[6], instance.Data[8], instance.Data[9], instance.Data[10], instance.Data[11], instance.Data[12], instance.Data[13], instance.Data[14], instance.Data[15]);
child_device_obj = &child_device_ctx->device_obj;
child_device_obj->context = context;
memcpy(&child_device_obj->deviceType, &type, sizeof(GUID));
memcpy(&child_device_obj->deviceInstance, &instance, sizeof(GUID));
memcpy(&child_device_ctx->class_id, &type, sizeof(GUID));
memcpy(&child_device_ctx->device_id, &instance, sizeof(GUID));
DPRINT_EXIT(VMBUS_DRV);
return child_device_obj;
}
/*++
Name: vmbus_child_device_register()
Desc: Register the child device on the specified bus
--*/
static int vmbus_child_device_register(DEVICE_OBJECT* root_device_obj, DEVICE_OBJECT* child_device_obj)
{
int ret=0;
struct device_context *root_device_ctx = to_device_context(root_device_obj);
struct device_context *child_device_ctx = to_device_context(child_device_obj);
static int device_num=0;
DPRINT_ENTER(VMBUS_DRV);
DPRINT_DBG(VMBUS_DRV, "child device (%p) registering", child_device_ctx);
//
// Make sure we are not registered already
//
if (child_device_ctx->device.bus_id[0] != '\0')
{
DPRINT_ERR(VMBUS_DRV, "child device (%p) already registered - busid %s", child_device_ctx, child_device_ctx->device.bus_id);
ret = -1;
goto Cleanup;
}
// Set the device bus id. Otherwise, device_register()will fail.
sprintf(child_device_ctx->device.bus_id, "vmbus_0_%d", InterlockedIncrement(&device_num));
// The new device belongs to this bus
child_device_ctx->device.bus = &g_vmbus_drv.bus; //device->dev.bus;
child_device_ctx->device.parent = &root_device_ctx->device;
child_device_ctx->device.release = vmbus_device_release;
// Register with the LDM. This will kick off the driver/device binding...which will
// eventually call vmbus_match() and vmbus_probe()
ret = device_register(&child_device_ctx->device);
// vmbus_probe() error does not get propergate to device_register().
ret = child_device_ctx->probe_error;
if (ret)
DPRINT_ERR(VMBUS_DRV, "unable to register child device (%p) (%d)", &child_device_ctx->device);
else
DPRINT_INFO(VMBUS_DRV, "child device (%p) registered", &child_device_ctx->device);
Cleanup:
DPRINT_EXIT(VMBUS_DRV);
return ret;
}
/*++
Name: vmbus_child_device_unregister()
Desc: Remove the specified child device from the vmbus.
--*/
static void vmbus_child_device_unregister(DEVICE_OBJECT* device_obj)
{
struct device_context *device_ctx = to_device_context(device_obj);
DPRINT_ENTER(VMBUS_DRV);
DPRINT_INFO(VMBUS_DRV, "unregistering child device (%p)", &device_ctx->device);
// Kick off the process of unregistering the device.
// This will call vmbus_remove() and eventually vmbus_device_release()
device_unregister(&device_ctx->device);
DPRINT_INFO(VMBUS_DRV, "child device (%p) unregistered", &device_ctx->device);
DPRINT_EXIT(VMBUS_DRV);
}
/*++
Name: vmbus_child_device_destroy()
Desc: Destroy the specified child device on the vmbus.
--*/
static void vmbus_child_device_destroy(DEVICE_OBJECT* device_obj)
{
DPRINT_ENTER(VMBUS_DRV);
DPRINT_EXIT(VMBUS_DRV);
}
/*++
Name: vmbus_uevent()
Desc: This routine is invoked when a device is added or removed on the vmbus to generate a uevent to udev in the
userspace. The udev will then look at its rule and the uevent generated here to load the appropriate driver
--*/
#if defined(KERNEL_2_6_5) || defined(KERNEL_2_6_9)
#elif defined(KERNEL_2_6_27)
static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env)
{
struct device_context *device_ctx = device_to_device_context(device);
int i=0;
int len=0;
int ret;
DPRINT_ENTER(VMBUS_DRV);
DPRINT_INFO(VMBUS_DRV, "generating uevent - VMBUS_DEVICE_CLASS_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
device_ctx->class_id.Data[3], device_ctx->class_id.Data[2], device_ctx->class_id.Data[1], device_ctx->class_id.Data[0],
device_ctx->class_id.Data[5], device_ctx->class_id.Data[4],
device_ctx->class_id.Data[7], device_ctx->class_id.Data[6],
device_ctx->class_id.Data[8], device_ctx->class_id.Data[9], device_ctx->class_id.Data[10], device_ctx->class_id.Data[11],
device_ctx->class_id.Data[12], device_ctx->class_id.Data[13], device_ctx->class_id.Data[14], device_ctx->class_id.Data[15]);
env->envp_idx = i;
env->buflen = len;
ret = add_uevent_var(env,
"VMBUS_DEVICE_CLASS_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
device_ctx->class_id.Data[3], device_ctx->class_id.Data[2], device_ctx->class_id.Data[1], device_ctx->class_id.Data[0],
device_ctx->class_id.Data[5], device_ctx->class_id.Data[4],
device_ctx->class_id.Data[7], device_ctx->class_id.Data[6],
device_ctx->class_id.Data[8], device_ctx->class_id.Data[9], device_ctx->class_id.Data[10], device_ctx->class_id.Data[11],
device_ctx->class_id.Data[12], device_ctx->class_id.Data[13], device_ctx->class_id.Data[14], device_ctx->class_id.Data[15]);
if (ret)
{
return ret;
}
ret = add_uevent_var(env,
"VMBUS_DEVICE_DEVICE_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
device_ctx->device_id.Data[3], device_ctx->device_id.Data[2], device_ctx->device_id.Data[1], device_ctx->device_id.Data[0],
device_ctx->device_id.Data[5], device_ctx->device_id.Data[4],
device_ctx->device_id.Data[7], device_ctx->device_id.Data[6],
device_ctx->device_id.Data[8], device_ctx->device_id.Data[9], device_ctx->device_id.Data[10], device_ctx->device_id.Data[11],
device_ctx->device_id.Data[12], device_ctx->device_id.Data[13], device_ctx->device_id.Data[14], device_ctx->device_id.Data[15]);
if (ret)
{
return ret;
}
env->envp[env->envp_idx] = NULL;
DPRINT_EXIT(VMBUS_DRV);
return 0;
}
#else
static int vmbus_uevent(struct device *device, char **envp, int num_envp, char *buffer, int buffer_size)
{
struct device_context *device_ctx = device_to_device_context(device);
int i=0;
int len=0;
int ret;
DPRINT_ENTER(VMBUS_DRV);
DPRINT_INFO(VMBUS_DRV, "generating uevent - VMBUS_DEVICE_CLASS_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
device_ctx->class_id.Data[3], device_ctx->class_id.Data[2], device_ctx->class_id.Data[1], device_ctx->class_id.Data[0],
device_ctx->class_id.Data[5], device_ctx->class_id.Data[4],
device_ctx->class_id.Data[7], device_ctx->class_id.Data[6],
device_ctx->class_id.Data[8], device_ctx->class_id.Data[9], device_ctx->class_id.Data[10], device_ctx->class_id.Data[11],
device_ctx->class_id.Data[12], device_ctx->class_id.Data[13], device_ctx->class_id.Data[14], device_ctx->class_id.Data[15]);
ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
"VMBUS_DEVICE_CLASS_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
device_ctx->class_id.Data[3], device_ctx->class_id.Data[2], device_ctx->class_id.Data[1], device_ctx->class_id.Data[0],
device_ctx->class_id.Data[5], device_ctx->class_id.Data[4],
device_ctx->class_id.Data[7], device_ctx->class_id.Data[6],
device_ctx->class_id.Data[8], device_ctx->class_id.Data[9], device_ctx->class_id.Data[10], device_ctx->class_id.Data[11],
device_ctx->class_id.Data[12], device_ctx->class_id.Data[13], device_ctx->class_id.Data[14], device_ctx->class_id.Data[15]);
if (ret)
{
return ret;
}
ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len,
"VMBUS_DEVICE_DEVICE_GUID={%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x%02x%02x}",
device_ctx->device_id.Data[3], device_ctx->device_id.Data[2], device_ctx->device_id.Data[1], device_ctx->device_id.Data[0],
device_ctx->device_id.Data[5], device_ctx->device_id.Data[4],
device_ctx->device_id.Data[7], device_ctx->device_id.Data[6],
device_ctx->device_id.Data[8], device_ctx->device_id.Data[9], device_ctx->device_id.Data[10], device_ctx->device_id.Data[11],
device_ctx->device_id.Data[12], device_ctx->device_id.Data[13], device_ctx->device_id.Data[14], device_ctx->device_id.Data[15]);
if (ret)
{
return ret;
}
envp[i] = NULL;
DPRINT_EXIT(VMBUS_DRV);
return 0;
}
#endif
/*++
Name: vmbus_match()
Desc: Attempt to match the specified device to the specified driver
--*/
static int vmbus_match(struct device *device, struct device_driver *driver)
{
int match=0;
struct driver_context *driver_ctx = driver_to_driver_context(driver);
struct device_context *device_ctx = device_to_device_context(device);
DPRINT_ENTER(VMBUS_DRV);
// We found our driver ?
if (memcmp(&device_ctx->class_id, &driver_ctx->class_id, sizeof(GUID)) == 0)
{
// !! NOTE: The driver_ctx is not a vmbus_drv_ctx. We typecast it here to access the
// DRIVER_OBJECT field
struct vmbus_driver_context *vmbus_drv_ctx = (struct vmbus_driver_context*)driver_ctx;
device_ctx->device_obj.Driver = &vmbus_drv_ctx->drv_obj.Base;
DPRINT_INFO(VMBUS_DRV, "device object (%p) set to driver object (%p)", &device_ctx->device_obj, device_ctx->device_obj.Driver);
match = 1;
}
DPRINT_EXIT(VMBUS_DRV);
return match;
}
/*++
Name: vmbus_probe_failed_cb()
Desc: Callback when a driver probe failed in vmbus_probe(). We need a callback because
we cannot invoked device_unregister() inside vmbus_probe() since vmbus_probe() may be
invoked inside device_register() i.e. we cannot call device_unregister() inside
device_register()
--*/
#ifdef KERNEL_2_6_27
static void vmbus_probe_failed_cb(struct work_struct *context)
#else
static void vmbus_probe_failed_cb(void* context)
#endif
{
struct device_context *device_ctx = (struct device_context*)context;
DPRINT_ENTER(VMBUS_DRV);
// Kick off the process of unregistering the device.
// This will call vmbus_remove() and eventually vmbus_device_release()
device_unregister(&device_ctx->device);
//put_device(&device_ctx->device);
DPRINT_EXIT(VMBUS_DRV);
}
/*++
Name: vmbus_probe()
Desc: Add the new vmbus's child device
--*/
static int vmbus_probe(struct device *child_device)
{
int ret=0;
struct driver_context *driver_ctx = driver_to_driver_context(child_device->driver);
struct device_context *device_ctx = device_to_device_context(child_device);
DPRINT_ENTER(VMBUS_DRV);
// Let the specific open-source driver handles the probe if it can
if (driver_ctx->probe)
{
ret = device_ctx->probe_error = driver_ctx->probe(child_device);
if (ret != 0)
{
DPRINT_ERR(VMBUS_DRV, "probe() failed for device %s (%p) on driver %s (%d)...", child_device->bus_id, child_device, child_device->driver->name, ret);
#ifdef KERNEL_2_6_27
INIT_WORK(&device_ctx->probe_failed_work_item, vmbus_probe_failed_cb);
#else
INIT_WORK(&device_ctx->probe_failed_work_item, vmbus_probe_failed_cb, device_ctx);
#endif
schedule_work(&device_ctx->probe_failed_work_item);
}
}
else
{
DPRINT_ERR(VMBUS_DRV, "probe() method not set for driver - %s", child_device->driver->name);
ret = -1;
}
DPRINT_EXIT(VMBUS_DRV);
return ret;
}
/*++
Name: vmbus_remove()
Desc: Remove a vmbus device
--*/
static int vmbus_remove(struct device *child_device)
{
int ret=0;
struct driver_context *driver_ctx;
DPRINT_ENTER(VMBUS_DRV);
// Special case root bus device
if (child_device->parent == NULL)
{
// No-op since it is statically defined and handle in vmbus_bus_exit()
DPRINT_EXIT(VMBUS_DRV);
return 0;
}
if (child_device->driver)
{
driver_ctx = driver_to_driver_context(child_device->driver);
// Let the specific open-source driver handles the removal if it can
if (driver_ctx->remove)
{
ret = driver_ctx->remove(child_device);
}
else
{
DPRINT_ERR(VMBUS_DRV, "remove() method not set for driver - %s", child_device->driver->name);
ret = -1;
}
}
else
{
}
DPRINT_EXIT(VMBUS_DRV);
return 0;
}
/*++
Name: vmbus_shutdown()
Desc: Shutdown a vmbus device
--*/
static void vmbus_shutdown(struct device *child_device)
{
struct driver_context *driver_ctx;
DPRINT_ENTER(VMBUS_DRV);
// Special case root bus device
if (child_device->parent == NULL)
{
// No-op since it is statically defined and handle in vmbus_bus_exit()
DPRINT_EXIT(VMBUS_DRV);
return;
}
// The device may not be attached yet
if (!child_device->driver)
{
DPRINT_EXIT(VMBUS_DRV);
return;
}
driver_ctx = driver_to_driver_context(child_device->driver);
// Let the specific open-source driver handles the removal if it can
if (driver_ctx->shutdown)
{
driver_ctx->shutdown(child_device);
}
DPRINT_EXIT(VMBUS_DRV);
return;
}
/*++
Name: vmbus_bus_release()
Desc: Final callback release of the vmbus root device
--*/
static void vmbus_bus_release(struct device *device)
{
DPRINT_ENTER(VMBUS_DRV);
DPRINT_EXIT(VMBUS_DRV);
}
/*++
Name: vmbus_device_release()
Desc: Final callback release of the vmbus child device
--*/
static void vmbus_device_release(struct device *device)
{
struct device_context *device_ctx = device_to_device_context(device);
DPRINT_ENTER(VMBUS_DRV);
//vmbus_child_device_destroy(&device_ctx->device_obj);
kfree(device_ctx);
// !!DO NOT REFERENCE device_ctx anymore at this point!!
DPRINT_EXIT(VMBUS_DRV);
return;
}
/*++
Name: vmbus_msg_dpc()
Desc: Tasklet routine to handle hypervisor messages
--*/
static void vmbus_msg_dpc(unsigned long data)
{
VMBUS_DRIVER_OBJECT* vmbus_drv_obj = (VMBUS_DRIVER_OBJECT*)data;
DPRINT_ENTER(VMBUS_DRV);
ASSERT(vmbus_drv_obj->OnMsgDpc != NULL);
// Call to bus driver to handle interrupt
vmbus_drv_obj->OnMsgDpc(&vmbus_drv_obj->Base);
DPRINT_EXIT(VMBUS_DRV);
}
/*++
Name: vmbus_msg_dpc()
Desc: Tasklet routine to handle hypervisor events
--*/
static void vmbus_event_dpc(unsigned long data)
{
VMBUS_DRIVER_OBJECT* vmbus_drv_obj = (VMBUS_DRIVER_OBJECT*)data;
DPRINT_ENTER(VMBUS_DRV);
ASSERT(vmbus_drv_obj->OnEventDpc != NULL);
// Call to bus driver to handle interrupt
vmbus_drv_obj->OnEventDpc(&vmbus_drv_obj->Base);
DPRINT_EXIT(VMBUS_DRV);
}
/*++
Name: vmbus_msg_dpc()
Desc: ISR routine
--*/
#ifdef KERNEL_2_6_27
static irqreturn_t vmbus_isr(int irq, void* dev_id)
#else
static int vmbus_isr(int irq, void* dev_id, struct pt_regs *regs)
#endif
{
int ret=0;
VMBUS_DRIVER_OBJECT* vmbus_driver_obj = &g_vmbus_drv.drv_obj;
DPRINT_ENTER(VMBUS_DRV);
ASSERT(vmbus_driver_obj->OnIsr != NULL);
// Call to bus driver to handle interrupt
ret = vmbus_driver_obj->OnIsr(&vmbus_driver_obj->Base);
// Schedules a dpc if necessary
if (ret > 0)
{
if (test_bit(0, (unsigned long*)&ret))
{
tasklet_schedule(&g_vmbus_drv.msg_dpc);
}
if (test_bit(1, (unsigned long*)&ret))
{
tasklet_schedule(&g_vmbus_drv.event_dpc);
}
DPRINT_EXIT(VMBUS_DRV);
return IRQ_HANDLED;
}
else
{
DPRINT_EXIT(VMBUS_DRV);
return IRQ_NONE;
}
}
MODULE_LICENSE("GPL");
/*++
Name: vmbus_init()
Desc: Main vmbus driver entry routine
--*/
static int __init vmbus_init(void)
{
int ret=0;
DPRINT_ENTER(VMBUS_DRV);
DPRINT_INFO(VMBUS_DRV,
"Vmbus initializing.... current log level 0x%x (%x,%x)",
vmbus_loglevel, HIWORD(vmbus_loglevel), LOWORD(vmbus_loglevel));
#ifdef KERNEL_2_6_27
//Todo: it is used for loglevel, to be ported to new kernel.
#else
vmbus_ctl_table_hdr = register_sysctl_table(vmus_root_ctl_table, 0);
if (!vmbus_ctl_table_hdr)
{
DPRINT_EXIT(VMBUS_DRV);
return -ENOMEM;
}
#endif
ret = vmbus_bus_init(VmbusInitialize);
DPRINT_EXIT(VMBUS_DRV);
return ret;
}
/*++
Name: vmbus_init()
Desc: Main vmbus driver exit routine
--*/
static void __exit vmbus_exit(void)
{
DPRINT_ENTER(VMBUS_DRV);
vmbus_bus_exit();
#ifdef KERNEL_2_6_27
//Todo: it is used for loglevel, to be ported to new kernel.
#else
unregister_sysctl_table(vmbus_ctl_table_hdr);
#endif
DPRINT_EXIT(VMBUS_DRV);
return;
}
#if defined(KERNEL_2_6_5)
#else
module_param(vmbus_irq, int, S_IRUGO);
module_param(vmbus_loglevel, int, S_IRUGO);
#endif
module_init(vmbus_init);
module_exit(vmbus_exit);
// eof
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment