[PATCH 2/9] mei: late_bind: add late binding component driver

Greg KH gregkh at linuxfoundation.org
Wed Jul 16 11:48:38 UTC 2025


On Thu, Jul 10, 2025 at 11:08:33AM -0400, Rodrigo Vivi wrote:
> From: Alexander Usyskin <alexander.usyskin at intel.com>
> 
> Introduce a new MEI client driver to support Late Binding firmware
> upload/update for Intel discrete graphics platforms.
> 
> Late Binding is a runtime firmware upload/update mechanism that allows
> payloads, such as fan control and voltage regulator, to be securely
> delivered and applied without requiring SPI flash updates or
> system reboots. This driver enables the Xe graphics driver and other
> user-space tools to push such firmware blobs to the authentication
> firmware via the MEI interface.
> 
> The driver handles authentication, versioning, and communication
> with the authentication firmware, which in turn coordinates with
> the PUnit/PCODE to apply the payload.
> 
> This is a foundational component for enabling dynamic, secure,
> and re-entrant configuration updates on platforms like Battlemage.
> 
> Signed-off-by: Alexander Usyskin <alexander.usyskin at intel.com>
> Signed-off-by: Badal Nilawar <badal.nilawar at intel.com>
> Reviewed-by: Anshuman Gupta <anshuman.gupta at intel.com>
> Signed-off-by: Rodrigo Vivi <rodrigo.vivi at intel.com>
> ---
> 
> Changes in this revision:
> - Proper commit message
> - Proper explanation of 'Late Binding' on Kconfig help and doc
> - Consistency in naming:
>   + mei_ prefix where it makes sense
>   + use 'lb' for short of 'Late Binding' instead of 'late_bind'
>     Including s/CONFIG_INTEL_MEI_LATE_BIND/CONFIG_INTEL_MEI_LB
>   + remove stray 'struct module'
>   + Fix structs and enum documentation style and fields
>   + Remove 'CSC' to avoid yet another acronym. 'Authentication firmware' it is.
>   + specify size unit
>   + s/push_config/push_payload
> 
>  drivers/misc/mei/Kconfig                   |  13 +
>  drivers/misc/mei/Makefile                  |   1 +
>  drivers/misc/mei/mei_lb.c                  | 315 +++++++++++++++++++++
>  include/drm/intel/i915_component.h         |   1 +
>  include/drm/intel/intel_lb_mei_interface.h |  70 +++++
>  5 files changed, 400 insertions(+)
>  create mode 100644 drivers/misc/mei/mei_lb.c
>  create mode 100644 include/drm/intel/intel_lb_mei_interface.h
> 
> diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig
> index 7575fee96cc6..f8b04e49e4ba 100644
> --- a/drivers/misc/mei/Kconfig
> +++ b/drivers/misc/mei/Kconfig
> @@ -81,6 +81,19 @@ config INTEL_MEI_VSC
>  	  This driver can also be built as a module. If so, the module
>  	  will be called mei-vsc.
>  
> +config INTEL_MEI_LB
> +	tristate "Intel Late Binding (LB) support on ME Interface"
> +	depends on INTEL_MEI_ME
> +	depends on DRM_XE
> +	help
> +	  Enable support for Intel Late Binding (LB) via the MEI interface.
> +
> +	  Late Binding is a method for applying firmware updates at runtime,
> +	  allowing the Intel Xe driver to load firmware payloads such as
> +	  fan controller or voltage regulator. These firmware updates are
> +	  authenticated and versioned, and do not require firmware flashing
> +	  or system reboot.
> +
>  source "drivers/misc/mei/hdcp/Kconfig"
>  source "drivers/misc/mei/pxp/Kconfig"
>  source "drivers/misc/mei/gsc_proxy/Kconfig"
> diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile
> index 6f9fdbf1a495..a203ed766b33 100644
> --- a/drivers/misc/mei/Makefile
> +++ b/drivers/misc/mei/Makefile
> @@ -31,6 +31,7 @@ CFLAGS_mei-trace.o = -I$(src)
>  obj-$(CONFIG_INTEL_MEI_HDCP) += hdcp/
>  obj-$(CONFIG_INTEL_MEI_PXP) += pxp/
>  obj-$(CONFIG_INTEL_MEI_GSC_PROXY) += gsc_proxy/
> +obj-$(CONFIG_INTEL_MEI_LB) += mei_lb.o
>  
>  obj-$(CONFIG_INTEL_MEI_VSC_HW) += mei-vsc-hw.o
>  mei-vsc-hw-y := vsc-tp.o
> diff --git a/drivers/misc/mei/mei_lb.c b/drivers/misc/mei/mei_lb.c
> new file mode 100644
> index 000000000000..fddef862712d
> --- /dev/null
> +++ b/drivers/misc/mei/mei_lb.c
> @@ -0,0 +1,315 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2025 Intel Corporation
> + */
> +#include <drm/intel/i915_component.h>
> +#include <drm/intel/intel_lb_mei_interface.h>
> +#include <linux/component.h>
> +#include <linux/pci.h>
> +#include <linux/mei_cl_bus.h>
> +#include <linux/module.h>
> +#include <linux/overflow.h>
> +#include <linux/slab.h>
> +#include <linux/uuid.h>
> +
> +#include "mkhi.h"
> +
> +/**
> + * DOC: Late Binding Firmware Update/Upload
> + *
> + * Late Binding is a firmware update/upload mechanism that allows configuration
> + * payloads to be securely delivered and applied at runtime, rather than
> + * being embedded in the system firmware image (e.g., IFWI or SPI flash).
> + *
> + * This mechanism is used to update device-level configuration such as:
> + * - Fan controller
> + * - Voltage regulator (VR)
> + *
> + * Key Characteristics:
> + * ---------------------
> + * - Runtime Delivery:
> + *   Firmware blobs are loaded by the host driver (e.g., Xe KMD)
> + *   after the GPU or SoC has booted.
> + *
> + * - Secure and Authenticated:
> + *   All payloads are signed and verified by the authentication firmware.
> + *
> + * - No Firmware Flashing Required:
> + *   Updates are applied in volatile memory and do not require SPI flash
> + *   modification or system reboot.
> + *
> + * - Re-entrant:
> + *   Multiple updates of the same or different types can be applied
> + *   sequentially within a single boot session.
> + *
> + * - Version Controlled:
> + *   Each payload includes version and security version number (SVN)
> + *   metadata to support anti-rollback enforcement.
> + *
> + * Upload Flow:
> + * ------------
> + * 1. Host driver (KMD or user-space tool) loads the late binding firmware.
> + * 2. Firmware is passed to the MEI interface and forwarded to
> + *    authentication firmware.
> + * 3. Authentication firmware authenticates the payload and extracts
> + *    command and data arrays.
> + * 4. Authentication firmware delivers the configuration to PUnit/PCODE.
> + * 5. Status is returned back to the host via MEI.
> + */
> +
> +#define INTEL_LB_CMD 0x12
> +#define INTEL_LB_RSP (INTEL_LB_CMD | 0x80)
> +
> +#define INTEL_LB_SEND_TIMEOUT_MSEC 3000
> +#define INTEL_LB_RECV_TIMEOUT_MSEC 3000
> +
> +/**
> + * struct mei_lb_req - Late Binding request structure
> + * @header: MKHI message header (see struct mkhi_msg_hdr)
> + * @type: Type of the Late Binding payload
> + * @flags: Flags to be passed to the authentication firmware (e.g. %INTEL_LB_FLAGS_IS_PERSISTENT)
> + * @reserved: Reserved for future use by authentication firmware, must be set to 0
> + * @payload_size: Size of the payload data in bytes
> + * @payload: Payload data to be sent to the authentication firmware
> + */
> +struct mei_lb_req {
> +	struct mkhi_msg_hdr header;
> +	__le32 type;
> +	__le32 flags;
> +	__le32 reserved[2];
> +	__le32 payload_size;
> +	u8  payload[] __counted_by(payload_size);
> +} __packed;
> +
> +/**
> + * struct mei_lb_rsp - Late Binding response structure
> + * @header: MKHI message header (see struct mkhi_msg_hdr)
> + * @type: Type of the Late Binding payload
> + * @reserved: Reserved for future use by authentication firmware, must be set to 0
> + * @status: Status returned by authentication firmware (see enum intel_lb_status)
> + */
> +struct mei_lb_rsp {
> +	struct mkhi_msg_hdr header;
> +	__le32 type;
> +	__le32 reserved[2];
> +	__le32 status;
> +} __packed;
> +
> +static int mei_lb_check_response(const struct device *dev, const struct mkhi_msg_hdr *hdr)
> +{
> +	if (hdr->group_id != MKHI_GROUP_ID_GFX) {
> +		dev_err(dev, "Mismatch group id: 0x%x instead of 0x%x\n",
> +			hdr->group_id, MKHI_GROUP_ID_GFX);
> +		return -EINVAL;
> +	}
> +
> +	if (hdr->command != INTEL_LB_RSP) {
> +		dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n",
> +			hdr->command, INTEL_LB_RSP);
> +		return -EINVAL;
> +	}
> +
> +	if (hdr->result) {
> +		dev_err(dev, "Error in result: 0x%x\n", hdr->result);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mei_lb_push_payload(struct device *dev,
> +			       enum intel_lb_type type, u32 flags,
> +			       const void *payload, size_t payload_size)
> +{
> +	struct mei_cl_device *cldev;
> +	struct mei_lb_req *req = NULL;
> +	struct mei_lb_rsp rsp;
> +	size_t req_size;
> +	ssize_t bytes;
> +	int ret;
> +
> +	cldev = to_mei_cl_device(dev);
> +
> +	ret = mei_cldev_enable(cldev);
> +	if (ret) {
> +		dev_dbg(dev, "mei_cldev_enable failed. %d\n", ret);
> +		return ret;
> +	}
> +
> +	req_size = struct_size(req, payload, payload_size);
> +	if (req_size > mei_cldev_mtu(cldev)) {
> +		dev_err(dev, "Payload is too big %zu\n", payload_size);
> +		ret = -EMSGSIZE;
> +		goto end;
> +	}
> +
> +	req = kmalloc(req_size, GFP_KERNEL);
> +	if (!req) {
> +		ret = -ENOMEM;
> +		goto end;
> +	}
> +
> +	req->header.group_id = MKHI_GROUP_ID_GFX;
> +	req->header.command = INTEL_LB_CMD;
> +	req->type = cpu_to_le32(type);
> +	req->flags = cpu_to_le32(flags);
> +	req->reserved[0] = 0;
> +	req->reserved[1] = 0;
> +	req->payload_size = cpu_to_le32(payload_size);
> +	memcpy(req->payload, payload, payload_size);
> +
> +	bytes = mei_cldev_send_timeout(cldev,
> +				       (void *)req, req_size, INTEL_LB_SEND_TIMEOUT_MSEC);
> +	if (bytes < 0) {
> +		dev_err(dev, "mei_cldev_send failed. %zd\n", bytes);
> +		ret = bytes;
> +		goto end;
> +	}
> +
> +	bytes = mei_cldev_recv_timeout(cldev,
> +				       (void *)&rsp, sizeof(rsp), INTEL_LB_RECV_TIMEOUT_MSEC);
> +	if (bytes < 0) {
> +		dev_err(dev, "mei_cldev_recv failed. %zd\n", bytes);
> +		ret = bytes;
> +		goto end;
> +	}
> +	if (bytes < sizeof(rsp.header)) {
> +		dev_err(dev, "bad response header from the firmware: size %zd < %zu\n",
> +			bytes, sizeof(rsp.header));
> +		ret = -EPROTO;
> +		goto end;
> +	}
> +	if (mei_lb_check_response(dev, &rsp.header)) {
> +		dev_err(dev, "bad result response from the firmware: 0x%x\n",
> +			*(uint32_t *)&rsp.header);

What exactly are you printing out to userspace here?  A pointer?  Or a
random value from the firmware?  Why?

> +		ret = -EPROTO;
> +		goto end;
> +	}

You forgot to check the type and reserved fields of the rsp structure :(

> +	if (bytes < sizeof(rsp)) {
> +		dev_err(dev, "bad response from the firmware: size %zd < %zu\n",
> +			bytes, sizeof(rsp));
> +		ret = -EPROTO;
> +		goto end;
> +	}

Why not check this above when you check against the size of the header?
You only need one size check, not 2.

thanks,

greg k-h


More information about the dri-devel mailing list