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