1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * SN Platform GRU Driver
4 *
5 * Dump GRU State
6 *
7 * Copyright (c) 2008 Silicon Graphics, Inc. All Rights Reserved.
8 */
9
10#include <linux/kernel.h>
11#include <linux/mm.h>
12#include <linux/spinlock.h>
13#include <linux/uaccess.h>
14#include <linux/delay.h>
15#include <linux/bitops.h>
16#include <asm/uv/uv_hub.h>
17
18#include <linux/nospec.h>
19
20#include "gru.h"
21#include "grutables.h"
22#include "gruhandles.h"
23#include "grulib.h"
24
25#define CCH_LOCK_ATTEMPTS 10
26
27static int gru_user_copy_handle(void __user **dp, void *s)
28{
29 if (copy_to_user(to: *dp, from: s, GRU_HANDLE_BYTES))
30 return -1;
31 *dp += GRU_HANDLE_BYTES;
32 return 0;
33}
34
35static int gru_dump_context_data(void *grubase,
36 struct gru_context_configuration_handle *cch,
37 void __user *ubuf, int ctxnum, int dsrcnt,
38 int flush_cbrs)
39{
40 void *cb, *cbe, *tfh, *gseg;
41 int i, scr;
42
43 gseg = grubase + ctxnum * GRU_GSEG_STRIDE;
44 cb = gseg + GRU_CB_BASE;
45 cbe = grubase + GRU_CBE_BASE;
46 tfh = grubase + GRU_TFH_BASE;
47
48 for_each_cbr_in_allocation_map(i, &cch->cbr_allocation_map, scr) {
49 if (flush_cbrs)
50 gru_flush_cache(p: cb);
51 if (gru_user_copy_handle(dp: &ubuf, s: cb))
52 goto fail;
53 if (gru_user_copy_handle(dp: &ubuf, s: tfh + i * GRU_HANDLE_STRIDE))
54 goto fail;
55 if (gru_user_copy_handle(dp: &ubuf, s: cbe + i * GRU_HANDLE_STRIDE))
56 goto fail;
57 cb += GRU_HANDLE_STRIDE;
58 }
59 if (dsrcnt)
60 memcpy(ubuf, gseg + GRU_DS_BASE, dsrcnt * GRU_HANDLE_STRIDE);
61 return 0;
62
63fail:
64 return -EFAULT;
65}
66
67static int gru_dump_tfm(struct gru_state *gru,
68 void __user *ubuf, void __user *ubufend)
69{
70 struct gru_tlb_fault_map *tfm;
71 int i;
72
73 if (GRU_NUM_TFM * GRU_CACHE_LINE_BYTES > ubufend - ubuf)
74 return -EFBIG;
75
76 for (i = 0; i < GRU_NUM_TFM; i++) {
77 tfm = get_tfm(base: gru->gs_gru_base_vaddr, ctxnum: i);
78 if (gru_user_copy_handle(dp: &ubuf, s: tfm))
79 goto fail;
80 }
81 return GRU_NUM_TFM * GRU_CACHE_LINE_BYTES;
82
83fail:
84 return -EFAULT;
85}
86
87static int gru_dump_tgh(struct gru_state *gru,
88 void __user *ubuf, void __user *ubufend)
89{
90 struct gru_tlb_global_handle *tgh;
91 int i;
92
93 if (GRU_NUM_TGH * GRU_CACHE_LINE_BYTES > ubufend - ubuf)
94 return -EFBIG;
95
96 for (i = 0; i < GRU_NUM_TGH; i++) {
97 tgh = get_tgh(base: gru->gs_gru_base_vaddr, ctxnum: i);
98 if (gru_user_copy_handle(dp: &ubuf, s: tgh))
99 goto fail;
100 }
101 return GRU_NUM_TGH * GRU_CACHE_LINE_BYTES;
102
103fail:
104 return -EFAULT;
105}
106
107static int gru_dump_context(struct gru_state *gru, int ctxnum,
108 void __user *ubuf, void __user *ubufend, char data_opt,
109 char lock_cch, char flush_cbrs)
110{
111 struct gru_dump_context_header hdr;
112 struct gru_dump_context_header __user *uhdr = ubuf;
113 struct gru_context_configuration_handle *cch, *ubufcch;
114 struct gru_thread_state *gts;
115 int try, cch_locked, cbrcnt = 0, dsrcnt = 0, bytes = 0, ret = 0;
116 void *grubase;
117
118 memset(&hdr, 0, sizeof(hdr));
119 grubase = gru->gs_gru_base_vaddr;
120 cch = get_cch(base: grubase, ctxnum);
121 for (try = 0; try < CCH_LOCK_ATTEMPTS; try++) {
122 cch_locked = trylock_cch_handle(cch);
123 if (cch_locked)
124 break;
125 msleep(msecs: 1);
126 }
127
128 ubuf += sizeof(hdr);
129 ubufcch = ubuf;
130 if (gru_user_copy_handle(dp: &ubuf, s: cch)) {
131 if (cch_locked)
132 unlock_cch_handle(cch);
133 return -EFAULT;
134 }
135 if (cch_locked)
136 ubufcch->delresp = 0;
137 bytes = sizeof(hdr) + GRU_CACHE_LINE_BYTES;
138
139 if (cch_locked || !lock_cch) {
140 gts = gru->gs_gts[ctxnum];
141 if (gts && gts->ts_vma) {
142 hdr.pid = gts->ts_tgid_owner;
143 hdr.vaddr = gts->ts_vma->vm_start;
144 }
145 if (cch->state != CCHSTATE_INACTIVE) {
146 cbrcnt = hweight64(cch->cbr_allocation_map) *
147 GRU_CBR_AU_SIZE;
148 dsrcnt = data_opt ? hweight32(cch->dsr_allocation_map) *
149 GRU_DSR_AU_CL : 0;
150 }
151 bytes += (3 * cbrcnt + dsrcnt) * GRU_CACHE_LINE_BYTES;
152 if (bytes > ubufend - ubuf)
153 ret = -EFBIG;
154 else
155 ret = gru_dump_context_data(grubase, cch, ubuf, ctxnum,
156 dsrcnt, flush_cbrs);
157 }
158 if (cch_locked)
159 unlock_cch_handle(cch);
160 if (ret)
161 return ret;
162
163 hdr.magic = GRU_DUMP_MAGIC;
164 hdr.gid = gru->gs_gid;
165 hdr.ctxnum = ctxnum;
166 hdr.cbrcnt = cbrcnt;
167 hdr.dsrcnt = dsrcnt;
168 hdr.cch_locked = cch_locked;
169 if (copy_to_user(to: uhdr, from: &hdr, n: sizeof(hdr)))
170 return -EFAULT;
171
172 return bytes;
173}
174
175int gru_dump_chiplet_request(unsigned long arg)
176{
177 struct gru_state *gru;
178 struct gru_dump_chiplet_state_req req;
179 void __user *ubuf;
180 void __user *ubufend;
181 int ctxnum, ret, cnt = 0;
182
183 if (copy_from_user(to: &req, from: (void __user *)arg, n: sizeof(req)))
184 return -EFAULT;
185
186 /* Currently, only dump by gid is implemented */
187 if (req.gid >= gru_max_gids)
188 return -EINVAL;
189 req.gid = array_index_nospec(req.gid, gru_max_gids);
190
191 gru = GID_TO_GRU(req.gid);
192 ubuf = req.buf;
193 ubufend = req.buf + req.buflen;
194
195 ret = gru_dump_tfm(gru, ubuf, ubufend);
196 if (ret < 0)
197 goto fail;
198 ubuf += ret;
199
200 ret = gru_dump_tgh(gru, ubuf, ubufend);
201 if (ret < 0)
202 goto fail;
203 ubuf += ret;
204
205 for (ctxnum = 0; ctxnum < GRU_NUM_CCH; ctxnum++) {
206 if (req.ctxnum == ctxnum || req.ctxnum < 0) {
207 ret = gru_dump_context(gru, ctxnum, ubuf, ubufend,
208 data_opt: req.data_opt, lock_cch: req.lock_cch,
209 flush_cbrs: req.flush_cbrs);
210 if (ret < 0)
211 goto fail;
212 ubuf += ret;
213 cnt++;
214 }
215 }
216
217 if (copy_to_user(to: (void __user *)arg, from: &req, n: sizeof(req)))
218 return -EFAULT;
219 return cnt;
220
221fail:
222 return ret;
223}
224

source code of linux/drivers/misc/sgi-gru/grukdump.c