mirror of https://github.com/torvalds/linux.git
289 lines
8.2 KiB
C
289 lines
8.2 KiB
C
// 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);
|