Commit 0881cbe0 authored by Daniele Ceraolo Spurio's avatar Daniele Ceraolo Spurio Committed by Rodrigo Vivi

drm/xe/gsc: Query GSC compatibility version

The version is obtained via a dedicated MKHI GSC HECI command.
The compatibility version is what we want to match against for the GSC,
so we need to call the FW version checker after obtaining the version.

Since this is the first time we send a GSC HECI command via the GSCCS,
this patch also introduces common infrastructure to send such commands
to the GSC. Communication with the GSC FW is done via input/output
buffers, whose addresses are provided via a GSCCS command. The buffers
contain a generic header and a client-specific packet (e.g. PXP, HDCP);
the clients don't care about the header format and/or the GSCCS command
in the batch, they only care about their client-specific header. This
patch therefore introduces helpers that allow the callers to
automatically fill in the input header, submit the GSCCS job and decode
the output header, to make it so that the caller only needs to worry about
their client-specific input and output messages.

v3: squash of 2 separate patches ahead of merge, so that the common
functions and their first user are added at the same time
Signed-off-by: default avatarDaniele Ceraolo Spurio <daniele.ceraolospurio@intel.com>
Cc: Alan Previn <alan.previn.teres.alexis@intel.com>
Cc: Suraj Kandpal <suraj.kandpal@intel.com>
Cc: John Harrison <John.C.Harrison@Intel.com>
Reviewed-by: John Harrison <John.C.Harrison@Intel.Com> #v1
Reviewed-by: default avatarSuraj Kandpal <suraj.kandpal@intel.com>
Signed-off-by: default avatarRodrigo Vivi <rodrigo.vivi@intel.com>
parent f63182b4
...@@ -68,6 +68,7 @@ xe-y += xe_bb.o \ ...@@ -68,6 +68,7 @@ xe-y += xe_bb.o \
xe_ggtt.o \ xe_ggtt.o \
xe_gpu_scheduler.o \ xe_gpu_scheduler.o \
xe_gsc.o \ xe_gsc.o \
xe_gsc_submit.o \
xe_gt.o \ xe_gt.o \
xe_gt_clock.o \ xe_gt_clock.o \
xe_gt_debugfs.o \ xe_gt_debugfs.o \
......
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2023 Intel Corporation
*/
#ifndef _ABI_GSC_COMMAND_HEADER_ABI_H
#define _ABI_GSC_COMMAND_HEADER_ABI_H
#include <linux/types.h>
struct intel_gsc_mtl_header {
u32 validity_marker;
#define GSC_HECI_VALIDITY_MARKER 0xA578875A
u8 heci_client_id;
u8 reserved1;
u16 header_version;
#define MTL_GSC_HEADER_VERSION 1
/* FW allows host to decide host_session handle as it sees fit. */
u64 host_session_handle;
/* handle generated by FW for messages that need to be re-submitted */
u64 gsc_message_handle;
u32 message_size; /* lower 20 bits only, upper 12 are reserved */
/*
* Flags mask:
* Bit 0: Pending
* Bit 1: Session Cleanup;
* Bits 2-15: Flags
* Bits 16-31: Extension Size
* According to internal spec flags are either input or output
* we distinguish the flags using OUTFLAG or INFLAG
*/
u32 flags;
#define GSC_OUTFLAG_MSG_PENDING BIT(0)
#define GSC_INFLAG_MSG_CLEANUP BIT(1)
u32 status;
} __packed;
#endif
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2023 Intel Corporation
*/
#ifndef _ABI_GSC_MKHI_COMMANDS_ABI_H
#define _ABI_GSC_MKHI_COMMANDS_ABI_H
#include <linux/types.h>
/* Heci client ID for MKHI commands */
#define HECI_MEADDRESS_MKHI 7
/* Generic MKHI header */
struct gsc_mkhi_header {
u8 group_id;
u8 command;
u8 reserved;
u8 result;
} __packed;
/* GFX_SRV commands */
#define MKHI_GROUP_ID_GFX_SRV 0x30
#define MKHI_GFX_SRV_GET_HOST_COMPATIBILITY_VERSION (0x42)
struct gsc_get_compatibility_version_in {
struct gsc_mkhi_header header;
} __packed;
struct gsc_get_compatibility_version_out {
struct gsc_mkhi_header header;
u16 proj_major;
u16 compat_major;
u16 compat_minor;
u16 reserved[5];
} __packed;
#endif
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
REG_FIELD_PREP(GSC_OPCODE, op) | \ REG_FIELD_PREP(GSC_OPCODE, op) | \
REG_FIELD_PREP(GSC_CMD_DATA_AND_LEN, dl)) REG_FIELD_PREP(GSC_CMD_DATA_AND_LEN, dl))
#define GSC_HECI_CMD_PKT __GSC_INSTR(0, 6)
#define GSC_FW_LOAD __GSC_INSTR(1, 2) #define GSC_FW_LOAD __GSC_INSTR(1, 2)
#define GSC_FW_LOAD_LIMIT_VALID REG_BIT(31) #define GSC_FW_LOAD_LIMIT_VALID REG_BIT(31)
......
...@@ -7,11 +7,13 @@ ...@@ -7,11 +7,13 @@
#include <drm/drm_managed.h> #include <drm/drm_managed.h>
#include "abi/gsc_mkhi_commands_abi.h"
#include "generated/xe_wa_oob.h" #include "generated/xe_wa_oob.h"
#include "xe_bb.h" #include "xe_bb.h"
#include "xe_bo.h" #include "xe_bo.h"
#include "xe_device.h" #include "xe_device.h"
#include "xe_exec_queue.h" #include "xe_exec_queue.h"
#include "xe_gsc_submit.h"
#include "xe_gt.h" #include "xe_gt.h"
#include "xe_gt_printk.h" #include "xe_gt_printk.h"
#include "xe_map.h" #include "xe_map.h"
...@@ -91,6 +93,78 @@ static int emit_gsc_upload(struct xe_gsc *gsc) ...@@ -91,6 +93,78 @@ static int emit_gsc_upload(struct xe_gsc *gsc)
return 0; return 0;
} }
#define version_query_wr(xe_, map_, offset_, field_, val_) \
xe_map_wr_field(xe_, map_, offset_, struct gsc_get_compatibility_version_in, field_, val_)
#define version_query_rd(xe_, map_, offset_, field_) \
xe_map_rd_field(xe_, map_, offset_, struct gsc_get_compatibility_version_out, field_)
static u32 emit_version_query_msg(struct xe_device *xe, struct iosys_map *map, u32 wr_offset)
{
xe_map_memset(xe, map, wr_offset, 0, sizeof(struct gsc_get_compatibility_version_in));
version_query_wr(xe, map, wr_offset, header.group_id, MKHI_GROUP_ID_GFX_SRV);
version_query_wr(xe, map, wr_offset, header.command,
MKHI_GFX_SRV_GET_HOST_COMPATIBILITY_VERSION);
return wr_offset + sizeof(struct gsc_get_compatibility_version_in);
}
#define GSC_VER_PKT_SZ SZ_4K /* 4K each for input and output */
static int query_compatibility_version(struct xe_gsc *gsc)
{
struct xe_uc_fw_version *compat = &gsc->fw.versions.found[XE_UC_FW_VER_COMPATIBILITY];
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;
u32 wr_offset;
u32 rd_offset;
u64 ggtt_offset;
int err;
bo = xe_bo_create_pin_map(xe, tile, NULL, GSC_VER_PKT_SZ * 2,
ttm_bo_type_kernel,
XE_BO_CREATE_SYSTEM_BIT |
XE_BO_CREATE_GGTT_BIT);
if (IS_ERR(bo)) {
xe_gt_err(gt, "failed to allocate bo for GSC version query\n");
return PTR_ERR(bo);
}
ggtt_offset = xe_bo_ggtt_addr(bo);
wr_offset = xe_gsc_emit_header(xe, &bo->vmap, 0, HECI_MEADDRESS_MKHI, 0,
sizeof(struct gsc_get_compatibility_version_in));
wr_offset = emit_version_query_msg(xe, &bo->vmap, wr_offset);
err = xe_gsc_pkt_submit_kernel(gsc, ggtt_offset, wr_offset,
ggtt_offset + GSC_VER_PKT_SZ,
GSC_VER_PKT_SZ);
if (err) {
xe_gt_err(gt,
"failed to submit GSC request for compatibility version: %d\n",
err);
goto out_bo;
}
err = xe_gsc_read_out_header(xe, &bo->vmap, GSC_VER_PKT_SZ,
sizeof(struct gsc_get_compatibility_version_out),
&rd_offset);
if (err) {
xe_gt_err(gt, "HuC: invalid GSC reply for version query (err=%d)\n", err);
return err;
}
compat->major = version_query_rd(xe, &bo->vmap, rd_offset, compat_major);
compat->minor = version_query_rd(xe, &bo->vmap, rd_offset, compat_minor);
xe_gt_info(gt, "found GSC cv%u.%u\n", compat->major, compat->minor);
out_bo:
xe_bo_unpin_map_no_vm(bo);
return err;
}
static int gsc_fw_is_loaded(struct xe_gt *gt) static int gsc_fw_is_loaded(struct xe_gt *gt)
{ {
return xe_mmio_read32(gt, HECI_FWSTS1(MTL_GSC_HECI1_BASE)) & return xe_mmio_read32(gt, HECI_FWSTS1(MTL_GSC_HECI1_BASE)) &
...@@ -159,6 +233,14 @@ static int gsc_upload(struct xe_gsc *gsc) ...@@ -159,6 +233,14 @@ static int gsc_upload(struct xe_gsc *gsc)
return err; return err;
} }
err = query_compatibility_version(gsc);
if (err)
return err;
err = xe_uc_fw_check_version_requirements(&gsc->fw);
if (err)
return err;
xe_gt_dbg(gt, "GSC FW async load completed\n"); xe_gt_dbg(gt, "GSC FW async load completed\n");
return 0; return 0;
......
// SPDX-License-Identifier: MIT
/*
* Copyright © 2023 Intel Corporation
*/
#include "xe_gsc_submit.h"
#include "abi/gsc_command_header_abi.h"
#include "xe_bb.h"
#include "xe_exec_queue.h"
#include "xe_gt_printk.h"
#include "xe_gt_types.h"
#include "xe_map.h"
#include "xe_sched_job.h"
#include "instructions/xe_gsc_commands.h"
#include "regs/xe_gsc_regs.h"
#define GSC_HDR_SIZE (sizeof(struct intel_gsc_mtl_header)) /* shorthand define */
#define mtl_gsc_header_wr(xe_, map_, offset_, field_, val_) \
xe_map_wr_field(xe_, map_, offset_, struct intel_gsc_mtl_header, field_, val_)
#define mtl_gsc_header_rd(xe_, map_, offset_, field_) \
xe_map_rd_field(xe_, map_, offset_, struct intel_gsc_mtl_header, field_)
/*
* GSC FW allows us to define the host_session_handle as we see fit, as long
* as we use unique identifier for each user, with handle 0 being reserved for
* kernel usage.
* To be able to differentiate which client subsystem owns the given session, we
* include the client id in the top 8 bits of the handle.
*/
#define HOST_SESSION_CLIENT_MASK GENMASK_ULL(63, 56)
static struct xe_gt *
gsc_to_gt(struct xe_gsc *gsc)
{
return container_of(gsc, struct xe_gt, uc.gsc);
}
/**
* xe_gsc_emit_header - write 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 to write the header
* @heci_client_id: client id identifying the type of command (see abi for values)
* @host_session_id: host session ID of the caller
* @payload_size: size of the payload that follows the header
*
* Returns: offset memory location following the header
*/
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)
{
xe_assert(xe, !(host_session_id & HOST_SESSION_CLIENT_MASK));
if (host_session_id)
host_session_id |= FIELD_PREP(HOST_SESSION_CLIENT_MASK, heci_client_id);
xe_map_memset(xe, map, offset, 0, GSC_HDR_SIZE);
mtl_gsc_header_wr(xe, map, offset, validity_marker, GSC_HECI_VALIDITY_MARKER);
mtl_gsc_header_wr(xe, map, offset, heci_client_id, heci_client_id);
mtl_gsc_header_wr(xe, map, offset, host_session_handle, host_session_id);
mtl_gsc_header_wr(xe, map, offset, header_version, MTL_GSC_HEADER_VERSION);
mtl_gsc_header_wr(xe, map, offset, message_size, payload_size + GSC_HDR_SIZE);
return offset + 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
* @xe: the Xe device
* @in: the iosys map containing the input buffer
* @offset_in: offset within the iosys at which the input buffer is located
* @out: the iosys map containing the output buffer
* @offset_out: offset within the iosys at which the output buffer is located
*
* Returns: true if the pending bit was set, false otherwise
*/
bool xe_gsc_check_and_update_pending(struct xe_device *xe,
struct iosys_map *in, u32 offset_in,
struct iosys_map *out, u32 offset_out)
{
if (mtl_gsc_header_rd(xe, out, offset_out, flags) & GSC_OUTFLAG_MSG_PENDING) {
u64 handle = mtl_gsc_header_rd(xe, out, offset_out, gsc_message_handle);
mtl_gsc_header_wr(xe, in, offset_in, gsc_message_handle, handle);
return true;
}
return false;
}
/**
* xe_gsc_read_out_header - reads and validates the output header and returns
* the offset of the reply following the header
* @xe: the Xe device
* @map: the iosys map containing the output buffer
* @offset: offset within the iosys at which the output buffer is located
* @min_payload_size: minimum size of the message excluding the gsc header
* @payload_offset: optional pointer to be set to the payload offset
*
* Returns: -errno value on failure, 0 otherwise
*/
int xe_gsc_read_out_header(struct xe_device *xe,
struct iosys_map *map, u32 offset,
u32 min_payload_size,
u32 *payload_offset)
{
u32 marker = mtl_gsc_header_rd(xe, map, offset, validity_marker);
u32 size = mtl_gsc_header_rd(xe, map, offset, message_size);
u32 payload_size = size - GSC_HDR_SIZE;
if (marker != GSC_HECI_VALIDITY_MARKER)
return -EPROTO;
if (size < GSC_HDR_SIZE || payload_size < min_payload_size)
return -ENODATA;
if (payload_offset)
*payload_offset = offset + GSC_HDR_SIZE;
return 0;
}
/**
* xe_gsc_pkt_submit_kernel - submit a kernel heci pkt to the GSC
* @gsc: the GSC uC
* @addr_in: GGTT address of the message to send to the GSC
* @size_in: size of the message to send to the GSC
* @addr_out: GGTT address for the GSC to write the reply to
* @size_out: size of the memory reserved for the reply
*/
int xe_gsc_pkt_submit_kernel(struct xe_gsc *gsc, u64 addr_in, u32 size_in,
u64 addr_out, u32 size_out)
{
struct xe_gt *gt = gsc_to_gt(gsc);
struct xe_bb *bb;
struct xe_sched_job *job;
struct dma_fence *fence;
long timeout;
if (size_in < GSC_HDR_SIZE)
return -ENODATA;
if (size_out < GSC_HDR_SIZE)
return -ENOMEM;
bb = xe_bb_new(gt, 8, false);
if (IS_ERR(bb))
return PTR_ERR(bb);
bb->cs[bb->len++] = GSC_HECI_CMD_PKT;
bb->cs[bb->len++] = lower_32_bits(addr_in);
bb->cs[bb->len++] = upper_32_bits(addr_in);
bb->cs[bb->len++] = size_in;
bb->cs[bb->len++] = lower_32_bits(addr_out);
bb->cs[bb->len++] = upper_32_bits(addr_out);
bb->cs[bb->len++] = size_out;
bb->cs[bb->len++] = 0;
job = xe_bb_create_job(gsc->q, bb);
if (IS_ERR(job)) {
xe_bb_free(bb, NULL);
return PTR_ERR(job);
}
xe_sched_job_arm(job);
fence = dma_fence_get(&job->drm.s_fence->finished);
xe_sched_job_push(job);
timeout = dma_fence_wait_timeout(fence, false, HZ);
dma_fence_put(fence);
xe_bb_free(bb, NULL);
if (timeout < 0)
return timeout;
else if (!timeout)
return -ETIME;
return 0;
}
/* SPDX-License-Identifier: MIT */
/*
* Copyright © 2023 Intel Corporation
*/
#ifndef _XE_GSC_SUBMIT_H_
#define _XE_GSC_SUBMIT_H_
#include <linux/types.h>
struct iosys_map;
struct xe_device;
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);
bool xe_gsc_check_and_update_pending(struct xe_device *xe,
struct iosys_map *in, u32 offset_in,
struct iosys_map *out, u32 offset_out);
int xe_gsc_read_out_header(struct xe_device *xe,
struct iosys_map *map, u32 offset,
u32 min_payload_size,
u32 *payload_offset);
int xe_gsc_pkt_submit_kernel(struct xe_gsc *gsc, u64 addr_in, u32 size_in,
u64 addr_out, u32 size_out);
#endif
...@@ -224,8 +224,11 @@ uc_fw_auto_select(struct xe_device *xe, struct xe_uc_fw *uc_fw) ...@@ -224,8 +224,11 @@ uc_fw_auto_select(struct xe_device *xe, struct xe_uc_fw *uc_fw)
uc_fw->versions.wanted.minor = entries[i].minor; uc_fw->versions.wanted.minor = entries[i].minor;
uc_fw->full_ver_required = entries[i].full_ver_required; uc_fw->full_ver_required = entries[i].full_ver_required;
/* compatibility version checking coming soon */ if (uc_fw->type == XE_UC_FW_TYPE_GSC)
uc_fw->versions.wanted_type = XE_UC_FW_VER_RELEASE; uc_fw->versions.wanted_type = XE_UC_FW_VER_COMPATIBILITY;
else
uc_fw->versions.wanted_type = XE_UC_FW_VER_RELEASE;
break; break;
} }
} }
...@@ -321,7 +324,7 @@ static void guc_read_css_info(struct xe_uc_fw *uc_fw, struct uc_css_header *css) ...@@ -321,7 +324,7 @@ static void guc_read_css_info(struct xe_uc_fw *uc_fw, struct uc_css_header *css)
uc_fw->private_data_size = css->private_data_size; uc_fw->private_data_size = css->private_data_size;
} }
static int uc_fw_check_version_requirements(struct xe_uc_fw *uc_fw) int xe_uc_fw_check_version_requirements(struct xe_uc_fw *uc_fw)
{ {
struct xe_device *xe = uc_fw_to_xe(uc_fw); struct xe_device *xe = uc_fw_to_xe(uc_fw);
struct xe_uc_fw_version *wanted = &uc_fw->versions.wanted; struct xe_uc_fw_version *wanted = &uc_fw->versions.wanted;
...@@ -678,9 +681,12 @@ int xe_uc_fw_init(struct xe_uc_fw *uc_fw) ...@@ -678,9 +681,12 @@ int xe_uc_fw_init(struct xe_uc_fw *uc_fw)
"Using %s firmware from %s", "Using %s firmware from %s",
xe_uc_fw_type_repr(uc_fw->type), uc_fw->path); xe_uc_fw_type_repr(uc_fw->type), uc_fw->path);
err = uc_fw_check_version_requirements(uc_fw); /* for GSC FW we want the compatibility version, which we query after load */
if (err) if (uc_fw->type != XE_UC_FW_TYPE_GSC) {
goto fail; err = xe_uc_fw_check_version_requirements(uc_fw);
if (err)
goto fail;
}
obj = xe_bo_create_from_data(xe, tile, fw->data, fw->size, obj = xe_bo_create_from_data(xe, tile, fw->data, fw->size,
ttm_bo_type_kernel, ttm_bo_type_kernel,
......
...@@ -17,6 +17,7 @@ struct drm_printer; ...@@ -17,6 +17,7 @@ struct drm_printer;
int xe_uc_fw_init(struct xe_uc_fw *uc_fw); int xe_uc_fw_init(struct xe_uc_fw *uc_fw);
size_t xe_uc_fw_copy_rsa(struct xe_uc_fw *uc_fw, void *dst, u32 max_len); size_t xe_uc_fw_copy_rsa(struct xe_uc_fw *uc_fw, void *dst, u32 max_len);
int xe_uc_fw_upload(struct xe_uc_fw *uc_fw, u32 offset, u32 dma_flags); int xe_uc_fw_upload(struct xe_uc_fw *uc_fw, u32 offset, u32 dma_flags);
int xe_uc_fw_check_version_requirements(struct xe_uc_fw *uc_fw);
void xe_uc_fw_print(struct xe_uc_fw *uc_fw, struct drm_printer *p); void xe_uc_fw_print(struct xe_uc_fw *uc_fw, struct drm_printer *p);
static inline u32 xe_uc_fw_rsa_offset(struct xe_uc_fw *uc_fw) static inline u32 xe_uc_fw_rsa_offset(struct xe_uc_fw *uc_fw)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment