162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * VAS user space API for its accelerators (Only NX-GZIP is supported now) 462306a36Sopenharmony_ci * Copyright (C) 2019 Haren Myneni, IBM Corp 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define pr_fmt(fmt) "vas-api: " fmt 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/device.h> 1162306a36Sopenharmony_ci#include <linux/cdev.h> 1262306a36Sopenharmony_ci#include <linux/fs.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/uaccess.h> 1562306a36Sopenharmony_ci#include <linux/kthread.h> 1662306a36Sopenharmony_ci#include <linux/sched/signal.h> 1762306a36Sopenharmony_ci#include <linux/mmu_context.h> 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <asm/vas.h> 2062306a36Sopenharmony_ci#include <uapi/asm/vas-api.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * The driver creates the device node that can be used as follows: 2462306a36Sopenharmony_ci * For NX-GZIP 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * fd = open("/dev/crypto/nx-gzip", O_RDWR); 2762306a36Sopenharmony_ci * rc = ioctl(fd, VAS_TX_WIN_OPEN, &attr); 2862306a36Sopenharmony_ci * paste_addr = mmap(NULL, PAGE_SIZE, prot, MAP_SHARED, fd, 0ULL). 2962306a36Sopenharmony_ci * vas_copy(&crb, 0, 1); 3062306a36Sopenharmony_ci * vas_paste(paste_addr, 0, 1); 3162306a36Sopenharmony_ci * close(fd) or exit process to close window. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * where "vas_copy" and "vas_paste" are defined in copy-paste.h. 3462306a36Sopenharmony_ci * copy/paste returns to the user space directly. So refer NX hardware 3562306a36Sopenharmony_ci * documentation for exact copy/paste usage and completion / error 3662306a36Sopenharmony_ci * conditions. 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* 4062306a36Sopenharmony_ci * Wrapper object for the nx-gzip device - there is just one instance of 4162306a36Sopenharmony_ci * this node for the whole system. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_cistatic struct coproc_dev { 4462306a36Sopenharmony_ci struct cdev cdev; 4562306a36Sopenharmony_ci struct device *device; 4662306a36Sopenharmony_ci char *name; 4762306a36Sopenharmony_ci dev_t devt; 4862306a36Sopenharmony_ci struct class *class; 4962306a36Sopenharmony_ci enum vas_cop_type cop_type; 5062306a36Sopenharmony_ci const struct vas_user_win_ops *vops; 5162306a36Sopenharmony_ci} coproc_device; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct coproc_instance { 5462306a36Sopenharmony_ci struct coproc_dev *coproc; 5562306a36Sopenharmony_ci struct vas_window *txwin; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic char *coproc_devnode(const struct device *dev, umode_t *mode) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "crypto/%s", dev_name(dev)); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* 6462306a36Sopenharmony_ci * Take reference to pid and mm 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ciint get_vas_user_win_ref(struct vas_user_win_ref *task_ref) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci /* 6962306a36Sopenharmony_ci * Window opened by a child thread may not be closed when 7062306a36Sopenharmony_ci * it exits. So take reference to its pid and release it 7162306a36Sopenharmony_ci * when the window is free by parent thread. 7262306a36Sopenharmony_ci * Acquire a reference to the task's pid to make sure 7362306a36Sopenharmony_ci * pid will not be re-used - needed only for multithread 7462306a36Sopenharmony_ci * applications. 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci task_ref->pid = get_task_pid(current, PIDTYPE_PID); 7762306a36Sopenharmony_ci /* 7862306a36Sopenharmony_ci * Acquire a reference to the task's mm. 7962306a36Sopenharmony_ci */ 8062306a36Sopenharmony_ci task_ref->mm = get_task_mm(current); 8162306a36Sopenharmony_ci if (!task_ref->mm) { 8262306a36Sopenharmony_ci put_pid(task_ref->pid); 8362306a36Sopenharmony_ci pr_err("pid(%d): mm_struct is not found\n", 8462306a36Sopenharmony_ci current->pid); 8562306a36Sopenharmony_ci return -EPERM; 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci mmgrab(task_ref->mm); 8962306a36Sopenharmony_ci mmput(task_ref->mm); 9062306a36Sopenharmony_ci /* 9162306a36Sopenharmony_ci * Process closes window during exit. In the case of 9262306a36Sopenharmony_ci * multithread application, the child thread can open 9362306a36Sopenharmony_ci * window and can exit without closing it. So takes tgid 9462306a36Sopenharmony_ci * reference until window closed to make sure tgid is not 9562306a36Sopenharmony_ci * reused. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_ci task_ref->tgid = find_get_pid(task_tgid_vnr(current)); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Successful return must release the task reference with 10462306a36Sopenharmony_ci * put_task_struct 10562306a36Sopenharmony_ci */ 10662306a36Sopenharmony_cistatic bool ref_get_pid_and_task(struct vas_user_win_ref *task_ref, 10762306a36Sopenharmony_ci struct task_struct **tskp, struct pid **pidp) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct task_struct *tsk; 11062306a36Sopenharmony_ci struct pid *pid; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci pid = task_ref->pid; 11362306a36Sopenharmony_ci tsk = get_pid_task(pid, PIDTYPE_PID); 11462306a36Sopenharmony_ci if (!tsk) { 11562306a36Sopenharmony_ci pid = task_ref->tgid; 11662306a36Sopenharmony_ci tsk = get_pid_task(pid, PIDTYPE_PID); 11762306a36Sopenharmony_ci /* 11862306a36Sopenharmony_ci * Parent thread (tgid) will be closing window when it 11962306a36Sopenharmony_ci * exits. So should not get here. 12062306a36Sopenharmony_ci */ 12162306a36Sopenharmony_ci if (WARN_ON_ONCE(!tsk)) 12262306a36Sopenharmony_ci return false; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Return if the task is exiting. */ 12662306a36Sopenharmony_ci if (tsk->flags & PF_EXITING) { 12762306a36Sopenharmony_ci put_task_struct(tsk); 12862306a36Sopenharmony_ci return false; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci *tskp = tsk; 13262306a36Sopenharmony_ci *pidp = pid; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci return true; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * Update the CSB to indicate a translation error. 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * User space will be polling on CSB after the request is issued. 14162306a36Sopenharmony_ci * If NX can handle the request without any issues, it updates CSB. 14262306a36Sopenharmony_ci * Whereas if NX encounters page fault, the kernel will handle the 14362306a36Sopenharmony_ci * fault and update CSB with translation error. 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * If we are unable to update the CSB means copy_to_user failed due to 14662306a36Sopenharmony_ci * invalid csb_addr, send a signal to the process. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_civoid vas_update_csb(struct coprocessor_request_block *crb, 14962306a36Sopenharmony_ci struct vas_user_win_ref *task_ref) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct coprocessor_status_block csb; 15262306a36Sopenharmony_ci struct kernel_siginfo info; 15362306a36Sopenharmony_ci struct task_struct *tsk; 15462306a36Sopenharmony_ci void __user *csb_addr; 15562306a36Sopenharmony_ci struct pid *pid; 15662306a36Sopenharmony_ci int rc; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* 15962306a36Sopenharmony_ci * NX user space windows can not be opened for task->mm=NULL 16062306a36Sopenharmony_ci * and faults will not be generated for kernel requests. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_ci if (WARN_ON_ONCE(!task_ref->mm)) 16362306a36Sopenharmony_ci return; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci csb_addr = (void __user *)be64_to_cpu(crb->csb_addr); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci memset(&csb, 0, sizeof(csb)); 16862306a36Sopenharmony_ci csb.cc = CSB_CC_FAULT_ADDRESS; 16962306a36Sopenharmony_ci csb.ce = CSB_CE_TERMINATION; 17062306a36Sopenharmony_ci csb.cs = 0; 17162306a36Sopenharmony_ci csb.count = 0; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci /* 17462306a36Sopenharmony_ci * NX operates and returns in BE format as defined CRB struct. 17562306a36Sopenharmony_ci * So saves fault_storage_addr in BE as NX pastes in FIFO and 17662306a36Sopenharmony_ci * expects user space to convert to CPU format. 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci csb.address = crb->stamp.nx.fault_storage_addr; 17962306a36Sopenharmony_ci csb.flags = 0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci /* 18262306a36Sopenharmony_ci * Process closes send window after all pending NX requests are 18362306a36Sopenharmony_ci * completed. In multi-thread applications, a child thread can 18462306a36Sopenharmony_ci * open a window and can exit without closing it. May be some 18562306a36Sopenharmony_ci * requests are pending or this window can be used by other 18662306a36Sopenharmony_ci * threads later. We should handle faults if NX encounters 18762306a36Sopenharmony_ci * pages faults on these requests. Update CSB with translation 18862306a36Sopenharmony_ci * error and fault address. If csb_addr passed by user space is 18962306a36Sopenharmony_ci * invalid, send SEGV signal to pid saved in window. If the 19062306a36Sopenharmony_ci * child thread is not running, send the signal to tgid. 19162306a36Sopenharmony_ci * Parent thread (tgid) will close this window upon its exit. 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * pid and mm references are taken when window is opened by 19462306a36Sopenharmony_ci * process (pid). So tgid is used only when child thread opens 19562306a36Sopenharmony_ci * a window and exits without closing it. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (!ref_get_pid_and_task(task_ref, &tsk, &pid)) 19962306a36Sopenharmony_ci return; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci kthread_use_mm(task_ref->mm); 20262306a36Sopenharmony_ci rc = copy_to_user(csb_addr, &csb, sizeof(csb)); 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * User space polls on csb.flags (first byte). So add barrier 20562306a36Sopenharmony_ci * then copy first byte with csb flags update. 20662306a36Sopenharmony_ci */ 20762306a36Sopenharmony_ci if (!rc) { 20862306a36Sopenharmony_ci csb.flags = CSB_V; 20962306a36Sopenharmony_ci /* Make sure update to csb.flags is visible now */ 21062306a36Sopenharmony_ci smp_mb(); 21162306a36Sopenharmony_ci rc = copy_to_user(csb_addr, &csb, sizeof(u8)); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci kthread_unuse_mm(task_ref->mm); 21462306a36Sopenharmony_ci put_task_struct(tsk); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* Success */ 21762306a36Sopenharmony_ci if (!rc) 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci pr_debug("Invalid CSB address 0x%p signalling pid(%d)\n", 22262306a36Sopenharmony_ci csb_addr, pid_vnr(pid)); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci clear_siginfo(&info); 22562306a36Sopenharmony_ci info.si_signo = SIGSEGV; 22662306a36Sopenharmony_ci info.si_errno = EFAULT; 22762306a36Sopenharmony_ci info.si_code = SEGV_MAPERR; 22862306a36Sopenharmony_ci info.si_addr = csb_addr; 22962306a36Sopenharmony_ci /* 23062306a36Sopenharmony_ci * process will be polling on csb.flags after request is sent to 23162306a36Sopenharmony_ci * NX. So generally CSB update should not fail except when an 23262306a36Sopenharmony_ci * application passes invalid csb_addr. So an error message will 23362306a36Sopenharmony_ci * be displayed and leave it to user space whether to ignore or 23462306a36Sopenharmony_ci * handle this signal. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci rcu_read_lock(); 23762306a36Sopenharmony_ci rc = kill_pid_info(SIGSEGV, &info, pid); 23862306a36Sopenharmony_ci rcu_read_unlock(); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci pr_devel("pid %d kill_proc_info() rc %d\n", pid_vnr(pid), rc); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_civoid vas_dump_crb(struct coprocessor_request_block *crb) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct data_descriptor_entry *dde; 24662306a36Sopenharmony_ci struct nx_fault_stamp *nx; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci dde = &crb->source; 24962306a36Sopenharmony_ci pr_devel("SrcDDE: addr 0x%llx, len %d, count %d, idx %d, flags %d\n", 25062306a36Sopenharmony_ci be64_to_cpu(dde->address), be32_to_cpu(dde->length), 25162306a36Sopenharmony_ci dde->count, dde->index, dde->flags); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci dde = &crb->target; 25462306a36Sopenharmony_ci pr_devel("TgtDDE: addr 0x%llx, len %d, count %d, idx %d, flags %d\n", 25562306a36Sopenharmony_ci be64_to_cpu(dde->address), be32_to_cpu(dde->length), 25662306a36Sopenharmony_ci dde->count, dde->index, dde->flags); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci nx = &crb->stamp.nx; 25962306a36Sopenharmony_ci pr_devel("NX Stamp: PSWID 0x%x, FSA 0x%llx, flags 0x%x, FS 0x%x\n", 26062306a36Sopenharmony_ci be32_to_cpu(nx->pswid), 26162306a36Sopenharmony_ci be64_to_cpu(crb->stamp.nx.fault_storage_addr), 26262306a36Sopenharmony_ci nx->flags, nx->fault_status); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic int coproc_open(struct inode *inode, struct file *fp) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci struct coproc_instance *cp_inst; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci cp_inst = kzalloc(sizeof(*cp_inst), GFP_KERNEL); 27062306a36Sopenharmony_ci if (!cp_inst) 27162306a36Sopenharmony_ci return -ENOMEM; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci cp_inst->coproc = container_of(inode->i_cdev, struct coproc_dev, 27462306a36Sopenharmony_ci cdev); 27562306a36Sopenharmony_ci fp->private_data = cp_inst; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int coproc_ioc_tx_win_open(struct file *fp, unsigned long arg) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci void __user *uptr = (void __user *)arg; 28362306a36Sopenharmony_ci struct vas_tx_win_open_attr uattr; 28462306a36Sopenharmony_ci struct coproc_instance *cp_inst; 28562306a36Sopenharmony_ci struct vas_window *txwin; 28662306a36Sopenharmony_ci int rc; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci cp_inst = fp->private_data; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * One window for file descriptor 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci if (cp_inst->txwin) 29462306a36Sopenharmony_ci return -EEXIST; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci rc = copy_from_user(&uattr, uptr, sizeof(uattr)); 29762306a36Sopenharmony_ci if (rc) { 29862306a36Sopenharmony_ci pr_err("copy_from_user() returns %d\n", rc); 29962306a36Sopenharmony_ci return -EFAULT; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (uattr.version != 1) { 30362306a36Sopenharmony_ci pr_err("Invalid window open API version\n"); 30462306a36Sopenharmony_ci return -EINVAL; 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (!cp_inst->coproc->vops || !cp_inst->coproc->vops->open_win) { 30862306a36Sopenharmony_ci pr_err("VAS API is not registered\n"); 30962306a36Sopenharmony_ci return -EACCES; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci txwin = cp_inst->coproc->vops->open_win(uattr.vas_id, uattr.flags, 31362306a36Sopenharmony_ci cp_inst->coproc->cop_type); 31462306a36Sopenharmony_ci if (IS_ERR(txwin)) { 31562306a36Sopenharmony_ci pr_err_ratelimited("VAS window open failed rc=%ld\n", 31662306a36Sopenharmony_ci PTR_ERR(txwin)); 31762306a36Sopenharmony_ci return PTR_ERR(txwin); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci mutex_init(&txwin->task_ref.mmap_mutex); 32162306a36Sopenharmony_ci cp_inst->txwin = txwin; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int coproc_release(struct inode *inode, struct file *fp) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct coproc_instance *cp_inst = fp->private_data; 32962306a36Sopenharmony_ci int rc; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (cp_inst->txwin) { 33262306a36Sopenharmony_ci if (cp_inst->coproc->vops && 33362306a36Sopenharmony_ci cp_inst->coproc->vops->close_win) { 33462306a36Sopenharmony_ci rc = cp_inst->coproc->vops->close_win(cp_inst->txwin); 33562306a36Sopenharmony_ci if (rc) 33662306a36Sopenharmony_ci return rc; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci cp_inst->txwin = NULL; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci kfree(cp_inst); 34262306a36Sopenharmony_ci fp->private_data = NULL; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * We don't know here if user has other receive windows 34662306a36Sopenharmony_ci * open, so we can't really call clear_thread_tidr(). 34762306a36Sopenharmony_ci * So, once the process calls set_thread_tidr(), the 34862306a36Sopenharmony_ci * TIDR value sticks around until process exits, resulting 34962306a36Sopenharmony_ci * in an extra copy in restore_sprs(). 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/* 35662306a36Sopenharmony_ci * If the executed instruction that caused the fault was a paste, then 35762306a36Sopenharmony_ci * clear regs CR0[EQ], advance NIP, and return 0. Else return error code. 35862306a36Sopenharmony_ci */ 35962306a36Sopenharmony_cistatic int do_fail_paste(void) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct pt_regs *regs = current->thread.regs; 36262306a36Sopenharmony_ci u32 instword; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci if (WARN_ON_ONCE(!regs)) 36562306a36Sopenharmony_ci return -EINVAL; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (WARN_ON_ONCE(!user_mode(regs))) 36862306a36Sopenharmony_ci return -EINVAL; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* 37162306a36Sopenharmony_ci * If we couldn't translate the instruction, the driver should 37262306a36Sopenharmony_ci * return success without handling the fault, it will be retried 37362306a36Sopenharmony_ci * or the instruction fetch will fault. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_ci if (get_user(instword, (u32 __user *)(regs->nip))) 37662306a36Sopenharmony_ci return -EAGAIN; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* 37962306a36Sopenharmony_ci * Not a paste instruction, driver may fail the fault. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ci if ((instword & PPC_INST_PASTE_MASK) != PPC_INST_PASTE) 38262306a36Sopenharmony_ci return -ENOENT; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci regs->ccr &= ~0xe0000000; /* Clear CR0[0-2] to fail paste */ 38562306a36Sopenharmony_ci regs_add_return_ip(regs, 4); /* Emulate the paste */ 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/* 39162306a36Sopenharmony_ci * This fault handler is invoked when the core generates page fault on 39262306a36Sopenharmony_ci * the paste address. Happens if the kernel closes window in hypervisor 39362306a36Sopenharmony_ci * (on pseries) due to lost credit or the paste address is not mapped. 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_cistatic vm_fault_t vas_mmap_fault(struct vm_fault *vmf) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 39862306a36Sopenharmony_ci struct file *fp = vma->vm_file; 39962306a36Sopenharmony_ci struct coproc_instance *cp_inst = fp->private_data; 40062306a36Sopenharmony_ci struct vas_window *txwin; 40162306a36Sopenharmony_ci vm_fault_t fault; 40262306a36Sopenharmony_ci u64 paste_addr; 40362306a36Sopenharmony_ci int ret; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* 40662306a36Sopenharmony_ci * window is not opened. Shouldn't expect this error. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci if (!cp_inst || !cp_inst->txwin) { 40962306a36Sopenharmony_ci pr_err("Unexpected fault on paste address with TX window closed\n"); 41062306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci txwin = cp_inst->txwin; 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * When the LPAR lost credits due to core removal or during 41662306a36Sopenharmony_ci * migration, invalidate the existing mapping for the current 41762306a36Sopenharmony_ci * paste addresses and set windows in-active (zap_vma_pages in 41862306a36Sopenharmony_ci * reconfig_close_windows()). 41962306a36Sopenharmony_ci * New mapping will be done later after migration or new credits 42062306a36Sopenharmony_ci * available. So continue to receive faults if the user space 42162306a36Sopenharmony_ci * issue NX request. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci if (txwin->task_ref.vma != vmf->vma) { 42462306a36Sopenharmony_ci pr_err("No previous mapping with paste address\n"); 42562306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci mutex_lock(&txwin->task_ref.mmap_mutex); 42962306a36Sopenharmony_ci /* 43062306a36Sopenharmony_ci * The window may be inactive due to lost credit (Ex: core 43162306a36Sopenharmony_ci * removal with DLPAR). If the window is active again when 43262306a36Sopenharmony_ci * the credit is available, map the new paste address at the 43362306a36Sopenharmony_ci * window virtual address. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ci if (txwin->status == VAS_WIN_ACTIVE) { 43662306a36Sopenharmony_ci paste_addr = cp_inst->coproc->vops->paste_addr(txwin); 43762306a36Sopenharmony_ci if (paste_addr) { 43862306a36Sopenharmony_ci fault = vmf_insert_pfn(vma, vma->vm_start, 43962306a36Sopenharmony_ci (paste_addr >> PAGE_SHIFT)); 44062306a36Sopenharmony_ci mutex_unlock(&txwin->task_ref.mmap_mutex); 44162306a36Sopenharmony_ci return fault; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci mutex_unlock(&txwin->task_ref.mmap_mutex); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* 44762306a36Sopenharmony_ci * Received this fault due to closing the actual window. 44862306a36Sopenharmony_ci * It can happen during migration or lost credits. 44962306a36Sopenharmony_ci * Since no mapping, return the paste instruction failure 45062306a36Sopenharmony_ci * to the user space. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci ret = do_fail_paste(); 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * The user space can retry several times until success (needed 45562306a36Sopenharmony_ci * for migration) or should fallback to SW compression or 45662306a36Sopenharmony_ci * manage with the existing open windows if available. 45762306a36Sopenharmony_ci * Looking at sysfs interface, it can determine whether these 45862306a36Sopenharmony_ci * failures are coming during migration or core removal: 45962306a36Sopenharmony_ci * nr_used_credits > nr_total_credits when lost credits 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci if (!ret || (ret == -EAGAIN)) 46262306a36Sopenharmony_ci return VM_FAULT_NOPAGE; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci return VM_FAULT_SIGBUS; 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_cistatic const struct vm_operations_struct vas_vm_ops = { 46862306a36Sopenharmony_ci .fault = vas_mmap_fault, 46962306a36Sopenharmony_ci}; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int coproc_mmap(struct file *fp, struct vm_area_struct *vma) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct coproc_instance *cp_inst = fp->private_data; 47462306a36Sopenharmony_ci struct vas_window *txwin; 47562306a36Sopenharmony_ci unsigned long pfn; 47662306a36Sopenharmony_ci u64 paste_addr; 47762306a36Sopenharmony_ci pgprot_t prot; 47862306a36Sopenharmony_ci int rc; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci txwin = cp_inst->txwin; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if ((vma->vm_end - vma->vm_start) > PAGE_SIZE) { 48362306a36Sopenharmony_ci pr_debug("size 0x%zx, PAGE_SIZE 0x%zx\n", 48462306a36Sopenharmony_ci (vma->vm_end - vma->vm_start), PAGE_SIZE); 48562306a36Sopenharmony_ci return -EINVAL; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Ensure instance has an open send window */ 48962306a36Sopenharmony_ci if (!txwin) { 49062306a36Sopenharmony_ci pr_err("No send window open?\n"); 49162306a36Sopenharmony_ci return -EINVAL; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (!cp_inst->coproc->vops || !cp_inst->coproc->vops->paste_addr) { 49562306a36Sopenharmony_ci pr_err("VAS API is not registered\n"); 49662306a36Sopenharmony_ci return -EACCES; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* 50062306a36Sopenharmony_ci * The initial mmap is done after the window is opened 50162306a36Sopenharmony_ci * with ioctl. But before mmap(), this window can be closed in 50262306a36Sopenharmony_ci * the hypervisor due to lost credit (core removal on pseries). 50362306a36Sopenharmony_ci * So if the window is not active, return mmap() failure with 50462306a36Sopenharmony_ci * -EACCES and expects the user space reissue mmap() when it 50562306a36Sopenharmony_ci * is active again or open new window when the credit is available. 50662306a36Sopenharmony_ci * mmap_mutex protects the paste address mmap() with DLPAR 50762306a36Sopenharmony_ci * close/open event and allows mmap() only when the window is 50862306a36Sopenharmony_ci * active. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci mutex_lock(&txwin->task_ref.mmap_mutex); 51162306a36Sopenharmony_ci if (txwin->status != VAS_WIN_ACTIVE) { 51262306a36Sopenharmony_ci pr_err("Window is not active\n"); 51362306a36Sopenharmony_ci rc = -EACCES; 51462306a36Sopenharmony_ci goto out; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci paste_addr = cp_inst->coproc->vops->paste_addr(txwin); 51862306a36Sopenharmony_ci if (!paste_addr) { 51962306a36Sopenharmony_ci pr_err("Window paste address failed\n"); 52062306a36Sopenharmony_ci rc = -EINVAL; 52162306a36Sopenharmony_ci goto out; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci pfn = paste_addr >> PAGE_SHIFT; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci /* flags, page_prot from cxl_mmap(), except we want cachable */ 52762306a36Sopenharmony_ci vm_flags_set(vma, VM_IO | VM_PFNMAP); 52862306a36Sopenharmony_ci vma->vm_page_prot = pgprot_cached(vma->vm_page_prot); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci prot = __pgprot(pgprot_val(vma->vm_page_prot) | _PAGE_DIRTY); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci rc = remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff, 53362306a36Sopenharmony_ci vma->vm_end - vma->vm_start, prot); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci pr_devel("paste addr %llx at %lx, rc %d\n", paste_addr, 53662306a36Sopenharmony_ci vma->vm_start, rc); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci txwin->task_ref.vma = vma; 53962306a36Sopenharmony_ci vma->vm_ops = &vas_vm_ops; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ciout: 54262306a36Sopenharmony_ci mutex_unlock(&txwin->task_ref.mmap_mutex); 54362306a36Sopenharmony_ci return rc; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic long coproc_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci switch (cmd) { 54962306a36Sopenharmony_ci case VAS_TX_WIN_OPEN: 55062306a36Sopenharmony_ci return coproc_ioc_tx_win_open(fp, arg); 55162306a36Sopenharmony_ci default: 55262306a36Sopenharmony_ci return -EINVAL; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic struct file_operations coproc_fops = { 55762306a36Sopenharmony_ci .open = coproc_open, 55862306a36Sopenharmony_ci .release = coproc_release, 55962306a36Sopenharmony_ci .mmap = coproc_mmap, 56062306a36Sopenharmony_ci .unlocked_ioctl = coproc_ioctl, 56162306a36Sopenharmony_ci}; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci/* 56462306a36Sopenharmony_ci * Supporting only nx-gzip coprocessor type now, but this API code 56562306a36Sopenharmony_ci * extended to other coprocessor types later. 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_ciint vas_register_coproc_api(struct module *mod, enum vas_cop_type cop_type, 56862306a36Sopenharmony_ci const char *name, 56962306a36Sopenharmony_ci const struct vas_user_win_ops *vops) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci int rc = -EINVAL; 57262306a36Sopenharmony_ci dev_t devno; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci rc = alloc_chrdev_region(&coproc_device.devt, 1, 1, name); 57562306a36Sopenharmony_ci if (rc) { 57662306a36Sopenharmony_ci pr_err("Unable to allocate coproc major number: %i\n", rc); 57762306a36Sopenharmony_ci return rc; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci pr_devel("%s device allocated, dev [%i,%i]\n", name, 58162306a36Sopenharmony_ci MAJOR(coproc_device.devt), MINOR(coproc_device.devt)); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci coproc_device.class = class_create(name); 58462306a36Sopenharmony_ci if (IS_ERR(coproc_device.class)) { 58562306a36Sopenharmony_ci rc = PTR_ERR(coproc_device.class); 58662306a36Sopenharmony_ci pr_err("Unable to create %s class %d\n", name, rc); 58762306a36Sopenharmony_ci goto err_class; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci coproc_device.class->devnode = coproc_devnode; 59062306a36Sopenharmony_ci coproc_device.cop_type = cop_type; 59162306a36Sopenharmony_ci coproc_device.vops = vops; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci coproc_fops.owner = mod; 59462306a36Sopenharmony_ci cdev_init(&coproc_device.cdev, &coproc_fops); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci devno = MKDEV(MAJOR(coproc_device.devt), 0); 59762306a36Sopenharmony_ci rc = cdev_add(&coproc_device.cdev, devno, 1); 59862306a36Sopenharmony_ci if (rc) { 59962306a36Sopenharmony_ci pr_err("cdev_add() failed %d\n", rc); 60062306a36Sopenharmony_ci goto err_cdev; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci coproc_device.device = device_create(coproc_device.class, NULL, 60462306a36Sopenharmony_ci devno, NULL, name, MINOR(devno)); 60562306a36Sopenharmony_ci if (IS_ERR(coproc_device.device)) { 60662306a36Sopenharmony_ci rc = PTR_ERR(coproc_device.device); 60762306a36Sopenharmony_ci pr_err("Unable to create coproc-%d %d\n", MINOR(devno), rc); 60862306a36Sopenharmony_ci goto err; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci pr_devel("Added dev [%d,%d]\n", MAJOR(devno), MINOR(devno)); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci return 0; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cierr: 61662306a36Sopenharmony_ci cdev_del(&coproc_device.cdev); 61762306a36Sopenharmony_cierr_cdev: 61862306a36Sopenharmony_ci class_destroy(coproc_device.class); 61962306a36Sopenharmony_cierr_class: 62062306a36Sopenharmony_ci unregister_chrdev_region(coproc_device.devt, 1); 62162306a36Sopenharmony_ci return rc; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_civoid vas_unregister_coproc_api(void) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci dev_t devno; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci cdev_del(&coproc_device.cdev); 62962306a36Sopenharmony_ci devno = MKDEV(MAJOR(coproc_device.devt), 0); 63062306a36Sopenharmony_ci device_destroy(coproc_device.class, devno); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci class_destroy(coproc_device.class); 63362306a36Sopenharmony_ci unregister_chrdev_region(coproc_device.devt, 1); 63462306a36Sopenharmony_ci} 635