#include "xe_gsc_submit.h"
#include <linux/poison.h>
#include "abi/gsc_command_header_abi.h"
#include "xe_assert.h"
#include "xe_bb.h"
#include "xe_exec_queue.h"
#include "xe_gt_types.h"
#include "xe_map.h"
#include "xe_sched_job.h"
#include "instructions/xe_gsc_commands.h"
#define GSC_HDR_SIZE (sizeof(struct intel_gsc_mtl_header))
#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_)
#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);
}
u64 xe_gsc_create_host_session_id(void)
{
u64 host_session_id;
get_random_bytes(&host_session_id, sizeof(u64));
host_session_id &= ~HOST_SESSION_CLIENT_MASK;
return host_session_id;
}
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;
};
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);
};
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;
}
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 status = mtl_gsc_header_rd(xe, map, offset, status);
u32 payload_size = size - GSC_HDR_SIZE;
if (marker != GSC_HECI_VALIDITY_MARKER)
return -EPROTO;
if (status != 0) {
drm_err(&xe->drm, "GSC header readout indicates error: %d\n",
status);
return -EINVAL;
}
if (size < GSC_HDR_SIZE || payload_size < min_payload_size)
return -ENODATA;
if (payload_offset)
*payload_offset = offset + GSC_HDR_SIZE;
return 0;
}
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;
}