From f70da6f99d4f40c5f481c92e3b65d5e36eaa6dc9 Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Thu, 28 Aug 2025 15:24:34 +0100 Subject: drm/gpusvm: pull out drm_gpusvm_pages substructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pull the pages stuff from the svm range into its own substructure, with the idea of having the main pages related routines, like get_pages(), unmap_pages() and free_pages() all operating on some lower level structures, which can then be re-used for stuff like userptr. v2: - Move seq into pages struct (Matt B) v3: - Small kernel-doc fixes Suggested-by: Matthew Brost Signed-off-by: Matthew Auld Cc: Thomas Hellström Reviewed-by: Matthew Brost Link: https://lore.kernel.org/r/20250828142430.615826-13-matthew.auld@intel.com --- include/drm/drm_gpusvm.h | 48 +++++++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 17 deletions(-) (limited to 'include/drm') diff --git a/include/drm/drm_gpusvm.h b/include/drm/drm_gpusvm.h index 0e336148309d..1ee4188c3067 100644 --- a/include/drm/drm_gpusvm.h +++ b/include/drm/drm_gpusvm.h @@ -106,16 +106,16 @@ struct drm_gpusvm_notifier { }; /** - * struct drm_gpusvm_range_flags - Structure representing a GPU SVM range flags + * struct drm_gpusvm_pages_flags - Structure representing a GPU SVM pages flags * - * @migrate_devmem: Flag indicating whether the range can be migrated to device memory - * @unmapped: Flag indicating if the range has been unmapped - * @partial_unmap: Flag indicating if the range has been partially unmapped - * @has_devmem_pages: Flag indicating if the range has devmem pages - * @has_dma_mapping: Flag indicating if the range has a DMA mapping - * @__flags: Flags for range in u16 form (used for READ_ONCE) + * @migrate_devmem: Flag indicating whether the pages can be migrated to device memory + * @unmapped: Flag indicating if the pages has been unmapped + * @partial_unmap: Flag indicating if the pages has been partially unmapped + * @has_devmem_pages: Flag indicating if the pages has devmem pages + * @has_dma_mapping: Flag indicating if the pages has a DMA mapping + * @__flags: Flags for pages in u16 form (used for READ_ONCE) */ -struct drm_gpusvm_range_flags { +struct drm_gpusvm_pages_flags { union { struct { /* All flags below must be set upon creation */ @@ -130,6 +130,27 @@ struct drm_gpusvm_range_flags { }; }; +/** + * struct drm_gpusvm_pages - Structure representing a GPU SVM mapped pages + * + * @dma_addr: Device address array + * @dpagemap: The struct drm_pagemap of the device pages we're dma-mapping. + * Note this is assuming only one drm_pagemap per range is allowed. + * @notifier_seq: Notifier sequence number of the range's pages + * @flags: Flags for range + * @flags.migrate_devmem: Flag indicating whether the range can be migrated to device memory + * @flags.unmapped: Flag indicating if the range has been unmapped + * @flags.partial_unmap: Flag indicating if the range has been partially unmapped + * @flags.has_devmem_pages: Flag indicating if the range has devmem pages + * @flags.has_dma_mapping: Flag indicating if the range has a DMA mapping + */ +struct drm_gpusvm_pages { + struct drm_pagemap_addr *dma_addr; + struct drm_pagemap *dpagemap; + unsigned long notifier_seq; + struct drm_gpusvm_pages_flags flags; +}; + /** * struct drm_gpusvm_range - Structure representing a GPU SVM range * @@ -138,11 +159,7 @@ struct drm_gpusvm_range_flags { * @refcount: Reference count for the range * @itree: Interval tree node for the range (inserted in GPU SVM notifier) * @entry: List entry to fast interval tree traversal - * @notifier_seq: Notifier sequence number of the range's pages - * @dma_addr: Device address array - * @dpagemap: The struct drm_pagemap of the device pages we're dma-mapping. - * Note this is assuming only one drm_pagemap per range is allowed. - * @flags: Flags for range + * @pages: The pages for this range. * * This structure represents a GPU SVM range used for tracking memory ranges * mapped in a DRM device. @@ -153,10 +170,7 @@ struct drm_gpusvm_range { struct kref refcount; struct interval_tree_node itree; struct list_head entry; - unsigned long notifier_seq; - struct drm_pagemap_addr *dma_addr; - struct drm_pagemap *dpagemap; - struct drm_gpusvm_range_flags flags; + struct drm_gpusvm_pages pages; }; /** -- cgit v1.2.3 From 83f706ecbde1dfdc377bafda773fdc57644cd479 Mon Sep 17 00:00:00 2001 From: Matthew Auld Date: Thu, 28 Aug 2025 15:24:36 +0100 Subject: drm/gpusvm: export drm_gpusvm_pages API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export get/unmap/free pages API. We also need to tweak the SVM init to allow skipping much of the unneeded parts. Signed-off-by: Matthew Auld Cc: Thomas Hellström Cc: Matthew Brost Reviewed-by: Matthew Brost Link: https://lore.kernel.org/r/20250828142430.615826-15-matthew.auld@intel.com --- drivers/gpu/drm/drm_gpusvm.c | 66 ++++++++++++++++++++++++++++++++++---------- include/drm/drm_gpusvm.h | 16 +++++++++++ 2 files changed, 67 insertions(+), 15 deletions(-) (limited to 'include/drm') diff --git a/drivers/gpu/drm/drm_gpusvm.c b/drivers/gpu/drm/drm_gpusvm.c index eb50ed0d0f55..eeeeb99cfdf6 100644 --- a/drivers/gpu/drm/drm_gpusvm.c +++ b/drivers/gpu/drm/drm_gpusvm.c @@ -373,6 +373,12 @@ static const struct mmu_interval_notifier_ops drm_gpusvm_notifier_ops = { * * This function initializes the GPU SVM. * + * Note: If only using the simple drm_gpusvm_pages API (get/unmap/free), + * then only @gpusvm, @name, and @drm are expected. However, the same base + * @gpusvm can also be used with both modes together in which case the full + * setup is needed, where the core drm_gpusvm_pages API will simply never use + * the other fields. + * * Return: 0 on success, a negative error code on failure. */ int drm_gpusvm_init(struct drm_gpusvm *gpusvm, @@ -383,8 +389,16 @@ int drm_gpusvm_init(struct drm_gpusvm *gpusvm, const struct drm_gpusvm_ops *ops, const unsigned long *chunk_sizes, int num_chunks) { - if (!ops->invalidate || !num_chunks) - return -EINVAL; + if (mm) { + if (!ops->invalidate || !num_chunks) + return -EINVAL; + mmgrab(mm); + } else { + /* No full SVM mode, only core drm_gpusvm_pages API. */ + if (ops || num_chunks || mm_range || notifier_size || + device_private_page_owner) + return -EINVAL; + } gpusvm->name = name; gpusvm->drm = drm; @@ -397,7 +411,6 @@ int drm_gpusvm_init(struct drm_gpusvm *gpusvm, gpusvm->chunk_sizes = chunk_sizes; gpusvm->num_chunks = num_chunks; - mmgrab(mm); gpusvm->root = RB_ROOT_CACHED; INIT_LIST_HEAD(&gpusvm->notifier_list); @@ -489,7 +502,8 @@ void drm_gpusvm_fini(struct drm_gpusvm *gpusvm) drm_gpusvm_range_remove(gpusvm, range); } - mmdrop(gpusvm->mm); + if (gpusvm->mm) + mmdrop(gpusvm->mm); WARN_ON(!RB_EMPTY_ROOT(&gpusvm->root.rb_root)); } EXPORT_SYMBOL_GPL(drm_gpusvm_fini); @@ -1044,6 +1058,27 @@ static void __drm_gpusvm_free_pages(struct drm_gpusvm *gpusvm, } } +/** + * drm_gpusvm_free_pages() - Free dma-mapping associated with GPU SVM pages + * struct + * @gpusvm: Pointer to the GPU SVM structure + * @svm_pages: Pointer to the GPU SVM pages structure + * @npages: Number of mapped pages + * + * This function unmaps and frees the dma address array associated with a GPU + * SVM pages struct. + */ +void drm_gpusvm_free_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_pages *svm_pages, + unsigned long npages) +{ + drm_gpusvm_notifier_lock(gpusvm); + __drm_gpusvm_unmap_pages(gpusvm, svm_pages, npages); + __drm_gpusvm_free_pages(gpusvm, svm_pages); + drm_gpusvm_notifier_unlock(gpusvm); +} +EXPORT_SYMBOL_GPL(drm_gpusvm_free_pages); + /** * drm_gpusvm_range_remove() - Remove GPU SVM range * @gpusvm: Pointer to the GPU SVM structure @@ -1220,13 +1255,12 @@ static bool drm_gpusvm_pages_valid_unlocked(struct drm_gpusvm *gpusvm, * * Return: 0 on success, negative error code on failure. */ -static int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, - struct drm_gpusvm_pages *svm_pages, - struct mm_struct *mm, - struct mmu_interval_notifier *notifier, - unsigned long pages_start, - unsigned long pages_end, - const struct drm_gpusvm_ctx *ctx) +int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_pages *svm_pages, + struct mm_struct *mm, + struct mmu_interval_notifier *notifier, + unsigned long pages_start, unsigned long pages_end, + const struct drm_gpusvm_ctx *ctx) { struct hmm_range hmm_range = { .default_flags = HMM_PFN_REQ_FAULT | (ctx->read_only ? 0 : @@ -1415,6 +1449,7 @@ err_free: goto retry; return err; } +EXPORT_SYMBOL_GPL(drm_gpusvm_get_pages); /** * drm_gpusvm_range_get_pages() - Get pages for a GPU SVM range @@ -1451,10 +1486,10 @@ EXPORT_SYMBOL_GPL(drm_gpusvm_range_get_pages); * Must be called in the invalidate() callback of the corresponding notifier for * IOMMU security model. */ -static void drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm, - struct drm_gpusvm_pages *svm_pages, - unsigned long npages, - const struct drm_gpusvm_ctx *ctx) +void drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_pages *svm_pages, + unsigned long npages, + const struct drm_gpusvm_ctx *ctx) { if (ctx->in_notifier) lockdep_assert_held_write(&gpusvm->notifier_lock); @@ -1466,6 +1501,7 @@ static void drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm, if (!ctx->in_notifier) drm_gpusvm_notifier_unlock(gpusvm); } +EXPORT_SYMBOL_GPL(drm_gpusvm_unmap_pages); /** * drm_gpusvm_range_unmap_pages() - Unmap pages associated with a GPU SVM range diff --git a/include/drm/drm_gpusvm.h b/include/drm/drm_gpusvm.h index 1ee4188c3067..5434048a2ca4 100644 --- a/include/drm/drm_gpusvm.h +++ b/include/drm/drm_gpusvm.h @@ -307,6 +307,22 @@ drm_gpusvm_range_find(struct drm_gpusvm_notifier *notifier, unsigned long start, void drm_gpusvm_range_set_unmapped(struct drm_gpusvm_range *range, const struct mmu_notifier_range *mmu_range); +int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_pages *svm_pages, + struct mm_struct *mm, + struct mmu_interval_notifier *notifier, + unsigned long pages_start, unsigned long pages_end, + const struct drm_gpusvm_ctx *ctx); + +void drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_pages *svm_pages, + unsigned long npages, + const struct drm_gpusvm_ctx *ctx); + +void drm_gpusvm_free_pages(struct drm_gpusvm *gpusvm, + struct drm_gpusvm_pages *svm_pages, + unsigned long npages); + #ifdef CONFIG_LOCKDEP /** * drm_gpusvm_driver_set_lock() - Set the lock protecting accesses to GPU SVM -- cgit v1.2.3 From 741eeabb7c78c555c4c8e39df91b2b8e8d6f5ec6 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Fri, 5 Sep 2025 21:19:46 +0530 Subject: mei: late_bind: add late binding component driver 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. Cc: Badal Nilawar Reviewed-by: Mika Westerberg Signed-off-by: Badal Nilawar Reviewed-by: Anshuman Gupta Signed-off-by: Rodrigo Vivi Signed-off-by: Alexander Usyskin Reviewed-by: Lucas De Marchi Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20250905154953.3974335-3-badal.nilawar@intel.com Signed-off-by: Lucas De Marchi --- drivers/misc/mei/Kconfig | 13 ++ drivers/misc/mei/Makefile | 1 + drivers/misc/mei/mei_lb.c | 312 +++++++++++++++++++++++++++++ include/drm/intel/i915_component.h | 1 + include/drm/intel/intel_lb_mei_interface.h | 70 +++++++ 5 files changed, 397 insertions(+) create mode 100644 drivers/misc/mei/mei_lb.c create mode 100644 include/drm/intel/intel_lb_mei_interface.h (limited to 'include/drm') 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..77686b108d3c --- /dev/null +++ b/drivers/misc/mei/mei_lb.c @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 bool mei_lb_check_response(const struct device *dev, ssize_t bytes, + struct mei_lb_rsp *rsp) +{ + /* + * Received message size may be smaller than the full message size when + * reply contains only MKHI header with result field set to the error code. + * Check the header size and content first to output exact error, if needed, + * and then process to the whole message. + */ + if (bytes < sizeof(rsp->header)) { + dev_err(dev, "Received less than header size from the firmware: %zd < %zu\n", + bytes, sizeof(rsp->header)); + return false; + } + if (rsp->header.group_id != MKHI_GROUP_ID_GFX) { + dev_err(dev, "Mismatch group id: 0x%x instead of 0x%x\n", + rsp->header.group_id, MKHI_GROUP_ID_GFX); + return false; + } + if (rsp->header.command != INTEL_LB_RSP) { + dev_err(dev, "Mismatch command: 0x%x instead of 0x%x\n", + rsp->header.command, INTEL_LB_RSP); + return false; + } + if (rsp->header.result) { + dev_err(dev, "Error in result: 0x%x\n", rsp->header.result); + return false; + } + if (bytes < sizeof(*rsp)) { + dev_err(dev, "Received less than message size from the firmware: %zd < %zu\n", + bytes, sizeof(*rsp)); + return false; + } + + return true; +} + +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, "Failed to enable firmware client. %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, (u8 *)req, req_size, + INTEL_LB_SEND_TIMEOUT_MSEC); + if (bytes < 0) { + dev_err(dev, "Failed to send late binding request to firmware. %zd\n", bytes); + ret = bytes; + goto end; + } + + bytes = mei_cldev_recv_timeout(cldev, (u8 *)&rsp, sizeof(rsp), + INTEL_LB_RECV_TIMEOUT_MSEC); + if (bytes < 0) { + dev_err(dev, "Failed to receive late binding reply from MEI firmware. %zd\n", + bytes); + ret = bytes; + goto end; + } + if (!mei_lb_check_response(dev, bytes, &rsp)) { + dev_err(dev, "Bad response from the firmware. header: %02x %02x %02x %02x\n", + rsp.header.group_id, rsp.header.command, + rsp.header.reserved, rsp.header.result); + ret = -EPROTO; + goto end; + } + + dev_dbg(dev, "status = %u\n", le32_to_cpu(rsp.status)); + ret = (int)le32_to_cpu(rsp.status); +end: + mei_cldev_disable(cldev); + kfree(req); + return ret; +} + +static const struct intel_lb_component_ops mei_lb_ops = { + .push_payload = mei_lb_push_payload, +}; + +static int mei_lb_component_master_bind(struct device *dev) +{ + return component_bind_all(dev, (void *)&mei_lb_ops); +} + +static void mei_lb_component_master_unbind(struct device *dev) +{ + component_unbind_all(dev, (void *)&mei_lb_ops); +} + +static const struct component_master_ops mei_lb_component_master_ops = { + .bind = mei_lb_component_master_bind, + .unbind = mei_lb_component_master_unbind, +}; + +static int mei_lb_component_match(struct device *dev, int subcomponent, + void *data) +{ + /* + * This function checks if requester is Intel %PCI_CLASS_DISPLAY_VGA or + * %PCI_CLASS_DISPLAY_OTHER device, and checks if the requester is the + * grand parent of mei_if i.e. late bind MEI device + */ + struct device *base = data; + struct pci_dev *pdev; + + if (!dev) + return 0; + + if (!dev_is_pci(dev)) + return 0; + + pdev = to_pci_dev(dev); + + if (pdev->vendor != PCI_VENDOR_ID_INTEL) + return 0; + + if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8) && + pdev->class != (PCI_CLASS_DISPLAY_OTHER << 8)) + return 0; + + if (subcomponent != INTEL_COMPONENT_LB) + return 0; + + base = base->parent; + if (!base) /* mei device */ + return 0; + + base = base->parent; /* pci device */ + + return !!base && dev == base; +} + +static int mei_lb_probe(struct mei_cl_device *cldev, + const struct mei_cl_device_id *id) +{ + struct component_match *master_match = NULL; + int ret; + + component_match_add_typed(&cldev->dev, &master_match, + mei_lb_component_match, &cldev->dev); + if (IS_ERR_OR_NULL(master_match)) + return -ENOMEM; + + ret = component_master_add_with_match(&cldev->dev, + &mei_lb_component_master_ops, + master_match); + if (ret < 0) + dev_err(&cldev->dev, "Failed to add late binding master component. %d\n", ret); + + return ret; +} + +static void mei_lb_remove(struct mei_cl_device *cldev) +{ + component_master_del(&cldev->dev, &mei_lb_component_master_ops); +} + +#define MEI_GUID_MKHI UUID_LE(0xe2c2afa2, 0x3817, 0x4d19, \ + 0x9d, 0x95, 0x6, 0xb1, 0x6b, 0x58, 0x8a, 0x5d) + +static const struct mei_cl_device_id mei_lb_tbl[] = { + { .uuid = MEI_GUID_MKHI, .version = MEI_CL_VERSION_ANY }, + { } +}; +MODULE_DEVICE_TABLE(mei, mei_lb_tbl); + +static struct mei_cl_driver mei_lb_driver = { + .id_table = mei_lb_tbl, + .name = "mei_lb", + .probe = mei_lb_probe, + .remove = mei_lb_remove, +}; + +module_mei_cl_driver(mei_lb_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MEI Late Binding Firmware Update/Upload"); diff --git a/include/drm/intel/i915_component.h b/include/drm/intel/i915_component.h index 4ea3b17aa143..8082db222e00 100644 --- a/include/drm/intel/i915_component.h +++ b/include/drm/intel/i915_component.h @@ -31,6 +31,7 @@ enum i915_component_type { I915_COMPONENT_HDCP, I915_COMPONENT_PXP, I915_COMPONENT_GSC_PROXY, + INTEL_COMPONENT_LB, }; /* MAX_PORT is the number of port diff --git a/include/drm/intel/intel_lb_mei_interface.h b/include/drm/intel/intel_lb_mei_interface.h new file mode 100644 index 000000000000..d65be2cba2ab --- /dev/null +++ b/include/drm/intel/intel_lb_mei_interface.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2025 Intel Corporation + */ + +#ifndef _INTEL_LB_MEI_INTERFACE_H_ +#define _INTEL_LB_MEI_INTERFACE_H_ + +#include + +struct device; + +/** + * define INTEL_LB_FLAG_IS_PERSISTENT - Mark the payload as persistent + * + * This flag indicates that the late binding payload should be stored + * persistently in flash across warm resets. + */ +#define INTEL_LB_FLAG_IS_PERSISTENT BIT(0) + +/** + * enum intel_lb_type - enum to determine late binding payload type + * @INTEL_LB_TYPE_FAN_CONTROL: Fan controller configuration + */ +enum intel_lb_type { + INTEL_LB_TYPE_FAN_CONTROL = 1, +}; + +/** + * enum intel_lb_status - Status codes returned on late binding transmissions + * @INTEL_LB_STATUS_SUCCESS: Operation completed successfully + * @INTEL_LB_STATUS_4ID_MISMATCH: Mismatch in the expected 4ID (firmware identity/token) + * @INTEL_LB_STATUS_ARB_FAILURE: Arbitration failure (e.g. conflicting access or state) + * @INTEL_LB_STATUS_GENERAL_ERROR: General firmware error not covered by other codes + * @INTEL_LB_STATUS_INVALID_PARAMS: One or more input parameters are invalid + * @INTEL_LB_STATUS_INVALID_SIGNATURE: Payload has an invalid or untrusted signature + * @INTEL_LB_STATUS_INVALID_PAYLOAD: Payload contents are not accepted by firmware + * @INTEL_LB_STATUS_TIMEOUT: Operation timed out before completion + */ +enum intel_lb_status { + INTEL_LB_STATUS_SUCCESS = 0, + INTEL_LB_STATUS_4ID_MISMATCH = 1, + INTEL_LB_STATUS_ARB_FAILURE = 2, + INTEL_LB_STATUS_GENERAL_ERROR = 3, + INTEL_LB_STATUS_INVALID_PARAMS = 4, + INTEL_LB_STATUS_INVALID_SIGNATURE = 5, + INTEL_LB_STATUS_INVALID_PAYLOAD = 6, + INTEL_LB_STATUS_TIMEOUT = 7, +}; + +/** + * struct intel_lb_component_ops - Ops for late binding services + */ +struct intel_lb_component_ops { + /** + * push_payload - Sends a payload to the authentication firmware + * @dev: Device struct corresponding to the mei device + * @type: Payload type (see &enum intel_lb_type) + * @flags: Payload flags bitmap (e.g. %INTEL_LB_FLAGS_IS_PERSISTENT) + * @payload: Pointer to payload buffer + * @payload_size: Payload buffer size in bytes + * + * Return: 0 success, negative errno value on transport failure, + * positive status returned by firmware + */ + int (*push_payload)(struct device *dev, u32 type, u32 flags, + const void *payload, size_t payload_size); +}; + +#endif /* _INTEL_LB_MEI_INTERFACE_H_ */ -- cgit v1.2.3