@@ -11,6 +11,7 @@
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>
+#include <linux/sched/signal.h>
#include <linux/mmu_context.h>
#include <asm/icswx.h>
@@ -25,6 +26,134 @@
*/
#define VAS_FAULT_WIN_FIFO_SIZE (4 << 20)
+static void notify_process(pid_t pid, u64 fault_addr)
+{
+ int rc;
+ struct kernel_siginfo info;
+
+ memset(&info, 0, sizeof(info));
+
+ info.si_signo = SIGSEGV;
+ info.si_errno = EFAULT;
+ info.si_code = SEGV_MAPERR;
+ info.si_addr = (void *)fault_addr;
+ /*
+ * process will be polling on csb.flags after request is sent to
+ * NX. So generally CSB update should not fail except when an
+ * application does not follow the process properly. So an error
+ * message will be displayed and leave it to user space whether
+ * to ignore or handle this signal.
+ */
+ rcu_read_lock();
+ rc = kill_pid_info(SIGSEGV, &info, find_vpid(pid));
+ rcu_read_unlock();
+
+ pr_devel("%s(): pid %d kill_proc_info() rc %d\n", __func__, pid, rc);
+}
+
+/*
+ * Update the CSB to indicate a translation error.
+ *
+ * If the fault is in the CSB address itself or if we are unable to
+ * update the CSB, send a signal to the process, because we have no
+ * other way of notifying the user process.
+ *
+ * Remaining settings in the CSB are based on wait_for_csb() of
+ * NX-GZIP.
+ */
+static void update_csb(struct vas_window *window,
+ struct coprocessor_request_block *crb)
+{
+ int rc;
+ pid_t pid;
+ int task_exit = 0;
+ void __user *csb_addr;
+ struct task_struct *tsk;
+ struct coprocessor_status_block csb;
+
+ /*
+ * NX user space windows can not be opened for task->mm=NULL
+ * and faults will not be generated for kernel requests.
+ */
+ if (!window->mm || !window->user_win)
+ return;
+
+ csb_addr = (void *)be64_to_cpu(crb->csb_addr);
+
+ csb.cc = CSB_CC_TRANSLATION;
+ csb.ce = CSB_CE_TERMINATION;
+ csb.cs = 0;
+ csb.count = 0;
+
+ /*
+ * Returns the fault address in CPU format since it is passed with
+ * signal. But if the user space expects BE format, need changes.
+ * i.e either kernel (here) or user should convert to CPU format.
+ * Not both!
+ */
+ csb.address = be64_to_cpu(crb->stamp.nx.fault_storage_addr);
+ csb.flags = 0;
+
+ use_mm(window->mm);
+ rc = copy_to_user(csb_addr, &csb, sizeof(csb));
+ /*
+ * User space polls on csb.flags (first byte). So add barrier
+ * then copy first byte with csb flags update.
+ */
+ smp_mb();
+ if (!rc) {
+ csb.flags = CSB_V;
+ rc = copy_to_user(csb_addr, &csb, sizeof(u8));
+ }
+ unuse_mm(window->mm);
+
+ /* Success */
+ if (!rc)
+ return;
+
+ /*
+ * User space passed invalid CSB address, Notify process with
+ * SEGV signal.
+ */
+ tsk = get_pid_task(window->pid, PIDTYPE_PID);
+ /*
+ * Send window will be closed after processing all NX requests
+ * and process exits after closing all windows. In multi-thread
+ * applications, thread may not exists, but does not close FD
+ * (means send window) upon exit. Parent thread (tgid) can use
+ * and close the window later.
+ * pid and mm references are taken when window is opened by
+ * process (pid). So tgid is used only when child thread is not
+ * available in multithread tasks.
+ *
+ */
+ if (tsk) {
+ if (tsk->flags & PF_EXITING)
+ task_exit = 1;
+ put_task_struct(tsk);
+ pid = vas_window_pid(window);
+ } else {
+ pid = window->tgid;
+
+ rcu_read_lock();
+ tsk = find_task_by_vpid(pid);
+ if (!tsk) {
+ rcu_read_unlock();
+ return;
+ }
+ if (tsk->flags & PF_EXITING)
+ task_exit = 1;
+ rcu_read_unlock();
+ }
+
+ /* Do not notify if the task is exiting. */
+ if (!task_exit) {
+ pr_err("Invalid CSB address 0x%p signalling pid(%d)\n",
+ csb_addr, pid);
+ notify_process(pid, (u64)csb_addr);
+ }
+}
+
/*
* Process CRBs that we receive on the fault window.
*/
@@ -102,6 +231,7 @@ irqreturn_t vas_fault_handler(int irq, void *data)
return IRQ_HANDLED;
}
+ update_csb(window, crb);
} while (true);
return IRQ_HANDLED;