Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/pseries/papr-indices.c
26481 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
3
#define pr_fmt(fmt) "papr-indices: " fmt
4
5
#include <linux/build_bug.h>
6
#include <linux/file.h>
7
#include <linux/fs.h>
8
#include <linux/init.h>
9
#include <linux/lockdep.h>
10
#include <linux/kernel.h>
11
#include <linux/miscdevice.h>
12
#include <linux/signal.h>
13
#include <linux/slab.h>
14
#include <linux/string.h>
15
#include <linux/string_helpers.h>
16
#include <linux/uaccess.h>
17
#include <asm/machdep.h>
18
#include <asm/rtas-work-area.h>
19
#include <asm/rtas.h>
20
#include <uapi/asm/papr-indices.h>
21
#include "papr-rtas-common.h"
22
23
/*
24
* Function-specific return values for ibm,set-dynamic-indicator and
25
* ibm,get-dynamic-sensor-state RTAS calls.
26
* PAPR+ v2.13 7.3.18 and 7.3.19.
27
*/
28
#define RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR -3
29
30
/**
31
* struct rtas_get_indices_params - Parameters (in and out) for
32
* ibm,get-indices.
33
* @is_sensor: In: Caller-provided whether sensor or indicator.
34
* @indice_type:In: Caller-provided indice (sensor or indicator) token
35
* @work_area: In: Caller-provided work area buffer for results.
36
* @next: In: Sequence number. Out: Next sequence number.
37
* @status: Out: RTAS call status.
38
*/
39
struct rtas_get_indices_params {
40
u8 is_sensor;
41
u32 indice_type;
42
struct rtas_work_area *work_area;
43
u32 next;
44
s32 status;
45
};
46
47
/*
48
* rtas_ibm_get_indices() - Call ibm,get-indices to fill a work area buffer.
49
* @params: See &struct rtas_ibm_get_indices_params.
50
*
51
* Calls ibm,get-indices until it errors or successfully deposits data
52
* into the supplied work area. Handles RTAS retry statuses. Maps RTAS
53
* error statuses to reasonable errno values.
54
*
55
* The caller is expected to invoke rtas_ibm_get_indices() multiple times
56
* to retrieve all indices data for the provided indice type. Only one
57
* sequence should be in progress at any time; starting a new sequence
58
* will disrupt any sequence already in progress. Serialization of
59
* indices retrieval sequences is the responsibility of the caller.
60
*
61
* The caller should inspect @params.status to determine whether more
62
* calls are needed to complete the sequence.
63
*
64
* Context: May sleep.
65
* Return: -ve on error, 0 otherwise.
66
*/
67
static int rtas_ibm_get_indices(struct rtas_get_indices_params *params)
68
{
69
struct rtas_work_area *work_area = params->work_area;
70
const s32 token = rtas_function_token(RTAS_FN_IBM_GET_INDICES);
71
u32 rets;
72
s32 fwrc;
73
int ret;
74
75
if (token == RTAS_UNKNOWN_SERVICE)
76
return -ENOENT;
77
78
lockdep_assert_held(&rtas_ibm_get_indices_lock);
79
80
do {
81
fwrc = rtas_call(token, 5, 2, &rets, params->is_sensor,
82
params->indice_type,
83
rtas_work_area_phys(work_area),
84
rtas_work_area_size(work_area),
85
params->next);
86
} while (rtas_busy_delay(fwrc));
87
88
switch (fwrc) {
89
case RTAS_HARDWARE_ERROR:
90
ret = -EIO;
91
break;
92
case RTAS_INVALID_PARAMETER: /* Indicator type is not supported */
93
ret = -EINVAL;
94
break;
95
case RTAS_SEQ_START_OVER:
96
ret = -EAGAIN;
97
pr_info_ratelimited("Indices changed during retrieval, retrying\n");
98
params->next = 1;
99
break;
100
case RTAS_SEQ_MORE_DATA:
101
params->next = rets;
102
ret = 0;
103
break;
104
case RTAS_SEQ_COMPLETE:
105
params->next = 0;
106
ret = 0;
107
break;
108
default:
109
ret = -EIO;
110
pr_err_ratelimited("unexpected ibm,get-indices status %d\n", fwrc);
111
break;
112
}
113
114
params->status = fwrc;
115
return ret;
116
}
117
118
/*
119
* Internal indices sequence APIs. A sequence is a series of calls to
120
* ibm,get-indices for a given location code. The sequence ends when
121
* an error is encountered or all indices for the input has been
122
* returned.
123
*/
124
125
/*
126
* indices_sequence_begin() - Begin a indices retrieval sequence.
127
*
128
* Context: May sleep.
129
*/
130
static void indices_sequence_begin(struct papr_rtas_sequence *seq)
131
{
132
struct rtas_get_indices_params *param;
133
134
param = (struct rtas_get_indices_params *)seq->params;
135
/*
136
* We could allocate the work area before acquiring the
137
* function lock, but that would allow concurrent requests to
138
* exhaust the limited work area pool for no benefit. So
139
* allocate the work area under the lock.
140
*/
141
mutex_lock(&rtas_ibm_get_indices_lock);
142
param->work_area = rtas_work_area_alloc(RTAS_GET_INDICES_BUF_SIZE);
143
param->next = 1;
144
param->status = 0;
145
}
146
147
/*
148
* indices_sequence_end() - Finalize a indices retrieval sequence.
149
*
150
* Releases resources obtained by indices_sequence_begin().
151
*/
152
static void indices_sequence_end(struct papr_rtas_sequence *seq)
153
{
154
struct rtas_get_indices_params *param;
155
156
param = (struct rtas_get_indices_params *)seq->params;
157
rtas_work_area_free(param->work_area);
158
mutex_unlock(&rtas_ibm_get_indices_lock);
159
}
160
161
/*
162
* Work function to be passed to papr_rtas_blob_generate().
163
*
164
* ibm,get-indices RTAS call fills the work area with the certain
165
* format but does not return the bytes written in the buffer. So
166
* instead of kernel parsing this work area to determine the buffer
167
* length, copy the complete work area (RTAS_GET_INDICES_BUF_SIZE)
168
* to the blob and let the user space to obtain the data.
169
* Means RTAS_GET_INDICES_BUF_SIZE data will be returned for each
170
* read().
171
*/
172
173
static const char *indices_sequence_fill_work_area(struct papr_rtas_sequence *seq,
174
size_t *len)
175
{
176
struct rtas_get_indices_params *p;
177
bool init_state;
178
179
p = (struct rtas_get_indices_params *)seq->params;
180
init_state = (p->next == 1) ? true : false;
181
182
if (papr_rtas_sequence_should_stop(seq, p->status, init_state))
183
return NULL;
184
if (papr_rtas_sequence_set_err(seq, rtas_ibm_get_indices(p)))
185
return NULL;
186
187
*len = RTAS_GET_INDICES_BUF_SIZE;
188
return rtas_work_area_raw_buf(p->work_area);
189
}
190
191
/*
192
* papr_indices_handle_read - returns indices blob data to the user space
193
*
194
* ibm,get-indices RTAS call fills the work area with the certian
195
* format but does not return the bytes written in the buffer and
196
* copied RTAS_GET_INDICES_BUF_SIZE data to the blob for each RTAS
197
* call. So send RTAS_GET_INDICES_BUF_SIZE buffer to the user space
198
* for each read().
199
*/
200
static ssize_t papr_indices_handle_read(struct file *file,
201
char __user *buf, size_t size, loff_t *off)
202
{
203
const struct papr_rtas_blob *blob = file->private_data;
204
205
/* we should not instantiate a handle without any data attached. */
206
if (!papr_rtas_blob_has_data(blob)) {
207
pr_err_once("handle without data\n");
208
return -EIO;
209
}
210
211
if (size < RTAS_GET_INDICES_BUF_SIZE) {
212
pr_err_once("Invalid buffer length %ld, expect %d\n",
213
size, RTAS_GET_INDICES_BUF_SIZE);
214
return -EINVAL;
215
} else if (size > RTAS_GET_INDICES_BUF_SIZE)
216
size = RTAS_GET_INDICES_BUF_SIZE;
217
218
return simple_read_from_buffer(buf, size, off, blob->data, blob->len);
219
}
220
221
static const struct file_operations papr_indices_handle_ops = {
222
.read = papr_indices_handle_read,
223
.llseek = papr_rtas_common_handle_seek,
224
.release = papr_rtas_common_handle_release,
225
};
226
227
/*
228
* papr_indices_create_handle() - Create a fd-based handle for reading
229
* indices data
230
* @ubuf: Input parameters to RTAS call such as whether sensor or indicator
231
* and indice type in user memory
232
*
233
* Handler for PAPR_INDICES_IOC_GET ioctl command. Validates @ubuf
234
* and instantiates an immutable indices "blob" for it. The blob is
235
* attached to a file descriptor for reading by user space. The memory
236
* backing the blob is freed when the file is released.
237
*
238
* The entire requested indices is retrieved by this call and all
239
* necessary RTAS interactions are performed before returning the fd
240
* to user space. This keeps the read handler simple and ensures that
241
* the kernel can prevent interleaving of ibm,get-indices call sequences.
242
*
243
* Return: The installed fd number if successful, -ve errno otherwise.
244
*/
245
static long papr_indices_create_handle(struct papr_indices_io_block __user *ubuf)
246
{
247
struct papr_rtas_sequence seq = {};
248
struct rtas_get_indices_params params = {};
249
int fd;
250
251
if (get_user(params.is_sensor, &ubuf->indices.is_sensor))
252
return -EFAULT;
253
254
if (get_user(params.indice_type, &ubuf->indices.indice_type))
255
return -EFAULT;
256
257
seq = (struct papr_rtas_sequence) {
258
.begin = indices_sequence_begin,
259
.end = indices_sequence_end,
260
.work = indices_sequence_fill_work_area,
261
};
262
263
seq.params = &params;
264
fd = papr_rtas_setup_file_interface(&seq,
265
&papr_indices_handle_ops, "[papr-indices]");
266
267
return fd;
268
}
269
270
/*
271
* Create work area with the input parameters. This function is used
272
* for both ibm,set-dynamic-indicator and ibm,get-dynamic-sensor-state
273
* RTAS Calls.
274
*/
275
static struct rtas_work_area *
276
papr_dynamic_indice_buf_from_user(struct papr_indices_io_block __user *ubuf,
277
struct papr_indices_io_block *kbuf)
278
{
279
struct rtas_work_area *work_area;
280
u32 length;
281
__be32 len_be;
282
283
if (copy_from_user(kbuf, ubuf, sizeof(*kbuf)))
284
return ERR_PTR(-EFAULT);
285
286
287
if (!string_is_terminated(kbuf->dynamic_param.location_code_str,
288
ARRAY_SIZE(kbuf->dynamic_param.location_code_str)))
289
return ERR_PTR(-EINVAL);
290
291
/*
292
* The input data in the work area should be as follows:
293
* - 32-bit integer length of the location code string,
294
* including NULL.
295
* - Location code string, NULL terminated, identifying the
296
* token (sensor or indicator).
297
* PAPR 2.13 - R1–7.3.18–5 ibm,set-dynamic-indicator
298
* - R1–7.3.19–5 ibm,get-dynamic-sensor-state
299
*/
300
/*
301
* Length that user space passed should also include NULL
302
* terminator.
303
*/
304
length = strlen(kbuf->dynamic_param.location_code_str) + 1;
305
if (length > LOC_CODE_SIZE)
306
return ERR_PTR(-EINVAL);
307
308
len_be = cpu_to_be32(length);
309
310
work_area = rtas_work_area_alloc(LOC_CODE_SIZE + sizeof(u32));
311
memcpy(rtas_work_area_raw_buf(work_area), &len_be, sizeof(u32));
312
memcpy((rtas_work_area_raw_buf(work_area) + sizeof(u32)),
313
&kbuf->dynamic_param.location_code_str, length);
314
315
return work_area;
316
}
317
318
/**
319
* papr_dynamic_indicator_ioc_set - ibm,set-dynamic-indicator RTAS Call
320
* PAPR 2.13 7.3.18
321
*
322
* @ubuf: Input parameters to RTAS call such as indicator token and
323
* new state.
324
*
325
* Returns success or -errno.
326
*/
327
static long papr_dynamic_indicator_ioc_set(struct papr_indices_io_block __user *ubuf)
328
{
329
struct papr_indices_io_block kbuf;
330
struct rtas_work_area *work_area;
331
s32 fwrc, token, ret;
332
333
token = rtas_function_token(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR);
334
if (token == RTAS_UNKNOWN_SERVICE)
335
return -ENOENT;
336
337
mutex_lock(&rtas_ibm_set_dynamic_indicator_lock);
338
work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);
339
if (IS_ERR(work_area)) {
340
ret = PTR_ERR(work_area);
341
goto out;
342
}
343
344
do {
345
fwrc = rtas_call(token, 3, 1, NULL,
346
kbuf.dynamic_param.token,
347
kbuf.dynamic_param.state,
348
rtas_work_area_phys(work_area));
349
} while (rtas_busy_delay(fwrc));
350
351
rtas_work_area_free(work_area);
352
353
switch (fwrc) {
354
case RTAS_SUCCESS:
355
ret = 0;
356
break;
357
case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR: /* No such indicator */
358
ret = -EOPNOTSUPP;
359
break;
360
default:
361
pr_err("unexpected ibm,set-dynamic-indicator result %d\n",
362
fwrc);
363
fallthrough;
364
case RTAS_HARDWARE_ERROR: /* Hardware/platform error */
365
ret = -EIO;
366
break;
367
}
368
369
out:
370
mutex_unlock(&rtas_ibm_set_dynamic_indicator_lock);
371
return ret;
372
}
373
374
/**
375
* papr_dynamic_sensor_ioc_get - ibm,get-dynamic-sensor-state RTAS Call
376
* PAPR 2.13 7.3.19
377
*
378
* @ubuf: Input parameters to RTAS call such as sensor token
379
* Copies the state in user space buffer.
380
*
381
*
382
* Returns success or -errno.
383
*/
384
385
static long papr_dynamic_sensor_ioc_get(struct papr_indices_io_block __user *ubuf)
386
{
387
struct papr_indices_io_block kbuf;
388
struct rtas_work_area *work_area;
389
s32 fwrc, token, ret;
390
u32 rets;
391
392
token = rtas_function_token(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE);
393
if (token == RTAS_UNKNOWN_SERVICE)
394
return -ENOENT;
395
396
mutex_lock(&rtas_ibm_get_dynamic_sensor_state_lock);
397
work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);
398
if (IS_ERR(work_area)) {
399
ret = PTR_ERR(work_area);
400
goto out;
401
}
402
403
do {
404
fwrc = rtas_call(token, 2, 2, &rets,
405
kbuf.dynamic_param.token,
406
rtas_work_area_phys(work_area));
407
} while (rtas_busy_delay(fwrc));
408
409
rtas_work_area_free(work_area);
410
411
switch (fwrc) {
412
case RTAS_SUCCESS:
413
if (put_user(rets, &ubuf->dynamic_param.state))
414
ret = -EFAULT;
415
else
416
ret = 0;
417
break;
418
case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR: /* No such indicator */
419
ret = -EOPNOTSUPP;
420
break;
421
default:
422
pr_err("unexpected ibm,get-dynamic-sensor result %d\n",
423
fwrc);
424
fallthrough;
425
case RTAS_HARDWARE_ERROR: /* Hardware/platform error */
426
ret = -EIO;
427
break;
428
}
429
430
out:
431
mutex_unlock(&rtas_ibm_get_dynamic_sensor_state_lock);
432
return ret;
433
}
434
435
/*
436
* Top-level ioctl handler for /dev/papr-indices.
437
*/
438
static long papr_indices_dev_ioctl(struct file *filp, unsigned int ioctl,
439
unsigned long arg)
440
{
441
void __user *argp = (__force void __user *)arg;
442
long ret;
443
444
switch (ioctl) {
445
case PAPR_INDICES_IOC_GET:
446
ret = papr_indices_create_handle(argp);
447
break;
448
case PAPR_DYNAMIC_SENSOR_IOC_GET:
449
ret = papr_dynamic_sensor_ioc_get(argp);
450
break;
451
case PAPR_DYNAMIC_INDICATOR_IOC_SET:
452
if (filp->f_mode & FMODE_WRITE)
453
ret = papr_dynamic_indicator_ioc_set(argp);
454
else
455
ret = -EBADF;
456
break;
457
default:
458
ret = -ENOIOCTLCMD;
459
break;
460
}
461
462
return ret;
463
}
464
465
static const struct file_operations papr_indices_ops = {
466
.unlocked_ioctl = papr_indices_dev_ioctl,
467
};
468
469
static struct miscdevice papr_indices_dev = {
470
.minor = MISC_DYNAMIC_MINOR,
471
.name = "papr-indices",
472
.fops = &papr_indices_ops,
473
};
474
475
static __init int papr_indices_init(void)
476
{
477
if (!rtas_function_implemented(RTAS_FN_IBM_GET_INDICES))
478
return -ENODEV;
479
480
if (!rtas_function_implemented(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR))
481
return -ENODEV;
482
483
if (!rtas_function_implemented(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE))
484
return -ENODEV;
485
486
return misc_register(&papr_indices_dev);
487
}
488
machine_device_initcall(pseries, papr_indices_init);
489
490