[PATCH i-g-t v1] tools/xe_guc_logger: Add guc logger for Xe

Zhanjun Dong zhanjun.dong at intel.com
Wed Mar 12 21:30:57 UTC 2025


Add guc logger for Xe, support save guc log in LFD format.

Reference:
https://coredocs.intel.com/InterfaceDocs/sphinx/core/kmd_log_file_format.html?highlight=lfd

Signed-off-by: Zhanjun Dong <zhanjun.dong at intel.com>
---
Cc: John Harrison <John.C.Harrison at Intel.com>
Cc: Alan Previn <alan.previn.teres.alexis at intel.com>

 tools/lfd.h           | 590 ++++++++++++++++++++++++++++++++++++++++
 tools/lfd_default.h   |  39 +++
 tools/meson.build     |   1 +
 tools/xe_guc_logger.c | 615 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1245 insertions(+)
 create mode 100644 tools/lfd.h
 create mode 100644 tools/lfd_default.h
 create mode 100644 tools/xe_guc_logger.c

diff --git a/tools/lfd.h b/tools/lfd.h
new file mode 100644
index 000000000..b29627f50
--- /dev/null
+++ b/tools/lfd.h
@@ -0,0 +1,590 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2016-2019 Intel Corporation
+ */
+
+#ifndef _INTEL_GUC_LFD_H_
+#define _INTEL_GUC_LFD_H_
+
+#include <stdint.h>
+
+/* The current major version of GuC-Log-File format. */
+#define GUC_LOG_FILE_FORMAT_VERSION_MAJOR	0x0001
+
+/* The current minor version of GuC-Log-File format. */
+#define GUC_LOG_FILE_FORMAT_VERSION_MINOR	0x0000
+
+/* Magic keys define */
+#define GUC_LOGFILE_LFD_MAGIC			0x8086
+#define LFD_DRIVER_KEY_1			0X808655CC
+#define LFD_DRIVER_KEY_1			0X808655CC
+#define LFD_DRIVER_KEY_1V2			0X808655DD
+#define LFD_DRIVER_KEY_2			0X8086EEAA
+#define LFD_DRIVER_KEY_2V2			0X8086EEBB
+#define LFD_DRIVER_KEY_STREAMING1		0X474C5346
+#define LFD_DRIVER_KEY_STREAMING2		0X8086AAAA
+#define LFD_DRIVER_KEY_STREAMING		0X8086AAAA474C5346
+#define LFD_LOG_BUFFER_MARKER_1			0XCABBA9E5
+#define LFD_LOG_BUFFER_MARKER_2			0XDEADFEED
+#define LFD_CRASH_DUMP_BUFFER_MARKER_1		0XCABBA9E5
+#define LFD_CRASH_DUMP_BUFFER_MARKER_2		0X8086DEAD
+#define LFD_STATE_CAPTURE_BUFFER_MARKER_1	0XCABBA9F6
+#define LFD_STATE_CAPTURE_BUFFER_MARKER_2	0XBEEFFEED
+#define LFD_LOG_BUFFER_MARKER_1V2		0XCABBA9E6
+#define LFD_CRASH_DUMP_BUFFER_MARKER_1V2	0XCABBA9E6
+#define LFD_STATE_CAPTURE_BUFFER_MARKER_1V2	0XCABBA9F7
+
+#define GUC_LOG_BUFFER_STATE_HEADER_LENGTH		4096
+#define GUC_LOG_BUFFER_STATE_HEADER_ENTRY_COUNT		4
+#define GUC_LOG_BUFFER_STATE_HEADER_ENTRY_LOG		0
+#define GUC_LOG_BUFFER_STATE_HEADER_ENTRY_CRASH		1
+#define GUC_LOG_BUFFER_STATE_HEADER_ENTRY_CAPTURE	2
+#define GUC_LOG_BUFFER_STATE_HEADER_ENTRY_INIT		3
+
+#define GUC_LOG_INIT_CONFIG_LIC_MAGIC			0x8086900D
+#define GUC_LOG_INIT_CONFIG_FORMAT_VERSION_MAJOR	0x0001
+#define GUC_LOG_INIT_CONFIG_FORMAT_VERSION_MINOR	0x0000
+
+#define GUC_LOG_EVENT_ENTRY_FORMAT_VERSION		2
+
+#define LFD_MAGIC_SIM_KEY				0X900DFEED
+
+/* Log format descriptor type. */
+enum  guc_lfd_type_t {
+	/* Start of range for required LFDs from GuC. */
+	GUC_LFD_TYPE_FW_REQUIRED_RANGE_START = 0x1,
+
+	/*
+	 * GuC Firmware Version structure. LFDs payload is
+	 * guc_lfd_data_fw_version_t
+	 */
+	GUC_LFD_TYPE_FW_VERSION = 0x1,
+
+	/*
+	 * GuC microcontroller device ID. LFDs payload is
+	 * guc_lfd_data_guc_devid_t
+	 */
+	GUC_LFD_TYPE_GUC_DEVICE_ID = 0x2,
+
+	/*
+	 * Frequency of GuC timestamps. LFDs payload is
+	 * guc_lfd_data_tsc_freq_t.
+	 */
+	GUC_LFD_TYPE_TSC_FREQUENCY = 0x3,
+
+	/* End of this range. */
+	GUC_LFD_TYPE_FW_REQUIRED_RANGE_END = 0x1FFF,
+
+	/* Start of range for required LFDs from GuC. */
+	GUC_LFD_TYPE_FW_OPTIONAL_RANGE_START = 0x2000,
+
+	/*
+	 * Log-event-entries buffer. LFDs payload is
+	 * guc_lfd_data_log_events_buf_t
+	 */
+	GUC_LFD_TYPE_LOG_EVENTS_BUFFER = 0x2000,
+
+	/*
+	 * GuC generated crash-dump blob. LFDs payload is
+	 * guc_lfd_data_fw_crashdump_t
+	 */
+	GUC_LFD_TYPE_FW_CRASH_DUMP = 0x2001,
+
+	/* End of this range. */
+	GUC_LFD_TYPE_FW_OPTIONAL_RANGE_END = 0x3FFF,
+
+	/* Start of range for required KMD LFDs. */
+	GUC_LFD_TYPE_KMD_REQUIRED_RANGE_START = 0x4000,
+
+	/* An identifier for the OS. LFDs payload is guc_lfd_data_os_id_t. */
+	GUC_LFD_TYPE_OS_ID = 0x4000,
+
+	/* End of this range. */
+	GUC_LFD_TYPE_KMD_REQUIRED_RANGE_END = 0x5FFF,
+
+	/* Start of range for optional KMD LFDs. */
+	GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_START = 0x6000,
+
+	/*
+	 * Binary representation of GuC log-events schema.
+	 * LFDs TLV payload is guc_lfd_data_binary_schema_t
+	 */
+	GUC_LFD_TYPE_BINARY_SCHEMA_FORMAT = 0x6000,
+
+	/*
+	 * ASCII string containing comments from the host/KMD.
+	 * LFDs TLV payload is guc_lfd_data_host_comment_t
+	 */
+	GUC_LFD_TYPE_HOST_COMMENT = 0x6001,
+
+	/* End of this range. */
+	GUC_LFD_TYPE_KMD_OPTIONAL_RANGE_END = 0x7FFF,
+
+	/* Start of reserved range. */
+	GUC_LFD_TYPE_RESERVED_RANGE_START = 0x8000,
+
+	/* End of this range. */
+	GUC_LFD_TYPE_RESERVED_RANGE_END = 0xFFFF,
+};
+
+/* OS Type LFD-ID. */
+enum  guc_lfd_os_type_t {
+	/* Windows OS. */
+	GUC_LFD_OS_TYPE_OSID_WIN = 0x1,
+
+	/* Linux OS. */
+	GUC_LFD_OS_TYPE_OSID_LIN = 0x2,
+
+	/* VMWare OS. */
+	GUC_LFD_OS_TYPE_OSID_VMW = 0x3,
+
+	/* Other. */
+	GUC_LFD_OS_TYPE_OSID_OTHER = 0x4,
+};
+
+/*
+ * Log format descriptor (LFD). A type of KLV with custom
+ * field-sizes + magic numbers.
+ */
+struct guc_logfile_lfd_t {
+	/* BR[15:0] Expected value: 0x8086. Helpful in detecting file errors. */
+	uint32_t magic : 16;
+
+	/*
+	 * BR[31:16] File descriptor type (the ‘T’ in TLV) is used to identify
+	 * how to interpret data below. For the range of types,
+	 * see guc_lfd_type_t
+	 */
+	uint32_t desc_type : 16;
+
+	/* Number of dwords the data field contains. */
+	uint32_t desc_dw_size;
+
+	/* Data defined by File descriptor type. */
+	uint32_t data[];
+};
+
+/*
+ * GuC Log File Format Version. Major-Minor is not a fractional number
+ * (i.e. Ver 1.3 would be older than 1.12)
+ */
+struct guc_logfile_fmt_ver_t {
+	/*
+	 * BR[15: 0] Guc-Log-File Format minor version.
+	 * Must be GUC_LOG_FILE_FORMAT_VERSION_MINOR
+	 */
+	uint32_t minor_version : 16;
+
+	/*
+	 * BR[31:16] Guc-Log-File Format major version.
+	 * Must be GUC_LOG_FILE_FORMAT_VERSION_MAJOR
+	 */
+	uint32_t major_version : 16;
+};
+
+/*
+ * GuC Log Streaming-LFD-File Format. This structure encapsulates the layout of
+ * the guc-log-file format.
+ */
+struct guc_logfile_t {
+	/*
+	 * A magic number set by producer of a GuC log file to identify that
+	 * file is a valid guc-log-file containing a stream of LFDs:
+	 * Expected value: 0x8086AAAA474C5346.
+	 */
+	uint64_t magic;
+
+	/* Version of this file format layout as per guc_logfile_fmt_ver_t. */
+	struct guc_logfile_fmt_ver_t version;
+
+	/* A stream of one or more guc_logfile_lfd_t LFD data. */
+	struct guc_logfile_lfd_t lfd_stream[];
+};
+
+/* This structure describes the full version of a software component. */
+struct guc_sw_version_t {
+	/* BR[ 7: 0] Patch version. */
+	uint32_t patch_version : 8;
+
+	/* BR[15: 8] Minor version. */
+	uint32_t minor_version : 8;
+
+	/* BR[23:16] Major version. */
+	uint32_t major_version : 8;
+
+	/* BR[31:24] Branch ID. */
+	uint32_t branch_id : 8;
+};
+
+/* GuC FW Version. This is mandatory fw LFD data. */
+struct guc_lfd_data_fw_version_t {
+	/* The full version of the GuC microkernel that generated the logs. */
+	struct guc_sw_version_t guc_sw_version;
+};
+
+/*
+ * Log Init Config Types.
+ * The first word of the each TLV payload of guc_log_init_config_t contains one
+ * of the following enum values. This value determines how subsequent bytes are
+ * parsed to obtain the TLV information.
+ */
+enum guc_log_lic_type_t {
+	/*
+	 * GuC firmware version.
+	 * Subsequent bytes of current log-init-config are parsed as per
+	 * guc_lic_guc_sw_version_t structure layout.
+	 */
+	GUC_LOG_LIC_TYPE_GUC_SW_VERSION = 0x1,
+
+	/*
+	 * TSC Frequency.
+	 * Subsequent bytes of current log-init-config are parsed as per
+	 * guc_lic_tsc_freq_t structure layout.
+	 */
+	GUC_LOG_LIC_TYPE_TSC_FREQUENCY = 0x2,
+
+	/*
+	 * GuC device id.
+	 * Subsequent bytes of current log-init-config are parsed as per
+	 * guc_lic_guc_devid_t structure layout.
+	 */
+	GUC_LOG_LIC_TYPE_GUC_DEVICE_ID = 0x3
+};
+
+/* Log Init Config: GUC software version. */
+struct guc_lic_guc_sw_version_t {
+	/* BR[31:16] Number of dwords in proceeding payload = 1. */
+	uint32_t size : 16;
+
+	/*
+	 * BR[15: 0] Log-init-config
+	 * TLV type enum = GUC_LOG_LIC_TYPE_GUC_SW_VERSION.
+	 */
+	uint32_t type : 16;
+
+	/* 32 bit number representing the Guc’s software version. */
+	struct guc_sw_version_t guc_sw_version;
+};
+
+/*
+ * Log Init Config Structure Version.
+ * Major-Minor is not a fractional number
+ * (i.e. Ver 1.3 would be older than 1.12)
+ */
+struct guc_lic_format_version_t {
+	/*
+	 * BR[15: 0] Log-Init-Config structure minor  version.
+	 * Must be GUC_LOG_FILE_FORMAT_VERSION_MINOR
+	 */
+	uint32_t minor_version : 16;
+
+	/*
+	 * BR[31:16] Log-Init-Config structure major version.
+	 * Must be GUC_LOG_FILE_FORMAT_VERSION_MAJOR
+	 */
+	uint32_t major_version : 16;
+};
+
+/* Log Init Config: GUC Device ID. */
+struct guc_lic_guc_devid_t {
+	/* BR[31:16] Number of dwords in proceeding payload = 1. */
+	uint32_t size : 16;
+
+	/*
+	 * BR[15: 0] Log-init-config TLV type enum =
+	 * GUC_LOG_LIC_TYPE_GUC_DEVICE_ID.
+	 */
+	uint32_t type : 16;
+
+	/*
+	 * BR[31: 0] Guc device ID. Refer bspec symbol GUC_DEVICEID
+	 * https://gfxspecs.intel.com/Predator/Home/Index/50668.
+	 */
+	uint32_t guc_devid : 32;
+};
+
+/* GuC Device ID. This is mandatory fw LFD data. */
+struct guc_lfd_data_guc_devid_t {
+	/* GuC microcontroller device ID defined as per guc_lic_guc_devid_t. */
+	struct guc_lic_guc_devid_t guc_devid;
+};
+
+/* Log Init Config: TSC Fequency. */
+struct guc_lic_tsc_freq_t {
+	/* BR[31:16] Number of dwords in proceeding payload = 1. */
+	uint32_t size : 16;
+
+	/*
+	 * BR[15: 0] Log-init-config TLV type enum =
+	 * GUC_LOG_LIC_TYPE_TSC_FREQUENCY.
+	 */
+	 uint32_t type : 16;
+
+	/*
+	 * 32 bit number representing frequency in kilohertz for all the
+	 * timestamps being used in the log-entry.
+	 */
+	uint32_t tsc_freq;
+};
+
+/* GuC TSC Fequency. This is mandatory fw LFD data. */
+struct guc_lfd_data_tsc_freq_t {
+	/*
+	 * The frequency of timestamps used in guc logs as described in
+	 * guc_lic_tsc_freq_t
+	 */
+	struct guc_lic_tsc_freq_t tsc_freq;
+};
+
+/*
+ * GuC Log-Init-Config structure.
+ * This is populated by the GUC at log init time and is located in the log
+ * buffer as per the Log Buffer Layout (In Memory). The array of guc log
+ * buffer states plus this structure must not exceed 4KB.
+ */
+struct guc_log_init_config_t {
+	/*
+	 * A magic number set by GuC to identify that this structure
+	 * contains valid information: lic_magic = 0x8086900D.
+	 * Used to verify the information in this structure is valid.
+	 */
+	uint32_t lic_magic;
+
+	/*
+	 * The version of the this structure.
+	 * Detail description is guc_lic_format_version_t
+	 */
+	struct guc_lic_format_version_t lic_ver;
+
+	/* Number of Dws the lic_data array contains. */
+	uint32_t lic_dw_size;
+
+	/*
+	 * Array of dwords representing a sequence of LIC TLVs types.
+	 * See guc_log_lic_type_t enums
+	 */
+	uint32_t lic_data[];
+};
+
+/* GuC Log entry format Version 2. */
+struct guc_log_format_version_2_t {
+	/* BR[31: 0] The GuC timestamp of the log event. */
+	uint32_t timestamp : 32;
+
+	/* BR[19: 0] The Vf ID the log entry has been generated for. */
+	uint32_t vf_id : 20;
+
+	/* BR[31:20] Reserved. */
+	uint32_t reserved_1 : 12;
+
+	/* BR[14: 0] The GuC Event ID value for the log event. */
+	uint32_t event_id : 15;
+
+	/* BR[15:15] Indicates if Event should be visible to Vf Host driver. */
+	uint32_t vf_visible : 1;
+
+	/*
+	 * BR[17:16] This field contains the verbosity level of the log entry,
+	 * see the Log Verbosity section. This can then be used by the PF driver
+	 * to filter the logs copied to a VF based on the verbosity requested by
+	 * a VF.
+	 */
+	uint32_t verbosity : 2;
+
+	/* BR[31:18] Reserved. */
+	uint32_t reserved_2 : 14;
+
+	/* BR[31: 0] The value of Parameter 1 of the log event. */
+	uint32_t parameter_1 : 32;
+
+	/* BR[31: 0] The value of Parameter 2 of the log event. */
+	uint32_t parameter_2 : 32;
+};
+
+/* GuC Log Buffer State. */
+struct guc_log_buffer_state_t {
+	/*
+	 * A marker set by the ukernel to identify the buffer state start
+	 * location in a binary file containing the GuC log. This is used
+	 * by log parsing tools. The Current values are:
+	 *
+	 * Log buffer
+	 * Marker[0] 0XCABBA9E6,
+	 * Marker[1] 0XDEADFEED
+	 * Crashdump
+	 * Marker[0] 0XCABBA9E6,
+	 * Marker[1] 0x8086DEAD
+	 * Error State Capture
+	 * Marker[0] 0XCABBA9F7,
+	 * Marker[1] 0XBEEFFEED
+	 * Schema
+	 * Marker[0] 0x808655DD,
+	 * Marker[1] 0x8086EEBB.
+	 * Note: The schema is never written to the memory by ukernel.
+	 * Its an optional feature for KMD to embed schema in guclog file.
+	 */
+	uint32_t Marker[2];
+
+	/*
+	 * BR[31: 0] This is the Last Byte Offset Location that was read by
+	 * KMD. KMD will write to this and uKernel will read this.
+	 */
+	uint32_t log_buf_rd_ptr : 32;
+
+	/*
+	 * BR[31: 0] This is the Byte Offset Location that will be written
+	 * by uKernel.
+	 */
+	uint32_t log_guc_wr_ptr : 32;
+
+	/*
+	 * BR[31: 0] Log buffer size.
+	 * This is written by the KMD and specifies the size of the buffer
+	 * in bytes
+	 */
+	uint32_t log_buf_size : 32;
+
+	/*
+	 * BR[31: 0] This is written by ukernel to the byte offset of the
+	 * next free entry in the buffer on log buffer half full or state
+	 * capture notification.
+	 */
+	uint32_t sampled_log_buf_wr_ptr : 32;
+
+	/*
+	 * BR[31: 0] This is the byte offset of location 1 byte after last
+	 * valid guc log event entry written by Guc firmware before there
+	 * was a wraparound.
+	 * This field is updated by guc firmware and should be used by Host
+	 * when copying buffer contents to file.
+	 */
+	uint32_t log_guc_buff_wrap_offset : 32;
+
+	/*
+	 * BR[ 0: 0] uKernel sets this when log buffer is half full or when
+	 * a flush has been requested by KMD through host2guc.
+	 * uKernel will send GUC2HOST only if this bit is cleared. This is
+	 * to avoid unnecessary interrupts from GuC.
+	 */
+	uint32_t log_buf_flush_to_file : 1;
+
+	/*
+	 * BR[ 4: 1] uKernel increments this when log buffer overflows.
+	 * This is initialized to 0 by KMD.
+	 */
+	uint32_t buffer_full_cnt : 4;
+
+	/* BR[31: 5] Reserved. */
+	uint32_t reserved : 27;
+
+	/*
+	 * BR[31: 0] The Guc-Log-Entry format version, a single integer.
+	 * Current version is GUC_LOG_EVENT_ENTRY_FORMAT_VERSION
+	 */
+	uint32_t version : 32;
+};
+
+/* GuC Log Events Buffer. This is optional fw LFD data. */
+struct guc_lfd_data_log_events_buf_t {
+	/* version of GuC log format of buffer */
+	uint32_t log_events_format_version;
+
+	/*
+	 * If log_format_version == 1, array of guc_log_format_version_1_t.
+	 * If log_format_version == 2, array of guc_log_format_version_2_t.
+	 * Dword size determined by guc_logfile_lfd_t.desc_dw_size - 1
+	 */
+	uint32_t log_format_buf[];
+};
+
+/* OS Version Information. This is mandatory host LFD data. */
+struct guc_lfd_data_os_id_t {
+	/*
+	 * enum values to identify the OS brand (1=Windows, 2=Linux, etc..).
+	 * See guc_lfd_os_type_t for the range of types
+	 */
+	uint32_t os_id;
+
+	/*
+	 * ASCII string containing OS build version information based on os_id.
+	 * String is padded with null characters to ensure its DWORD aligned.
+	 * Dword size determined by guc_logfile_lfd_t.desc_dw_size - 1
+	 */
+	char build_version[];
+};
+
+/* The type id for a Binary-Schema-Encoding record. */
+enum guc_bse_record_types_t {
+	/*
+	 * Event descriptor record.
+	 * Subsequent bytes of the binary schema encoding are parsed as per
+	 * guc_bse_event_t structure layout
+	 */
+	GUC_BSE_RECORD_TYPES_EVENT_DESC = 0x41,
+
+	/*
+	 * Parameter descriptor record.
+	 * Subsequent bytes of the binary schema encoding are parsed as per
+	 * guc_bse_param_t structure layout
+	 */
+	GUC_BSE_RECORD_TYPES_PARAM_DESC = 0x42,
+
+	/*
+	 * Time stamp record.
+	 * Subsequent bytes of the binary schema encoding are parsed as per
+	 * guc_bse_timestamp_t structure layout
+	 */
+	GUC_BSE_RECORD_TYPES_TIMESTAMP = 0x43,
+
+	/*
+	 * Schema version record.
+	 * Subsequent bytes of current binary schema descriptor are parsed as
+	 * per guc_bse_schema_ver_t structure layout.
+	 * This shall be the first record descriptor to appear in the
+	 * binary-schema stream.
+	 */
+	GUC_BSE_RECORD_TYPES_SCHEMA_VERSION_RECORD = 0x44,
+
+	/*
+	 * GuC Firmware version record.
+	 * Subsequent bytes of the binary schema encoding are parsed as per
+	 * guc_bse_fw_ver_t structure layout
+	 */
+	GUC_BSE_RECORD_TYPES_FW_VERSION_RECORD = 0x45
+};
+
+/* Binary Schema Encoding for the general log record. */
+struct guc_bse_record_t {
+	/*
+	 * 8 bit identifier for the type of binary schema record descriptor
+	 * record being. This value must be one of guc_bse_record_types_t
+	 */
+	uint8_t type;
+
+	/*
+	 * Stream of bytes that contain the encoded payload for the
+	 * corresponding record type. Bytes are parsed based on ‘type’
+	 * structure
+	 */
+	uint8_t enc_payload[];
+};
+
+/*
+ * Binary Schema Encoding Stream. This structure encapsulates a stream of
+ * guc-log-translation-schema for all log record types.
+ */
+struct guc_bse_stream_t {
+	/* A stream of one or more guc_bse_record_t encodings. */
+	struct guc_bse_record_t bse_records;
+};
+
+/* GuC Schema Binary. This is optional host LFD data. */
+struct guc_lfd_data_binary_schema_t {
+	/*
+	 * Binary schema encoding followed by any byte padding to align to
+	 * dword size. Dword size determined by guc_logfile_lfd_t.desc_dw_size
+	 */
+	struct guc_bse_stream_t binary_schema;
+};
+
+#endif
diff --git a/tools/lfd_default.h b/tools/lfd_default.h
new file mode 100644
index 000000000..aa0d3a834
--- /dev/null
+++ b/tools/lfd_default.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2016-2019 Intel Corporation
+ */
+
+#ifndef _INTEL_GUC_LFD_DEFAULT_H_
+#define _INTEL_GUC_LFD_DEFAULT_H_
+
+#include "lfd.h"
+
+const struct guc_logfile_lfd_t default_guc_logfile_lfd = {
+	.magic = GUC_LOGFILE_LFD_MAGIC,
+};
+
+const struct guc_logfile_t default_guc_logfile = {
+	.magic = LFD_DRIVER_KEY_STREAMING,
+	.version = {
+		.minor_version = GUC_LOG_FILE_FORMAT_VERSION_MINOR,
+		.major_version = GUC_LOG_FILE_FORMAT_VERSION_MAJOR
+	}
+};
+
+const struct guc_lic_guc_devid_t default_guc_lic_guc_devid = {
+	.type = GUC_LOG_LIC_TYPE_GUC_DEVICE_ID,
+	.size = 1,
+	.guc_devid = 0
+};
+
+const struct guc_lic_tsc_freq_t default_guc_lic_tsc_freq = {
+	.type = GUC_LOG_LIC_TYPE_TSC_FREQUENCY,
+	.size = 1,
+	.tsc_freq = 0
+};
+
+const struct guc_lfd_data_log_events_buf_t default_lfd_data_log_events_buf = {
+	.log_events_format_version = GUC_LOG_EVENT_ENTRY_FORMAT_VERSION,
+};
+
+#endif
diff --git a/tools/meson.build b/tools/meson.build
index de866c392..6fadc40fa 100644
--- a/tools/meson.build
+++ b/tools/meson.build
@@ -46,6 +46,7 @@ tools_progs = [
 	'intel_gvtg_test',
 	'dpcd_reg',
 	'lsgpu',
+	'xe_guc_logger',
 ]
 tool_deps = igt_deps
 tool_deps += zlib
