18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SCLP "store data in absolute storage"
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2003, 2013
68c2ecf20Sopenharmony_ci * Author(s): Michael Holzheu
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "sclp_sdias"
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/completion.h>
138c2ecf20Sopenharmony_ci#include <linux/sched.h>
148c2ecf20Sopenharmony_ci#include <asm/sclp.h>
158c2ecf20Sopenharmony_ci#include <asm/debug.h>
168c2ecf20Sopenharmony_ci#include <asm/ipl.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "sclp_sdias.h"
198c2ecf20Sopenharmony_ci#include "sclp.h"
208c2ecf20Sopenharmony_ci#include "sclp_rw.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define SDIAS_RETRIES 300
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic struct debug_info *sdias_dbf;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic struct sclp_register sclp_sdias_register = {
298c2ecf20Sopenharmony_ci	.send_mask = EVTYP_SDIAS_MASK,
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic struct sdias_sccb *sclp_sdias_sccb;
338c2ecf20Sopenharmony_cistatic struct sdias_evbuf sdias_evbuf;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic DECLARE_COMPLETION(evbuf_accepted);
368c2ecf20Sopenharmony_cistatic DECLARE_COMPLETION(evbuf_done);
378c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(sdias_mutex);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/*
408c2ecf20Sopenharmony_ci * Called by SCLP base when read event data has been completed (async mode only)
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_cistatic void sclp_sdias_receiver_fn(struct evbuf_header *evbuf)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	memcpy(&sdias_evbuf, evbuf,
458c2ecf20Sopenharmony_ci	       min_t(unsigned long, sizeof(sdias_evbuf), evbuf->length));
468c2ecf20Sopenharmony_ci	complete(&evbuf_done);
478c2ecf20Sopenharmony_ci	TRACE("sclp_sdias_receiver_fn done\n");
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/*
518c2ecf20Sopenharmony_ci * Called by SCLP base when sdias event has been accepted
528c2ecf20Sopenharmony_ci */
538c2ecf20Sopenharmony_cistatic void sdias_callback(struct sclp_req *request, void *data)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	complete(&evbuf_accepted);
568c2ecf20Sopenharmony_ci	TRACE("callback done\n");
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int sdias_sclp_send(struct sclp_req *req)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct sdias_sccb *sccb = sclp_sdias_sccb;
628c2ecf20Sopenharmony_ci	int retries;
638c2ecf20Sopenharmony_ci	int rc;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	for (retries = SDIAS_RETRIES; retries; retries--) {
668c2ecf20Sopenharmony_ci		TRACE("add request\n");
678c2ecf20Sopenharmony_ci		rc = sclp_add_request(req);
688c2ecf20Sopenharmony_ci		if (rc) {
698c2ecf20Sopenharmony_ci			/* not initiated, wait some time and retry */
708c2ecf20Sopenharmony_ci			set_current_state(TASK_INTERRUPTIBLE);
718c2ecf20Sopenharmony_ci			TRACE("add request failed: rc = %i\n",rc);
728c2ecf20Sopenharmony_ci			schedule_timeout(msecs_to_jiffies(500));
738c2ecf20Sopenharmony_ci			continue;
748c2ecf20Sopenharmony_ci		}
758c2ecf20Sopenharmony_ci		/* initiated, wait for completion of service call */
768c2ecf20Sopenharmony_ci		wait_for_completion(&evbuf_accepted);
778c2ecf20Sopenharmony_ci		if (req->status == SCLP_REQ_FAILED) {
788c2ecf20Sopenharmony_ci			TRACE("sclp request failed\n");
798c2ecf20Sopenharmony_ci			continue;
808c2ecf20Sopenharmony_ci		}
818c2ecf20Sopenharmony_ci		/* if not accepted, retry */
828c2ecf20Sopenharmony_ci		if (!(sccb->evbuf.hdr.flags & 0x80)) {
838c2ecf20Sopenharmony_ci			TRACE("sclp request failed: flags=%x\n",
848c2ecf20Sopenharmony_ci			      sccb->evbuf.hdr.flags);
858c2ecf20Sopenharmony_ci			continue;
868c2ecf20Sopenharmony_ci		}
878c2ecf20Sopenharmony_ci		/*
888c2ecf20Sopenharmony_ci		 * for the sync interface the response is in the initial sccb
898c2ecf20Sopenharmony_ci		 */
908c2ecf20Sopenharmony_ci		if (!sclp_sdias_register.receiver_fn) {
918c2ecf20Sopenharmony_ci			memcpy(&sdias_evbuf, &sccb->evbuf, sizeof(sdias_evbuf));
928c2ecf20Sopenharmony_ci			TRACE("sync request done\n");
938c2ecf20Sopenharmony_ci			return 0;
948c2ecf20Sopenharmony_ci		}
958c2ecf20Sopenharmony_ci		/* otherwise we wait for completion */
968c2ecf20Sopenharmony_ci		wait_for_completion(&evbuf_done);
978c2ecf20Sopenharmony_ci		TRACE("request done\n");
988c2ecf20Sopenharmony_ci		return 0;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci	return -EIO;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci/*
1048c2ecf20Sopenharmony_ci * Get number of blocks (4K) available in the HSA
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_ciint sclp_sdias_blk_count(void)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct sdias_sccb *sccb = sclp_sdias_sccb;
1098c2ecf20Sopenharmony_ci	struct sclp_req request;
1108c2ecf20Sopenharmony_ci	int rc;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	mutex_lock(&sdias_mutex);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	memset(sccb, 0, sizeof(*sccb));
1158c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	sccb->hdr.length = sizeof(*sccb);
1188c2ecf20Sopenharmony_ci	sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
1198c2ecf20Sopenharmony_ci	sccb->evbuf.hdr.type = EVTYP_SDIAS;
1208c2ecf20Sopenharmony_ci	sccb->evbuf.event_qual = SDIAS_EQ_SIZE;
1218c2ecf20Sopenharmony_ci	sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
1228c2ecf20Sopenharmony_ci	sccb->evbuf.event_id = 4712;
1238c2ecf20Sopenharmony_ci	sccb->evbuf.dbs = 1;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	request.sccb = sccb;
1268c2ecf20Sopenharmony_ci	request.command = SCLP_CMDW_WRITE_EVENT_DATA;
1278c2ecf20Sopenharmony_ci	request.status = SCLP_REQ_FILLED;
1288c2ecf20Sopenharmony_ci	request.callback = sdias_callback;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	rc = sdias_sclp_send(&request);
1318c2ecf20Sopenharmony_ci	if (rc) {
1328c2ecf20Sopenharmony_ci		pr_err("sclp_send failed for get_nr_blocks\n");
1338c2ecf20Sopenharmony_ci		goto out;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci	if (sccb->hdr.response_code != 0x0020) {
1368c2ecf20Sopenharmony_ci		TRACE("send failed: %x\n", sccb->hdr.response_code);
1378c2ecf20Sopenharmony_ci		rc = -EIO;
1388c2ecf20Sopenharmony_ci		goto out;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	switch (sdias_evbuf.event_status) {
1428c2ecf20Sopenharmony_ci		case 0:
1438c2ecf20Sopenharmony_ci			rc = sdias_evbuf.blk_cnt;
1448c2ecf20Sopenharmony_ci			break;
1458c2ecf20Sopenharmony_ci		default:
1468c2ecf20Sopenharmony_ci			pr_err("SCLP error: %x\n", sdias_evbuf.event_status);
1478c2ecf20Sopenharmony_ci			rc = -EIO;
1488c2ecf20Sopenharmony_ci			goto out;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci	TRACE("%i blocks\n", rc);
1518c2ecf20Sopenharmony_ciout:
1528c2ecf20Sopenharmony_ci	mutex_unlock(&sdias_mutex);
1538c2ecf20Sopenharmony_ci	return rc;
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/*
1578c2ecf20Sopenharmony_ci * Copy from HSA to absolute storage (not reentrant):
1588c2ecf20Sopenharmony_ci *
1598c2ecf20Sopenharmony_ci * @dest     : Address of buffer where data should be copied
1608c2ecf20Sopenharmony_ci * @start_blk: Start Block (beginning with 1)
1618c2ecf20Sopenharmony_ci * @nr_blks  : Number of 4K blocks to copy
1628c2ecf20Sopenharmony_ci *
1638c2ecf20Sopenharmony_ci * Return Value: 0 : Requested 'number' of blocks of data copied
1648c2ecf20Sopenharmony_ci *		 <0: ERROR - negative event status
1658c2ecf20Sopenharmony_ci */
1668c2ecf20Sopenharmony_ciint sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct sdias_sccb *sccb = sclp_sdias_sccb;
1698c2ecf20Sopenharmony_ci	struct sclp_req request;
1708c2ecf20Sopenharmony_ci	int rc;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	mutex_lock(&sdias_mutex);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	memset(sccb, 0, sizeof(*sccb));
1758c2ecf20Sopenharmony_ci	memset(&request, 0, sizeof(request));
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	sccb->hdr.length = sizeof(*sccb);
1788c2ecf20Sopenharmony_ci	sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
1798c2ecf20Sopenharmony_ci	sccb->evbuf.hdr.type = EVTYP_SDIAS;
1808c2ecf20Sopenharmony_ci	sccb->evbuf.hdr.flags = 0;
1818c2ecf20Sopenharmony_ci	sccb->evbuf.event_qual = SDIAS_EQ_STORE_DATA;
1828c2ecf20Sopenharmony_ci	sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
1838c2ecf20Sopenharmony_ci	sccb->evbuf.event_id = 4712;
1848c2ecf20Sopenharmony_ci	sccb->evbuf.asa_size = SDIAS_ASA_SIZE_64;
1858c2ecf20Sopenharmony_ci	sccb->evbuf.event_status = 0;
1868c2ecf20Sopenharmony_ci	sccb->evbuf.blk_cnt = nr_blks;
1878c2ecf20Sopenharmony_ci	sccb->evbuf.asa = (unsigned long)dest;
1888c2ecf20Sopenharmony_ci	sccb->evbuf.fbn = start_blk;
1898c2ecf20Sopenharmony_ci	sccb->evbuf.lbn = 0;
1908c2ecf20Sopenharmony_ci	sccb->evbuf.dbs = 1;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	request.sccb	 = sccb;
1938c2ecf20Sopenharmony_ci	request.command  = SCLP_CMDW_WRITE_EVENT_DATA;
1948c2ecf20Sopenharmony_ci	request.status	 = SCLP_REQ_FILLED;
1958c2ecf20Sopenharmony_ci	request.callback = sdias_callback;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	rc = sdias_sclp_send(&request);
1988c2ecf20Sopenharmony_ci	if (rc) {
1998c2ecf20Sopenharmony_ci		pr_err("sclp_send failed: %x\n", rc);
2008c2ecf20Sopenharmony_ci		goto out;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	if (sccb->hdr.response_code != 0x0020) {
2038c2ecf20Sopenharmony_ci		TRACE("copy failed: %x\n", sccb->hdr.response_code);
2048c2ecf20Sopenharmony_ci		rc = -EIO;
2058c2ecf20Sopenharmony_ci		goto out;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	switch (sdias_evbuf.event_status) {
2098c2ecf20Sopenharmony_ci	case SDIAS_EVSTATE_ALL_STORED:
2108c2ecf20Sopenharmony_ci		TRACE("all stored\n");
2118c2ecf20Sopenharmony_ci		break;
2128c2ecf20Sopenharmony_ci	case SDIAS_EVSTATE_PART_STORED:
2138c2ecf20Sopenharmony_ci		TRACE("part stored: %i\n", sdias_evbuf.blk_cnt);
2148c2ecf20Sopenharmony_ci		break;
2158c2ecf20Sopenharmony_ci	case SDIAS_EVSTATE_NO_DATA:
2168c2ecf20Sopenharmony_ci		TRACE("no data\n");
2178c2ecf20Sopenharmony_ci		fallthrough;
2188c2ecf20Sopenharmony_ci	default:
2198c2ecf20Sopenharmony_ci		pr_err("Error from SCLP while copying hsa. Event status = %x\n",
2208c2ecf20Sopenharmony_ci		       sdias_evbuf.event_status);
2218c2ecf20Sopenharmony_ci		rc = -EIO;
2228c2ecf20Sopenharmony_ci	}
2238c2ecf20Sopenharmony_ciout:
2248c2ecf20Sopenharmony_ci	mutex_unlock(&sdias_mutex);
2258c2ecf20Sopenharmony_ci	return rc;
2268c2ecf20Sopenharmony_ci}
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_cistatic int __init sclp_sdias_register_check(void)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	int rc;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	rc = sclp_register(&sclp_sdias_register);
2338c2ecf20Sopenharmony_ci	if (rc)
2348c2ecf20Sopenharmony_ci		return rc;
2358c2ecf20Sopenharmony_ci	if (sclp_sdias_blk_count() == 0) {
2368c2ecf20Sopenharmony_ci		sclp_unregister(&sclp_sdias_register);
2378c2ecf20Sopenharmony_ci		return -ENODEV;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci	return 0;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic int __init sclp_sdias_init_sync(void)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	TRACE("Try synchronous mode\n");
2458c2ecf20Sopenharmony_ci	sclp_sdias_register.receive_mask = 0;
2468c2ecf20Sopenharmony_ci	sclp_sdias_register.receiver_fn = NULL;
2478c2ecf20Sopenharmony_ci	return sclp_sdias_register_check();
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic int __init sclp_sdias_init_async(void)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	TRACE("Try asynchronous mode\n");
2538c2ecf20Sopenharmony_ci	sclp_sdias_register.receive_mask = EVTYP_SDIAS_MASK;
2548c2ecf20Sopenharmony_ci	sclp_sdias_register.receiver_fn = sclp_sdias_receiver_fn;
2558c2ecf20Sopenharmony_ci	return sclp_sdias_register_check();
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ciint __init sclp_sdias_init(void)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	if (!is_ipl_type_dump())
2618c2ecf20Sopenharmony_ci		return 0;
2628c2ecf20Sopenharmony_ci	sclp_sdias_sccb = (void *) __get_free_page(GFP_KERNEL | GFP_DMA);
2638c2ecf20Sopenharmony_ci	BUG_ON(!sclp_sdias_sccb);
2648c2ecf20Sopenharmony_ci	sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));
2658c2ecf20Sopenharmony_ci	debug_register_view(sdias_dbf, &debug_sprintf_view);
2668c2ecf20Sopenharmony_ci	debug_set_level(sdias_dbf, 6);
2678c2ecf20Sopenharmony_ci	if (sclp_sdias_init_sync() == 0)
2688c2ecf20Sopenharmony_ci		goto out;
2698c2ecf20Sopenharmony_ci	if (sclp_sdias_init_async() == 0)
2708c2ecf20Sopenharmony_ci		goto out;
2718c2ecf20Sopenharmony_ci	TRACE("init failed\n");
2728c2ecf20Sopenharmony_ci	free_page((unsigned long) sclp_sdias_sccb);
2738c2ecf20Sopenharmony_ci	return -ENODEV;
2748c2ecf20Sopenharmony_ciout:
2758c2ecf20Sopenharmony_ci	TRACE("init done\n");
2768c2ecf20Sopenharmony_ci	return 0;
2778c2ecf20Sopenharmony_ci}
278