xref: /linux/drivers/misc/sgi-gru/grukdump.c (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29cc9b056SJack Steiner /*
39cc9b056SJack Steiner  * SN Platform GRU Driver
49cc9b056SJack Steiner  *
59cc9b056SJack Steiner  *            Dump GRU State
69cc9b056SJack Steiner  *
79cc9b056SJack Steiner  *  Copyright (c) 2008 Silicon Graphics, Inc.  All Rights Reserved.
89cc9b056SJack Steiner  */
99cc9b056SJack Steiner 
109cc9b056SJack Steiner #include <linux/kernel.h>
119cc9b056SJack Steiner #include <linux/mm.h>
129cc9b056SJack Steiner #include <linux/spinlock.h>
139cc9b056SJack Steiner #include <linux/uaccess.h>
149cc9b056SJack Steiner #include <linux/delay.h>
159cc9b056SJack Steiner #include <linux/bitops.h>
169cc9b056SJack Steiner #include <asm/uv/uv_hub.h>
17fee05f45SGustavo A. R. Silva 
18fee05f45SGustavo A. R. Silva #include <linux/nospec.h>
19fee05f45SGustavo A. R. Silva 
209cc9b056SJack Steiner #include "gru.h"
219cc9b056SJack Steiner #include "grutables.h"
229cc9b056SJack Steiner #include "gruhandles.h"
239cc9b056SJack Steiner #include "grulib.h"
249cc9b056SJack Steiner 
259cc9b056SJack Steiner #define CCH_LOCK_ATTEMPTS	10
269cc9b056SJack Steiner 
gru_user_copy_handle(void __user ** dp,void * s)279cc9b056SJack Steiner static int gru_user_copy_handle(void __user **dp, void *s)
289cc9b056SJack Steiner {
292b702b28SJack Steiner 	if (copy_to_user(*dp, s, GRU_HANDLE_BYTES))
309cc9b056SJack Steiner 		return -1;
319cc9b056SJack Steiner 	*dp += GRU_HANDLE_BYTES;
329cc9b056SJack Steiner 	return 0;
339cc9b056SJack Steiner }
349cc9b056SJack Steiner 
gru_dump_context_data(void * grubase,struct gru_context_configuration_handle * cch,void __user * ubuf,int ctxnum,int dsrcnt,int flush_cbrs)359cc9b056SJack Steiner static int gru_dump_context_data(void *grubase,
369cc9b056SJack Steiner 			struct gru_context_configuration_handle *cch,
37b8229bedSJack Steiner 			void __user *ubuf, int ctxnum, int dsrcnt,
38b8229bedSJack Steiner 			int flush_cbrs)
399cc9b056SJack Steiner {
409cc9b056SJack Steiner 	void *cb, *cbe, *tfh, *gseg;
419cc9b056SJack Steiner 	int i, scr;
429cc9b056SJack Steiner 
439cc9b056SJack Steiner 	gseg = grubase + ctxnum * GRU_GSEG_STRIDE;
449cc9b056SJack Steiner 	cb = gseg + GRU_CB_BASE;
459cc9b056SJack Steiner 	cbe = grubase + GRU_CBE_BASE;
469cc9b056SJack Steiner 	tfh = grubase + GRU_TFH_BASE;
479cc9b056SJack Steiner 
489cc9b056SJack Steiner 	for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) {
49b8229bedSJack Steiner 		if (flush_cbrs)
50b8229bedSJack Steiner 			gru_flush_cache(cb);
519cc9b056SJack Steiner 		if (gru_user_copy_handle(&ubuf, cb))
529cc9b056SJack Steiner 			goto fail;
539cc9b056SJack Steiner 		if (gru_user_copy_handle(&ubuf, tfh + i * GRU_HANDLE_STRIDE))
549cc9b056SJack Steiner 			goto fail;
559cc9b056SJack Steiner 		if (gru_user_copy_handle(&ubuf, cbe + i * GRU_HANDLE_STRIDE))
569cc9b056SJack Steiner 			goto fail;
579cc9b056SJack Steiner 		cb += GRU_HANDLE_STRIDE;
589cc9b056SJack Steiner 	}
599cc9b056SJack Steiner 	if (dsrcnt)
609cc9b056SJack Steiner 		memcpy(ubuf, gseg + GRU_DS_BASE, dsrcnt * GRU_HANDLE_STRIDE);
619cc9b056SJack Steiner 	return 0;
629cc9b056SJack Steiner 
639cc9b056SJack Steiner fail:
649cc9b056SJack Steiner 	return -EFAULT;
659cc9b056SJack Steiner }
669cc9b056SJack Steiner 
gru_dump_tfm(struct gru_state * gru,void __user * ubuf,void __user * ubufend)679cc9b056SJack Steiner static int gru_dump_tfm(struct gru_state *gru,
689cc9b056SJack Steiner 		void __user *ubuf, void __user *ubufend)
699cc9b056SJack Steiner {
709cc9b056SJack Steiner 	struct gru_tlb_fault_map *tfm;
71a010d276SSudip Mukherjee 	int i;
729cc9b056SJack Steiner 
73a010d276SSudip Mukherjee 	if (GRU_NUM_TFM * GRU_CACHE_LINE_BYTES > ubufend - ubuf)
74a010d276SSudip Mukherjee 		return -EFBIG;
759cc9b056SJack Steiner 
769cc9b056SJack Steiner 	for (i = 0; i < GRU_NUM_TFM; i++) {
779cc9b056SJack Steiner 		tfm = get_tfm(gru->gs_gru_base_vaddr, i);
789cc9b056SJack Steiner 		if (gru_user_copy_handle(&ubuf, tfm))
799cc9b056SJack Steiner 			goto fail;
809cc9b056SJack Steiner 	}
819cc9b056SJack Steiner 	return GRU_NUM_TFM * GRU_CACHE_LINE_BYTES;
829cc9b056SJack Steiner 
839cc9b056SJack Steiner fail:
849cc9b056SJack Steiner 	return -EFAULT;
859cc9b056SJack Steiner }
869cc9b056SJack Steiner 
gru_dump_tgh(struct gru_state * gru,void __user * ubuf,void __user * ubufend)879cc9b056SJack Steiner static int gru_dump_tgh(struct gru_state *gru,
889cc9b056SJack Steiner 		void __user *ubuf, void __user *ubufend)
899cc9b056SJack Steiner {
909cc9b056SJack Steiner 	struct gru_tlb_global_handle *tgh;
91a010d276SSudip Mukherjee 	int i;
929cc9b056SJack Steiner 
93a010d276SSudip Mukherjee 	if (GRU_NUM_TGH * GRU_CACHE_LINE_BYTES > ubufend - ubuf)
94a010d276SSudip Mukherjee 		return -EFBIG;
959cc9b056SJack Steiner 
969cc9b056SJack Steiner 	for (i = 0; i < GRU_NUM_TGH; i++) {
979cc9b056SJack Steiner 		tgh = get_tgh(gru->gs_gru_base_vaddr, i);
989cc9b056SJack Steiner 		if (gru_user_copy_handle(&ubuf, tgh))
999cc9b056SJack Steiner 			goto fail;
1009cc9b056SJack Steiner 	}
1019cc9b056SJack Steiner 	return GRU_NUM_TGH * GRU_CACHE_LINE_BYTES;
1029cc9b056SJack Steiner 
1039cc9b056SJack Steiner fail:
1049cc9b056SJack Steiner 	return -EFAULT;
1059cc9b056SJack Steiner }
1069cc9b056SJack Steiner 
gru_dump_context(struct gru_state * gru,int ctxnum,void __user * ubuf,void __user * ubufend,char data_opt,char lock_cch,char flush_cbrs)1079cc9b056SJack Steiner static int gru_dump_context(struct gru_state *gru, int ctxnum,
1089cc9b056SJack Steiner 		void __user *ubuf, void __user *ubufend, char data_opt,
109b8229bedSJack Steiner 		char lock_cch, char flush_cbrs)
1109cc9b056SJack Steiner {
1119cc9b056SJack Steiner 	struct gru_dump_context_header hdr;
1129cc9b056SJack Steiner 	struct gru_dump_context_header __user *uhdr = ubuf;
1132b702b28SJack Steiner 	struct gru_context_configuration_handle *cch, *ubufcch;
1149cc9b056SJack Steiner 	struct gru_thread_state *gts;
1159cc9b056SJack Steiner 	int try, cch_locked, cbrcnt = 0, dsrcnt = 0, bytes = 0, ret = 0;
1169cc9b056SJack Steiner 	void *grubase;
1179cc9b056SJack Steiner 
1189cc9b056SJack Steiner 	memset(&hdr, 0, sizeof(hdr));
1199cc9b056SJack Steiner 	grubase = gru->gs_gru_base_vaddr;
1209cc9b056SJack Steiner 	cch = get_cch(grubase, ctxnum);
1219cc9b056SJack Steiner 	for (try = 0; try < CCH_LOCK_ATTEMPTS; try++) {
1229cc9b056SJack Steiner 		cch_locked =  trylock_cch_handle(cch);
1239cc9b056SJack Steiner 		if (cch_locked)
1249cc9b056SJack Steiner 			break;
1259cc9b056SJack Steiner 		msleep(1);
1269cc9b056SJack Steiner 	}
1279cc9b056SJack Steiner 
1289cc9b056SJack Steiner 	ubuf += sizeof(hdr);
1292b702b28SJack Steiner 	ubufcch = ubuf;
13049d3d6c3SDan Carpenter 	if (gru_user_copy_handle(&ubuf, cch)) {
13149d3d6c3SDan Carpenter 		if (cch_locked)
13249d3d6c3SDan Carpenter 			unlock_cch_handle(cch);
13349d3d6c3SDan Carpenter 		return -EFAULT;
13449d3d6c3SDan Carpenter 	}
1352b702b28SJack Steiner 	if (cch_locked)
1362b702b28SJack Steiner 		ubufcch->delresp = 0;
1379cc9b056SJack Steiner 	bytes = sizeof(hdr) + GRU_CACHE_LINE_BYTES;
1389cc9b056SJack Steiner 
1399cc9b056SJack Steiner 	if (cch_locked || !lock_cch) {
1409cc9b056SJack Steiner 		gts = gru->gs_gts[ctxnum];
141836ce679SJack Steiner 		if (gts && gts->ts_vma) {
1429cc9b056SJack Steiner 			hdr.pid = gts->ts_tgid_owner;
1439cc9b056SJack Steiner 			hdr.vaddr = gts->ts_vma->vm_start;
1449cc9b056SJack Steiner 		}
1459cc9b056SJack Steiner 		if (cch->state != CCHSTATE_INACTIVE) {
1469cc9b056SJack Steiner 			cbrcnt = hweight64(cch->cbr_allocation_map) *
1479cc9b056SJack Steiner 						GRU_CBR_AU_SIZE;
1489cc9b056SJack Steiner 			dsrcnt = data_opt ? hweight32(cch->dsr_allocation_map) *
1499cc9b056SJack Steiner 						GRU_DSR_AU_CL : 0;
1509cc9b056SJack Steiner 		}
1519cc9b056SJack Steiner 		bytes += (3 * cbrcnt + dsrcnt) * GRU_CACHE_LINE_BYTES;
1529cc9b056SJack Steiner 		if (bytes > ubufend - ubuf)
1539cc9b056SJack Steiner 			ret = -EFBIG;
1549cc9b056SJack Steiner 		else
1559cc9b056SJack Steiner 			ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum,
156b8229bedSJack Steiner 							dsrcnt, flush_cbrs);
1579cc9b056SJack Steiner 	}
1589cc9b056SJack Steiner 	if (cch_locked)
1599cc9b056SJack Steiner 		unlock_cch_handle(cch);
1609cc9b056SJack Steiner 	if (ret)
1619cc9b056SJack Steiner 		return ret;
1629cc9b056SJack Steiner 
1639cc9b056SJack Steiner 	hdr.magic = GRU_DUMP_MAGIC;
1642b702b28SJack Steiner 	hdr.gid = gru->gs_gid;
1659cc9b056SJack Steiner 	hdr.ctxnum = ctxnum;
1669cc9b056SJack Steiner 	hdr.cbrcnt = cbrcnt;
1679cc9b056SJack Steiner 	hdr.dsrcnt = dsrcnt;
1689cc9b056SJack Steiner 	hdr.cch_locked = cch_locked;
169b6a83d92SDan Carpenter 	if (copy_to_user(uhdr, &hdr, sizeof(hdr)))
170b6a83d92SDan Carpenter 		return -EFAULT;
1719cc9b056SJack Steiner 
172b6a83d92SDan Carpenter 	return bytes;
1739cc9b056SJack Steiner }
1749cc9b056SJack Steiner 
gru_dump_chiplet_request(unsigned long arg)1759cc9b056SJack Steiner int gru_dump_chiplet_request(unsigned long arg)
1769cc9b056SJack Steiner {
1779cc9b056SJack Steiner 	struct gru_state *gru;
1789cc9b056SJack Steiner 	struct gru_dump_chiplet_state_req req;
1799cc9b056SJack Steiner 	void __user *ubuf;
1809cc9b056SJack Steiner 	void __user *ubufend;
1819cc9b056SJack Steiner 	int ctxnum, ret, cnt = 0;
1829cc9b056SJack Steiner 
1839cc9b056SJack Steiner 	if (copy_from_user(&req, (void __user *)arg, sizeof(req)))
1849cc9b056SJack Steiner 		return -EFAULT;
1859cc9b056SJack Steiner 
1869cc9b056SJack Steiner 	/* Currently, only dump by gid is implemented */
187c2ed545cSSudip Mukherjee 	if (req.gid >= gru_max_gids)
1889cc9b056SJack Steiner 		return -EINVAL;
189fee05f45SGustavo A. R. Silva 	req.gid = array_index_nospec(req.gid, gru_max_gids);
1909cc9b056SJack Steiner 
1919cc9b056SJack Steiner 	gru = GID_TO_GRU(req.gid);
1929cc9b056SJack Steiner 	ubuf = req.buf;
1939cc9b056SJack Steiner 	ubufend = req.buf + req.buflen;
1949cc9b056SJack Steiner 
1959cc9b056SJack Steiner 	ret = gru_dump_tfm(gru, ubuf, ubufend);
1969cc9b056SJack Steiner 	if (ret < 0)
1979cc9b056SJack Steiner 		goto fail;
1989cc9b056SJack Steiner 	ubuf += ret;
1999cc9b056SJack Steiner 
2009cc9b056SJack Steiner 	ret = gru_dump_tgh(gru, ubuf, ubufend);
2019cc9b056SJack Steiner 	if (ret < 0)
2029cc9b056SJack Steiner 		goto fail;
2039cc9b056SJack Steiner 	ubuf += ret;
2049cc9b056SJack Steiner 
2059cc9b056SJack Steiner 	for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) {
2069cc9b056SJack Steiner 		if (req.ctxnum == ctxnum || req.ctxnum < 0) {
2079cc9b056SJack Steiner 			ret = gru_dump_context(gru, ctxnum, ubuf, ubufend,
208b8229bedSJack Steiner 						req.data_opt, req.lock_cch,
209b8229bedSJack Steiner 						req.flush_cbrs);
2109cc9b056SJack Steiner 			if (ret < 0)
2119cc9b056SJack Steiner 				goto fail;
2129cc9b056SJack Steiner 			ubuf += ret;
2139cc9b056SJack Steiner 			cnt++;
2149cc9b056SJack Steiner 		}
2159cc9b056SJack Steiner 	}
2169cc9b056SJack Steiner 
2179cc9b056SJack Steiner 	if (copy_to_user((void __user *)arg, &req, sizeof(req)))
2189cc9b056SJack Steiner 		return -EFAULT;
2199cc9b056SJack Steiner 	return cnt;
2209cc9b056SJack Steiner 
2219cc9b056SJack Steiner fail:
2229cc9b056SJack Steiner 	return ret;
2239cc9b056SJack Steiner }
224