xref: /OK3568_Linux_fs/kernel/arch/powerpc/platforms/powernv/vas-api.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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