Staging: add cowloop driver
[safe/jmp/linux-2.6] / drivers / staging / hv / Vmbus.c
1 /*
2  * Copyright (c) 2009, Microsoft Corporation.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms and conditions of the GNU General Public License,
6  * version 2, as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
11  * more details.
12  *
13  * You should have received a copy of the GNU General Public License along with
14  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
15  * Place - Suite 330, Boston, MA 02111-1307 USA.
16  *
17  * Authors:
18  *   Haiyang Zhang <haiyangz@microsoft.com>
19  *   Hank Janssen  <hjanssen@microsoft.com>
20  *
21  */
22 #include <linux/kernel.h>
23 #include <linux/mm.h>
24 #include "osd.h"
25 #include "logging.h"
26 #include "VersionInfo.h"
27 #include "VmbusPrivate.h"
28
29 static const char *gDriverName = "vmbus";
30
31 /*
32  * Windows vmbus does not defined this.
33  * We defined this to be consistent with other devices
34  */
35 /* {c5295816-f63a-4d5f-8d1a-4daf999ca185} */
36 static const struct hv_guid gVmbusDeviceType = {
37         .data = {
38                 0x16, 0x58, 0x29, 0xc5, 0x3a, 0xf6, 0x5f, 0x4d,
39                 0x8d, 0x1a, 0x4d, 0xaf, 0x99, 0x9c, 0xa1, 0x85
40         }
41 };
42
43 /* {ac3760fc-9adf-40aa-9427-a70ed6de95c5} */
44 static const struct hv_guid gVmbusDeviceId = {
45         .data = {
46                 0xfc, 0x60, 0x37, 0xac, 0xdf, 0x9a, 0xaa, 0x40,
47                 0x94, 0x27, 0xa7, 0x0e, 0xd6, 0xde, 0x95, 0xc5
48         }
49 };
50
51 static struct hv_driver *gDriver; /* vmbus driver object */
52 static struct hv_device *gDevice; /* vmbus root device */
53
54 /**
55  * VmbusGetChannelOffers - Retrieve the channel offers from the parent partition
56  */
57 static void VmbusGetChannelOffers(void)
58 {
59         DPRINT_ENTER(VMBUS);
60         VmbusChannelRequestOffers();
61         DPRINT_EXIT(VMBUS);
62 }
63
64 /**
65  * VmbusGetChannelInterface - Get the channel interface
66  */
67 static void VmbusGetChannelInterface(struct vmbus_channel_interface *Interface)
68 {
69         GetChannelInterface(Interface);
70 }
71
72 /**
73  * VmbusGetChannelInfo - Get the device info for the specified device object
74  */
75 static void VmbusGetChannelInfo(struct hv_device *DeviceObject,
76                                 struct hv_device_info *DeviceInfo)
77 {
78         GetChannelInfo(DeviceObject, DeviceInfo);
79 }
80
81 /**
82  * VmbusCreateChildDevice - Creates the child device on the bus that represents the channel offer
83  */
84 struct hv_device *VmbusChildDeviceCreate(struct hv_guid *DeviceType,
85                                          struct hv_guid *DeviceInstance,
86                                          void *Context)
87 {
88         struct vmbus_driver *vmbusDriver = (struct vmbus_driver *)gDriver;
89
90         return vmbusDriver->OnChildDeviceCreate(DeviceType, DeviceInstance,
91                                                 Context);
92 }
93
94 /**
95  * VmbusChildDeviceAdd - Registers the child device with the vmbus
96  */
97 int VmbusChildDeviceAdd(struct hv_device *ChildDevice)
98 {
99         struct vmbus_driver *vmbusDriver = (struct vmbus_driver *)gDriver;
100
101         return vmbusDriver->OnChildDeviceAdd(gDevice, ChildDevice);
102 }
103
104 /**
105  * VmbusChildDeviceRemove Unregisters the child device from the vmbus
106  */
107 void VmbusChildDeviceRemove(struct hv_device *ChildDevice)
108 {
109         struct vmbus_driver *vmbusDriver = (struct vmbus_driver *)gDriver;
110
111         vmbusDriver->OnChildDeviceRemove(ChildDevice);
112 }
113
114 /**
115  * VmbusOnDeviceAdd - Callback when the root bus device is added
116  */
117 static int VmbusOnDeviceAdd(struct hv_device *dev, void *AdditionalInfo)
118 {
119         u32 *irqvector = AdditionalInfo;
120         int ret;
121
122         DPRINT_ENTER(VMBUS);
123
124         gDevice = dev;
125
126         memcpy(&gDevice->deviceType, &gVmbusDeviceType, sizeof(struct hv_guid));
127         memcpy(&gDevice->deviceInstance, &gVmbusDeviceId,
128                sizeof(struct hv_guid));
129
130         /* strcpy(dev->name, "vmbus"); */
131         /* SynIC setup... */
132         ret = HvSynicInit(*irqvector);
133
134         /* Connect to VMBus in the root partition */
135         ret = VmbusConnect();
136
137         /* VmbusSendEvent(device->localPortId+1); */
138         DPRINT_EXIT(VMBUS);
139
140         return ret;
141 }
142
143 /**
144  * VmbusOnDeviceRemove - Callback when the root bus device is removed
145  */
146 static int VmbusOnDeviceRemove(struct hv_device *dev)
147 {
148         int ret = 0;
149
150         DPRINT_ENTER(VMBUS);
151         VmbusChannelReleaseUnattachedChannels();
152         VmbusDisconnect();
153         HvSynicCleanup();
154         DPRINT_EXIT(VMBUS);
155
156         return ret;
157 }
158
159 /**
160  * VmbusOnCleanup - Perform any cleanup when the driver is removed
161  */
162 static void VmbusOnCleanup(struct hv_driver *drv)
163 {
164         /* struct vmbus_driver *driver = (struct vmbus_driver *)drv; */
165
166         DPRINT_ENTER(VMBUS);
167         HvCleanup();
168         DPRINT_EXIT(VMBUS);
169 }
170
171 /**
172  * VmbusOnMsgDPC - DPC routine to handle messages from the hypervisior
173  */
174 static void VmbusOnMsgDPC(struct hv_driver *drv)
175 {
176         void *page_addr = gHvContext.synICMessagePage[0];
177         struct hv_message *msg = (struct hv_message *)page_addr +
178                                   VMBUS_MESSAGE_SINT;
179         struct hv_message *copied;
180
181         while (1) {
182                 if (msg->Header.MessageType == HvMessageTypeNone) {
183                         /* no msg */
184                         break;
185                 } else {
186                         copied = kmalloc(sizeof(*copied), GFP_ATOMIC);
187                         if (copied == NULL)
188                                 continue;
189
190                         memcpy(copied, msg, sizeof(*copied));
191                         osd_schedule_callback(gVmbusConnection.WorkQueue,
192                                               VmbusOnChannelMessage,
193                                               (void *)copied);
194                 }
195
196                 msg->Header.MessageType = HvMessageTypeNone;
197
198                 /*
199                  * Make sure the write to MessageType (ie set to
200                  * HvMessageTypeNone) happens before we read the
201                  * MessagePending and EOMing. Otherwise, the EOMing
202                  * will not deliver any more messages since there is
203                  * no empty slot
204                  */
205                 mb();
206
207                 if (msg->Header.MessageFlags.MessagePending) {
208                         /*
209                          * This will cause message queue rescan to
210                          * possibly deliver another msg from the
211                          * hypervisor
212                          */
213                         wrmsrl(HV_X64_MSR_EOM, 0);
214                 }
215         }
216 }
217
218 /**
219  * VmbusOnEventDPC - DPC routine to handle events from the hypervisior
220  */
221 static void VmbusOnEventDPC(struct hv_driver *drv)
222 {
223         /* TODO: Process any events */
224         VmbusOnEvents();
225 }
226
227 /**
228  * VmbusOnISR - ISR routine
229  */
230 static int VmbusOnISR(struct hv_driver *drv)
231 {
232         int ret = 0;
233         void *page_addr;
234         struct hv_message *msg;
235         union hv_synic_event_flags *event;
236
237         page_addr = gHvContext.synICMessagePage[0];
238         msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
239
240         DPRINT_ENTER(VMBUS);
241
242         /* Check if there are actual msgs to be process */
243         if (msg->Header.MessageType != HvMessageTypeNone) {
244                 DPRINT_DBG(VMBUS, "received msg type %d size %d",
245                                 msg->Header.MessageType,
246                                 msg->Header.PayloadSize);
247                 ret |= 0x1;
248         }
249
250         /* TODO: Check if there are events to be process */
251         page_addr = gHvContext.synICEventPage[0];
252         event = (union hv_synic_event_flags *)page_addr + VMBUS_MESSAGE_SINT;
253
254         /* Since we are a child, we only need to check bit 0 */
255         if (test_and_clear_bit(0, (unsigned long *) &event->Flags32[0])) {
256                 DPRINT_DBG(VMBUS, "received event %d", event->Flags32[0]);
257                 ret |= 0x2;
258         }
259
260         DPRINT_EXIT(VMBUS);
261         return ret;
262 }
263
264 /**
265  * VmbusInitialize - Main entry point
266  */
267 int VmbusInitialize(struct hv_driver *drv)
268 {
269         struct vmbus_driver *driver = (struct vmbus_driver *)drv;
270         int ret;
271
272         DPRINT_ENTER(VMBUS);
273
274         DPRINT_INFO(VMBUS, "+++++++ Build Date=%s %s +++++++",
275                         VersionDate, VersionTime);
276         DPRINT_INFO(VMBUS, "+++++++ Build Description=%s +++++++",
277                         VersionDesc);
278         DPRINT_INFO(VMBUS, "+++++++ Vmbus supported version = %d +++++++",
279                         VMBUS_REVISION_NUMBER);
280         DPRINT_INFO(VMBUS, "+++++++ Vmbus using SINT %d +++++++",
281                         VMBUS_MESSAGE_SINT);
282         DPRINT_DBG(VMBUS, "sizeof(VMBUS_CHANNEL_PACKET_PAGE_BUFFER)=%zd, "
283                         "sizeof(VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER)=%zd",
284                         sizeof(struct VMBUS_CHANNEL_PACKET_PAGE_BUFFER),
285                         sizeof(struct VMBUS_CHANNEL_PACKET_MULITPAGE_BUFFER));
286
287         drv->name = gDriverName;
288         memcpy(&drv->deviceType, &gVmbusDeviceType, sizeof(struct hv_guid));
289
290         /* Setup dispatch table */
291         driver->Base.OnDeviceAdd        = VmbusOnDeviceAdd;
292         driver->Base.OnDeviceRemove     = VmbusOnDeviceRemove;
293         driver->Base.OnCleanup          = VmbusOnCleanup;
294         driver->OnIsr                   = VmbusOnISR;
295         driver->OnMsgDpc                = VmbusOnMsgDPC;
296         driver->OnEventDpc              = VmbusOnEventDPC;
297         driver->GetChannelOffers        = VmbusGetChannelOffers;
298         driver->GetChannelInterface     = VmbusGetChannelInterface;
299         driver->GetChannelInfo          = VmbusGetChannelInfo;
300
301         /* Hypervisor initialization...setup hypercall page..etc */
302         ret = HvInit();
303         if (ret != 0)
304                 DPRINT_ERR(VMBUS, "Unable to initialize the hypervisor - 0x%x",
305                                 ret);
306         gDriver = drv;
307
308         DPRINT_EXIT(VMBUS);
309
310         return ret;
311 }