1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * VMware VMCI Driver |
4 | * |
5 | * Copyright (C) 2012 VMware, Inc. All rights reserved. |
6 | */ |
7 | |
8 | #include <linux/vmw_vmci_defs.h> |
9 | #include <linux/vmw_vmci_api.h> |
10 | #include <linux/module.h> |
11 | #include <linux/sched.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/bug.h> |
14 | |
15 | #include "vmci_datagram.h" |
16 | #include "vmci_resource.h" |
17 | #include "vmci_context.h" |
18 | #include "vmci_driver.h" |
19 | #include "vmci_event.h" |
20 | #include "vmci_route.h" |
21 | |
22 | /* |
23 | * struct datagram_entry describes the datagram entity. It is used for datagram |
24 | * entities created only on the host. |
25 | */ |
26 | struct datagram_entry { |
27 | struct vmci_resource resource; |
28 | u32 flags; |
29 | bool run_delayed; |
30 | vmci_datagram_recv_cb recv_cb; |
31 | void *client_data; |
32 | u32 priv_flags; |
33 | }; |
34 | |
35 | struct delayed_datagram_info { |
36 | struct datagram_entry *entry; |
37 | struct work_struct work; |
38 | bool in_dg_host_queue; |
39 | /* msg and msg_payload must be together. */ |
40 | struct vmci_datagram msg; |
41 | u8 msg_payload[]; |
42 | }; |
43 | |
44 | /* Number of in-flight host->host datagrams */ |
45 | static atomic_t delayed_dg_host_queue_size = ATOMIC_INIT(0); |
46 | |
47 | /* |
48 | * Create a datagram entry given a handle pointer. |
49 | */ |
50 | static int dg_create_handle(u32 resource_id, |
51 | u32 flags, |
52 | u32 priv_flags, |
53 | vmci_datagram_recv_cb recv_cb, |
54 | void *client_data, struct vmci_handle *out_handle) |
55 | { |
56 | int result; |
57 | u32 context_id; |
58 | struct vmci_handle handle; |
59 | struct datagram_entry *entry; |
60 | |
61 | if ((flags & VMCI_FLAG_WELLKNOWN_DG_HND) != 0) |
62 | return VMCI_ERROR_INVALID_ARGS; |
63 | |
64 | if ((flags & VMCI_FLAG_ANYCID_DG_HND) != 0) { |
65 | context_id = VMCI_INVALID_ID; |
66 | } else { |
67 | context_id = vmci_get_context_id(); |
68 | if (context_id == VMCI_INVALID_ID) |
69 | return VMCI_ERROR_NO_RESOURCES; |
70 | } |
71 | |
72 | handle = vmci_make_handle(context_id, resource_id); |
73 | |
74 | entry = kmalloc(size: sizeof(*entry), GFP_KERNEL); |
75 | if (!entry) { |
76 | pr_warn("Failed allocating memory for datagram entry\n" ); |
77 | return VMCI_ERROR_NO_MEM; |
78 | } |
79 | |
80 | entry->run_delayed = (flags & VMCI_FLAG_DG_DELAYED_CB) ? true : false; |
81 | entry->flags = flags; |
82 | entry->recv_cb = recv_cb; |
83 | entry->client_data = client_data; |
84 | entry->priv_flags = priv_flags; |
85 | |
86 | /* Make datagram resource live. */ |
87 | result = vmci_resource_add(resource: &entry->resource, |
88 | resource_type: VMCI_RESOURCE_TYPE_DATAGRAM, |
89 | handle); |
90 | if (result != VMCI_SUCCESS) { |
91 | pr_warn("Failed to add new resource (handle=0x%x:0x%x), error: %d\n" , |
92 | handle.context, handle.resource, result); |
93 | kfree(objp: entry); |
94 | return result; |
95 | } |
96 | |
97 | *out_handle = vmci_resource_handle(resource: &entry->resource); |
98 | return VMCI_SUCCESS; |
99 | } |
100 | |
101 | /* |
102 | * Internal utility function with the same purpose as |
103 | * vmci_datagram_get_priv_flags that also takes a context_id. |
104 | */ |
105 | static int vmci_datagram_get_priv_flags(u32 context_id, |
106 | struct vmci_handle handle, |
107 | u32 *priv_flags) |
108 | { |
109 | if (context_id == VMCI_INVALID_ID) |
110 | return VMCI_ERROR_INVALID_ARGS; |
111 | |
112 | if (context_id == VMCI_HOST_CONTEXT_ID) { |
113 | struct datagram_entry *src_entry; |
114 | struct vmci_resource *resource; |
115 | |
116 | resource = vmci_resource_by_handle(resource_handle: handle, |
117 | resource_type: VMCI_RESOURCE_TYPE_DATAGRAM); |
118 | if (!resource) |
119 | return VMCI_ERROR_INVALID_ARGS; |
120 | |
121 | src_entry = container_of(resource, struct datagram_entry, |
122 | resource); |
123 | *priv_flags = src_entry->priv_flags; |
124 | vmci_resource_put(resource); |
125 | } else if (context_id == VMCI_HYPERVISOR_CONTEXT_ID) |
126 | *priv_flags = VMCI_MAX_PRIVILEGE_FLAGS; |
127 | else |
128 | *priv_flags = vmci_context_get_priv_flags(context_id); |
129 | |
130 | return VMCI_SUCCESS; |
131 | } |
132 | |
133 | /* |
134 | * Calls the specified callback in a delayed context. |
135 | */ |
136 | static void dg_delayed_dispatch(struct work_struct *work) |
137 | { |
138 | struct delayed_datagram_info *dg_info = |
139 | container_of(work, struct delayed_datagram_info, work); |
140 | |
141 | dg_info->entry->recv_cb(dg_info->entry->client_data, &dg_info->msg); |
142 | |
143 | vmci_resource_put(resource: &dg_info->entry->resource); |
144 | |
145 | if (dg_info->in_dg_host_queue) |
146 | atomic_dec(v: &delayed_dg_host_queue_size); |
147 | |
148 | kfree(objp: dg_info); |
149 | } |
150 | |
151 | /* |
152 | * Dispatch datagram as a host, to the host, or other vm context. This |
153 | * function cannot dispatch to hypervisor context handlers. This should |
154 | * have been handled before we get here by vmci_datagram_dispatch. |
155 | * Returns number of bytes sent on success, error code otherwise. |
156 | */ |
157 | static int dg_dispatch_as_host(u32 context_id, struct vmci_datagram *dg) |
158 | { |
159 | int retval; |
160 | size_t dg_size; |
161 | u32 src_priv_flags; |
162 | |
163 | dg_size = VMCI_DG_SIZE(dg); |
164 | |
165 | /* Host cannot send to the hypervisor. */ |
166 | if (dg->dst.context == VMCI_HYPERVISOR_CONTEXT_ID) |
167 | return VMCI_ERROR_DST_UNREACHABLE; |
168 | |
169 | /* Check that source handle matches sending context. */ |
170 | if (dg->src.context != context_id) { |
171 | pr_devel("Sender context (ID=0x%x) is not owner of src datagram entry (handle=0x%x:0x%x)\n" , |
172 | context_id, dg->src.context, dg->src.resource); |
173 | return VMCI_ERROR_NO_ACCESS; |
174 | } |
175 | |
176 | /* Get hold of privileges of sending endpoint. */ |
177 | retval = vmci_datagram_get_priv_flags(context_id, handle: dg->src, |
178 | priv_flags: &src_priv_flags); |
179 | if (retval != VMCI_SUCCESS) { |
180 | pr_warn("Couldn't get privileges (handle=0x%x:0x%x)\n" , |
181 | dg->src.context, dg->src.resource); |
182 | return retval; |
183 | } |
184 | |
185 | /* Determine if we should route to host or guest destination. */ |
186 | if (dg->dst.context == VMCI_HOST_CONTEXT_ID) { |
187 | /* Route to host datagram entry. */ |
188 | struct datagram_entry *dst_entry; |
189 | struct vmci_resource *resource; |
190 | |
191 | if (dg->src.context == VMCI_HYPERVISOR_CONTEXT_ID && |
192 | dg->dst.resource == VMCI_EVENT_HANDLER) { |
193 | return vmci_event_dispatch(msg: dg); |
194 | } |
195 | |
196 | resource = vmci_resource_by_handle(resource_handle: dg->dst, |
197 | resource_type: VMCI_RESOURCE_TYPE_DATAGRAM); |
198 | if (!resource) { |
199 | pr_devel("Sending to invalid destination (handle=0x%x:0x%x)\n" , |
200 | dg->dst.context, dg->dst.resource); |
201 | return VMCI_ERROR_INVALID_RESOURCE; |
202 | } |
203 | dst_entry = container_of(resource, struct datagram_entry, |
204 | resource); |
205 | if (vmci_deny_interaction(part_one: src_priv_flags, |
206 | part_two: dst_entry->priv_flags)) { |
207 | vmci_resource_put(resource); |
208 | return VMCI_ERROR_NO_ACCESS; |
209 | } |
210 | |
211 | /* |
212 | * If a VMCI datagram destined for the host is also sent by the |
213 | * host, we always run it delayed. This ensures that no locks |
214 | * are held when the datagram callback runs. |
215 | */ |
216 | if (dst_entry->run_delayed || |
217 | dg->src.context == VMCI_HOST_CONTEXT_ID) { |
218 | struct delayed_datagram_info *dg_info; |
219 | |
220 | if (atomic_add_return(i: 1, v: &delayed_dg_host_queue_size) |
221 | == VMCI_MAX_DELAYED_DG_HOST_QUEUE_SIZE) { |
222 | atomic_dec(v: &delayed_dg_host_queue_size); |
223 | vmci_resource_put(resource); |
224 | return VMCI_ERROR_NO_MEM; |
225 | } |
226 | |
227 | dg_info = kmalloc(struct_size(dg_info, msg_payload, dg->payload_size), |
228 | GFP_ATOMIC); |
229 | if (!dg_info) { |
230 | atomic_dec(v: &delayed_dg_host_queue_size); |
231 | vmci_resource_put(resource); |
232 | return VMCI_ERROR_NO_MEM; |
233 | } |
234 | |
235 | dg_info->in_dg_host_queue = true; |
236 | dg_info->entry = dst_entry; |
237 | dg_info->msg = *dg; |
238 | memcpy(&dg_info->msg_payload, dg + 1, dg->payload_size); |
239 | |
240 | INIT_WORK(&dg_info->work, dg_delayed_dispatch); |
241 | schedule_work(work: &dg_info->work); |
242 | retval = VMCI_SUCCESS; |
243 | |
244 | } else { |
245 | retval = dst_entry->recv_cb(dst_entry->client_data, dg); |
246 | vmci_resource_put(resource); |
247 | if (retval < VMCI_SUCCESS) |
248 | return retval; |
249 | } |
250 | } else { |
251 | /* Route to destination VM context. */ |
252 | struct vmci_datagram *new_dg; |
253 | |
254 | if (context_id != dg->dst.context) { |
255 | if (vmci_deny_interaction(part_one: src_priv_flags, |
256 | part_two: vmci_context_get_priv_flags |
257 | (context_id: dg->dst.context))) { |
258 | return VMCI_ERROR_NO_ACCESS; |
259 | } else if (VMCI_CONTEXT_IS_VM(context_id)) { |
260 | /* |
261 | * If the sending context is a VM, it |
262 | * cannot reach another VM. |
263 | */ |
264 | |
265 | pr_devel("Datagram communication between VMs not supported (src=0x%x, dst=0x%x)\n" , |
266 | context_id, dg->dst.context); |
267 | return VMCI_ERROR_DST_UNREACHABLE; |
268 | } |
269 | } |
270 | |
271 | /* We make a copy to enqueue. */ |
272 | new_dg = kmemdup(p: dg, size: dg_size, GFP_KERNEL); |
273 | if (new_dg == NULL) |
274 | return VMCI_ERROR_NO_MEM; |
275 | |
276 | retval = vmci_ctx_enqueue_datagram(cid: dg->dst.context, dg: new_dg); |
277 | if (retval < VMCI_SUCCESS) { |
278 | kfree(objp: new_dg); |
279 | return retval; |
280 | } |
281 | } |
282 | |
283 | /* |
284 | * We currently truncate the size to signed 32 bits. This doesn't |
285 | * matter for this handler as it only support 4Kb messages. |
286 | */ |
287 | return (int)dg_size; |
288 | } |
289 | |
290 | /* |
291 | * Dispatch datagram as a guest, down through the VMX and potentially to |
292 | * the host. |
293 | * Returns number of bytes sent on success, error code otherwise. |
294 | */ |
295 | static int dg_dispatch_as_guest(struct vmci_datagram *dg) |
296 | { |
297 | int retval; |
298 | struct vmci_resource *resource; |
299 | |
300 | resource = vmci_resource_by_handle(resource_handle: dg->src, |
301 | resource_type: VMCI_RESOURCE_TYPE_DATAGRAM); |
302 | if (!resource) |
303 | return VMCI_ERROR_NO_HANDLE; |
304 | |
305 | retval = vmci_send_datagram(dg); |
306 | vmci_resource_put(resource); |
307 | return retval; |
308 | } |
309 | |
310 | /* |
311 | * Dispatch datagram. This will determine the routing for the datagram |
312 | * and dispatch it accordingly. |
313 | * Returns number of bytes sent on success, error code otherwise. |
314 | */ |
315 | int vmci_datagram_dispatch(u32 context_id, |
316 | struct vmci_datagram *dg, bool from_guest) |
317 | { |
318 | int retval; |
319 | enum vmci_route route; |
320 | |
321 | BUILD_BUG_ON(sizeof(struct vmci_datagram) != 24); |
322 | |
323 | if (dg->payload_size > VMCI_MAX_DG_SIZE || |
324 | VMCI_DG_SIZE(dg) > VMCI_MAX_DG_SIZE) { |
325 | pr_devel("Payload (size=%llu bytes) too big to send\n" , |
326 | (unsigned long long)dg->payload_size); |
327 | return VMCI_ERROR_INVALID_ARGS; |
328 | } |
329 | |
330 | retval = vmci_route(src: &dg->src, dst: &dg->dst, from_guest, route: &route); |
331 | if (retval < VMCI_SUCCESS) { |
332 | pr_devel("Failed to route datagram (src=0x%x, dst=0x%x, err=%d)\n" , |
333 | dg->src.context, dg->dst.context, retval); |
334 | return retval; |
335 | } |
336 | |
337 | if (VMCI_ROUTE_AS_HOST == route) { |
338 | if (VMCI_INVALID_ID == context_id) |
339 | context_id = VMCI_HOST_CONTEXT_ID; |
340 | return dg_dispatch_as_host(context_id, dg); |
341 | } |
342 | |
343 | if (VMCI_ROUTE_AS_GUEST == route) |
344 | return dg_dispatch_as_guest(dg); |
345 | |
346 | pr_warn("Unknown route (%d) for datagram\n" , route); |
347 | return VMCI_ERROR_DST_UNREACHABLE; |
348 | } |
349 | |
350 | /* |
351 | * Invoke the handler for the given datagram. This is intended to be |
352 | * called only when acting as a guest and receiving a datagram from the |
353 | * virtual device. |
354 | */ |
355 | int vmci_datagram_invoke_guest_handler(struct vmci_datagram *dg) |
356 | { |
357 | struct vmci_resource *resource; |
358 | struct datagram_entry *dst_entry; |
359 | |
360 | resource = vmci_resource_by_handle(resource_handle: dg->dst, |
361 | resource_type: VMCI_RESOURCE_TYPE_DATAGRAM); |
362 | if (!resource) { |
363 | pr_devel("destination (handle=0x%x:0x%x) doesn't exist\n" , |
364 | dg->dst.context, dg->dst.resource); |
365 | return VMCI_ERROR_NO_HANDLE; |
366 | } |
367 | |
368 | dst_entry = container_of(resource, struct datagram_entry, resource); |
369 | if (dst_entry->run_delayed) { |
370 | struct delayed_datagram_info *dg_info; |
371 | |
372 | dg_info = kmalloc(size: sizeof(*dg_info) + (size_t)dg->payload_size, |
373 | GFP_ATOMIC); |
374 | if (!dg_info) { |
375 | vmci_resource_put(resource); |
376 | return VMCI_ERROR_NO_MEM; |
377 | } |
378 | |
379 | dg_info->in_dg_host_queue = false; |
380 | dg_info->entry = dst_entry; |
381 | dg_info->msg = *dg; |
382 | memcpy(&dg_info->msg_payload, dg + 1, dg->payload_size); |
383 | |
384 | INIT_WORK(&dg_info->work, dg_delayed_dispatch); |
385 | schedule_work(work: &dg_info->work); |
386 | } else { |
387 | dst_entry->recv_cb(dst_entry->client_data, dg); |
388 | vmci_resource_put(resource); |
389 | } |
390 | |
391 | return VMCI_SUCCESS; |
392 | } |
393 | |
394 | /* |
395 | * vmci_datagram_create_handle_priv() - Create host context datagram endpoint |
396 | * @resource_id: The resource ID. |
397 | * @flags: Datagram Flags. |
398 | * @priv_flags: Privilege Flags. |
399 | * @recv_cb: Callback when receiving datagrams. |
400 | * @client_data: Pointer for a datagram_entry struct |
401 | * @out_handle: vmci_handle that is populated as a result of this function. |
402 | * |
403 | * Creates a host context datagram endpoint and returns a handle to it. |
404 | */ |
405 | int vmci_datagram_create_handle_priv(u32 resource_id, |
406 | u32 flags, |
407 | u32 priv_flags, |
408 | vmci_datagram_recv_cb recv_cb, |
409 | void *client_data, |
410 | struct vmci_handle *out_handle) |
411 | { |
412 | if (out_handle == NULL) |
413 | return VMCI_ERROR_INVALID_ARGS; |
414 | |
415 | if (recv_cb == NULL) { |
416 | pr_devel("Client callback needed when creating datagram\n" ); |
417 | return VMCI_ERROR_INVALID_ARGS; |
418 | } |
419 | |
420 | if (priv_flags & ~VMCI_PRIVILEGE_ALL_FLAGS) |
421 | return VMCI_ERROR_INVALID_ARGS; |
422 | |
423 | return dg_create_handle(resource_id, flags, priv_flags, recv_cb, |
424 | client_data, out_handle); |
425 | } |
426 | EXPORT_SYMBOL_GPL(vmci_datagram_create_handle_priv); |
427 | |
428 | /* |
429 | * vmci_datagram_create_handle() - Create host context datagram endpoint |
430 | * @resource_id: Resource ID. |
431 | * @flags: Datagram Flags. |
432 | * @recv_cb: Callback when receiving datagrams. |
433 | * @client_ata: Pointer for a datagram_entry struct |
434 | * @out_handle: vmci_handle that is populated as a result of this function. |
435 | * |
436 | * Creates a host context datagram endpoint and returns a handle to |
437 | * it. Same as vmci_datagram_create_handle_priv without the priviledge |
438 | * flags argument. |
439 | */ |
440 | int vmci_datagram_create_handle(u32 resource_id, |
441 | u32 flags, |
442 | vmci_datagram_recv_cb recv_cb, |
443 | void *client_data, |
444 | struct vmci_handle *out_handle) |
445 | { |
446 | return vmci_datagram_create_handle_priv( |
447 | resource_id, flags, |
448 | VMCI_DEFAULT_PROC_PRIVILEGE_FLAGS, |
449 | recv_cb, client_data, |
450 | out_handle); |
451 | } |
452 | EXPORT_SYMBOL_GPL(vmci_datagram_create_handle); |
453 | |
454 | /* |
455 | * vmci_datagram_destroy_handle() - Destroys datagram handle |
456 | * @handle: vmci_handle to be destroyed and reaped. |
457 | * |
458 | * Use this function to destroy any datagram handles created by |
459 | * vmci_datagram_create_handle{,Priv} functions. |
460 | */ |
461 | int vmci_datagram_destroy_handle(struct vmci_handle handle) |
462 | { |
463 | struct datagram_entry *entry; |
464 | struct vmci_resource *resource; |
465 | |
466 | resource = vmci_resource_by_handle(resource_handle: handle, resource_type: VMCI_RESOURCE_TYPE_DATAGRAM); |
467 | if (!resource) { |
468 | pr_devel("Failed to destroy datagram (handle=0x%x:0x%x)\n" , |
469 | handle.context, handle.resource); |
470 | return VMCI_ERROR_NOT_FOUND; |
471 | } |
472 | |
473 | entry = container_of(resource, struct datagram_entry, resource); |
474 | |
475 | vmci_resource_put(resource: &entry->resource); |
476 | vmci_resource_remove(resource: &entry->resource); |
477 | kfree(objp: entry); |
478 | |
479 | return VMCI_SUCCESS; |
480 | } |
481 | EXPORT_SYMBOL_GPL(vmci_datagram_destroy_handle); |
482 | |
483 | /* |
484 | * vmci_datagram_send() - Send a datagram |
485 | * @msg: The datagram to send. |
486 | * |
487 | * Sends the provided datagram on its merry way. |
488 | */ |
489 | int vmci_datagram_send(struct vmci_datagram *msg) |
490 | { |
491 | if (msg == NULL) |
492 | return VMCI_ERROR_INVALID_ARGS; |
493 | |
494 | return vmci_datagram_dispatch(VMCI_INVALID_ID, dg: msg, from_guest: false); |
495 | } |
496 | EXPORT_SYMBOL_GPL(vmci_datagram_send); |
497 | |