[PATCH v2 1/2] drm/xe/gsc: Initialize GSC proxy

Daniele Ceraolo Spurio daniele.ceraolospurio at intel.com
Tue Jan 16 16:53:25 UTC 2024



On 1/15/2024 10:13 PM, Kandpal, Suraj wrote:
> I was under the impression that this patch series will introduce a function which
> Checks the running status of the firmware is it not so or is that going to be a part
> Another series.

The checkers should be all there, they're just not exposed outside of 
the gsc_proxy file because there is no user for them at the moment. Note 
that a big difference is that here we expect the caller to handle the PM 
refs (Xe in general is a lot more explicit about the PM handling).

>
>> Subject: [PATCH v2 1/2] drm/xe/gsc: Initialize GSC proxy
>>
>> The GSC uC needs to communicate with the CSME to perform certain
>> operations. Since the GSC can't perform this communication directly on
>> platforms where it is integrated in GT, the graphics driver needs to transfer the
>> messages from GSC to CSME and back. The proxy flow must be manually
>> started after the GSC is loaded to signal to GSC that we're ready to handle its
>> messages and allow it to query its init data from CSME.
>>
>> Note that the component must be removed before the pci_remove call
>> completes, so we can't use a drmm helper for it and we need to instead
> Also a typo here *drm helper

not a typo, the functions have 2 "m" in their names ;)

Daniele

