18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * VAS user space API for its accelerators (Only NX-GZIP is supported now)
48c2ecf20Sopenharmony_ci * Copyright (C) 2019 Haren Myneni, IBM Corp
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/device.h>
98c2ecf20Sopenharmony_ci#include <linux/cdev.h>
108c2ecf20Sopenharmony_ci#include <linux/fs.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
138c2ecf20Sopenharmony_ci#include <asm/vas.h>
148c2ecf20Sopenharmony_ci#include <uapi/asm/vas-api.h>
158c2ecf20Sopenharmony_ci#include "vas.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/*
188c2ecf20Sopenharmony_ci * The driver creates the device node that can be used as follows:
198c2ecf20Sopenharmony_ci * For NX-GZIP
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci *	fd = open("/dev/crypto/nx-gzip", O_RDWR);
228c2ecf20Sopenharmony_ci *	rc = ioctl(fd, VAS_TX_WIN_OPEN, &attr);
238c2ecf20Sopenharmony_ci *	paste_addr = mmap(NULL, PAGE_SIZE, prot, MAP_SHARED, fd, 0ULL).
248c2ecf20Sopenharmony_ci *	vas_copy(&crb, 0, 1);
258c2ecf20Sopenharmony_ci *	vas_paste(paste_addr, 0, 1);
268c2ecf20Sopenharmony_ci *	close(fd) or exit process to close window.
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci * where "vas_copy" and "vas_paste" are defined in copy-paste.h.
298c2ecf20Sopenharmony_ci * copy/paste returns to the user space directly. So refer NX hardware
308c2ecf20Sopenharmony_ci * documententation for exact copy/paste usage and completion / error
318c2ecf20Sopenharmony_ci * conditions.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/*
358c2ecf20Sopenharmony_ci * Wrapper object for the nx-gzip device - there is just one instance of
368c2ecf20Sopenharmony_ci * this node for the whole system.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_cistatic struct coproc_dev {
398c2ecf20Sopenharmony_ci	struct cdev cdev;
408c2ecf20Sopenharmony_ci	struct device *device;
418c2ecf20Sopenharmony_ci	char *name;
428c2ecf20Sopenharmony_ci	dev_t devt;
438c2ecf20Sopenharmony_ci	struct class *class;
448c2ecf20Sopenharmony_ci	enum vas_cop_type cop_type;
458c2ecf20Sopenharmony_ci} coproc_device;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistruct coproc_instance {
488c2ecf20Sopenharmony_ci	struct coproc_dev *coproc;
498c2ecf20Sopenharmony_ci	struct vas_window *txwin;
508c2ecf20Sopenharmony_ci};
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic char *coproc_devnode(struct device *dev, umode_t *mode)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	return kasprintf(GFP_KERNEL, "crypto/%s", dev_name(dev));
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int coproc_open(struct inode *inode, struct file *fp)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct coproc_instance *cp_inst;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	cp_inst = kzalloc(sizeof(*cp_inst), GFP_KERNEL);
628c2ecf20Sopenharmony_ci	if (!cp_inst)
638c2ecf20Sopenharmony_ci		return -ENOMEM;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	cp_inst->coproc = container_of(inode->i_cdev, struct coproc_dev,
668c2ecf20Sopenharmony_ci					cdev);
678c2ecf20Sopenharmony_ci	fp->private_data = cp_inst;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	return 0;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic int coproc_ioc_tx_win_open(struct file *fp, unsigned long arg)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	void __user *uptr = (void __user *)arg;
758c2ecf20Sopenharmony_ci	struct vas_tx_win_attr txattr = {};
768c2ecf20Sopenharmony_ci	struct vas_tx_win_open_attr uattr;
778c2ecf20Sopenharmony_ci	struct coproc_instance *cp_inst;
788c2ecf20Sopenharmony_ci	struct vas_window *txwin;
798c2ecf20Sopenharmony_ci	int rc, vasid;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	cp_inst = fp->private_data;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/*
848c2ecf20Sopenharmony_ci	 * One window for file descriptor
858c2ecf20Sopenharmony_ci	 */
868c2ecf20Sopenharmony_ci	if (cp_inst->txwin)
878c2ecf20Sopenharmony_ci		return -EEXIST;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	rc = copy_from_user(&uattr, uptr, sizeof(uattr));
908c2ecf20Sopenharmony_ci	if (rc) {
918c2ecf20Sopenharmony_ci		pr_err("%s(): copy_from_user() returns %d\n", __func__, rc);
928c2ecf20Sopenharmony_ci		return -EFAULT;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (uattr.version != 1) {
968c2ecf20Sopenharmony_ci		pr_err("Invalid version\n");
978c2ecf20Sopenharmony_ci		return -EINVAL;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	vasid = uattr.vas_id;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	vas_init_tx_win_attr(&txattr, cp_inst->coproc->cop_type);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	txattr.lpid = mfspr(SPRN_LPID);
1058c2ecf20Sopenharmony_ci	txattr.pidr = mfspr(SPRN_PID);
1068c2ecf20Sopenharmony_ci	txattr.user_win = true;
1078c2ecf20Sopenharmony_ci	txattr.rsvd_txbuf_count = false;
1088c2ecf20Sopenharmony_ci	txattr.pswid = false;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	pr_devel("Pid %d: Opening txwin, PIDR %ld\n", txattr.pidr,
1118c2ecf20Sopenharmony_ci				mfspr(SPRN_PID));
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	txwin = vas_tx_win_open(vasid, cp_inst->coproc->cop_type, &txattr);
1148c2ecf20Sopenharmony_ci	if (IS_ERR(txwin)) {
1158c2ecf20Sopenharmony_ci		pr_err("%s() vas_tx_win_open() failed, %ld\n", __func__,
1168c2ecf20Sopenharmony_ci					PTR_ERR(txwin));
1178c2ecf20Sopenharmony_ci		return PTR_ERR(txwin);
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	cp_inst->txwin = txwin;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	return 0;
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic int coproc_release(struct inode *inode, struct file *fp)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	struct coproc_instance *cp_inst = fp->private_data;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (cp_inst->txwin) {
1308c2ecf20Sopenharmony_ci		vas_win_close(cp_inst->txwin);
1318c2ecf20Sopenharmony_ci		cp_inst->txwin = NULL;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	kfree(cp_inst);
1358c2ecf20Sopenharmony_ci	fp->private_data = NULL;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/*
1388c2ecf20Sopenharmony_ci	 * We don't know here if user has other receive windows
1398c2ecf20Sopenharmony_ci	 * open, so we can't really call clear_thread_tidr().
1408c2ecf20Sopenharmony_ci	 * So, once the process calls set_thread_tidr(), the
1418c2ecf20Sopenharmony_ci	 * TIDR value sticks around until process exits, resulting
1428c2ecf20Sopenharmony_ci	 * in an extra copy in restore_sprs().
1438c2ecf20Sopenharmony_ci	 */
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	return 0;
1468c2ecf20Sopenharmony_ci}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_cistatic int coproc_mmap(struct file *fp, struct vm_area_struct *vma)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct coproc_instance *cp_inst = fp->private_data;
1518c2ecf20Sopenharmony_ci	struct vas_window *txwin;
1528c2ecf20Sopenharmony_ci	unsigned long pfn;
1538c2ecf20Sopenharmony_ci	u64 paste_addr;
1548c2ecf20Sopenharmony_ci	pgprot_t prot;
1558c2ecf20Sopenharmony_ci	int rc;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	txwin = cp_inst->txwin;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) {
1608c2ecf20Sopenharmony_ci		pr_debug("%s(): size 0x%zx, PAGE_SIZE 0x%zx\n", __func__,
1618c2ecf20Sopenharmony_ci				(vma->vm_end - vma->vm_start), PAGE_SIZE);
1628c2ecf20Sopenharmony_ci		return -EINVAL;
1638c2ecf20Sopenharmony_ci	}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	/* Ensure instance has an open send window */
1668c2ecf20Sopenharmony_ci	if (!txwin) {
1678c2ecf20Sopenharmony_ci		pr_err("%s(): No send window open?\n", __func__);
1688c2ecf20Sopenharmony_ci		return -EINVAL;
1698c2ecf20Sopenharmony_ci	}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	vas_win_paste_addr(txwin, &paste_addr, NULL);
1728c2ecf20Sopenharmony_ci	pfn = paste_addr >> PAGE_SHIFT;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	/* flags, page_prot from cxl_mmap(), except we want cachable */
1758c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_IO | VM_PFNMAP;
1768c2ecf20Sopenharmony_ci	vma->vm_page_prot = pgprot_cached(vma->vm_page_prot);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	prot = __pgprot(pgprot_val(vma->vm_page_prot) | _PAGE_DIRTY);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	rc = remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
1818c2ecf20Sopenharmony_ci			vma->vm_end - vma->vm_start, prot);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	pr_devel("%s(): paste addr %llx at %lx, rc %d\n", __func__,
1848c2ecf20Sopenharmony_ci			paste_addr, vma->vm_start, rc);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return rc;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic long coproc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	switch (cmd) {
1928c2ecf20Sopenharmony_ci	case VAS_TX_WIN_OPEN:
1938c2ecf20Sopenharmony_ci		return coproc_ioc_tx_win_open(fp, arg);
1948c2ecf20Sopenharmony_ci	default:
1958c2ecf20Sopenharmony_ci		return -EINVAL;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic struct file_operations coproc_fops = {
2008c2ecf20Sopenharmony_ci	.open = coproc_open,
2018c2ecf20Sopenharmony_ci	.release = coproc_release,
2028c2ecf20Sopenharmony_ci	.mmap = coproc_mmap,
2038c2ecf20Sopenharmony_ci	.unlocked_ioctl = coproc_ioctl,
2048c2ecf20Sopenharmony_ci};
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci/*
2078c2ecf20Sopenharmony_ci * Supporting only nx-gzip coprocessor type now, but this API code
2088c2ecf20Sopenharmony_ci * extended to other coprocessor types later.
2098c2ecf20Sopenharmony_ci */
2108c2ecf20Sopenharmony_ciint vas_register_coproc_api(struct module *mod, enum vas_cop_type cop_type,
2118c2ecf20Sopenharmony_ci				const char *name)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	int rc = -EINVAL;
2148c2ecf20Sopenharmony_ci	dev_t devno;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	rc = alloc_chrdev_region(&coproc_device.devt, 1, 1, name);
2178c2ecf20Sopenharmony_ci	if (rc) {
2188c2ecf20Sopenharmony_ci		pr_err("Unable to allocate coproc major number: %i\n", rc);
2198c2ecf20Sopenharmony_ci		return rc;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	pr_devel("%s device allocated, dev [%i,%i]\n", name,
2238c2ecf20Sopenharmony_ci			MAJOR(coproc_device.devt), MINOR(coproc_device.devt));
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	coproc_device.class = class_create(mod, name);
2268c2ecf20Sopenharmony_ci	if (IS_ERR(coproc_device.class)) {
2278c2ecf20Sopenharmony_ci		rc = PTR_ERR(coproc_device.class);
2288c2ecf20Sopenharmony_ci		pr_err("Unable to create %s class %d\n", name, rc);
2298c2ecf20Sopenharmony_ci		goto err_class;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci	coproc_device.class->devnode = coproc_devnode;
2328c2ecf20Sopenharmony_ci	coproc_device.cop_type = cop_type;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	coproc_fops.owner = mod;
2358c2ecf20Sopenharmony_ci	cdev_init(&coproc_device.cdev, &coproc_fops);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	devno = MKDEV(MAJOR(coproc_device.devt), 0);
2388c2ecf20Sopenharmony_ci	rc = cdev_add(&coproc_device.cdev, devno, 1);
2398c2ecf20Sopenharmony_ci	if (rc) {
2408c2ecf20Sopenharmony_ci		pr_err("cdev_add() failed %d\n", rc);
2418c2ecf20Sopenharmony_ci		goto err_cdev;
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	coproc_device.device = device_create(coproc_device.class, NULL,
2458c2ecf20Sopenharmony_ci			devno, NULL, name, MINOR(devno));
2468c2ecf20Sopenharmony_ci	if (IS_ERR(coproc_device.device)) {
2478c2ecf20Sopenharmony_ci		rc = PTR_ERR(coproc_device.device);
2488c2ecf20Sopenharmony_ci		pr_err("Unable to create coproc-%d %d\n", MINOR(devno), rc);
2498c2ecf20Sopenharmony_ci		goto err;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	pr_devel("%s: Added dev [%d,%d]\n", __func__, MAJOR(devno),
2538c2ecf20Sopenharmony_ci			MINOR(devno));
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return 0;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cierr:
2588c2ecf20Sopenharmony_ci	cdev_del(&coproc_device.cdev);
2598c2ecf20Sopenharmony_cierr_cdev:
2608c2ecf20Sopenharmony_ci	class_destroy(coproc_device.class);
2618c2ecf20Sopenharmony_cierr_class:
2628c2ecf20Sopenharmony_ci	unregister_chrdev_region(coproc_device.devt, 1);
2638c2ecf20Sopenharmony_ci	return rc;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vas_register_coproc_api);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_civoid vas_unregister_coproc_api(void)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	dev_t devno;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	cdev_del(&coproc_device.cdev);
2728c2ecf20Sopenharmony_ci	devno = MKDEV(MAJOR(coproc_device.devt), 0);
2738c2ecf20Sopenharmony_ci	device_destroy(coproc_device.class, devno);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	class_destroy(coproc_device.class);
2768c2ecf20Sopenharmony_ci	unregister_chrdev_region(coproc_device.devt, 1);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vas_unregister_coproc_api);
279