18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SN Platform GRU Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *            Dump GRU State
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright (c) 2008 Silicon Graphics, Inc.  All Rights Reserved.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/mm.h>
128c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
138c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/bitops.h>
168c2ecf20Sopenharmony_ci#include <asm/uv/uv_hub.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/nospec.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "gru.h"
218c2ecf20Sopenharmony_ci#include "grutables.h"
228c2ecf20Sopenharmony_ci#include "gruhandles.h"
238c2ecf20Sopenharmony_ci#include "grulib.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define CCH_LOCK_ATTEMPTS	10
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int gru_user_copy_handle(void __user **dp, void *s)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	if (copy_to_user(*dp, s, GRU_HANDLE_BYTES))
308c2ecf20Sopenharmony_ci		return -1;
318c2ecf20Sopenharmony_ci	*dp += GRU_HANDLE_BYTES;
328c2ecf20Sopenharmony_ci	return 0;
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int gru_dump_context_data(void *grubase,
368c2ecf20Sopenharmony_ci			struct gru_context_configuration_handle *cch,
378c2ecf20Sopenharmony_ci			void __user *ubuf, int ctxnum, int dsrcnt,
388c2ecf20Sopenharmony_ci			int flush_cbrs)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	void *cb, *cbe, *tfh, *gseg;
418c2ecf20Sopenharmony_ci	int i, scr;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	gseg = grubase + ctxnum * GRU_GSEG_STRIDE;
448c2ecf20Sopenharmony_ci	cb = gseg + GRU_CB_BASE;
458c2ecf20Sopenharmony_ci	cbe = grubase + GRU_CBE_BASE;
468c2ecf20Sopenharmony_ci	tfh = grubase + GRU_TFH_BASE;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) {
498c2ecf20Sopenharmony_ci		if (flush_cbrs)
508c2ecf20Sopenharmony_ci			gru_flush_cache(cb);
518c2ecf20Sopenharmony_ci		if (gru_user_copy_handle(&ubuf, cb))
528c2ecf20Sopenharmony_ci			goto fail;
538c2ecf20Sopenharmony_ci		if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE))
548c2ecf20Sopenharmony_ci			goto fail;
558c2ecf20Sopenharmony_ci		if (gru_user_copy_handle(&ubuf, cbe + i * GRU_HANDLE_STRIDE))
568c2ecf20Sopenharmony_ci			goto fail;
578c2ecf20Sopenharmony_ci		cb += GRU_HANDLE_STRIDE;
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci	if (dsrcnt)
608c2ecf20Sopenharmony_ci		memcpy(ubuf, gseg + GRU_DS_BASE, dsrcnt * GRU_HANDLE_STRIDE);
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cifail:
648c2ecf20Sopenharmony_ci	return -EFAULT;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic int gru_dump_tfm(struct gru_state *gru,
688c2ecf20Sopenharmony_ci		void __user *ubuf, void __user *ubufend)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct gru_tlb_fault_map *tfm;
718c2ecf20Sopenharmony_ci	int i;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	if (GRU_NUM_TFM * GRU_CACHE_LINE_BYTES > ubufend - ubuf)
748c2ecf20Sopenharmony_ci		return -EFBIG;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	for (i = 0; i < GRU_NUM_TFM; i++) {
778c2ecf20Sopenharmony_ci		tfm = get_tfm(gru->gs_gru_base_vaddr, i);
788c2ecf20Sopenharmony_ci		if (gru_user_copy_handle(&ubuf, tfm))
798c2ecf20Sopenharmony_ci			goto fail;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci	return GRU_NUM_TFM * GRU_CACHE_LINE_BYTES;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cifail:
848c2ecf20Sopenharmony_ci	return -EFAULT;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int gru_dump_tgh(struct gru_state *gru,
888c2ecf20Sopenharmony_ci		void __user *ubuf, void __user *ubufend)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct gru_tlb_global_handle *tgh;
918c2ecf20Sopenharmony_ci	int i;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (GRU_NUM_TGH * GRU_CACHE_LINE_BYTES > ubufend - ubuf)
948c2ecf20Sopenharmony_ci		return -EFBIG;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	for (i = 0; i < GRU_NUM_TGH; i++) {
978c2ecf20Sopenharmony_ci		tgh = get_tgh(gru->gs_gru_base_vaddr, i);
988c2ecf20Sopenharmony_ci		if (gru_user_copy_handle(&ubuf, tgh))
998c2ecf20Sopenharmony_ci			goto fail;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	return GRU_NUM_TGH * GRU_CACHE_LINE_BYTES;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cifail:
1048c2ecf20Sopenharmony_ci	return -EFAULT;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic int gru_dump_context(struct gru_state *gru, int ctxnum,
1088c2ecf20Sopenharmony_ci		void __user *ubuf, void __user *ubufend, char data_opt,
1098c2ecf20Sopenharmony_ci		char lock_cch, char flush_cbrs)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct gru_dump_context_header hdr;
1128c2ecf20Sopenharmony_ci	struct gru_dump_context_header __user *uhdr = ubuf;
1138c2ecf20Sopenharmony_ci	struct gru_context_configuration_handle *cch, *ubufcch;
1148c2ecf20Sopenharmony_ci	struct gru_thread_state *gts;
1158c2ecf20Sopenharmony_ci	int try, cch_locked, cbrcnt = 0, dsrcnt = 0, bytes = 0, ret = 0;
1168c2ecf20Sopenharmony_ci	void *grubase;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	memset(&hdr, 0, sizeof(hdr));
1198c2ecf20Sopenharmony_ci	grubase = gru->gs_gru_base_vaddr;
1208c2ecf20Sopenharmony_ci	cch = get_cch(grubase, ctxnum);
1218c2ecf20Sopenharmony_ci	for (try = 0; try < CCH_LOCK_ATTEMPTS; try++) {
1228c2ecf20Sopenharmony_ci		cch_locked =  trylock_cch_handle(cch);
1238c2ecf20Sopenharmony_ci		if (cch_locked)
1248c2ecf20Sopenharmony_ci			break;
1258c2ecf20Sopenharmony_ci		msleep(1);
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	ubuf += sizeof(hdr);
1298c2ecf20Sopenharmony_ci	ubufcch = ubuf;
1308c2ecf20Sopenharmony_ci	if (gru_user_copy_handle(&ubuf, cch)) {
1318c2ecf20Sopenharmony_ci		if (cch_locked)
1328c2ecf20Sopenharmony_ci			unlock_cch_handle(cch);
1338c2ecf20Sopenharmony_ci		return -EFAULT;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci	if (cch_locked)
1368c2ecf20Sopenharmony_ci		ubufcch->delresp = 0;
1378c2ecf20Sopenharmony_ci	bytes = sizeof(hdr) + GRU_CACHE_LINE_BYTES;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (cch_locked || !lock_cch) {
1408c2ecf20Sopenharmony_ci		gts = gru->gs_gts[ctxnum];
1418c2ecf20Sopenharmony_ci		if (gts && gts->ts_vma) {
1428c2ecf20Sopenharmony_ci			hdr.pid = gts->ts_tgid_owner;
1438c2ecf20Sopenharmony_ci			hdr.vaddr = gts->ts_vma->vm_start;
1448c2ecf20Sopenharmony_ci		}
1458c2ecf20Sopenharmony_ci		if (cch->state != CCHSTATE_INACTIVE) {
1468c2ecf20Sopenharmony_ci			cbrcnt = hweight64(cch->cbr_allocation_map) *
1478c2ecf20Sopenharmony_ci						GRU_CBR_AU_SIZE;
1488c2ecf20Sopenharmony_ci			dsrcnt = data_opt ? hweight32(cch->dsr_allocation_map) *
1498c2ecf20Sopenharmony_ci						GRU_DSR_AU_CL : 0;
1508c2ecf20Sopenharmony_ci		}
1518c2ecf20Sopenharmony_ci		bytes += (3 * cbrcnt + dsrcnt) * GRU_CACHE_LINE_BYTES;
1528c2ecf20Sopenharmony_ci		if (bytes > ubufend - ubuf)
1538c2ecf20Sopenharmony_ci			ret = -EFBIG;
1548c2ecf20Sopenharmony_ci		else
1558c2ecf20Sopenharmony_ci			ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum,
1568c2ecf20Sopenharmony_ci							dsrcnt, flush_cbrs);
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci	if (cch_locked)
1598c2ecf20Sopenharmony_ci		unlock_cch_handle(cch);
1608c2ecf20Sopenharmony_ci	if (ret)
1618c2ecf20Sopenharmony_ci		return ret;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	hdr.magic = GRU_DUMP_MAGIC;
1648c2ecf20Sopenharmony_ci	hdr.gid = gru->gs_gid;
1658c2ecf20Sopenharmony_ci	hdr.ctxnum = ctxnum;
1668c2ecf20Sopenharmony_ci	hdr.cbrcnt = cbrcnt;
1678c2ecf20Sopenharmony_ci	hdr.dsrcnt = dsrcnt;
1688c2ecf20Sopenharmony_ci	hdr.cch_locked = cch_locked;
1698c2ecf20Sopenharmony_ci	if (copy_to_user(uhdr, &hdr, sizeof(hdr)))
1708c2ecf20Sopenharmony_ci		return -EFAULT;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	return bytes;
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ciint gru_dump_chiplet_request(unsigned long arg)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct gru_state *gru;
1788c2ecf20Sopenharmony_ci	struct gru_dump_chiplet_state_req req;
1798c2ecf20Sopenharmony_ci	void __user *ubuf;
1808c2ecf20Sopenharmony_ci	void __user *ubufend;
1818c2ecf20Sopenharmony_ci	int ctxnum, ret, cnt = 0;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
1848c2ecf20Sopenharmony_ci		return -EFAULT;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* Currently, only dump by gid is implemented */
1878c2ecf20Sopenharmony_ci	if (req.gid >= gru_max_gids)
1888c2ecf20Sopenharmony_ci		return -EINVAL;
1898c2ecf20Sopenharmony_ci	req.gid = array_index_nospec(req.gid, gru_max_gids);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	gru = GID_TO_GRU(req.gid);
1928c2ecf20Sopenharmony_ci	ubuf = req.buf;
1938c2ecf20Sopenharmony_ci	ubufend = req.buf + req.buflen;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	ret = gru_dump_tfm(gru, ubuf, ubufend);
1968c2ecf20Sopenharmony_ci	if (ret < 0)
1978c2ecf20Sopenharmony_ci		goto fail;
1988c2ecf20Sopenharmony_ci	ubuf += ret;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	ret = gru_dump_tgh(gru, ubuf, ubufend);
2018c2ecf20Sopenharmony_ci	if (ret < 0)
2028c2ecf20Sopenharmony_ci		goto fail;
2038c2ecf20Sopenharmony_ci	ubuf += ret;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) {
2068c2ecf20Sopenharmony_ci		if (req.ctxnum == ctxnum || req.ctxnum < 0) {
2078c2ecf20Sopenharmony_ci			ret = gru_dump_context(gru, ctxnum, ubuf, ubufend,
2088c2ecf20Sopenharmony_ci						req.data_opt, req.lock_cch,
2098c2ecf20Sopenharmony_ci						req.flush_cbrs);
2108c2ecf20Sopenharmony_ci			if (ret < 0)
2118c2ecf20Sopenharmony_ci				goto fail;
2128c2ecf20Sopenharmony_ci			ubuf += ret;
2138c2ecf20Sopenharmony_ci			cnt++;
2148c2ecf20Sopenharmony_ci		}
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (copy_to_user((void __user *)arg, &req, sizeof(req)))
2188c2ecf20Sopenharmony_ci		return -EFAULT;
2198c2ecf20Sopenharmony_ci	return cnt;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cifail:
2228c2ecf20Sopenharmony_ci	return ret;
2238c2ecf20Sopenharmony_ci}
224