blob: 3803915622b8b6d58f297581cfdf27f81abcc442 [file] [log] [blame]
/*
*
* 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/kernel.h>
#include "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
)
{
u32 *irqvector = (u32*) 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