• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4  */
5 
6 #include <linux/eventfd.h>
7 #include <linux/device/driver.h>
8 #include <linux/file.h>
9 #include <linux/fs.h>
10 #include <linux/gunyah.h>
11 #include <linux/module.h>
12 #include <linux/printk.h>
13 
14 #include <uapi/linux/gunyah.h>
15 
16 struct gunyah_ioeventfd {
17 	struct gunyah_vm_function_instance *f;
18 	struct gunyah_vm_io_handler io_handler;
19 
20 	struct eventfd_ctx *ctx;
21 };
22 
gunyah_write_ioeventfd(struct gunyah_vm_io_handler * io_dev,u64 addr,u32 len,u64 data)23 static int gunyah_write_ioeventfd(struct gunyah_vm_io_handler *io_dev, u64 addr,
24 				  u32 len, u64 data)
25 {
26 	struct gunyah_ioeventfd *iofd =
27 		container_of(io_dev, struct gunyah_ioeventfd, io_handler);
28 
29 	eventfd_signal(iofd->ctx, 1);
30 	return 0;
31 }
32 
33 static struct gunyah_vm_io_handler_ops io_ops = {
34 	.write = gunyah_write_ioeventfd,
35 };
36 
gunyah_ioeventfd_bind(struct gunyah_vm_function_instance * f)37 static long gunyah_ioeventfd_bind(struct gunyah_vm_function_instance *f)
38 {
39 	const struct gunyah_fn_ioeventfd_arg *args = f->argp;
40 	struct gunyah_ioeventfd *iofd;
41 	struct eventfd_ctx *ctx;
42 	int ret;
43 
44 	if (f->arg_size != sizeof(*args))
45 		return -EINVAL;
46 
47 	/* All other flag bits are reserved for future use */
48 	if (args->flags & ~GUNYAH_IOEVENTFD_FLAGS_DATAMATCH)
49 		return -EINVAL;
50 
51 	/* must be natural-word sized, or 0 to ignore length */
52 	switch (args->len) {
53 	case 0:
54 	case 1:
55 	case 2:
56 	case 4:
57 	case 8:
58 		break;
59 	default:
60 		return -EINVAL;
61 	}
62 
63 	/* check for range overflow */
64 	if (overflows_type(args->addr + args->len, u64))
65 		return -EINVAL;
66 
67 	/* ioeventfd with no length can't be combined with DATAMATCH */
68 	if (!args->len && (args->flags & GUNYAH_IOEVENTFD_FLAGS_DATAMATCH))
69 		return -EINVAL;
70 
71 	ctx = eventfd_ctx_fdget(args->fd);
72 	if (IS_ERR(ctx))
73 		return PTR_ERR(ctx);
74 
75 	iofd = kzalloc(sizeof(*iofd), GFP_KERNEL);
76 	if (!iofd) {
77 		ret = -ENOMEM;
78 		goto err_eventfd;
79 	}
80 
81 	f->data = iofd;
82 	iofd->f = f;
83 
84 	iofd->ctx = ctx;
85 
86 	if (args->flags & GUNYAH_IOEVENTFD_FLAGS_DATAMATCH) {
87 		iofd->io_handler.datamatch = true;
88 		iofd->io_handler.len = args->len;
89 		iofd->io_handler.data = args->datamatch;
90 	}
91 	iofd->io_handler.addr = args->addr;
92 	iofd->io_handler.ops = &io_ops;
93 
94 	ret = gunyah_vm_add_io_handler(f->ghvm, &iofd->io_handler);
95 	if (ret)
96 		goto err_io_dev_add;
97 
98 	return 0;
99 
100 err_io_dev_add:
101 	kfree(iofd);
102 err_eventfd:
103 	eventfd_ctx_put(ctx);
104 	return ret;
105 }
106 
gunyah_ioevent_unbind(struct gunyah_vm_function_instance * f)107 static void gunyah_ioevent_unbind(struct gunyah_vm_function_instance *f)
108 {
109 	struct gunyah_ioeventfd *iofd = f->data;
110 
111 	gunyah_vm_remove_io_handler(iofd->f->ghvm, &iofd->io_handler);
112 	eventfd_ctx_put(iofd->ctx);
113 	kfree(iofd);
114 }
115 
gunyah_ioevent_compare(const struct gunyah_vm_function_instance * f,const void * arg,size_t size)116 static bool gunyah_ioevent_compare(const struct gunyah_vm_function_instance *f,
117 				   const void *arg, size_t size)
118 {
119 	const struct gunyah_fn_ioeventfd_arg *instance = f->argp, *other = arg;
120 
121 	if (sizeof(*other) != size)
122 		return false;
123 
124 	if (instance->addr != other->addr || instance->len != other->len ||
125 	    instance->flags != other->flags)
126 		return false;
127 
128 	if ((instance->flags & GUNYAH_IOEVENTFD_FLAGS_DATAMATCH) &&
129 	    instance->datamatch != other->datamatch)
130 		return false;
131 
132 	return true;
133 }
134 
135 DECLARE_GUNYAH_VM_FUNCTION_INIT(ioeventfd, GUNYAH_FN_IOEVENTFD, 3,
136 				gunyah_ioeventfd_bind, gunyah_ioevent_unbind,
137 				gunyah_ioevent_compare);
138 MODULE_DESCRIPTION("Gunyah ioeventfd VM Function");
139 MODULE_LICENSE("GPL");
140