1*90e6f191SSumanth Korikkar // SPDX-License-Identifier: GPL-2.0 2*90e6f191SSumanth Korikkar /* 3*90e6f191SSumanth Korikkar * Request power readings for resources in a computing environment via 4*90e6f191SSumanth Korikkar * diag 0x324. diag 0x324 stores the power readings in the power information 5*90e6f191SSumanth Korikkar * block (pib). 6*90e6f191SSumanth Korikkar * 7*90e6f191SSumanth Korikkar * Copyright IBM Corp. 2024 8*90e6f191SSumanth Korikkar */ 9*90e6f191SSumanth Korikkar 10*90e6f191SSumanth Korikkar #define pr_fmt(fmt) "diag324: " fmt 11*90e6f191SSumanth Korikkar #include <linux/fs.h> 12*90e6f191SSumanth Korikkar #include <linux/gfp.h> 13*90e6f191SSumanth Korikkar #include <linux/ioctl.h> 14*90e6f191SSumanth Korikkar #include <linux/jiffies.h> 15*90e6f191SSumanth Korikkar #include <linux/kernel.h> 16*90e6f191SSumanth Korikkar #include <linux/ktime.h> 17*90e6f191SSumanth Korikkar #include <linux/string.h> 18*90e6f191SSumanth Korikkar #include <linux/slab.h> 19*90e6f191SSumanth Korikkar #include <linux/timer.h> 20*90e6f191SSumanth Korikkar #include <linux/types.h> 21*90e6f191SSumanth Korikkar #include <linux/uaccess.h> 22*90e6f191SSumanth Korikkar #include <linux/vmalloc.h> 23*90e6f191SSumanth Korikkar 24*90e6f191SSumanth Korikkar #include <asm/diag.h> 25*90e6f191SSumanth Korikkar #include <asm/sclp.h> 26*90e6f191SSumanth Korikkar #include <asm/timex.h> 27*90e6f191SSumanth Korikkar #include <uapi/asm/diag.h> 28*90e6f191SSumanth Korikkar #include "diag_ioctl.h" 29*90e6f191SSumanth Korikkar 30*90e6f191SSumanth Korikkar enum subcode { 31*90e6f191SSumanth Korikkar DIAG324_SUBC_0 = 0, 32*90e6f191SSumanth Korikkar DIAG324_SUBC_1 = 1, 33*90e6f191SSumanth Korikkar DIAG324_SUBC_2 = 2, 34*90e6f191SSumanth Korikkar }; 35*90e6f191SSumanth Korikkar 36*90e6f191SSumanth Korikkar enum retcode { 37*90e6f191SSumanth Korikkar DIAG324_RET_SUCCESS = 0x0001, 38*90e6f191SSumanth Korikkar DIAG324_RET_SUBC_NOTAVAIL = 0x0103, 39*90e6f191SSumanth Korikkar DIAG324_RET_INSUFFICIENT_SIZE = 0x0104, 40*90e6f191SSumanth Korikkar DIAG324_RET_READING_UNAVAILABLE = 0x0105, 41*90e6f191SSumanth Korikkar }; 42*90e6f191SSumanth Korikkar 43*90e6f191SSumanth Korikkar union diag324_response { 44*90e6f191SSumanth Korikkar u64 response; 45*90e6f191SSumanth Korikkar struct { 46*90e6f191SSumanth Korikkar u64 installed : 32; 47*90e6f191SSumanth Korikkar u64 : 16; 48*90e6f191SSumanth Korikkar u64 rc : 16; 49*90e6f191SSumanth Korikkar } sc0; 50*90e6f191SSumanth Korikkar struct { 51*90e6f191SSumanth Korikkar u64 format : 16; 52*90e6f191SSumanth Korikkar u64 : 16; 53*90e6f191SSumanth Korikkar u64 pib_len : 16; 54*90e6f191SSumanth Korikkar u64 rc : 16; 55*90e6f191SSumanth Korikkar } sc1; 56*90e6f191SSumanth Korikkar struct { 57*90e6f191SSumanth Korikkar u64 : 48; 58*90e6f191SSumanth Korikkar u64 rc : 16; 59*90e6f191SSumanth Korikkar } sc2; 60*90e6f191SSumanth Korikkar }; 61*90e6f191SSumanth Korikkar 62*90e6f191SSumanth Korikkar union diag324_request { 63*90e6f191SSumanth Korikkar u64 request; 64*90e6f191SSumanth Korikkar struct { 65*90e6f191SSumanth Korikkar u64 : 32; 66*90e6f191SSumanth Korikkar u64 allocated : 16; 67*90e6f191SSumanth Korikkar u64 : 12; 68*90e6f191SSumanth Korikkar u64 sc : 4; 69*90e6f191SSumanth Korikkar } sc2; 70*90e6f191SSumanth Korikkar }; 71*90e6f191SSumanth Korikkar 72*90e6f191SSumanth Korikkar struct pib { 73*90e6f191SSumanth Korikkar u32 : 8; 74*90e6f191SSumanth Korikkar u32 num : 8; 75*90e6f191SSumanth Korikkar u32 len : 16; 76*90e6f191SSumanth Korikkar u32 : 24; 77*90e6f191SSumanth Korikkar u32 hlen : 8; 78*90e6f191SSumanth Korikkar u64 : 64; 79*90e6f191SSumanth Korikkar u64 intv; 80*90e6f191SSumanth Korikkar u8 r[]; 81*90e6f191SSumanth Korikkar } __packed; 82*90e6f191SSumanth Korikkar 83*90e6f191SSumanth Korikkar struct pibdata { 84*90e6f191SSumanth Korikkar struct pib *pib; 85*90e6f191SSumanth Korikkar ktime_t expire; 86*90e6f191SSumanth Korikkar u64 sequence; 87*90e6f191SSumanth Korikkar size_t len; 88*90e6f191SSumanth Korikkar int rc; 89*90e6f191SSumanth Korikkar }; 90*90e6f191SSumanth Korikkar 91*90e6f191SSumanth Korikkar static DEFINE_MUTEX(pibmutex); 92*90e6f191SSumanth Korikkar static struct pibdata pibdata; 93*90e6f191SSumanth Korikkar 94*90e6f191SSumanth Korikkar #define PIBWORK_DELAY (5 * NSEC_PER_SEC) 95*90e6f191SSumanth Korikkar 96*90e6f191SSumanth Korikkar static void pibwork_handler(struct work_struct *work); 97*90e6f191SSumanth Korikkar static DECLARE_DELAYED_WORK(pibwork, pibwork_handler); 98*90e6f191SSumanth Korikkar 99*90e6f191SSumanth Korikkar static unsigned long diag324(unsigned long subcode, void *addr) 100*90e6f191SSumanth Korikkar { 101*90e6f191SSumanth Korikkar union register_pair rp = { .even = (unsigned long)addr }; 102*90e6f191SSumanth Korikkar 103*90e6f191SSumanth Korikkar diag_stat_inc(DIAG_STAT_X324); 104*90e6f191SSumanth Korikkar asm volatile("diag %[rp],%[subcode],0x324\n" 105*90e6f191SSumanth Korikkar : [rp] "+d" (rp.pair) 106*90e6f191SSumanth Korikkar : [subcode] "d" (subcode) 107*90e6f191SSumanth Korikkar : "memory"); 108*90e6f191SSumanth Korikkar return rp.odd; 109*90e6f191SSumanth Korikkar } 110*90e6f191SSumanth Korikkar 111*90e6f191SSumanth Korikkar static void pibwork_handler(struct work_struct *work) 112*90e6f191SSumanth Korikkar { 113*90e6f191SSumanth Korikkar struct pibdata *data = &pibdata; 114*90e6f191SSumanth Korikkar ktime_t timedout; 115*90e6f191SSumanth Korikkar 116*90e6f191SSumanth Korikkar mutex_lock(&pibmutex); 117*90e6f191SSumanth Korikkar timedout = ktime_add_ns(data->expire, PIBWORK_DELAY); 118*90e6f191SSumanth Korikkar if (ktime_before(ktime_get(), timedout)) { 119*90e6f191SSumanth Korikkar mod_delayed_work(system_wq, &pibwork, nsecs_to_jiffies(PIBWORK_DELAY)); 120*90e6f191SSumanth Korikkar goto out; 121*90e6f191SSumanth Korikkar } 122*90e6f191SSumanth Korikkar vfree(data->pib); 123*90e6f191SSumanth Korikkar data->pib = NULL; 124*90e6f191SSumanth Korikkar out: 125*90e6f191SSumanth Korikkar mutex_unlock(&pibmutex); 126*90e6f191SSumanth Korikkar } 127*90e6f191SSumanth Korikkar 128*90e6f191SSumanth Korikkar static void pib_update(struct pibdata *data) 129*90e6f191SSumanth Korikkar { 130*90e6f191SSumanth Korikkar union diag324_request req = { .sc2.sc = DIAG324_SUBC_2, .sc2.allocated = data->len }; 131*90e6f191SSumanth Korikkar union diag324_response res; 132*90e6f191SSumanth Korikkar int rc; 133*90e6f191SSumanth Korikkar 134*90e6f191SSumanth Korikkar memset(data->pib, 0, data->len); 135*90e6f191SSumanth Korikkar res.response = diag324(req.request, data->pib); 136*90e6f191SSumanth Korikkar switch (res.sc2.rc) { 137*90e6f191SSumanth Korikkar case DIAG324_RET_SUCCESS: 138*90e6f191SSumanth Korikkar rc = 0; 139*90e6f191SSumanth Korikkar break; 140*90e6f191SSumanth Korikkar case DIAG324_RET_SUBC_NOTAVAIL: 141*90e6f191SSumanth Korikkar rc = -ENOENT; 142*90e6f191SSumanth Korikkar break; 143*90e6f191SSumanth Korikkar case DIAG324_RET_INSUFFICIENT_SIZE: 144*90e6f191SSumanth Korikkar rc = -EMSGSIZE; 145*90e6f191SSumanth Korikkar break; 146*90e6f191SSumanth Korikkar case DIAG324_RET_READING_UNAVAILABLE: 147*90e6f191SSumanth Korikkar rc = -EBUSY; 148*90e6f191SSumanth Korikkar break; 149*90e6f191SSumanth Korikkar default: 150*90e6f191SSumanth Korikkar rc = -EINVAL; 151*90e6f191SSumanth Korikkar } 152*90e6f191SSumanth Korikkar data->rc = rc; 153*90e6f191SSumanth Korikkar } 154*90e6f191SSumanth Korikkar 155*90e6f191SSumanth Korikkar long diag324_pibbuf(unsigned long arg) 156*90e6f191SSumanth Korikkar { 157*90e6f191SSumanth Korikkar struct diag324_pib __user *udata = (struct diag324_pib __user *)arg; 158*90e6f191SSumanth Korikkar struct pibdata *data = &pibdata; 159*90e6f191SSumanth Korikkar static bool first = true; 160*90e6f191SSumanth Korikkar u64 address; 161*90e6f191SSumanth Korikkar int rc; 162*90e6f191SSumanth Korikkar 163*90e6f191SSumanth Korikkar if (!data->len) 164*90e6f191SSumanth Korikkar return -EOPNOTSUPP; 165*90e6f191SSumanth Korikkar if (get_user(address, &udata->address)) 166*90e6f191SSumanth Korikkar return -EFAULT; 167*90e6f191SSumanth Korikkar mutex_lock(&pibmutex); 168*90e6f191SSumanth Korikkar rc = -ENOMEM; 169*90e6f191SSumanth Korikkar if (!data->pib) 170*90e6f191SSumanth Korikkar data->pib = vmalloc(data->len); 171*90e6f191SSumanth Korikkar if (!data->pib) 172*90e6f191SSumanth Korikkar goto out; 173*90e6f191SSumanth Korikkar if (first || ktime_after(ktime_get(), data->expire)) { 174*90e6f191SSumanth Korikkar pib_update(data); 175*90e6f191SSumanth Korikkar data->sequence++; 176*90e6f191SSumanth Korikkar data->expire = ktime_add_ns(ktime_get(), tod_to_ns(data->pib->intv)); 177*90e6f191SSumanth Korikkar mod_delayed_work(system_wq, &pibwork, nsecs_to_jiffies(PIBWORK_DELAY)); 178*90e6f191SSumanth Korikkar first = false; 179*90e6f191SSumanth Korikkar } 180*90e6f191SSumanth Korikkar rc = data->rc; 181*90e6f191SSumanth Korikkar if (rc != 0 && rc != -EBUSY) 182*90e6f191SSumanth Korikkar goto out; 183*90e6f191SSumanth Korikkar rc = copy_to_user((void __user *)address, data->pib, data->pib->len); 184*90e6f191SSumanth Korikkar rc |= put_user(data->sequence, &udata->sequence); 185*90e6f191SSumanth Korikkar if (rc) 186*90e6f191SSumanth Korikkar rc = -EFAULT; 187*90e6f191SSumanth Korikkar out: 188*90e6f191SSumanth Korikkar mutex_unlock(&pibmutex); 189*90e6f191SSumanth Korikkar return rc; 190*90e6f191SSumanth Korikkar } 191*90e6f191SSumanth Korikkar 192*90e6f191SSumanth Korikkar long diag324_piblen(unsigned long arg) 193*90e6f191SSumanth Korikkar { 194*90e6f191SSumanth Korikkar struct pibdata *data = &pibdata; 195*90e6f191SSumanth Korikkar 196*90e6f191SSumanth Korikkar if (!data->len) 197*90e6f191SSumanth Korikkar return -EOPNOTSUPP; 198*90e6f191SSumanth Korikkar if (put_user(data->len, (size_t __user *)arg)) 199*90e6f191SSumanth Korikkar return -EFAULT; 200*90e6f191SSumanth Korikkar return 0; 201*90e6f191SSumanth Korikkar } 202*90e6f191SSumanth Korikkar 203*90e6f191SSumanth Korikkar static int __init diag324_init(void) 204*90e6f191SSumanth Korikkar { 205*90e6f191SSumanth Korikkar union diag324_response res; 206*90e6f191SSumanth Korikkar unsigned long installed; 207*90e6f191SSumanth Korikkar 208*90e6f191SSumanth Korikkar if (!sclp.has_diag324) 209*90e6f191SSumanth Korikkar return -EOPNOTSUPP; 210*90e6f191SSumanth Korikkar res.response = diag324(DIAG324_SUBC_0, NULL); 211*90e6f191SSumanth Korikkar if (res.sc0.rc != DIAG324_RET_SUCCESS) 212*90e6f191SSumanth Korikkar return -EOPNOTSUPP; 213*90e6f191SSumanth Korikkar installed = res.response; 214*90e6f191SSumanth Korikkar if (!test_bit_inv(DIAG324_SUBC_1, &installed)) 215*90e6f191SSumanth Korikkar return -EOPNOTSUPP; 216*90e6f191SSumanth Korikkar if (!test_bit_inv(DIAG324_SUBC_2, &installed)) 217*90e6f191SSumanth Korikkar return -EOPNOTSUPP; 218*90e6f191SSumanth Korikkar res.response = diag324(DIAG324_SUBC_1, NULL); 219*90e6f191SSumanth Korikkar if (res.sc1.rc != DIAG324_RET_SUCCESS) 220*90e6f191SSumanth Korikkar return -EOPNOTSUPP; 221*90e6f191SSumanth Korikkar pibdata.len = res.sc1.pib_len; 222*90e6f191SSumanth Korikkar return 0; 223*90e6f191SSumanth Korikkar } 224*90e6f191SSumanth Korikkar device_initcall(diag324_init); 225