--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) "papr-phy-attest: " 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-physical-attestation.h>
+#include "papr-rtas-common.h"
+
+/**
+ * struct rtas_phy_attest_params - Parameters (in and out) for
+ * ibm,physical-attestation.
+ *
+ * @cmd:  In: Caller-provided attestation command buffer. Must be
+ *        RTAS-addressable.
+ * @work_area: In: Caller-provided work area buffer for attestation
+ *             command structure
+ *             Out: Caller-provided work area buffer for the response
+ * @cmd_len:   In: Caller-provided attestation command structure
+ *             length
+ * @sequence:  In: Sequence number. Out: Next sequence number.
+ * @written:   Out: Bytes written by ibm,physical-attestation to
+ *             @work_area.
+ * @status:    Out: RTAS call status.
+ */
+struct rtas_phy_attest_params {
+       struct papr_phy_attest_io_block cmd;
+       struct rtas_work_area *work_area;
+       u32 cmd_len;
+       u32 sequence;
+       u32 written;
+       s32 status;
+};
+
+/**
+ * rtas_physical_attestation() - Call ibm,physical-attestation to
+ * fill a work area buffer.
+ * @params: See &struct rtas_phy_attest_params.
+ *
+ * Calls ibm,physical-attestation 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_physical_attestation()
+ * multiple times to retrieve all the data for the provided
+ * attestation command. Only one sequence should be in progress at
+ * any time; starting a new sequence will disrupt any sequence
+ * already in progress. Serialization of attestation 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_physical_attestation(struct rtas_phy_attest_params *params)
+{
+       struct rtas_work_area *work_area;
+       s32 fwrc, token;
+       u32 rets[2];
+       int ret;
+
+       work_area = params->work_area;
+       token = rtas_function_token(RTAS_FN_IBM_PHYSICAL_ATTESTATION);
+       if (token == RTAS_UNKNOWN_SERVICE)
+               return -ENOENT;
+
+       lockdep_assert_held(&rtas_ibm_physical_attestation_lock);
+
+       do {
+               fwrc = rtas_call(token, 3, 3, rets,
+                                rtas_work_area_phys(work_area),
+                                params->cmd_len,
+                                params->sequence);
+       } while (rtas_busy_delay(fwrc));
+
+       switch (fwrc) {
+       case RTAS_HARDWARE_ERROR:
+               ret = -EIO;
+               break;
+       case RTAS_INVALID_PARAMETER:
+               ret = -EINVAL;
+               break;
+       case RTAS_SEQ_MORE_DATA:
+               params->sequence = rets[0];
+               fallthrough;
+       case RTAS_SEQ_COMPLETE:
+               params->written = rets[1];
+               /*
+                * Kernel or firmware bug, do not continue.
+                */
+               if (WARN(params->written > rtas_work_area_size(work_area),
+                        "possible write beyond end of work area"))
+                       ret = -EFAULT;
+               else
+                       ret = 0;
+               break;
+       default:
+               ret = -EIO;
+               pr_err_ratelimited("unexpected ibm,get-phy_attest status %d\n", fwrc);
+               break;
+       }
+
+       params->status = fwrc;
+       return ret;
+}
+
+/*
+ * Internal physical-attestation sequence APIs. A physical-attestation
+ * sequence is a series of calls to get ibm,physical-attestation
+ * for a given attestation command. The sequence ends when an error
+ * is encountered or all data for the attestation command has been
+ * returned.
+ */
+
+/**
+ * phy_attest_sequence_begin() - Begin a response data for attestation
+ * command retrieval sequence.
+ * @seq: user specified parameters for RTAS call from seq struct.
+ *
+ * Context: May sleep.
+ */
+static void phy_attest_sequence_begin(struct papr_rtas_sequence *seq)
+{
+       struct rtas_phy_attest_params *param;
+
+       /*
+        * 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_physical_attestation_lock);
+       param =  (struct rtas_phy_attest_params *)seq->params;
+       param->work_area = rtas_work_area_alloc(SZ_4K);
+       memcpy(rtas_work_area_raw_buf(param->work_area), ¶m->cmd,
+                       param->cmd_len);
+       param->sequence = 1;
+       param->status = 0;
+}
+
+/**
+ * phy_attest_sequence_end() - Finalize a attestation command
+ * response retrieval sequence.
+ * @seq: Sequence state.
+ *
+ * Releases resources obtained by phy_attest_sequence_begin().
+ */
+static void phy_attest_sequence_end(struct papr_rtas_sequence *seq)
+{
+       struct rtas_phy_attest_params *param;
+
+       param =  (struct rtas_phy_attest_params *)seq->params;
+       rtas_work_area_free(param->work_area);
+       mutex_unlock(&rtas_ibm_physical_attestation_lock);
+       kfree(param);
+}
+
+/*
+ * Generator function to be passed to papr_rtas_blob_generate().
+ */
+static const char *phy_attest_sequence_fill_work_area(struct papr_rtas_sequence *seq,
+                                               size_t *len)
+{
+       struct rtas_phy_attest_params *p;
+       bool init_state;
+
+       p = (struct rtas_phy_attest_params *)seq->params;
+       init_state = (p->written == 0) ? true : false;
+
+       if (papr_rtas_sequence_should_stop(seq, p->status, init_state))
+               return NULL;
+       if (papr_rtas_sequence_set_err(seq, rtas_physical_attestation(p)))
+               return NULL;
+       *len = p->written;
+       return rtas_work_area_raw_buf(p->work_area);
+}
+
+static const struct file_operations papr_phy_attest_handle_ops = {
+       .read = papr_rtas_common_handle_read,
+       .llseek = papr_rtas_common_handle_seek,
+       .release = papr_rtas_common_handle_release,
+};
+
+/**
+ * papr_phy_attest_create_handle() - Create a fd-based handle for
+ * reading the response for the given attestation command.
+ * @ulc: Attestation command in user memory; defines the scope of
+ *       data for the attestation command to retrieve.
+ *
+ * Handler for PAPR_PHYSICAL_ATTESTATION_IOC_CREATE_HANDLE ioctl
+ * command. Validates @ulc and instantiates an immutable response
+ * "blob" for attestation command. 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 response buffer for the attestation command
+ * 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 kernel can prevent
+ * interleaving ibm,physical-attestation call sequences.
+ *
+ * Return: The installed fd number if successful, -ve errno otherwise.
+ */
+static long papr_phy_attest_create_handle(struct papr_phy_attest_io_block __user *ulc)
+{
+       struct rtas_phy_attest_params *params;
+       struct papr_rtas_sequence seq = {};
+       int fd;
+
+       /*
+        * Freed in phy_attest_sequence_end().
+        */
+       params =  kzalloc(sizeof(*params), GFP_KERNEL_ACCOUNT);
+       if (!params)
+               return -ENOMEM;
+
+       if (copy_from_user(¶ms->cmd, ulc,
+                       sizeof(struct papr_phy_attest_io_block)))
+               return -EFAULT;
+
+       params->cmd_len = be32_to_cpu(params->cmd.length);
+       seq = (struct papr_rtas_sequence) {
+               .begin = phy_attest_sequence_begin,
+               .end = phy_attest_sequence_end,
+               .work = phy_attest_sequence_fill_work_area,
+       };
+
+       seq.params = (void *)params;
+
+       fd = papr_rtas_setup_file_interface(&seq,
+                       &papr_phy_attest_handle_ops,
+                       "[papr-physical-attestation]");
+
+       return fd;
+}
+
+/*
+ * Top-level ioctl handler for /dev/papr-physical-attestation.
+ */
+static long papr_phy_attest_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
+{
+       void __user *argp = (__force void __user *)arg;
+       long ret;
+
+       switch (ioctl) {
+       case PAPR_PHY_ATTEST_IOC_HANDLE:
+               ret = papr_phy_attest_create_handle(argp);
+               break;
+       default:
+               ret = -ENOIOCTLCMD;
+               break;
+       }
+       return ret;
+}
+
+static const struct file_operations papr_phy_attest_ops = {
+       .unlocked_ioctl = papr_phy_attest_dev_ioctl,
+};
+
+static struct miscdevice papr_phy_attest_dev = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "papr-physical-attestation",
+       .fops = &papr_phy_attest_ops,
+};
+
+static __init int papr_phy_attest_init(void)
+{
+       if (!rtas_function_implemented(RTAS_FN_IBM_PHYSICAL_ATTESTATION))
+               return -ENODEV;
+
+       return misc_register(&papr_phy_attest_dev);
+}
+machine_device_initcall(pseries, papr_phy_attest_init);