3 * Copyright (c) 2009, Microsoft Corporation.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
16 * Place - Suite 330, Boston, MA 02111-1307 USA.
19 * Haiyang Zhang <haiyangz@microsoft.com>
20 * Hank Janssen <hjanssen@microsoft.com>
25 #include "include/logging.h"
27 #include "VmbusPrivate.h"
34 VMBUS_CONNECTION gVmbusConnection = {
35 .ConnectState = Disconnected,
36 .NextGpadlHandle = 0xE1E10,
46 Sends a connect request on the partition service connection
54 VMBUS_CHANNEL_MSGINFO *msgInfo=NULL;
55 VMBUS_CHANNEL_INITIATE_CONTACT *msg;
59 // Make sure we are not connecting or connected
60 if (gVmbusConnection.ConnectState != Disconnected)
63 // Initialize the vmbus connection
64 gVmbusConnection.ConnectState = Connecting;
65 gVmbusConnection.WorkQueue = WorkQueueCreate("vmbusQ");
67 INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelMsgList);
68 gVmbusConnection.ChannelMsgLock = SpinlockCreate();
70 INITIALIZE_LIST_HEAD(&gVmbusConnection.ChannelList);
71 gVmbusConnection.ChannelLock = SpinlockCreate();
73 // Setup the vmbus event connection for channel interrupt abstraction stuff
74 gVmbusConnection.InterruptPage = PageAlloc(1);
75 if (gVmbusConnection.InterruptPage == NULL)
81 gVmbusConnection.RecvInterruptPage = gVmbusConnection.InterruptPage;
82 gVmbusConnection.SendInterruptPage = (void*)((ULONG_PTR)gVmbusConnection.InterruptPage + (PAGE_SIZE >> 1));
84 // Setup the monitor notification facility. The 1st page for parent->child and the 2nd page for child->parent
85 gVmbusConnection.MonitorPages = PageAlloc(2);
86 if (gVmbusConnection.MonitorPages == NULL)
92 msgInfo = (VMBUS_CHANNEL_MSGINFO*)MemAllocZeroed(sizeof(VMBUS_CHANNEL_MSGINFO) + sizeof(VMBUS_CHANNEL_INITIATE_CONTACT));
99 msgInfo->WaitEvent = WaitEventCreate();
100 msg = (VMBUS_CHANNEL_INITIATE_CONTACT*)msgInfo->Msg;
102 msg->Header.MessageType = ChannelMessageInitiateContact;
103 msg->VMBusVersionRequested = VMBUS_REVISION_NUMBER;
104 msg->InterruptPage = GetPhysicalAddress(gVmbusConnection.InterruptPage);
105 msg->MonitorPage1 = GetPhysicalAddress(gVmbusConnection.MonitorPages);
106 msg->MonitorPage2 = GetPhysicalAddress((void *)((ULONG_PTR)gVmbusConnection.MonitorPages + PAGE_SIZE));
108 // Add to list before we send the request since we may receive the response
109 // before returning from this routine
110 SpinlockAcquire(gVmbusConnection.ChannelMsgLock);
111 INSERT_TAIL_LIST(&gVmbusConnection.ChannelMsgList, &msgInfo->MsgListEntry);
112 SpinlockRelease(gVmbusConnection.ChannelMsgLock);
114 DPRINT_DBG(VMBUS, "Vmbus connection - interrupt pfn %llx, monitor1 pfn %llx,, monitor2 pfn %llx",
115 msg->InterruptPage, msg->MonitorPage1, msg->MonitorPage2);
117 DPRINT_DBG(VMBUS, "Sending channel initiate msg...");
119 ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_INITIATE_CONTACT));
122 REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
126 // Wait for the connection response
127 WaitEventWait(msgInfo->WaitEvent);
129 REMOVE_ENTRY_LIST(&msgInfo->MsgListEntry);
131 // Check if successful
132 if (msgInfo->Response.VersionResponse.VersionSupported)
134 DPRINT_INFO(VMBUS, "Vmbus connected!!");
135 gVmbusConnection.ConnectState = Connected;
140 DPRINT_ERR(VMBUS, "Vmbus connection failed!!...current version (%d) not supported", VMBUS_REVISION_NUMBER);
147 WaitEventClose(msgInfo->WaitEvent);
155 gVmbusConnection.ConnectState = Disconnected;
157 WorkQueueClose(gVmbusConnection.WorkQueue);
158 SpinlockClose(gVmbusConnection.ChannelLock);
159 SpinlockClose(gVmbusConnection.ChannelMsgLock);
161 if (gVmbusConnection.InterruptPage)
163 PageFree(gVmbusConnection.InterruptPage, 1);
164 gVmbusConnection.InterruptPage = NULL;
167 if (gVmbusConnection.MonitorPages)
169 PageFree(gVmbusConnection.MonitorPages, 2);
170 gVmbusConnection.MonitorPages = NULL;
175 if (msgInfo->WaitEvent)
176 WaitEventClose(msgInfo->WaitEvent);
193 Sends a disconnect request on the partition service connection
202 VMBUS_CHANNEL_UNLOAD *msg;
206 // Make sure we are connected
207 if (gVmbusConnection.ConnectState != Connected)
210 msg = MemAllocZeroed(sizeof(VMBUS_CHANNEL_UNLOAD));
212 msg->MessageType = ChannelMessageUnload;
214 ret = VmbusPostMessage(msg, sizeof(VMBUS_CHANNEL_UNLOAD));
221 PageFree(gVmbusConnection.InterruptPage, 1);
223 // TODO: iterate thru the msg list and free up
225 SpinlockClose(gVmbusConnection.ChannelMsgLock);
227 WorkQueueClose(gVmbusConnection.WorkQueue);
229 gVmbusConnection.ConnectState = Disconnected;
231 DPRINT_INFO(VMBUS, "Vmbus disconnected!!");
248 GetChannelFromRelId()
251 Get the channel object given its child relative id (ie channel id)
259 VMBUS_CHANNEL* channel;
260 VMBUS_CHANNEL* foundChannel=NULL;
264 SpinlockAcquire(gVmbusConnection.ChannelLock);
265 ITERATE_LIST_ENTRIES(anchor, curr, &gVmbusConnection.ChannelList)
267 channel = CONTAINING_RECORD(curr, VMBUS_CHANNEL, ListEntry);
269 if (channel->OfferMsg.ChildRelId == relId)
271 foundChannel = channel;
275 SpinlockRelease(gVmbusConnection.ChannelLock);
285 VmbusProcessChannelEvent()
288 Process a channel event notification
292 VmbusProcessChannelEvent(
296 VMBUS_CHANNEL* channel;
297 UINT32 relId = (UINT32)(ULONG_PTR)context;
301 // Find the channel based on this relid and invokes
302 // the channel callback to process the event
303 channel = GetChannelFromRelId(relId);
307 VmbusChannelOnChannelEvent(channel);
308 //WorkQueueQueueWorkItem(channel->dataWorkQueue, VmbusChannelOnChannelEvent, (void*)channel);
312 DPRINT_ERR(VMBUS, "channel not found for relid - %d.", relId);
332 //int maxdword = PAGE_SIZE >> 3; // receive size is 1/2 page and divide that by 4 bytes
333 int maxdword = MAX_NUM_CHANNELS_SUPPORTED >> 5;
336 UINT32* recvInterruptPage = gVmbusConnection.RecvInterruptPage;
337 //VMBUS_CHANNEL_MESSAGE* receiveMsg;
342 if (recvInterruptPage)
344 for (dword = 0; dword < maxdword; dword++)
346 if (recvInterruptPage[dword])
348 for (bit = 0; bit < 32; bit++)
350 if (BitTestAndClear(&recvInterruptPage[dword], bit))
352 relid = (dword << 5) + bit;
354 DPRINT_DBG(VMBUS, "event detected for relid - %d", relid);
356 if (relid == 0) // special case - vmbus channel protocol msg
358 DPRINT_DBG(VMBUS, "invalid relid - %d", relid);
363 //QueueWorkItem(VmbusProcessEvent, (void*)relid);
364 //ret = WorkQueueQueueWorkItem(gVmbusConnection.workQueue, VmbusProcessChannelEvent, (void*)relid);
365 VmbusProcessChannelEvent((void*)(ULONG_PTR)relid);
383 Send a msg on the vmbus's message connection
393 HV_CONNECTION_ID connId;
397 connId.u.Id = VMBUS_MESSAGE_CONNECTION_ID;
413 Send an event notification to the parent
417 VmbusSetEvent(UINT32 childRelId)
423 // Each UINT32 represents 32 channels
424 BitSet((UINT32*)gVmbusConnection.SendInterruptPage + (childRelId >> 5), childRelId & 31);
425 ret = HvSignalEvent();