18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/*
48c2ecf20Sopenharmony_ci * Copyright 2020 IBM Corp.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Author: Bulent Abali <abali@us.ibm.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <stdio.h>
108c2ecf20Sopenharmony_ci#include <stdlib.h>
118c2ecf20Sopenharmony_ci#include <string.h>
128c2ecf20Sopenharmony_ci#include <unistd.h>
138c2ecf20Sopenharmony_ci#include <stdint.h>
148c2ecf20Sopenharmony_ci#include <sys/types.h>
158c2ecf20Sopenharmony_ci#include <sys/stat.h>
168c2ecf20Sopenharmony_ci#include <sys/time.h>
178c2ecf20Sopenharmony_ci#include <sys/fcntl.h>
188c2ecf20Sopenharmony_ci#include <sys/mman.h>
198c2ecf20Sopenharmony_ci#include <endian.h>
208c2ecf20Sopenharmony_ci#include <bits/endian.h>
218c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
228c2ecf20Sopenharmony_ci#include <assert.h>
238c2ecf20Sopenharmony_ci#include <errno.h>
248c2ecf20Sopenharmony_ci#include <signal.h>
258c2ecf20Sopenharmony_ci#include "vas-api.h"
268c2ecf20Sopenharmony_ci#include "nx.h"
278c2ecf20Sopenharmony_ci#include "copy-paste.h"
288c2ecf20Sopenharmony_ci#include "nxu.h"
298c2ecf20Sopenharmony_ci#include "nx_dbg.h"
308c2ecf20Sopenharmony_ci#include <sys/platform/ppc.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define barrier()
338c2ecf20Sopenharmony_ci#define hwsync()    ({ asm volatile("sync" ::: "memory"); })
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#ifndef NX_NO_CPU_PRI
368c2ecf20Sopenharmony_ci#define cpu_pri_default()  ({ asm volatile ("or 2, 2, 2"); })
378c2ecf20Sopenharmony_ci#define cpu_pri_low()      ({ asm volatile ("or 31, 31, 31"); })
388c2ecf20Sopenharmony_ci#else
398c2ecf20Sopenharmony_ci#define cpu_pri_default()
408c2ecf20Sopenharmony_ci#define cpu_pri_low()
418c2ecf20Sopenharmony_ci#endif
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_civoid *nx_fault_storage_address;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistruct nx_handle {
468c2ecf20Sopenharmony_ci	int fd;
478c2ecf20Sopenharmony_ci	int function;
488c2ecf20Sopenharmony_ci	void *paste_addr;
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic int open_device_nodes(char *devname, int pri, struct nx_handle *handle)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	int rc, fd;
548c2ecf20Sopenharmony_ci	void *addr;
558c2ecf20Sopenharmony_ci	struct vas_tx_win_open_attr txattr;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	fd = open(devname, O_RDWR);
588c2ecf20Sopenharmony_ci	if (fd < 0) {
598c2ecf20Sopenharmony_ci		fprintf(stderr, " open device name %s\n", devname);
608c2ecf20Sopenharmony_ci		return -errno;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	memset(&txattr, 0, sizeof(txattr));
648c2ecf20Sopenharmony_ci	txattr.version = 1;
658c2ecf20Sopenharmony_ci	txattr.vas_id = pri;
668c2ecf20Sopenharmony_ci	rc = ioctl(fd, VAS_TX_WIN_OPEN, (unsigned long)&txattr);
678c2ecf20Sopenharmony_ci	if (rc < 0) {
688c2ecf20Sopenharmony_ci		fprintf(stderr, "ioctl() n %d, error %d\n", rc, errno);
698c2ecf20Sopenharmony_ci		rc = -errno;
708c2ecf20Sopenharmony_ci		goto out;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	addr = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0ULL);
748c2ecf20Sopenharmony_ci	if (addr == MAP_FAILED) {
758c2ecf20Sopenharmony_ci		fprintf(stderr, "mmap() failed, errno %d\n", errno);
768c2ecf20Sopenharmony_ci		rc = -errno;
778c2ecf20Sopenharmony_ci		goto out;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci	handle->fd = fd;
808c2ecf20Sopenharmony_ci	handle->paste_addr = (void *)((char *)addr + 0x400);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	rc = 0;
838c2ecf20Sopenharmony_ciout:
848c2ecf20Sopenharmony_ci	close(fd);
858c2ecf20Sopenharmony_ci	return rc;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_civoid *nx_function_begin(int function, int pri)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	int rc;
918c2ecf20Sopenharmony_ci	char *devname = "/dev/crypto/nx-gzip";
928c2ecf20Sopenharmony_ci	struct nx_handle *nxhandle;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (function != NX_FUNC_COMP_GZIP) {
958c2ecf20Sopenharmony_ci		errno = EINVAL;
968c2ecf20Sopenharmony_ci		fprintf(stderr, " NX_FUNC_COMP_GZIP not found\n");
978c2ecf20Sopenharmony_ci		return NULL;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	nxhandle = malloc(sizeof(*nxhandle));
1028c2ecf20Sopenharmony_ci	if (!nxhandle) {
1038c2ecf20Sopenharmony_ci		errno = ENOMEM;
1048c2ecf20Sopenharmony_ci		fprintf(stderr, " No memory\n");
1058c2ecf20Sopenharmony_ci		return NULL;
1068c2ecf20Sopenharmony_ci	}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	nxhandle->function = function;
1098c2ecf20Sopenharmony_ci	rc = open_device_nodes(devname, pri, nxhandle);
1108c2ecf20Sopenharmony_ci	if (rc < 0) {
1118c2ecf20Sopenharmony_ci		errno = -rc;
1128c2ecf20Sopenharmony_ci		fprintf(stderr, " open_device_nodes failed\n");
1138c2ecf20Sopenharmony_ci		return NULL;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return nxhandle;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ciint nx_function_end(void *handle)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	int rc = 0;
1228c2ecf20Sopenharmony_ci	struct nx_handle *nxhandle = handle;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	rc = munmap(nxhandle->paste_addr - 0x400, 4096);
1258c2ecf20Sopenharmony_ci	if (rc < 0) {
1268c2ecf20Sopenharmony_ci		fprintf(stderr, "munmap() failed, errno %d\n", errno);
1278c2ecf20Sopenharmony_ci		return rc;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	close(nxhandle->fd);
1308c2ecf20Sopenharmony_ci	free(nxhandle);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return rc;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int nx_wait_for_csb(struct nx_gzip_crb_cpb_t *cmdp)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	long poll = 0;
1388c2ecf20Sopenharmony_ci	uint64_t t;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	/* Save power and let other threads use the h/w. top may show
1418c2ecf20Sopenharmony_ci	 * 100% but only because OS doesn't know we slowed the this
1428c2ecf20Sopenharmony_ci	 * h/w thread while polling. We're letting other threads have
1438c2ecf20Sopenharmony_ci	 * higher throughput on the core.
1448c2ecf20Sopenharmony_ci	 */
1458c2ecf20Sopenharmony_ci	cpu_pri_low();
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci#define CSB_MAX_POLL 200000000UL
1488c2ecf20Sopenharmony_ci#define USLEEP_TH     300000UL
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	t = __ppc_get_timebase();
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	while (getnn(cmdp->crb.csb, csb_v) == 0) {
1538c2ecf20Sopenharmony_ci		++poll;
1548c2ecf20Sopenharmony_ci		hwsync();
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci		cpu_pri_low();
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		/* usleep(0) takes around 29000 ticks ~60 us.
1598c2ecf20Sopenharmony_ci		 * 300000 is spinning for about 600 us then
1608c2ecf20Sopenharmony_ci		 * start sleeping.
1618c2ecf20Sopenharmony_ci		 */
1628c2ecf20Sopenharmony_ci		if ((__ppc_get_timebase() - t) > USLEEP_TH) {
1638c2ecf20Sopenharmony_ci			cpu_pri_default();
1648c2ecf20Sopenharmony_ci			usleep(1);
1658c2ecf20Sopenharmony_ci		}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		if (poll > CSB_MAX_POLL)
1688c2ecf20Sopenharmony_ci			break;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		/* Fault address from signal handler */
1718c2ecf20Sopenharmony_ci		if (nx_fault_storage_address) {
1728c2ecf20Sopenharmony_ci			cpu_pri_default();
1738c2ecf20Sopenharmony_ci			return -EAGAIN;
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	cpu_pri_default();
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* hw has updated csb and output buffer */
1818c2ecf20Sopenharmony_ci	hwsync();
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* Check CSB flags. */
1848c2ecf20Sopenharmony_ci	if (getnn(cmdp->crb.csb, csb_v) == 0) {
1858c2ecf20Sopenharmony_ci		fprintf(stderr, "CSB still not valid after %d polls.\n",
1868c2ecf20Sopenharmony_ci			(int) poll);
1878c2ecf20Sopenharmony_ci		prt_err("CSB still not valid after %d polls, giving up.\n",
1888c2ecf20Sopenharmony_ci			(int) poll);
1898c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	return 0;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int nxu_run_job(struct nx_gzip_crb_cpb_t *cmdp, void *handle)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	int i, ret, retries;
1988c2ecf20Sopenharmony_ci	struct nx_handle *nxhandle = handle;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	assert(handle != NULL);
2018c2ecf20Sopenharmony_ci	i = 0;
2028c2ecf20Sopenharmony_ci	retries = 5000;
2038c2ecf20Sopenharmony_ci	while (i++ < retries) {
2048c2ecf20Sopenharmony_ci		hwsync();
2058c2ecf20Sopenharmony_ci		vas_copy(&cmdp->crb, 0);
2068c2ecf20Sopenharmony_ci		ret = vas_paste(nxhandle->paste_addr, 0);
2078c2ecf20Sopenharmony_ci		hwsync();
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci		NXPRT(fprintf(stderr, "Paste attempt %d/%d returns 0x%x\n",
2108c2ecf20Sopenharmony_ci				i, retries, ret));
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		if ((ret == 2) || (ret == 3)) {
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci			ret = nx_wait_for_csb(cmdp);
2158c2ecf20Sopenharmony_ci			if (!ret) {
2168c2ecf20Sopenharmony_ci				goto out;
2178c2ecf20Sopenharmony_ci			} else if (ret == -EAGAIN) {
2188c2ecf20Sopenharmony_ci				long x;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci				prt_err("Touching address %p, 0x%lx\n",
2218c2ecf20Sopenharmony_ci					 nx_fault_storage_address,
2228c2ecf20Sopenharmony_ci					 *(long *) nx_fault_storage_address);
2238c2ecf20Sopenharmony_ci				x = *(long *) nx_fault_storage_address;
2248c2ecf20Sopenharmony_ci				*(long *) nx_fault_storage_address = x;
2258c2ecf20Sopenharmony_ci				nx_fault_storage_address = 0;
2268c2ecf20Sopenharmony_ci				continue;
2278c2ecf20Sopenharmony_ci			} else {
2288c2ecf20Sopenharmony_ci				prt_err("wait_for_csb() returns %d\n", ret);
2298c2ecf20Sopenharmony_ci				break;
2308c2ecf20Sopenharmony_ci			}
2318c2ecf20Sopenharmony_ci		} else {
2328c2ecf20Sopenharmony_ci			if (i < 10) {
2338c2ecf20Sopenharmony_ci				/* spin for few ticks */
2348c2ecf20Sopenharmony_ci#define SPIN_TH 500UL
2358c2ecf20Sopenharmony_ci				uint64_t fail_spin;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci				fail_spin = __ppc_get_timebase();
2388c2ecf20Sopenharmony_ci				while ((__ppc_get_timebase() - fail_spin) <
2398c2ecf20Sopenharmony_ci					 SPIN_TH)
2408c2ecf20Sopenharmony_ci					;
2418c2ecf20Sopenharmony_ci			} else {
2428c2ecf20Sopenharmony_ci				/* sleep */
2438c2ecf20Sopenharmony_ci				unsigned int pr = 0;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci				if (pr++ % 100 == 0) {
2468c2ecf20Sopenharmony_ci					prt_err("Paste attempt %d/", i);
2478c2ecf20Sopenharmony_ci					prt_err("%d, failed pid= %d\n", retries,
2488c2ecf20Sopenharmony_ci						getpid());
2498c2ecf20Sopenharmony_ci				}
2508c2ecf20Sopenharmony_ci				usleep(1);
2518c2ecf20Sopenharmony_ci			}
2528c2ecf20Sopenharmony_ci			continue;
2538c2ecf20Sopenharmony_ci		}
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ciout:
2578c2ecf20Sopenharmony_ci	cpu_pri_default();
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	return ret;
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ciint nxu_submit_job(struct nx_gzip_crb_cpb_t *cmdp, void *handle)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	int cc;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	cc = nxu_run_job(cmdp, handle);
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (!cc)
2698c2ecf20Sopenharmony_ci		cc = getnn(cmdp->crb.csb, csb_cc);      /* CC Table 6-8 */
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	return cc;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_civoid nxu_sigsegv_handler(int sig, siginfo_t *info, void *ctx)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	fprintf(stderr, "%d: Got signal %d si_code %d, si_addr %p\n", getpid(),
2788c2ecf20Sopenharmony_ci		sig, info->si_code, info->si_addr);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	nx_fault_storage_address = info->si_addr;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci/*
2848c2ecf20Sopenharmony_ci * Fault in pages prior to NX job submission.  wr=1 may be required to
2858c2ecf20Sopenharmony_ci * touch writeable pages.  System zero pages do not fault-in the page as
2868c2ecf20Sopenharmony_ci * intended.  Typically set wr=1 for NX target pages and set wr=0 for NX
2878c2ecf20Sopenharmony_ci * source pages.
2888c2ecf20Sopenharmony_ci */
2898c2ecf20Sopenharmony_ciint nxu_touch_pages(void *buf, long buf_len, long page_len, int wr)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	char *begin = buf;
2928c2ecf20Sopenharmony_ci	char *end = (char *) buf + buf_len - 1;
2938c2ecf20Sopenharmony_ci	volatile char t;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	assert(buf_len >= 0 && !!buf);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	NXPRT(fprintf(stderr, "touch %p %p len 0x%lx wr=%d\n", buf,
2988c2ecf20Sopenharmony_ci			(buf + buf_len), buf_len, wr));
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	if (buf_len <= 0 || buf == NULL)
3018c2ecf20Sopenharmony_ci		return -1;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	do {
3048c2ecf20Sopenharmony_ci		t = *begin;
3058c2ecf20Sopenharmony_ci		if (wr)
3068c2ecf20Sopenharmony_ci			*begin = t;
3078c2ecf20Sopenharmony_ci		begin = begin + page_len;
3088c2ecf20Sopenharmony_ci	} while (begin < end);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* When buf_sz is small or buf tail is in another page */
3118c2ecf20Sopenharmony_ci	t = *end;
3128c2ecf20Sopenharmony_ci	if (wr)
3138c2ecf20Sopenharmony_ci		*end = t;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	return 0;
3168c2ecf20Sopenharmony_ci}
317