mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 23:09:29 +02:00
issei: initial driver skeleton
The ISSEI (Intel Silicon Security Engine Interface) subsystem provides a communication channel between the host and the Silicon Security Engine. Prepare basic driver functions and character device for user-space communication. Add DMA access routines for ISSEI HECI devices. Add of DMA-related structures and implementation of routines for setting up DMA, as well as reading and writing DMA buffers. Reviewed-by: Karol Wachowski <karol.wachowski@linux.intel.com> Co-developed-by: Vitaly Lubart <lubvital@gmail.com> Signed-off-by: Vitaly Lubart <lubvital@gmail.com> Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Link: https://patch.msgid.link/20260513-issei-for-upstream-v1-1-f590038678f9@intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
5d577fa6fe
commit
65f0ecb9ec
@@ -105,6 +105,7 @@ Subsystem-specific APIs
|
||||
interconnect
|
||||
ipmb
|
||||
ipmi
|
||||
issei/index
|
||||
libata
|
||||
mailbox
|
||||
md/index
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. include:: <isonum.txt>
|
||||
|
||||
=========================================================
|
||||
The Intel Silicon Security Engine Interface (Intel SSEI)
|
||||
=========================================================
|
||||
|
||||
**Copyright** |copy| 2026 Intel Corporation
|
||||
|
||||
|
||||
.. toctree::
|
||||
:caption: Table of Contents
|
||||
:maxdepth: 3
|
||||
|
||||
issei
|
||||
@@ -0,0 +1,135 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
The Intel Silicon Security Engine (Intel SSE) is an isolated and
|
||||
protected computing resource (Co-processor) residing inside
|
||||
Intel client chipsets released in 2024 (Lunar Lake) or later.
|
||||
The Intel SSE provide security support and platform boot orchestration.
|
||||
The actual feature set depends on the Intel chipset SKU.
|
||||
|
||||
The Intel Silicon Security Engine Interface (Intel SSEI)
|
||||
is the interface between the Host and Intel SSE.
|
||||
This interface is exposed to the host as one or more PCI devices.
|
||||
The Intel SSEI Driver is in charge of the communication channel between
|
||||
a host application and the Intel SSE features.
|
||||
|
||||
Each Intel SSE feature, or Intel SSE Client is addressed by a unique UUID and
|
||||
each client has its own protocol. The protocol is message-based with a
|
||||
header and payload up to maximal number of bytes advertised by the client,
|
||||
upon connection.
|
||||
|
||||
Intel SSEI Driver
|
||||
=================
|
||||
|
||||
The driver exposes a character device with device nodes /dev/isseiX.
|
||||
|
||||
An application maintains communication with an Intel SSE feature while
|
||||
/dev/isseiX is open. The binding to a specific feature is performed by calling
|
||||
:c:macro:`IOCTL_ISSEI_CONNECT_CLIENT`, which passes the desired UUID.
|
||||
The number of instances of an Intel SSE feature that can be opened
|
||||
at the same time is limited to single instance.
|
||||
|
||||
The driver is transparent to data that are passed between firmware feature
|
||||
and host application.
|
||||
|
||||
Because some of the Intel SSE features can change the system
|
||||
configuration, the driver by default allows only a privileged
|
||||
user to access it.
|
||||
|
||||
The connection termination is performed by calling
|
||||
:c:macro:`IOCTL_ISSEI_DISCONNECT_CLIENT`.
|
||||
|
||||
The session is terminated calling :c:expr:`close(fd)`.
|
||||
|
||||
A code snippet for an application communicating with SPDM client:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
struct issei_connect_client_data data = {.in_client_uuid =
|
||||
{0xe8, 0x51, 0x49, 0xdf, 0x94, 0x47, 0x4C,
|
||||
0x9A, 0x83, 0x67, 0xC4, 0xE3, 0x34, 0x64, 0xF1, 0xB4}};
|
||||
__u8 req_data[] = {0x10, 0x84, 0x00, 0x00}; /* SPDM Get Version */
|
||||
size_t req_data_len = sizeof(req_data);
|
||||
__u8 res_data[256];
|
||||
size_t res_data_len = sizeof(res_data);
|
||||
int fd = open("/dev/issei0", O_RDWR);
|
||||
|
||||
ioctl(fd, IOCTL_ISSEI_CONNECT_CLIENT, &data);
|
||||
|
||||
printf("Ver=%d, MaxLen=%u, Flags=0x%08X\n",
|
||||
data.out_client_properties.protocol_version,
|
||||
data.out_client_properties.max_msg_length,
|
||||
data.out_client_properties.flags);
|
||||
|
||||
[...]
|
||||
|
||||
write(fd, req_data, req_data_len);
|
||||
|
||||
[...]
|
||||
|
||||
read(fd, res_data, res_data_len);
|
||||
|
||||
printf("SPDM version count %u, version[0]=%02X%02X\n",
|
||||
res_data[5], res_data[6], res_data[7]);
|
||||
|
||||
[...]
|
||||
|
||||
ioctl(fd, IOCTL_ISSEI_DISCONNECT_CLIENT, &data);
|
||||
|
||||
[...]
|
||||
|
||||
close(fd);
|
||||
|
||||
|
||||
User space API ioctl
|
||||
====================
|
||||
|
||||
The Intel SSEI Driver supports the following ioctl commands:
|
||||
|
||||
IOCTL_ISSEI_CONNECT_CLIENT
|
||||
--------------------------
|
||||
Connect to firmware Feature/Client.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Usage:
|
||||
|
||||
struct issei_connect_client_data client_data;
|
||||
|
||||
ioctl(fd, IOCTL_ISSEI_CONNECT_CLIENT, &client_data);
|
||||
|
||||
struct issei_connect_client_data - contain the following
|
||||
Inputs:
|
||||
in_client_uuid - UUID of the FW Feature that needs to connect to.
|
||||
Outputs:
|
||||
out_client_properties - Client Properties: MTU, Protocol Version and Flags.
|
||||
|
||||
Error returns:
|
||||
ENOTTY No such client (i.e. wrong UUID) or connection is not allowed.
|
||||
EINVAL Wrong IOCTL Number
|
||||
ENODEV Device or Connection is not initialized or ready.
|
||||
ENOMEM Unable to allocate memory to client internal data.
|
||||
EFAULT Fatal Error (e.g. Unable to access user input data)
|
||||
EBUSY Connection Already Open
|
||||
|
||||
:Note:
|
||||
max_msg_length (MTU) in client properties describes the maximum
|
||||
data that can be sent or received. (e.g. with MTU=2K, can send
|
||||
requests up to bytes 2k and received responses up to 2k bytes).
|
||||
|
||||
IOCTL_ISSEI_DISCONNECT_CLIENT
|
||||
-----------------------------
|
||||
Disconnect from firmware Feature/Client.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
Usage:
|
||||
|
||||
ioctl(fd, IOCTL_ISSEI_DISCONNECT_CLIENT, NULL);
|
||||
|
||||
Error returns:
|
||||
EINVAL Wrong IOCTL Number
|
||||
ENODEV Device or Connection is not initialized or ready.
|
||||
ENOTCONN Feature/Client is not connected.
|
||||
@@ -13360,6 +13360,13 @@ F: drivers/platform/x86/intel/sdsi.c
|
||||
F: tools/arch/x86/intel_sdsi/
|
||||
F: tools/testing/selftests/drivers/sdsi/
|
||||
|
||||
INTEL SILICON SECURITY ENGINE INTERFACE (ISSEI)
|
||||
M: Alexander Usyskin <alexander.usyskin@intel.com>
|
||||
S: Supported
|
||||
F: Documentation/driver-api/issei/issei.rst
|
||||
F: drivers/misc/issei/
|
||||
F: include/uapi/linux/issei.h
|
||||
|
||||
INTEL SGX
|
||||
M: Jarkko Sakkinen <jarkko@kernel.org>
|
||||
R: Dave Hansen <dave.hansen@linux.intel.com>
|
||||
|
||||
@@ -651,4 +651,5 @@ source "drivers/misc/mchp_pci1xxxx/Kconfig"
|
||||
source "drivers/misc/keba/Kconfig"
|
||||
source "drivers/misc/amd-sbi/Kconfig"
|
||||
source "drivers/misc/rp1/Kconfig"
|
||||
source "drivers/misc/issei/Kconfig"
|
||||
endmenu
|
||||
|
||||
@@ -74,3 +74,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) += lan966x-pci.o
|
||||
obj-y += keba/
|
||||
obj-y += amd-sbi/
|
||||
obj-$(CONFIG_MISC_RP1) += rp1/
|
||||
obj-$(CONFIG_INTEL_SSEI) += issei/
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (C) 2023-2026 Intel Corporation
|
||||
config INTEL_SSEI
|
||||
tristate "Intel Silicon Security Engine Interface"
|
||||
help
|
||||
The ISSEI (Intel Silicon Security Engine Interface)
|
||||
subsystem provides a communication channel between the host and the
|
||||
Silicon Security Engine.
|
||||
Enable this driver to get SPDM and other features on Intel client CPUs
|
||||
released in 2024 (Lunar Lake) or later.
|
||||
|
||||
If selected, the /dev/isseiX device will be created.
|
||||
If in doubt, select N.
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (C) 2023-2026 Intel Corporation
|
||||
ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE='"INTEL_SSEI"'
|
||||
|
||||
obj-$(CONFIG_INTEL_SSEI) += issei.o
|
||||
issei-objs += cdev.o
|
||||
issei-objs += dma.o
|
||||
@@ -0,0 +1,219 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (C) 2023-2026 Intel Corporation */
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/issei.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
#include "issei_dev.h"
|
||||
#include "cdev.h"
|
||||
|
||||
struct class *issei_class;
|
||||
static dev_t issei_devt;
|
||||
|
||||
#define ISSEI_MAX_DEVS MINORMASK
|
||||
|
||||
static DEFINE_XARRAY_ALLOC(issei_minor_xa);
|
||||
|
||||
static ssize_t fw_ver_show(struct device *device,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct issei_device *idev = dev_get_drvdata(device);
|
||||
|
||||
return sysfs_emit(buf, "%u.%u.%u.%u\n", idev->fw_version[0], idev->fw_version[1],
|
||||
idev->fw_version[2], idev->fw_version[3]);
|
||||
}
|
||||
static DEVICE_ATTR_RO(fw_ver);
|
||||
|
||||
static struct attribute *issei_attrs[] = {
|
||||
&dev_attr_fw_ver.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(issei);
|
||||
|
||||
static const struct file_operations issei_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void issei_device_release(struct device *dev)
|
||||
{
|
||||
kfree(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static void issei_device_init(struct issei_device *idev, struct device *parent,
|
||||
const struct issei_dma_length *dma_length,
|
||||
const struct issei_hw_ops *ops)
|
||||
{
|
||||
idev->parent = parent;
|
||||
idev->power_down = false;
|
||||
init_waitqueue_head(&idev->wait_has_data);
|
||||
idev->has_data = false;
|
||||
init_waitqueue_head(&idev->wait_rst_state);
|
||||
idev->rst_state = ISSEI_RST_STATE_INIT;
|
||||
|
||||
mutex_init(&idev->client_lock);
|
||||
INIT_LIST_HEAD(&idev->host_client_list);
|
||||
idev->host_client_last_id = 0;
|
||||
idev->host_client_count = 0;
|
||||
INIT_LIST_HEAD(&idev->fw_client_list);
|
||||
INIT_LIST_HEAD(&idev->write_queue);
|
||||
idev->last_write_ts = 0;
|
||||
|
||||
idev->dma.length = *dma_length;
|
||||
|
||||
idev->ops = ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* issei_register: register issei character device
|
||||
* @hw_size: size of the hardware structure to allocate
|
||||
* @parent: parent device
|
||||
* @dma_length: structure with DMA sizes
|
||||
* @ops: hardware-related operations
|
||||
*
|
||||
* Return: pointer allocated to issei_device structure, error on failure
|
||||
*/
|
||||
struct issei_device *issei_register(size_t hw_size, struct device *parent,
|
||||
const struct issei_dma_length *dma_length,
|
||||
const struct issei_hw_ops *ops)
|
||||
{
|
||||
struct issei_device *idev;
|
||||
u32 minor;
|
||||
int ret, devno;
|
||||
|
||||
idev = kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL);
|
||||
if (!idev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
issei_device_init(idev, parent, dma_length, ops);
|
||||
|
||||
ret = xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEVS), GFP_KERNEL);
|
||||
if (ret < 0) {
|
||||
dev_err(&idev->dev, "Failed to allocate minor. ret = %d\n", ret);
|
||||
kfree(idev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
idev->minor = minor;
|
||||
devno = MKDEV(MAJOR(issei_devt), idev->minor);
|
||||
|
||||
device_initialize(&idev->dev);
|
||||
idev->dev.devt = devno;
|
||||
idev->dev.class = issei_class;
|
||||
idev->dev.parent = parent;
|
||||
idev->dev.groups = issei_groups;
|
||||
idev->dev.release = issei_device_release;
|
||||
dev_set_drvdata(&idev->dev, idev);
|
||||
|
||||
idev->cdev = cdev_alloc();
|
||||
if (!idev->cdev) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
idev->cdev->ops = &issei_fops;
|
||||
if (parent->driver)
|
||||
idev->cdev->owner = parent->driver->owner;
|
||||
cdev_set_parent(idev->cdev, &idev->dev.kobj);
|
||||
|
||||
ret = cdev_add(idev->cdev, devno, 1);
|
||||
if (ret) {
|
||||
dev_err(parent, "unable to add device %d:%u ret = %d\n",
|
||||
MAJOR(issei_devt), idev->minor, ret);
|
||||
goto err_del_cdev;
|
||||
}
|
||||
|
||||
ret = dev_set_name(&idev->dev, "issei%u", idev->minor);
|
||||
if (ret) {
|
||||
dev_err(parent, "unable to set name to device %d:%u ret = %d\n",
|
||||
MAJOR(issei_devt), idev->minor, ret);
|
||||
goto err_del_cdev;
|
||||
}
|
||||
|
||||
ret = device_add(&idev->dev);
|
||||
if (ret) {
|
||||
dev_err(parent, "unable to add device %d:%u ret = %d\n",
|
||||
MAJOR(issei_devt), idev->minor, ret);
|
||||
goto err_del_cdev;
|
||||
}
|
||||
|
||||
idev->fw_clients = kset_create_and_add("fw_clients", NULL, &idev->dev.kobj);
|
||||
if (!idev->fw_clients) {
|
||||
ret = -ENOMEM;
|
||||
goto err_del_dev;
|
||||
}
|
||||
|
||||
return idev;
|
||||
|
||||
err_del_dev:
|
||||
device_del(&idev->dev);
|
||||
err_del_cdev:
|
||||
cdev_del(idev->cdev);
|
||||
err:
|
||||
put_device(&idev->dev);
|
||||
xa_erase(&issei_minor_xa, minor);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(issei_register);
|
||||
|
||||
/**
|
||||
* issei_deregister: remove issei character device
|
||||
* @idev: the device structure
|
||||
*/
|
||||
void issei_deregister(struct issei_device *idev)
|
||||
{
|
||||
u32 minor = idev->minor;
|
||||
|
||||
cdev_del(idev->cdev);
|
||||
|
||||
kset_unregister(idev->fw_clients);
|
||||
|
||||
device_del(&idev->dev);
|
||||
|
||||
put_device(&idev->dev);
|
||||
|
||||
xa_erase(&issei_minor_xa, minor);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(issei_deregister);
|
||||
|
||||
static int __init issei_cdev_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
issei_class = class_create("issei");
|
||||
if (IS_ERR(issei_class)) {
|
||||
pr_err("couldn't create class\n");
|
||||
return PTR_ERR(issei_class);
|
||||
}
|
||||
|
||||
ret = alloc_chrdev_region(&issei_devt, 0, ISSEI_MAX_DEVS, "issei");
|
||||
if (ret < 0) {
|
||||
pr_err("unable to allocate char dev region\n");
|
||||
class_destroy(issei_class);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit issei_cdev_exit(void)
|
||||
{
|
||||
unregister_chrdev_region(issei_devt, ISSEI_MAX_DEVS);
|
||||
class_destroy(issei_class);
|
||||
}
|
||||
|
||||
module_init(issei_cdev_init);
|
||||
module_exit(issei_cdev_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2023-2026 Intel Corporation */
|
||||
#ifndef _ISSEI_CDEV_H_
|
||||
#define _ISSEI_CDEV_H_
|
||||
|
||||
struct device;
|
||||
struct issei_device;
|
||||
struct issei_dma_length;
|
||||
struct issei_hw_ops;
|
||||
|
||||
struct issei_device *issei_register(size_t hw_size, struct device *parent,
|
||||
const struct issei_dma_length *dma_length,
|
||||
const struct issei_hw_ops *ops);
|
||||
void issei_deregister(struct issei_device *idev);
|
||||
|
||||
#endif /* _ISSEI_CDEV_H_ */
|
||||
@@ -0,0 +1,154 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (C) 2023-2026 Intel Corporation */
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
#include "issei_dev.h"
|
||||
#include "hw_msg.h"
|
||||
|
||||
static inline size_t __issei_dma_size(const struct issei_dma *dma)
|
||||
{
|
||||
return dma->length.h2f + dma->length.f2h + dma->length.ctl;
|
||||
}
|
||||
|
||||
/**
|
||||
* issei_dmam_setup - setup DMA buffer and clean it
|
||||
* @idev: issei device object
|
||||
*
|
||||
* Return: 0 on success, <0 on failures
|
||||
*/
|
||||
int issei_dmam_setup(struct issei_device *idev)
|
||||
{
|
||||
struct issei_dma *dma = &idev->dma;
|
||||
size_t size;
|
||||
|
||||
size = __issei_dma_size(dma);
|
||||
if (!size)
|
||||
return -EINVAL;
|
||||
|
||||
if (!dma->vaddr)
|
||||
dma->vaddr = dmam_alloc_coherent(idev->parent, size, &dma->daddr,
|
||||
GFP_KERNEL | __GFP_ZERO);
|
||||
if (dma->vaddr)
|
||||
memset(dma->vaddr, 0, size);
|
||||
return dma->vaddr ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static inline struct control_buffer *__dma_get_ctl_buf(struct issei_dma *dma)
|
||||
{
|
||||
return dma->vaddr + dma->length.h2f + dma->length.f2h;
|
||||
}
|
||||
|
||||
static bool __issei_dma_is_read_busy(struct issei_dma *dma)
|
||||
{
|
||||
struct control_buffer *ctl = __dma_get_ctl_buf(dma);
|
||||
|
||||
return ctl->f2h_counter_wr != ctl->f2h_counter_rd;
|
||||
}
|
||||
|
||||
static bool __issei_dma_is_write_busy(struct issei_dma *dma)
|
||||
{
|
||||
struct control_buffer *ctl = __dma_get_ctl_buf(dma);
|
||||
|
||||
return ctl->h2f_counter_wr != ctl->h2f_counter_rd;
|
||||
}
|
||||
|
||||
static void __issei_dma_read_finalize(struct issei_device *idev)
|
||||
{
|
||||
struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma);
|
||||
|
||||
dev_dbg(&idev->dev, "ctl->f2h_counter_rd %u\n", ctl->f2h_counter_rd);
|
||||
/* No need to check overflow - the firmware counters overflow the same way */
|
||||
ctl->f2h_counter_rd++;
|
||||
}
|
||||
|
||||
static void __issei_dma_write_finalize(struct issei_device *idev)
|
||||
{
|
||||
struct control_buffer *ctl = __dma_get_ctl_buf(&idev->dma);
|
||||
|
||||
dev_dbg(&idev->dev, "ctl->h2f_counter_wr %u\n", ctl->h2f_counter_wr);
|
||||
/* No need to check overflow - the firmware counters overflow the same way */
|
||||
ctl->h2f_counter_wr++;
|
||||
}
|
||||
|
||||
/**
|
||||
* issei_dma_write - write data package to DMA
|
||||
* @idev: issei device object
|
||||
* @data: data atructure
|
||||
*
|
||||
* Return: 0 on success, <0 on failures
|
||||
*/
|
||||
int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data)
|
||||
{
|
||||
u8 *write_buf = idev->dma.vaddr;
|
||||
struct ham_message_header *hdr = (struct ham_message_header *)write_buf;
|
||||
|
||||
if (data->length > idev->dma.length.h2f - sizeof(*hdr)) {
|
||||
dev_err(&idev->dev, "Message is too big\n");
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
if (__issei_dma_is_write_busy(&idev->dma)) {
|
||||
if (ktime_ms_delta(ktime_get(), idev->last_write_ts) > ISSEI_WRITE_TIMEOUT_MSEC) {
|
||||
dev_err(&idev->dev, "Write stuck in queue\n");
|
||||
return -EIO;
|
||||
}
|
||||
dev_info(&idev->dev, "Write is busy\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
hdr->length = data->length;
|
||||
hdr->fw_id = data->fw_id;
|
||||
hdr->host_id = data->host_id;
|
||||
hdr->flags = data->flags;
|
||||
hdr->status = data->status;
|
||||
hdr->reserved = 0;
|
||||
|
||||
memcpy(write_buf + sizeof(*hdr), data->buf, data->length);
|
||||
|
||||
__issei_dma_write_finalize(idev);
|
||||
idev->last_write_ts = ktime_get();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* issei_dma_read - read data package from DMA
|
||||
* @idev: issei device object
|
||||
* @data: data atructure
|
||||
*
|
||||
* Return: %0 on success, <0 on failures
|
||||
*/
|
||||
int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data)
|
||||
{
|
||||
u8 *read_buf = idev->dma.vaddr + idev->dma.length.h2f;
|
||||
struct ham_message_header *hdr = (struct ham_message_header *)read_buf;
|
||||
|
||||
if (!__issei_dma_is_read_busy(&idev->dma)) {
|
||||
dev_dbg(&idev->dev, "Nothing to read\n");
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
dev_dbg(&idev->dev, "Reading header\n");
|
||||
data->length = hdr->length;
|
||||
data->fw_id = hdr->fw_id;
|
||||
data->host_id = hdr->host_id;
|
||||
data->flags = hdr->flags;
|
||||
data->status = hdr->status;
|
||||
|
||||
if (data->length > idev->dma.length.f2h - sizeof(*hdr)) {
|
||||
dev_err(&idev->dev, "Message length %u is bigger than buffer %zu\n",
|
||||
data->length, idev->dma.length.f2h - sizeof(*hdr));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev_dbg(&idev->dev, "Reading data (size %u)\n", data->length);
|
||||
data->buf = kmemdup(read_buf + sizeof(*hdr), data->length, GFP_KERNEL);
|
||||
if (!data->buf)
|
||||
return -ENOMEM;
|
||||
__issei_dma_read_finalize(idev);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2023-2026 Intel Corporation */
|
||||
#ifndef _ISSEI_DMA_H_
|
||||
#define _ISSEI_DMA_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct issei_device;
|
||||
|
||||
/**
|
||||
* struct issei_dma_length - sizes of DMA memory portions
|
||||
* @h2f: host to firmware buffer size
|
||||
* @f2h: firmware to host buffer size
|
||||
* @ctl: control buffer size
|
||||
*/
|
||||
struct issei_dma_length {
|
||||
size_t h2f;
|
||||
size_t f2h;
|
||||
size_t ctl;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct issei_dma - DMA memory structure
|
||||
* @vaddr: virtual address
|
||||
* @daddr: physical address
|
||||
* @length: memory sizes structure
|
||||
*/
|
||||
struct issei_dma {
|
||||
void *vaddr;
|
||||
dma_addr_t daddr;
|
||||
struct issei_dma_length length;
|
||||
};
|
||||
|
||||
/* Operation statuses */
|
||||
#define HAMS_SUCCESS 0x00
|
||||
#define HAMS_PROTOCOL_NOT_SUPPORTED 0x01
|
||||
#define HAMS_DEPRECATED_BUS_MSG 0x02
|
||||
#define HAMS_CLIENT_NOT_EXISTS 0x03
|
||||
#define HAMS_MSG_TOO_BIG 0x04
|
||||
#define HAMS_MSG_NOT_CONSUMED 0x05
|
||||
#define HAMS_CORRUPTED_BUS_MSG 0x06
|
||||
#define HAMS_CORRUPTED_HEADER 0x07
|
||||
#define HAMS_INVALID_LENGTH 0x08
|
||||
#define HAMS_SHARED_MEMORY_SIZE_UNSUPPORTED 0x09
|
||||
#define HAMS_GENERAL_FATAL_ERROR 0xff
|
||||
|
||||
/**
|
||||
* struct issei_dma_data - data passed through channel
|
||||
* @fw_id: firmware client id
|
||||
* @host_id: host client id
|
||||
* @flags: flags bitmap
|
||||
* @status: operation status
|
||||
* @length: data length
|
||||
* @buf: pointer to data buffer
|
||||
*/
|
||||
struct issei_dma_data {
|
||||
u16 fw_id;
|
||||
u16 host_id;
|
||||
u32 flags;
|
||||
u32 status;
|
||||
u32 length;
|
||||
void *buf;
|
||||
};
|
||||
|
||||
int issei_dmam_setup(struct issei_device *idev);
|
||||
int issei_dma_write(struct issei_device *idev, const struct issei_dma_data *data);
|
||||
int issei_dma_read(struct issei_device *idev, struct issei_dma_data *data);
|
||||
|
||||
#endif /*_ISSEI_DMA_H_*/
|
||||
@@ -0,0 +1,163 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2023-2026 Intel Corporation */
|
||||
#ifndef _ISSEI_HW_MSG_H_
|
||||
#define _ISSEI_HW_MSG_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/uuid.h>
|
||||
|
||||
#define HAM_CB_MESSAGE_ID_REQ 0x8086cafe
|
||||
#define HAM_CB_MESSAGE_ID_RES 0xcafe8086
|
||||
#define HAM_CB_MESSAGE_VER 0x1
|
||||
|
||||
/**
|
||||
* struct ham_setup_shared_memory_req - shared memory setup request
|
||||
* @msg_id: message id, should be %HAM_CB_MESSAGE_ID_REQ
|
||||
* @ver: message version (%HAM_CB_MESSAGE_VER)
|
||||
* @reserved: reserved
|
||||
* @buffer_physical_address: physical address of DMA buffer
|
||||
* @host_to_fw_section_length: memory size for host to fw communication
|
||||
* @fw_to_host_section_length: memory size for fw to host communication
|
||||
* @control_length: memory size for control buffer
|
||||
*/
|
||||
struct ham_setup_shared_memory_req {
|
||||
u32 msg_id;
|
||||
u16 ver;
|
||||
u16 reserved;
|
||||
u64 buffer_physical_address;
|
||||
u32 host_to_fw_section_length;
|
||||
u32 fw_to_host_section_length;
|
||||
u32 control_length;
|
||||
} __packed __aligned(4);
|
||||
|
||||
/**
|
||||
* struct ham_setup_shared_memory_res - shared memory setup response
|
||||
* @msg_id: message id, should be %HAM_CB_MESSAGE_ID_RES
|
||||
* @status: operation status
|
||||
*/
|
||||
struct ham_setup_shared_memory_res {
|
||||
u32 msg_id;
|
||||
u32 status;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct control_buffer - control buffer structure
|
||||
* @h2f_counter_wr: write counter host to fw
|
||||
* @h2f_counter_rd: read counter host to fw
|
||||
* @f2h_counter_wr: write counter fw to host
|
||||
* @f2h_counter_rd: read counter fw to host
|
||||
*/
|
||||
struct control_buffer {
|
||||
u32 h2f_counter_wr;
|
||||
u32 h2f_counter_rd;
|
||||
u32 f2h_counter_wr;
|
||||
u32 f2h_counter_rd;
|
||||
};
|
||||
|
||||
/* HAM messages over DMA */
|
||||
|
||||
/**
|
||||
* struct ham_message_header - message header over DMA
|
||||
* @length: message length (payload only, not including header)
|
||||
* @fw_id: firmware client id (0 means Bus Message)
|
||||
* @host_id: host client id (0 means Bus Message)
|
||||
* @flags: message flags
|
||||
* @status: operation status
|
||||
* @reserved: reserved
|
||||
*/
|
||||
struct ham_message_header {
|
||||
u32 length;
|
||||
u16 fw_id;
|
||||
u16 host_id;
|
||||
u32 flags;
|
||||
u32 status;
|
||||
u32 reserved;
|
||||
};
|
||||
|
||||
/* Bus Commands */
|
||||
#define HAM_BUS_CMD_START_REQ 0x00
|
||||
#define HAM_BUS_CMD_START_RSP 0x80
|
||||
#define HAM_BUS_CMD_CLIENT_REQ 0x01
|
||||
#define HAM_BUS_CMD_CLIENT_RSP 0x81
|
||||
|
||||
/**
|
||||
* struct ham_bus_message - bus message header
|
||||
* @cmd: command code
|
||||
*/
|
||||
struct ham_bus_message {
|
||||
u32 cmd;
|
||||
};
|
||||
|
||||
#define HAM_SUPPORTED_VERSION 0x01
|
||||
|
||||
/**
|
||||
* struct ham_start_message_req - start message
|
||||
* @header: bus message header (%HAM_BUS_CMD_START_REQ)
|
||||
* @supported_version: supported protocol version
|
||||
* @heci_capabilities_length: protocol capabilities length in bytes
|
||||
* @heci_capabilities: protocol capabilities data
|
||||
*/
|
||||
struct ham_start_message_req {
|
||||
struct ham_bus_message header;
|
||||
u16 supported_version;
|
||||
u8 heci_capabilities_length;
|
||||
u8 heci_capabilities[] __counted_by(heci_capabilities_length);
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ham_start_message_res - start message response
|
||||
* @header: bus message header (%HAM_BUS_CMD_START_RSP)
|
||||
* @fw_version: firmware version (four u16 blocks)
|
||||
* @supported_version: supported protocol version
|
||||
* @heci_capabilities_length: protocol capabilities length in bytes
|
||||
* @heci_capabilities: protocol capabilities data
|
||||
*/
|
||||
struct ham_start_message_res {
|
||||
struct ham_bus_message header;
|
||||
u16 fw_version[4];
|
||||
u16 supported_version;
|
||||
u8 heci_capabilities_length;
|
||||
u8 heci_capabilities[] __counted_by(heci_capabilities_length);
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct ham_get_clients_req - clients list request
|
||||
* @header: bus message header (%HAM_BUS_CMD_CLIENT_REQ)
|
||||
*/
|
||||
struct ham_get_clients_req {
|
||||
struct ham_bus_message header;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ham_client_properties - single client properties
|
||||
* @client_number: client id in firmware
|
||||
* @protocol_ver: client protocol version
|
||||
* @reserved: reserved
|
||||
* @client_uuid: protocol name (UUID)
|
||||
* @client_mtu: max message length supported by client
|
||||
* @flags: client flags
|
||||
*/
|
||||
struct ham_client_properties {
|
||||
u16 client_number;
|
||||
u8 protocol_ver;
|
||||
u8 reserved;
|
||||
uuid_t client_uuid;
|
||||
u32 client_mtu;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ham_get_clients_res - client properties response
|
||||
* @header: bus message header (%HAM_BUS_CMD_CLIENT_RSP)
|
||||
* @client_count: number of clients in firmware
|
||||
* @reserved: reserved
|
||||
* @clients_props: list of client properties
|
||||
*/
|
||||
struct ham_get_clients_res {
|
||||
struct ham_bus_message header;
|
||||
u16 client_count;
|
||||
u16 reserved;
|
||||
struct ham_client_properties clients_props[] __counted_by(client_count);
|
||||
};
|
||||
|
||||
#endif /* _ISSEI_HW_MSG_H_ */
|
||||
@@ -0,0 +1,160 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (C) 2023-2026 Intel Corporation */
|
||||
#ifndef _ISSEI_DEV_H_
|
||||
#define _ISSEI_DEV_H_
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/time64.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include "dma.h"
|
||||
|
||||
struct cdev;
|
||||
struct kset;
|
||||
|
||||
struct issei_device;
|
||||
struct issei_host_client;
|
||||
|
||||
extern struct class *issei_class;
|
||||
|
||||
#define ISSEI_HOST_CLIENTS_MAX 255
|
||||
|
||||
#define ISSEI_SUPPORTED_PROTOCOL_VER 1
|
||||
|
||||
#define ISSEI_MAX_CONSEC_RESET 3
|
||||
|
||||
#define ISSEI_RST_HW_READY_TIMEOUT_MSEC (2 * MSEC_PER_SEC)
|
||||
#define ISSEI_RST_STEP_TIMEOUT_MSEC (2 * MSEC_PER_SEC)
|
||||
#define ISSEI_STOP_TIMEOUT_MSEC 500
|
||||
#define ISSEI_WRITE_TIMEOUT_MSEC (MSEC_PER_SEC)
|
||||
|
||||
/**
|
||||
* struct issei_write_buf - write buffer object
|
||||
* @list: linked list pointer
|
||||
* @cl: host client that requested this write
|
||||
* @data: data to write
|
||||
* @data_size: data size
|
||||
*/
|
||||
struct issei_write_buf {
|
||||
struct list_head list;
|
||||
struct issei_host_client *cl;
|
||||
const u8 *data;
|
||||
size_t data_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct issei_hw_ops - callbacks for hardware operations
|
||||
* @irq_clear: clear irq
|
||||
* @irq_enable: enable irq
|
||||
* @irq_disable: disable irq
|
||||
* @irq_sync: sync irq
|
||||
* @hw_reset: initiate hardware reset
|
||||
* @hw_config: initial hardware config
|
||||
* @hw_is_ready: check if hardware is ready
|
||||
* @hw_reset_release: release hardware from reset
|
||||
* @host_set_ready: set host ready indicator
|
||||
* @setup_message_send: send setup message
|
||||
* @setup_message_recv: receive setup message
|
||||
* @irq_write_generate: generate interrupt on write complete
|
||||
*/
|
||||
struct issei_hw_ops {
|
||||
void (*irq_clear)(struct issei_device *idev);
|
||||
void (*irq_enable)(struct issei_device *idev);
|
||||
void (*irq_disable)(struct issei_device *idev);
|
||||
void (*irq_sync)(struct issei_device *idev);
|
||||
int (*hw_reset)(struct issei_device *idev, bool enable);
|
||||
int (*hw_config)(struct issei_device *idev);
|
||||
bool (*hw_is_ready)(struct issei_device *idev);
|
||||
void (*hw_reset_release)(struct issei_device *idev);
|
||||
void (*host_set_ready)(struct issei_device *idev);
|
||||
int (*setup_message_send)(struct issei_device *idev);
|
||||
int (*setup_message_recv)(struct issei_device *idev);
|
||||
int (*irq_write_generate)(struct issei_device *idev);
|
||||
};
|
||||
|
||||
/**
|
||||
* enum issei_rst_state: driver reset flow states
|
||||
* @ISSEI_RST_STATE_INIT: initial state
|
||||
* @ISSEI_RST_STATE_HW_READY: waiting for HW to be ready
|
||||
* @ISSEI_RST_STATE_SETUP: waiting for channel setup completion
|
||||
* @ISSEI_RST_STATE_START: waiting for start handshake completion
|
||||
* @ISSEI_RST_STATE_CLIENT_ENUM: waiting for client enumeration
|
||||
* @ISSEI_RST_STATE_DONE: reset flow is done
|
||||
* @ISSEI_RST_STATE_DISABLED: flow is disabled
|
||||
*/
|
||||
enum issei_rst_state {
|
||||
ISSEI_RST_STATE_INIT,
|
||||
ISSEI_RST_STATE_HW_READY,
|
||||
ISSEI_RST_STATE_SETUP,
|
||||
ISSEI_RST_STATE_START,
|
||||
ISSEI_RST_STATE_CLIENT_ENUM,
|
||||
ISSEI_RST_STATE_DONE,
|
||||
ISSEI_RST_STATE_DISABLED,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct issei_device - issei device
|
||||
* @parent: parent device object
|
||||
* @dev: associated device object
|
||||
* @cdev: character device
|
||||
* @minor: allocated minor number
|
||||
* @wait_has_data: wait queue for data
|
||||
* @has_data: there are data to process
|
||||
* @power_down: device is powering down
|
||||
* @wait_rst_state: waitqueue for reset state processing
|
||||
* @rst_state: reset state
|
||||
* @fw_protocol_ver: protocol version
|
||||
* @fw_version: firmware version
|
||||
* @process_thread: worker thread
|
||||
* @reset_count: number of consecutive link reset attempts
|
||||
* @all_reset_count: cumilative number of link reset attempts
|
||||
* @client_lock: mutex to protect client lists and write queue
|
||||
* @host_client_list: host clients list
|
||||
* @host_client_last_id: last allocated host client id
|
||||
* @host_client_count: number of active host clients
|
||||
* @fw_client_list: firmware clients list
|
||||
* @write_queue: write queue
|
||||
* @last_write_ts: last write timestamp
|
||||
* @dma: DMA memory configuration
|
||||
* @ops: hardware operations
|
||||
* @hw: hw-specific data
|
||||
*/
|
||||
struct issei_device {
|
||||
struct device *parent;
|
||||
struct device dev;
|
||||
struct cdev *cdev;
|
||||
u32 minor;
|
||||
wait_queue_head_t wait_has_data;
|
||||
bool has_data;
|
||||
bool power_down;
|
||||
wait_queue_head_t wait_rst_state;
|
||||
enum issei_rst_state rst_state;
|
||||
u16 fw_protocol_ver;
|
||||
u16 fw_version[4];
|
||||
/* reset flow */
|
||||
struct task_struct *process_thread;
|
||||
u8 reset_count;
|
||||
u8 all_reset_count;
|
||||
/* clients */
|
||||
struct mutex client_lock;
|
||||
struct list_head host_client_list;
|
||||
u16 host_client_last_id;
|
||||
u8 host_client_count;
|
||||
struct kset *fw_clients;
|
||||
struct list_head fw_client_list;
|
||||
struct list_head write_queue;
|
||||
ktime_t last_write_ts;
|
||||
struct issei_dma dma;
|
||||
const struct issei_hw_ops *ops;
|
||||
char hw[];
|
||||
};
|
||||
|
||||
static inline void issei_poke_process_thread(struct issei_device *idev)
|
||||
{
|
||||
WRITE_ONCE(idev->has_data, true);
|
||||
wake_up_interruptible(&idev->wait_has_data);
|
||||
}
|
||||
#endif /* _ISSEI_DEV_H_ */
|
||||
@@ -0,0 +1,69 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Copyright (C) 2023-2026 Intel Corporation
|
||||
* Intel Silicon Security Engine Interface (ISSEI) Linux driver:
|
||||
* ISSEI Interface Header
|
||||
*/
|
||||
#ifndef _LINUX_ISSEI_H
|
||||
#define _LINUX_ISSEI_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* This ioctl is used to associate the current file descriptor with a
|
||||
* FW Client (given by UUID). This opens a communication channel
|
||||
* between a host client and a FW client. From this point every read and write
|
||||
* will communicate with the associated FW client.
|
||||
* The communication between the clients can be terminated by
|
||||
* IOCTL_ISSEI_DISCONNECT_CLIENT IOCTL or by
|
||||
* closing the file descriptor (file_operation release()).
|
||||
*
|
||||
* The ioctl argument is a struct with a union that contains
|
||||
* the input parameter and the output parameter for this ioctl.
|
||||
*
|
||||
* The input parameter is UUID of the FW Client.
|
||||
* The output parameter is the properties of the FW client
|
||||
* (FW protocol version, max message size and client flags).
|
||||
*/
|
||||
#define IOCTL_ISSEI_CONNECT_CLIENT \
|
||||
_IOWR('H', 0x01, struct issei_connect_client_data)
|
||||
|
||||
/**
|
||||
* struct issei_client - ISSEI client information structure
|
||||
* @max_msg_length: maximum message length supported by the firmware client (in bytes)
|
||||
* @protocol_version: protocol version reported by the firmware client
|
||||
* @reserved1: reserved
|
||||
* @flags: flag bitmask reported by the firmware client
|
||||
* @reserved2: reserved
|
||||
*/
|
||||
struct issei_client {
|
||||
__u32 max_msg_length;
|
||||
__u8 protocol_version;
|
||||
__u8 reserved1[3];
|
||||
__u32 flags;
|
||||
__u32 reserved2;
|
||||
};
|
||||
|
||||
#define ISSEI_IOCTL_UUID_LEN 16
|
||||
|
||||
/**
|
||||
* struct issei_connect_client_data - ioctl Connect Client Data structure
|
||||
* @in_client_uuid: unique id of the firmware client to connect to (from user space to kernel)
|
||||
* @out_client_properties: connected firmware client properties (from kernel to user space)
|
||||
*/
|
||||
struct issei_connect_client_data {
|
||||
union {
|
||||
__u8 in_client_uuid[ISSEI_IOCTL_UUID_LEN];
|
||||
struct issei_client out_client_properties;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* This ioctl is used to terminate association between
|
||||
* the host client and the FW client.
|
||||
*/
|
||||
#define IOCTL_ISSEI_DISCONNECT_CLIENT \
|
||||
_IO('H', 0x02)
|
||||
|
||||
#endif /* _LINUX_ISSEI_H */
|
||||
Reference in New Issue
Block a user