diff --git a/tools/xe_guc_logger.c b/tools/xe_guc_logger.c
new file mode 100644
index 000000000..a8b5551dd
--- /dev/null
+++ b/tools/xe_guc_logger.c
@@ -0,0 +1,615 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2016-2019 Intel Corporation
+ */
+
+#include <assert.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <zlib.h>
+#include <arpa/inet.h>
+
+#include "igt.h"
+#include "lfd.h"
+#include "lfd_default.h"
+
+#define PR	printf("@ %s:%d\n", __func__, __LINE__)
+
+#define DEFAULT_FILE_LEN		(256 * 1024 * 1024)
+#define LINE_BUF_SIZE			(16 * 1024 * 1024)
+
+/* Optional Space */
+#define SPC_O				"[ \t]*"
+/* Required Space */
+#define SPC				": "
+/* Optional Non-Space */
+#define NSPC_O				"([^:]*)"
+/* Required Non-Space */
+#define NSPC				"([^:]+)"
+#define BEG				"^" SPC_O
+#define REQ_FIELD			NSPC SPC
+#define OPT_FIELD			NSPC_O SPC_O
+#define END				SPC_O "$"
+
+#define REGEX_NON_SPACE_GROUPS	BEG REQ_FIELD OPT_FIELD OPT_FIELD OPT_FIELD END
+#define REGEX_NON_SPACE_GROUPS_COUNT	4
+
+#define INDEX_KEY			1
+#define INDEX_VALUE			2
+
+#define OS_VERSION_FILENAME		"/proc/version"
+#define DEFAULT_GUC_LOG_FILENAME	"guc_log"
+#define DEFAULT_OUTPUT_FILE_NAME	"guc_log_dump.dat"
+
+static struct guc_log_buffer_entry_list {
+	uint32_t key[2];
+	uint32_t offset;
+	uint32_t rd_ptr;
+	uint32_t wr_ptr;
+	uint32_t buf_size;
+} entry_list[GUC_LOG_BUFFER_STATE_HEADER_ENTRY_COUNT] = {
+	{
+		{
+			LFD_LOG_BUFFER_MARKER_1V2,
+			LFD_LOG_BUFFER_MARKER_2
+		}
+	},
+	{
+		{
+			LFD_LOG_BUFFER_MARKER_1V2,
+			LFD_CRASH_DUMP_BUFFER_MARKER_2
+		}
+	},
+	{
+		{
+			LFD_STATE_CAPTURE_BUFFER_MARKER_1V2,
+			LFD_STATE_CAPTURE_BUFFER_MARKER_2
+		}
+	},
+	{
+		{
+			GUC_LOG_INIT_CONFIG_LIC_MAGIC,
+			((GUC_LOG_INIT_CONFIG_FORMAT_VERSION_MAJOR << 16)
+			 | GUC_LOG_INIT_CONFIG_FORMAT_VERSION_MINOR)
+		}
+	},
+};
+
+regex_t regex;
+int verbose = 0;
+struct guc_sw_version_t fw_ver;
+struct guc_lic_tsc_freq_t fw_tsc_freq;
+struct guc_lic_guc_devid_t fw_dev_id;
+struct guc_log_buffer_state_t log_buf_state;
+char *out_filename = NULL;
+char *guc_log_filename = NULL;
+
+/*
+ * Header example:
+ * **** GuC Log ****
+ * CS reference clock: 19200000
+ * GuC firmware: i915/mtl_guc_70.bin
+ * GuC version: 70.36.0 (wanted 70.29.2)
+ * Kernel timestamp: 0x1DE9CFB690A4 [32890049433764]
+ * GuC timestamp: 0x325FF4AA2 [13522389666]
+ * Log level: 3
+ * [LOG].length: 0x113000
+ * [LOG].data: <ASCII85 DATA>
+ */
+#define TAG_GUC_LOG_START	"**** GuC Log ****"
+
+enum TAG_TYPE {
+	TAG_GUC_FIRMWARE = 0,
+	TAG_GUC_VERSION,
+	TAG_CS_REFERENCE_CLOCK,
+	TAG_KERNEL_TIMESTAMP,
+	TAG_GUC_TIMESTAMP,
+	TAG_LOG_LEVEL,
+	TAG_LOG_LENGTH,
+	TAG_LOG_DATA,
+	TAG_MAX
+};
+
+static const char *tags[TAG_MAX] = {
+	"GuC firmware",
+	"GuC version",
+	"CS reference clock",
+	"Kernel timestamp",
+	"GuC timestamp",
+	"Log level",
+	"[LOG].length",
+	"[LOG].data: " /* This tag only being matched by strcmp, so add ": " */
+};
+
+static char tag_values[TAG_MAX - 1][80];
+
+static int zlib_inflate(uint32_t **ptr, int len)
+{
+	struct z_stream_s zstream;
+	void *out;
+
+	memset(&zstream, 0, sizeof(zstream));
+
+	zstream.next_in = (unsigned char *)*ptr;
+	zstream.avail_in = 4 * len;
+
+	if (inflateInit(&zstream) != Z_OK)
+		return 0;
+
+	out = malloc(128 * 4096); /* approximate obj size */
+	zstream.next_out = out;
+	zstream.avail_out = 128 * 4096;
+
+	do {
+		switch (inflate(&zstream, Z_SYNC_FLUSH)) {
+		case Z_STREAM_END:
+			goto end;
+		case Z_OK:
+			break;
+		default:
+			inflateEnd(&zstream);
+			return 0;
+		}
+
+		if (zstream.avail_out)
+			break;
+
+		out = realloc(out, 2 * zstream.total_out);
+		if (out == NULL) {
+			inflateEnd(&zstream);
+			return 0;
+		}
+
+		zstream.next_out = (unsigned char *)out + zstream.total_out;
+		zstream.avail_out = zstream.total_out;
+	} while (1);
+end:
+	inflateEnd(&zstream);
+	free(*ptr);
+	*ptr = out;
+	return zstream.total_out / 4;
+}
+
+static unsigned long
+ascii85_decode(char *in, uint32_t **out)
+{
+	unsigned long len = 0, size = 1024;
+	bool inflated;
+
+	*out = realloc(*out, sizeof(uint32_t) * size);
+	if (*out == NULL)
+		return 0;
+
+	inflated = (in[0] == ':');
+
+	while (*in >= '!' && *in <= 'z') {
+		uint32_t v = 0;
+
+		if (len == size) {
+			size *= 2;
+			*out = realloc(*out, sizeof(uint32_t) * size);
+			if (*out == NULL)
+				return 0;
+		}
+
+		if (*in == 'z') {
+			in++;
+		} else {
+			v += in[0] - 33; v *= 85;
+			v += in[1] - 33; v *= 85;
+			v += in[2] - 33; v *= 85;
+			v += in[3] - 33; v *= 85;
+			v += in[4] - 33;
+			in += 5;
+		}
+		(*out)[len++] = v;
+	}
+
+	if (!inflated)
+		return len;
+
+	return zlib_inflate(out, len);
+}
+
+static enum TAG_TYPE get_tag_entry(char *line)
+{
+	int i;
+	regmatch_t match[REGEX_NON_SPACE_GROUPS_COUNT];
+
+	if (strncmp(tags[TAG_LOG_DATA], line, strlen(tags[TAG_LOG_DATA])) == 0)
+		return TAG_LOG_DATA;
+
+	if ((regexec(&regex, line, REGEX_NON_SPACE_GROUPS_COUNT, match, 0)) == 0) {
+		char *key = NULL, *value = NULL;
+
+		if (match[INDEX_KEY].rm_so >= 0) {
+			key = &line[match[INDEX_KEY].rm_so];
+			line[match[INDEX_KEY].rm_eo] = '\0';
+		}
+		if (match[INDEX_VALUE].rm_so >= 0) {
+			value = &line[match[INDEX_VALUE].rm_so];
+			line[match[INDEX_VALUE].rm_eo] = '\0';
+		}
+
+		for (i = 0; i < TAG_LOG_DATA; i++) {
+			if (key && value && strcmp(tags[i], key) == 0) {
+				strcpy(tag_values[i], value);
+				return i;
+			}
+		}
+	}
+	return TAG_MAX;
+}
+
+static int load_guc_log(uint32_t **bin_buf)
+{
+	FILE *fd;
+	int log_len = 0;
+	enum TAG_TYPE type;
+	char *fname;
+	char *line =  (char *)malloc(LINE_BUF_SIZE);
+	int i = 0;
+
+	igt_assert_f(line, "Out of memory\n");
+
+	fname = guc_log_filename ? : (char *)DEFAULT_GUC_LOG_FILENAME;
+	if (verbose)
+		igt_info("Loading %s\n", fname);
+
+	fd = fopen(fname, "r");
+	igt_assert_f(fd, "couldn't open the file: %s\n", fname);
+
+	while (fgets(line, LINE_BUF_SIZE, fd)) {
+		if (i == 0) {
+			/* Require line 0 is start tag  */
+			igt_assert_f(!strncmp(line, TAG_GUC_LOG_START,
+					      sizeof(TAG_GUC_LOG_START) - 1),
+				     "Invalid guc log\n");
+		}
+		type = get_tag_entry(line);
+		if (type == TAG_LOG_DATA) {
+			char *encoded = &line[strlen(tags[TAG_LOG_DATA])];
+
+			log_len = ascii85_decode(encoded, bin_buf);
+			if (verbose)
+				igt_info("Ascii85 decoded length: 0x%x\n", log_len);
+		}
+		i++;
+	}
+
+	if (ferror(fd) != 0)
+		printf("Failed to read file, error: %d\n", ferror(fd));
+
+	fclose(fd);
+	free(line);
+	if (guc_log_filename)
+		free(guc_log_filename);
+
+	return log_len;
+}
+
+static int add_lfd_headr(char *buf)
+{
+	int len;
+
+	igt_assert(buf);
+
+	len = sizeof(default_guc_logfile_lfd);
+	memcpy(buf, &default_guc_logfile_lfd, len);
+
+	return len;
+}
+
+static int add_payload(char *buf, uint32_t data_len, void *data)
+{
+	struct guc_logfile_lfd_t *lfd = (struct guc_logfile_lfd_t *)buf;
+
+	igt_assert(buf);
+
+	/* make length DW aligned */
+	lfd->desc_dw_size = data_len / 4;
+	if (data_len % 4)
+		lfd->desc_dw_size++;
+
+	if (lfd->data != data)
+		memcpy(lfd->data, data, data_len);
+
+	return lfd->desc_dw_size * 4;
+}
+
+static int add_typed_payload(char *buf, uint32_t type, uint32_t data_len, void *data)
+{
+	int len;
+	struct guc_logfile_lfd_t *lfd = (struct guc_logfile_lfd_t *)buf;
+
+	igt_assert(buf);
+
+	len = add_lfd_headr(buf);
+	lfd->desc_type = type;
+	len += add_payload(buf, data_len, data);
+
+	return len;
+}
+
+static int add_sw_ver(char *buf)
+{
+	return add_typed_payload(buf, GUC_LFD_TYPE_FW_VERSION,
+				sizeof(fw_ver), &fw_ver);
+}
+
+static int add_guc_device_id(char *buf)
+{
+	return add_typed_payload(buf, GUC_LFD_TYPE_GUC_DEVICE_ID,
+				sizeof(fw_dev_id),
+				(void *)&fw_dev_id);
+}
+
+static int add_os_id(char *buf, uint32_t os_id)
+{
+	int len;
+	FILE *f;
+	char *temp;
+
+	temp = malloc(BUFSIZ);
+	assert(buf);
+
+	/* Get host build version */
+	f = fopen(OS_VERSION_FILENAME, "r");
+	fgets(temp, BUFSIZ, f);
+	fclose(f);
+
+	/* Remove trailing newline */
+	temp[strcspn(temp, "\n\r")] = 0;
+	len = add_typed_payload(buf, GUC_LFD_TYPE_OS_ID, strlen(temp), temp);
+	free(temp);
+
+	return len;
+}
+
+static int add_log_event(char *buf, char *log_bin)
+{
+	int len;
+	char *data;
+	uint32_t data_len;
+	struct guc_lfd_data_log_events_buf_t *events_buf;
+	struct guc_logfile_lfd_t *lfd = (struct guc_logfile_lfd_t *)buf;
+	struct guc_log_buffer_entry_list *entry;
+
+	entry = &entry_list[GUC_LOG_BUFFER_STATE_HEADER_ENTRY_LOG];
+
+	/* Skip empty log */
+	if (entry->rd_ptr == entry->wr_ptr)
+		return 0;
+
+	len = add_lfd_headr(buf);
+	lfd = (struct guc_logfile_lfd_t *)buf;
+
+	lfd->desc_type = GUC_LFD_TYPE_LOG_EVENTS_BUFFER;
+	events_buf = (struct guc_lfd_data_log_events_buf_t *)&lfd->data;
+	events_buf->log_events_format_version = log_buf_state.version;
+
+	data = log_bin + entry->offset + entry->rd_ptr;
+	if (entry->rd_ptr < entry->wr_ptr) {
+		data_len = entry->wr_ptr - entry->rd_ptr;
+		memcpy(events_buf->log_format_buf, data, data_len);
+	} else {
+		/* Copy rd to buf end 1st */
+		data_len = entry->buf_size - entry->rd_ptr;
+		memcpy(events_buf->log_format_buf, data, data_len);
+
+		/* Copy buf start to wr */
+		data = &log_bin[entry->offset];
+		memcpy(&events_buf->log_format_buf[data_len / 4], data, entry->wr_ptr);
+		data_len += entry->wr_ptr;
+	}
+
+	if (verbose)
+		igt_info("log event data length: 0x%x\n", data_len);
+
+	/* make length DW aligned */
+	lfd->desc_dw_size = data_len / 4;
+	if (data_len % 4)
+		lfd->desc_dw_size++;
+
+	/* Addup log_events_format_version size */
+	lfd->desc_dw_size++;
+
+	return len + lfd->desc_dw_size * 4;
+}
+
+static void loop_log_init(struct guc_log_init_config_t *init)
+{
+	int i;
+	struct guc_lic_tsc_freq_t *p;
+
+	assert(init);
+	p = (struct guc_lic_tsc_freq_t *)init->lic_data;
+
+	for (i = 0; i < init->lic_dw_size; i += 2) {
+		switch (p->type) {
+		case GUC_LOG_LIC_TYPE_GUC_SW_VERSION:
+			fw_ver = ((struct guc_lic_guc_sw_version_t *)p)->guc_sw_version;
+			break;
+		case GUC_LOG_LIC_TYPE_TSC_FREQUENCY:
+			fw_dev_id = *(struct guc_lic_guc_devid_t *)p;
+			break;
+		case GUC_LOG_LIC_TYPE_GUC_DEVICE_ID:
+			fw_tsc_freq = *(struct guc_lic_tsc_freq_t *)p;
+			fw_tsc_freq.type = GUC_LOG_LIC_TYPE_TSC_FREQUENCY;
+			break;
+		default:
+			break;
+		}
+		p++;
+	}
+}
+
+static void load_log_buffer(uint32_t *guc_log, int len)
+{
+	int i = 0;
+	uint32_t offset = GUC_LOG_BUFFER_STATE_HEADER_LENGTH;
+	struct guc_log_buffer_state_t *p = (struct guc_log_buffer_state_t *)guc_log;
+	struct guc_log_init_config_t *init;
+
+	assert(guc_log);
+
+	while (p) {
+		for (i = 0; i < GUC_LOG_BUFFER_STATE_HEADER_ENTRY_COUNT; i++) {
+			if (p->Marker[0] == entry_list[i].key[0] &&
+			    p->Marker[1] == entry_list[i].key[1]) {
+				entry_list[i].offset = offset;
+				entry_list[i].rd_ptr = p->log_buf_rd_ptr;
+				entry_list[i].wr_ptr = p->log_guc_wr_ptr;
+				entry_list[i].buf_size = p->log_buf_size;
+
+				if (i == GUC_LOG_BUFFER_STATE_HEADER_ENTRY_LOG)
+					log_buf_state = *p;
+
+				if (i != GUC_LOG_BUFFER_STATE_HEADER_ENTRY_INIT) {
+					offset += p->log_buf_size;
+					p++;
+				} else {
+					int init_size_with_data;
+
+					init = (struct guc_log_init_config_t *)p;
+					/* Load log init config */
+					loop_log_init(init);
+					init_size_with_data = init->lic_dw_size * 4 +
+						sizeof(struct guc_log_init_config_t);
+					offset += init_size_with_data;
+					p = (struct guc_log_buffer_state_t *)
+					     ((char *)p + init_size_with_data);
+				}
+			}
+		}
+		if (i >= GUC_LOG_BUFFER_STATE_HEADER_ENTRY_COUNT)
+			break;
+	}
+}
+
+static void save_output_file(char *buf, uint32_t *guc_log_bin)
+{
+	int ret, index = 0;
+	char *bin = (char *)guc_log_bin;
+	struct guc_logfile_t *guc_logfile;
+	struct guc_log_buffer_entry_list *entry;
+
+	char *fname = out_filename ? : (char *)DEFAULT_OUTPUT_FILE_NAME;
+	int fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0600);
+
+	igt_assert_f(fd >= 0, "couldn't open the output file\n");
+
+	if (verbose)
+		igt_info("Creating binary file: %s\n", fname);
+
+	guc_logfile = (struct guc_logfile_t *)buf;
+	*guc_logfile = default_guc_logfile;
+
+	index = offsetof(struct guc_logfile_t, lfd_stream);
+	index += add_sw_ver(&buf[index]);
+	index += add_guc_device_id(&buf[index]);
+	index += add_typed_payload(&buf[index], GUC_LFD_TYPE_TSC_FREQUENCY,
+				   sizeof(fw_tsc_freq), (void *)&fw_tsc_freq);
+	index += add_os_id(&buf[index], GUC_LFD_OS_TYPE_OSID_LIN);
+	index += add_log_event(&buf[index], bin);
+
+	/* For Crash dump, rd/wr ptr has no effect, do simple payload add */
+	entry = &entry_list[GUC_LOG_BUFFER_STATE_HEADER_ENTRY_CRASH];
+	if (entry->buf_size) {
+		index += add_typed_payload(&buf[index], GUC_LFD_TYPE_FW_CRASH_DUMP,
+					entry->buf_size, &bin[entry->offset]);
+	}
+
+	ret = write(fd, buf, index);
+	if (ret != index)
+		igt_info("Couldn't save output file. errno: %d\n", errno);
+
+	close(fd);
+
+	if (verbose)
+		igt_info("Binary file saved.\n");
+
+	if (out_filename)
+		free(out_filename);
+}
+
+static int parse_options(int opt, int opt_index, void *data)
+{
+	switch (opt) {
+	case 'i':
+		guc_log_filename = strdup(optarg);
+		igt_assert_f(guc_log_filename, "Couldn't allocate the input filename\n");
+		igt_debug("logs to be read %s\n", guc_log_filename);
+		break;
+	case 'o':
+		out_filename = strdup(optarg);
+		igt_assert_f(out_filename, "Couldn't allocate the output filename\n");
+		igt_debug("logs to be stored in file %s\n", out_filename);
+		break;
+	case 'v':
+		verbose = 1;
+		break;
+	}
+
+	return IGT_OPT_HANDLER_SUCCESS;
+}
+
+static void process_command_line(int argc, char **argv)
+{
+	static struct option long_options[] = {
+		{"outputfile", required_argument, 0, 'o'},
+		{"inputfile", required_argument, 0, 'i'},
+		{"verbosity", optional_argument, 0, 'v'},
+		{ 0, 0, 0, 0 }
+	};
+
+	const char *help =
+		"  -i --inputfile=name\tname of the guc log file, including the path\n"
+		"  -o --outputfile=name\tname of the output file, including the location, where logs will be stored\n"
+		"  -v --verbosity=level   verbosity level of output\n";
+
+		igt_simple_init_parse_opts(&argc, argv, "o:i:v", long_options,
+					   help, parse_options, NULL);
+}
+
+int main(int argc, char **argv)
+{
+	int decode_len;
+	char *buf;
+	uint32_t *guc_log_bin = NULL;
+
+	buf = malloc(DEFAULT_FILE_LEN);
+	assert(buf);
+	memset(buf, 0, DEFAULT_FILE_LEN);
+	guc_log_bin = NULL;
+
+	regcomp(&regex, REGEX_NON_SPACE_GROUPS, REG_EXTENDED | REG_NEWLINE);
+
+	process_command_line(argc, argv);
+
+	/* guc_log_bin will be alloc by load_guc_log/ascii85_decode */
+	decode_len = load_guc_log(&guc_log_bin);
+
+	/* Only keep hex value part for timestamp string */
+	tag_values[TAG_KERNEL_TIMESTAMP][strcspn(tag_values[TAG_KERNEL_TIMESTAMP], " ")] = 0;
+	tag_values[TAG_GUC_TIMESTAMP][strcspn(tag_values[TAG_GUC_TIMESTAMP], " ")] = 0;
+
+	load_log_buffer(guc_log_bin, decode_len);
+
+	save_output_file(buf, guc_log_bin);
+
+	free(buf);
+	free(guc_log_bin);
+	regfree(&regex);
+	igt_exit();
+
+	return 0;
+}
+
-- 
2.34.1



More information about the igt-dev mailing list