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: Hank Janssen <hjanssen@microsoft.com>
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Hank Janssen 2009-07-13 16:02:34 -07:00 committed by Greg Kroah-Hartman
parent ab05778195
commit 3e7ee4902f
17 changed files with 7108 additions and 0 deletions

1199
drivers/staging/hv/Channel.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,157 @@
/*
*
* 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_

View file

@ -0,0 +1,222 @@
/*
*
* 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;
}
}

View file

@ -0,0 +1,41 @@
/*
*
* 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_

View file

@ -0,0 +1,826 @@
/*
*
* 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

View file

@ -0,0 +1,156 @@
/*
*
* 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_

View file

@ -0,0 +1,432 @@
/*
*
* 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

672
drivers/staging/hv/Hv.c Normal file
View file

@ -0,0 +1,672 @@
/*
*
* 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

184
drivers/staging/hv/Hv.h Normal file
View file

@ -0,0 +1,184 @@
/*
*
* 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__

View file

@ -0,0 +1,630 @@
/*
*
* 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

View file

@ -0,0 +1,123 @@
/*
*
* 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_

View file

@ -0,0 +1,31 @@
/*
*
* 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"

View file

@ -0,0 +1,29 @@
/*
*
* 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";

508
drivers/staging/hv/Vmbus.c Normal file
View file

@ -0,0 +1,508 @@
/*
*
* 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

View file

@ -0,0 +1,170 @@
/*
*
* 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_

500
drivers/staging/hv/osd.c Normal file
View file

@ -0,0 +1,500 @@
/*
*
* 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);
}

File diff suppressed because it is too large Load diff