1*0d308717SMete Durlu // SPDX-License-Identifier: GPL-2.0 2*0d308717SMete Durlu /* 3*0d308717SMete Durlu * Request memory topology information via diag0x310. 4*0d308717SMete Durlu * 5*0d308717SMete Durlu * Copyright IBM Corp. 2025 6*0d308717SMete Durlu */ 7*0d308717SMete Durlu 8*0d308717SMete Durlu #include <linux/kernel.h> 9*0d308717SMete Durlu #include <linux/types.h> 10*0d308717SMete Durlu #include <linux/uaccess.h> 11*0d308717SMete Durlu #include <linux/vmalloc.h> 12*0d308717SMete Durlu #include <asm/diag.h> 13*0d308717SMete Durlu #include <asm/sclp.h> 14*0d308717SMete Durlu #include <uapi/asm/diag.h> 15*0d308717SMete Durlu #include "diag_ioctl.h" 16*0d308717SMete Durlu 17*0d308717SMete Durlu #define DIAG310_LEVELMIN 1 18*0d308717SMete Durlu #define DIAG310_LEVELMAX 6 19*0d308717SMete Durlu 20*0d308717SMete Durlu enum diag310_sc { 21*0d308717SMete Durlu DIAG310_SUBC_0 = 0, 22*0d308717SMete Durlu DIAG310_SUBC_1 = 1, 23*0d308717SMete Durlu DIAG310_SUBC_4 = 4, 24*0d308717SMete Durlu DIAG310_SUBC_5 = 5 25*0d308717SMete Durlu }; 26*0d308717SMete Durlu 27*0d308717SMete Durlu enum diag310_retcode { 28*0d308717SMete Durlu DIAG310_RET_SUCCESS = 0x0001, 29*0d308717SMete Durlu DIAG310_RET_BUSY = 0x0101, 30*0d308717SMete Durlu DIAG310_RET_OPNOTSUPP = 0x0102, 31*0d308717SMete Durlu DIAG310_RET_SC4_INVAL = 0x0401, 32*0d308717SMete Durlu DIAG310_RET_SC4_NODATA = 0x0402, 33*0d308717SMete Durlu DIAG310_RET_SC5_INVAL = 0x0501, 34*0d308717SMete Durlu DIAG310_RET_SC5_NODATA = 0x0502, 35*0d308717SMete Durlu DIAG310_RET_SC5_ESIZE = 0x0503 36*0d308717SMete Durlu }; 37*0d308717SMete Durlu 38*0d308717SMete Durlu union diag310_response { 39*0d308717SMete Durlu u64 response; 40*0d308717SMete Durlu struct { 41*0d308717SMete Durlu u64 result : 32; 42*0d308717SMete Durlu u64 : 16; 43*0d308717SMete Durlu u64 rc : 16; 44*0d308717SMete Durlu }; 45*0d308717SMete Durlu }; 46*0d308717SMete Durlu 47*0d308717SMete Durlu union diag310_req_subcode { 48*0d308717SMete Durlu u64 subcode; 49*0d308717SMete Durlu struct { 50*0d308717SMete Durlu u64 : 48; 51*0d308717SMete Durlu u64 st : 8; 52*0d308717SMete Durlu u64 sc : 8; 53*0d308717SMete Durlu }; 54*0d308717SMete Durlu }; 55*0d308717SMete Durlu 56*0d308717SMete Durlu union diag310_req_size { 57*0d308717SMete Durlu u64 size; 58*0d308717SMete Durlu struct { 59*0d308717SMete Durlu u64 page_count : 32; 60*0d308717SMete Durlu u64 : 32; 61*0d308717SMete Durlu }; 62*0d308717SMete Durlu }; 63*0d308717SMete Durlu 64*0d308717SMete Durlu static inline unsigned long diag310(unsigned long subcode, unsigned long size, void *addr) 65*0d308717SMete Durlu { 66*0d308717SMete Durlu union register_pair rp = { .even = (unsigned long)addr, .odd = size }; 67*0d308717SMete Durlu 68*0d308717SMete Durlu diag_stat_inc(DIAG_STAT_X310); 69*0d308717SMete Durlu asm volatile("diag %[rp],%[subcode],0x310\n" 70*0d308717SMete Durlu : [rp] "+d" (rp.pair) 71*0d308717SMete Durlu : [subcode] "d" (subcode) 72*0d308717SMete Durlu : "memory"); 73*0d308717SMete Durlu return rp.odd; 74*0d308717SMete Durlu } 75*0d308717SMete Durlu 76*0d308717SMete Durlu static int diag310_result_to_errno(unsigned int result) 77*0d308717SMete Durlu { 78*0d308717SMete Durlu switch (result) { 79*0d308717SMete Durlu case DIAG310_RET_BUSY: 80*0d308717SMete Durlu return -EBUSY; 81*0d308717SMete Durlu case DIAG310_RET_OPNOTSUPP: 82*0d308717SMete Durlu return -EOPNOTSUPP; 83*0d308717SMete Durlu default: 84*0d308717SMete Durlu return -EINVAL; 85*0d308717SMete Durlu } 86*0d308717SMete Durlu } 87*0d308717SMete Durlu 88*0d308717SMete Durlu static int diag310_get_subcode_mask(unsigned long *mask) 89*0d308717SMete Durlu { 90*0d308717SMete Durlu union diag310_response res; 91*0d308717SMete Durlu 92*0d308717SMete Durlu res.response = diag310(DIAG310_SUBC_0, 0, NULL); 93*0d308717SMete Durlu if (res.rc != DIAG310_RET_SUCCESS) 94*0d308717SMete Durlu return diag310_result_to_errno(res.rc); 95*0d308717SMete Durlu *mask = res.response; 96*0d308717SMete Durlu return 0; 97*0d308717SMete Durlu } 98*0d308717SMete Durlu 99*0d308717SMete Durlu static int diag310_get_memtop_stride(unsigned long *stride) 100*0d308717SMete Durlu { 101*0d308717SMete Durlu union diag310_response res; 102*0d308717SMete Durlu 103*0d308717SMete Durlu res.response = diag310(DIAG310_SUBC_1, 0, NULL); 104*0d308717SMete Durlu if (res.rc != DIAG310_RET_SUCCESS) 105*0d308717SMete Durlu return diag310_result_to_errno(res.rc); 106*0d308717SMete Durlu *stride = res.result; 107*0d308717SMete Durlu return 0; 108*0d308717SMete Durlu } 109*0d308717SMete Durlu 110*0d308717SMete Durlu static int diag310_get_memtop_size(unsigned long *pages, unsigned long level) 111*0d308717SMete Durlu { 112*0d308717SMete Durlu union diag310_req_subcode req = { .sc = DIAG310_SUBC_4, .st = level }; 113*0d308717SMete Durlu union diag310_response res; 114*0d308717SMete Durlu 115*0d308717SMete Durlu res.response = diag310(req.subcode, 0, NULL); 116*0d308717SMete Durlu switch (res.rc) { 117*0d308717SMete Durlu case DIAG310_RET_SUCCESS: 118*0d308717SMete Durlu *pages = res.result; 119*0d308717SMete Durlu return 0; 120*0d308717SMete Durlu case DIAG310_RET_SC4_NODATA: 121*0d308717SMete Durlu return -ENODATA; 122*0d308717SMete Durlu case DIAG310_RET_SC4_INVAL: 123*0d308717SMete Durlu return -EINVAL; 124*0d308717SMete Durlu default: 125*0d308717SMete Durlu return diag310_result_to_errno(res.rc); 126*0d308717SMete Durlu } 127*0d308717SMete Durlu } 128*0d308717SMete Durlu 129*0d308717SMete Durlu static int diag310_store_topology_map(void *buf, unsigned long pages, unsigned long level) 130*0d308717SMete Durlu { 131*0d308717SMete Durlu union diag310_req_subcode req_sc = { .sc = DIAG310_SUBC_5, .st = level }; 132*0d308717SMete Durlu union diag310_req_size req_size = { .page_count = pages }; 133*0d308717SMete Durlu union diag310_response res; 134*0d308717SMete Durlu 135*0d308717SMete Durlu res.response = diag310(req_sc.subcode, req_size.size, buf); 136*0d308717SMete Durlu switch (res.rc) { 137*0d308717SMete Durlu case DIAG310_RET_SUCCESS: 138*0d308717SMete Durlu return 0; 139*0d308717SMete Durlu case DIAG310_RET_SC5_NODATA: 140*0d308717SMete Durlu return -ENODATA; 141*0d308717SMete Durlu case DIAG310_RET_SC5_ESIZE: 142*0d308717SMete Durlu return -EOVERFLOW; 143*0d308717SMete Durlu case DIAG310_RET_SC5_INVAL: 144*0d308717SMete Durlu return -EINVAL; 145*0d308717SMete Durlu default: 146*0d308717SMete Durlu return diag310_result_to_errno(res.rc); 147*0d308717SMete Durlu } 148*0d308717SMete Durlu } 149*0d308717SMete Durlu 150*0d308717SMete Durlu static int diag310_check_features(void) 151*0d308717SMete Durlu { 152*0d308717SMete Durlu static int features_available; 153*0d308717SMete Durlu unsigned long mask; 154*0d308717SMete Durlu int rc; 155*0d308717SMete Durlu 156*0d308717SMete Durlu if (READ_ONCE(features_available)) 157*0d308717SMete Durlu return 0; 158*0d308717SMete Durlu if (!sclp.has_diag310) 159*0d308717SMete Durlu return -EOPNOTSUPP; 160*0d308717SMete Durlu rc = diag310_get_subcode_mask(&mask); 161*0d308717SMete Durlu if (rc) 162*0d308717SMete Durlu return rc; 163*0d308717SMete Durlu if (!test_bit_inv(DIAG310_SUBC_1, &mask)) 164*0d308717SMete Durlu return -EOPNOTSUPP; 165*0d308717SMete Durlu if (!test_bit_inv(DIAG310_SUBC_4, &mask)) 166*0d308717SMete Durlu return -EOPNOTSUPP; 167*0d308717SMete Durlu if (!test_bit_inv(DIAG310_SUBC_5, &mask)) 168*0d308717SMete Durlu return -EOPNOTSUPP; 169*0d308717SMete Durlu WRITE_ONCE(features_available, 1); 170*0d308717SMete Durlu return 0; 171*0d308717SMete Durlu } 172*0d308717SMete Durlu 173*0d308717SMete Durlu static int memtop_get_stride_len(unsigned long *res) 174*0d308717SMete Durlu { 175*0d308717SMete Durlu static unsigned long memtop_stride; 176*0d308717SMete Durlu unsigned long stride; 177*0d308717SMete Durlu int rc; 178*0d308717SMete Durlu 179*0d308717SMete Durlu stride = READ_ONCE(memtop_stride); 180*0d308717SMete Durlu if (!stride) { 181*0d308717SMete Durlu rc = diag310_get_memtop_stride(&stride); 182*0d308717SMete Durlu if (rc) 183*0d308717SMete Durlu return rc; 184*0d308717SMete Durlu WRITE_ONCE(memtop_stride, stride); 185*0d308717SMete Durlu } 186*0d308717SMete Durlu *res = stride; 187*0d308717SMete Durlu return 0; 188*0d308717SMete Durlu } 189*0d308717SMete Durlu 190*0d308717SMete Durlu static int memtop_get_page_count(unsigned long *res, unsigned long level) 191*0d308717SMete Durlu { 192*0d308717SMete Durlu static unsigned long memtop_pages[DIAG310_LEVELMAX]; 193*0d308717SMete Durlu unsigned long pages; 194*0d308717SMete Durlu int rc; 195*0d308717SMete Durlu 196*0d308717SMete Durlu if (level > DIAG310_LEVELMAX || level < DIAG310_LEVELMIN) 197*0d308717SMete Durlu return -EINVAL; 198*0d308717SMete Durlu pages = READ_ONCE(memtop_pages[level - 1]); 199*0d308717SMete Durlu if (!pages) { 200*0d308717SMete Durlu rc = diag310_get_memtop_size(&pages, level); 201*0d308717SMete Durlu if (rc) 202*0d308717SMete Durlu return rc; 203*0d308717SMete Durlu WRITE_ONCE(memtop_pages[level - 1], pages); 204*0d308717SMete Durlu } 205*0d308717SMete Durlu *res = pages; 206*0d308717SMete Durlu return 0; 207*0d308717SMete Durlu } 208*0d308717SMete Durlu 209*0d308717SMete Durlu long diag310_memtop_stride(unsigned long arg) 210*0d308717SMete Durlu { 211*0d308717SMete Durlu size_t __user *argp = (void __user *)arg; 212*0d308717SMete Durlu unsigned long stride; 213*0d308717SMete Durlu int rc; 214*0d308717SMete Durlu 215*0d308717SMete Durlu rc = diag310_check_features(); 216*0d308717SMete Durlu if (rc) 217*0d308717SMete Durlu return rc; 218*0d308717SMete Durlu rc = memtop_get_stride_len(&stride); 219*0d308717SMete Durlu if (rc) 220*0d308717SMete Durlu return rc; 221*0d308717SMete Durlu if (put_user(stride, argp)) 222*0d308717SMete Durlu return -EFAULT; 223*0d308717SMete Durlu return 0; 224*0d308717SMete Durlu } 225*0d308717SMete Durlu 226*0d308717SMete Durlu long diag310_memtop_len(unsigned long arg) 227*0d308717SMete Durlu { 228*0d308717SMete Durlu size_t __user *argp = (void __user *)arg; 229*0d308717SMete Durlu unsigned long pages, level; 230*0d308717SMete Durlu int rc; 231*0d308717SMete Durlu 232*0d308717SMete Durlu rc = diag310_check_features(); 233*0d308717SMete Durlu if (rc) 234*0d308717SMete Durlu return rc; 235*0d308717SMete Durlu if (get_user(level, argp)) 236*0d308717SMete Durlu return -EFAULT; 237*0d308717SMete Durlu rc = memtop_get_page_count(&pages, level); 238*0d308717SMete Durlu if (rc) 239*0d308717SMete Durlu return rc; 240*0d308717SMete Durlu if (put_user(pages * PAGE_SIZE, argp)) 241*0d308717SMete Durlu return -EFAULT; 242*0d308717SMete Durlu return 0; 243*0d308717SMete Durlu } 244*0d308717SMete Durlu 245*0d308717SMete Durlu long diag310_memtop_buf(unsigned long arg) 246*0d308717SMete Durlu { 247*0d308717SMete Durlu struct diag310_memtop __user *udata = (struct diag310_memtop __user *)arg; 248*0d308717SMete Durlu unsigned long level, pages, data_size; 249*0d308717SMete Durlu u64 address; 250*0d308717SMete Durlu void *buf; 251*0d308717SMete Durlu int rc; 252*0d308717SMete Durlu 253*0d308717SMete Durlu rc = diag310_check_features(); 254*0d308717SMete Durlu if (rc) 255*0d308717SMete Durlu return rc; 256*0d308717SMete Durlu if (get_user(level, &udata->nesting_lvl)) 257*0d308717SMete Durlu return -EFAULT; 258*0d308717SMete Durlu if (get_user(address, &udata->address)) 259*0d308717SMete Durlu return -EFAULT; 260*0d308717SMete Durlu rc = memtop_get_page_count(&pages, level); 261*0d308717SMete Durlu if (rc) 262*0d308717SMete Durlu return rc; 263*0d308717SMete Durlu data_size = pages * PAGE_SIZE; 264*0d308717SMete Durlu buf = __vmalloc_node(data_size, PAGE_SIZE, GFP_KERNEL | __GFP_ZERO | __GFP_ACCOUNT, 265*0d308717SMete Durlu NUMA_NO_NODE, __builtin_return_address(0)); 266*0d308717SMete Durlu if (!buf) 267*0d308717SMete Durlu return -ENOMEM; 268*0d308717SMete Durlu rc = diag310_store_topology_map(buf, pages, level); 269*0d308717SMete Durlu if (rc) 270*0d308717SMete Durlu goto out; 271*0d308717SMete Durlu if (copy_to_user((void __user *)address, buf, data_size)) 272*0d308717SMete Durlu rc = -EFAULT; 273*0d308717SMete Durlu out: 274*0d308717SMete Durlu vfree(buf); 275*0d308717SMete Durlu return rc; 276*0d308717SMete Durlu } 277