>
> Regards,
> Suraj Kandpal
>> perform the cleanup as part of the removal flow.
>>
>> v2: add function documentation, more targeted memory clear, clearer logs
>> and variable names (Alan)
>>
>> Signed-off-by: Daniele Ceraolo Spurio <daniele.ceraolospurio at intel.com>
>> Cc: Alan Previn <alan.previn.teres.alexis at intel.com>
>> Cc: Suraj Kandpal <suraj.kandpal at intel.com>
>> ---
>>   drivers/gpu/drm/xe/Makefile                   |   1 +
>>   .../gpu/drm/xe/abi/gsc_proxy_commands_abi.h   |  44 ++
>>   drivers/gpu/drm/xe/xe_device.c                |  22 +-
>>   drivers/gpu/drm/xe/xe_gsc.c                   |  52 +-
>>   drivers/gpu/drm/xe/xe_gsc.h                   |   1 +
>>   drivers/gpu/drm/xe/xe_gsc_proxy.c             | 468 ++++++++++++++++++
>>   drivers/gpu/drm/xe/xe_gsc_proxy.h             |  17 +
>>   drivers/gpu/drm/xe/xe_gsc_submit.c            |  13 +
>>   drivers/gpu/drm/xe/xe_gsc_submit.h            |   1 +
>>   drivers/gpu/drm/xe/xe_gsc_types.h             |  23 +
>>   drivers/gpu/drm/xe/xe_gt.c                    |  13 +
>>   drivers/gpu/drm/xe/xe_gt.h                    |   1 +
>>   drivers/gpu/drm/xe/xe_uc.c                    |  14 +
>>   drivers/gpu/drm/xe/xe_uc.h                    |   1 +
>>   14 files changed, 658 insertions(+), 13 deletions(-)  create mode 100644
>> drivers/gpu/drm/xe/abi/gsc_proxy_commands_abi.h
>>   create mode 100644 drivers/gpu/drm/xe/xe_gsc_proxy.c  create mode
>> 100644 drivers/gpu/drm/xe/xe_gsc_proxy.h
>>
>> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index
>> 6952da8979ea..d8dd43bb34b0 100644
>> --- a/drivers/gpu/drm/xe/Makefile
>> +++ b/drivers/gpu/drm/xe/Makefile
>> @@ -77,6 +77,7 @@ xe-y += xe_bb.o \
>>   	xe_ggtt.o \
>>   	xe_gpu_scheduler.o \
>>   	xe_gsc.o \
>> +	xe_gsc_proxy.o \
>>   	xe_gsc_submit.o \
>>   	xe_gt.o \
>>   	xe_gt_ccs_mode.o \
>> diff --git a/drivers/gpu/drm/xe/abi/gsc_proxy_commands_abi.h
>> b/drivers/gpu/drm/xe/abi/gsc_proxy_commands_abi.h
>> new file mode 100644
>> index 000000000000..80bbf06a3eb8
>> --- /dev/null
>> +++ b/drivers/gpu/drm/xe/abi/gsc_proxy_commands_abi.h
>> @@ -0,0 +1,44 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2023 Intel Corporation
>> + */
>> +
>> +#ifndef _ABI_GSC_PROXY_COMMANDS_ABI_H
>> +#define _ABI_GSC_PROXY_COMMANDS_ABI_H
>> +
>> +#include <linux/types.h>
>> +
>> +/* Heci client ID for proxy commands */ #define HECI_MEADDRESS_PROXY 10
>> +
>> +/* FW-defined proxy header */
>> +struct xe_gsc_proxy_header {
>> +	/*
>> +	 * hdr:
>> +	 * Bits 0-7: type of the proxy message (see enum xe_gsc_proxy_type)
>> +	 * Bits 8-15: rsvd
>> +	 * Bits 16-31: length in bytes of the payload following the proxy
>> header
>> +	 */
>> +	u32 hdr;
>> +#define GSC_PROXY_TYPE		 GENMASK(7, 0)
>> +#define GSC_PROXY_PAYLOAD_LENGTH GENMASK(31, 16)
>> +
>> +	u32 source;		/* Source of the Proxy message */
>> +	u32 destination;	/* Destination of the Proxy message */
>> +#define GSC_PROXY_ADDRESSING_KMD  0x10000 #define
>> +GSC_PROXY_ADDRESSING_GSC  0x20000 #define
>> GSC_PROXY_ADDRESSING_CSME
>> +0x30000
>> +
>> +	u32 status;		/* Command status */
>> +} __packed;
>> +
>> +/* FW-defined proxy types */
>> +enum xe_gsc_proxy_type {
>> +	GSC_PROXY_MSG_TYPE_PROXY_INVALID = 0,
>> +	GSC_PROXY_MSG_TYPE_PROXY_QUERY = 1,
>> +	GSC_PROXY_MSG_TYPE_PROXY_PAYLOAD = 2,
>> +	GSC_PROXY_MSG_TYPE_PROXY_END = 3,
>> +	GSC_PROXY_MSG_TYPE_PROXY_NOTIFICATION = 4, };
>> +
>> +#endif
>> diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
>> index 004e65544e8d..7e1d39488472 100644
>> --- a/drivers/gpu/drm/xe/xe_device.c
>> +++ b/drivers/gpu/drm/xe/xe_device.c
>> @@ -26,6 +26,7 @@
>>   #include "xe_exec_queue.h"
>>   #include "xe_exec.h"
>>   #include "xe_ggtt.h"
>> +#include "xe_gsc_proxy.h"
>>   #include "xe_gt.h"
>>   #include "xe_gt_mcr.h"
>>   #include "xe_irq.h"
>> @@ -434,6 +435,7 @@ int xe_device_probe(struct xe_device *xe)
>>   	struct xe_tile *tile;
>>   	struct xe_gt *gt;
>>   	int err;
>> +	u8 last_gt;
>>   	u8 id;
>>
>>   	xe_pat_init_early(xe);
>> @@ -521,16 +523,18 @@ int xe_device_probe(struct xe_device *xe)
>>   		goto err_irq_shutdown;
>>
>>   	for_each_gt(gt, xe, id) {
>> +		last_gt = id;
>> +
>>   		err = xe_gt_init(gt);
>>   		if (err)
>> -			goto err_irq_shutdown;
>> +			goto err_fini_gt;
>>   	}
>>
>>   	xe_heci_gsc_init(xe);
>>
>>   	err = xe_display_init(xe);
>>   	if (err)
>> -		goto err_irq_shutdown;
>> +		goto err_fini_gt;
>>
>>   	err = drm_dev_register(&xe->drm, 0);
>>   	if (err)
>> @@ -551,6 +555,14 @@ int xe_device_probe(struct xe_device *xe)
>>   err_fini_display:
>>   	xe_display_driver_remove(xe);
>>
>> +err_fini_gt:
>> +	for_each_gt(gt, xe, id) {
>> +		if (id < last_gt)
>> +			xe_gt_remove(gt);
>> +		else
>> +			break;
>> +	}
>> +
>>   err_irq_shutdown:
>>   	xe_irq_shutdown(xe);
>>   err:
>> @@ -568,12 +580,18 @@ static void xe_device_remove_display(struct
>> xe_device *xe)
>>
>>   void xe_device_remove(struct xe_device *xe)  {
>> +	struct xe_gt *gt;
>> +	u8 id;
>> +
>>   	xe_device_remove_display(xe);
>>
>>   	xe_display_fini(xe);
>>
>>   	xe_heci_gsc_fini(xe);
>>
>> +	for_each_gt(gt, xe, id)
>> +		xe_gt_remove(gt);
>> +
>>   	xe_irq_shutdown(xe);
>>   }
>>
>> diff --git a/drivers/gpu/drm/xe/xe_gsc.c b/drivers/gpu/drm/xe/xe_gsc.c index
>> a8a895cf4b44..b19e3f676a10 100644
>> --- a/drivers/gpu/drm/xe/xe_gsc.c
>> +++ b/drivers/gpu/drm/xe/xe_gsc.c
>> @@ -13,6 +13,7 @@
>>   #include "xe_bo.h"
>>   #include "xe_device.h"
>>   #include "xe_exec_queue.h"
>> +#include "xe_gsc_proxy.h"
>>   #include "xe_gsc_submit.h"
>>   #include "xe_gt.h"
>>   #include "xe_gt_printk.h"
>> @@ -242,8 +243,31 @@ static int gsc_upload(struct xe_gsc *gsc)
>>   	if (err)
>>   		return err;
>>
>> +	return 0;
>> +}
>> +
>> +static int gsc_upload_and_init(struct xe_gsc *gsc) {
>> +	struct xe_gt *gt = gsc_to_gt(gsc);
>> +	int ret;
>> +
>> +	ret = gsc_upload(gsc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	xe_uc_fw_change_status(&gsc->fw,
>> XE_UC_FIRMWARE_TRANSFERRED);
>>   	xe_gt_dbg(gt, "GSC FW async load completed\n");
>>
>> +	/* HuC auth failure is not fatal */
>> +	if (xe_huc_is_authenticated(&gt->uc.huc, XE_HUC_AUTH_VIA_GUC))
>> +		xe_huc_auth(&gt->uc.huc, XE_HUC_AUTH_VIA_GSC);
>> +
>> +	ret = xe_gsc_proxy_start(gsc);
>> +	if (ret)
>> +		return ret;
>> +
>> +	xe_gt_dbg(gt, "GSC proxy init completed\n");
>> +
>>   	return 0;
>>   }
>>
>> @@ -257,19 +281,12 @@ static void gsc_work(struct work_struct *work)
>>   	xe_device_mem_access_get(xe);
>>   	xe_force_wake_get(gt_to_fw(gt), XE_FW_GSC);
>>
>> -	ret = gsc_upload(gsc);
>> -	if (ret && ret != -EEXIST) {
>> +	ret = gsc_upload_and_init(gsc);
>> +	if (ret && ret != -EEXIST)
>>   		xe_uc_fw_change_status(&gsc->fw,
>> XE_UC_FIRMWARE_LOAD_FAIL);
>> -		goto out;
>> -	}
>> -
>> -	xe_uc_fw_change_status(&gsc->fw,
>> XE_UC_FIRMWARE_TRANSFERRED);
>> -
>> -	/* HuC auth failure is not fatal */
>> -	if (xe_huc_is_authenticated(&gt->uc.huc, XE_HUC_AUTH_VIA_GUC))
>> -		xe_huc_auth(&gt->uc.huc, XE_HUC_AUTH_VIA_GSC);
>> +	else
>> +		xe_uc_fw_change_status(&gsc->fw,
>> XE_UC_FIRMWARE_RUNNING);
>>
>> -out:
>>   	xe_force_wake_put(gt_to_fw(gt), XE_FW_GSC);
>>   	xe_device_mem_access_put(xe);
>>   }
>> @@ -302,6 +319,10 @@ int xe_gsc_init(struct xe_gsc *gsc)
>>   	else if (ret)
>>   		goto out;
>>
>> +	ret = xe_gsc_proxy_init(gsc);
>> +	if (ret && ret != -ENODEV)
>> +		goto out;
>> +
>>   	return 0;
>>
>>   out:
>> @@ -410,6 +431,15 @@ void xe_gsc_wait_for_worker_completion(struct
>> xe_gsc *gsc)
>>   		flush_work(&gsc->work);
>>   }
>>
>> +/**
>> + * xe_gsc_remove() - Clean up the GSC structures before driver removal
>> + * @gsc: the GSC uC
>> + */
>> +void xe_gsc_remove(struct xe_gsc *gsc)
>> +{
>> +	xe_gsc_proxy_remove(gsc);
>> +}
>> +
>>   /*
>>    * wa_14015076503: if the GSC FW is loaded, we need to alert it before doing
>> a
>>    * GSC engine reset by writing a notification bit in the GS1 register and then
>> diff --git a/drivers/gpu/drm/xe/xe_gsc.h b/drivers/gpu/drm/xe/xe_gsc.h index
>> bc1ef7f31ea2..c6fb32e3fd79 100644
>> --- a/drivers/gpu/drm/xe/xe_gsc.h
>> +++ b/drivers/gpu/drm/xe/xe_gsc.h
>> @@ -14,6 +14,7 @@ int xe_gsc_init(struct xe_gsc *gsc);  int
>> xe_gsc_init_post_hwconfig(struct xe_gsc *gsc);  void
>> xe_gsc_wait_for_worker_completion(struct xe_gsc *gsc);  void
>> xe_gsc_load_start(struct xe_gsc *gsc);
>> +void xe_gsc_remove(struct xe_gsc *gsc);
>>
>>   void xe_gsc_wa_14015076503(struct xe_gt *gt, bool prep);
>>
>> diff --git a/drivers/gpu/drm/xe/xe_gsc_proxy.c
>> b/drivers/gpu/drm/xe/xe_gsc_proxy.c
>> new file mode 100644
>> index 000000000000..86353c5a81cd
>> --- /dev/null
>> +++ b/drivers/gpu/drm/xe/xe_gsc_proxy.c
>> @@ -0,0 +1,468 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2023 Intel Corporation
>> + */
>> +
>> +#include "xe_gsc_proxy.h"
>> +
>> +#include <linux/component.h>
>> +#include <linux/delay.h>
>> +
>> +#include <drm/drm_managed.h>
>> +#include <drm/i915_component.h>
>> +#include <drm/i915_gsc_proxy_mei_interface.h>
>> +
>> +#include "abi/gsc_proxy_commands_abi.h"
>> +#include "regs/xe_gsc_regs.h"
>> +#include "xe_bo.h"
>> +#include "xe_gsc.h"
>> +#include "xe_gsc_submit.h"
>> +#include "xe_gt.h"
>> +#include "xe_gt_printk.h"
>> +#include "xe_map.h"
>> +#include "xe_mmio.h"
>> +
>> +/*
>> + * GSC proxy:
>> + * The GSC uC needs to communicate with the CSME to perform certain
>> operations.
>> + * Since the GSC can't perform this communication directly on platforms
>> +where it
>> + * is integrated in GT, the graphics driver needs to transfer the
>> +messages from
>> + * GSC to CSME and back. The proxy flow must be manually started after
>> +the GSC
>> + * is loaded to signal to GSC that we're ready to handle its messages
>> +and allow
>> + * it to query its init data from CSME; GSC will then trigger an HECI2
>> +interrupt
>> + * if it needs to send messages to CSME again.
>> + * The proxy flow is as follow:
>> + * 1 - Xe submits a request to GSC asking for the message to CSME
>> + * 2 - GSC replies with the proxy header + payload for CSME
>> + * 3 - Xe sends the reply from GSC as-is to CSME via the mei proxy
>> +component
>> + * 4 - CSME replies with the proxy header + payload for GSC
>> + * 5 - Xe submits a request to GSC with the reply from CSME
>> + * 6 - GSC replies either with a new header + payload (same as step 2, so we
>> + *     restart from there) or with an end message.
>> + */
>> +
>> +/*
>> + * The component should load quite quickly in most cases, but it could
>> +take
>> + * a bit. Using a very big timeout just to cover the worst case
>> +scenario  */ #define GSC_PROXY_INIT_TIMEOUT_MS 20000
>> +
>> +/* shorthand define for code compactness */ #define PROXY_HDR_SIZE
>> +(sizeof(struct xe_gsc_proxy_header))
>> +
>> +/* the protocol supports up to 32K in each direction */ #define
>> +GSC_PROXY_BUFFER_SIZE SZ_32K #define GSC_PROXY_CHANNEL_SIZE
>> +(GSC_PROXY_BUFFER_SIZE * 2)
>> +
>> +static struct xe_gt *
>> +gsc_to_gt(struct xe_gsc *gsc)
>> +{
>> +	return container_of(gsc, struct xe_gt, uc.gsc); }
>> +
>> +static inline struct xe_device *kdev_to_xe(struct device *kdev) {
>> +	return dev_get_drvdata(kdev);
>> +}
>> +
>> +static bool gsc_proxy_init_done(struct xe_gsc *gsc) {
>> +	struct xe_gt *gt = gsc_to_gt(gsc);
>> +	u32 fwsts1 = xe_mmio_read32(gt,
>> HECI_FWSTS1(MTL_GSC_HECI1_BASE));
>> +
>> +	return REG_FIELD_GET(HECI1_FWSTS1_CURRENT_STATE, fwsts1) ==
>> +	       HECI1_FWSTS1_PROXY_STATE_NORMAL; }
>> +
>> +static int proxy_send_to_csme(struct xe_gsc *gsc, u32 size) {
>> +	struct xe_gt *gt = gsc_to_gt(gsc);
>> +	struct i915_gsc_proxy_component *comp = gsc->proxy.component;
>> +	int ret;
>> +
>> +	ret = comp->ops->send(comp->mei_dev, gsc->proxy.to_csme, size);
>> +	if (ret < 0) {
>> +		xe_gt_err(gt, "Failed to send CSME proxy message\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = comp->ops->recv(comp->mei_dev, gsc->proxy.from_csme,
>> GSC_PROXY_BUFFER_SIZE);
>> +	if (ret < 0) {
>> +		xe_gt_err(gt, "Failed to receive CSME proxy message\n");
>> +		return ret;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +static int proxy_send_to_gsc(struct xe_gsc *gsc, u32 size) {
>> +	struct xe_gt *gt = gsc_to_gt(gsc);
>> +	u64 addr_in = xe_bo_ggtt_addr(gsc->proxy.bo);
>> +	u64 addr_out = addr_in + GSC_PROXY_BUFFER_SIZE;
>> +	int err;
>> +
>> +	/* the message must contain at least the gsc and proxy headers */
>> +	if (size > GSC_PROXY_BUFFER_SIZE) {
>> +		xe_gt_err(gt, "Invalid GSC proxy message size: %u\n", size);
>> +		return -EINVAL;
>> +	}
>> +
>> +	err = xe_gsc_pkt_submit_kernel(gsc, addr_in, size,
>> +				       addr_out, GSC_PROXY_BUFFER_SIZE);
>> +	if (err) {
>> +		xe_gt_err(gt, "Failed to submit gsc proxy rq (%pe)\n",
>> ERR_PTR(err));
>> +		return err;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int validate_proxy_header(struct xe_gsc_proxy_header *header,
>> +				 u32 source, u32 dest, u32 max_size) {
>> +	u32 type = FIELD_GET(GSC_PROXY_TYPE, header->hdr);
>> +	u32 length = FIELD_GET(GSC_PROXY_PAYLOAD_LENGTH, header-
>>> hdr);
>> +
>> +	if (header->destination != dest || header->source != source)
>> +		return -ENOEXEC;
>> +
>> +	if (length + PROXY_HDR_SIZE > max_size)
>> +		return -E2BIG;
>> +
>> +	switch (type) {
>> +	case GSC_PROXY_MSG_TYPE_PROXY_PAYLOAD:
>> +		if (length > 0)
>> +			break;
>> +		fallthrough;
>> +	case GSC_PROXY_MSG_TYPE_PROXY_INVALID:
>> +		return -EIO;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +#define proxy_header_wr(xe_, map_, offset_, field_, val_) \
>> +	xe_map_wr_field(xe_, map_, offset_, struct xe_gsc_proxy_header,
>> +field_, val_)
>> +
>> +#define proxy_header_rd(xe_, map_, offset_, field_) \
>> +	xe_map_rd_field(xe_, map_, offset_, struct xe_gsc_proxy_header,
>> +field_)
>> +
>> +static u32 emit_proxy_header(struct xe_device *xe, struct iosys_map
>> +*map, u32 offset) {
>> +	xe_map_memset(xe, map, offset, 0, PROXY_HDR_SIZE);
>> +
>> +	proxy_header_wr(xe, map, offset, hdr,
>> +			FIELD_PREP(GSC_PROXY_TYPE,
>> GSC_PROXY_MSG_TYPE_PROXY_QUERY) |
>> +			FIELD_PREP(GSC_PROXY_PAYLOAD_LENGTH, 0));
>> +
>> +	proxy_header_wr(xe, map, offset, source,
>> GSC_PROXY_ADDRESSING_KMD);
>> +	proxy_header_wr(xe, map, offset, destination,
>> GSC_PROXY_ADDRESSING_GSC);
>> +	proxy_header_wr(xe, map, offset, status, 0);
>> +
>> +	return offset + PROXY_HDR_SIZE;
>> +}
>> +
>> +static int proxy_query(struct xe_gsc *gsc) {
>> +	struct xe_gt *gt = gsc_to_gt(gsc);
>> +	struct xe_device *xe = gt_to_xe(gt);
>> +	struct xe_gsc_proxy_header *to_csme_hdr = gsc->proxy.to_csme;
>> +	void *to_csme_payload = gsc->proxy.to_csme + PROXY_HDR_SIZE;
>> +	u32 wr_offset;
>> +	u32 reply_offset;
>> +	u32 size;
>> +	int ret;
>> +
>> +	wr_offset = xe_gsc_emit_header(xe, &gsc->proxy.to_gsc, 0,
>> +				       HECI_MEADDRESS_PROXY, 0,
>> PROXY_HDR_SIZE);
>> +	wr_offset = emit_proxy_header(xe, &gsc->proxy.to_gsc, wr_offset);
>> +
>> +	size = wr_offset;
>> +
>> +	while (1) {
>> +		/*
>> +		 * Poison the GSC response header space to make sure we
>> don't
>> +		 * read a stale reply.
>> +		 */
>> +		xe_gsc_poison_header(xe, &gsc->proxy.from_gsc, 0);
>> +
>> +		/* send proxy message to GSC */
>> +		ret = proxy_send_to_gsc(gsc, size);
>> +		if (ret)
>> +			goto proxy_error;
>> +
>> +		/* check the reply from GSC */
>> +		ret = xe_gsc_read_out_header(xe, &gsc->proxy.from_gsc, 0,
>> +					     PROXY_HDR_SIZE, &reply_offset);
>> +		if (ret) {
>> +			xe_gt_err(gt, "Invalid gsc header in proxy reply
>> (%pe)\n",
>> +				  ERR_PTR(ret));
>> +			goto proxy_error;
>> +		}
>> +
>> +		/* copy the proxy header reply from GSC */
>> +		xe_map_memcpy_from(xe, to_csme_hdr, &gsc-
>>> proxy.from_gsc,
>> +				   reply_offset, PROXY_HDR_SIZE);
>> +
>> +		/* stop if this was the last message */
>> +		if (FIELD_GET(GSC_PROXY_TYPE, to_csme_hdr->hdr) ==
>> GSC_PROXY_MSG_TYPE_PROXY_END)
>> +			break;
>> +
>> +		/* make sure the GSC-to-CSME proxy header is sane */
>> +		ret = validate_proxy_header(to_csme_hdr,
>> +					    GSC_PROXY_ADDRESSING_GSC,
>> +					    GSC_PROXY_ADDRESSING_CSME,
>> +					    GSC_PROXY_BUFFER_SIZE -
>> reply_offset);
>> +		if (ret) {
>> +			xe_gt_err(gt, "invalid GSC to CSME proxy header!
>> (%pe)\n",
>> +				  ERR_PTR(ret));
>> +			goto proxy_error;
>> +		}
>> +
>> +		/* copy the rest of the message */
>> +		size = FIELD_GET(GSC_PROXY_PAYLOAD_LENGTH,
>> to_csme_hdr->hdr);
>> +		xe_map_memcpy_from(xe, to_csme_payload, &gsc-
>>> proxy.from_gsc,
>> +				   reply_offset + PROXY_HDR_SIZE, size);
>> +
>> +		/* send the GSC message to the CSME */
>> +		ret = proxy_send_to_csme(gsc, size + PROXY_HDR_SIZE);
>> +		if (ret < 0)
>> +			goto proxy_error;
>> +
>> +		/* reply size from CSME, including the proxy header */
>> +		size = ret;
>> +		if (size < PROXY_HDR_SIZE) {
>> +			xe_gt_err(gt, "CSME to GSC proxy msg too small:
>> 0x%x\n", size);
>> +			ret = -EPROTO;
>> +			goto proxy_error;
>> +		}
>> +
>> +		/* make sure the CSME-to-GSC proxy header is sane */
>> +		ret = validate_proxy_header(gsc->proxy.from_csme,
>> +					    GSC_PROXY_ADDRESSING_CSME,
>> +					    GSC_PROXY_ADDRESSING_GSC,
>> +					    GSC_PROXY_BUFFER_SIZE -
>> reply_offset);
>> +		if (ret) {
>> +			xe_gt_err(gt, "invalid CSME to GSC proxy header!
>> %d\n", ret);
>> +			goto proxy_error;
>> +		}
>> +
>> +		/* Emit a new header for sending the reply to the GSC */
>> +		wr_offset = xe_gsc_emit_header(xe, &gsc->proxy.to_gsc, 0,
>> +					       HECI_MEADDRESS_PROXY, 0, size);
>> +
>> +		/* copy the CSME reply and update the total msg size to
>> include the GSC header */
>> +		xe_map_memcpy_to(xe, &gsc->proxy.to_gsc, wr_offset,
>> +gsc->proxy.from_csme, size);
>> +
>> +		size += wr_offset;
>> +	}
>> +
>> +proxy_error:
>> +	return ret < 0 ? ret : 0;
>> +}
>> +
>> +static int gsc_proxy_request_handler(struct xe_gsc *gsc) {
>> +	struct xe_gt *gt = gsc_to_gt(gsc);
>> +	int slept;
>> +	int err;
>> +
>> +	if (!gsc->proxy.component_added)
>> +		return -ENODEV;
>> +
>> +	/* when GSC is loaded, we can queue this before the component is
>> bound */
>> +	for (slept = 0; slept < GSC_PROXY_INIT_TIMEOUT_MS; slept += 100) {
>> +		if (gsc->proxy.component)
>> +			break;
>> +
>> +		msleep(100);
>> +	}
>> +
>> +	mutex_lock(&gsc->proxy.mutex);
>> +	if (!gsc->proxy.component) {
>> +		xe_gt_err(gt, "GSC proxy component not bound!\n");
>> +		err = -EIO;
>> +	} else {
>> +		err = proxy_query(gsc);
>> +	}
>> +	mutex_unlock(&gsc->proxy.mutex);
>> +	return err;
>> +}
>> +
>> +static int xe_gsc_proxy_component_bind(struct device *xe_kdev,
>> +				       struct device *mei_kdev, void *data) {
>> +	struct xe_device *xe = kdev_to_xe(xe_kdev);
>> +	struct xe_gt *gt = xe->tiles[0].media_gt;
>> +	struct xe_gsc *gsc = &gt->uc.gsc;
>> +
>> +	mutex_lock(&gsc->proxy.mutex);
>> +	gsc->proxy.component = data;
>> +	gsc->proxy.component->mei_dev = mei_kdev;
>> +	mutex_unlock(&gsc->proxy.mutex);
>> +
>> +	return 0;
>> +}
>> +
>> +static void xe_gsc_proxy_component_unbind(struct device *xe_kdev,
>> +					  struct device *mei_kdev, void *data)
>> {
>> +	struct xe_device *xe = kdev_to_xe(xe_kdev);
>> +	struct xe_gt *gt = xe->tiles[0].media_gt;
>> +	struct xe_gsc *gsc = &gt->uc.gsc;
>> +
>> +	xe_gsc_wait_for_worker_completion(gsc);
>> +
>> +	mutex_lock(&gsc->proxy.mutex);
>> +	gsc->proxy.component = NULL;
>> +	mutex_unlock(&gsc->proxy.mutex);
>> +}
>> +
>> +static const struct component_ops xe_gsc_proxy_component_ops = {
>> +	.bind   = xe_gsc_proxy_component_bind,
>> +	.unbind = xe_gsc_proxy_component_unbind, };
>> +
>> +static void proxy_channel_free(struct drm_device *drm, void *arg) {
>> +	struct xe_gsc *gsc = arg;
>> +
>> +	if (!gsc->proxy.bo)
>> +		return;
>> +
>> +	if (gsc->proxy.to_csme) {
>> +		kfree(gsc->proxy.to_csme);
>> +		gsc->proxy.to_csme = NULL;
>> +		gsc->proxy.from_csme = NULL;
>> +	}
>> +
>> +	if (gsc->proxy.bo) {
>> +		iosys_map_clear(&gsc->proxy.to_gsc);
>> +		iosys_map_clear(&gsc->proxy.from_gsc);
>> +		xe_bo_unpin_map_no_vm(gsc->proxy.bo);
>> +		gsc->proxy.bo = NULL;
>> +	}
>> +}
>> +
>> +static int proxy_channel_alloc(struct xe_gsc *gsc) {
>> +	struct xe_gt *gt = gsc_to_gt(gsc);
>> +	struct xe_tile *tile = gt_to_tile(gt);
>> +	struct xe_device *xe = gt_to_xe(gt);
>> +	struct xe_bo *bo;
>> +	void *csme;
>> +	int err;
>> +
>> +	csme = kzalloc(GSC_PROXY_CHANNEL_SIZE, GFP_KERNEL);
>> +	if (!csme)
>> +		return -ENOMEM;
>> +
>> +	bo = xe_bo_create_pin_map(xe, tile, NULL,
>> GSC_PROXY_CHANNEL_SIZE,
>> +				  ttm_bo_type_kernel,
>> +				  XE_BO_CREATE_SYSTEM_BIT |
>> +				  XE_BO_CREATE_GGTT_BIT);
>> +	if (IS_ERR(bo)) {
>> +		kfree(csme);
>> +		return PTR_ERR(bo);
>> +	}
>> +
>> +	gsc->proxy.bo = bo;
>> +	gsc->proxy.to_gsc = IOSYS_MAP_INIT_OFFSET(&bo->vmap, 0);
>> +	gsc->proxy.from_gsc = IOSYS_MAP_INIT_OFFSET(&bo->vmap,
>> GSC_PROXY_BUFFER_SIZE);
>> +	gsc->proxy.to_csme = csme;
>> +	gsc->proxy.from_csme = csme + GSC_PROXY_BUFFER_SIZE;
>> +
>> +	err = drmm_add_action_or_reset(&xe->drm, proxy_channel_free,
>> gsc);
>> +	if (err)
>> +		return err;
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * xe_gsc_proxy_init() - init objects and MEI component required by GSC
>> +proxy
>> + * @gsc: the GSC uC
>> + *
>> + * Return: 0 if the initialization was successful, a negative errno otherwise.
>> + */
>> +int xe_gsc_proxy_init(struct xe_gsc *gsc) {
>> +	int err;
>> +	struct xe_gt *gt = gsc_to_gt(gsc);
>> +	struct xe_tile *tile = gt_to_tile(gt);
>> +	struct xe_device *xe = tile_to_xe(tile);
>> +
>> +	mutex_init(&gsc->proxy.mutex);
>> +
>> +	if (!IS_ENABLED(CONFIG_INTEL_MEI_GSC_PROXY)) {
>> +		xe_gt_info(gt, "can't init GSC proxy due to missing mei
>> component\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	/* no multi-tile devices with this feature yet */
>> +	if (tile->id > 0) {
>> +		xe_gt_err(gt, "unexpected GSC proxy init on tile %u\n", tile-
>>> id);
>> +		return -EINVAL;
>> +	}
>> +
>> +	err = proxy_channel_alloc(gsc);
>> +	if (err)
>> +		return err;
>> +
>> +	err = component_add_typed(xe->drm.dev,
>> &xe_gsc_proxy_component_ops,
>> +				  I915_COMPONENT_GSC_PROXY);
>> +	if (err < 0) {
>> +		xe_gt_err(gt, "Failed to add GSC_PROXY component (%pe)\n",
>> ERR_PTR(err));
>> +		return err;
>> +	}
>> +
>> +	gsc->proxy.component_added = true;
>> +
>> +	/* the component must be removed before unload, so can't use drmm
>> for
>> +cleanup */
>> +
>> +	return 0;
>> +}
>> +
>> +/**
>> + * xe_gsc_proxy_remove() - remove the GSC proxy MEI component
>> + * @gsc: the GSC uC
>> + */
>> +void xe_gsc_proxy_remove(struct xe_gsc *gsc) {
>> +	struct xe_gt *gt = gsc_to_gt(gsc);
>> +	struct xe_device *xe = gt_to_xe(gt);
>> +
>> +	if (gsc->proxy.component_added) {
>> +		component_del(xe->drm.dev,
>> &xe_gsc_proxy_component_ops);
>> +		gsc->proxy.component_added = false;
>> +	}
>> +}
>> +
>> +/**
>> + * xe_gsc_proxy_start() - start the proxy by submitting the first
>> +request
>> + * @gsc: the GSC uC
>> + *
>> + * Return: 0 if the proxy are now enabled, a negative errno otherwise.
>> + */
>> +int xe_gsc_proxy_start(struct xe_gsc *gsc) {
>> +	int err;
>> +
>> +	/*
>> +	 * The handling of the first proxy request must be manually triggered
>> to
>> +	 * notify the GSC that we're ready to support the proxy flow.
>> +	 */
>> +	err = gsc_proxy_request_handler(gsc);
>> +	if (err)
>> +		return err;
>> +
>> +	if (!gsc_proxy_init_done(gsc)) {
>> +		xe_gt_err(gsc_to_gt(gsc), "GSC FW reports proxy init not
>> completed\n");
>> +		return -EIO;
>> +	}
>> +
>> +	return 0;
>> +}
>> diff --git a/drivers/gpu/drm/xe/xe_gsc_proxy.h
>> b/drivers/gpu/drm/xe/xe_gsc_proxy.h
>> new file mode 100644
>> index 000000000000..5dc6321efbaf
>> --- /dev/null
>> +++ b/drivers/gpu/drm/xe/xe_gsc_proxy.h
>> @@ -0,0 +1,17 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2023 Intel Corporation
>> + */
>> +
>> +#ifndef _XE_GSC_PROXY_H_
>> +#define _XE_GSC_PROXY_H_
>> +
>> +#include <linux/types.h>
>> +
>> +struct xe_gsc;
>> +
>> +int xe_gsc_proxy_init(struct xe_gsc *gsc); void
>> +xe_gsc_proxy_remove(struct xe_gsc *gsc); int xe_gsc_proxy_start(struct
>> +xe_gsc *gsc);
>> +
>> +#endif
>> diff --git a/drivers/gpu/drm/xe/xe_gsc_submit.c
>> b/drivers/gpu/drm/xe/xe_gsc_submit.c
>> index 8c5381e5913f..9ecc1ead6844 100644
>> --- a/drivers/gpu/drm/xe/xe_gsc_submit.c
>> +++ b/drivers/gpu/drm/xe/xe_gsc_submit.c
>> @@ -5,6 +5,8 @@
>>
>>   #include "xe_gsc_submit.h"
>>
>> +#include <linux/poison.h>
>> +
>>   #include "abi/gsc_command_header_abi.h"
>>   #include "xe_bb.h"
>>   #include "xe_exec_queue.h"
>> @@ -68,6 +70,17 @@ u32 xe_gsc_emit_header(struct xe_device *xe, struct
>> iosys_map *map, u32 offset,
>>   	return offset + GSC_HDR_SIZE;
>>   };
>>
>> +/**
>> + * xe_gsc_poison_header - poison the MTL GSC header in memory
>> + * @xe: the Xe device
>> + * @map: the iosys map to write to
>> + * @offset: offset from the start of the map at which the header
>> +resides  */ void xe_gsc_poison_header(struct xe_device *xe, struct
>> +iosys_map *map, u32 offset) {
>> +	xe_map_memset(xe, map, offset, POISON_FREE, GSC_HDR_SIZE); };
>> +
>>   /**
>>    * xe_gsc_check_and_update_pending - check the pending bit and update the
>> input
>>    * header with the retry handle from the output header diff --git
>> a/drivers/gpu/drm/xe/xe_gsc_submit.h
>> b/drivers/gpu/drm/xe/xe_gsc_submit.h
>> index 0801da5d446a..1939855031a6 100644
>> --- a/drivers/gpu/drm/xe/xe_gsc_submit.h
>> +++ b/drivers/gpu/drm/xe/xe_gsc_submit.h
>> @@ -14,6 +14,7 @@ struct xe_gsc;
>>
>>   u32 xe_gsc_emit_header(struct xe_device *xe, struct iosys_map *map, u32
>> offset,
>>   		       u8 heci_client_id, u64 host_session_id, u32 payload_size);
>> +void xe_gsc_poison_header(struct xe_device *xe, struct iosys_map *map,
>> +u32 offset);
>>
>>   bool xe_gsc_check_and_update_pending(struct xe_device *xe,
>>   				     struct iosys_map *in, u32 offset_in, diff --
>> git a/drivers/gpu/drm/xe/xe_gsc_types.h
>> b/drivers/gpu/drm/xe/xe_gsc_types.h
>> index 57fefd66a7ea..805f26be4e9b 100644
>> --- a/drivers/gpu/drm/xe/xe_gsc_types.h
>> +++ b/drivers/gpu/drm/xe/xe_gsc_types.h
>> @@ -6,12 +6,16 @@
>>   #ifndef _XE_GSC_TYPES_H_
>>   #define _XE_GSC_TYPES_H_
>>
>> +#include <linux/iosys-map.h>
>> +#include <linux/mutex.h>
>> +#include <linux/types.h>
>>   #include <linux/workqueue.h>
>>
>>   #include "xe_uc_fw_types.h"
>>
>>   struct xe_bo;
>>   struct xe_exec_queue;
>> +struct i915_gsc_proxy_component;
>>
>>   /**
>>    * struct xe_gsc - GSC
>> @@ -34,6 +38,25 @@ struct xe_gsc {
>>
>>   	/** @work: delayed load and proxy handling work */
>>   	struct work_struct work;
>> +
>> +	struct {
>> +		/** @component: struct for communication with mei
>> component */
>> +		struct i915_gsc_proxy_component *component;
>> +		/** @mutex: protects the component binding and usage */
>> +		struct mutex mutex;
>> +		/** @component_added: whether the component has been
>> added */
>> +		bool component_added;
>> +		/** @bo: object to store message to and from the GSC */
>> +		struct xe_bo *bo;
>> +		/** @to_gsc: map of the memory used to send messages to
>> the GSC */
>> +		struct iosys_map to_gsc;
>> +		/** @from_gsc: map of the memory used to recv messages
>> from the GSC */
>> +		struct iosys_map from_gsc;
>> +		/** @to_csme: pointer to the memory used to send messages
>> to CSME */
>> +		void *to_csme;
>> +		/** @from_csme: pointer to the memory used to recv
>> messages from CSME */
>> +		void *from_csme;
>> +	} proxy;
>>   };
>>
>>   #endif
>> diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c index
>> 3af2adec1295..b4c021e98606 100644
>> --- a/drivers/gpu/drm/xe/xe_gt.c
>> +++ b/drivers/gpu/drm/xe/xe_gt.c
>> @@ -78,6 +78,19 @@ void xe_gt_sanitize(struct xe_gt *gt)
>>   	gt->uc.guc.submission_state.enabled = false;  }
>>
>> +/**
>> + * xe_gt_remove() - Clean up the GT structures before driver removal
>> + * @uc: the GT object
>> + *
>> + * This function should only act on objects/structures that must be
>> +cleaned
>> + * before the driver removal callback is complete and therefore can't
>> +be
>> + * deferred to a drmm action.
>> + */
>> +void xe_gt_remove(struct xe_gt *gt)
>> +{
>> +	xe_uc_remove(&gt->uc);
>> +}
>> +
>>   static void gt_fini(struct drm_device *drm, void *arg)  {
>>   	struct xe_gt *gt = arg;
>> diff --git a/drivers/gpu/drm/xe/xe_gt.h b/drivers/gpu/drm/xe/xe_gt.h index
>> 4486e083f5ef..c1675bd44cf6 100644
>> --- a/drivers/gpu/drm/xe/xe_gt.h
>> +++ b/drivers/gpu/drm/xe/xe_gt.h
>> @@ -41,6 +41,7 @@ int xe_gt_suspend(struct xe_gt *gt);  int
>> xe_gt_resume(struct xe_gt *gt);  void xe_gt_reset_async(struct xe_gt *gt);
>> void xe_gt_sanitize(struct xe_gt *gt);
>> +void xe_gt_remove(struct xe_gt *gt);
>>
>>   /**
>>    * xe_gt_any_hw_engine_by_reset_domain - scan the list of engines and
>> return the diff --git a/drivers/gpu/drm/xe/xe_uc.c
>> b/drivers/gpu/drm/xe/xe_uc.c index 4408ea1751e7..8f37a809525f 100644
>> --- a/drivers/gpu/drm/xe/xe_uc.c
>> +++ b/drivers/gpu/drm/xe/xe_uc.c
>> @@ -7,6 +7,7 @@
>>
>>   #include "xe_device.h"
>>   #include "xe_gsc.h"
>> +#include "xe_gsc_proxy.h"
>>   #include "xe_gt.h"
>>   #include "xe_guc.h"
>>   #include "xe_guc_db_mgr.h"
>> @@ -261,3 +262,16 @@ int xe_uc_suspend(struct xe_uc *uc)
>>
>>   	return xe_guc_suspend(&uc->guc);
>>   }
>> +
>> +/**
>> + * xe_uc_remove() - Clean up the UC structures before driver removal
>> + * @uc: the UC object
>> + *
>> + * This function should only act on objects/structures that must be
>> +cleaned
>> + * before the driver removal callback is complete and therefore can't
>> +be
>> + * deferred to a drmm action.
>> + */
>> +void xe_uc_remove(struct xe_uc *uc)
>> +{
>> +	xe_gsc_remove(&uc->gsc);
>> +}
>> diff --git a/drivers/gpu/drm/xe/xe_uc.h b/drivers/gpu/drm/xe/xe_uc.h index
>> 5d5110c0c834..e4d4e3c99f0e 100644
>> --- a/drivers/gpu/drm/xe/xe_uc.h
>> +++ b/drivers/gpu/drm/xe/xe_uc.h
>> @@ -20,5 +20,6 @@ int xe_uc_stop(struct xe_uc *uc);  int xe_uc_start(struct
>> xe_uc *uc);  int xe_uc_suspend(struct xe_uc *uc);  int
>> xe_uc_sanitize_reset(struct xe_uc *uc);
>> +void xe_uc_remove(struct xe_uc *uc);
>>
>>   #endif
>> --
>> 2.43.0



More information about the Intel-xe mailing list