Path: blob/master/arch/powerpc/platforms/pseries/papr-vpd.c
26481 views
// SPDX-License-Identifier: GPL-2.0-only12#define pr_fmt(fmt) "papr-vpd: " fmt34#include <linux/build_bug.h>5#include <linux/file.h>6#include <linux/fs.h>7#include <linux/init.h>8#include <linux/lockdep.h>9#include <linux/kernel.h>10#include <linux/miscdevice.h>11#include <linux/signal.h>12#include <linux/slab.h>13#include <linux/string.h>14#include <linux/string_helpers.h>15#include <linux/uaccess.h>16#include <asm/machdep.h>17#include <asm/papr-vpd.h>18#include <asm/rtas-work-area.h>19#include <asm/rtas.h>20#include <uapi/asm/papr-vpd.h>21#include "papr-rtas-common.h"2223/**24* struct rtas_ibm_get_vpd_params - Parameters (in and out) for ibm,get-vpd.25* @loc_code: In: Caller-provided location code buffer. Must be RTAS-addressable.26* @work_area: In: Caller-provided work area buffer for results.27* @sequence: In: Sequence number. Out: Next sequence number.28* @written: Out: Bytes written by ibm,get-vpd to @work_area.29* @status: Out: RTAS call status.30*/31struct rtas_ibm_get_vpd_params {32const struct papr_location_code *loc_code;33struct rtas_work_area *work_area;34u32 sequence;35u32 written;36s32 status;37};3839/**40* rtas_ibm_get_vpd() - Call ibm,get-vpd to fill a work area buffer.41* @params: See &struct rtas_ibm_get_vpd_params.42*43* Calls ibm,get-vpd until it errors or successfully deposits data44* into the supplied work area. Handles RTAS retry statuses. Maps RTAS45* error statuses to reasonable errno values.46*47* The caller is expected to invoke rtas_ibm_get_vpd() multiple times48* to retrieve all the VPD for the provided location code. Only one49* sequence should be in progress at any time; starting a new sequence50* will disrupt any sequence already in progress. Serialization of VPD51* retrieval sequences is the responsibility of the caller.52*53* The caller should inspect @params.status to determine whether more54* calls are needed to complete the sequence.55*56* Context: May sleep.57* Return: -ve on error, 0 otherwise.58*/59static int rtas_ibm_get_vpd(struct rtas_ibm_get_vpd_params *params)60{61const struct papr_location_code *loc_code = params->loc_code;62struct rtas_work_area *work_area = params->work_area;63u32 rets[2];64s32 fwrc;65int ret;6667lockdep_assert_held(&rtas_ibm_get_vpd_lock);6869do {70fwrc = rtas_call(rtas_function_token(RTAS_FN_IBM_GET_VPD), 4, 3,71rets,72__pa(loc_code),73rtas_work_area_phys(work_area),74rtas_work_area_size(work_area),75params->sequence);76} while (rtas_busy_delay(fwrc));7778switch (fwrc) {79case RTAS_HARDWARE_ERROR:80ret = -EIO;81break;82case RTAS_INVALID_PARAMETER:83ret = -EINVAL;84break;85case RTAS_SEQ_START_OVER:86ret = -EAGAIN;87pr_info_ratelimited("VPD changed during retrieval, retrying\n");88break;89case RTAS_SEQ_MORE_DATA:90params->sequence = rets[0];91fallthrough;92case RTAS_SEQ_COMPLETE:93params->written = rets[1];94/*95* Kernel or firmware bug, do not continue.96*/97if (WARN(params->written > rtas_work_area_size(work_area),98"possible write beyond end of work area"))99ret = -EFAULT;100else101ret = 0;102break;103default:104ret = -EIO;105pr_err_ratelimited("unexpected ibm,get-vpd status %d\n", fwrc);106break;107}108109params->status = fwrc;110return ret;111}112113/*114* Internal VPD sequence APIs. A VPD sequence is a series of calls to115* ibm,get-vpd for a given location code. The sequence ends when an116* error is encountered or all VPD for the location code has been117* returned.118*/119120/**121* vpd_sequence_begin() - Begin a VPD retrieval sequence.122* @seq: vpd call parameters from sequence struct123*124* Context: May sleep.125*/126static void vpd_sequence_begin(struct papr_rtas_sequence *seq)127{128struct rtas_ibm_get_vpd_params *vpd_params;129/*130* Use a static data structure for the location code passed to131* RTAS to ensure it's in the RMA and avoid a separate work132* area allocation. Guarded by the function lock.133*/134static struct papr_location_code static_loc_code;135136vpd_params = (struct rtas_ibm_get_vpd_params *)seq->params;137/*138* We could allocate the work area before acquiring the139* function lock, but that would allow concurrent requests to140* exhaust the limited work area pool for no benefit. So141* allocate the work area under the lock.142*/143mutex_lock(&rtas_ibm_get_vpd_lock);144static_loc_code = *(struct papr_location_code *)vpd_params->loc_code;145vpd_params = (struct rtas_ibm_get_vpd_params *)seq->params;146vpd_params->work_area = rtas_work_area_alloc(SZ_4K);147vpd_params->loc_code = &static_loc_code;148vpd_params->sequence = 1;149vpd_params->status = 0;150}151152/**153* vpd_sequence_end() - Finalize a VPD retrieval sequence.154* @seq: Sequence state.155*156* Releases resources obtained by vpd_sequence_begin().157*/158static void vpd_sequence_end(struct papr_rtas_sequence *seq)159{160struct rtas_ibm_get_vpd_params *vpd_params;161162vpd_params = (struct rtas_ibm_get_vpd_params *)seq->params;163rtas_work_area_free(vpd_params->work_area);164mutex_unlock(&rtas_ibm_get_vpd_lock);165}166167/*168* Generator function to be passed to papr_rtas_blob_generate().169*/170static const char *vpd_sequence_fill_work_area(struct papr_rtas_sequence *seq,171size_t *len)172{173struct rtas_ibm_get_vpd_params *p;174bool init_state;175176p = (struct rtas_ibm_get_vpd_params *)seq->params;177init_state = (p->written == 0) ? true : false;178179if (papr_rtas_sequence_should_stop(seq, p->status, init_state))180return NULL;181if (papr_rtas_sequence_set_err(seq, rtas_ibm_get_vpd(p)))182return NULL;183*len = p->written;184return rtas_work_area_raw_buf(p->work_area);185}186187static const struct file_operations papr_vpd_handle_ops = {188.read = papr_rtas_common_handle_read,189.llseek = papr_rtas_common_handle_seek,190.release = papr_rtas_common_handle_release,191};192193/**194* papr_vpd_create_handle() - Create a fd-based handle for reading VPD.195* @ulc: Location code in user memory; defines the scope of the VPD to196* retrieve.197*198* Handler for PAPR_VPD_IOC_CREATE_HANDLE ioctl command. Validates199* @ulc and instantiates an immutable VPD "blob" for it. The blob is200* attached to a file descriptor for reading by user space. The memory201* backing the blob is freed when the file is released.202*203* The entire requested VPD is retrieved by this call and all204* necessary RTAS interactions are performed before returning the fd205* to user space. This keeps the read handler simple and ensures that206* the kernel can prevent interleaving of ibm,get-vpd call sequences.207*208* Return: The installed fd number if successful, -ve errno otherwise.209*/210static long papr_vpd_create_handle(struct papr_location_code __user *ulc)211{212struct rtas_ibm_get_vpd_params vpd_params = {};213struct papr_rtas_sequence seq = {};214struct papr_location_code klc;215int fd;216217if (copy_from_user(&klc, ulc, sizeof(klc)))218return -EFAULT;219220if (!string_is_terminated(klc.str, ARRAY_SIZE(klc.str)))221return -EINVAL;222223seq = (struct papr_rtas_sequence) {224.begin = vpd_sequence_begin,225.end = vpd_sequence_end,226.work = vpd_sequence_fill_work_area,227};228229vpd_params.loc_code = &klc;230seq.params = (void *)&vpd_params;231232fd = papr_rtas_setup_file_interface(&seq, &papr_vpd_handle_ops,233"[papr-vpd]");234235return fd;236}237238/*239* Top-level ioctl handler for /dev/papr-vpd.240*/241static long papr_vpd_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)242{243void __user *argp = (__force void __user *)arg;244long ret;245246switch (ioctl) {247case PAPR_VPD_IOC_CREATE_HANDLE:248ret = papr_vpd_create_handle(argp);249break;250default:251ret = -ENOIOCTLCMD;252break;253}254return ret;255}256257static const struct file_operations papr_vpd_ops = {258.unlocked_ioctl = papr_vpd_dev_ioctl,259};260261static struct miscdevice papr_vpd_dev = {262.minor = MISC_DYNAMIC_MINOR,263.name = "papr-vpd",264.fops = &papr_vpd_ops,265};266267static __init int papr_vpd_init(void)268{269if (!rtas_function_implemented(RTAS_FN_IBM_GET_VPD))270return -ENODEV;271272return misc_register(&papr_vpd_dev);273}274machine_device_initcall(pseries, papr_vpd_init);275276277