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