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