Quelle papr-indices.c
Sprache: C
// SPDX-License-Identifier: GPL-2.0-only
#define pr_fmt(fmt) "papr-indices: " fmt
#include <linux/build_bug.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/lockdep.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/string_helpers.h>
#include <linux/uaccess.h>
#include <asm /machdep.h>
#include <asm /rtas-work-area.h>
#include <asm /rtas.h>
#include <uapi/asm /papr-indices.h>
#include "papr-rtas-common.h"
/*
* Function - specific return values for ibm , set - dynamic - indicator and
* ibm , get - dynamic - sensor - state RTAS calls .
* PAPR + v2 . 13 7 . 3 . 18 and 7 . 3 . 19 .
*/
#define RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR -3
/**
* struct rtas_get_indices_params - Parameters ( in and out ) for
* ibm , get - indices .
* @ is_sensor : In : Caller - provided whether sensor or indicator .
* @ indice_type : In : Caller - provided indice ( sensor or indicator ) token
* @ work_area : In : Caller - provided work area buffer for results .
* @ next : In : Sequence number . Out : Next sequence number .
* @ status : Out : RTAS call status .
*/
struct rtas_get_indices_params {
u8 is_sensor;
u32 indice_type;
struct rtas_work_area *work_area;
u32 next;
s32 status;
};
/*
* rtas_ibm_get_indices ( ) - Call ibm , get - indices to fill a work area buffer .
* @ params : See & struct rtas_ibm_get_indices_params .
*
* Calls ibm , get - indices until it errors or successfully deposits data
* into the supplied work area . Handles RTAS retry statuses . Maps RTAS
* error statuses to reasonable errno values .
*
* The caller is expected to invoke rtas_ibm_get_indices ( ) multiple times
* to retrieve all indices data for the provided indice type . Only one
* sequence should be in progress at any time ; starting a new sequence
* will disrupt any sequence already in progress . Serialization of
* indices retrieval sequences is the responsibility of the caller .
*
* The caller should inspect @ params . status to determine whether more
* calls are needed to complete the sequence .
*
* Context : May sleep .
* Return : - ve on error , 0 otherwise .
*/
static int rtas_ibm_get_indices(struct rtas_get_indices_params *params)
{
struct rtas_work_area *work_area = params->work_area;
const s32 token = rtas_function_token(RTAS_FN_IBM_GET_INDICES);
u32 rets;
s32 fwrc;
int ret;
if (token == RTAS_UNKNOWN_SERVICE)
return -ENOENT;
lockdep_assert_held(&rtas_ibm_get_indices_lock);
do {
fwrc = rtas_call(token, 5 , 2 , &rets, params->is_sensor,
params->indice_type,
rtas_work_area_phys(work_area),
rtas_work_area_size(work_area),
params->next);
} while (rtas_busy_delay(fwrc));
switch (fwrc) {
case RTAS_HARDWARE_ERROR:
ret = -EIO;
break ;
case RTAS_INVALID_PARAMETER: /* Indicator type is not supported */
ret = -EINVAL;
break ;
case RTAS_SEQ_START_OVER:
ret = -EAGAIN;
pr_info_ratelimited("Indices changed during retrieval, retrying\n" );
params->next = 1 ;
break ;
case RTAS_SEQ_MORE_DATA:
params->next = rets;
ret = 0 ;
break ;
case RTAS_SEQ_COMPLETE:
params->next = 0 ;
ret = 0 ;
break ;
default :
ret = -EIO;
pr_err_ratelimited("unexpected ibm,get-indices status %d\n" , fwrc);
break ;
}
params->status = fwrc;
return ret;
}
/*
* Internal indices sequence APIs . A sequence is a series of calls to
* ibm , get - indices for a given location code . The sequence ends when
* an error is encountered or all indices for the input has been
* returned .
*/
/*
* indices_sequence_begin ( ) - Begin a indices retrieval sequence .
*
* Context : May sleep .
*/
static void indices_sequence_begin(struct papr_rtas_sequence *seq)
{
struct rtas_get_indices_params *param;
param = (struct rtas_get_indices_params *)seq->params;
/*
* We could allocate the work area before acquiring the
* function lock , but that would allow concurrent requests to
* exhaust the limited work area pool for no benefit . So
* allocate the work area under the lock .
*/
mutex_lock(&rtas_ibm_get_indices_lock);
param->work_area = rtas_work_area_alloc(RTAS_GET_INDICES_BUF_SIZE);
param->next = 1 ;
param->status = 0 ;
}
/*
* indices_sequence_end ( ) - Finalize a indices retrieval sequence .
*
* Releases resources obtained by indices_sequence_begin ( ) .
*/
static void indices_sequence_end(struct papr_rtas_sequence *seq)
{
struct rtas_get_indices_params *param;
param = (struct rtas_get_indices_params *)seq->params;
rtas_work_area_free(param->work_area);
mutex_unlock(&rtas_ibm_get_indices_lock);
}
/*
* Work function to be passed to papr_rtas_blob_generate ( ) .
*
* ibm , get - indices RTAS call fills the work area with the certain
* format but does not return the bytes written in the buffer . So
* instead of kernel parsing this work area to determine the buffer
* length , copy the complete work area ( RTAS_GET_INDICES_BUF_SIZE )
* to the blob and let the user space to obtain the data .
* Means RTAS_GET_INDICES_BUF_SIZE data will be returned for each
* read ( ) .
*/
static const char *indices_sequence_fill_work_area(struct papr_rtas_sequence *seq,
size_t *len)
{
struct rtas_get_indices_params *p;
bool init_state;
p = (struct rtas_get_indices_params *)seq->params;
init_state = (p->next == 1 ) ? true : false ;
if (papr_rtas_sequence_should_stop(seq, p->status, init_state))
return NULL;
if (papr_rtas_sequence_set_err(seq, rtas_ibm_get_indices(p)))
return NULL;
*len = RTAS_GET_INDICES_BUF_SIZE;
return rtas_work_area_raw_buf(p->work_area);
}
/*
* papr_indices_handle_read - returns indices blob data to the user space
*
* ibm , get - indices RTAS call fills the work area with the certian
* format but does not return the bytes written in the buffer and
* copied RTAS_GET_INDICES_BUF_SIZE data to the blob for each RTAS
* call . So send RTAS_GET_INDICES_BUF_SIZE buffer to the user space
* for each read ( ) .
*/
static ssize_t papr_indices_handle_read(struct file *file,
char __user *buf, size_t size, loff_t *off)
{
const struct papr_rtas_blob *blob = file->private_data;
/* we should not instantiate a handle without any data attached. */
if (!papr_rtas_blob_has_data(blob)) {
pr_err_once("handle without data\n" );
return -EIO;
}
if (size < RTAS_GET_INDICES_BUF_SIZE) {
pr_err_once("Invalid buffer length %ld, expect %d\n" ,
size, RTAS_GET_INDICES_BUF_SIZE);
return -EINVAL;
} else if (size > RTAS_GET_INDICES_BUF_SIZE)
size = RTAS_GET_INDICES_BUF_SIZE;
return simple_read_from_buffer(buf, size, off, blob->data, blob->len);
}
static const struct file_operations papr_indices_handle_ops = {
.read = papr_indices_handle_read,
.llseek = papr_rtas_common_handle_seek,
.release = papr_rtas_common_handle_release,
};
/*
* papr_indices_create_handle ( ) - Create a fd - based handle for reading
* indices data
* @ ubuf : Input parameters to RTAS call such as whether sensor or indicator
* and indice type in user memory
*
* Handler for PAPR_INDICES_IOC_GET ioctl command . Validates @ ubuf
* and instantiates an immutable indices " blob " for it . The blob is
* attached to a file descriptor for reading by user space . The memory
* backing the blob is freed when the file is released .
*
* The entire requested indices is retrieved by this call and all
* necessary RTAS interactions are performed before returning the fd
* to user space . This keeps the read handler simple and ensures that
* the kernel can prevent interleaving of ibm , get - indices call sequences .
*
* Return : The installed fd number if successful , - ve errno otherwise .
*/
static long papr_indices_create_handle(struct papr_indices_io_block __user *ubuf)
{
struct papr_rtas_sequence seq = {};
struct rtas_get_indices_params params = {};
int fd;
if (get_user(params.is_sensor, &ubuf->indices.is_sensor))
return -EFAULT;
if (get_user(params.indice_type, &ubuf->indices.indice_type))
return -EFAULT;
seq = (struct papr_rtas_sequence) {
.begin = indices_sequence_begin,
.end = indices_sequence_end,
.work = indices_sequence_fill_work_area,
};
seq.params = ¶ms;
fd = papr_rtas_setup_file_interface(&seq,
&papr_indices_handle_ops, "[papr-indices]" );
return fd;
}
/*
* Create work area with the input parameters . This function is used
* for both ibm , set - dynamic - indicator and ibm , get - dynamic - sensor - state
* RTAS Calls .
*/
static struct rtas_work_area *
papr_dynamic_indice_buf_from_user(struct papr_indices_io_block __user *ubuf,
struct papr_indices_io_block *kbuf)
{
struct rtas_work_area *work_area;
u32 length;
__be32 len_be;
if (copy_from_user(kbuf, ubuf, sizeof (*kbuf)))
return ERR_PTR(-EFAULT);
if (!string_is_terminated(kbuf->dynamic_param.location_code_str,
ARRAY_SIZE(kbuf->dynamic_param.location_code_str)))
return ERR_PTR(-EINVAL);
/*
* The input data in the work area should be as follows :
* - 32 - bit integer length of the location code string ,
* including NULL .
* - Location code string , NULL terminated , identifying the
* token ( sensor or indicator ) .
* PAPR 2 . 13 - R1 – 7 . 3 . 18 – 5 ibm , set - dynamic - indicator
* - R1 – 7 . 3 . 19 – 5 ibm , get - dynamic - sensor - state
*/
/*
* Length that user space passed should also include NULL
* terminator .
*/
length = strlen(kbuf->dynamic_param.location_code_str) + 1 ;
if (length > LOC_CODE_SIZE)
return ERR_PTR(-EINVAL);
len_be = cpu_to_be32(length);
work_area = rtas_work_area_alloc(LOC_CODE_SIZE + sizeof (u32));
memcpy(rtas_work_area_raw_buf(work_area), &len_be, sizeof (u32));
memcpy((rtas_work_area_raw_buf(work_area) + sizeof (u32)),
&kbuf->dynamic_param.location_code_str, length);
return work_area;
}
/**
* papr_dynamic_indicator_ioc_set - ibm , set - dynamic - indicator RTAS Call
* PAPR 2 . 13 7 . 3 . 18
*
* @ ubuf : Input parameters to RTAS call such as indicator token and
* new state .
*
* Returns success or - errno .
*/
static long papr_dynamic_indicator_ioc_set(struct papr_indices_io_block __user *ubuf)
{
struct papr_indices_io_block kbuf;
struct rtas_work_area *work_area;
s32 fwrc, token, ret;
token = rtas_function_token(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR);
if (token == RTAS_UNKNOWN_SERVICE)
return -ENOENT;
mutex_lock(&rtas_ibm_set_dynamic_indicator_lock);
work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);
if (IS_ERR(work_area)) {
ret = PTR_ERR(work_area);
goto out;
}
do {
fwrc = rtas_call(token, 3 , 1 , NULL,
kbuf.dynamic_param.token,
kbuf.dynamic_param.state,
rtas_work_area_phys(work_area));
} while (rtas_busy_delay(fwrc));
rtas_work_area_free(work_area);
switch (fwrc) {
case RTAS_SUCCESS:
ret = 0 ;
break ;
case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR: /* No such indicator */
ret = -EOPNOTSUPP;
break ;
default :
pr_err("unexpected ibm,set-dynamic-indicator result %d\n" ,
fwrc);
fallthrough;
case RTAS_HARDWARE_ERROR: /* Hardware/platform error */
ret = -EIO;
break ;
}
out:
mutex_unlock(&rtas_ibm_set_dynamic_indicator_lock);
return ret;
}
/**
* papr_dynamic_sensor_ioc_get - ibm , get - dynamic - sensor - state RTAS Call
* PAPR 2 . 13 7 . 3 . 19
*
* @ ubuf : Input parameters to RTAS call such as sensor token
* Copies the state in user space buffer .
*
*
* Returns success or - errno .
*/
static long papr_dynamic_sensor_ioc_get(struct papr_indices_io_block __user *ubuf)
{
struct papr_indices_io_block kbuf;
struct rtas_work_area *work_area;
s32 fwrc, token, ret;
u32 rets;
token = rtas_function_token(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE);
if (token == RTAS_UNKNOWN_SERVICE)
return -ENOENT;
mutex_lock(&rtas_ibm_get_dynamic_sensor_state_lock);
work_area = papr_dynamic_indice_buf_from_user(ubuf, &kbuf);
if (IS_ERR(work_area)) {
ret = PTR_ERR(work_area);
goto out;
}
do {
fwrc = rtas_call(token, 2 , 2 , &rets,
kbuf.dynamic_param.token,
rtas_work_area_phys(work_area));
} while (rtas_busy_delay(fwrc));
rtas_work_area_free(work_area);
switch (fwrc) {
case RTAS_SUCCESS:
if (put_user(rets, &ubuf->dynamic_param.state))
ret = -EFAULT;
else
ret = 0 ;
break ;
case RTAS_IBM_DYNAMIC_INDICE_NO_INDICATOR: /* No such indicator */
ret = -EOPNOTSUPP;
break ;
default :
pr_err("unexpected ibm,get-dynamic-sensor result %d\n" ,
fwrc);
fallthrough;
case RTAS_HARDWARE_ERROR: /* Hardware/platform error */
ret = -EIO;
break ;
}
out:
mutex_unlock(&rtas_ibm_get_dynamic_sensor_state_lock);
return ret;
}
/*
* Top - level ioctl handler for / dev / papr - indices .
*/
static long papr_indices_dev_ioctl(struct file *filp, unsigned int ioctl,
unsigned long arg)
{
void __user *argp = (__force void __user *)arg;
long ret;
switch (ioctl) {
case PAPR_INDICES_IOC_GET:
ret = papr_indices_create_handle(argp);
break ;
case PAPR_DYNAMIC_SENSOR_IOC_GET:
ret = papr_dynamic_sensor_ioc_get(argp);
break ;
case PAPR_DYNAMIC_INDICATOR_IOC_SET:
if (filp->f_mode & FMODE_WRITE)
ret = papr_dynamic_indicator_ioc_set(argp);
else
ret = -EBADF;
break ;
default :
ret = -ENOIOCTLCMD;
break ;
}
return ret;
}
static const struct file_operations papr_indices_ops = {
.unlocked_ioctl = papr_indices_dev_ioctl,
};
static struct miscdevice papr_indices_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "papr-indices" ,
.fops = &papr_indices_ops,
};
static __init int papr_indices_init(void )
{
if (!rtas_function_implemented(RTAS_FN_IBM_GET_INDICES))
return -ENODEV;
if (!rtas_function_implemented(RTAS_FN_IBM_SET_DYNAMIC_INDICATOR))
return -ENODEV;
if (!rtas_function_implemented(RTAS_FN_IBM_GET_DYNAMIC_SENSOR_STATE))
return -ENODEV;
return misc_register(&papr_indices_dev);
}
machine_device_initcall(pseries, papr_indices_init);
Messung V0.5 in Prozent C=95 H=96 G=95
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126438
#Domains=1897691