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

Kandpal, Suraj suraj.kandpal at intel.com
Tue Jan 16 06:13:06 UTC 2024


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.

> 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

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