1 /*-
2 * Copyright (c) 2018 VMware, Inc.
3 *
4 * SPDX-License-Identifier: (BSD-2-Clause OR GPL-2.0)
5 */
6
7 /* VMCI initialization. */
8
9 #include <sys/cdefs.h>
10 __FBSDID("$FreeBSD$");
11
12 #include "vmci.h"
13 #include "vmci_doorbell.h"
14 #include "vmci_driver.h"
15 #include "vmci_event.h"
16 #include "vmci_kernel_api.h"
17 #include "vmci_kernel_defs.h"
18 #include "vmci_resource.h"
19
20 #define LGPFX "vmci: "
21 #define VMCI_UTIL_NUM_RESOURCES 1
22
23 static vmci_id ctx_update_sub_id = VMCI_INVALID_ID;
24 static volatile int vm_context_id = VMCI_INVALID_ID;
25
26 /*
27 *------------------------------------------------------------------------------
28 *
29 * vmci_util_cid_update --
30 *
31 * Gets called with the new context id if updated or resumed.
32 *
33 * Results:
34 * Context id.
35 *
36 * Side effects:
37 * None.
38 *
39 *------------------------------------------------------------------------------
40 */
41
42 static void
vmci_util_cid_update(vmci_id sub_id,struct vmci_event_data * event_data,void * client_data)43 vmci_util_cid_update(vmci_id sub_id, struct vmci_event_data *event_data,
44 void *client_data)
45 {
46 struct vmci_event_payload_context *ev_payload;
47
48 ev_payload = vmci_event_data_payload(event_data);
49
50 if (sub_id != ctx_update_sub_id) {
51 VMCI_LOG_DEBUG(LGPFX"Invalid subscriber (ID=0x%x).\n", sub_id);
52 return;
53 }
54 if (event_data == NULL || ev_payload->context_id == VMCI_INVALID_ID) {
55 VMCI_LOG_DEBUG(LGPFX"Invalid event data.\n");
56 return;
57 }
58 VMCI_LOG_INFO(LGPFX"Updating context from (ID=0x%x) to (ID=0x%x) on "
59 "event (type=%d).\n", atomic_load_int(&vm_context_id),
60 ev_payload->context_id, event_data->event);
61 atomic_store_int(&vm_context_id, ev_payload->context_id);
62 }
63
64 /*
65 *------------------------------------------------------------------------------
66 *
67 * vmci_util_init --
68 *
69 * Subscribe to context id update event.
70 *
71 * Results:
72 * None.
73 *
74 * Side effects:
75 * None.
76 *
77 *------------------------------------------------------------------------------
78 */
79
80 void
vmci_util_init(void)81 vmci_util_init(void)
82 {
83
84 /*
85 * We subscribe to the VMCI_EVENT_CTX_ID_UPDATE here so we can update
86 * the internal context id when needed.
87 */
88 if (vmci_event_subscribe(VMCI_EVENT_CTX_ID_UPDATE,
89 vmci_util_cid_update, NULL, &ctx_update_sub_id) < VMCI_SUCCESS) {
90 VMCI_LOG_WARNING(LGPFX"Failed to subscribe to event "
91 "(type=%d).\n", VMCI_EVENT_CTX_ID_UPDATE);
92 }
93 }
94
95 /*
96 *------------------------------------------------------------------------------
97 *
98 * vmci_util_exit --
99 *
100 * Cleanup
101 *
102 * Results:
103 * None.
104 *
105 * Side effects:
106 * None.
107 *
108 *------------------------------------------------------------------------------
109 */
110
111 void
vmci_util_exit(void)112 vmci_util_exit(void)
113 {
114
115 if (vmci_event_unsubscribe(ctx_update_sub_id) < VMCI_SUCCESS)
116 VMCI_LOG_WARNING(LGPFX"Failed to unsubscribe to event "
117 "(type=%d) with subscriber (ID=0x%x).\n",
118 VMCI_EVENT_CTX_ID_UPDATE, ctx_update_sub_id);
119 }
120
121 /*
122 *------------------------------------------------------------------------------
123 *
124 * vmci_util_check_host_capabilities --
125 *
126 * Verify that the host supports the hypercalls we need. If it does not, try
127 * to find fallback hypercalls and use those instead.
128 *
129 * Results:
130 * true if required hypercalls (or fallback hypercalls) are supported by the
131 * host, false otherwise.
132 *
133 * Side effects:
134 * None.
135 *
136 *------------------------------------------------------------------------------
137 */
138
139 static bool
vmci_util_check_host_capabilities(void)140 vmci_util_check_host_capabilities(void)
141 {
142 struct vmci_resources_query_msg *msg;
143 struct vmci_datagram *check_msg;
144 int result;
145 uint32_t msg_size;
146
147 msg_size = sizeof(struct vmci_resources_query_hdr) +
148 VMCI_UTIL_NUM_RESOURCES * sizeof(vmci_resource);
149 check_msg = vmci_alloc_kernel_mem(msg_size, VMCI_MEMORY_NORMAL);
150
151 if (check_msg == NULL) {
152 VMCI_LOG_WARNING(LGPFX"Check host: Insufficient memory.\n");
153 return (false);
154 }
155
156 check_msg->dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
157 VMCI_RESOURCES_QUERY);
158 check_msg->src = VMCI_ANON_SRC_HANDLE;
159 check_msg->payload_size = msg_size - VMCI_DG_HEADERSIZE;
160 msg = (struct vmci_resources_query_msg *)VMCI_DG_PAYLOAD(check_msg);
161
162 msg->num_resources = VMCI_UTIL_NUM_RESOURCES;
163 msg->resources[0] = VMCI_GET_CONTEXT_ID;
164
165 result = vmci_send_datagram(check_msg);
166 vmci_free_kernel_mem(check_msg, msg_size);
167
168 /* We need the vector. There are no fallbacks. */
169 return (result == 0x1);
170 }
171
172 /*
173 *------------------------------------------------------------------------------
174 *
175 * vmci_check_host_capabilities --
176 *
177 * Tell host which guestcalls we support and let each API check that the
178 * host supports the hypercalls it needs. If a hypercall is not supported,
179 * the API can check for a fallback hypercall, or fail the check.
180 *
181 * Results:
182 * true if successful, false otherwise.
183 *
184 * Side effects:
185 * Fallback mechanisms may be enabled in the API and vmmon.
186 *
187 *------------------------------------------------------------------------------
188 */
189
190 bool
vmci_check_host_capabilities(void)191 vmci_check_host_capabilities(void)
192 {
193 bool result;
194
195 result = vmci_event_check_host_capabilities();
196 result &= vmci_datagram_check_host_capabilities();
197 result &= vmci_util_check_host_capabilities();
198
199 if (!result) {
200 /*
201 * If it failed, then make sure this goes to the system event
202 * log.
203 */
204 VMCI_LOG_WARNING(LGPFX"Host capability checked failed.\n");
205 } else
206 VMCI_LOG_DEBUG(LGPFX"Host capability check passed.\n");
207
208 return (result);
209 }
210
211 /*
212 *------------------------------------------------------------------------------
213 *
214 * vmci_read_datagrams_from_port --
215 *
216 * Reads datagrams from the data in port and dispatches them. We always
217 * start reading datagrams into only the first page of the datagram buffer.
218 * If the datagrams don't fit into one page, we use the maximum datagram
219 * buffer size for the remainder of the invocation. This is a simple
220 * heuristic for not penalizing small datagrams.
221 *
222 * This function assumes that it has exclusive access to the data in port
223 * for the duration of the call.
224 *
225 * Results:
226 * No result.
227 *
228 * Side effects:
229 * Datagram handlers may be invoked.
230 *
231 *------------------------------------------------------------------------------
232 */
233
234 void
vmci_read_datagrams_from_port(vmci_io_handle io_handle,vmci_io_port dg_in_port,uint8_t * dg_in_buffer,size_t dg_in_buffer_size)235 vmci_read_datagrams_from_port(vmci_io_handle io_handle, vmci_io_port dg_in_port,
236 uint8_t *dg_in_buffer, size_t dg_in_buffer_size)
237 {
238 struct vmci_datagram *dg;
239 size_t current_dg_in_buffer_size;
240 size_t remaining_bytes;
241
242 current_dg_in_buffer_size = PAGE_SIZE;
243
244 ASSERT(dg_in_buffer_size >= PAGE_SIZE);
245
246 vmci_read_port_bytes(io_handle, dg_in_port, dg_in_buffer,
247 current_dg_in_buffer_size);
248 dg = (struct vmci_datagram *)dg_in_buffer;
249 remaining_bytes = current_dg_in_buffer_size;
250
251 while (dg->dst.resource != VMCI_INVALID_ID ||
252 remaining_bytes > PAGE_SIZE) {
253 size_t dg_in_size;
254
255 /*
256 * When the input buffer spans multiple pages, a datagram can
257 * start on any page boundary in the buffer.
258 */
259
260 if (dg->dst.resource == VMCI_INVALID_ID) {
261 ASSERT(remaining_bytes > PAGE_SIZE);
262 dg = (struct vmci_datagram *)ROUNDUP((uintptr_t)dg + 1,
263 PAGE_SIZE);
264 ASSERT((uint8_t *)dg < dg_in_buffer +
265 current_dg_in_buffer_size);
266 remaining_bytes = (size_t)(dg_in_buffer +
267 current_dg_in_buffer_size - (uint8_t *)dg);
268 continue;
269 }
270
271 dg_in_size = VMCI_DG_SIZE_ALIGNED(dg);
272
273 if (dg_in_size <= dg_in_buffer_size) {
274 int result;
275
276 /*
277 * If the remaining bytes in the datagram buffer doesn't
278 * contain the complete datagram, we first make sure we
279 * have enough room for it and then we read the reminder
280 * of the datagram and possibly any following datagrams.
281 */
282
283 if (dg_in_size > remaining_bytes) {
284
285 if (remaining_bytes !=
286 current_dg_in_buffer_size) {
287
288 /*
289 * We move the partial datagram to the
290 * front and read the reminder of the
291 * datagram and possibly following calls
292 * into the following bytes.
293 */
294
295 memmove(dg_in_buffer, dg_in_buffer +
296 current_dg_in_buffer_size -
297 remaining_bytes,
298 remaining_bytes);
299
300 dg = (struct vmci_datagram *)
301 dg_in_buffer;
302 }
303
304 if (current_dg_in_buffer_size !=
305 dg_in_buffer_size)
306 current_dg_in_buffer_size =
307 dg_in_buffer_size;
308
309 vmci_read_port_bytes(io_handle, dg_in_port,
310 dg_in_buffer + remaining_bytes,
311 current_dg_in_buffer_size -
312 remaining_bytes);
313 }
314
315 /*
316 * We special case event datagrams from the
317 * hypervisor.
318 */
319 if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID &&
320 dg->dst.resource == VMCI_EVENT_HANDLER)
321 result = vmci_event_dispatch(dg);
322 else
323 result =
324 vmci_datagram_invoke_guest_handler(dg);
325 if (result < VMCI_SUCCESS)
326 VMCI_LOG_DEBUG(LGPFX"Datagram with resource"
327 " (ID=0x%x) failed (err=%d).\n",
328 dg->dst.resource, result);
329
330 /* On to the next datagram. */
331 dg = (struct vmci_datagram *)((uint8_t *)dg +
332 dg_in_size);
333 } else {
334 size_t bytes_to_skip;
335
336 /*
337 * Datagram doesn't fit in datagram buffer of maximal
338 * size. We drop it.
339 */
340
341 VMCI_LOG_DEBUG(LGPFX"Failed to receive datagram "
342 "(size=%zu bytes).\n", dg_in_size);
343
344 bytes_to_skip = dg_in_size - remaining_bytes;
345 if (current_dg_in_buffer_size != dg_in_buffer_size)
346 current_dg_in_buffer_size = dg_in_buffer_size;
347 for (;;) {
348 vmci_read_port_bytes(io_handle, dg_in_port,
349 dg_in_buffer, current_dg_in_buffer_size);
350 if (bytes_to_skip <=
351 current_dg_in_buffer_size)
352 break;
353 bytes_to_skip -= current_dg_in_buffer_size;
354 }
355 dg = (struct vmci_datagram *)(dg_in_buffer +
356 bytes_to_skip);
357 }
358
359 remaining_bytes = (size_t) (dg_in_buffer +
360 current_dg_in_buffer_size - (uint8_t *)dg);
361
362 if (remaining_bytes < VMCI_DG_HEADERSIZE) {
363 /* Get the next batch of datagrams. */
364
365 vmci_read_port_bytes(io_handle, dg_in_port,
366 dg_in_buffer, current_dg_in_buffer_size);
367 dg = (struct vmci_datagram *)dg_in_buffer;
368 remaining_bytes = current_dg_in_buffer_size;
369 }
370 }
371 }
372
373 /*
374 *------------------------------------------------------------------------------
375 *
376 * vmci_get_context_id --
377 *
378 * Returns the current context ID. Note that since this is accessed only
379 * from code running in the host, this always returns the host context ID.
380 *
381 * Results:
382 * Context ID.
383 *
384 * Side effects:
385 * None.
386 *
387 *------------------------------------------------------------------------------
388 */
389
390 vmci_id
vmci_get_context_id(void)391 vmci_get_context_id(void)
392 {
393 if (atomic_load_int(&vm_context_id) == VMCI_INVALID_ID) {
394 uint32_t result;
395 struct vmci_datagram get_cid_msg;
396 get_cid_msg.dst = VMCI_MAKE_HANDLE(VMCI_HYPERVISOR_CONTEXT_ID,
397 VMCI_GET_CONTEXT_ID);
398 get_cid_msg.src = VMCI_ANON_SRC_HANDLE;
399 get_cid_msg.payload_size = 0;
400 result = vmci_send_datagram(&get_cid_msg);
401 atomic_store_int(&vm_context_id, result);
402 }
403 return (atomic_load_int(&vm_context_id));
404 }
405
406 /*
407 *------------------------------------------------------------------------------
408 *
409 * vmci_components_init --
410 *
411 * Initializes VMCI components and registers core hypercalls.
412 *
413 * Results:
414 * VMCI_SUCCESS if successful, appropriate error code otherwise.
415 *
416 * Side effects:
417 * None.
418 *
419 *------------------------------------------------------------------------------
420 */
421
422 int
vmci_components_init(void)423 vmci_components_init(void)
424 {
425 int result;
426
427 result = vmci_resource_init();
428 if (result < VMCI_SUCCESS) {
429 VMCI_LOG_WARNING(LGPFX"Failed to initialize vmci_resource "
430 "(result=%d).\n", result);
431 goto error_exit;
432 }
433
434 result = vmci_event_init();
435 if (result < VMCI_SUCCESS) {
436 VMCI_LOG_WARNING(LGPFX"Failed to initialize vmci_event "
437 "(result=%d).\n", result);
438 goto resource_exit;
439 }
440
441 result = vmci_doorbell_init();
442 if (result < VMCI_SUCCESS) {
443 VMCI_LOG_WARNING(LGPFX"Failed to initialize vmci_doorbell "
444 "(result=%d).\n", result);
445 goto event_exit;
446 }
447
448 VMCI_LOG_DEBUG(LGPFX"components initialized.\n");
449 return (VMCI_SUCCESS);
450
451 event_exit:
452 vmci_event_exit();
453
454 resource_exit:
455 vmci_resource_exit();
456
457 error_exit:
458 return (result);
459 }
460
461 /*
462 *------------------------------------------------------------------------------
463 *
464 * vmci_components_cleanup --
465 *
466 * Cleans up VMCI components.
467 *
468 * Results:
469 * None.
470 *
471 * Side effects:
472 * None.
473 *
474 *------------------------------------------------------------------------------
475 */
476
477 void
vmci_components_cleanup(void)478 vmci_components_cleanup(void)
479 {
480
481 vmci_doorbell_exit();
482 vmci_event_exit();
483 vmci_resource_exit();
484 }
485