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