// SPDX-License-Identifier: GPL-2.0-only #define pr_fmt(fmt) "papr-platform-dump: " fmt #include #include #include #include #include #include #include #include #include #include /* * Function-specific return values for ibm,platform-dump, derived from * PAPR+ v2.13 7.3.3.4.1 "ibm,platform-dump RTAS Call". */ #define RTAS_IBM_PLATFORM_DUMP_COMPLETE 0 /* Complete dump retrieved. */ #define RTAS_IBM_PLATFORM_DUMP_CONTINUE 1 /* Continue dump */ #define RTAS_NOT_AUTHORIZED -9002 /* Not Authorized */ #define RTAS_IBM_PLATFORM_DUMP_START 2 /* Linux status to start dump */ /** * struct ibm_platform_dump_params - Parameters (in and out) for * ibm,platform-dump * @work_area: In: work area buffer for results. * @buf_length: In: work area buffer length in bytes * @dump_tag_hi: In: Most-significant 32 bits of a Dump_Tag representing * an id of the dump being processed. * @dump_tag_lo: In: Least-significant 32 bits of a Dump_Tag representing * an id of the dump being processed. * @sequence_hi: In: Sequence number in most-significant 32 bits. * Out: Next sequence number in most-significant 32 bits. * @sequence_lo: In: Sequence number in Least-significant 32 bits * Out: Next sequence number in Least-significant 32 bits. * @bytes_ret_hi: Out: Bytes written in most-significant 32 bits. * @bytes_ret_lo: Out: Bytes written in Least-significant 32 bits. * @status: Out: RTAS call status. * @list: Maintain the list of dumps are in progress. Can * retrieve multiple dumps with different dump IDs at * the same time but not with the same dump ID. This list * is used to determine whether the dump for the same ID * is in progress. */ struct ibm_platform_dump_params { struct rtas_work_area *work_area; u32 buf_length; u32 dump_tag_hi; u32 dump_tag_lo; u32 sequence_hi; u32 sequence_lo; u32 bytes_ret_hi; u32 bytes_ret_lo; s32 status; struct list_head list; }; /* * Multiple dumps with different dump IDs can be retrieved at the same * time, but not with dame dump ID. platform_dump_list_mutex and * platform_dump_list are used to prevent this behavior. */ static DEFINE_MUTEX(platform_dump_list_mutex); static LIST_HEAD(platform_dump_list); /** * rtas_ibm_platform_dump() - Call ibm,platform-dump to fill a work area * buffer. * @params: See &struct ibm_platform_dump_params. * @buf_addr: Address of dump buffer (work_area) * @buf_length: Length of the buffer in bytes (min. 1024) * * Calls ibm,platform-dump until it errors or successfully deposits data * into the supplied work area. Handles RTAS retry statuses. Maps RTAS * error statuses to reasonable errno values. * * Can request multiple dumps with different dump IDs at the same time, * but not with the same dump ID which is prevented with the check in * the ioctl code (papr_platform_dump_create_handle()). * * 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 for dump complete and 1 for continue dump */ static int rtas_ibm_platform_dump(struct ibm_platform_dump_params *params, phys_addr_t buf_addr, u32 buf_length) { u32 rets[4]; s32 fwrc; int ret = 0; do { fwrc = rtas_call(rtas_function_token(RTAS_FN_IBM_PLATFORM_DUMP), 6, 5, rets, params->dump_tag_hi, params->dump_tag_lo, params->sequence_hi, params->sequence_lo, buf_addr, buf_length); } while (rtas_busy_delay(fwrc)); switch (fwrc) { case RTAS_HARDWARE_ERROR: ret = -EIO; break; case RTAS_NOT_AUTHORIZED: ret = -EPERM; break; case RTAS_IBM_PLATFORM_DUMP_CONTINUE: case RTAS_IBM_PLATFORM_DUMP_COMPLETE: params->sequence_hi = rets[0]; params->sequence_lo = rets[1]; params->bytes_ret_hi = rets[2]; params->bytes_ret_lo = rets[3]; break; default: ret = -EIO; pr_err_ratelimited("unexpected ibm,platform-dump status %d\n", fwrc); break; } params->status = fwrc; return ret; } /* * Platform dump is used with multiple RTAS calls to retrieve the * complete dump for the provided dump ID. Once the complete dump is * retrieved, the hypervisor returns dump complete status (0) for the * last RTAS call and expects the caller issues one more call with * NULL buffer to invalidate the dump so that the hypervisor can remove * the dump. * * After the specific dump is invalidated in the hypervisor, expect the * dump complete status for the new sequence - the user space initiates * new request for the same dump ID. */ static ssize_t papr_platform_dump_handle_read(struct file *file, char __user *buf, size_t size, loff_t *off) { struct ibm_platform_dump_params *params = file->private_data; u64 total_bytes; s32 fwrc; /* * Dump already completed with the previous read calls. * In case if the user space issues further reads, returns * -EINVAL. */ if (!params->buf_length) { pr_warn_once("Platform dump completed for dump ID %llu\n", (u64) (((u64)params->dump_tag_hi << 32) | params->dump_tag_lo)); return -EINVAL; } /* * The hypervisor returns status 0 if no more data available to * download. The dump will be invalidated with ioctl (see below). */ if (params->status == RTAS_IBM_PLATFORM_DUMP_COMPLETE) { params->buf_length = 0; /* * Returns 0 to the user space so that user * space read stops. */ return 0; } if (size < SZ_1K) { pr_err_once("Buffer length should be minimum 1024 bytes\n"); return -EINVAL; } else if (size > params->buf_length) { /* * Allocate 4K work area. So if the user requests > 4K, * resize the buffer length. */ size = params->buf_length; } fwrc = rtas_ibm_platform_dump(params, rtas_work_area_phys(params->work_area), size); if (fwrc < 0) return fwrc; total_bytes = (u64) (((u64)params->bytes_ret_hi << 32) | params->bytes_ret_lo); /* * Kernel or firmware bug, do not continue. */ if (WARN(total_bytes > size, "possible write beyond end of work area")) return -EFAULT; if (copy_to_user(buf, rtas_work_area_raw_buf(params->work_area), total_bytes)) return -EFAULT; return total_bytes; } static int papr_platform_dump_handle_release(struct inode *inode, struct file *file) { struct ibm_platform_dump_params *params = file->private_data; if (params->work_area) rtas_work_area_free(params->work_area); mutex_lock(&platform_dump_list_mutex); list_del(¶ms->list); mutex_unlock(&platform_dump_list_mutex); kfree(params); file->private_data = NULL; return 0; } /* * This ioctl is used to invalidate the dump assuming the user space * issue this ioctl after obtain the complete dump. * Issue the last RTAS call with NULL buffer to invalidate the dump * which means dump will be freed in the hypervisor. */ static long papr_platform_dump_invalidate_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) { struct ibm_platform_dump_params *params; u64 __user *argp = (void __user *)arg; u64 param_dump_tag, dump_tag; if (ioctl != PAPR_PLATFORM_DUMP_IOC_INVALIDATE) return -ENOIOCTLCMD; if (get_user(dump_tag, argp)) return -EFAULT; /* * private_data is freeded during release(), so should not * happen. */ if (!file->private_data) { pr_err("No valid FD to invalidate dump for the ID(%llu)\n", dump_tag); return -EINVAL; } params = file->private_data; param_dump_tag = (u64) (((u64)params->dump_tag_hi << 32) | params->dump_tag_lo); if (dump_tag != param_dump_tag) { pr_err("Invalid dump ID(%llu) to invalidate dump\n", dump_tag); return -EINVAL; } if (params->status != RTAS_IBM_PLATFORM_DUMP_COMPLETE) { pr_err("Platform dump is not complete, but requested " "to invalidate dump for ID(%llu)\n", dump_tag); return -EINPROGRESS; } return rtas_ibm_platform_dump(params, 0, 0); } static const struct file_operations papr_platform_dump_handle_ops = { .read = papr_platform_dump_handle_read, .release = papr_platform_dump_handle_release, .unlocked_ioctl = papr_platform_dump_invalidate_ioctl, }; /** * papr_platform_dump_create_handle() - Create a fd-based handle for * reading platform dump * * Handler for PAPR_PLATFORM_DUMP_IOC_CREATE_HANDLE ioctl command * Allocates RTAS parameter struct and work area and attached to the * file descriptor for reading by user space with the multiple RTAS * calls until the dump is completed. This memory allocation is freed * when the file is released. * * Multiple dump requests with different IDs are allowed at the same * time, but not with the same dump ID. So if the user space is * already opened file descriptor for the specific dump ID, return * -EALREADY for the next request. * * @dump_tag: Dump ID for the dump requested to retrieve from the * hypervisor * * Return: The installed fd number if successful, -ve errno otherwise. */ static long papr_platform_dump_create_handle(u64 dump_tag) { struct ibm_platform_dump_params *params; u64 param_dump_tag; struct file *file; long err; int fd; /* * Return failure if the user space is already opened FD for * the specific dump ID. This check will prevent multiple dump * requests for the same dump ID at the same time. Generally * should not expect this, but in case. */ list_for_each_entry(params, &platform_dump_list, list) { param_dump_tag = (u64) (((u64)params->dump_tag_hi << 32) | params->dump_tag_lo); if (dump_tag == param_dump_tag) { pr_err("Platform dump for ID(%llu) is already in progress\n", dump_tag); return -EALREADY; } } params = kzalloc(sizeof(struct ibm_platform_dump_params), GFP_KERNEL_ACCOUNT); if (!params) return -ENOMEM; params->work_area = rtas_work_area_alloc(SZ_4K); params->buf_length = SZ_4K; params->dump_tag_hi = (u32)(dump_tag >> 32); params->dump_tag_lo = (u32)(dump_tag & 0x00000000ffffffffULL); params->status = RTAS_IBM_PLATFORM_DUMP_START; fd = get_unused_fd_flags(O_RDONLY | O_CLOEXEC); if (fd < 0) { err = fd; goto free_area; } file = anon_inode_getfile_fmode("[papr-platform-dump]", &papr_platform_dump_handle_ops, (void *)params, O_RDONLY, FMODE_LSEEK | FMODE_PREAD); if (IS_ERR(file)) { err = PTR_ERR(file); goto put_fd; } fd_install(fd, file); list_add(¶ms->list, &platform_dump_list); pr_info("%s (%d) initiated platform dump for dump tag %llu\n", current->comm, current->pid, dump_tag); return fd; put_fd: put_unused_fd(fd); free_area: rtas_work_area_free(params->work_area); kfree(params); return err; } /* * Top-level ioctl handler for /dev/papr-platform-dump. */ static long papr_platform_dump_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { u64 __user *argp = (void __user *)arg; u64 dump_tag; long ret; if (get_user(dump_tag, argp)) return -EFAULT; switch (ioctl) { case PAPR_PLATFORM_DUMP_IOC_CREATE_HANDLE: mutex_lock(&platform_dump_list_mutex); ret = papr_platform_dump_create_handle(dump_tag); mutex_unlock(&platform_dump_list_mutex); break; default: ret = -ENOIOCTLCMD; break; } return ret; } static const struct file_operations papr_platform_dump_ops = { .unlocked_ioctl = papr_platform_dump_dev_ioctl, }; static struct miscdevice papr_platform_dump_dev = { .minor = MISC_DYNAMIC_MINOR, .name = "papr-platform-dump", .fops = &papr_platform_dump_ops, }; static __init int papr_platform_dump_init(void) { if (!rtas_function_implemented(RTAS_FN_IBM_PLATFORM_DUMP)) return -ENODEV; return misc_register(&papr_platform_dump_dev); } machine_device_initcall(pseries, papr_platform_dump_init);