From nobody Fri Jun 12 17:36:49 2026 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D2C33427A10 for ; Wed, 13 May 2026 14:40:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.16 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778683240; cv=none; b=I+FvB2jbm/+VbZPX+lUJUBcBSL0mMe+s7SFoXFIxsTD06BGxQT/6uS4XnF12VtILW4pn2rbq2GbMROEtVTB7Uab05PI0vSdRWq1KMhQUn1jHTbuifWXftQNi2GHLsXWIfYyyxM6Ufpf03+Zzu7XfWcvrx2p82id27xyjTvnvlqk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778683240; c=relaxed/simple; bh=8FJuvml63+MmLhlQmZw00XiT1+DsIf+GRXu+GPLQ0Pk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=k+nRJJVXmTEXr9zD28IlCriXHyVBTD9a2F+nB1uD+enhitfnXL+S1Ls8tonfY8xqHnyTU5HrLHiC3jW6n5h/GN1cxUb2oyenwPW26ux1JnMGRXJ/BCvYTezvrOKT4Dyn/mxbusha4DM0nAuw4AI6RgWUUBfbaEUT/7gP/tSz18E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=Vy+X7srW; arc=none smtp.client-ip=192.198.163.16 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="Vy+X7srW" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1778683238; x=1810219238; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=8FJuvml63+MmLhlQmZw00XiT1+DsIf+GRXu+GPLQ0Pk=; b=Vy+X7srWK7JRh5rFh9XmKQ65Ac2L2cGYlxVrwq6/ZzPOov0AmZ9Q5ykK 7Pzh08nHzKAccInjQ84IjEfQs9wPA8xFS9sZBS8er2G439ZHk96tBO0w7 TtEQNF5ozcFwTtwvwCLA43QrqY9vPjeZwnGRAOZaP/rasbAkK/Vk3lW/n yCXmEVvrFiKok32WYVbItcoZJjsSyPW7y32Ltl0xR2FLMTJOOaj2ast6M SlTeYAaVL821nPyQO6VIgAQ9RN/N7nO4slHulKlRp2hgUVwYVVIqJ3L34 LFeH0dl/M7dSUt1FRnCYvWSnI4HpAtXPW4xAzFU66EyZTQwGfwhynNMVF w==; X-CSE-ConnectionGUID: R5s6vcQ2S6Sq0neXsDccVA== X-CSE-MsgGUID: jWeE2b4OQSKQFbNcAOOZQw== X-IronPort-AV: E=McAfee;i="6800,10657,11784"; a="67141721" X-IronPort-AV: E=Sophos;i="6.23,232,1770624000"; d="scan'208";a="67141721" Received: from orviesa004.jf.intel.com ([10.64.159.144]) by fmvoesa110.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 May 2026 07:40:37 -0700 X-CSE-ConnectionGUID: /DWUDi+CSm2smr0pBwdh+g== X-CSE-MsgGUID: uf658xfsS6qeSE5nrkSuEQ== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.23,232,1770624000"; d="scan'208";a="242447859" Received: from sannilnx-dsk.jer.intel.com (HELO [127.0.1.1]) ([10.12.231.107]) by orviesa004-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 May 2026 07:40:36 -0700 From: Alexander Usyskin Date: Wed, 13 May 2026 17:18:42 +0300 Subject: [PATCH 1/4] issei: initial driver skeleton Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260513-issei-for-upstream-v1-1-f590038678f9@intel.com> References: <20260513-issei-for-upstream-v1-0-f590038678f9@intel.com> In-Reply-To: <20260513-issei-for-upstream-v1-0-f590038678f9@intel.com> To: Greg Kroah-Hartman Cc: Menachem Adin , Alexander Usyskin , linux-kernel@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1778681944; l=36151; i=alexander.usyskin@intel.com; s=20260315; h=from:subject:message-id; bh=8FJuvml63+MmLhlQmZw00XiT1+DsIf+GRXu+GPLQ0Pk=; b=wz17XiVnNUMaBslIMK7OwyD3P8qdnAvVeTVO9hXWtw6y8c1kFO7emlB1N98C2G8GcQqONHl++ meVfrEFY4tyCATKfFgE/rN7abr9kAGXXW7nADOsBmZliaKxEIrb5QVe X-Developer-Key: i=alexander.usyskin@intel.com; a=ed25519; pk=X+qoF/nFCdDOV04IForWSxnkyoCAbUE10egZi6PSfcU= 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 Co-developed-by: Vitaly Lubart Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin --- Documentation/driver-api/index.rst | 1 + Documentation/driver-api/issei/index.rst | 16 +++ Documentation/driver-api/issei/issei.rst | 135 +++++++++++++++++++ MAINTAINERS | 7 + drivers/misc/Kconfig | 1 + drivers/misc/Makefile | 1 + drivers/misc/issei/Kconfig | 13 ++ drivers/misc/issei/Makefile | 7 + drivers/misc/issei/cdev.c | 219 +++++++++++++++++++++++++++= ++++ drivers/misc/issei/cdev.h | 16 +++ drivers/misc/issei/dma.c | 154 ++++++++++++++++++++++ drivers/misc/issei/dma.h | 69 ++++++++++ drivers/misc/issei/hw_msg.h | 163 +++++++++++++++++++++++ drivers/misc/issei/issei_dev.h | 160 ++++++++++++++++++++++ include/uapi/linux/issei.h | 69 ++++++++++ 15 files changed, 1031 insertions(+) diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/= index.rst index eaf7161ff957..6601a258690f 100644 --- a/Documentation/driver-api/index.rst +++ b/Documentation/driver-api/index.rst @@ -105,6 +105,7 @@ Subsystem-specific APIs interconnect ipmb ipmi + issei/index libata mailbox md/index diff --git a/Documentation/driver-api/issei/index.rst b/Documentation/drive= r-api/issei/index.rst new file mode 100644 index 000000000000..604267463fd4 --- /dev/null +++ b/Documentation/driver-api/issei/index.rst @@ -0,0 +1,16 @@ +.. SPDX-License-Identifier: GPL-2.0 + +.. include:: + +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D +The Intel Silicon Security Engine Interface (Intel SSEI) +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D + +**Copyright** |copy| 2026 Intel Corporation + + +.. toctree:: + :caption: Table of Contents + :maxdepth: 3 + + issei diff --git a/Documentation/driver-api/issei/issei.rst b/Documentation/drive= r-api/issei/issei.rst new file mode 100644 index 000000000000..a5e99e92e095 --- /dev/null +++ b/Documentation/driver-api/issei/issei.rst @@ -0,0 +1,135 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Introduction +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +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 +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +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 cal= ling +: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 =3D {.in_client_uuid =3D + {0xe8, 0x51, 0x49, 0xdf, 0x94, 0x47, 0x4C, + 0x9A, 0x83, 0x67, 0xC4, 0xE3, 0x34, 0x64, 0xF1, 0xB4}}; + __u8 req_data[] =3D {0x10, 0x84, 0x00, 0x00}; /* SPDM Get Version = */ + size_t req_data_len =3D sizeof(req_data); + __u8 res_data[256]; + size_t res_data_len =3D sizeof(res_data); + int fd =3D open("/dev/issei0", O_RDWR); + + ioctl(fd, IOCTL_ISSEI_CONNECT_CLIENT, &data); + + printf("Ver=3D%d, MaxLen=3D%u, Flags=3D0x%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]=3D%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 +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + +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 V= ersion 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=3D2K, 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. diff --git a/MAINTAINERS b/MAINTAINERS index 882214b0e7db..9ca5bd782487 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13230,6 +13230,13 @@ F: drivers/platform/x86/intel/sdsi.c F: tools/arch/x86/intel_sdsi/ F: tools/testing/selftests/drivers/sdsi/ =20 +INTEL SILICON SECURITY ENGINE INTERFACE (ISSEI) +M: Alexander Usyskin +S: Supported +F: Documentation/driver-api/issei/issei.rst +F: drivers/misc/issei/ +F: include/uapi/linux/issei.h + INTEL SGX M: Jarkko Sakkinen R: Dave Hansen diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 00683bf06258..c6fadcffd48a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -661,4 +661,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 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index b32a2597d246..01072ebecf82 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -75,3 +75,4 @@ obj-$(CONFIG_MCHP_LAN966X_PCI) +=3D lan966x-pci.o obj-y +=3D keba/ obj-y +=3D amd-sbi/ obj-$(CONFIG_MISC_RP1) +=3D rp1/ +obj-$(CONFIG_INTEL_SSEI) +=3D issei/ diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig new file mode 100644 index 000000000000..d98ac7925ce6 --- /dev/null +++ b/drivers/misc/issei/Kconfig @@ -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. diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile new file mode 100644 index 000000000000..f13bcf3e1699 --- /dev/null +++ b/drivers/misc/issei/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2023-2026 Intel Corporation +ccflags-y +=3D -DDEFAULT_SYMBOL_NAMESPACE=3D'"INTEL_SSEI"' + +obj-$(CONFIG_INTEL_SSEI) +=3D issei.o +issei-objs +=3D cdev.o +issei-objs +=3D dma.o diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c new file mode 100644 index 000000000000..d3d53dad088e --- /dev/null +++ b/drivers/misc/issei/cdev.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 =3D dev_get_drvdata(device); + + return sysfs_emit(buf, "%u.%u.%u.%u\n", idev->fw_version[0], idev->fw_ver= sion[1], + idev->fw_version[2], idev->fw_version[3]); +} +static DEVICE_ATTR_RO(fw_ver); + +static struct attribute *issei_attrs[] =3D { + &dev_attr_fw_ver.attr, + NULL +}; +ATTRIBUTE_GROUPS(issei); + +static const struct file_operations issei_fops =3D { + .owner =3D 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 *pa= rent, + const struct issei_dma_length *dma_length, + const struct issei_hw_ops *ops) +{ + idev->parent =3D parent; + idev->power_down =3D false; + init_waitqueue_head(&idev->wait_has_data); + idev->has_data =3D false; + init_waitqueue_head(&idev->wait_rst_state); + idev->rst_state =3D ISSEI_RST_STATE_INIT; + + mutex_init(&idev->client_lock); + INIT_LIST_HEAD(&idev->host_client_list); + idev->host_client_last_id =3D 0; + idev->host_client_count =3D 0; + INIT_LIST_HEAD(&idev->fw_client_list); + INIT_LIST_HEAD(&idev->write_queue); + idev->last_write_ts =3D 0; + + idev->dma.length =3D *dma_length; + + idev->ops =3D 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 =3D kzalloc(sizeof(*idev) + hw_size, GFP_KERNEL); + if (!idev) + return ERR_PTR(-ENOMEM); + + issei_device_init(idev, parent, dma_length, ops); + + ret =3D xa_alloc(&issei_minor_xa, &minor, idev, XA_LIMIT(0, ISSEI_MAX_DEV= S), GFP_KERNEL); + if (ret < 0) { + dev_err(&idev->dev, "Failed to allocate minor. ret =3D %d\n", ret); + kfree(idev); + return ERR_PTR(ret); + } + + idev->minor =3D minor; + devno =3D MKDEV(MAJOR(issei_devt), idev->minor); + + device_initialize(&idev->dev); + idev->dev.devt =3D devno; + idev->dev.class =3D issei_class; + idev->dev.parent =3D parent; + idev->dev.groups =3D issei_groups; + idev->dev.release =3D issei_device_release; + dev_set_drvdata(&idev->dev, idev); + + idev->cdev =3D cdev_alloc(); + if (!idev->cdev) { + ret =3D -ENOMEM; + goto err; + } + idev->cdev->ops =3D &issei_fops; + if (parent->driver) + idev->cdev->owner =3D parent->driver->owner; + cdev_set_parent(idev->cdev, &idev->dev.kobj); + + ret =3D cdev_add(idev->cdev, devno, 1); + if (ret) { + dev_err(parent, "unable to add device %d:%u ret =3D %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + ret =3D dev_set_name(&idev->dev, "issei%u", idev->minor); + if (ret) { + dev_err(parent, "unable to set name to device %d:%u ret =3D %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + ret =3D device_add(&idev->dev); + if (ret) { + dev_err(parent, "unable to add device %d:%u ret =3D %d\n", + MAJOR(issei_devt), idev->minor, ret); + goto err_del_cdev; + } + + idev->fw_clients =3D kset_create_and_add("fw_clients", NULL, &idev->dev.k= obj); + if (!idev->fw_clients) { + ret =3D -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 =3D 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 =3D class_create("issei"); + if (IS_ERR(issei_class)) { + pr_err("couldn't create class\n"); + return PTR_ERR(issei_class); + } + + ret =3D 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"); diff --git a/drivers/misc/issei/cdev.h b/drivers/misc/issei/cdev.h new file mode 100644 index 000000000000..30075a624f2d --- /dev/null +++ b/drivers/misc/issei/cdev.h @@ -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_ */ diff --git a/drivers/misc/issei/dma.c b/drivers/misc/issei/dma.c new file mode 100644 index 000000000000..457d28f31c06 --- /dev/null +++ b/drivers/misc/issei/dma.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include + +#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 =3D &idev->dma; + size_t size; + + size =3D __issei_dma_size(dma); + if (!size) + return -EINVAL; + + if (!dma->vaddr) + dma->vaddr =3D 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 *d= ma) +{ + return dma->vaddr + dma->length.h2f + dma->length.f2h; +} + +static bool __issei_dma_is_read_busy(struct issei_dma *dma) +{ + struct control_buffer *ctl =3D __dma_get_ctl_buf(dma); + + return ctl->f2h_counter_wr !=3D ctl->f2h_counter_rd; +} + +static bool __issei_dma_is_write_busy(struct issei_dma *dma) +{ + struct control_buffer *ctl =3D __dma_get_ctl_buf(dma); + + return ctl->h2f_counter_wr !=3D ctl->h2f_counter_rd; +} + +static void __issei_dma_read_finalize(struct issei_device *idev) +{ + struct control_buffer *ctl =3D __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 wa= y */ + ctl->f2h_counter_rd++; +} + +static void __issei_dma_write_finalize(struct issei_device *idev) +{ + struct control_buffer *ctl =3D __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 wa= y */ + 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 =3D idev->dma.vaddr; + struct ham_message_header *hdr =3D (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_TIMEO= UT_MSEC) { + dev_err(&idev->dev, "Write stuck in queue\n"); + return -EIO; + } + dev_info(&idev->dev, "Write is busy\n"); + return -EBUSY; + } + + hdr->length =3D data->length; + hdr->fw_id =3D data->fw_id; + hdr->host_id =3D data->host_id; + hdr->flags =3D data->flags; + hdr->status =3D data->status; + hdr->reserved =3D 0; + + memcpy(write_buf + sizeof(*hdr), data->buf, data->length); + + __issei_dma_write_finalize(idev); + idev->last_write_ts =3D 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 =3D idev->dma.vaddr + idev->dma.length.h2f; + struct ham_message_header *hdr =3D (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 =3D hdr->length; + data->fw_id =3D hdr->fw_id; + data->host_id =3D hdr->host_id; + data->flags =3D hdr->flags; + data->status =3D 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 =3D kmemdup(read_buf + sizeof(*hdr), data->length, GFP_KERNEL); + if (!data->buf) + return -ENOMEM; + __issei_dma_read_finalize(idev); + return 0; +} diff --git a/drivers/misc/issei/dma.h b/drivers/misc/issei/dma.h new file mode 100644 index 000000000000..e6b7aeb50ae6 --- /dev/null +++ b/drivers/misc/issei/dma.h @@ -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 + +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_*/ diff --git a/drivers/misc/issei/hw_msg.h b/drivers/misc/issei/hw_msg.h new file mode 100644 index 000000000000..28fd3775f64c --- /dev/null +++ b/drivers/misc/issei/hw_msg.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 +#include + +#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_ */ diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h new file mode 100644 index 000000000000..c742e7fe6cb6 --- /dev/null +++ b/drivers/misc/issei/issei_dev.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 +#include +#include +#include +#include +#include + +#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_ */ diff --git a/include/uapi/linux/issei.h b/include/uapi/linux/issei.h new file mode 100644 index 000000000000..3bfb89330265 --- /dev/null +++ b/include/uapi/linux/issei.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 +#include + +/* + * 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 w= rite + * 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 clien= t (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 u= ser space to kernel) + * @out_client_properties: connected firmware client properties (from kern= el 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 */ --=20 2.43.0 From nobody Fri Jun 12 17:36:49 2026 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 442A04418CA for ; Wed, 13 May 2026 14:40:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.16 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778683242; cv=none; b=rfIgF8QDbI5mAasuK89PXWLhzrZMO3G067Isez2B0mjKe+4dqNiCL5Uc+8fAzYWhWIuhPfmqs08N45davcRtOKVT6sY34n1NK4C8A/lIAktjPG3aq0iXviw+TAKblGtq8ERPzrEV80/3vggZrpTQr2INtOOTO07ZfmCychHF+Fc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778683242; c=relaxed/simple; bh=nAFNMWcJk7bXo3HuGcWVlr2qrPLfucXDgTTtmO1Gg3M=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=b1cDcupcrJQShfUBc9PV3mJCq4Rh2gityOiQq6Zm89efWwmOGkiYbWvFi8GtIZZgh++PTCZtYG4v12z6cEABCDU5NKfeUPekkiF34wtv17Tv+us3y6ROWJ4w3UTEFYbHECXWwJJue+z687WCDUUWXml1Wfhu0uYchU5EIO+IVVE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=Sco6MS3f; arc=none smtp.client-ip=192.198.163.16 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="Sco6MS3f" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1778683239; x=1810219239; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=nAFNMWcJk7bXo3HuGcWVlr2qrPLfucXDgTTtmO1Gg3M=; b=Sco6MS3fKao4D45jUzUUtL7S5WTTcatR4y2JiAcFHPjUjvkAS/oUxuh2 KVKin+tUZijWVUfJGuTrDKYnA28JJ88Mu72qZau9wE61gE35tomMcOWzt YOhw+bsAWiW648CtH7PKVeBn8MvaZWmE+KUnftY+xArUrru1dGIK1Obkn BuFwpeLQO84z10YxzkNLHY5MW7aI3hq/+/hgDa04cEcsDEIEVFgeBQ8vF vD/lqSEZummxFBGNFgDY4v84ra4bJ+6H68vO7whJP7W5cPF6VRXsK9S/i ys3M1OW5YJo2Tsy6+//lATTmNn3+Nr3u8xcvoCvTbBdYqRmMd0WaGw6/p g==; X-CSE-ConnectionGUID: iJJhnDTGSkqIv1k/PU08lw== X-CSE-MsgGUID: R4gamWiAROe89ZglFNufig== X-IronPort-AV: E=McAfee;i="6800,10657,11784"; a="67141723" X-IronPort-AV: E=Sophos;i="6.23,232,1770624000"; d="scan'208";a="67141723" Received: from orviesa004.jf.intel.com ([10.64.159.144]) by fmvoesa110.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 May 2026 07:40:39 -0700 X-CSE-ConnectionGUID: nMzI/Wl4Rf+vAYQuXFnAnw== X-CSE-MsgGUID: s/mXqkV/SIWGxWFubGZs9A== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.23,232,1770624000"; d="scan'208";a="242447862" Received: from sannilnx-dsk.jer.intel.com (HELO [127.0.1.1]) ([10.12.231.107]) by orviesa004-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 May 2026 07:40:38 -0700 From: Alexander Usyskin Date: Wed, 13 May 2026 17:18:43 +0300 Subject: [PATCH 2/4] issei: add firmware and host clients implementation, finish character device Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260513-issei-for-upstream-v1-2-f590038678f9@intel.com> References: <20260513-issei-for-upstream-v1-0-f590038678f9@intel.com> In-Reply-To: <20260513-issei-for-upstream-v1-0-f590038678f9@intel.com> To: Greg Kroah-Hartman Cc: Menachem Adin , Alexander Usyskin , linux-kernel@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1778681944; l=36735; i=alexander.usyskin@intel.com; s=20260315; h=from:subject:message-id; bh=nAFNMWcJk7bXo3HuGcWVlr2qrPLfucXDgTTtmO1Gg3M=; b=TweY92ODav3jujWTARe0aDZ4dkGovBWbNJxUwRS8+87D32S9poRT/jbca6OfCdU+XeJ27JRYe Qf43/YIuQKYC2aujAyRfcpzqYlaivYgSj8e9Z9WGCRO2FYAau410wjK X-Developer-Key: i=alexander.usyskin@intel.com; a=ed25519; pk=X+qoF/nFCdDOV04IForWSxnkyoCAbUE10egZi6PSfcU= Add the core implementation for firmware and host client management within the ISSEI (Intel Silicon Security Engine Interface) subsystem support for a character device to expose the ISSEI HECI interface to user space. The firmware client (fw_client) and host client (host_client) modules are responsible for managing communication between the host software and the firmware. The character device provides a communication channel for user-space applications to interact with the firmware on the platform. The client modules enable the ISSEI driver to manage multiple host clients communicating with corresponding firmware clients, facilitating data transfers and control operations over the HECI interface. The character device allows user-space applications to establish connections to firmware clients using UUIDs, exchange messages, and control the communication flow using standard file operation calls. Reviewed-by: Karol Wachowski Co-developed-by: Vitaly Lubart Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin --- Documentation/ABI/testing/sysfs-class-issei | 73 ++++ MAINTAINERS | 1 + drivers/misc/issei/Makefile | 2 + drivers/misc/issei/cdev.c | 227 ++++++++++++ drivers/misc/issei/fw_client.c | 240 +++++++++++++ drivers/misc/issei/fw_client.h | 45 +++ drivers/misc/issei/host_client.c | 519 ++++++++++++++++++++++++= ++++ drivers/misc/issei/host_client.h | 75 ++++ 8 files changed, 1182 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-issei b/Documentation/AB= I/testing/sysfs-class-issei new file mode 100644 index 000000000000..73a01f4627cb --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-issei @@ -0,0 +1,73 @@ +What: /sys/class/issei/ +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The issei/ class sub-directory belongs to issei device class + +What: /sys/class/issei/issei/ +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The /sys/class/issei/isseiN directory is created for + each probed issei device + +What: /sys/class/issei/issei/fw_ver +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Display the ISSE firmware version. + + The version of the ISSE firmware is in format: + .... + +What: /sys/class/issei/issei/fw_clients +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The fw_clients directory stores all firmware clients on the + probed issei device + +What: /sys/class/issei/issei/fw_clients/ +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: + The /sys/class/issei/isseiN/fw_client/M directory is created for + each firmware client on the probed issei device where M is the + id of firmware client. + +What: /sys/class/issei/issei/fw_clients//id +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays id of the firmware client + + The id of firmware client is it's number in client enumeration order, + starting from 1. + +What: /sys/class/issei/issei/fw_clients//uuid +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays uuid of the firmware client + + The universally unique identifier of the firmware client + +What: /sys/class/issei/issei/fw_clients//mtu +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays maximum transmission unit of the firmware client + + The maximum transmission unit (in bytes) used by the firmware client. + +What: /sys/class/issei/issei/fw_clients//ver +Date: June 2026 +KernelVersion: 7.2 +Contact: Alexander Usyskin +Description: Displays version of the firmware client + + The version of the firmware client diff --git a/MAINTAINERS b/MAINTAINERS index 9ca5bd782487..49fd6f8d5e7c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13233,6 +13233,7 @@ F: tools/testing/selftests/drivers/sdsi/ INTEL SILICON SECURITY ENGINE INTERFACE (ISSEI) M: Alexander Usyskin S: Supported +F: Documentation/ABI/testing/sysfs-class-issei F: Documentation/driver-api/issei/issei.rst F: drivers/misc/issei/ F: include/uapi/linux/issei.h diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile index f13bcf3e1699..1471ed99d619 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -5,3 +5,5 @@ ccflags-y +=3D -DDEFAULT_SYMBOL_NAMESPACE=3D'"INTEL_SSEI"' obj-$(CONFIG_INTEL_SSEI) +=3D issei.o issei-objs +=3D cdev.o issei-objs +=3D dma.o +issei-objs +=3D fw_client.o +issei-objs +=3D host_client.o diff --git a/drivers/misc/issei/cdev.c b/drivers/misc/issei/cdev.c index d3d53dad088e..b7d65e2d0813 100644 --- a/drivers/misc/issei/cdev.c +++ b/drivers/misc/issei/cdev.c @@ -2,6 +2,7 @@ /* Copyright (C) 2023-2026 Intel Corporation */ #include #include +#include #include #include #include @@ -11,11 +12,15 @@ #include #include #include +#include +#include +#include #include #include #include =20 #include "issei_dev.h" +#include "host_client.h" #include "cdev.h" =20 struct class *issei_class; @@ -25,6 +30,221 @@ static dev_t issei_devt; =20 static DEFINE_XARRAY_ALLOC(issei_minor_xa); =20 +static int issei_open(struct inode *inode, struct file *fp) +{ + struct issei_host_client *cl; + struct issei_device *idev; + + xa_lock(&issei_minor_xa); + idev =3D xa_load(&issei_minor_xa, iminor(inode)); + if (idev) + get_device(&idev->dev); + xa_unlock(&issei_minor_xa); + if (!idev) + return -ENODEV; + + cl =3D issei_cl_create(idev, fp); + if (IS_ERR(cl)) { + put_device(&idev->dev); + return PTR_ERR(cl); + } + fp->private_data =3D cl; + + return nonseekable_open(inode, fp); +} + +static int issei_release(struct inode *inode, struct file *fp) +{ + struct issei_host_client *cl =3D fp->private_data; + struct issei_device *idev =3D cl->idev; + + issei_cl_remove(cl); + put_device(&idev->dev); + + return 0; +} + +static long issei_ioctl(struct file *file, unsigned int cmd, unsigned long= data) +{ + struct issei_host_client *cl =3D file->private_data; + struct issei_connect_client_data conn; + struct issei_device *idev =3D cl->idev; + int ret; + + switch (cmd) { + case IOCTL_ISSEI_CONNECT_CLIENT: + dev_dbg(&idev->dev, "IOCTL_ISSEI_CONNECT_CLIENT\n"); + + if (idev->rst_state !=3D ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + + if (copy_from_user(&conn, (char __user *)data, sizeof(conn))) { + dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return -EFAULT; + } + + ret =3D issei_cl_connect(cl, (uuid_t *)&conn.in_client_uuid, + &conn.out_client_properties.max_msg_length, + &conn.out_client_properties.protocol_version, + &conn.out_client_properties.flags); + if (ret) + return ret; + + if (copy_to_user((char __user *)data, &conn, sizeof(conn))) { + dev_dbg(&idev->dev, "failed to copy data to userland\n"); + issei_cl_disconnect(cl); + return -EFAULT; + } + return 0; + + case IOCTL_ISSEI_DISCONNECT_CLIENT: + dev_dbg(&idev->dev, "IOCTL_ISSEI_DISCONNECT_CLIENT\n"); + + if (idev->rst_state !=3D ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -ENODEV; + } + + return issei_cl_disconnect(cl); + + default: + return -ENOIOCTLCMD; + } +} + +static ssize_t issei_write(struct file *file, const char __user *ubuf, + size_t length, loff_t *offset) +{ + struct issei_host_client *cl =3D file->private_data; + struct issei_device *idev =3D cl->idev; + ssize_t ret; + + if (!length) + return 0; + + if (idev->rst_state !=3D ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.h2f) { + dev_dbg(&idev->dev, "Write is too big %zu > %zu\n", + length, idev->dma.length.h2f); + return -EFBIG; + } + + u8 *buf __free(kfree) =3D memdup_user(ubuf, length); + if (IS_ERR(buf)) { + dev_dbg(&idev->dev, "failed to copy data from userland\n"); + return PTR_ERR(buf); + } + + do { + ret =3D issei_cl_write(cl, buf, length); + if (ret < 0 && ret !=3D -EAGAIN) + return ret; + /* buf is consumed by issei_cl_write on success */ + if (ret >=3D 0) + retain_and_null_ptr(buf); + if (wait_event_interruptible(cl->write_wait, issei_cl_check_write(cl) != =3D 1)) { + issei_cl_clean_all_wbuf(cl); + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + } while (ret =3D=3D -EAGAIN); + + return ret; +} + +static ssize_t issei_read(struct file *file, char __user *ubuf, + size_t length, loff_t *offset) +{ + struct issei_host_client *cl =3D file->private_data; + struct issei_device *idev =3D cl->idev; + u8 *data =3D NULL; + ssize_t ret; + + if (!length) + return 0; + + if (idev->rst_state !=3D ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return -EBUSY; + } + + /* sanity check */ + if (length > idev->dma.length.f2h) { + dev_dbg(&idev->dev, "Read is too big %zu > %zu\n", + length, idev->dma.length.f2h); + return -EFBIG; + } + + ret =3D issei_cl_read(cl, &data, length); + if (ret < 0) { + if (ret !=3D -ENOENT) + return ret; + + if (wait_event_interruptible(cl->read_wait, issei_cl_check_read(cl) !=3D= 0)) { + if (signal_pending(current)) + return -EINTR; + return -ERESTARTSYS; + } + + ret =3D issei_cl_read(cl, &data, length); + if (ret < 0) + return ret; + } + + if (copy_to_user(ubuf, data, ret)) { + dev_dbg(&idev->dev, "failed to copy data to userland\n"); + ret =3D -EFAULT; + } else { + *offset =3D 0; + } + + kfree(data); + + return ret; +} + +static __poll_t issei_poll(struct file *file, poll_table *wait) +{ + __poll_t req_events =3D poll_requested_events(wait); + struct issei_host_client *cl =3D file->private_data; + struct issei_device *idev =3D cl->idev; + __poll_t mask =3D 0; + int ret; + + if (idev->rst_state !=3D ISSEI_RST_STATE_DONE) { + dev_dbg(&idev->dev, "Device is in transition\n"); + return EPOLLERR; + } + + if (req_events & (EPOLLIN | EPOLLRDNORM)) { + poll_wait(file, &cl->read_wait, wait); + ret =3D issei_cl_check_read(cl); + if (ret =3D=3D 1) + mask |=3D EPOLLIN | EPOLLRDNORM; + else if (ret < 0) + mask |=3D EPOLLERR; + } + + if (req_events & (EPOLLOUT | EPOLLWRNORM)) { + poll_wait(file, &cl->write_wait, wait); + ret =3D issei_cl_check_write(cl); + if (ret =3D=3D 0) + mask |=3D EPOLLOUT | EPOLLWRNORM; + else if (ret < 0) + mask |=3D EPOLLERR; + } + + return mask; +} + static ssize_t fw_ver_show(struct device *device, struct device_attribute *attr, char *buf) { @@ -43,6 +263,13 @@ ATTRIBUTE_GROUPS(issei); =20 static const struct file_operations issei_fops =3D { .owner =3D THIS_MODULE, + .open =3D issei_open, + .unlocked_ioctl =3D issei_ioctl, + .compat_ioctl =3D compat_ptr_ioctl, + .write =3D issei_write, + .read =3D issei_read, + .release =3D issei_release, + .poll =3D issei_poll, }; =20 static void issei_device_release(struct device *dev) diff --git a/drivers/misc/issei/fw_client.c b/drivers/misc/issei/fw_client.c new file mode 100644 index 000000000000..b8e48dbf1c9c --- /dev/null +++ b/drivers/misc/issei/fw_client.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "issei_dev.h" +#include "fw_client.h" + +/* + * Specific attribute handlers for fw_clients kset. + * Provide only show function as all fw_client attributes are read-only. + */ + +struct issei_fw_cl_attr { + struct attribute attr; + ssize_t (*show)(struct issei_fw_client *fw_cl, const struct issei_fw_cl_a= ttr *attr, + char *buf); +}; +#define to_issei_fw_cl_attr(x) container_of_const(x, struct issei_fw_cl_at= tr, attr) + +static ssize_t fw_cl_attr_show(struct kobject *kobj, struct attribute *att= r, char *buf) +{ + const struct issei_fw_cl_attr *issei_attr; + struct issei_fw_client *fw_cl; + + issei_attr =3D to_issei_fw_cl_attr(attr); + fw_cl =3D to_issei_fw_client(kobj); + + if (!issei_attr->show) + return -EIO; + + return issei_attr->show(fw_cl, issei_attr, buf); +} + +static const struct sysfs_ops fw_cl_sysfs_ops =3D { + .show =3D fw_cl_attr_show, +}; + +#define FW_CL_ATTR_RO(_name) \ + struct issei_fw_cl_attr fw_cl_attr_##_name =3D __ATTR_RO(_name) + +/* fw_client attributes */ + +static ssize_t id_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->id); +} +static FW_CL_ATTR_RO(id); + +static ssize_t ver_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->ver); +} +static FW_CL_ATTR_RO(ver); + +static ssize_t uuid_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%pUb\n", &fw_cl->uuid); +} +static FW_CL_ATTR_RO(uuid); + +static ssize_t mtu_show(struct issei_fw_client *fw_cl, + const struct issei_fw_cl_attr *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", fw_cl->mtu); +} +static FW_CL_ATTR_RO(mtu); + +static const struct attribute *const fw_cl_attrs[] =3D { + &fw_cl_attr_id.attr, + &fw_cl_attr_ver.attr, + &fw_cl_attr_uuid.attr, + &fw_cl_attr_mtu.attr, + NULL, +}; + +static const struct attribute_group fw_cl_group =3D { + .attrs_const =3D fw_cl_attrs, +}; +__ATTRIBUTE_GROUPS(fw_cl); + +static void issei_fw_cl_init(struct issei_fw_client *fw_cl, u16 id, u8 ver= , const uuid_t *uuid, + u32 mtu, u32 flags) +{ + INIT_LIST_HEAD(&fw_cl->list); + fw_cl->id =3D id; + fw_cl->ver =3D ver; + fw_cl->uuid =3D *uuid; + fw_cl->mtu =3D mtu; + fw_cl->flags =3D flags; +} + +static void fw_cl_release(struct kobject *kobj) +{ + struct issei_fw_client *fw_cl =3D to_issei_fw_client(kobj); + + kfree(fw_cl); +} + +static const struct kobj_type fw_client_ktype =3D { + .sysfs_ops =3D &fw_cl_sysfs_ops, + .release =3D fw_cl_release, + .default_groups =3D fw_cl_groups, +}; + +/** + * issei_fw_cl_create - create firmware client object and add to list + * @idev: issei device object + * @id: firmware client id + * @ver: firmware client version + * @uuid: firmware client unique id + * @mtu: firmware client maximum message size + * @flags: firmware client flags + * + * Should be called under idev->client_lock + * + * Return: pointer to newly created object on success, ERR_PTR on failure + */ +struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 = id, u8 ver, + const uuid_t *uuid, u32 mtu, u32 flags) +{ + int ret; + struct issei_fw_client *fw_cl =3D kzalloc_obj(*fw_cl); + + if (!fw_cl) + return ERR_PTR(-ENOMEM); + + WARN_ON(!mutex_is_locked(&idev->client_lock)); + + issei_fw_cl_init(fw_cl, id, ver, uuid, mtu, flags); + fw_cl->kobj.kset =3D idev->fw_clients; + + ret =3D kobject_init_and_add(&fw_cl->kobj, &fw_client_ktype, NULL, "%u", = id); + if (ret) { + kobject_put(&fw_cl->kobj); + return ERR_PTR(ret); + } + + list_add_tail(&fw_cl->list, &idev->fw_client_list); + + dev_dbg(&idev->dev, "FW client %pUb created\n", uuid); + + kobject_uevent(&fw_cl->kobj, KOBJ_ADD); + + return fw_cl; +} + +static void __issei_fw_cl_remove(struct issei_device *idev, struct issei_f= w_client *fw_cl) +{ + WARN(fw_cl->cl, "Removing connected client!\n"); + + dev_dbg(&idev->dev, "FW client %pUb will be removed\n", &fw_cl->uuid); + + list_del(&fw_cl->list); + kobject_put(&fw_cl->kobj); +} + +/** + * issei_fw_cl_remove_all - remove all firmware client objects + * @idev: issei device object + */ +void issei_fw_cl_remove_all(struct issei_device *idev) +{ + struct issei_fw_client *fw_cl, *next; + + guard(mutex)(&idev->client_lock); + + list_for_each_entry_safe(fw_cl, next, &idev->fw_client_list, list) + __issei_fw_cl_remove(idev, fw_cl); +} + +/** + * issei_fw_cl_find_by_uuid - find firmware client by uuid + * @idev: issei device object + * @uuid: uuid to search by it + * + * Should be called under idev->client_lock + * + * Return: pointer to firmware client object if found, NULL on failure + */ +struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev= , const uuid_t *uuid) +{ + struct issei_fw_client *fw_cl; + + WARN_ON(!mutex_is_locked(&idev->client_lock)); + + list_for_each_entry(fw_cl, &idev->fw_client_list, list) { + if (uuid_equal(&fw_cl->uuid, uuid)) { + kobject_get(&fw_cl->kobj); + return fw_cl; + } + } + return NULL; +} + +/** + * issei_fw_cl_connect - connect firmware and host client + * @fw_cl: firmware client + * @cl: host client + * + * Should be called under idev->client_lock + * + * Return: 0 on success, -EBUSY if already connected + */ +int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_c= lient *cl) +{ + if (fw_cl->cl) + return -EBUSY; + + kobject_get(&fw_cl->kobj); + fw_cl->cl =3D cl; + return 0; +} + +/** + * issei_fw_cl_disconnect - disconnect firmware and host client + * @fw_cl: firmware client + * + * Should be called under idev->client_lock + */ +void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl) +{ + WARN_ON(!fw_cl->cl); + + fw_cl->cl =3D NULL; + kobject_put(&fw_cl->kobj); +} diff --git a/drivers/misc/issei/fw_client.h b/drivers/misc/issei/fw_client.h new file mode 100644 index 000000000000..377f733d7e91 --- /dev/null +++ b/drivers/misc/issei/fw_client.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_FW_CLIENT_H_ +#define _ISSEI_FW_CLIENT_H_ + +#include +#include +#include +#include + +struct issei_device; +struct issei_host_client; + +/** + * struct issei_fw_client - represents firmware queue + * @kobj: associated kobject + * @list: link in firmware clients list + * @id: firmware client id + * @ver: firmware client version + * @uuid: firmware client protocol id + * @mtu: firmware client maximum buffer size + * @flags: firmware client flags + * @cl: pointer to host client, if connected + */ +struct issei_fw_client { + struct kobject kobj; + struct list_head list; + u16 id; + u8 ver; + uuid_t uuid; + u32 mtu; + u32 flags; + struct issei_host_client *cl; +}; +#define to_issei_fw_client(x) container_of(x, struct issei_fw_client, kobj) + +struct issei_fw_client *issei_fw_cl_create(struct issei_device *idev, u16 = id, u8 ver, + const uuid_t *uuid, u32 mtu, u32 flags); +void issei_fw_cl_remove_all(struct issei_device *idev); +struct issei_fw_client *issei_fw_cl_find_by_uuid(struct issei_device *idev= , const uuid_t *uuid); + +int issei_fw_cl_connect(struct issei_fw_client *fw_cl, struct issei_host_c= lient *cl); +void issei_fw_cl_disconnect(struct issei_fw_client *fw_cl); + +#endif /* _ISSEI_FW_CLIENT_H_ */ diff --git a/drivers/misc/issei/host_client.c b/drivers/misc/issei/host_cli= ent.c new file mode 100644 index 000000000000..8f36d1c319ec --- /dev/null +++ b/drivers/misc/issei/host_client.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fw_client.h" +#include "host_client.h" +#include "issei_dev.h" + +static inline u8 __issei_cl_fw_id(const struct issei_host_client *cl) +{ + return cl->fw_cl ? cl->fw_cl->id : 0; +} + +#define ISSEI_CL_FMT "cl:host=3D%02d fw=3D%02d " + +#define cl_dbg(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ =3D _cl_; \ + dev_dbg(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +#define cl_warn(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ =3D _cl_; \ + dev_warn(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +#define cl_err(_dev_, _cl_, format, arg...) do { \ + struct issei_host_client *_l_cl_ =3D _cl_; \ + dev_err(&(_dev_)->dev, ISSEI_CL_FMT format, _l_cl_->id, \ + __issei_cl_fw_id(_l_cl_), ##arg); \ +} while (0) + +static void __issei_cl_clean_wbuf(struct issei_write_buf *wbuf) +{ + list_del(&wbuf->list); + kfree(wbuf->data); + kfree(wbuf); +} + +static void __issei_cl_release_rbuf(struct issei_host_client *cl) +{ + cl->read_data =3D NULL; + cl->read_data_size =3D 0; +} + +static void __issei_cl_clean_rbuf(struct issei_host_client *cl) +{ + kfree(cl->read_data); + __issei_cl_release_rbuf(cl); +} + +static void __issei_cl_clean_all_wbuf(struct issei_device *idev, struct is= sei_host_client *cl) +{ + struct issei_write_buf *wbuf, *next; + + if (!cl->write_in_progress) + return; + list_for_each_entry_safe(wbuf, next, &idev->write_queue, list) { + if (wbuf->cl =3D=3D cl) { + __issei_cl_clean_wbuf(wbuf); + break; + } + } + cl->write_in_progress =3D false; + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->write_wait)) + wake_up_interruptible(&cl->write_wait); +} + +static struct issei_host_client *__issei_cl_by_id(struct issei_device *ide= v, u16 id) +{ + struct issei_host_client *cl; + + list_for_each_entry(cl, &idev->host_client_list, list) { + if (cl->id =3D=3D id) + return cl; + } + return NULL; +} + +static void __issei_cl_disconnect(struct issei_device *idev, struct issei_= host_client *cl) +{ + if (cl->state =3D=3D ISSEI_HOST_CL_STATE_DISCONNECTED) + return; + + __issei_cl_clean_all_wbuf(idev, cl); + + if (!WARN_ON(!cl->fw_cl)) { + issei_fw_cl_disconnect(cl->fw_cl); + cl->fw_cl =3D NULL; + } + cl->state =3D ISSEI_HOST_CL_STATE_DISCONNECTED; + + if (cl->read_data) + __issei_cl_clean_rbuf(cl); + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->read_wait)) + wake_up_interruptible(&cl->read_wait); + cl_dbg(idev, cl, "Disconnected\n"); +} + +static void __issei_cl_init(struct issei_host_client *cl, struct issei_dev= ice *idev, + u16 id, struct file *fp) +{ + INIT_LIST_HEAD(&cl->list); + cl->idev =3D idev; + cl->id =3D id; + cl->state =3D ISSEI_HOST_CL_STATE_DISCONNECTED; + cl->fp =3D fp; + init_waitqueue_head(&cl->write_wait); + init_waitqueue_head(&cl->read_wait); + __issei_cl_release_rbuf(cl); +} + +/** + * issei_cl_create - create the host client + * @idev: issei device + * @fp: file pointer to associate with host client + * + * Return: client pointer on success, ERR_PTR on error + */ +struct issei_host_client *issei_cl_create(struct issei_device *idev, struc= t file *fp) +{ + struct issei_host_client *cl; + u16 id; + + guard(mutex)(&idev->client_lock); + + if (idev->host_client_count =3D=3D ISSEI_HOST_CLIENTS_MAX) { + dev_err(&idev->dev, "Maximum open clients %d is reached.\n", + ISSEI_HOST_CLIENTS_MAX); + return ERR_PTR(-EMFILE); + } + + do { + if (check_add_overflow(idev->host_client_last_id, 1, &id)) /* overflow */ + id =3D 1; + idev->host_client_last_id =3D id; + /* Not an endless loop as we have less clients then id's */ + } while (__issei_cl_by_id(idev, id)); + + cl =3D kzalloc_obj(*cl); + if (!cl) + return ERR_PTR(-ENOMEM); + + __issei_cl_init(cl, idev, id, fp); + list_add_tail(&cl->list, &idev->host_client_list); + idev->host_client_count++; + + cl_dbg(idev, cl, "Created\n"); + return cl; +} + +/** + * issei_cl_remove - disconnect and free the host client + * @cl: host client + */ +void issei_cl_remove(struct issei_host_client *cl) +{ + struct issei_device *idev; + + /* don't shout on error exit path */ + if (!cl) + return; + + idev =3D cl->idev; + + guard(mutex)(&idev->client_lock); + + idev->host_client_count--; + list_del(&cl->list); + + __issei_cl_disconnect(idev, cl); + + cl_dbg(idev, cl, "Removed\n"); + kfree(cl); +} + +/** + * issei_cl_connect - connect between FW and host client + * @cl: host client + * @uuid: FW client unique ID + * @mtu: memory for FW client max message size + * @ver: memory for FW client version + * @flags: memory for FW client flags + * + * Search for firmware client by UUID and connect it to provided + * host client, if not already connected to some client. + * + * Return: 0 on success, <0 on error + */ +int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32= *mtu, u8 *ver, + u32 *flags) +{ + struct issei_device *idev =3D cl->idev; + struct issei_fw_client *fw_cl; + int ret; + + guard(mutex)(&idev->client_lock); + + if (cl->state =3D=3D ISSEI_HOST_CL_STATE_CONNECTED) { + cl_err(idev, cl, "Already connected\n"); + return -EISCONN; + } + + fw_cl =3D issei_fw_cl_find_by_uuid(idev, uuid); + if (!fw_cl) { + cl_dbg(idev, cl, "FW client %pUb not found\n", uuid); + return -ENOTTY; + } + + ret =3D issei_fw_cl_connect(fw_cl, cl); /* calls kobject_get for fw_cl on= success */ + kobject_put(&fw_cl->kobj); + if (ret) { + cl_err(idev, cl, "FW client is already connected ret =3D %d\n", ret); + return ret; + } + + cl->fw_cl =3D fw_cl; + cl->state =3D ISSEI_HOST_CL_STATE_CONNECTED; + + *mtu =3D fw_cl->mtu; + *ver =3D fw_cl->ver; + *flags =3D fw_cl->flags; + cl_dbg(idev, cl, "Connected\n"); + return 0; +} + +/** + * issei_cl_disconnect - disconnect between FW and host client + * @cl: host client + * + * Return: 0 on success, -ENOTCONN if not connected + */ +int issei_cl_disconnect(struct issei_host_client *cl) +{ + struct issei_device *idev =3D cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state !=3D ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + __issei_cl_disconnect(idev, cl); + return 0; +} + +/** + * issei_cl_all_disconnect - disconnect all FW clients + * @idev: issei device + */ +void issei_cl_all_disconnect(struct issei_device *idev) +{ + struct issei_host_client *cl; + + guard(mutex)(&idev->client_lock); + + list_for_each_entry(cl, &idev->host_client_list, list) + __issei_cl_disconnect(idev, cl); +} + +/** + * issei_cl_write - enqueue write request + * @cl: host client + * @buf: buffer to write + * @buf_size: buffer size + * + * Add write request to the write queue and wakes working thread. + * This call takes ownership of buf memory, if succeeded. + * + * Return: size of data on success, <0 on error + */ +ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t= buf_size) +{ + struct issei_device *idev =3D cl->idev; + struct issei_write_buf *wbuf; + + guard(mutex)(&idev->client_lock); + + if (cl->state !=3D ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + + if (cl->write_in_progress) { + cl_dbg(idev, cl, "Another write is in progress\n"); + return -EAGAIN; + } + + if (buf_size > cl->fw_cl->mtu) { + cl_err(idev, cl, "Write is too big %zu > %u\n", buf_size, cl->fw_cl->mtu= ); + return -EFBIG; + } + + wbuf =3D kmalloc_obj(*wbuf); + if (!wbuf) + return -ENOMEM; + wbuf->cl =3D cl; + wbuf->data =3D buf; + wbuf->data_size =3D buf_size; + list_add_tail(&wbuf->list, &idev->write_queue); + cl->write_in_progress =3D true; + cl_dbg(idev, cl, "Write queued %zu bytes\n", buf_size); + + issei_poke_process_thread(idev); + + return buf_size; +} + +/** + * issei_cl_write_from_queue - writes first request from queue to firmware + * @idev: issei device + * + * Tries to write first request from the write queue to firmware. + * Releases buf memory, if succeeded. + * + * Return: 0 on success, <0 on error + */ +int issei_cl_write_from_queue(struct issei_device *idev) +{ + struct issei_write_buf *wbuf; + struct issei_dma_data data; + struct issei_host_client *cl; + int ret; + + guard(mutex)(&idev->client_lock); + + wbuf =3D list_first_entry_or_null(&idev->write_queue, struct issei_write_= buf, list); + if (!wbuf) + return 0; + + cl =3D wbuf->cl; + + data.fw_id =3D cl->fw_cl->id; + data.host_id =3D cl->id; + data.flags =3D 0; + data.status =3D 0; + data.length =3D wbuf->data_size; + data.buf =3D (void *)wbuf->data; + ret =3D issei_dma_write(idev, &data); + if (ret =3D=3D -EBUSY) + return 0; + if (ret =3D=3D -EIO) + return ret; + if (ret >=3D 0) + idev->ops->irq_write_generate(idev); + cl->write_in_progress =3D false; + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->write_wait)) + wake_up_interruptible(&cl->write_wait); + cl_dbg(idev, cl, "Write %zu bytes\n", wbuf->data_size); + __issei_cl_clean_wbuf(wbuf); + return 0; +} + +static struct issei_host_client *__issei_cl_read_buf_check(struct issei_de= vice *idev, u16 fw_id, + u16 host_id, size_t buf_size) +{ + struct issei_host_client *cl; + + cl =3D __issei_cl_by_id(idev, host_id); + if (!cl) { + dev_dbg(&idev->dev, "No client %u\n", host_id); + return ERR_PTR(-ENOTTY); + } + + if (cl->state !=3D ISSEI_HOST_CL_STATE_CONNECTED) { + cl_dbg(idev, cl, "Not connected\n"); + return ERR_PTR(-ENODEV); + } + if (cl->fw_cl->id !=3D fw_id) { + cl_dbg(idev, cl, "Wrong firmware client %u ?=3D %u\n", cl->fw_cl->id, fw= _id); + return ERR_PTR(-ENODEV); + } + + if (buf_size > cl->fw_cl->mtu) { + cl_err(idev, cl, "Read is too big %zu > %u\n", buf_size, cl->fw_cl->mtu); + __issei_cl_disconnect(idev, cl); + return NULL; + } + + if (cl->read_data) { + cl_err(idev, cl, "Previous data was not read by user-space, disconnectin= g\n"); + __issei_cl_disconnect(idev, cl); + return NULL; + } + + return cl; +} + +/** + * issei_cl_read_buf - process data from firmware + * @idev: issei device + * @fw_id: firmware client id + * @host_id: host client id + * @buf: buffer with data + * @buf_size: buffer size + * + * Puts data from firmware into provided host client storage. + * Free buffer or consume it. + * + * Return: 0 on success or recoverable error, <0 on unrecoverable error + */ +int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u= 8 *buf, size_t buf_size) +{ + struct issei_host_client *cl; + + guard(mutex)(&idev->client_lock); + + cl =3D __issei_cl_read_buf_check(idev, fw_id, host_id, buf_size); + if (IS_ERR_OR_NULL(cl)) { + kfree(buf); + return PTR_ERR(cl); + } + + cl->read_data =3D buf; + cl->read_data_size =3D buf_size; + + /* synchronized under host client mutex */ + if (waitqueue_active(&cl->read_wait)) + wake_up_interruptible(&cl->read_wait); + cl_dbg(idev, cl, "Read %zu bytes\n", buf_size); + + return 0; +} + +/** + * issei_cl_read - read data from queue to provided buffer + * @cl: host client + * @buf: buffer to store data + * @buf_size: buffer size + * + * Tries to take data buffer from client and return it to caller. + * The caller receives ownership of the data buffer. + * + * Return: read data size on success, <0 on error + */ +ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_s= ize) +{ + struct issei_device *idev =3D cl->idev; + size_t read_data_size; + + guard(mutex)(&idev->client_lock); + + if (cl->state !=3D ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + + if (!cl->read_data) + return -ENOENT; + + if (cl->read_data_size > buf_size) { + cl_err(idev, cl, "Buffer is too small %zu > %zu\n", + cl->read_data_size, buf_size); + return -EFBIG; + } + + *buf =3D cl->read_data; + read_data_size =3D cl->read_data_size; + cl_dbg(idev, cl, "Read by client %zu bytes\n", read_data_size); + __issei_cl_release_rbuf(cl); + return read_data_size; +} + +/** + * issei_cl_check_read - check if client has data to read + * + * @cl: host client + * + * Return: 1 - data available, 0 - no data, < 0 on error + */ +int issei_cl_check_read(struct issei_host_client *cl) +{ + struct issei_device *idev =3D cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state !=3D ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + if (!cl->read_data) + return 0; + return 1; +} + +/** + * issei_cl_check_write - check if client is ready to write + * + * @cl: host client + * + * Return: 1 - can not write, 0 - can write, < 0 on error + */ +int issei_cl_check_write(struct issei_host_client *cl) +{ + struct issei_device *idev =3D cl->idev; + + guard(mutex)(&idev->client_lock); + + if (cl->state !=3D ISSEI_HOST_CL_STATE_CONNECTED) + return -ENOTCONN; + if (cl->write_in_progress) + return 1; + return 0; +} + +void issei_cl_clean_all_wbuf(struct issei_host_client *cl) +{ + struct issei_device *idev =3D cl->idev; + + guard(mutex)(&idev->client_lock); + + __issei_cl_clean_all_wbuf(idev, cl); +} diff --git a/drivers/misc/issei/host_client.h b/drivers/misc/issei/host_cli= ent.h new file mode 100644 index 000000000000..05e2f4ade9c3 --- /dev/null +++ b/drivers/misc/issei/host_client.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HOST_CLIENT_H_ +#define _ISSEI_HOST_CLIENT_H_ + +#include +#include +#include + +struct file; + +struct issei_device; +struct issei_fw_client; + +/** + * enum issei_host_client_state - host client states + * @ISSEI_HOST_CL_STATE_DISCONNECTED: host client is disconnected + * @ISSEI_HOST_CL_STATE_CONNECTED: host client is connected + */ +enum issei_host_client_state { + ISSEI_HOST_CL_STATE_DISCONNECTED, + ISSEI_HOST_CL_STATE_CONNECTED, +}; + +/** + * struct issei_host_client - represents host client + * @list: link in host clients list + * @idev: issei parent device + * @id: host client id + * @fp: file associated with client + * + * @write_wait: waitqueue for pending write data + * @write_in_progress: indicator for write in process + * + * @state: host client state + * @fw_cl: pointer to firmware client, if connected + * + * @read_wait: waitqueue for read object + * @read_data: received data pointer + * @read_data_size: received data size + */ +struct issei_host_client { + struct list_head list; + struct issei_device *idev; + u16 id; + const struct file *fp; + + wait_queue_head_t write_wait; + bool write_in_progress; + + enum issei_host_client_state state; + struct issei_fw_client *fw_cl; + + wait_queue_head_t read_wait; + u8 *read_data; + size_t read_data_size; +}; + +struct issei_host_client *issei_cl_create(struct issei_device *idev, struc= t file *fp); +void issei_cl_remove(struct issei_host_client *cl); + +int issei_cl_connect(struct issei_host_client *cl, const uuid_t *uuid, u32= *mtu, u8 *ver, + u32 *flags); +int issei_cl_disconnect(struct issei_host_client *cl); +void issei_cl_all_disconnect(struct issei_device *idev); + +ssize_t issei_cl_write(struct issei_host_client *cl, const u8 *buf, size_t= buf_size); +int issei_cl_write_from_queue(struct issei_device *idev); +int issei_cl_read_buf(struct issei_device *idev, u16 fw_id, u16 host_id, u= 8 *buf, size_t buf_size); +ssize_t issei_cl_read(struct issei_host_client *cl, u8 **buf, size_t buf_s= ize); +int issei_cl_check_read(struct issei_host_client *cl); +int issei_cl_check_write(struct issei_host_client *cl); +void issei_cl_clean_all_wbuf(struct issei_host_client *cl); + +#endif /* ISSEI_HOST_CLIENT_H_ */ --=20 2.43.0 From nobody Fri Jun 12 17:36:49 2026 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A1B4E44BC94 for ; Wed, 13 May 2026 14:40:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.16 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778683242; cv=none; b=urDDMVWw2UAzpuBXqrP++Im0PXPpaJJpVwL32ZPlACPYYLY8ykBkzYscgXxDa93ASN8nHAJUKvF2ss7sqoBfHx3Wqo1xYknGQ5jkdjH/LMKatNYuwtUPWEcnNyvWGUsEMF2bTfzM0kkeWBiJYh/lpVxOh7xrCZijO9/KBrEiCFg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778683242; c=relaxed/simple; bh=TnNl6ECxuBC4/q8cEujwjm+X/oKnWNuW1qvcYbelBm8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LJKMO6XP0PPWx/5oKI6lKFFU2nrmZM+Gl7vBApNjxs0X4dWSWueoC+fJpLmS1pMkm8ioDSR+2jk105PwXY+qcaB5NU+KmCaQMwbfHEV/gqBlOgK0mPh1NsDcWDEKobJWDYJG3G5kNVR1NRz8QO+Ie3DU8PQ2syi2mDyLiLWWFdA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=QuyR3B6W; arc=none smtp.client-ip=192.198.163.16 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="QuyR3B6W" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1778683241; x=1810219241; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=TnNl6ECxuBC4/q8cEujwjm+X/oKnWNuW1qvcYbelBm8=; b=QuyR3B6WOjOvFrtGlvpg1Vp7BOnPfynX3pw6mPiC4QW/ofegrpLDuY73 9HdqNWPgVueC7U3aVGpVKzRgje1p2La8rx9+AncdamzI8Tc557tNPVRDP /ibPG3M6Tg9vhgQ5zQh+5saWDI7eV7CQ40LvaVKkhoQ4PUMGts8B0tUl4 0dBFosAl9H2Po4JbtJfnGrrl1C111X3szx7zUqH4iReZamk1a4p/KQ+tJ JfgrVYXtbt3Mm8tzuHrOE3W3En5bnnAQa23I054e7a9kOp6D8Xva4t1WM jFOwbSQveRAErcaqv15Gsom2AXPESCcktjXnxEymOFk2+zGiVr+0dmuzX A==; X-CSE-ConnectionGUID: iMg7vixOSK+hKjH6YhVjEA== X-CSE-MsgGUID: JyQ7zHXYSNW/BphruVYIJQ== X-IronPort-AV: E=McAfee;i="6800,10657,11784"; a="67141728" X-IronPort-AV: E=Sophos;i="6.23,232,1770624000"; d="scan'208";a="67141728" Received: from orviesa004.jf.intel.com ([10.64.159.144]) by fmvoesa110.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 May 2026 07:40:40 -0700 X-CSE-ConnectionGUID: A7vea9YlSxmRmrsMYgPY5A== X-CSE-MsgGUID: z/AyjWayTZiSorS9BvSluA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.23,232,1770624000"; d="scan'208";a="242447866" Received: from sannilnx-dsk.jer.intel.com (HELO [127.0.1.1]) ([10.12.231.107]) by orviesa004-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 May 2026 07:40:40 -0700 From: Alexander Usyskin Date: Wed, 13 May 2026 17:18:44 +0300 Subject: [PATCH 3/4] issei: implement main thread and ham messages Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260513-issei-for-upstream-v1-3-f590038678f9@intel.com> References: <20260513-issei-for-upstream-v1-0-f590038678f9@intel.com> In-Reply-To: <20260513-issei-for-upstream-v1-0-f590038678f9@intel.com> To: Greg Kroah-Hartman Cc: Menachem Adin , Alexander Usyskin , linux-kernel@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1778681944; l=16058; i=alexander.usyskin@intel.com; s=20260315; h=from:subject:message-id; bh=TnNl6ECxuBC4/q8cEujwjm+X/oKnWNuW1qvcYbelBm8=; b=v01Mz0y6jp+dgi91z67cmftG8CxttqX5K38r0QCjwZbsEQIfz/H37SkJ98ZhXUYe7WjHcWd+z WfEFOrfuhVEALba5dr5CghpnFpHEXwjMLImRMNwUwbR7QmGwr5Qj0R6 X-Developer-Key: i=alexander.usyskin@intel.com; a=ed25519; pk=X+qoF/nFCdDOV04IForWSxnkyoCAbUE10egZi6PSfcU= Introduce the main thread and HECI Active Management (HAM) message handling for the ISSEI (Intel Silicon Security Engine Interface) subsystem. The main thread is responsible for managing the reset flow and processing messages, while the HAM message handling is crucial for initializing communication with the firmware and managing clients. With this implementation, the ISSEI driver is capable of performing the required initialization and management of communication between the host and the firmware. Reviewed-by: Karol Wachowski Co-developed-by: Vitaly Lubart Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin --- drivers/misc/issei/Makefile | 2 + drivers/misc/issei/ham.c | 163 +++++++++++++++++++++++ drivers/misc/issei/ham.h | 20 +++ drivers/misc/issei/issei_dev.h | 3 + drivers/misc/issei/main.c | 296 +++++++++++++++++++++++++++++++++++++= ++++ 5 files changed, 484 insertions(+) diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile index 1471ed99d619..f21e0b985c94 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -7,3 +7,5 @@ issei-objs +=3D cdev.o issei-objs +=3D dma.o issei-objs +=3D fw_client.o issei-objs +=3D host_client.o +issei-objs +=3D ham.o +issei-objs +=3D main.o diff --git a/drivers/misc/issei/ham.c b/drivers/misc/issei/ham.c new file mode 100644 index 000000000000..17eae91f077d --- /dev/null +++ b/drivers/misc/issei/ham.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include + +#include "dma.h" +#include "fw_client.h" +#include "ham.h" +#include "hw_msg.h" +#include "issei_dev.h" + +static int __issei_ham_send_msg(struct issei_device *idev, u32 length, voi= d *buf) +{ + struct issei_dma_data data =3D { }; + int ret; + + data.length =3D length; + data.buf =3D buf; + ret =3D issei_dma_write(idev, &data); + if (ret) + return ret; + return idev->ops->irq_write_generate(idev); +} + +/** + * issei_ham_send_start_req - send start request to firmware + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_send_start_req(struct issei_device *idev) +{ + struct ham_start_message_req req; + + req.header.cmd =3D HAM_BUS_CMD_START_REQ; + req.supported_version =3D ISSEI_SUPPORTED_PROTOCOL_VER; + req.heci_capabilities_length =3D 0; + + return __issei_ham_send_msg(idev, sizeof(req), &req); +} + +/** + * issei_ham_send_clients_req - send clients request to firmware + * @idev: issei device object + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_send_clients_req(struct issei_device *idev) +{ + struct ham_get_clients_req req; + + req.header.cmd =3D HAM_BUS_CMD_CLIENT_REQ; + + return __issei_ham_send_msg(idev, sizeof(req), &req); +} + +static int issei_ham_start_rsp(struct issei_device *idev, const u8 *buf, s= ize_t length) +{ + struct ham_start_message_res *res =3D (struct ham_start_message_res *)buf; + int ret; + + if (idev->rst_state !=3D ISSEI_RST_STATE_START) { + dev_err(&idev->dev, "Wrong state %d !=3D %d\n", + idev->rst_state, ISSEI_RST_STATE_START); + return -EPROTO; + } + + if (length < sizeof(*res)) { + dev_err(&idev->dev, "Small start response size %zu < %zu\n", + length, sizeof(*res)); + return -EPROTO; + } + + if (length - sizeof(*res) !=3D res->heci_capabilities_length) { + dev_err(&idev->dev, "Wrong start response size %zu !=3D %u\n", + length - sizeof(*res), res->heci_capabilities_length); + return -EPROTO; + } + + memcpy(idev->fw_version, res->fw_version, sizeof(idev->fw_version)); + idev->fw_protocol_ver =3D res->supported_version; + dev_dbg(&idev->dev, "FW protocol: %u FW version %u.%u.%u.%u", idev->fw_pr= otocol_ver, + idev->fw_version[0], idev->fw_version[1], + idev->fw_version[2], idev->fw_version[3]); + + ret =3D issei_ham_send_clients_req(idev); + if (ret =3D=3D -EBUSY) + ret =3D 0; + + return ret; +} + +static int issei_ham_client_rsp(struct issei_device *idev, const u8 *buf, = size_t length) +{ + struct ham_get_clients_res *res =3D (struct ham_get_clients_res *)buf; + struct ham_client_properties *client; + + if (idev->rst_state !=3D ISSEI_RST_STATE_CLIENT_ENUM) { + dev_err(&idev->dev, "Wrong state %d !=3D %d\n", + idev->rst_state, ISSEI_RST_STATE_CLIENT_ENUM); + return -EPROTO; + } + + if (length < sizeof(*res)) { + dev_err(&idev->dev, "Small response size %zu < %zu\n", length, sizeof(*r= es)); + return -EPROTO; + } + + if (length - sizeof(*res) !=3D res->client_count * sizeof(struct ham_clie= nt_properties)) { + dev_err(&idev->dev, "Wrong response size %zu < %zu\n", + length - sizeof(*res), + res->client_count * sizeof(struct ham_client_properties)); + return -EPROTO; + } + + guard(mutex)(&idev->client_lock); + + for (size_t i =3D 0; i < res->client_count; i++) { + client =3D &res->clients_props[i]; + dev_dbg(&idev->dev, "client: id =3D %u ver =3D %u uuid =3D %pUb mtu =3D = %u flags =3D %u", + client->client_number, client->protocol_ver, &client->client_uuid, + client->client_mtu, client->flags); + issei_fw_cl_create(idev, client->client_number, client->protocol_ver, + &client->client_uuid, client->client_mtu, client->flags); + } + return 0; +} + +static int __issei_ham_process_ham_rsp(struct issei_device *idev, const u8= *buf, size_t length) +{ + struct ham_bus_message *hdr =3D (struct ham_bus_message *)buf; + + switch (hdr->cmd) { + case HAM_BUS_CMD_START_RSP: + return issei_ham_start_rsp(idev, buf, length); + + case HAM_BUS_CMD_CLIENT_RSP: + return issei_ham_client_rsp(idev, buf, length); + + default: + dev_err(&idev->dev, "Unexpected command 0x%x", hdr->cmd); + return -EPROTO; + } +} + +/** + * issei_ham_process_ham_rsp - process response from firmware and release = buffer + * @idev: issei device object + * @buf: response buffer + * @length: response buffer length + * + * Return: 0 on success, <0 on failures + */ +int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, si= ze_t length) +{ + int ret; + + ret =3D __issei_ham_process_ham_rsp(idev, buf, length); + kfree(buf); + return ret; +} diff --git a/drivers/misc/issei/ham.h b/drivers/misc/issei/ham.h new file mode 100644 index 000000000000..37ea64bb2920 --- /dev/null +++ b/drivers/misc/issei/ham.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HAM_H_ +#define _ISSEI_HAM_H_ + +#include + +struct issei_device; + +int issei_ham_send_start_req(struct issei_device *idev); +int issei_ham_send_clients_req(struct issei_device *idev); + +static inline bool issei_is_ham_rsp(u16 fw_id, u16 host_id) +{ + return fw_id =3D=3D 0 && host_id =3D=3D 0; +} + +int issei_ham_process_ham_rsp(struct issei_device *idev, const u8 *buf, si= ze_t length); + +#endif /* _ISSEI_HAM_H_ */ diff --git a/drivers/misc/issei/issei_dev.h b/drivers/misc/issei/issei_dev.h index c742e7fe6cb6..b47c4a7c2da4 100644 --- a/drivers/misc/issei/issei_dev.h +++ b/drivers/misc/issei/issei_dev.h @@ -157,4 +157,7 @@ static inline void issei_poke_process_thread(struct iss= ei_device *idev) WRITE_ONCE(idev->has_data, true); wake_up_interruptible(&idev->wait_has_data); } + +int issei_start(struct issei_device *idev); +void issei_stop(struct issei_device *idev); #endif /* _ISSEI_DEV_H_ */ diff --git a/drivers/misc/issei/main.c b/drivers/misc/issei/main.c new file mode 100644 index 000000000000..5987fb340250 --- /dev/null +++ b/drivers/misc/issei/main.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdev.h" +#include "fw_client.h" +#include "host_client.h" +#include "ham.h" +#include "issei_dev.h" + +static void issei_rst_state_set(struct issei_device *idev, enum issei_rst_= state state) +{ + idev->rst_state =3D state; + /* wake up the thread */ + if (waitqueue_active(&idev->wait_rst_state)) + wake_up(&idev->wait_rst_state); +} + +static int issei_reset(struct issei_device *idev) +{ + int ret; + + idev->ops->irq_clear(idev); + + issei_cl_all_disconnect(idev); + issei_fw_cl_remove_all(idev); + /* No need to check for overflow here, the counter is used only for info = */ + idev->all_reset_count++; + ret =3D idev->ops->hw_reset(idev, !idev->power_down); + issei_dmam_setup(idev); + if (ret) { + dev_err(&idev->dev, "hw_reset failed ret =3D %d\n", ret); + return ret; + } + + if (idev->power_down) { + dev_dbg(&idev->dev, "powering down: end of reset\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + return -ENODEV; + } + return 0; +} + +static int issei_process_read_msg(struct issei_device *idev) +{ + struct issei_dma_data data =3D {}; + int ret; + + ret =3D issei_dma_read(idev, &data); + if (ret) + return ret; + + dev_dbg(&idev->dev, "Processing response %u %u %u %u\n", data.fw_id, data= .host_id, + data.status, data.length); + if (data.status !=3D HAMS_SUCCESS) { + dev_err(&idev->dev, "Command failed with status 0x%02X", data.status); + kfree(data.buf); + ret =3D -EIO; + } else { + if (issei_is_ham_rsp(data.fw_id, data.host_id)) + ret =3D issei_ham_process_ham_rsp(idev, data.buf, data.length); + else + ret =3D issei_cl_read_buf(idev, data.fw_id, data.host_id, + data.buf, data.length); + } + idev->ops->irq_write_generate(idev); + return ret; +} + +static int issei_process_write_msg(struct issei_device *idev) +{ + if (idev->rst_state !=3D ISSEI_RST_STATE_DONE) + return 0; + + return issei_cl_write_from_queue(idev); +} + +static int issei_process_thread(void *_dev) +{ + long timeout, old_timeout =3D MAX_SCHEDULE_TIMEOUT; + struct issei_device *idev =3D _dev; + int ret; + + while (!kthread_should_stop()) { + dev_dbg(&idev->dev, "process_work in %d\n", idev->rst_state); + if (!idev->ops->hw_is_ready(idev) && idev->rst_state > ISSEI_RST_STATE_H= W_READY) { + if (!idev->power_down) + dev_dbg(&idev->dev, "HW not ready, resetting\n"); + idev->rst_state =3D ISSEI_RST_STATE_INIT; + } + if (idev->power_down) + idev->rst_state =3D ISSEI_RST_STATE_INIT; + WRITE_ONCE(idev->has_data, false); + dev_dbg(&idev->dev, "reset_step in %d\n", idev->rst_state); + timeout =3D MAX_SCHEDULE_TIMEOUT; + ret =3D 0; + switch (idev->rst_state) { + case ISSEI_RST_STATE_DISABLED: + if (idev->power_down) { + dev_dbg(&idev->dev, "Interrupt in power down?\n"); + break; + } + idev->rst_state =3D ISSEI_RST_STATE_INIT; + fallthrough; + + case ISSEI_RST_STATE_INIT: + idev->ops->irq_clear(idev); + idev->ops->irq_sync(idev); + + if (!idev->power_down) { + idev->reset_count++; + if (idev->reset_count > ISSEI_MAX_CONSEC_RESET) { + dev_err(&idev->dev, "reset: reached maximal consecutive resets: disab= ling the device\n"); + issei_rst_state_set(idev, ISSEI_RST_STATE_DISABLED); + break; + } + } + + ret =3D issei_reset(idev); + if (idev->power_down) { + dev_dbg(&idev->dev, "Powering down\n"); + return 0; + } + if (ret) + break; + + idev->rst_state =3D ISSEI_RST_STATE_HW_READY; + timeout =3D msecs_to_jiffies(ISSEI_RST_HW_READY_TIMEOUT_MSEC); + break; + + case ISSEI_RST_STATE_HW_READY: + if (!idev->ops->hw_is_ready(idev)) { + dev_dbg(&idev->dev, "HW is not ready?\n"); + timeout =3D old_timeout; + break; + } + + dev_dbg(&idev->dev, "HW is ready\n"); + idev->ops->hw_reset_release(idev); + idev->ops->host_set_ready(idev); + ret =3D idev->ops->setup_message_send(idev); + if (ret) + break; + + idev->rst_state =3D ISSEI_RST_STATE_SETUP; + timeout =3D msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + break; + + case ISSEI_RST_STATE_SETUP: + ret =3D idev->ops->setup_message_recv(idev); + if (ret) { + if (ret =3D=3D -ENODATA) { + ret =3D 0; + timeout =3D old_timeout; + } + } else { + timeout =3D msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + ret =3D issei_ham_send_start_req(idev); + idev->rst_state =3D ISSEI_RST_STATE_START; + } + break; + + case ISSEI_RST_STATE_START: + ret =3D issei_process_read_msg(idev); + if (!ret) { + timeout =3D msecs_to_jiffies(ISSEI_RST_STEP_TIMEOUT_MSEC); + idev->rst_state =3D ISSEI_RST_STATE_CLIENT_ENUM; + } else if (ret =3D=3D -ENODATA) { + ret =3D 0; + timeout =3D old_timeout; + } + break; + + case ISSEI_RST_STATE_CLIENT_ENUM: + ret =3D issei_process_read_msg(idev); + if (ret) { + if (ret =3D=3D -ENODATA) { + ret =3D 0; + timeout =3D old_timeout; + } + } else { + idev->reset_count =3D 0; + idev->rst_state =3D ISSEI_RST_STATE_DONE; + dev_dbg(&idev->dev, "Reset finished successfully\n"); + } + break; + + case ISSEI_RST_STATE_DONE: + ret =3D issei_process_read_msg(idev); + if (ret !=3D 0 && ret !=3D -ENODATA) + break; + + ret =3D issei_process_write_msg(idev); + break; + } + + if (ret) { + dev_warn(&idev->dev, "Process failed ret =3D %d\n", ret); + idev->rst_state =3D ISSEI_RST_STATE_INIT; + continue; + } + + /* + * Every thread that has data to process sets the 'has_data' flag and + * triggers the wait queue. + * The processing thread, in each loop iteration, resets 'has_data' + * and processes all available data. + * + * After processing, the thread waits for 'has_data' to be set again. + * + * If the wait function times out but 'has_data' becomes 1 before + * the subsequent atomic read check, this is acceptable from a flow + * perspective - the thread will continue processing the data. + * + * The 'has_data' flag cannot become 0 between the wait function and + * the atomic read check, since only this thread is allowed to reset it = to 0. + */ + + old_timeout =3D wait_event_interruptible_timeout(idev->wait_has_data, + READ_ONCE(idev->has_data), + timeout); + if (idev->rst_state =3D=3D ISSEI_RST_STATE_DISABLED) + continue; + + if (!READ_ONCE(idev->has_data)) { + dev_warn(&idev->dev, "Timed out at state %d, resetting\n", + idev->rst_state); + idev->rst_state =3D ISSEI_RST_STATE_INIT; + } + } + + return 0; +} + +/** + * issei_start - configure HW device and start processing thread. + * @idev: the device structure + * + * Return: 0 on success, < 0 on failure + */ +int issei_start(struct issei_device *idev) +{ + int ret; + + idev->power_down =3D false; + + ret =3D issei_dmam_setup(idev); + if (ret) + return ret; + + idev->ops->irq_clear(idev); + + ret =3D idev->ops->hw_config(idev); + if (ret) + return ret; + + idev->process_thread =3D kthread_run(issei_process_thread, idev, + "kisseiprocess/%s", dev_name(&idev->dev)); + if (IS_ERR(idev->process_thread)) { + ret =3D PTR_ERR(idev->process_thread); + dev_err(&idev->dev, "unable to create process thread. ret =3D %d\n", ret= ); + return ret; + } + + issei_poke_process_thread(idev); + return 0; +} +EXPORT_SYMBOL_GPL(issei_start); + +/** + * issei_stop - stop interrupts and processing thread. + * @idev: the device structure + */ +void issei_stop(struct issei_device *idev) +{ + idev->power_down =3D true; + + idev->ops->irq_clear(idev); + idev->ops->irq_sync(idev); + + issei_poke_process_thread(idev); + + wait_event_timeout(idev->wait_rst_state, + (idev->rst_state =3D=3D ISSEI_RST_STATE_DISABLED), + msecs_to_jiffies(ISSEI_STOP_TIMEOUT_MSEC)); + kthread_stop(idev->process_thread); +} +EXPORT_SYMBOL_GPL(issei_stop); --=20 2.43.0 From nobody Fri Jun 12 17:36:49 2026 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.16]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 331A944BCBE for ; Wed, 13 May 2026 14:40:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.16 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778683244; cv=none; b=t46r5KxeTI4h3XSmRAUjc6Bj8Fsetv2ICM1Y8pgTXk+SWlrAkvGDRSFPJ4VYCNToZmOeXw73z+UItt93fXkQhbIhFWFJ+XWQv5P05G981VDT4Wyro+pcujk6JPzB9Z5m9GmvnQdFYjC8mcS1APQ7xVjbpGW7kpFgcx1DJyB2+pk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778683244; c=relaxed/simple; bh=vLO/Lxn8S1FB3AI9sgmcA/4vcwzRISKKmDTyYgwVnD4=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=pL4yYmsNT37IqmUr30YvGU2yo3oqZLBbLCij4OlewZHDtd7ngXBsqcuEJgIsTZT+TG2qrnatu+b4WkXoDUskN1DpxY0QOMdGzYo/wnuF/ojKpYrxNS9BgToP/ayo9ZxbOhQF2YH1wM2Zt4EMoXMNd8ZdFJbPVdmSOvXR6hekN0M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com; spf=pass smtp.mailfrom=intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=WxaB2ZY+; arc=none smtp.client-ip=192.198.163.16 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="WxaB2ZY+" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1778683242; x=1810219242; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=vLO/Lxn8S1FB3AI9sgmcA/4vcwzRISKKmDTyYgwVnD4=; b=WxaB2ZY+hVGA3jm6rtpS4gKcRUh3jXU+vFejr5nTV+nT4+QUqpIY2hUt 7JPjO3AbNjpk4SCMhbaSKCIDVMqhUqsxjx4QPvqbXtVAdlsolw1Qv1RbS tZHCkyrtg6fUYiOHaZTZA5iWbg01bMFLv+UbpMvauKgkzVtX54Oq5xLsx Klk1WPsJDWIp6ia0CILVNjmfAMEYPbiOjdReHx1idfZpAf47KcUXSMLbY +IpYQGWE08R3AOPYxT5HdIyAFRcJ+8G/1M3XEZMHccbiBoJKW+P6nRTc2 3eespSBxFXtT0VZcEKgJP4LEIyjdzs+A48qRmfpmGeZWLKxBZ1MKPgGfi w==; X-CSE-ConnectionGUID: piQOS3vMQROQDiVyrjlL1g== X-CSE-MsgGUID: h3ud57gnQHmgY3yMEydMKw== X-IronPort-AV: E=McAfee;i="6800,10657,11784"; a="67141734" X-IronPort-AV: E=Sophos;i="6.23,232,1770624000"; d="scan'208";a="67141734" Received: from orviesa004.jf.intel.com ([10.64.159.144]) by fmvoesa110.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 May 2026 07:40:42 -0700 X-CSE-ConnectionGUID: RG/DeARqRSSKWeByBHsIzw== X-CSE-MsgGUID: UV4OUIAnQxGxaAUTHQVvuA== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.23,232,1770624000"; d="scan'208";a="242447876" Received: from sannilnx-dsk.jer.intel.com (HELO [127.0.1.1]) ([10.12.231.107]) by orviesa004-auth.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 13 May 2026 07:40:41 -0700 From: Alexander Usyskin Date: Wed, 13 May 2026 17:18:45 +0300 Subject: [PATCH 4/4] issei: add heci hardware module Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260513-issei-for-upstream-v1-4-f590038678f9@intel.com> References: <20260513-issei-for-upstream-v1-0-f590038678f9@intel.com> In-Reply-To: <20260513-issei-for-upstream-v1-0-f590038678f9@intel.com> To: Greg Kroah-Hartman Cc: Menachem Adin , Alexander Usyskin , linux-kernel@vger.kernel.org X-Mailer: b4 0.13.0 X-Developer-Signature: v=1; a=ed25519-sha256; t=1778681944; l=24116; i=alexander.usyskin@intel.com; s=20260315; h=from:subject:message-id; bh=vLO/Lxn8S1FB3AI9sgmcA/4vcwzRISKKmDTyYgwVnD4=; b=FkQSWnxFkfu5in0jKSfWy966Wuecyu35nkdWneyMdq3AoWUuVDQGjO2rjrGrELVKqWJV8FP0D zvyqsCfZVMbBjHLNrMNUJu34UiYU3pUovogrhIqKUeB496fqUbUdl1r X-Developer-Key: i=alexander.usyskin@intel.com; a=ed25519; pk=X+qoF/nFCdDOV04IForWSxnkyoCAbUE10egZi6PSfcU= Add support for the ISSEI (Intel Silicon Security Engine Interface) HECI PCI devices. Add the necessary PCI handling routines, hardware definitions, register mappings and hardware access routines. This enables the communication via HECI PCI device advertized by BIOS. Reviewed-by: Karol Wachowski Co-developed-by: Vitaly Lubart Signed-off-by: Vitaly Lubart Signed-off-by: Alexander Usyskin --- drivers/misc/issei/Kconfig | 16 ++ drivers/misc/issei/Makefile | 4 + drivers/misc/issei/hw_heci.c | 550 ++++++++++++++++++++++++++++++++++= ++++ drivers/misc/issei/hw_heci.h | 47 ++++ drivers/misc/issei/hw_heci_regs.h | 35 +++ drivers/misc/issei/pci_heci.c | 151 +++++++++++ 6 files changed, 803 insertions(+) diff --git a/drivers/misc/issei/Kconfig b/drivers/misc/issei/Kconfig index d98ac7925ce6..12a3fd809969 100644 --- a/drivers/misc/issei/Kconfig +++ b/drivers/misc/issei/Kconfig @@ -11,3 +11,19 @@ config INTEL_SSEI =20 If selected, the /dev/isseiX device will be created. If in doubt, select N. + +if INTEL_SSEI + +config INTEL_SSEI_HW_HECI + tristate "Intel Silicon Security Engine Interface Hardware" + depends on X86 && PCI + help + HECI interface of communication channel between + the host and the Silicon Security Engine. + + Implementation of ISSEI communication channel over + the HECI hardware PCI device. + This device is available on Intel client CPUs released in 2024 + (Lunar Lake) or later. + +endif diff --git a/drivers/misc/issei/Makefile b/drivers/misc/issei/Makefile index f21e0b985c94..8fac360c3342 100644 --- a/drivers/misc/issei/Makefile +++ b/drivers/misc/issei/Makefile @@ -9,3 +9,7 @@ issei-objs +=3D fw_client.o issei-objs +=3D host_client.o issei-objs +=3D ham.o issei-objs +=3D main.o + +obj-$(CONFIG_INTEL_SSEI_HW_HECI) +=3D issei-heci.o +issei-heci-objs :=3D pci_heci.o +issei-heci-objs +=3D hw_heci.o diff --git a/drivers/misc/issei/hw_heci.c b/drivers/misc/issei/hw_heci.c new file mode 100644 index 000000000000..35c55de5c67a --- /dev/null +++ b/drivers/misc/issei/hw_heci.c @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hw_heci.h" +#include "hw_heci_regs.h" +#include "hw_msg.h" + +/** + * heci_reg_read - Reads 32bit data from the issei heci device + * @hw: the heci hardware structure + * @offset: offset from which to read the data + * + * Return: register value (u32) + */ +static inline u32 heci_reg_read(const struct issei_heci_hw *hw, unsigned l= ong offset) +{ + return ioread32(hw->mem_addr + offset); +} + +/** + * heci_reg_write - Writes 32bit data to the issei heci device + * + * @hw: the heci hardware structure + * @offset: offset from which to write the data + * @value: register value to write (u32) + */ +static inline void heci_reg_write(const struct issei_heci_hw *hw, unsigned= long offset, u32 value) +{ + iowrite32(value, hw->mem_addr + offset); +} + +/** + * heci_fwcbrw_read - Reads 32bit data from heci circular buffer + * @hw: the heci hardware structure + * + * Return: FW_CB_RW register value (u32) + */ +static inline u32 heci_fwcbrw_read(const struct issei_heci_hw *hw) +{ + return heci_reg_read(hw, FW_CB_RW); +} + +/** + * heci_hcbww_write - write 32bit data to the host circular buffer + * @hw: the heci hardware structure + * @data: 32bit data to be written to the host circular buffer + */ +static inline void heci_hcbww_write(const struct issei_heci_hw *hw, u32 da= ta) +{ + heci_reg_write(hw, H_CB_WW, data); +} + +/** + * heci_irq_src - Filters IRQ source bits from the host CSR + * @hcsr: host CSR register value + * + * Return: interrupt source bits of host CSR + */ +static inline u32 heci_irq_src(u32 hcsr) +{ + return hcsr & H_CSR_IS; +} + +/** + * heci_hcsr_read - Reads 32bit data from the host CSR + * @idev: the device structure + * + * Return: H_CSR register value (u32) + */ +static inline u32 heci_hcsr_read(const struct issei_device *idev) +{ + return heci_reg_read(to_heci_hw(idev), H_CSR); +} + +/** + * heci_hcsr_write - writes H_CSR register to device + * @idev: the device structure + * @reg: new register value + */ +static inline void heci_hcsr_write(struct issei_device *idev, u32 reg) +{ + heci_reg_write(to_heci_hw(idev), H_CSR, reg); +} + +/** + * heci_hcsr_set - writes H_CSR register to the heci device + * @idev: the device structure + * @reg: new register value + * + * Writes H_CSR register to the heci device + * and ignores the H_IS bit for it is write-one-to-zero. + * + */ +static inline void heci_hcsr_set(struct issei_device *idev, u32 reg) +{ + reg &=3D ~H_CSR_IS; + heci_hcsr_write(idev, reg); +} + +/** + * heci_hcsr_set_hig - set host interrupt (set H_CSR_IG) + * @idev: the device structure + */ +static inline void heci_hcsr_set_hig(struct issei_device *idev) +{ + u32 reg; + + reg =3D heci_hcsr_read(idev) | H_CSR_IG; + heci_hcsr_set(idev, reg); +} + +/** + * heci_fwcsr_read - Reads 32bit data from the FW CSR + * @idev: the device structure + * + * Return: FW_CSR_HA register value (u32) + */ +static inline u32 heci_fwcsr_read(const struct issei_device *idev) +{ + return heci_reg_read(to_heci_hw(idev), FW_CSR_HA); +} + +/** + * heci_count_full_read_slots - counts read full slots. + * @idev: the device structure + * + * Return: -EOVERFLOW if overflow, otherwise filled slots count + */ +static int heci_count_full_read_slots(struct issei_device *idev) +{ + u8 buffer_depth, filled_slots; + u8 read_ptr, write_ptr; + u32 reg; + + reg =3D heci_fwcsr_read(idev); + buffer_depth =3D (u8)FIELD_GET(FW_CSR_CBD, reg); + read_ptr =3D (u8)FIELD_GET(FW_CSR_CBRP, reg); + write_ptr =3D (u8)FIELD_GET(FW_CSR_CBWP, reg); + filled_slots =3D write_ptr - read_ptr; + + /* check for overflow */ + if (filled_slots > buffer_depth) + return -EOVERFLOW; + + dev_dbg(&idev->dev, "filled_slots =3D %08x\n", filled_slots); + return filled_slots; +} + +/** + * heci_irq_disable - disables heci device interrupts + * @idev: the device structure + * @reg: supplied hcsr register value + * + * disables heci device interrupts using supplied hcsr register value. + */ +static inline void heci_irq_disable(struct issei_device *idev, u32 reg) +{ + reg &=3D ~H_CSR_IE; + heci_hcsr_set(idev, reg); +} + +/** + * heci_irq_clear - clear and stop interrupts + * @idev: the device structure + * @reg: supplied hcsr register value + */ +static inline void heci_irq_clear(struct issei_device *idev, u32 reg) +{ + if (heci_irq_src(reg)) + heci_hcsr_write(idev, reg); +} + +/** + * issei_heci_irq_clear - clear and stop interrupts + * @idev: the device structure + */ +static void issei_heci_irq_clear(struct issei_device *idev) +{ + u32 reg =3D heci_hcsr_read(idev); + + heci_irq_clear(idev, reg); +} + +/** + * issei_heci_irq_enable - enables heci device interrupts + * @idev: the device structure + */ +static void issei_heci_irq_enable(struct issei_device *idev) +{ + u32 reg; + + reg =3D heci_hcsr_read(idev) | H_CSR_IE; + heci_hcsr_set(idev, reg); +} + +/** + * issei_heci_irq_disable - disables heci device interrupts + * @idev: the device structure + */ +static void issei_heci_irq_disable(struct issei_device *idev) +{ + u32 reg =3D heci_hcsr_read(idev); + + heci_irq_disable(idev, reg); +} + +static void issei_heci_irq_sync(struct issei_device *idev) +{ + synchronize_irq(to_heci_hw(idev)->irq); +} + +/** + * issei_heci_hw_reset_release - release device from the reset + * @idev: the device structure + */ +static void issei_heci_hw_reset_release(struct issei_device *idev) +{ + u32 reg =3D heci_hcsr_read(idev); + + reg |=3D H_CSR_IG; + reg &=3D ~H_CSR_RST; + heci_hcsr_set(idev, reg); +} + +/** + * heci_hw_is_ready - check whether the hw has turned ready + * @idev: the device structure + * + * Return: bool + */ +static bool heci_hw_is_ready(struct issei_device *idev) +{ + u32 reg =3D heci_fwcsr_read(idev); + + return reg & FW_CSR_RDY; +} + +/** + * heci_hw_is_resetting - check whether the hw is in reset + * @idev: the device structure + * + * Return: bool + */ +static bool heci_hw_is_resetting(struct issei_device *idev) +{ + u32 reg =3D heci_fwcsr_read(idev); + + return reg & FW_CSR_RST; +} + +/** + * issei_heci_host_set_ready - enable device + * @idev: the device structure + */ +static void issei_heci_host_set_ready(struct issei_device *idev) +{ + u32 reg =3D heci_hcsr_read(idev); + + reg |=3D H_CSR_IE | H_CSR_IG | H_CSR_RDY; + heci_hcsr_set(idev, reg); +} + +/** + * issei_heci_hw_reset - resets fw via heci csr register. + * @idev: the device structure + * @enable: if interrupt should be enabled after reset. + * + * Return: 0 on success an error code otherwise + */ +static int issei_heci_hw_reset(struct issei_device *idev, bool enable) +{ + u32 reg; + + if (enable) + issei_heci_irq_enable(idev); + + reg =3D heci_hcsr_read(idev); + /* + * H_CSR_RST may be found lit before reset is started, + * for example if preceding reset flow hasn't completed. + * In that case asserting H_CSR_RST will be ignored, therefore + * we need to clean H_CSR_RST bit to start a successful reset sequence. + */ + if (reg & H_CSR_RST) { + dev_warn(&idev->dev, "H_CSR_RST is set =3D 0x%08X", reg); + reg &=3D ~H_CSR_RST; + heci_hcsr_set(idev, reg); + reg =3D heci_hcsr_read(idev); + } + + reg |=3D H_CSR_RST | H_CSR_IG | H_CSR_IS; + + if (!enable) + reg &=3D ~H_CSR_IE; + + heci_hcsr_write(idev, reg); + + /* + * Host reads the H_CSR once to ensure that the + * posted write to H_CSR completes. + */ + reg =3D heci_hcsr_read(idev); + + if (!(reg & H_CSR_RST)) + dev_warn(&idev->dev, "H_CSR_RST is not set =3D 0x%08X", reg); + + if (reg & H_CSR_RDY) + dev_warn(&idev->dev, "H_CSR_RDY is not cleared 0x%08X", reg); + + if (!enable) + issei_heci_hw_reset_release(idev); + return 0; +} + +/** + * issei_heci_irq_write_generate - generate interrupt to signal write comp= letion. + * @idev: the device structure + * + * Return: 0 on success, -EIO if hardware is not ready and requires reset + */ +static int issei_heci_irq_write_generate(struct issei_device *idev) +{ + struct issei_heci_hw *hw =3D to_heci_hw(idev); + + scoped_guard(spinlock_irqsave, &hw->access_lock) + heci_hcsr_set_hig(idev); + if (!heci_hw_is_ready(idev)) + return -EIO; + return 0; +} + +/** + * issei_heci_hw_config - initial hardware configuration. + * @idev: the device structure + * + * Return: 0 always + */ +static int issei_heci_hw_config(struct issei_device *idev) +{ + struct issei_heci_hw *hw =3D to_heci_hw(idev); + u32 reg; + + /* Doesn't change in runtime */ + reg =3D heci_hcsr_read(idev); + hw->hbuf_depth =3D FIELD_GET(H_CSR_CBD, reg); + + return 0; +} + +/** + * heci_write_hbuf - write to hardware buffer + * @idev: the device structure + * @data: data to write + * @data_len: data size + * + * Return: 0 on success, <0 on error + */ +static int heci_write_hbuf(struct issei_device *idev, const void *data, si= ze_t data_len) +{ + struct issei_heci_hw *hw =3D to_heci_hw(idev); + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { + dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + const u32 *reg_buf =3D data; + size_t i; + + for (i =3D 0; i < data_len / CB_SLOT_SIZE; i++) + heci_hcbww_write(hw, reg_buf[i]); + } + + return issei_heci_irq_write_generate(idev); +} + +/** + * heci_read_hbuf - read data from hardware buffer + * @idev: the device structure + * @data: buffer to store read data + * @data_len: buffer size + * + * Return: 0 on success, <0 on error + */ +static int heci_read_hbuf(struct issei_device *idev, void *data, size_t da= ta_len) +{ + struct issei_heci_hw *hw =3D to_heci_hw(idev); + u32 *reg_buf =3D data; + + if (!IS_ALIGNED(data_len, CB_SLOT_SIZE)) { + dev_err(&idev->dev, "Data size %zu not aligned to slot size %lu\n", + data_len, CB_SLOT_SIZE); + return -EINVAL; + } + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + for (; data_len >=3D CB_SLOT_SIZE; data_len -=3D CB_SLOT_SIZE) + *reg_buf++ =3D heci_fwcbrw_read(hw); + } + return 0; +} + +/** + * issei_heci_setup_message_send - send setup message to firmware + * @idev: the device structure + * + * Return: 0 on success, <0 on error + */ +static int issei_heci_setup_message_send(struct issei_device *idev) +{ + struct ham_setup_shared_memory_req req =3D { + .msg_id =3D HAM_CB_MESSAGE_ID_REQ, + .ver =3D HAM_CB_MESSAGE_VER, + .reserved =3D 0, + .buffer_physical_address =3D idev->dma.daddr, + .host_to_fw_section_length =3D idev->dma.length.h2f, + .fw_to_host_section_length =3D idev->dma.length.f2h, + .control_length =3D idev->dma.length.ctl + }; + int ret; + + ret =3D heci_write_hbuf(idev, &req, sizeof(req)); + if (ret) + dev_err(&idev->dev, "Shared memory req write failed ret =3D %d\n", ret); + + return ret; +} + +/** + * issei_heci_setup_message_recv - receive setup message response from fir= mware + * @idev: the device structure + * + * Return: 0 on success, <0 on error + */ +static int issei_heci_setup_message_recv(struct issei_device *idev) +{ + struct ham_setup_shared_memory_res res; + int ret; + + if (heci_count_full_read_slots(idev) !=3D sizeof(res) / CB_SLOT_SIZE) { + dev_dbg(&idev->dev, "Setup response is not fully received\n"); + return -ENODATA; + } + + ret =3D heci_read_hbuf(idev, &res, sizeof(res)); + if (ret) { + dev_err(&idev->dev, "Shared memory res read failed ret =3D %d\n", ret); + return ret; + } + + if (res.msg_id !=3D HAM_CB_MESSAGE_ID_RES) { + dev_err(&idev->dev, "Shared memory res header 0x%x !=3D 0x%x\n", + res.msg_id, HAM_CB_MESSAGE_ID_RES); + return -EPROTO; + } + if (res.status !=3D 0) { + dev_err(&idev->dev, "Shared memory res status %d !=3D 0\n", res.status); + return -EPROTO; + } + + return 0; +} + +static const struct issei_hw_ops hw_heci_ops =3D { + .irq_clear =3D issei_heci_irq_clear, + .irq_enable =3D issei_heci_irq_enable, + .irq_disable =3D issei_heci_irq_disable, + .irq_sync =3D issei_heci_irq_sync, + + .hw_reset =3D issei_heci_hw_reset, + .hw_config =3D issei_heci_hw_config, + .hw_reset_release =3D issei_heci_hw_reset_release, + .host_set_ready =3D issei_heci_host_set_ready, + + .hw_is_ready =3D heci_hw_is_ready, + + .setup_message_send =3D issei_heci_setup_message_send, + .setup_message_recv =3D issei_heci_setup_message_recv, + .irq_write_generate =3D issei_heci_irq_write_generate, +}; + +const struct issei_hw_ops *issei_heci_get_ops(void) +{ + return &hw_heci_ops; +} + +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id) +{ + struct issei_device *idev =3D dev_id; + struct issei_heci_hw *hw =3D to_heci_hw(idev); + u32 reg; + + reg =3D heci_hcsr_read(idev); + if (!heci_irq_src(reg)) + return IRQ_NONE; + + scoped_guard(spinlock_irqsave, &hw->access_lock) { + reg =3D heci_hcsr_read(idev); + heci_irq_clear(idev, reg); + + if (heci_hw_is_resetting(idev)) + heci_hcsr_set_hig(idev); + } + + dev_dbg(&idev->dev, "interrupt source 0x%08X\n", heci_irq_src(reg)); + + issei_poke_process_thread(idev); + + return IRQ_HANDLED; +} + +static const struct hw_heci_cfg hw_heci_pch_cfg =3D { + .dma_length.h2f =3D SZ_64K, + .dma_length.f2h =3D SZ_64K, + .dma_length.ctl =3D SZ_4K, +}; + +const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx) +{ + return &hw_heci_pch_cfg; +} + +/** + * issei_heci_dev_init - initializes the issei device structure with hw_he= ci + * @idev: device structure + * @mem_addr: memory address on bar + * @cfg: per device generation config + */ +void issei_heci_dev_init(struct issei_device *idev, + void __iomem *mem_addr, const struct hw_heci_cfg *cfg) +{ + struct issei_heci_hw *hw =3D to_heci_hw(idev); + + spin_lock_init(&hw->access_lock); + hw->mem_addr =3D mem_addr; + hw->cfg =3D cfg; +} diff --git a/drivers/misc/issei/hw_heci.h b/drivers/misc/issei/hw_heci.h new file mode 100644 index 000000000000..0ae2faeedf12 --- /dev/null +++ b/drivers/misc/issei/hw_heci.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_HECI_H_ +#define _ISSEI_HW_HECI_H_ +#include +#include +#include + +#include "issei_dev.h" + +/* + * hw_heci_cfg - issei heci device configuration + * + * @dma_length: DMA area length + */ +struct hw_heci_cfg { + const struct issei_dma_length dma_length; +}; + +/** + * struct issei_heci_hw - issei heci hw specific data + * + * @cfg: per device generation config and ops + * @mem_addr: io memory address + * @irq: device irq number + * @access_lock: spinlock to protect hw access + * @hbuf_depth: depth of hardware host/write buffer in slots + */ +struct issei_heci_hw { + const struct hw_heci_cfg *cfg; + void __iomem *mem_addr; + int irq; + spinlock_t access_lock; + u8 hbuf_depth; +}; + +#define to_heci_hw(dev) ((struct issei_heci_hw *)(dev)->hw) + +const struct hw_heci_cfg *issei_heci_get_cfg(kernel_ulong_t idx); +const struct issei_hw_ops *issei_heci_get_ops(void); + +void issei_heci_dev_init(struct issei_device *idev, + void __iomem *mem_addr, const struct hw_heci_cfg *cfg); + +irqreturn_t issei_heci_irq_quick_handler(int irq, void *dev_id); + +#endif /* _ISSEI_HW_HECI_H_ */ diff --git a/drivers/misc/issei/hw_heci_regs.h b/drivers/misc/issei/hw_heci= _regs.h new file mode 100644 index 000000000000..a991b670a9d3 --- /dev/null +++ b/drivers/misc/issei/hw_heci_regs.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright (C) 2023-2026 Intel Corporation */ +#ifndef _ISSEI_HW_HECI_REGS_H_ +#define _ISSEI_HW_HECI_REGS_H_ + +#include + +/* H_CB_WW - Host Circular Buffer (CB) Write Window register */ +#define H_CB_WW 0x0 +/* H_CSR - Host Control Status register */ +#define H_CSR 0x4 +#define H_CSR_CBD GENMASK(31, 24) /* Host Circular Buffer Depth */ +#define H_CSR_CBWP GENMASK(23, 16) /* Host Circular Buffer Write Pointer = */ +#define H_CSR_CBRP GENMASK(15, 8) /* Host Circular Buffer Read Pointer */ +#define H_CSR_RST BIT(4) /* Host Reset */ +#define H_CSR_RDY BIT(3) /* Host Ready */ +#define H_CSR_IG BIT(2) /* Host Interrupt Generate */ +#define H_CSR_IS BIT(1) /* Host Interrupt Status */ +#define H_CSR_IE BIT(0) /* Host Interrupt Enable */ +/* FW_CB_RW - FW Circular Buffer Read Window register (read only) */ +#define FW_CB_RW 0x8 +/* FW_CSR_HA - FW Control Status Host Access register (read only) */ +#define FW_CSR_HA 0xC +#define FW_CSR_CBD GENMASK(31, 24) /* FW CB (Circular Buffer) Depth */ +#define FW_CSR_CBWP GENMASK(23, 16) /* FW CB Write Pointer */ +#define FW_CSR_CBRP GENMASK(15, 8) /* FW CB Read Pointer */ +#define FW_CSR_RST BIT(4) /* FW Reset */ +#define FW_CSR_RDY BIT(3) /* FW Ready */ +#define FW_CSR_IG BIT(2) /* FW Interrupt Generate */ +#define FW_CSR_IS BIT(1) /* FW Interrupt Status */ +#define FW_CSR_IE BIT(0) /* FW Interrupt Enable */ + +#define CB_SLOT_SIZE sizeof(u32) /* Circular Buffer windows size */ + +#endif /* _ISSEI_HW_HECI_REGS_H_ */ diff --git a/drivers/misc/issei/pci_heci.c b/drivers/misc/issei/pci_heci.c new file mode 100644 index 000000000000..86c567a4bf0f --- /dev/null +++ b/drivers/misc/issei/pci_heci.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2023-2026 Intel Corporation */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdev.h" +#include "hw_heci.h" +#include "hw_heci_regs.h" + +static int issei_heci_probe(struct pci_dev *pdev, const struct pci_device_= id *ent) +{ + struct device *dev =3D &pdev->dev; + const struct hw_heci_cfg *cfg; + struct issei_device *idev; + struct issei_heci_hw *hw; + char __iomem *registers; + int err; + + cfg =3D issei_heci_get_cfg(ent->driver_data); + if (!cfg) + return dev_err_probe(dev, -ENODEV, "no usable configuration.\n"); + + err =3D pcim_enable_device(pdev); + if (err) + return dev_err_probe(dev, err, "failed to enable pci device.\n"); + + pci_set_master(pdev); + + registers =3D pcim_iomap_region(pdev, 0, KBUILD_MODNAME); + if (IS_ERR(registers)) + return dev_err_probe(dev, PTR_ERR(registers), "failed to get pci region.= \n"); + + err =3D dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64)); + if (err) + return dev_err_probe(dev, err, "no usable DMA configuration.\n"); + + idev =3D issei_register(sizeof(*hw), dev, &cfg->dma_length, issei_heci_ge= t_ops()); + if (IS_ERR(idev)) + return dev_err_probe(dev, PTR_ERR(idev), "register failure.\n"); + + issei_heci_dev_init(idev, registers, cfg); + + pci_set_drvdata(pdev, idev); + + err =3D pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); + if (err < 0) { + dev_err_probe(dev, err, "pci_alloc_irq_vectors failure.\n"); + goto deregister; + } + + hw =3D to_heci_hw(idev); + hw->irq =3D pci_irq_vector(pdev, 0); + + err =3D request_threaded_irq(hw->irq, + issei_heci_irq_quick_handler, + NULL, + IRQF_SHARED, KBUILD_MODNAME, idev); + if (err) + goto release_irq; + + err =3D issei_start(idev); + if (err) { + dev_err_probe(dev, err, "init hw failure.\n"); + goto free_irq; + } + + return 0; + +free_irq: + idev->ops->irq_disable(idev); + free_irq(hw->irq, idev); +release_irq: + pci_free_irq_vectors(pdev); +deregister: + issei_deregister(idev); + return err; +} + +static void issei_heci_shutdown(struct pci_dev *pdev) +{ + struct issei_device *idev =3D pci_get_drvdata(pdev); + struct issei_heci_hw *hw =3D to_heci_hw(idev); + + issei_stop(idev); + + idev->ops->irq_disable(idev); + free_irq(hw->irq, idev); + pci_free_irq_vectors(pdev); +} + +static void issei_heci_remove(struct pci_dev *pdev) +{ + issei_heci_shutdown(pdev); + + issei_deregister(pci_get_drvdata(pdev)); +} + +static int issei_heci_pm_suspend(struct device *device) +{ + struct issei_device *idev =3D dev_get_drvdata(device); + + issei_stop(idev); + idev->ops->irq_disable(idev); + + return 0; +} + +static int issei_heci_pm_resume(struct device *device) +{ + struct issei_device *idev =3D dev_get_drvdata(device); + + return issei_start(idev); +} + +static const struct dev_pm_ops issei_heci_pm_ops =3D { + SYSTEM_SLEEP_PM_OPS(issei_heci_pm_suspend, issei_heci_pm_resume) +}; + +static const struct pci_device_id heci_pci_tbl[] =3D { + {PCI_VDEVICE(INTEL, 0xA85D)}, /* Lunar Lake M */ + {PCI_VDEVICE(INTEL, 0xE35D)}, /* Panther Lake H */ + {PCI_VDEVICE(INTEL, 0xE45D)}, /* Panther Lake P */ + {PCI_VDEVICE(INTEL, 0xD470)}, /* Nova Lake S */ + {PCI_VDEVICE(INTEL, 0xD358)}, /* Nova Lake H */ + {PCI_VDEVICE(INTEL, 0x4D5D)}, /* Wildcat Lake */ + {} +}; +MODULE_DEVICE_TABLE(pci, heci_pci_tbl); + +static struct pci_driver issei_heci_driver =3D { + .name =3D KBUILD_MODNAME, + .id_table =3D heci_pci_tbl, + .probe =3D issei_heci_probe, + .remove =3D issei_heci_remove, + .shutdown =3D issei_heci_shutdown, + .driver =3D { + .pm =3D &issei_heci_pm_ops, + .probe_type =3D PROBE_PREFER_ASYNCHRONOUS, + }, +}; +module_pci_driver(issei_heci_driver); + +MODULE_DESCRIPTION("Intel(R) Silicon Security Engine Interface - HECI"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("INTEL_SSEI"); --=20 2.43.0