1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * VAS user space API for its accelerators (Only NX-GZIP is supported now) 4 * Copyright (C) 2019 Haren Myneni, IBM Corp 5 */ 6 7#include <linux/kernel.h> 8#include <linux/device.h> 9#include <linux/cdev.h> 10#include <linux/fs.h> 11#include <linux/slab.h> 12#include <linux/uaccess.h> 13#include <asm/vas.h> 14#include <uapi/asm/vas-api.h> 15#include "vas.h" 16 17/* 18 * The driver creates the device node that can be used as follows: 19 * For NX-GZIP 20 * 21 * fd = open("/dev/crypto/nx-gzip", O_RDWR); 22 * rc = ioctl(fd, VAS_TX_WIN_OPEN, &attr); 23 * paste_addr = mmap(NULL, PAGE_SIZE, prot, MAP_SHARED, fd, 0ULL). 24 * vas_copy(&crb, 0, 1); 25 * vas_paste(paste_addr, 0, 1); 26 * close(fd) or exit process to close window. 27 * 28 * where "vas_copy" and "vas_paste" are defined in copy-paste.h. 29 * copy/paste returns to the user space directly. So refer NX hardware 30 * documententation for exact copy/paste usage and completion / error 31 * conditions. 32 */ 33 34/* 35 * Wrapper object for the nx-gzip device - there is just one instance of 36 * this node for the whole system. 37 */ 38static struct coproc_dev { 39 struct cdev cdev; 40 struct device *device; 41 char *name; 42 dev_t devt; 43 struct class *class; 44 enum vas_cop_type cop_type; 45} coproc_device; 46 47struct coproc_instance { 48 struct coproc_dev *coproc; 49 struct vas_window *txwin; 50}; 51 52static char *coproc_devnode(struct device *dev, umode_t *mode) 53{ 54 return kasprintf(GFP_KERNEL, "crypto/%s", dev_name(dev)); 55} 56 57static int coproc_open(struct inode *inode, struct file *fp) 58{ 59 struct coproc_instance *cp_inst; 60 61 cp_inst = kzalloc(sizeof(*cp_inst), GFP_KERNEL); 62 if (!cp_inst) 63 return -ENOMEM; 64 65 cp_inst->coproc = container_of(inode->i_cdev, struct coproc_dev, 66 cdev); 67 fp->private_data = cp_inst; 68 69 return 0; 70} 71 72static int coproc_ioc_tx_win_open(struct file *fp, unsigned long arg) 73{ 74 void __user *uptr = (void __user *)arg; 75 struct vas_tx_win_attr txattr = {}; 76 struct vas_tx_win_open_attr uattr; 77 struct coproc_instance *cp_inst; 78 struct vas_window *txwin; 79 int rc, vasid; 80 81 cp_inst = fp->private_data; 82 83 /* 84 * One window for file descriptor 85 */ 86 if (cp_inst->txwin) 87 return -EEXIST; 88 89 rc = copy_from_user(&uattr, uptr, sizeof(uattr)); 90 if (rc) { 91 pr_err("%s(): copy_from_user() returns %d\n", __func__, rc); 92 return -EFAULT; 93 } 94 95 if (uattr.version != 1) { 96 pr_err("Invalid version\n"); 97 return -EINVAL; 98 } 99 100 vasid = uattr.vas_id; 101 102 vas_init_tx_win_attr(&txattr, cp_inst->coproc->cop_type); 103 104 txattr.lpid = mfspr(SPRN_LPID); 105 txattr.pidr = mfspr(SPRN_PID); 106 txattr.user_win = true; 107 txattr.rsvd_txbuf_count = false; 108 txattr.pswid = false; 109 110 pr_devel("Pid %d: Opening txwin, PIDR %ld\n", txattr.pidr, 111 mfspr(SPRN_PID)); 112 113 txwin = vas_tx_win_open(vasid, cp_inst->coproc->cop_type, &txattr); 114 if (IS_ERR(txwin)) { 115 pr_err("%s() vas_tx_win_open() failed, %ld\n", __func__, 116 PTR_ERR(txwin)); 117 return PTR_ERR(txwin); 118 } 119 120 cp_inst->txwin = txwin; 121 122 return 0; 123} 124 125static int coproc_release(struct inode *inode, struct file *fp) 126{ 127 struct coproc_instance *cp_inst = fp->private_data; 128 129 if (cp_inst->txwin) { 130 vas_win_close(cp_inst->txwin); 131 cp_inst->txwin = NULL; 132 } 133 134 kfree(cp_inst); 135 fp->private_data = NULL; 136 137 /* 138 * We don't know here if user has other receive windows 139 * open, so we can't really call clear_thread_tidr(). 140 * So, once the process calls set_thread_tidr(), the 141 * TIDR value sticks around until process exits, resulting 142 * in an extra copy in restore_sprs(). 143 */ 144 145 return 0; 146} 147 148static int coproc_mmap(struct file *fp, struct vm_area_struct *vma) 149{ 150 struct coproc_instance *cp_inst = fp->private_data; 151 struct vas_window *txwin; 152 unsigned long pfn; 153 u64 paste_addr; 154 pgprot_t prot; 155 int rc; 156 157 txwin = cp_inst->txwin; 158 159 if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) { 160 pr_debug("%s(): size 0x%zx, PAGE_SIZE 0x%zx\n", __func__, 161 (vma->vm_end - vma->vm_start), PAGE_SIZE); 162 return -EINVAL; 163 } 164 165 /* Ensure instance has an open send window */ 166 if (!txwin) { 167 pr_err("%s(): No send window open?\n", __func__); 168 return -EINVAL; 169 } 170 171 vas_win_paste_addr(txwin, &paste_addr, NULL); 172 pfn = paste_addr >> PAGE_SHIFT; 173 174 /* flags, page_prot from cxl_mmap(), except we want cachable */ 175 vma->vm_flags |= VM_IO | VM_PFNMAP; 176 vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); 177 178 prot = __pgprot(pgprot_val(vma->vm_page_prot) | _PAGE_DIRTY); 179 180 rc = remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff, 181 vma->vm_end - vma->vm_start, prot); 182 183 pr_devel("%s(): paste addr %llx at %lx, rc %d\n", __func__, 184 paste_addr, vma->vm_start, rc); 185 186 return rc; 187} 188 189static long coproc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 190{ 191 switch (cmd) { 192 case VAS_TX_WIN_OPEN: 193 return coproc_ioc_tx_win_open(fp, arg); 194 default: 195 return -EINVAL; 196 } 197} 198 199static struct file_operations coproc_fops = { 200 .open = coproc_open, 201 .release = coproc_release, 202 .mmap = coproc_mmap, 203 .unlocked_ioctl = coproc_ioctl, 204}; 205 206/* 207 * Supporting only nx-gzip coprocessor type now, but this API code 208 * extended to other coprocessor types later. 209 */ 210int vas_register_coproc_api(struct module *mod, enum vas_cop_type cop_type, 211 const char *name) 212{ 213 int rc = -EINVAL; 214 dev_t devno; 215 216 rc = alloc_chrdev_region(&coproc_device.devt, 1, 1, name); 217 if (rc) { 218 pr_err("Unable to allocate coproc major number: %i\n", rc); 219 return rc; 220 } 221 222 pr_devel("%s device allocated, dev [%i,%i]\n", name, 223 MAJOR(coproc_device.devt), MINOR(coproc_device.devt)); 224 225 coproc_device.class = class_create(mod, name); 226 if (IS_ERR(coproc_device.class)) { 227 rc = PTR_ERR(coproc_device.class); 228 pr_err("Unable to create %s class %d\n", name, rc); 229 goto err_class; 230 } 231 coproc_device.class->devnode = coproc_devnode; 232 coproc_device.cop_type = cop_type; 233 234 coproc_fops.owner = mod; 235 cdev_init(&coproc_device.cdev, &coproc_fops); 236 237 devno = MKDEV(MAJOR(coproc_device.devt), 0); 238 rc = cdev_add(&coproc_device.cdev, devno, 1); 239 if (rc) { 240 pr_err("cdev_add() failed %d\n", rc); 241 goto err_cdev; 242 } 243 244 coproc_device.device = device_create(coproc_device.class, NULL, 245 devno, NULL, name, MINOR(devno)); 246 if (IS_ERR(coproc_device.device)) { 247 rc = PTR_ERR(coproc_device.device); 248 pr_err("Unable to create coproc-%d %d\n", MINOR(devno), rc); 249 goto err; 250 } 251 252 pr_devel("%s: Added dev [%d,%d]\n", __func__, MAJOR(devno), 253 MINOR(devno)); 254 255 return 0; 256 257err: 258 cdev_del(&coproc_device.cdev); 259err_cdev: 260 class_destroy(coproc_device.class); 261err_class: 262 unregister_chrdev_region(coproc_device.devt, 1); 263 return rc; 264} 265EXPORT_SYMBOL_GPL(vas_register_coproc_api); 266 267void vas_unregister_coproc_api(void) 268{ 269 dev_t devno; 270 271 cdev_del(&coproc_device.cdev); 272 devno = MKDEV(MAJOR(coproc_device.devt), 0); 273 device_destroy(coproc_device.class, devno); 274 275 class_destroy(coproc_device.class); 276 unregister_chrdev_region(coproc_device.devt, 1); 277} 278EXPORT_SYMBOL_GPL(vas_unregister_coproc_api); 279