1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (C) 2023-2026 Intel Corporation */ 3 #include <linux/atomic.h> 4 #include <linux/cdev.h> 5 #include <linux/cleanup.h> 6 #include <linux/device.h> 7 #include <linux/err.h> 8 #include <linux/export.h> 9 #include <linux/fs.h> 10 #include <linux/issei.h> 11 #include <linux/kobject.h> 12 #include <linux/list.h> 13 #include <linux/module.h> 14 #include <linux/mutex.h> 15 #include <linux/poll.h> 16 #include <linux/sched/signal.h> 17 #include <linux/slab.h> 18 #include <linux/types.h> 19 #include <linux/wait.h> 20 #include <linux/xarray.h> 21 22 #include "issei_dev.h" 23 #include "host_client.h" 24 #include "cdev.h" 25 26 struct class *issei_class; 27 static dev_t issei_devt; 28 29 #define ISSEI_MAX_DEVS MINORMASK 30 31 static DEFINE_XARRAY_ALLOC(issei_minor_xa); 32 33 static int issei_open(struct inode *inode, struct file *fp) 34 { 35 struct issei_host_client *cl; 36 struct issei_device *idev; 37 38 xa_lock(&issei_minor_xa); 39 idev = xa_load(&issei_minor_xa, iminor(inode)); 40 if (idev) 41 get_device(&idev->dev); 42 xa_unlock(&issei_minor_xa); 43 if (!idev) 44 return -ENODEV; 45 46 cl = issei_cl_create(idev, fp); 47 if (IS_ERR(cl)) { 48 put_device(&idev->dev); 49 return PTR_ERR(cl); 50 } 51 fp->private_data = cl; 52 53 return nonseekable_open(inode, fp); 54 } 55 56 static int issei_release(struct inode *inode, struct file *fp) 57 { 58 struct issei_host_client *cl = fp->private_data; 59 struct issei_device *idev = cl->idev; 60 61 issei_cl_remove(cl); 62 put_device(&idev->dev); 63 64 return 0; 65 } 66 67 static long issei_ioctl(struct file *file, unsigned int cmd, unsigned long data) 68 { 69 struct issei_host_client *cl = file->private_data; 70 struct issei_connect_client_data conn; 71 struct issei_device *idev = cl->idev; 72 int ret; 73 74 switch (cmd) { 75 case IOCTL_ISSEI_CONNECT_CLIENT: 76 dev_dbg(&idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n"); 77 78 if (idev->rst_state != ISSEI_RST_STATE_DONE) { 79 dev_dbg(&idev->dev, "Device is in transition\n"); 80 return -ENODEV; 81 } 82 83 if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { 84 dev_dbg(&idev->dev, "failed to copy data from userland\n"); 85 return -EFAULT; 86 } 87 88 ret = issei_cl_connect(cl, (uuid_t *)&conn.in_client_uuid, 89 &conn.out_client_properties.max_msg_length, 90 &conn.out_client_properties.protocol_version, 91 &conn.out_client_properties.flags); 92 if (ret) 93 return ret; 94 95 if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { 96 dev_dbg(&idev->dev, "failed to copy data to userland\n"); 97 issei_cl_disconnect(cl); 98 return -EFAULT; 99 } 100 return 0; 101 102 case IOCTL_ISSEI_DISCONNECT_CLIENT: 103 dev_dbg(&idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n"); 104 105 if (idev->rst_state != ISSEI_RST_STATE_DONE) { 106 dev_dbg(&idev->dev, "Device is in transition\n"); 107 return -ENODEV; 108 } 109 110 return issei_cl_disconnect(cl); 111 112 default: 113 return -ENOIOCTLCMD; 114 } 115 } 116 117 static ssize_t issei_write(struct file *file, const char __user *ubuf, 118 size_t length, loff_t *offset) 119 { 120 struct issei_host_client *cl = file->private_data; 121 struct issei_device *idev = cl->idev; 122 ssize_t ret; 123 124 if (!length) 125 return 0; 126 127 if (idev->rst_state != ISSEI_RST_STATE_DONE) { 128 dev_dbg(&idev->dev, "Device is in transition\n"); 129 return -EBUSY; 130 } 131 132 /* sanity check */ 133 if (length > idev->dma.length.h2f) { 134 dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", 135 length, idev->dma.length.h2f); 136 return -EFBIG; 137 } 138 139 u8 *buf __free(kfree) = memdup_user(ubuf, length); 140 if (IS_ERR(buf)) { 141 dev_dbg(&idev->dev, "failed to copy data from userland\n"); 142 return PTR_ERR(buf); 143 } 144 145 do { 146 ret = issei_cl_write(cl, buf, length); 147 if (ret < 0 && ret != -EAGAIN) 148 return ret; 149 /* buf is consumed by issei_cl_write on success */ 150 if (ret >= 0) 151 retain_and_null_ptr(buf); 152 if (wait_event_interruptible(cl->write_wait, issei_cl_check_write(cl) != 1)) { 153 issei_cl_clean_all_wbuf(cl); 154 if (signal_pending(current)) 155 return -EINTR; 156 return -ERESTARTSYS; 157 } 158 } while (ret == -EAGAIN); 159 160 return ret; 161 } 162 163 static ssize_t issei_read(struct file *file, char __user *ubuf, 164 size_t length, loff_t *offset) 165 { 166 struct issei_host_client *cl = file->private_data; 167 struct issei_device *idev = cl->idev; 168 u8 *data = NULL; 169 ssize_t ret; 170 171 if (!length) 172 return 0; 173 174 if (idev->rst_state != ISSEI_RST_STATE_DONE) { 175 dev_dbg(&idev->dev, "Device is in transition\n"); 176 return -EBUSY; 177 } 178 179 /* sanity check */ 180 if (length > idev->dma.length.f2h) { 181 dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", 182 length, idev->dma.length.f2h); 183 return -EFBIG; 184 } 185 186 ret = issei_cl_read(cl, &data, length); 187 if (ret < 0) { 188 if (ret != -ENOENT) 189 return ret; 190 191 if (wait_event_interruptible(cl->read_wait, issei_cl_check_read(cl) != 0)) { 192 if (signal_pending(current)) 193 return -EINTR; 194 return -ERESTARTSYS; 195 } 196 197 ret = issei_cl_read(cl, &data, length); 198 if (ret < 0) 199 return ret; 200 } 201 202 if (copy_to_user(ubuf, data, ret)) { 203 dev_dbg(&idev->dev, "failed to copy data to userland\n"); 204 ret = -EFAULT; 205 } else { 206 *offset = 0; 207 } 208 209 kfree(data); 210 211 return ret; 212 } 213 214 static __poll_t issei_poll(struct file *file, poll_table *wait) 215 { 216 __poll_t req_events = poll_requested_events(wait); 217 struct issei_host_client *cl = file->private_data; 218 struct issei_device *idev = cl->idev; 219 __poll_t mask = 0; 220 int ret; 221 222 if (idev->rst_state != ISSEI_RST_STATE_DONE) { 223 dev_dbg(&idev->dev, "Device is in transition\n"); 224 return EPOLLERR; 225 } 226 227 if (req_events & (EPOLLIN | EPOLLRDNORM)) { 228 poll_wait(file, &cl->read_wait, wait); 229 ret = issei_cl_check_read(cl); 230 if (ret == 1) 231 mask |= EPOLLIN | EPOLLRDNORM; 232 else if (ret < 0) 233 mask |= EPOLLERR; 234 } 235 236 if (req_events & (EPOLLOUT | EPOLLWRNORM)) { 237 poll_wait(file, &cl->write_wait, wait); 238 ret = issei_cl_check_write(cl); 239 if (ret == 0) 240 mask |= EPOLLOUT | EPOLLWRNORM; 241 else if (ret < 0) 242 mask |= EPOLLERR; 243 } 244 245 return mask; 246 } 247 248 static ssize_t fw_ver_show(struct device *device, 249 struct device_attribute *attr, char *buf) 250 { 251 struct issei_device *idev = dev_get_drvdata(device); 252 253 return sysfs_emit(buf, "%u.%u.%u.%u\n", idev->fw_version[0], idev->fw_version[1], 254 idev->fw_version[2], idev->fw_version[3]); 255 } 256 static DEVICE_ATTR_RO(fw_ver); 257 258 static struct attribute *issei_attrs[] = { 259 &dev_attr_fw_ver.attr, 260 NULL 261 }; 262 ATTRIBUTE_GROUPS(issei); 263 264 static const struct file_operations issei_fops = { 265 .owner = THIS_MODULE, 266 .open = issei_open, 267 .unlocked_ioctl = issei_ioctl, 268 .compat_ioctl = compat_ptr_ioctl, 269 .write = issei_write, 270 .read = issei_read, 271 .release = issei_release, 272 .poll = issei_poll, 273 }; 274 275 static void issei_device_release(struct device *dev) 276 { 277 kfree(dev_get_drvdata(dev)); 278 } 279 280 static void issei_device_init(struct issei_device *idev, struct device *parent, 281 const struct issei_dma_length *dma_length, 282 const struct issei_hw_ops *ops) 283 { 284 idev->parent = parent; 285 idev->power_down = false; 286 init_waitqueue_head(&idev->wait_has_data); 287 idev->has_data = false; 288 init_waitqueue_head(&idev->wait_rst_state); 289 idev->rst_state = ISSEI_RST_STATE_INIT; 290 291 mutex_init(&idev->client_lock); 292 INIT_LIST_HEAD(&idev->host_client_list); 293 idev->host_client_last_id = 0; 294 idev->host_client_count = 0; 295 INIT_LIST_HEAD(&idev->fw_client_list); 296 INIT_LIST_HEAD(&idev->write_queue); 297 idev->last_write_ts = 0; 298 299 idev->dma.length = *dma_length; 300 301 idev->ops = ops; 302 } 303 304 /** 305 * issei_register: register issei character device 306 * @hw_size: size of the hardware structure to allocate 307 * @parent: parent device 308 * @dma_length: structure with DMA sizes 309 * @ops: hardware-related operations 310 * 311 * Return: pointer allocated to issei_device structure, error on failure 312 */ 313 struct issei_device *issei_register(size_t hw_size, struct device *parent, 314 const struct issei_dma_length *dma_length, 315 const struct issei_hw_ops *ops) 316 { 317 struct issei_device *idev; 318 u32 minor; 319 int ret, devno; 320 321 idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL); 322 if (!idev) 323 return ERR_PTR(-ENOMEM); 324 325 issei_device_init(idev, parent, dma_length, ops); 326 327 ret = xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEVS), GFP_KERNEL); 328 if (ret < 0) { 329 dev_err(&idev->dev, "Failed to allocate minor. ret = %d\n", ret); 330 kfree(idev); 331 return ERR_PTR(ret); 332 } 333 334 idev->minor = minor; 335 devno = MKDEV(MAJOR(issei_devt), idev->minor); 336 337 device_initialize(&idev->dev); 338 idev->dev.devt = devno; 339 idev->dev.class = issei_class; 340 idev->dev.parent = parent; 341 idev->dev.groups = issei_groups; 342 idev->dev.release = issei_device_release; 343 dev_set_drvdata(&idev->dev, idev); 344 345 idev->cdev = cdev_alloc(); 346 if (!idev->cdev) { 347 ret = -ENOMEM; 348 goto err; 349 } 350 idev->cdev->ops = &issei_fops; 351 if (parent->driver) 352 idev->cdev->owner = parent->driver->owner; 353 cdev_set_parent(idev->cdev, &idev->dev.kobj); 354 355 ret = cdev_add(idev->cdev, devno, 1); 356 if (ret) { 357 dev_err(parent, "unable to add device %d:%u ret = %d\n", 358 MAJOR(issei_devt), idev->minor, ret); 359 goto err_del_cdev; 360 } 361 362 ret = dev_set_name(&idev->dev, "issei%u", idev->minor); 363 if (ret) { 364 dev_err(parent, "unable to set name to device %d:%u ret = %d\n", 365 MAJOR(issei_devt), idev->minor, ret); 366 goto err_del_cdev; 367 } 368 369 ret = device_add(&idev->dev); 370 if (ret) { 371 dev_err(parent, "unable to add device %d:%u ret = %d\n", 372 MAJOR(issei_devt), idev->minor, ret); 373 goto err_del_cdev; 374 } 375 376 idev->fw_clients = kset_create_and_add("fw_clients", NULL, &idev->dev.kobj); 377 if (!idev->fw_clients) { 378 ret = -ENOMEM; 379 goto err_del_dev; 380 } 381 382 return idev; 383 384 err_del_dev: 385 device_del(&idev->dev); 386 err_del_cdev: 387 cdev_del(idev->cdev); 388 err: 389 put_device(&idev->dev); 390 xa_erase(&issei_minor_xa, minor); 391 392 return ERR_PTR(ret); 393 } 394 EXPORT_SYMBOL_GPL(issei_register); 395 396 /** 397 * issei_deregister: remove issei character device 398 * @idev: the device structure 399 */ 400 void issei_deregister(struct issei_device *idev) 401 { 402 u32 minor = idev->minor; 403 404 cdev_del(idev->cdev); 405 406 kset_unregister(idev->fw_clients); 407 408 device_del(&idev->dev); 409 410 put_device(&idev->dev); 411 412 xa_erase(&issei_minor_xa, minor); 413 } 414 EXPORT_SYMBOL_GPL(issei_deregister); 415 416 static int __init issei_cdev_init(void) 417 { 418 int ret; 419 420 issei_class = class_create("issei"); 421 if (IS_ERR(issei_class)) { 422 pr_err("couldn't create class\n"); 423 return PTR_ERR(issei_class); 424 } 425 426 ret = alloc_chrdev_region(&issei_devt, 0, ISSEI_MAX_DEVS, "issei"); 427 if (ret < 0) { 428 pr_err("unable to allocate char dev region\n"); 429 class_destroy(issei_class); 430 return ret; 431 } 432 433 return 0; 434 } 435 436 static void __exit issei_cdev_exit(void) 437 { 438 unregister_chrdev_region(issei_devt, ISSEI_MAX_DEVS); 439 class_destroy(issei_class); 440 } 441 442 module_init(issei_cdev_init); 443 module_exit(issei_cdev_exit); 444 445 MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface"); 446 MODULE_LICENSE("GPL"); 447