1/*-
2 * Copyright (c) 2009-2012 Microsoft Corp.
3 * Copyright (c) 2012 NetApp Inc.
4 * Copyright (c) 2012 Citrix Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice unmodified, this list of conditions, and the following
12 *    disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: releng/10.3/sys/dev/hyperv/vmbus/hv_connection.c 303984 2016-08-12 04:01:16Z glebius $");
31
32#include <sys/param.h>
33#include <sys/kernel.h>
34#include <sys/malloc.h>
35#include <sys/systm.h>
36#include <sys/lock.h>
37#include <sys/mutex.h>
38#include <machine/bus.h>
39#include <vm/vm.h>
40#include <vm/vm_param.h>
41#include <vm/pmap.h>
42
43#include "hv_vmbus_priv.h"
44
45/*
46 * Globals
47 */
48hv_vmbus_connection hv_vmbus_g_connection =
49	{ .connect_state = HV_DISCONNECTED,
50	  .next_gpadl_handle = 0xE1E10, };
51
52uint32_t hv_vmbus_protocal_version = HV_VMBUS_VERSION_WS2008;
53
54static uint32_t
55hv_vmbus_get_next_version(uint32_t current_ver)
56{
57	switch (current_ver) {
58	case (HV_VMBUS_VERSION_WIN7):
59		return(HV_VMBUS_VERSION_WS2008);
60
61	case (HV_VMBUS_VERSION_WIN8):
62		return(HV_VMBUS_VERSION_WIN7);
63
64	case (HV_VMBUS_VERSION_WIN8_1):
65		return(HV_VMBUS_VERSION_WIN8);
66
67	case (HV_VMBUS_VERSION_WS2008):
68	default:
69		return(HV_VMBUS_VERSION_INVALID);
70	}
71}
72
73/**
74 * Negotiate the highest supported hypervisor version.
75 */
76static int
77hv_vmbus_negotiate_version(hv_vmbus_channel_msg_info *msg_info,
78	uint32_t version)
79{
80	int					ret = 0;
81	hv_vmbus_channel_initiate_contact	*msg;
82
83	sema_init(&msg_info->wait_sema, 0, "Msg Info Sema");
84	msg = (hv_vmbus_channel_initiate_contact*) msg_info->msg;
85
86	msg->header.message_type = HV_CHANNEL_MESSAGE_INITIATED_CONTACT;
87	msg->vmbus_version_requested = version;
88
89	msg->interrupt_page = hv_get_phys_addr(
90		hv_vmbus_g_connection.interrupt_page);
91
92	msg->monitor_page_1 = hv_get_phys_addr(
93		hv_vmbus_g_connection.monitor_pages);
94
95	msg->monitor_page_2 =
96		hv_get_phys_addr(
97			((uint8_t *) hv_vmbus_g_connection.monitor_pages
98			+ PAGE_SIZE));
99
100	/**
101	 * Add to list before we send the request since we may receive the
102	 * response before returning from this routine
103	 */
104	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
105
106	TAILQ_INSERT_TAIL(
107		&hv_vmbus_g_connection.channel_msg_anchor,
108		msg_info,
109		msg_list_entry);
110
111	mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
112
113	ret = hv_vmbus_post_message(
114		msg,
115		sizeof(hv_vmbus_channel_initiate_contact));
116
117	if (ret != 0) {
118		mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
119		TAILQ_REMOVE(
120			&hv_vmbus_g_connection.channel_msg_anchor,
121			msg_info,
122			msg_list_entry);
123		mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
124		return (ret);
125	}
126
127	/**
128	 * Wait for the connection response
129	 */
130	ret = sema_timedwait(&msg_info->wait_sema, 5 * hz); /* KYS 5 seconds */
131
132	mtx_lock(&hv_vmbus_g_connection.channel_msg_lock);
133	TAILQ_REMOVE(
134		&hv_vmbus_g_connection.channel_msg_anchor,
135		msg_info,
136		msg_list_entry);
137	mtx_unlock(&hv_vmbus_g_connection.channel_msg_lock);
138
139	/**
140	 * Check if successful
141	 */
142	if (msg_info->response.version_response.version_supported) {
143		hv_vmbus_g_connection.connect_state = HV_CONNECTED;
144	} else {
145		ret = ECONNREFUSED;
146	}
147
148	return (ret);
149}
150
151/**
152 * Send a connect request on the partition service connection
153 */
154int
155hv_vmbus_connect(void) {
156	int					ret = 0;
157	uint32_t				version;
158	hv_vmbus_channel_msg_info*		msg_info = NULL;
159
160	/**
161	 * Make sure we are not connecting or connected
162	 */
163	if (hv_vmbus_g_connection.connect_state != HV_DISCONNECTED) {
164		return (-1);
165	}
166
167	/**
168	 * Initialize the vmbus connection
169	 */
170	hv_vmbus_g_connection.connect_state = HV_CONNECTING;
171	hv_vmbus_g_connection.work_queue = hv_work_queue_create("vmbusQ");
172	sema_init(&hv_vmbus_g_connection.control_sema, 1, "control_sema");
173
174	TAILQ_INIT(&hv_vmbus_g_connection.channel_msg_anchor);
175	mtx_init(&hv_vmbus_g_connection.channel_msg_lock, "vmbus channel msg",
176		NULL, MTX_DEF);
177
178	TAILQ_INIT(&hv_vmbus_g_connection.channel_anchor);
179	mtx_init(&hv_vmbus_g_connection.channel_lock, "vmbus channel",
180		NULL, MTX_DEF);
181
182	/**
183	 * Setup the vmbus event connection for channel interrupt abstraction
184	 * stuff
185	 */
186	hv_vmbus_g_connection.interrupt_page = contigmalloc(
187					PAGE_SIZE, M_DEVBUF,
188					M_NOWAIT | M_ZERO, 0UL,
189					BUS_SPACE_MAXADDR,
190					PAGE_SIZE, 0);
191	KASSERT(hv_vmbus_g_connection.interrupt_page != NULL,
192	    ("Error VMBUS: malloc failed to allocate Channel"
193		" Request Event message!"));
194	if (hv_vmbus_g_connection.interrupt_page == NULL) {
195	    ret = ENOMEM;
196	    goto cleanup;
197	}
198
199	hv_vmbus_g_connection.recv_interrupt_page =
200		hv_vmbus_g_connection.interrupt_page;
201
202	hv_vmbus_g_connection.send_interrupt_page =
203		((uint8_t *) hv_vmbus_g_connection.interrupt_page +
204		    (PAGE_SIZE >> 1));
205
206	/**
207	 * Set up the monitor notification facility. The 1st page for
208	 * parent->child and the 2nd page for child->parent
209	 */
210	hv_vmbus_g_connection.monitor_pages = contigmalloc(
211		2 * PAGE_SIZE,
212		M_DEVBUF,
213		M_NOWAIT | M_ZERO,
214		0UL,
215		BUS_SPACE_MAXADDR,
216		PAGE_SIZE,
217		0);
218	KASSERT(hv_vmbus_g_connection.monitor_pages != NULL,
219	    ("Error VMBUS: malloc failed to allocate Monitor Pages!"));
220	if (hv_vmbus_g_connection.monitor_pages == NULL) {
221	    ret = ENOMEM;
222	    goto cleanup;
223	}
224
225	msg_info = (hv_vmbus_channel_msg_info*)
226		malloc(sizeof(hv_vmbus_channel_msg_info) +
227			sizeof(hv_vmbus_channel_initiate_contact),
228			M_DEVBUF, M_NOWAIT | M_ZERO);
229	KASSERT(msg_info != NULL,
230	    ("Error VMBUS: malloc failed for Initiate Contact message!"));
231	if (msg_info == NULL) {
232	    ret = ENOMEM;
233	    goto cleanup;
234	}
235
236	hv_vmbus_g_connection.channels = malloc(sizeof(hv_vmbus_channel*) *
237		HV_CHANNEL_MAX_COUNT,
238		M_DEVBUF, M_WAITOK | M_ZERO);
239	/*
240	 * Find the highest vmbus version number we can support.
241	 */
242	version = HV_VMBUS_VERSION_CURRENT;
243
244	do {
245		ret = hv_vmbus_negotiate_version(msg_info, version);
246		if (ret == EWOULDBLOCK) {
247			/*
248			 * We timed out.
249			 */
250			goto cleanup;
251		}
252
253		if (hv_vmbus_g_connection.connect_state == HV_CONNECTED)
254			break;
255
256		version = hv_vmbus_get_next_version(version);
257	} while (version != HV_VMBUS_VERSION_INVALID);
258
259	hv_vmbus_protocal_version = version;
260	if (bootverbose)
261		printf("VMBUS: Protocol Version: %d.%d\n",
262		    version >> 16, version & 0xFFFF);
263
264	sema_destroy(&msg_info->wait_sema);
265	free(msg_info, M_DEVBUF);
266
267	return (0);
268
269	/*
270	 * Cleanup after failure!
271	 */
272	cleanup:
273
274	hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
275
276	hv_work_queue_close(hv_vmbus_g_connection.work_queue);
277	sema_destroy(&hv_vmbus_g_connection.control_sema);
278	mtx_destroy(&hv_vmbus_g_connection.channel_lock);
279	mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
280
281	if (hv_vmbus_g_connection.interrupt_page != NULL) {
282		contigfree(
283			hv_vmbus_g_connection.interrupt_page,
284			PAGE_SIZE,
285			M_DEVBUF);
286		hv_vmbus_g_connection.interrupt_page = NULL;
287	}
288
289	if (hv_vmbus_g_connection.monitor_pages != NULL) {
290		contigfree(
291			hv_vmbus_g_connection.monitor_pages,
292			2 * PAGE_SIZE,
293			M_DEVBUF);
294		hv_vmbus_g_connection.monitor_pages = NULL;
295	}
296
297	if (msg_info) {
298		sema_destroy(&msg_info->wait_sema);
299		free(msg_info, M_DEVBUF);
300	}
301
302	free(hv_vmbus_g_connection.channels, M_DEVBUF);
303	return (ret);
304}
305
306/**
307 * Send a disconnect request on the partition service connection
308 */
309int
310hv_vmbus_disconnect(void) {
311	int			 ret = 0;
312	hv_vmbus_channel_unload* msg;
313
314	msg = malloc(sizeof(hv_vmbus_channel_unload),
315	    M_DEVBUF, M_NOWAIT | M_ZERO);
316	KASSERT(msg != NULL,
317	    ("Error VMBUS: malloc failed to allocate Channel Unload Msg!"));
318	if (msg == NULL)
319	    return (ENOMEM);
320
321	msg->message_type = HV_CHANNEL_MESSAGE_UNLOAD;
322
323	ret = hv_vmbus_post_message(msg, sizeof(hv_vmbus_channel_unload));
324
325
326	contigfree(hv_vmbus_g_connection.interrupt_page, PAGE_SIZE, M_DEVBUF);
327
328	mtx_destroy(&hv_vmbus_g_connection.channel_msg_lock);
329
330	hv_work_queue_close(hv_vmbus_g_connection.work_queue);
331	sema_destroy(&hv_vmbus_g_connection.control_sema);
332
333	free(hv_vmbus_g_connection.channels, M_DEVBUF);
334	hv_vmbus_g_connection.connect_state = HV_DISCONNECTED;
335
336	free(msg, M_DEVBUF);
337
338	return (ret);
339}
340
341/**
342 * Process a channel event notification
343 */
344static void
345VmbusProcessChannelEvent(uint32_t relid)
346{
347	void* arg;
348	uint32_t bytes_to_read;
349	hv_vmbus_channel* channel;
350	boolean_t is_batched_reading;
351
352	/**
353	 * Find the channel based on this relid and invokes
354	 * the channel callback to process the event
355	 */
356
357	channel = hv_vmbus_g_connection.channels[relid];
358
359	if (channel == NULL) {
360		return;
361	}
362	/**
363	 * To deal with the race condition where we might
364	 * receive a packet while the relevant driver is
365	 * being unloaded, dispatch the callback while
366	 * holding the channel lock. The unloading driver
367	 * will acquire the same channel lock to set the
368	 * callback to NULL. This closes the window.
369	 */
370
371	/*
372	 * Disable the lock due to newly added WITNESS check in r277723.
373	 * Will seek other way to avoid race condition.
374	 * -- whu
375	 */
376	// mtx_lock(&channel->inbound_lock);
377	if (channel->on_channel_callback != NULL) {
378		arg = channel->channel_callback_context;
379		is_batched_reading = channel->batched_reading;
380		/*
381		 * Optimize host to guest signaling by ensuring:
382		 * 1. While reading the channel, we disable interrupts from
383		 *    host.
384		 * 2. Ensure that we process all posted messages from the host
385		 *    before returning from this callback.
386		 * 3. Once we return, enable signaling from the host. Once this
387		 *    state is set we check to see if additional packets are
388		 *    available to read. In this case we repeat the process.
389		 */
390		do {
391			if (is_batched_reading)
392				hv_ring_buffer_read_begin(&channel->inbound);
393
394			channel->on_channel_callback(arg);
395
396			if (is_batched_reading)
397				bytes_to_read =
398				    hv_ring_buffer_read_end(&channel->inbound);
399			else
400				bytes_to_read = 0;
401		} while (is_batched_reading && (bytes_to_read != 0));
402	}
403	// mtx_unlock(&channel->inbound_lock);
404}
405
406/**
407 * Handler for events
408 */
409void
410hv_vmbus_on_events(void *arg)
411{
412	int bit;
413	int cpu;
414	int dword;
415	void *page_addr;
416	uint32_t* recv_interrupt_page = NULL;
417	int rel_id;
418	int maxdword;
419	hv_vmbus_synic_event_flags *event;
420	/* int maxdword = PAGE_SIZE >> 3; */
421
422	cpu = (int)(long)arg;
423	KASSERT(cpu <= mp_maxid, ("VMBUS: hv_vmbus_on_events: "
424	    "cpu out of range!"));
425
426	if ((hv_vmbus_protocal_version == HV_VMBUS_VERSION_WS2008) ||
427	    (hv_vmbus_protocal_version == HV_VMBUS_VERSION_WIN7)) {
428		maxdword = HV_MAX_NUM_CHANNELS_SUPPORTED >> 5;
429		/*
430		 * receive size is 1/2 page and divide that by 4 bytes
431		 */
432		recv_interrupt_page =
433		    hv_vmbus_g_connection.recv_interrupt_page;
434	} else {
435		/*
436		 * On Host with Win8 or above, the event page can be
437		 * checked directly to get the id of the channel
438		 * that has the pending interrupt.
439		 */
440		maxdword = HV_EVENT_FLAGS_DWORD_COUNT;
441		page_addr = hv_vmbus_g_context.syn_ic_event_page[cpu];
442		event = (hv_vmbus_synic_event_flags *)
443		    page_addr + HV_VMBUS_MESSAGE_SINT;
444		recv_interrupt_page = event->flags32;
445	}
446
447	/*
448	 * Check events
449	 */
450	if (recv_interrupt_page != NULL) {
451	    for (dword = 0; dword < maxdword; dword++) {
452		if (recv_interrupt_page[dword]) {
453		    for (bit = 0; bit < HV_CHANNEL_DWORD_LEN; bit++) {
454			if (synch_test_and_clear_bit(bit,
455			    (uint32_t *) &recv_interrupt_page[dword])) {
456			    rel_id = (dword << 5) + bit;
457			    if (rel_id == 0) {
458				/*
459				 * Special case -
460				 * vmbus channel protocol msg.
461				 */
462				continue;
463			    } else {
464				VmbusProcessChannelEvent(rel_id);
465
466			    }
467			}
468		    }
469		}
470	    }
471	}
472
473	return;
474}
475
476/**
477 * Send a msg on the vmbus's message connection
478 */
479int hv_vmbus_post_message(void *buffer, size_t bufferLen)
480{
481	hv_vmbus_connection_id connId;
482	sbintime_t time = SBT_1MS;
483	int retries;
484	int ret;
485
486	connId.as_uint32_t = 0;
487	connId.u.id = HV_VMBUS_MESSAGE_CONNECTION_ID;
488
489	/*
490	 * We retry to cope with transient failures caused by host side's
491	 * insufficient resources. 20 times should suffice in practice.
492	 */
493	for (retries = 0; retries < 20; retries++) {
494		ret = hv_vmbus_post_msg_via_msg_ipc(connId, 1, buffer,
495						    bufferLen);
496		if (ret == HV_STATUS_SUCCESS)
497			return (0);
498
499		pause_sbt("pstmsg", time, 0, C_HARDCLOCK);
500		if (time < SBT_1S * 2)
501			time *= 2;
502	}
503
504	KASSERT(ret == HV_STATUS_SUCCESS,
505		("Error VMBUS: Message Post Failed, ret=%d\n", ret));
506
507	return (EAGAIN);
508}
509
510/**
511 * Send an event notification to the parent
512 */
513int
514hv_vmbus_set_event(hv_vmbus_channel *channel) {
515	int ret = 0;
516	uint32_t child_rel_id = channel->offer_msg.child_rel_id;
517
518	/* Each uint32_t represents 32 channels */
519
520	synch_set_bit(child_rel_id & 31,
521		(((uint32_t *)hv_vmbus_g_connection.send_interrupt_page
522			+ (child_rel_id >> 5))));
523	ret = hv_vmbus_signal_event(channel->signal_event_param);
524
525	return (ret);
526}
527