linux-loongson/include/linux/fwctl.h
Jason Gunthorpe 840cfb7cf5 fwctl: FWCTL_RPC to execute a Remote Procedure Call to device firmware
Add the FWCTL_RPC ioctl which allows a request/response RPC call to device
firmware. Drivers implementing this call must follow the security
guidelines under Documentation/userspace-api/fwctl.rst

The core code provides some memory management helpers to get the messages
copied from and back to userspace. The driver is responsible for
allocating the output message memory and delivering the message to the
device.

Link: https://patch.msgid.link/r/5-v5-642aa0c94070+4447f-fwctl_jgg@nvidia.com
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Shannon Nelson <shannon.nelson@amd.com>
Tested-by: Dave Jiang <dave.jiang@intel.com>
Tested-by: Shannon Nelson <shannon.nelson@amd.com>
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
2025-03-06 15:13:13 -04:00

136 lines
4.3 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
*/
#ifndef __LINUX_FWCTL_H
#define __LINUX_FWCTL_H
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/cleanup.h>
#include <uapi/fwctl/fwctl.h>
struct fwctl_device;
struct fwctl_uctx;
/**
* struct fwctl_ops - Driver provided operations
*
* fwctl_unregister() will wait until all excuting ops are completed before it
* returns. Drivers should be mindful to not let their ops run for too long as
* it will block device hot unplug and module unloading.
*/
struct fwctl_ops {
/**
* @device_type: The drivers assigned device_type number. This is uABI.
*/
enum fwctl_device_type device_type;
/**
* @uctx_size: The size of the fwctl_uctx struct to allocate. The first
* bytes of this memory will be a fwctl_uctx. The driver can use the
* remaining bytes as its private memory.
*/
size_t uctx_size;
/**
* @open_uctx: Called when a file descriptor is opened before the uctx
* is ever used.
*/
int (*open_uctx)(struct fwctl_uctx *uctx);
/**
* @close_uctx: Called when the uctx is destroyed, usually when the FD
* is closed.
*/
void (*close_uctx)(struct fwctl_uctx *uctx);
/**
* @info: Implement FWCTL_INFO. Return a kmalloc() memory that is copied
* to out_device_data. On input length indicates the size of the user
* buffer on output it indicates the size of the memory. The driver can
* ignore length on input, the core code will handle everything.
*/
void *(*info)(struct fwctl_uctx *uctx, size_t *length);
/**
* @fw_rpc: Implement FWCTL_RPC. Deliver rpc_in/in_len to the FW and
* return the response and set out_len. rpc_in can be returned as the
* response pointer. Otherwise the returned pointer is freed with
* kvfree().
*/
void *(*fw_rpc)(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
void *rpc_in, size_t in_len, size_t *out_len);
};
/**
* struct fwctl_device - Per-driver registration struct
* @dev: The sysfs (class/fwctl/fwctlXX) device
*
* Each driver instance will have one of these structs with the driver private
* data following immediately after. This struct is refcounted, it is freed by
* calling fwctl_put().
*/
struct fwctl_device {
struct device dev;
/* private: */
struct cdev cdev;
/* Protect uctx_list */
struct mutex uctx_list_lock;
struct list_head uctx_list;
/*
* Protect ops, held for write when ops becomes NULL during unregister,
* held for read whenever ops is loaded or an ops function is running.
*/
struct rw_semaphore registration_lock;
const struct fwctl_ops *ops;
};
struct fwctl_device *_fwctl_alloc_device(struct device *parent,
const struct fwctl_ops *ops,
size_t size);
/**
* fwctl_alloc_device - Allocate a fwctl
* @parent: Physical device that provides the FW interface
* @ops: Driver ops to register
* @drv_struct: 'struct driver_fwctl' that holds the struct fwctl_device
* @member: Name of the struct fwctl_device in @drv_struct
*
* This allocates and initializes the fwctl_device embedded in the drv_struct.
* Upon success the pointer must be freed via fwctl_put(). Returns a 'drv_struct
* \*' on success, NULL on error.
*/
#define fwctl_alloc_device(parent, ops, drv_struct, member) \
({ \
static_assert(__same_type(struct fwctl_device, \
((drv_struct *)NULL)->member)); \
static_assert(offsetof(drv_struct, member) == 0); \
(drv_struct *)_fwctl_alloc_device(parent, ops, \
sizeof(drv_struct)); \
})
static inline struct fwctl_device *fwctl_get(struct fwctl_device *fwctl)
{
get_device(&fwctl->dev);
return fwctl;
}
static inline void fwctl_put(struct fwctl_device *fwctl)
{
put_device(&fwctl->dev);
}
DEFINE_FREE(fwctl, struct fwctl_device *, if (_T) fwctl_put(_T));
int fwctl_register(struct fwctl_device *fwctl);
void fwctl_unregister(struct fwctl_device *fwctl);
/**
* struct fwctl_uctx - Per user FD context
* @fwctl: fwctl instance that owns the context
*
* Every FD opened by userspace will get a unique context allocation. Any driver
* private data will follow immediately after.
*/
struct fwctl_uctx {
struct fwctl_device *fwctl;
/* private: */
/* Head at fwctl_device::uctx_list */
struct list_head uctx_list_entry;
};
#endif