xref: /linux/drivers/fwctl/main.c (revision 0e79a47fb197b6937709a2af2a138c526a9bc374)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
4  */
5 #define pr_fmt(fmt) "fwctl: " fmt
6 #include <linux/fwctl.h>
7 
8 #include <linux/container_of.h>
9 #include <linux/fs.h>
10 #include <linux/module.h>
11 #include <linux/slab.h>
12 
13 #include <uapi/fwctl/fwctl.h>
14 
15 enum {
16 	FWCTL_MAX_DEVICES = 4096,
17 };
18 static_assert(FWCTL_MAX_DEVICES < (1U << MINORBITS));
19 
20 static dev_t fwctl_dev;
21 static DEFINE_IDA(fwctl_ida);
22 
23 struct fwctl_ucmd {
24 	struct fwctl_uctx *uctx;
25 	void __user *ubuffer;
26 	void *cmd;
27 	u32 user_size;
28 };
29 
30 /* On stack memory for the ioctl structs */
31 union fwctl_ucmd_buffer {
32 };
33 
34 struct fwctl_ioctl_op {
35 	unsigned int size;
36 	unsigned int min_size;
37 	unsigned int ioctl_num;
38 	int (*execute)(struct fwctl_ucmd *ucmd);
39 };
40 
41 #define IOCTL_OP(_ioctl, _fn, _struct, _last)                               \
42 	[_IOC_NR(_ioctl) - FWCTL_CMD_BASE] = {                              \
43 		.size = sizeof(_struct) +                                   \
44 			BUILD_BUG_ON_ZERO(sizeof(union fwctl_ucmd_buffer) < \
45 					  sizeof(_struct)),                 \
46 		.min_size = offsetofend(_struct, _last),                    \
47 		.ioctl_num = _ioctl,                                        \
48 		.execute = _fn,                                             \
49 	}
50 static const struct fwctl_ioctl_op fwctl_ioctl_ops[] = {
51 };
52 
53 static long fwctl_fops_ioctl(struct file *filp, unsigned int cmd,
54 			       unsigned long arg)
55 {
56 	struct fwctl_uctx *uctx = filp->private_data;
57 	const struct fwctl_ioctl_op *op;
58 	struct fwctl_ucmd ucmd = {};
59 	union fwctl_ucmd_buffer buf;
60 	unsigned int nr;
61 	int ret;
62 
63 	nr = _IOC_NR(cmd);
64 	if ((nr - FWCTL_CMD_BASE) >= ARRAY_SIZE(fwctl_ioctl_ops))
65 		return -ENOIOCTLCMD;
66 
67 	op = &fwctl_ioctl_ops[nr - FWCTL_CMD_BASE];
68 	if (op->ioctl_num != cmd)
69 		return -ENOIOCTLCMD;
70 
71 	ucmd.uctx = uctx;
72 	ucmd.cmd = &buf;
73 	ucmd.ubuffer = (void __user *)arg;
74 	ret = get_user(ucmd.user_size, (u32 __user *)ucmd.ubuffer);
75 	if (ret)
76 		return ret;
77 
78 	if (ucmd.user_size < op->min_size)
79 		return -EINVAL;
80 
81 	ret = copy_struct_from_user(ucmd.cmd, op->size, ucmd.ubuffer,
82 				    ucmd.user_size);
83 	if (ret)
84 		return ret;
85 
86 	guard(rwsem_read)(&uctx->fwctl->registration_lock);
87 	if (!uctx->fwctl->ops)
88 		return -ENODEV;
89 	return op->execute(&ucmd);
90 }
91 
92 static int fwctl_fops_open(struct inode *inode, struct file *filp)
93 {
94 	struct fwctl_device *fwctl =
95 		container_of(inode->i_cdev, struct fwctl_device, cdev);
96 	int ret;
97 
98 	guard(rwsem_read)(&fwctl->registration_lock);
99 	if (!fwctl->ops)
100 		return -ENODEV;
101 
102 	struct fwctl_uctx *uctx __free(kfree) =
103 		kzalloc(fwctl->ops->uctx_size, GFP_KERNEL_ACCOUNT);
104 	if (!uctx)
105 		return -ENOMEM;
106 
107 	uctx->fwctl = fwctl;
108 	ret = fwctl->ops->open_uctx(uctx);
109 	if (ret)
110 		return ret;
111 
112 	scoped_guard(mutex, &fwctl->uctx_list_lock) {
113 		list_add_tail(&uctx->uctx_list_entry, &fwctl->uctx_list);
114 	}
115 
116 	get_device(&fwctl->dev);
117 	filp->private_data = no_free_ptr(uctx);
118 	return 0;
119 }
120 
121 static void fwctl_destroy_uctx(struct fwctl_uctx *uctx)
122 {
123 	lockdep_assert_held(&uctx->fwctl->uctx_list_lock);
124 	list_del(&uctx->uctx_list_entry);
125 	uctx->fwctl->ops->close_uctx(uctx);
126 }
127 
128 static int fwctl_fops_release(struct inode *inode, struct file *filp)
129 {
130 	struct fwctl_uctx *uctx = filp->private_data;
131 	struct fwctl_device *fwctl = uctx->fwctl;
132 
133 	scoped_guard(rwsem_read, &fwctl->registration_lock) {
134 		/*
135 		 * NULL ops means fwctl_unregister() has already removed the
136 		 * driver and destroyed the uctx.
137 		 */
138 		if (fwctl->ops) {
139 			guard(mutex)(&fwctl->uctx_list_lock);
140 			fwctl_destroy_uctx(uctx);
141 		}
142 	}
143 
144 	kfree(uctx);
145 	fwctl_put(fwctl);
146 	return 0;
147 }
148 
149 static const struct file_operations fwctl_fops = {
150 	.owner = THIS_MODULE,
151 	.open = fwctl_fops_open,
152 	.release = fwctl_fops_release,
153 	.unlocked_ioctl = fwctl_fops_ioctl,
154 };
155 
156 static void fwctl_device_release(struct device *device)
157 {
158 	struct fwctl_device *fwctl =
159 		container_of(device, struct fwctl_device, dev);
160 
161 	ida_free(&fwctl_ida, fwctl->dev.devt - fwctl_dev);
162 	mutex_destroy(&fwctl->uctx_list_lock);
163 	kfree(fwctl);
164 }
165 
166 static char *fwctl_devnode(const struct device *dev, umode_t *mode)
167 {
168 	return kasprintf(GFP_KERNEL, "fwctl/%s", dev_name(dev));
169 }
170 
171 static struct class fwctl_class = {
172 	.name = "fwctl",
173 	.dev_release = fwctl_device_release,
174 	.devnode = fwctl_devnode,
175 };
176 
177 static struct fwctl_device *
178 _alloc_device(struct device *parent, const struct fwctl_ops *ops, size_t size)
179 {
180 	struct fwctl_device *fwctl __free(kfree) = kzalloc(size, GFP_KERNEL);
181 	int devnum;
182 
183 	if (!fwctl)
184 		return NULL;
185 
186 	devnum = ida_alloc_max(&fwctl_ida, FWCTL_MAX_DEVICES - 1, GFP_KERNEL);
187 	if (devnum < 0)
188 		return NULL;
189 
190 	fwctl->dev.devt = fwctl_dev + devnum;
191 	fwctl->dev.class = &fwctl_class;
192 	fwctl->dev.parent = parent;
193 
194 	init_rwsem(&fwctl->registration_lock);
195 	mutex_init(&fwctl->uctx_list_lock);
196 	INIT_LIST_HEAD(&fwctl->uctx_list);
197 
198 	device_initialize(&fwctl->dev);
199 	return_ptr(fwctl);
200 }
201 
202 /* Drivers use the fwctl_alloc_device() wrapper */
203 struct fwctl_device *_fwctl_alloc_device(struct device *parent,
204 					 const struct fwctl_ops *ops,
205 					 size_t size)
206 {
207 	struct fwctl_device *fwctl __free(fwctl) =
208 		_alloc_device(parent, ops, size);
209 
210 	if (!fwctl)
211 		return NULL;
212 
213 	cdev_init(&fwctl->cdev, &fwctl_fops);
214 	/*
215 	 * The driver module is protected by fwctl_register/unregister(),
216 	 * unregister won't complete until we are done with the driver's module.
217 	 */
218 	fwctl->cdev.owner = THIS_MODULE;
219 
220 	if (dev_set_name(&fwctl->dev, "fwctl%d", fwctl->dev.devt - fwctl_dev))
221 		return NULL;
222 
223 	fwctl->ops = ops;
224 	return_ptr(fwctl);
225 }
226 EXPORT_SYMBOL_NS_GPL(_fwctl_alloc_device, "FWCTL");
227 
228 /**
229  * fwctl_register - Register a new device to the subsystem
230  * @fwctl: Previously allocated fwctl_device
231  *
232  * On return the device is visible through sysfs and /dev, driver ops may be
233  * called.
234  */
235 int fwctl_register(struct fwctl_device *fwctl)
236 {
237 	return cdev_device_add(&fwctl->cdev, &fwctl->dev);
238 }
239 EXPORT_SYMBOL_NS_GPL(fwctl_register, "FWCTL");
240 
241 /**
242  * fwctl_unregister - Unregister a device from the subsystem
243  * @fwctl: Previously allocated and registered fwctl_device
244  *
245  * Undoes fwctl_register(). On return no driver ops will be called. The
246  * caller must still call fwctl_put() to free the fwctl.
247  *
248  * Unregister will return even if userspace still has file descriptors open.
249  * This will call ops->close_uctx() on any open FDs and after return no driver
250  * op will be called. The FDs remain open but all fops will return -ENODEV.
251  *
252  * The design of fwctl allows this sort of disassociation of the driver from the
253  * subsystem primarily by keeping memory allocations owned by the core subsytem.
254  * The fwctl_device and fwctl_uctx can both be freed without requiring a driver
255  * callback. This allows the module to remain unlocked while FDs are open.
256  */
257 void fwctl_unregister(struct fwctl_device *fwctl)
258 {
259 	struct fwctl_uctx *uctx;
260 
261 	cdev_device_del(&fwctl->cdev, &fwctl->dev);
262 
263 	/* Disable and free the driver's resources for any still open FDs. */
264 	guard(rwsem_write)(&fwctl->registration_lock);
265 	guard(mutex)(&fwctl->uctx_list_lock);
266 	while ((uctx = list_first_entry_or_null(&fwctl->uctx_list,
267 						struct fwctl_uctx,
268 						uctx_list_entry)))
269 		fwctl_destroy_uctx(uctx);
270 
271 	/*
272 	 * The driver module may unload after this returns, the op pointer will
273 	 * not be valid.
274 	 */
275 	fwctl->ops = NULL;
276 }
277 EXPORT_SYMBOL_NS_GPL(fwctl_unregister, "FWCTL");
278 
279 static int __init fwctl_init(void)
280 {
281 	int ret;
282 
283 	ret = alloc_chrdev_region(&fwctl_dev, 0, FWCTL_MAX_DEVICES, "fwctl");
284 	if (ret)
285 		return ret;
286 
287 	ret = class_register(&fwctl_class);
288 	if (ret)
289 		goto err_chrdev;
290 	return 0;
291 
292 err_chrdev:
293 	unregister_chrdev_region(fwctl_dev, FWCTL_MAX_DEVICES);
294 	return ret;
295 }
296 
297 static void __exit fwctl_exit(void)
298 {
299 	class_unregister(&fwctl_class);
300 	unregister_chrdev_region(fwctl_dev, FWCTL_MAX_DEVICES);
301 }
302 
303 module_init(fwctl_init);
304 module_exit(fwctl_exit);
305 MODULE_DESCRIPTION("fwctl device firmware access framework");
306 MODULE_LICENSE("GPL");
307