| // SPDX-License-Identifier: GPL-2.0-or-later | 
|   | 
| /* | 
|  * Copyright 2020 IBM Corp. | 
|  * | 
|  * Author: Bulent Abali <abali@us.ibm.com> | 
|  * | 
|  */ | 
| #include <stdio.h> | 
| #include <stdlib.h> | 
| #include <string.h> | 
| #include <unistd.h> | 
| #include <stdint.h> | 
| #include <sys/types.h> | 
| #include <sys/stat.h> | 
| #include <sys/time.h> | 
| #include <sys/fcntl.h> | 
| #include <sys/mman.h> | 
| #include <endian.h> | 
| #include <bits/endian.h> | 
| #include <sys/ioctl.h> | 
| #include <assert.h> | 
| #include <errno.h> | 
| #include <signal.h> | 
| #include "vas-api.h" | 
| #include "nx.h" | 
| #include "copy-paste.h" | 
| #include "nxu.h" | 
| #include "nx_dbg.h" | 
| #include <sys/platform/ppc.h> | 
|   | 
| #define barrier() | 
| #define hwsync()    ({ asm volatile("sync" ::: "memory"); }) | 
|   | 
| #ifndef NX_NO_CPU_PRI | 
| #define cpu_pri_default()  ({ asm volatile ("or 2, 2, 2"); }) | 
| #define cpu_pri_low()      ({ asm volatile ("or 31, 31, 31"); }) | 
| #else | 
| #define cpu_pri_default() | 
| #define cpu_pri_low() | 
| #endif | 
|   | 
| void *nx_fault_storage_address; | 
|   | 
| struct nx_handle { | 
|     int fd; | 
|     int function; | 
|     void *paste_addr; | 
| }; | 
|   | 
| static int open_device_nodes(char *devname, int pri, struct nx_handle *handle) | 
| { | 
|     int rc, fd; | 
|     void *addr; | 
|     struct vas_tx_win_open_attr txattr; | 
|   | 
|     fd = open(devname, O_RDWR); | 
|     if (fd < 0) { | 
|         fprintf(stderr, " open device name %s\n", devname); | 
|         return -errno; | 
|     } | 
|   | 
|     memset(&txattr, 0, sizeof(txattr)); | 
|     txattr.version = 1; | 
|     txattr.vas_id = pri; | 
|     rc = ioctl(fd, VAS_TX_WIN_OPEN, (unsigned long)&txattr); | 
|     if (rc < 0) { | 
|         fprintf(stderr, "ioctl() n %d, error %d\n", rc, errno); | 
|         rc = -errno; | 
|         goto out; | 
|     } | 
|   | 
|     addr = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0ULL); | 
|     if (addr == MAP_FAILED) { | 
|         fprintf(stderr, "mmap() failed, errno %d\n", errno); | 
|         rc = -errno; | 
|         goto out; | 
|     } | 
|     handle->fd = fd; | 
|     handle->paste_addr = (void *)((char *)addr + 0x400); | 
|   | 
|     rc = 0; | 
| out: | 
|     close(fd); | 
|     return rc; | 
| } | 
|   | 
| void *nx_function_begin(int function, int pri) | 
| { | 
|     int rc; | 
|     char *devname = "/dev/crypto/nx-gzip"; | 
|     struct nx_handle *nxhandle; | 
|   | 
|     if (function != NX_FUNC_COMP_GZIP) { | 
|         errno = EINVAL; | 
|         fprintf(stderr, " NX_FUNC_COMP_GZIP not found\n"); | 
|         return NULL; | 
|     } | 
|   | 
|   | 
|     nxhandle = malloc(sizeof(*nxhandle)); | 
|     if (!nxhandle) { | 
|         errno = ENOMEM; | 
|         fprintf(stderr, " No memory\n"); | 
|         return NULL; | 
|     } | 
|   | 
|     nxhandle->function = function; | 
|     rc = open_device_nodes(devname, pri, nxhandle); | 
|     if (rc < 0) { | 
|         errno = -rc; | 
|         fprintf(stderr, " open_device_nodes failed\n"); | 
|         return NULL; | 
|     } | 
|   | 
|     return nxhandle; | 
| } | 
|   | 
| int nx_function_end(void *handle) | 
| { | 
|     int rc = 0; | 
|     struct nx_handle *nxhandle = handle; | 
|   | 
|     rc = munmap(nxhandle->paste_addr - 0x400, 4096); | 
|     if (rc < 0) { | 
|         fprintf(stderr, "munmap() failed, errno %d\n", errno); | 
|         return rc; | 
|     } | 
|     close(nxhandle->fd); | 
|     free(nxhandle); | 
|   | 
|     return rc; | 
| } | 
|   | 
| static int nx_wait_for_csb(struct nx_gzip_crb_cpb_t *cmdp) | 
| { | 
|     long poll = 0; | 
|     uint64_t t; | 
|   | 
|     /* Save power and let other threads use the h/w. top may show | 
|      * 100% but only because OS doesn't know we slowed the this | 
|      * h/w thread while polling. We're letting other threads have | 
|      * higher throughput on the core. | 
|      */ | 
|     cpu_pri_low(); | 
|   | 
| #define CSB_MAX_POLL 200000000UL | 
| #define USLEEP_TH     300000UL | 
|   | 
|     t = __ppc_get_timebase(); | 
|   | 
|     while (getnn(cmdp->crb.csb, csb_v) == 0) { | 
|         ++poll; | 
|         hwsync(); | 
|   | 
|         cpu_pri_low(); | 
|   | 
|         /* usleep(0) takes around 29000 ticks ~60 us. | 
|          * 300000 is spinning for about 600 us then | 
|          * start sleeping. | 
|          */ | 
|         if ((__ppc_get_timebase() - t) > USLEEP_TH) { | 
|             cpu_pri_default(); | 
|             usleep(1); | 
|         } | 
|   | 
|         if (poll > CSB_MAX_POLL) | 
|             break; | 
|   | 
|         /* Fault address from signal handler */ | 
|         if (nx_fault_storage_address) { | 
|             cpu_pri_default(); | 
|             return -EAGAIN; | 
|         } | 
|   | 
|     } | 
|   | 
|     cpu_pri_default(); | 
|   | 
|     /* hw has updated csb and output buffer */ | 
|     hwsync(); | 
|   | 
|     /* Check CSB flags. */ | 
|     if (getnn(cmdp->crb.csb, csb_v) == 0) { | 
|         fprintf(stderr, "CSB still not valid after %d polls.\n", | 
|             (int) poll); | 
|         prt_err("CSB still not valid after %d polls, giving up.\n", | 
|             (int) poll); | 
|         return -ETIMEDOUT; | 
|     } | 
|   | 
|     return 0; | 
| } | 
|   | 
| static int nxu_run_job(struct nx_gzip_crb_cpb_t *cmdp, void *handle) | 
| { | 
|     int i, ret, retries; | 
|     struct nx_handle *nxhandle = handle; | 
|   | 
|     assert(handle != NULL); | 
|     i = 0; | 
|     retries = 5000; | 
|     while (i++ < retries) { | 
|         hwsync(); | 
|         vas_copy(&cmdp->crb, 0); | 
|         ret = vas_paste(nxhandle->paste_addr, 0); | 
|         hwsync(); | 
|   | 
|         NXPRT(fprintf(stderr, "Paste attempt %d/%d returns 0x%x\n", | 
|                 i, retries, ret)); | 
|   | 
|         if ((ret == 2) || (ret == 3)) { | 
|   | 
|             ret = nx_wait_for_csb(cmdp); | 
|             if (!ret) { | 
|                 goto out; | 
|             } else if (ret == -EAGAIN) { | 
|                 long x; | 
|   | 
|                 prt_err("Touching address %p, 0x%lx\n", | 
|                      nx_fault_storage_address, | 
|                      *(long *) nx_fault_storage_address); | 
|                 x = *(long *) nx_fault_storage_address; | 
|                 *(long *) nx_fault_storage_address = x; | 
|                 nx_fault_storage_address = 0; | 
|                 continue; | 
|             } else { | 
|                 prt_err("wait_for_csb() returns %d\n", ret); | 
|                 break; | 
|             } | 
|         } else { | 
|             if (i < 10) { | 
|                 /* spin for few ticks */ | 
| #define SPIN_TH 500UL | 
|                 uint64_t fail_spin; | 
|   | 
|                 fail_spin = __ppc_get_timebase(); | 
|                 while ((__ppc_get_timebase() - fail_spin) < | 
|                      SPIN_TH) | 
|                     ; | 
|             } else { | 
|                 /* sleep */ | 
|                 unsigned int pr = 0; | 
|   | 
|                 if (pr++ % 100 == 0) { | 
|                     prt_err("Paste attempt %d/", i); | 
|                     prt_err("%d, failed pid= %d\n", retries, | 
|                         getpid()); | 
|                 } | 
|                 usleep(1); | 
|             } | 
|             continue; | 
|         } | 
|     } | 
|   | 
| out: | 
|     cpu_pri_default(); | 
|   | 
|     return ret; | 
| } | 
|   | 
| int nxu_submit_job(struct nx_gzip_crb_cpb_t *cmdp, void *handle) | 
| { | 
|     int cc; | 
|   | 
|     cc = nxu_run_job(cmdp, handle); | 
|   | 
|     if (!cc) | 
|         cc = getnn(cmdp->crb.csb, csb_cc);      /* CC Table 6-8 */ | 
|   | 
|     return cc; | 
| } | 
|   | 
|   | 
| void nxu_sigsegv_handler(int sig, siginfo_t *info, void *ctx) | 
| { | 
|     fprintf(stderr, "%d: Got signal %d si_code %d, si_addr %p\n", getpid(), | 
|         sig, info->si_code, info->si_addr); | 
|   | 
|     nx_fault_storage_address = info->si_addr; | 
| } | 
|   | 
| /* | 
|  * Fault in pages prior to NX job submission.  wr=1 may be required to | 
|  * touch writeable pages.  System zero pages do not fault-in the page as | 
|  * intended.  Typically set wr=1 for NX target pages and set wr=0 for NX | 
|  * source pages. | 
|  */ | 
| int nxu_touch_pages(void *buf, long buf_len, long page_len, int wr) | 
| { | 
|     char *begin = buf; | 
|     char *end = (char *) buf + buf_len - 1; | 
|     volatile char t; | 
|   | 
|     assert(buf_len >= 0 && !!buf); | 
|   | 
|     NXPRT(fprintf(stderr, "touch %p %p len 0x%lx wr=%d\n", buf, | 
|             (buf + buf_len), buf_len, wr)); | 
|   | 
|     if (buf_len <= 0 || buf == NULL) | 
|         return -1; | 
|   | 
|     do { | 
|         t = *begin; | 
|         if (wr) | 
|             *begin = t; | 
|         begin = begin + page_len; | 
|     } while (begin < end); | 
|   | 
|     /* When buf_sz is small or buf tail is in another page */ | 
|     t = *end; | 
|     if (wr) | 
|         *end = t; | 
|   | 
|     return 0; | 
| } |