19c8c7a7eSZhanjun Dong // SPDX-License-Identifier: MIT 29c8c7a7eSZhanjun Dong /* 39c8c7a7eSZhanjun Dong * Copyright © 2021-2024 Intel Corporation 49c8c7a7eSZhanjun Dong */ 59c8c7a7eSZhanjun Dong 69c8c7a7eSZhanjun Dong #include <linux/types.h> 79c8c7a7eSZhanjun Dong 89c8c7a7eSZhanjun Dong #include <drm/drm_managed.h> 99c8c7a7eSZhanjun Dong #include <drm/drm_print.h> 109c8c7a7eSZhanjun Dong 119c8c7a7eSZhanjun Dong #include "abi/guc_actions_abi.h" 129c8c7a7eSZhanjun Dong #include "abi/guc_capture_abi.h" 13*8bfc4963SZhanjun Dong #include "abi/guc_log_abi.h" 149c8c7a7eSZhanjun Dong #include "regs/xe_engine_regs.h" 159c8c7a7eSZhanjun Dong #include "regs/xe_gt_regs.h" 169c8c7a7eSZhanjun Dong #include "regs/xe_guc_regs.h" 179c8c7a7eSZhanjun Dong #include "regs/xe_regs.h" 189c8c7a7eSZhanjun Dong 199c8c7a7eSZhanjun Dong #include "xe_bo.h" 209c8c7a7eSZhanjun Dong #include "xe_device.h" 219c8c7a7eSZhanjun Dong #include "xe_exec_queue_types.h" 229c8c7a7eSZhanjun Dong #include "xe_gt.h" 239c8c7a7eSZhanjun Dong #include "xe_gt_mcr.h" 249c8c7a7eSZhanjun Dong #include "xe_gt_printk.h" 259c8c7a7eSZhanjun Dong #include "xe_guc.h" 2684d15f42SZhanjun Dong #include "xe_guc_ads.h" 279c8c7a7eSZhanjun Dong #include "xe_guc_capture.h" 289c8c7a7eSZhanjun Dong #include "xe_guc_capture_types.h" 299c8c7a7eSZhanjun Dong #include "xe_guc_ct.h" 309c8c7a7eSZhanjun Dong #include "xe_guc_log.h" 319c8c7a7eSZhanjun Dong #include "xe_guc_submit.h" 329c8c7a7eSZhanjun Dong #include "xe_hw_engine_types.h" 339c8c7a7eSZhanjun Dong #include "xe_macros.h" 349c8c7a7eSZhanjun Dong #include "xe_map.h" 359c8c7a7eSZhanjun Dong 369c8c7a7eSZhanjun Dong /* 37*8bfc4963SZhanjun Dong * struct __guc_capture_bufstate 38*8bfc4963SZhanjun Dong * 39*8bfc4963SZhanjun Dong * Book-keeping structure used to track read and write pointers 40*8bfc4963SZhanjun Dong * as we extract error capture data from the GuC-log-buffer's 41*8bfc4963SZhanjun Dong * error-capture region as a stream of dwords. 42*8bfc4963SZhanjun Dong */ 43*8bfc4963SZhanjun Dong struct __guc_capture_bufstate { 44*8bfc4963SZhanjun Dong u32 size; 45*8bfc4963SZhanjun Dong u32 data_offset; 46*8bfc4963SZhanjun Dong u32 rd; 47*8bfc4963SZhanjun Dong u32 wr; 48*8bfc4963SZhanjun Dong }; 49*8bfc4963SZhanjun Dong 50*8bfc4963SZhanjun Dong /* 51*8bfc4963SZhanjun Dong * struct __guc_capture_parsed_output - extracted error capture node 52*8bfc4963SZhanjun Dong * 53*8bfc4963SZhanjun Dong * A single unit of extracted error-capture output data grouped together 54*8bfc4963SZhanjun Dong * at an engine-instance level. We keep these nodes in a linked list. 55*8bfc4963SZhanjun Dong * See cachelist and outlist below. 56*8bfc4963SZhanjun Dong */ 57*8bfc4963SZhanjun Dong struct __guc_capture_parsed_output { 58*8bfc4963SZhanjun Dong /* 59*8bfc4963SZhanjun Dong * A single set of 3 capture lists: a global-list 60*8bfc4963SZhanjun Dong * an engine-class-list and an engine-instance list. 61*8bfc4963SZhanjun Dong * outlist in __guc_capture_parsed_output will keep 62*8bfc4963SZhanjun Dong * a linked list of these nodes that will eventually 63*8bfc4963SZhanjun Dong * be detached from outlist and attached into to 64*8bfc4963SZhanjun Dong * xe_codedump in response to a context reset 65*8bfc4963SZhanjun Dong */ 66*8bfc4963SZhanjun Dong struct list_head link; 67*8bfc4963SZhanjun Dong bool is_partial; 68*8bfc4963SZhanjun Dong u32 eng_class; 69*8bfc4963SZhanjun Dong u32 eng_inst; 70*8bfc4963SZhanjun Dong u32 guc_id; 71*8bfc4963SZhanjun Dong u32 lrca; 72*8bfc4963SZhanjun Dong struct gcap_reg_list_info { 73*8bfc4963SZhanjun Dong u32 vfid; 74*8bfc4963SZhanjun Dong u32 num_regs; 75*8bfc4963SZhanjun Dong struct guc_mmio_reg *regs; 76*8bfc4963SZhanjun Dong } reginfo[GUC_STATE_CAPTURE_TYPE_MAX]; 77*8bfc4963SZhanjun Dong #define GCAP_PARSED_REGLIST_INDEX_GLOBAL BIT(GUC_STATE_CAPTURE_TYPE_GLOBAL) 78*8bfc4963SZhanjun Dong #define GCAP_PARSED_REGLIST_INDEX_ENGCLASS BIT(GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS) 79*8bfc4963SZhanjun Dong }; 80*8bfc4963SZhanjun Dong 81*8bfc4963SZhanjun Dong /* 829c8c7a7eSZhanjun Dong * Define all device tables of GuC error capture register lists 839c8c7a7eSZhanjun Dong * NOTE: 849c8c7a7eSZhanjun Dong * For engine-registers, GuC only needs the register offsets 859c8c7a7eSZhanjun Dong * from the engine-mmio-base 869c8c7a7eSZhanjun Dong * 879c8c7a7eSZhanjun Dong * 64 bit registers need 2 entries for low 32 bit register and high 32 bit 889c8c7a7eSZhanjun Dong * register, for example: 899c8c7a7eSZhanjun Dong * Register data_type flags mask Register name 909c8c7a7eSZhanjun Dong * { XXX_REG_LO(0), REG_64BIT_LOW_DW, 0, 0, NULL}, 919c8c7a7eSZhanjun Dong * { XXX_REG_HI(0), REG_64BIT_HI_DW,, 0, 0, "XXX_REG"}, 929c8c7a7eSZhanjun Dong * 1. data_type: Indicate is hi/low 32 bit for a 64 bit register 939c8c7a7eSZhanjun Dong * A 64 bit register define requires 2 consecutive entries, 949c8c7a7eSZhanjun Dong * with low dword first and hi dword the second. 959c8c7a7eSZhanjun Dong * 2. Register name: null for incompleted define 969c8c7a7eSZhanjun Dong */ 979c8c7a7eSZhanjun Dong #define COMMON_XELP_BASE_GLOBAL \ 989c8c7a7eSZhanjun Dong { FORCEWAKE_GT, REG_32BIT, 0, 0, "FORCEWAKE_GT"} 999c8c7a7eSZhanjun Dong 1009c8c7a7eSZhanjun Dong #define COMMON_BASE_ENGINE_INSTANCE \ 1019c8c7a7eSZhanjun Dong { RING_HWSTAM(0), REG_32BIT, 0, 0, "HWSTAM"}, \ 1029c8c7a7eSZhanjun Dong { RING_HWS_PGA(0), REG_32BIT, 0, 0, "RING_HWS_PGA"}, \ 1039c8c7a7eSZhanjun Dong { RING_HEAD(0), REG_32BIT, 0, 0, "RING_HEAD"}, \ 1049c8c7a7eSZhanjun Dong { RING_TAIL(0), REG_32BIT, 0, 0, "RING_TAIL"}, \ 1059c8c7a7eSZhanjun Dong { RING_CTL(0), REG_32BIT, 0, 0, "RING_CTL"}, \ 1069c8c7a7eSZhanjun Dong { RING_MI_MODE(0), REG_32BIT, 0, 0, "RING_MI_MODE"}, \ 1079c8c7a7eSZhanjun Dong { RING_MODE(0), REG_32BIT, 0, 0, "RING_MODE"}, \ 1089c8c7a7eSZhanjun Dong { RING_ESR(0), REG_32BIT, 0, 0, "RING_ESR"}, \ 1099c8c7a7eSZhanjun Dong { RING_EMR(0), REG_32BIT, 0, 0, "RING_EMR"}, \ 1109c8c7a7eSZhanjun Dong { RING_EIR(0), REG_32BIT, 0, 0, "RING_EIR"}, \ 1119c8c7a7eSZhanjun Dong { RING_IMR(0), REG_32BIT, 0, 0, "RING_IMR"}, \ 1129c8c7a7eSZhanjun Dong { RING_IPEHR(0), REG_32BIT, 0, 0, "IPEHR"}, \ 1139c8c7a7eSZhanjun Dong { RING_INSTDONE(0), REG_32BIT, 0, 0, "RING_INSTDONE"}, \ 1149c8c7a7eSZhanjun Dong { INDIRECT_RING_STATE(0), REG_32BIT, 0, 0, "INDIRECT_RING_STATE"}, \ 1159c8c7a7eSZhanjun Dong { RING_ACTHD(0), REG_64BIT_LOW_DW, 0, 0, NULL}, \ 1169c8c7a7eSZhanjun Dong { RING_ACTHD_UDW(0), REG_64BIT_HI_DW, 0, 0, "ACTHD"}, \ 1179c8c7a7eSZhanjun Dong { RING_BBADDR(0), REG_64BIT_LOW_DW, 0, 0, NULL}, \ 1189c8c7a7eSZhanjun Dong { RING_BBADDR_UDW(0), REG_64BIT_HI_DW, 0, 0, "RING_BBADDR"}, \ 1199c8c7a7eSZhanjun Dong { RING_START(0), REG_64BIT_LOW_DW, 0, 0, NULL}, \ 1209c8c7a7eSZhanjun Dong { RING_START_UDW(0), REG_64BIT_HI_DW, 0, 0, "RING_START"}, \ 1219c8c7a7eSZhanjun Dong { RING_DMA_FADD(0), REG_64BIT_LOW_DW, 0, 0, NULL}, \ 1229c8c7a7eSZhanjun Dong { RING_DMA_FADD_UDW(0), REG_64BIT_HI_DW, 0, 0, "RING_DMA_FADD"}, \ 1239c8c7a7eSZhanjun Dong { RING_EXECLIST_STATUS_LO(0), REG_64BIT_LOW_DW, 0, 0, NULL}, \ 1249c8c7a7eSZhanjun Dong { RING_EXECLIST_STATUS_HI(0), REG_64BIT_HI_DW, 0, 0, "RING_EXECLIST_STATUS"}, \ 1259c8c7a7eSZhanjun Dong { RING_EXECLIST_SQ_CONTENTS_LO(0), REG_64BIT_LOW_DW, 0, 0, NULL}, \ 1269c8c7a7eSZhanjun Dong { RING_EXECLIST_SQ_CONTENTS_HI(0), REG_64BIT_HI_DW, 0, 0, "RING_EXECLIST_SQ_CONTENTS"} 1279c8c7a7eSZhanjun Dong 1289c8c7a7eSZhanjun Dong #define COMMON_XELP_RC_CLASS \ 1299c8c7a7eSZhanjun Dong { RCU_MODE, REG_32BIT, 0, 0, "RCU_MODE"} 1309c8c7a7eSZhanjun Dong 1319c8c7a7eSZhanjun Dong #define COMMON_XELP_RC_CLASS_INSTDONE \ 1329c8c7a7eSZhanjun Dong { SC_INSTDONE, REG_32BIT, 0, 0, "SC_INSTDONE"}, \ 1339c8c7a7eSZhanjun Dong { SC_INSTDONE_EXTRA, REG_32BIT, 0, 0, "SC_INSTDONE_EXTRA"}, \ 1349c8c7a7eSZhanjun Dong { SC_INSTDONE_EXTRA2, REG_32BIT, 0, 0, "SC_INSTDONE_EXTRA2"} 1359c8c7a7eSZhanjun Dong 1369c8c7a7eSZhanjun Dong #define XELP_VEC_CLASS_REGS \ 1379c8c7a7eSZhanjun Dong { SFC_DONE(0), 0, 0, 0, "SFC_DONE[0]"}, \ 1389c8c7a7eSZhanjun Dong { SFC_DONE(1), 0, 0, 0, "SFC_DONE[1]"}, \ 1399c8c7a7eSZhanjun Dong { SFC_DONE(2), 0, 0, 0, "SFC_DONE[2]"}, \ 1409c8c7a7eSZhanjun Dong { SFC_DONE(3), 0, 0, 0, "SFC_DONE[3]"} 1419c8c7a7eSZhanjun Dong 1429c8c7a7eSZhanjun Dong /* XE_LP Global */ 1439c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr xe_lp_global_regs[] = { 1449c8c7a7eSZhanjun Dong COMMON_XELP_BASE_GLOBAL, 1459c8c7a7eSZhanjun Dong }; 1469c8c7a7eSZhanjun Dong 1479c8c7a7eSZhanjun Dong /* Render / Compute Per-Engine-Instance */ 1489c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr xe_rc_inst_regs[] = { 1499c8c7a7eSZhanjun Dong COMMON_BASE_ENGINE_INSTANCE, 1509c8c7a7eSZhanjun Dong }; 1519c8c7a7eSZhanjun Dong 1529c8c7a7eSZhanjun Dong /* Render / Compute Engine-Class */ 1539c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr xe_rc_class_regs[] = { 1549c8c7a7eSZhanjun Dong COMMON_XELP_RC_CLASS, 1559c8c7a7eSZhanjun Dong COMMON_XELP_RC_CLASS_INSTDONE, 1569c8c7a7eSZhanjun Dong }; 1579c8c7a7eSZhanjun Dong 1589c8c7a7eSZhanjun Dong /* Render / Compute Engine-Class for xehpg */ 1599c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr xe_hpg_rc_class_regs[] = { 1609c8c7a7eSZhanjun Dong COMMON_XELP_RC_CLASS, 1619c8c7a7eSZhanjun Dong }; 1629c8c7a7eSZhanjun Dong 1639c8c7a7eSZhanjun Dong /* Media Decode/Encode Per-Engine-Instance */ 1649c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr xe_vd_inst_regs[] = { 1659c8c7a7eSZhanjun Dong COMMON_BASE_ENGINE_INSTANCE, 1669c8c7a7eSZhanjun Dong }; 1679c8c7a7eSZhanjun Dong 1689c8c7a7eSZhanjun Dong /* Video Enhancement Engine-Class */ 1699c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr xe_vec_class_regs[] = { 1709c8c7a7eSZhanjun Dong XELP_VEC_CLASS_REGS, 1719c8c7a7eSZhanjun Dong }; 1729c8c7a7eSZhanjun Dong 1739c8c7a7eSZhanjun Dong /* Video Enhancement Per-Engine-Instance */ 1749c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr xe_vec_inst_regs[] = { 1759c8c7a7eSZhanjun Dong COMMON_BASE_ENGINE_INSTANCE, 1769c8c7a7eSZhanjun Dong }; 1779c8c7a7eSZhanjun Dong 1789c8c7a7eSZhanjun Dong /* Blitter Per-Engine-Instance */ 1799c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr xe_blt_inst_regs[] = { 1809c8c7a7eSZhanjun Dong COMMON_BASE_ENGINE_INSTANCE, 1819c8c7a7eSZhanjun Dong }; 1829c8c7a7eSZhanjun Dong 1839c8c7a7eSZhanjun Dong /* XE_LP - GSC Per-Engine-Instance */ 1849c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr xe_lp_gsc_inst_regs[] = { 1859c8c7a7eSZhanjun Dong COMMON_BASE_ENGINE_INSTANCE, 1869c8c7a7eSZhanjun Dong }; 1879c8c7a7eSZhanjun Dong 1889c8c7a7eSZhanjun Dong /* 1899c8c7a7eSZhanjun Dong * Empty list to prevent warnings about unknown class/instance types 1909c8c7a7eSZhanjun Dong * as not all class/instance types have entries on all platforms. 1919c8c7a7eSZhanjun Dong */ 1929c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr empty_regs_list[] = { 1939c8c7a7eSZhanjun Dong }; 1949c8c7a7eSZhanjun Dong 1959c8c7a7eSZhanjun Dong #define TO_GCAP_DEF_OWNER(x) (GUC_CAPTURE_LIST_INDEX_##x) 1969c8c7a7eSZhanjun Dong #define TO_GCAP_DEF_TYPE(x) (GUC_STATE_CAPTURE_TYPE_##x) 1979c8c7a7eSZhanjun Dong #define MAKE_REGLIST(regslist, regsowner, regstype, class) \ 1989c8c7a7eSZhanjun Dong { \ 1999c8c7a7eSZhanjun Dong regslist, \ 2009c8c7a7eSZhanjun Dong ARRAY_SIZE(regslist), \ 2019c8c7a7eSZhanjun Dong TO_GCAP_DEF_OWNER(regsowner), \ 2029c8c7a7eSZhanjun Dong TO_GCAP_DEF_TYPE(regstype), \ 2039c8c7a7eSZhanjun Dong class \ 2049c8c7a7eSZhanjun Dong } 2059c8c7a7eSZhanjun Dong 2069c8c7a7eSZhanjun Dong /* List of lists for legacy graphic product version < 1255 */ 2079c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr_group xe_lp_lists[] = { 2089c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_lp_global_regs, PF, GLOBAL, 0), 2099c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_rc_class_regs, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE), 2109c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_rc_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE), 2119c8c7a7eSZhanjun Dong MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_VIDEO), 2129c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_vd_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_VIDEO), 2139c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_vec_class_regs, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE), 2149c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_vec_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE), 2159c8c7a7eSZhanjun Dong MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_BLITTER), 2169c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_blt_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_BLITTER), 2179c8c7a7eSZhanjun Dong MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_GSC_OTHER), 2189c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_lp_gsc_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_GSC_OTHER), 2199c8c7a7eSZhanjun Dong {} 2209c8c7a7eSZhanjun Dong }; 2219c8c7a7eSZhanjun Dong 2229c8c7a7eSZhanjun Dong /* List of lists for graphic product version >= 1255 */ 2239c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr_group xe_hpg_lists[] = { 2249c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_lp_global_regs, PF, GLOBAL, 0), 2259c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_hpg_rc_class_regs, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE), 2269c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_rc_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE), 2279c8c7a7eSZhanjun Dong MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_VIDEO), 2289c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_vd_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_VIDEO), 2299c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_vec_class_regs, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE), 2309c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_vec_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_VIDEOENHANCE), 2319c8c7a7eSZhanjun Dong MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_BLITTER), 2329c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_blt_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_BLITTER), 2339c8c7a7eSZhanjun Dong MAKE_REGLIST(empty_regs_list, PF, ENGINE_CLASS, GUC_CAPTURE_LIST_CLASS_GSC_OTHER), 2349c8c7a7eSZhanjun Dong MAKE_REGLIST(xe_lp_gsc_inst_regs, PF, ENGINE_INSTANCE, GUC_CAPTURE_LIST_CLASS_GSC_OTHER), 2359c8c7a7eSZhanjun Dong {} 2369c8c7a7eSZhanjun Dong }; 2379c8c7a7eSZhanjun Dong 2389c8c7a7eSZhanjun Dong static const char * const capture_list_type_names[] = { 2399c8c7a7eSZhanjun Dong "Global", 2409c8c7a7eSZhanjun Dong "Class", 2419c8c7a7eSZhanjun Dong "Instance", 2429c8c7a7eSZhanjun Dong }; 2439c8c7a7eSZhanjun Dong 2449c8c7a7eSZhanjun Dong static const char * const capture_engine_class_names[] = { 2459c8c7a7eSZhanjun Dong "Render/Compute", 2469c8c7a7eSZhanjun Dong "Video", 2479c8c7a7eSZhanjun Dong "VideoEnhance", 2489c8c7a7eSZhanjun Dong "Blitter", 2499c8c7a7eSZhanjun Dong "GSC-Other", 2509c8c7a7eSZhanjun Dong }; 2519c8c7a7eSZhanjun Dong 2529c8c7a7eSZhanjun Dong struct __guc_capture_ads_cache { 2539c8c7a7eSZhanjun Dong bool is_valid; 2549c8c7a7eSZhanjun Dong void *ptr; 2559c8c7a7eSZhanjun Dong size_t size; 2569c8c7a7eSZhanjun Dong int status; 2579c8c7a7eSZhanjun Dong }; 2589c8c7a7eSZhanjun Dong 2599c8c7a7eSZhanjun Dong struct xe_guc_state_capture { 2609c8c7a7eSZhanjun Dong const struct __guc_mmio_reg_descr_group *reglists; 261b170d696SZhanjun Dong /** 262b170d696SZhanjun Dong * NOTE: steered registers have multiple instances depending on the HW configuration 263b170d696SZhanjun Dong * (slices or dual-sub-slices) and thus depends on HW fuses discovered 264b170d696SZhanjun Dong */ 265b170d696SZhanjun Dong struct __guc_mmio_reg_descr_group *extlists; 2669c8c7a7eSZhanjun Dong struct __guc_capture_ads_cache ads_cache[GUC_CAPTURE_LIST_INDEX_MAX] 2679c8c7a7eSZhanjun Dong [GUC_STATE_CAPTURE_TYPE_MAX] 2689c8c7a7eSZhanjun Dong [GUC_CAPTURE_LIST_CLASS_MAX]; 2699c8c7a7eSZhanjun Dong void *ads_null_cache; 270*8bfc4963SZhanjun Dong struct list_head cachelist; 271*8bfc4963SZhanjun Dong #define PREALLOC_NODES_MAX_COUNT (3 * GUC_MAX_ENGINE_CLASSES * GUC_MAX_INSTANCES_PER_CLASS) 272*8bfc4963SZhanjun Dong #define PREALLOC_NODES_DEFAULT_NUMREGS 64 273*8bfc4963SZhanjun Dong 274*8bfc4963SZhanjun Dong int max_mmio_per_node; 275*8bfc4963SZhanjun Dong struct list_head outlist; 2769c8c7a7eSZhanjun Dong }; 2779c8c7a7eSZhanjun Dong 2789c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr_group * 2799c8c7a7eSZhanjun Dong guc_capture_get_device_reglist(struct xe_device *xe) 2809c8c7a7eSZhanjun Dong { 2819c8c7a7eSZhanjun Dong if (GRAPHICS_VERx100(xe) >= 1255) 2829c8c7a7eSZhanjun Dong return xe_hpg_lists; 2839c8c7a7eSZhanjun Dong else 2849c8c7a7eSZhanjun Dong return xe_lp_lists; 2859c8c7a7eSZhanjun Dong } 2869c8c7a7eSZhanjun Dong 2879c8c7a7eSZhanjun Dong static const struct __guc_mmio_reg_descr_group * 2889c8c7a7eSZhanjun Dong guc_capture_get_one_list(const struct __guc_mmio_reg_descr_group *reglists, 2899c8c7a7eSZhanjun Dong u32 owner, u32 type, enum guc_capture_list_class_type capture_class) 2909c8c7a7eSZhanjun Dong { 2919c8c7a7eSZhanjun Dong int i; 2929c8c7a7eSZhanjun Dong 2939c8c7a7eSZhanjun Dong if (!reglists) 2949c8c7a7eSZhanjun Dong return NULL; 2959c8c7a7eSZhanjun Dong 2969c8c7a7eSZhanjun Dong for (i = 0; reglists[i].list; ++i) { 2979c8c7a7eSZhanjun Dong if (reglists[i].owner == owner && reglists[i].type == type && 2989c8c7a7eSZhanjun Dong (reglists[i].engine == capture_class || 2999c8c7a7eSZhanjun Dong reglists[i].type == GUC_STATE_CAPTURE_TYPE_GLOBAL)) 3009c8c7a7eSZhanjun Dong return ®lists[i]; 3019c8c7a7eSZhanjun Dong } 3029c8c7a7eSZhanjun Dong 3039c8c7a7eSZhanjun Dong return NULL; 3049c8c7a7eSZhanjun Dong } 3059c8c7a7eSZhanjun Dong 306b170d696SZhanjun Dong struct __ext_steer_reg { 307b170d696SZhanjun Dong const char *name; 308b170d696SZhanjun Dong struct xe_reg_mcr reg; 309b170d696SZhanjun Dong }; 310b170d696SZhanjun Dong 311b170d696SZhanjun Dong static const struct __ext_steer_reg xe_extregs[] = { 312b170d696SZhanjun Dong {"SAMPLER_INSTDONE", SAMPLER_INSTDONE}, 313b170d696SZhanjun Dong {"ROW_INSTDONE", ROW_INSTDONE} 314b170d696SZhanjun Dong }; 315b170d696SZhanjun Dong 316b170d696SZhanjun Dong static const struct __ext_steer_reg xehpg_extregs[] = { 317b170d696SZhanjun Dong {"SC_INSTDONE", XEHPG_SC_INSTDONE}, 318b170d696SZhanjun Dong {"SC_INSTDONE_EXTRA", XEHPG_SC_INSTDONE_EXTRA}, 319b170d696SZhanjun Dong {"SC_INSTDONE_EXTRA2", XEHPG_SC_INSTDONE_EXTRA2}, 320b170d696SZhanjun Dong {"INSTDONE_GEOM_SVGUNIT", XEHPG_INSTDONE_GEOM_SVGUNIT} 321b170d696SZhanjun Dong }; 322b170d696SZhanjun Dong 323b170d696SZhanjun Dong static void __fill_ext_reg(struct __guc_mmio_reg_descr *ext, 324b170d696SZhanjun Dong const struct __ext_steer_reg *extlist, 325b170d696SZhanjun Dong int slice_id, int subslice_id) 326b170d696SZhanjun Dong { 327b170d696SZhanjun Dong if (!ext || !extlist) 328b170d696SZhanjun Dong return; 329b170d696SZhanjun Dong 330b170d696SZhanjun Dong ext->reg = XE_REG(extlist->reg.__reg.addr); 331b170d696SZhanjun Dong ext->flags = FIELD_PREP(GUC_REGSET_STEERING_NEEDED, 1); 332b170d696SZhanjun Dong ext->flags = FIELD_PREP(GUC_REGSET_STEERING_GROUP, slice_id); 333b170d696SZhanjun Dong ext->flags |= FIELD_PREP(GUC_REGSET_STEERING_INSTANCE, subslice_id); 334b170d696SZhanjun Dong ext->regname = extlist->name; 335b170d696SZhanjun Dong } 336b170d696SZhanjun Dong 337b170d696SZhanjun Dong static int 338b170d696SZhanjun Dong __alloc_ext_regs(struct drm_device *drm, struct __guc_mmio_reg_descr_group *newlist, 339b170d696SZhanjun Dong const struct __guc_mmio_reg_descr_group *rootlist, int num_regs) 340b170d696SZhanjun Dong { 341b170d696SZhanjun Dong struct __guc_mmio_reg_descr *list; 342b170d696SZhanjun Dong 343b170d696SZhanjun Dong list = drmm_kzalloc(drm, num_regs * sizeof(struct __guc_mmio_reg_descr), GFP_KERNEL); 344b170d696SZhanjun Dong if (!list) 345b170d696SZhanjun Dong return -ENOMEM; 346b170d696SZhanjun Dong 347b170d696SZhanjun Dong newlist->list = list; 348b170d696SZhanjun Dong newlist->num_regs = num_regs; 349b170d696SZhanjun Dong newlist->owner = rootlist->owner; 350b170d696SZhanjun Dong newlist->engine = rootlist->engine; 351b170d696SZhanjun Dong newlist->type = rootlist->type; 352b170d696SZhanjun Dong 353b170d696SZhanjun Dong return 0; 354b170d696SZhanjun Dong } 355b170d696SZhanjun Dong 356b170d696SZhanjun Dong static int guc_capture_get_steer_reg_num(struct xe_device *xe) 357b170d696SZhanjun Dong { 358b170d696SZhanjun Dong int num = ARRAY_SIZE(xe_extregs); 359b170d696SZhanjun Dong 360b170d696SZhanjun Dong if (GRAPHICS_VERx100(xe) >= 1255) 361b170d696SZhanjun Dong num += ARRAY_SIZE(xehpg_extregs); 362b170d696SZhanjun Dong 363b170d696SZhanjun Dong return num; 364b170d696SZhanjun Dong } 365b170d696SZhanjun Dong 366b170d696SZhanjun Dong static void guc_capture_alloc_steered_lists(struct xe_guc *guc) 367b170d696SZhanjun Dong { 368b170d696SZhanjun Dong struct xe_gt *gt = guc_to_gt(guc); 369b170d696SZhanjun Dong u16 slice, subslice; 370b170d696SZhanjun Dong int iter, i, total = 0; 371b170d696SZhanjun Dong const struct __guc_mmio_reg_descr_group *lists = guc->capture->reglists; 372b170d696SZhanjun Dong const struct __guc_mmio_reg_descr_group *list; 373b170d696SZhanjun Dong struct __guc_mmio_reg_descr_group *extlists; 374b170d696SZhanjun Dong struct __guc_mmio_reg_descr *extarray; 375b170d696SZhanjun Dong bool has_xehpg_extregs = GRAPHICS_VERx100(gt_to_xe(gt)) >= 1255; 376b170d696SZhanjun Dong struct drm_device *drm = >_to_xe(gt)->drm; 377b170d696SZhanjun Dong bool has_rcs_ccs = false; 378b170d696SZhanjun Dong struct xe_hw_engine *hwe; 379b170d696SZhanjun Dong enum xe_hw_engine_id id; 380b170d696SZhanjun Dong 381b170d696SZhanjun Dong /* 382b170d696SZhanjun Dong * If GT has no rcs/ccs, no need to alloc steered list. 383b170d696SZhanjun Dong * Currently, only rcs/ccs has steering register, if in the future, 384b170d696SZhanjun Dong * other engine types has steering register, this condition check need 385b170d696SZhanjun Dong * to be extended 386b170d696SZhanjun Dong */ 387b170d696SZhanjun Dong for_each_hw_engine(hwe, gt, id) { 388b170d696SZhanjun Dong if (xe_engine_class_to_guc_capture_class(hwe->class) == 389b170d696SZhanjun Dong GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE) { 390b170d696SZhanjun Dong has_rcs_ccs = true; 391b170d696SZhanjun Dong break; 392b170d696SZhanjun Dong } 393b170d696SZhanjun Dong } 394b170d696SZhanjun Dong 395b170d696SZhanjun Dong if (!has_rcs_ccs) 396b170d696SZhanjun Dong return; 397b170d696SZhanjun Dong 398b170d696SZhanjun Dong /* steered registers currently only exist for the render-class */ 399b170d696SZhanjun Dong list = guc_capture_get_one_list(lists, GUC_CAPTURE_LIST_INDEX_PF, 400b170d696SZhanjun Dong GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS, 401b170d696SZhanjun Dong GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE); 402b170d696SZhanjun Dong /* 403b170d696SZhanjun Dong * Skip if this platform has no engine class registers or if extlists 404b170d696SZhanjun Dong * was previously allocated 405b170d696SZhanjun Dong */ 406b170d696SZhanjun Dong if (!list || guc->capture->extlists) 407b170d696SZhanjun Dong return; 408b170d696SZhanjun Dong 409b170d696SZhanjun Dong total = bitmap_weight(gt->fuse_topo.g_dss_mask, sizeof(gt->fuse_topo.g_dss_mask) * 8) * 410b170d696SZhanjun Dong guc_capture_get_steer_reg_num(guc_to_xe(guc)); 411b170d696SZhanjun Dong 412b170d696SZhanjun Dong if (!total) 413b170d696SZhanjun Dong return; 414b170d696SZhanjun Dong 415b170d696SZhanjun Dong /* allocate an extra for an end marker */ 416b170d696SZhanjun Dong extlists = drmm_kzalloc(drm, 2 * sizeof(struct __guc_mmio_reg_descr_group), GFP_KERNEL); 417b170d696SZhanjun Dong if (!extlists) 418b170d696SZhanjun Dong return; 419b170d696SZhanjun Dong 420b170d696SZhanjun Dong if (__alloc_ext_regs(drm, &extlists[0], list, total)) { 421b170d696SZhanjun Dong drmm_kfree(drm, extlists); 422b170d696SZhanjun Dong return; 423b170d696SZhanjun Dong } 424b170d696SZhanjun Dong 425b170d696SZhanjun Dong /* For steering registers, the list is generated at run-time */ 426b170d696SZhanjun Dong extarray = (struct __guc_mmio_reg_descr *)extlists[0].list; 427b170d696SZhanjun Dong for_each_dss_steering(iter, gt, slice, subslice) { 428b170d696SZhanjun Dong for (i = 0; i < ARRAY_SIZE(xe_extregs); ++i) { 429b170d696SZhanjun Dong __fill_ext_reg(extarray, &xe_extregs[i], slice, subslice); 430b170d696SZhanjun Dong ++extarray; 431b170d696SZhanjun Dong } 432b170d696SZhanjun Dong 433b170d696SZhanjun Dong if (has_xehpg_extregs) 434b170d696SZhanjun Dong for (i = 0; i < ARRAY_SIZE(xehpg_extregs); ++i) { 435b170d696SZhanjun Dong __fill_ext_reg(extarray, &xehpg_extregs[i], slice, subslice); 436b170d696SZhanjun Dong ++extarray; 437b170d696SZhanjun Dong } 438b170d696SZhanjun Dong } 439b170d696SZhanjun Dong 440b170d696SZhanjun Dong extlists[0].num_regs = total; 441b170d696SZhanjun Dong 442b170d696SZhanjun Dong xe_gt_dbg(guc_to_gt(guc), "capture found %d ext-regs.\n", total); 443b170d696SZhanjun Dong guc->capture->extlists = extlists; 444b170d696SZhanjun Dong } 445b170d696SZhanjun Dong 4469c8c7a7eSZhanjun Dong static int 4479c8c7a7eSZhanjun Dong guc_capture_list_init(struct xe_guc *guc, u32 owner, u32 type, 4489c8c7a7eSZhanjun Dong enum guc_capture_list_class_type capture_class, struct guc_mmio_reg *ptr, 4499c8c7a7eSZhanjun Dong u16 num_entries) 4509c8c7a7eSZhanjun Dong { 451b170d696SZhanjun Dong u32 ptr_idx = 0, list_idx = 0; 4529c8c7a7eSZhanjun Dong const struct __guc_mmio_reg_descr_group *reglists = guc->capture->reglists; 453b170d696SZhanjun Dong struct __guc_mmio_reg_descr_group *extlists = guc->capture->extlists; 4549c8c7a7eSZhanjun Dong const struct __guc_mmio_reg_descr_group *match; 455b170d696SZhanjun Dong u32 list_num; 4569c8c7a7eSZhanjun Dong 4579c8c7a7eSZhanjun Dong if (!reglists) 4589c8c7a7eSZhanjun Dong return -ENODEV; 4599c8c7a7eSZhanjun Dong 4609c8c7a7eSZhanjun Dong match = guc_capture_get_one_list(reglists, owner, type, capture_class); 4619c8c7a7eSZhanjun Dong if (!match) 4629c8c7a7eSZhanjun Dong return -ENODATA; 4639c8c7a7eSZhanjun Dong 464b170d696SZhanjun Dong list_num = match->num_regs; 465b170d696SZhanjun Dong for (list_idx = 0; ptr_idx < num_entries && list_idx < list_num; ++list_idx, ++ptr_idx) { 466b170d696SZhanjun Dong ptr[ptr_idx].offset = match->list[list_idx].reg.addr; 467b170d696SZhanjun Dong ptr[ptr_idx].value = 0xDEADF00D; 468b170d696SZhanjun Dong ptr[ptr_idx].flags = match->list[list_idx].flags; 469b170d696SZhanjun Dong ptr[ptr_idx].mask = match->list[list_idx].mask; 4709c8c7a7eSZhanjun Dong } 4719c8c7a7eSZhanjun Dong 472b170d696SZhanjun Dong match = guc_capture_get_one_list(extlists, owner, type, capture_class); 473b170d696SZhanjun Dong if (match) 474b170d696SZhanjun Dong for (ptr_idx = list_num, list_idx = 0; 475b170d696SZhanjun Dong ptr_idx < num_entries && list_idx < match->num_regs; 476b170d696SZhanjun Dong ++ptr_idx, ++list_idx) { 477b170d696SZhanjun Dong ptr[ptr_idx].offset = match->list[list_idx].reg.addr; 478b170d696SZhanjun Dong ptr[ptr_idx].value = 0xDEADF00D; 479b170d696SZhanjun Dong ptr[ptr_idx].flags = match->list[list_idx].flags; 480b170d696SZhanjun Dong ptr[ptr_idx].mask = match->list[list_idx].mask; 481b170d696SZhanjun Dong } 482b170d696SZhanjun Dong 483b170d696SZhanjun Dong if (ptr_idx < num_entries) 484b170d696SZhanjun Dong xe_gt_dbg(guc_to_gt(guc), "Got short capture reglist init: %d out-of %d.\n", 485b170d696SZhanjun Dong ptr_idx, num_entries); 4869c8c7a7eSZhanjun Dong 4879c8c7a7eSZhanjun Dong return 0; 4889c8c7a7eSZhanjun Dong } 4899c8c7a7eSZhanjun Dong 4909c8c7a7eSZhanjun Dong static int 4919c8c7a7eSZhanjun Dong guc_cap_list_num_regs(struct xe_guc *guc, u32 owner, u32 type, 4929c8c7a7eSZhanjun Dong enum guc_capture_list_class_type capture_class) 4939c8c7a7eSZhanjun Dong { 4949c8c7a7eSZhanjun Dong const struct __guc_mmio_reg_descr_group *match; 495b170d696SZhanjun Dong int num_regs = 0; 4969c8c7a7eSZhanjun Dong 4979c8c7a7eSZhanjun Dong match = guc_capture_get_one_list(guc->capture->reglists, owner, type, capture_class); 498b170d696SZhanjun Dong if (match) 499b170d696SZhanjun Dong num_regs = match->num_regs; 5009c8c7a7eSZhanjun Dong 501b170d696SZhanjun Dong match = guc_capture_get_one_list(guc->capture->extlists, owner, type, capture_class); 502b170d696SZhanjun Dong if (match) 503b170d696SZhanjun Dong num_regs += match->num_regs; 504b170d696SZhanjun Dong else 505*8bfc4963SZhanjun Dong /* 506*8bfc4963SZhanjun Dong * If a caller wants the full register dump size but we have 507*8bfc4963SZhanjun Dong * not yet got the hw-config, which is before max_mmio_per_node 508*8bfc4963SZhanjun Dong * is initialized, then provide a worst-case number for 509*8bfc4963SZhanjun Dong * extlists based on max dss fuse bits, but only ever for 510*8bfc4963SZhanjun Dong * render/compute 511*8bfc4963SZhanjun Dong */ 512*8bfc4963SZhanjun Dong if (owner == GUC_CAPTURE_LIST_INDEX_PF && 513*8bfc4963SZhanjun Dong type == GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS && 514*8bfc4963SZhanjun Dong capture_class == GUC_CAPTURE_LIST_CLASS_RENDER_COMPUTE && 515*8bfc4963SZhanjun Dong !guc->capture->max_mmio_per_node) 516b170d696SZhanjun Dong num_regs += guc_capture_get_steer_reg_num(guc_to_xe(guc)) * 517b170d696SZhanjun Dong XE_MAX_DSS_FUSE_BITS; 518b170d696SZhanjun Dong 519b170d696SZhanjun Dong return num_regs; 5209c8c7a7eSZhanjun Dong } 5219c8c7a7eSZhanjun Dong 5229c8c7a7eSZhanjun Dong static int 5239c8c7a7eSZhanjun Dong guc_capture_getlistsize(struct xe_guc *guc, u32 owner, u32 type, 5249c8c7a7eSZhanjun Dong enum guc_capture_list_class_type capture_class, 5259c8c7a7eSZhanjun Dong size_t *size, bool is_purpose_est) 5269c8c7a7eSZhanjun Dong { 5279c8c7a7eSZhanjun Dong struct xe_guc_state_capture *gc = guc->capture; 5289c8c7a7eSZhanjun Dong struct xe_gt *gt = guc_to_gt(guc); 5299c8c7a7eSZhanjun Dong struct __guc_capture_ads_cache *cache; 5309c8c7a7eSZhanjun Dong int num_regs; 5319c8c7a7eSZhanjun Dong 5329c8c7a7eSZhanjun Dong xe_gt_assert(gt, type < GUC_STATE_CAPTURE_TYPE_MAX); 5339c8c7a7eSZhanjun Dong xe_gt_assert(gt, capture_class < GUC_CAPTURE_LIST_CLASS_MAX); 5349c8c7a7eSZhanjun Dong 5359c8c7a7eSZhanjun Dong cache = &gc->ads_cache[owner][type][capture_class]; 5369c8c7a7eSZhanjun Dong if (!gc->reglists) { 5379c8c7a7eSZhanjun Dong xe_gt_warn(gt, "No capture reglist for this device\n"); 5389c8c7a7eSZhanjun Dong return -ENODEV; 5399c8c7a7eSZhanjun Dong } 5409c8c7a7eSZhanjun Dong 5419c8c7a7eSZhanjun Dong if (cache->is_valid) { 5429c8c7a7eSZhanjun Dong *size = cache->size; 5439c8c7a7eSZhanjun Dong return cache->status; 5449c8c7a7eSZhanjun Dong } 5459c8c7a7eSZhanjun Dong 5469c8c7a7eSZhanjun Dong if (!is_purpose_est && owner == GUC_CAPTURE_LIST_INDEX_PF && 5479c8c7a7eSZhanjun Dong !guc_capture_get_one_list(gc->reglists, owner, type, capture_class)) { 5489c8c7a7eSZhanjun Dong if (type == GUC_STATE_CAPTURE_TYPE_GLOBAL) 5499c8c7a7eSZhanjun Dong xe_gt_warn(gt, "Missing capture reglist: global!\n"); 5509c8c7a7eSZhanjun Dong else 5519c8c7a7eSZhanjun Dong xe_gt_warn(gt, "Missing capture reglist: %s(%u):%s(%u)!\n", 5529c8c7a7eSZhanjun Dong capture_list_type_names[type], type, 5539c8c7a7eSZhanjun Dong capture_engine_class_names[capture_class], capture_class); 5549c8c7a7eSZhanjun Dong return -ENODEV; 5559c8c7a7eSZhanjun Dong } 5569c8c7a7eSZhanjun Dong 5579c8c7a7eSZhanjun Dong num_regs = guc_cap_list_num_regs(guc, owner, type, capture_class); 5589c8c7a7eSZhanjun Dong /* intentional empty lists can exist depending on hw config */ 5599c8c7a7eSZhanjun Dong if (!num_regs) 5609c8c7a7eSZhanjun Dong return -ENODATA; 5619c8c7a7eSZhanjun Dong 5629c8c7a7eSZhanjun Dong if (size) 5639c8c7a7eSZhanjun Dong *size = PAGE_ALIGN((sizeof(struct guc_debug_capture_list)) + 5649c8c7a7eSZhanjun Dong (num_regs * sizeof(struct guc_mmio_reg))); 5659c8c7a7eSZhanjun Dong 5669c8c7a7eSZhanjun Dong return 0; 5679c8c7a7eSZhanjun Dong } 5689c8c7a7eSZhanjun Dong 5699c8c7a7eSZhanjun Dong /** 5709c8c7a7eSZhanjun Dong * xe_guc_capture_getlistsize - Get list size for owner/type/class combination 5719c8c7a7eSZhanjun Dong * @guc: The GuC object 5729c8c7a7eSZhanjun Dong * @owner: PF/VF owner 5739c8c7a7eSZhanjun Dong * @type: GuC capture register type 5749c8c7a7eSZhanjun Dong * @capture_class: GuC capture engine class id 5759c8c7a7eSZhanjun Dong * @size: Point to the size 5769c8c7a7eSZhanjun Dong * 5779c8c7a7eSZhanjun Dong * This function will get the list for the owner/type/class combination, and 5789c8c7a7eSZhanjun Dong * return the page aligned list size. 5799c8c7a7eSZhanjun Dong * 5809c8c7a7eSZhanjun Dong * Returns: 0 on success or a negative error code on failure. 5819c8c7a7eSZhanjun Dong */ 5829c8c7a7eSZhanjun Dong int 5839c8c7a7eSZhanjun Dong xe_guc_capture_getlistsize(struct xe_guc *guc, u32 owner, u32 type, 5849c8c7a7eSZhanjun Dong enum guc_capture_list_class_type capture_class, size_t *size) 5859c8c7a7eSZhanjun Dong { 5869c8c7a7eSZhanjun Dong return guc_capture_getlistsize(guc, owner, type, capture_class, size, false); 5879c8c7a7eSZhanjun Dong } 5889c8c7a7eSZhanjun Dong 5899c8c7a7eSZhanjun Dong /** 5909c8c7a7eSZhanjun Dong * xe_guc_capture_getlist - Get register capture list for owner/type/class 5919c8c7a7eSZhanjun Dong * combination 5929c8c7a7eSZhanjun Dong * @guc: The GuC object 5939c8c7a7eSZhanjun Dong * @owner: PF/VF owner 5949c8c7a7eSZhanjun Dong * @type: GuC capture register type 5959c8c7a7eSZhanjun Dong * @capture_class: GuC capture engine class id 5969c8c7a7eSZhanjun Dong * @outptr: Point to cached register capture list 5979c8c7a7eSZhanjun Dong * 5989c8c7a7eSZhanjun Dong * This function will get the register capture list for the owner/type/class 5999c8c7a7eSZhanjun Dong * combination. 6009c8c7a7eSZhanjun Dong * 6019c8c7a7eSZhanjun Dong * Returns: 0 on success or a negative error code on failure. 6029c8c7a7eSZhanjun Dong */ 6039c8c7a7eSZhanjun Dong int 6049c8c7a7eSZhanjun Dong xe_guc_capture_getlist(struct xe_guc *guc, u32 owner, u32 type, 6059c8c7a7eSZhanjun Dong enum guc_capture_list_class_type capture_class, void **outptr) 6069c8c7a7eSZhanjun Dong { 6079c8c7a7eSZhanjun Dong struct xe_guc_state_capture *gc = guc->capture; 6089c8c7a7eSZhanjun Dong struct __guc_capture_ads_cache *cache = &gc->ads_cache[owner][type][capture_class]; 6099c8c7a7eSZhanjun Dong struct guc_debug_capture_list *listnode; 6109c8c7a7eSZhanjun Dong int ret, num_regs; 6119c8c7a7eSZhanjun Dong u8 *caplist, *tmp; 6129c8c7a7eSZhanjun Dong size_t size = 0; 6139c8c7a7eSZhanjun Dong 6149c8c7a7eSZhanjun Dong if (!gc->reglists) 6159c8c7a7eSZhanjun Dong return -ENODEV; 6169c8c7a7eSZhanjun Dong 6179c8c7a7eSZhanjun Dong if (cache->is_valid) { 6189c8c7a7eSZhanjun Dong *outptr = cache->ptr; 6199c8c7a7eSZhanjun Dong return cache->status; 6209c8c7a7eSZhanjun Dong } 6219c8c7a7eSZhanjun Dong 6229c8c7a7eSZhanjun Dong ret = xe_guc_capture_getlistsize(guc, owner, type, capture_class, &size); 6239c8c7a7eSZhanjun Dong if (ret) { 6249c8c7a7eSZhanjun Dong cache->is_valid = true; 6259c8c7a7eSZhanjun Dong cache->ptr = NULL; 6269c8c7a7eSZhanjun Dong cache->size = 0; 6279c8c7a7eSZhanjun Dong cache->status = ret; 6289c8c7a7eSZhanjun Dong return ret; 6299c8c7a7eSZhanjun Dong } 6309c8c7a7eSZhanjun Dong 6319c8c7a7eSZhanjun Dong caplist = drmm_kzalloc(guc_to_drm(guc), size, GFP_KERNEL); 6329c8c7a7eSZhanjun Dong if (!caplist) 6339c8c7a7eSZhanjun Dong return -ENOMEM; 6349c8c7a7eSZhanjun Dong 6359c8c7a7eSZhanjun Dong /* populate capture list header */ 6369c8c7a7eSZhanjun Dong tmp = caplist; 6379c8c7a7eSZhanjun Dong num_regs = guc_cap_list_num_regs(guc, owner, type, capture_class); 6389c8c7a7eSZhanjun Dong listnode = (struct guc_debug_capture_list *)tmp; 6399c8c7a7eSZhanjun Dong listnode->header.info = FIELD_PREP(GUC_CAPTURELISTHDR_NUMDESCR, (u32)num_regs); 6409c8c7a7eSZhanjun Dong 6419c8c7a7eSZhanjun Dong /* populate list of register descriptor */ 6429c8c7a7eSZhanjun Dong tmp += sizeof(struct guc_debug_capture_list); 6439c8c7a7eSZhanjun Dong guc_capture_list_init(guc, owner, type, capture_class, 6449c8c7a7eSZhanjun Dong (struct guc_mmio_reg *)tmp, num_regs); 6459c8c7a7eSZhanjun Dong 6469c8c7a7eSZhanjun Dong /* cache this list */ 6479c8c7a7eSZhanjun Dong cache->is_valid = true; 6489c8c7a7eSZhanjun Dong cache->ptr = caplist; 6499c8c7a7eSZhanjun Dong cache->size = size; 6509c8c7a7eSZhanjun Dong cache->status = 0; 6519c8c7a7eSZhanjun Dong 6529c8c7a7eSZhanjun Dong *outptr = caplist; 6539c8c7a7eSZhanjun Dong 6549c8c7a7eSZhanjun Dong return 0; 6559c8c7a7eSZhanjun Dong } 6569c8c7a7eSZhanjun Dong 6579c8c7a7eSZhanjun Dong /** 6589c8c7a7eSZhanjun Dong * xe_guc_capture_getnullheader - Get a null list for register capture 6599c8c7a7eSZhanjun Dong * @guc: The GuC object 6609c8c7a7eSZhanjun Dong * @outptr: Point to cached register capture list 6619c8c7a7eSZhanjun Dong * @size: Point to the size 6629c8c7a7eSZhanjun Dong * 6639c8c7a7eSZhanjun Dong * This function will alloc for a null list for register capture. 6649c8c7a7eSZhanjun Dong * 6659c8c7a7eSZhanjun Dong * Returns: 0 on success or a negative error code on failure. 6669c8c7a7eSZhanjun Dong */ 6679c8c7a7eSZhanjun Dong int 6689c8c7a7eSZhanjun Dong xe_guc_capture_getnullheader(struct xe_guc *guc, void **outptr, size_t *size) 6699c8c7a7eSZhanjun Dong { 6709c8c7a7eSZhanjun Dong struct xe_guc_state_capture *gc = guc->capture; 6719c8c7a7eSZhanjun Dong int tmp = sizeof(u32) * 4; 6729c8c7a7eSZhanjun Dong void *null_header; 6739c8c7a7eSZhanjun Dong 6749c8c7a7eSZhanjun Dong if (gc->ads_null_cache) { 6759c8c7a7eSZhanjun Dong *outptr = gc->ads_null_cache; 6769c8c7a7eSZhanjun Dong *size = tmp; 6779c8c7a7eSZhanjun Dong return 0; 6789c8c7a7eSZhanjun Dong } 6799c8c7a7eSZhanjun Dong 6809c8c7a7eSZhanjun Dong null_header = drmm_kzalloc(guc_to_drm(guc), tmp, GFP_KERNEL); 6819c8c7a7eSZhanjun Dong if (!null_header) 6829c8c7a7eSZhanjun Dong return -ENOMEM; 6839c8c7a7eSZhanjun Dong 6849c8c7a7eSZhanjun Dong gc->ads_null_cache = null_header; 6859c8c7a7eSZhanjun Dong *outptr = null_header; 6869c8c7a7eSZhanjun Dong *size = tmp; 6879c8c7a7eSZhanjun Dong 6889c8c7a7eSZhanjun Dong return 0; 6899c8c7a7eSZhanjun Dong } 6909c8c7a7eSZhanjun Dong 6919c8c7a7eSZhanjun Dong /** 6929c8c7a7eSZhanjun Dong * xe_guc_capture_ads_input_worst_size - Calculate the worst size for GuC register capture 6939c8c7a7eSZhanjun Dong * @guc: point to xe_guc structure 6949c8c7a7eSZhanjun Dong * 6959c8c7a7eSZhanjun Dong * Calculate the worst size for GuC register capture by including all possible engines classes. 6969c8c7a7eSZhanjun Dong * 6979c8c7a7eSZhanjun Dong * Returns: Calculated size 6989c8c7a7eSZhanjun Dong */ 6999c8c7a7eSZhanjun Dong size_t xe_guc_capture_ads_input_worst_size(struct xe_guc *guc) 7009c8c7a7eSZhanjun Dong { 7019c8c7a7eSZhanjun Dong size_t total_size, class_size, instance_size, global_size; 7029c8c7a7eSZhanjun Dong int i, j; 7039c8c7a7eSZhanjun Dong 7049c8c7a7eSZhanjun Dong /* 7059c8c7a7eSZhanjun Dong * This function calculates the worst case register lists size by 7069c8c7a7eSZhanjun Dong * including all possible engines classes. It is called during the 7079c8c7a7eSZhanjun Dong * first of a two-phase GuC (and ADS-population) initialization 7089c8c7a7eSZhanjun Dong * sequence, that is, during the pre-hwconfig phase before we have 7099c8c7a7eSZhanjun Dong * the exact engine fusing info. 7109c8c7a7eSZhanjun Dong */ 7119c8c7a7eSZhanjun Dong total_size = PAGE_SIZE; /* Pad a page in front for empty lists */ 7129c8c7a7eSZhanjun Dong for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; i++) { 7139c8c7a7eSZhanjun Dong for (j = 0; j < GUC_CAPTURE_LIST_CLASS_MAX; j++) { 7149c8c7a7eSZhanjun Dong if (xe_guc_capture_getlistsize(guc, i, 7159c8c7a7eSZhanjun Dong GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS, 7169c8c7a7eSZhanjun Dong j, &class_size) < 0) 7179c8c7a7eSZhanjun Dong class_size = 0; 7189c8c7a7eSZhanjun Dong if (xe_guc_capture_getlistsize(guc, i, 7199c8c7a7eSZhanjun Dong GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE, 7209c8c7a7eSZhanjun Dong j, &instance_size) < 0) 7219c8c7a7eSZhanjun Dong instance_size = 0; 7229c8c7a7eSZhanjun Dong total_size += class_size + instance_size; 7239c8c7a7eSZhanjun Dong } 7249c8c7a7eSZhanjun Dong if (xe_guc_capture_getlistsize(guc, i, 7259c8c7a7eSZhanjun Dong GUC_STATE_CAPTURE_TYPE_GLOBAL, 7269c8c7a7eSZhanjun Dong 0, &global_size) < 0) 7279c8c7a7eSZhanjun Dong global_size = 0; 7289c8c7a7eSZhanjun Dong total_size += global_size; 7299c8c7a7eSZhanjun Dong } 7309c8c7a7eSZhanjun Dong 7319c8c7a7eSZhanjun Dong return PAGE_ALIGN(total_size); 7329c8c7a7eSZhanjun Dong } 7339c8c7a7eSZhanjun Dong 73484d15f42SZhanjun Dong static int guc_capture_output_size_est(struct xe_guc *guc) 73584d15f42SZhanjun Dong { 73684d15f42SZhanjun Dong struct xe_gt *gt = guc_to_gt(guc); 73784d15f42SZhanjun Dong struct xe_hw_engine *hwe; 73884d15f42SZhanjun Dong enum xe_hw_engine_id id; 73984d15f42SZhanjun Dong 74084d15f42SZhanjun Dong int capture_size = 0; 74184d15f42SZhanjun Dong size_t tmp = 0; 74284d15f42SZhanjun Dong 74384d15f42SZhanjun Dong if (!guc->capture) 74484d15f42SZhanjun Dong return -ENODEV; 74584d15f42SZhanjun Dong 74684d15f42SZhanjun Dong /* 74784d15f42SZhanjun Dong * If every single engine-instance suffered a failure in quick succession but 74884d15f42SZhanjun Dong * were all unrelated, then a burst of multiple error-capture events would dump 74984d15f42SZhanjun Dong * registers for every one engine instance, one at a time. In this case, GuC 75084d15f42SZhanjun Dong * would even dump the global-registers repeatedly. 75184d15f42SZhanjun Dong * 75284d15f42SZhanjun Dong * For each engine instance, there would be 1 x guc_state_capture_group_t output 75384d15f42SZhanjun Dong * followed by 3 x guc_state_capture_t lists. The latter is how the register 75484d15f42SZhanjun Dong * dumps are split across different register types (where the '3' are global vs class 75584d15f42SZhanjun Dong * vs instance). 75684d15f42SZhanjun Dong */ 75784d15f42SZhanjun Dong for_each_hw_engine(hwe, gt, id) { 75884d15f42SZhanjun Dong enum guc_capture_list_class_type capture_class; 75984d15f42SZhanjun Dong 76084d15f42SZhanjun Dong capture_class = xe_engine_class_to_guc_capture_class(hwe->class); 76184d15f42SZhanjun Dong capture_size += sizeof(struct guc_state_capture_group_header_t) + 76284d15f42SZhanjun Dong (3 * sizeof(struct guc_state_capture_header_t)); 76384d15f42SZhanjun Dong 76484d15f42SZhanjun Dong if (!guc_capture_getlistsize(guc, 0, GUC_STATE_CAPTURE_TYPE_GLOBAL, 76584d15f42SZhanjun Dong 0, &tmp, true)) 76684d15f42SZhanjun Dong capture_size += tmp; 76784d15f42SZhanjun Dong if (!guc_capture_getlistsize(guc, 0, GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS, 76884d15f42SZhanjun Dong capture_class, &tmp, true)) 76984d15f42SZhanjun Dong capture_size += tmp; 77084d15f42SZhanjun Dong if (!guc_capture_getlistsize(guc, 0, GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE, 77184d15f42SZhanjun Dong capture_class, &tmp, true)) 77284d15f42SZhanjun Dong capture_size += tmp; 77384d15f42SZhanjun Dong } 77484d15f42SZhanjun Dong 77584d15f42SZhanjun Dong return capture_size; 77684d15f42SZhanjun Dong } 77784d15f42SZhanjun Dong 77884d15f42SZhanjun Dong /* 77984d15f42SZhanjun Dong * Add on a 3x multiplier to allow for multiple back-to-back captures occurring 78084d15f42SZhanjun Dong * before the Xe can read the data out and process it 78184d15f42SZhanjun Dong */ 78284d15f42SZhanjun Dong #define GUC_CAPTURE_OVERBUFFER_MULTIPLIER 3 78384d15f42SZhanjun Dong 78484d15f42SZhanjun Dong static void check_guc_capture_size(struct xe_guc *guc) 78584d15f42SZhanjun Dong { 78684d15f42SZhanjun Dong int capture_size = guc_capture_output_size_est(guc); 78784d15f42SZhanjun Dong int spare_size = capture_size * GUC_CAPTURE_OVERBUFFER_MULTIPLIER; 78884d15f42SZhanjun Dong u32 buffer_size = xe_guc_log_section_size_capture(&guc->log); 78984d15f42SZhanjun Dong 79084d15f42SZhanjun Dong /* 79184d15f42SZhanjun Dong * NOTE: capture_size is much smaller than the capture region 79284d15f42SZhanjun Dong * allocation (DG2: <80K vs 1MB). 79384d15f42SZhanjun Dong * Additionally, its based on space needed to fit all engines getting 79484d15f42SZhanjun Dong * reset at once within the same G2H handler task slot. This is very 79584d15f42SZhanjun Dong * unlikely. However, if GuC really does run out of space for whatever 79684d15f42SZhanjun Dong * reason, we will see an separate warning message when processing the 79784d15f42SZhanjun Dong * G2H event capture-notification, search for: 79884d15f42SZhanjun Dong * xe_guc_STATE_CAPTURE_EVENT_STATUS_NOSPACE. 79984d15f42SZhanjun Dong */ 80084d15f42SZhanjun Dong if (capture_size < 0) 80184d15f42SZhanjun Dong xe_gt_dbg(guc_to_gt(guc), 80284d15f42SZhanjun Dong "Failed to calculate error state capture buffer minimum size: %d!\n", 80384d15f42SZhanjun Dong capture_size); 80484d15f42SZhanjun Dong if (capture_size > buffer_size) 80584d15f42SZhanjun Dong xe_gt_dbg(guc_to_gt(guc), "Error state capture buffer maybe small: %d < %d\n", 80684d15f42SZhanjun Dong buffer_size, capture_size); 80784d15f42SZhanjun Dong else if (spare_size > buffer_size) 80884d15f42SZhanjun Dong xe_gt_dbg(guc_to_gt(guc), 80984d15f42SZhanjun Dong "Error state capture buffer lacks spare size: %d < %d (min = %d)\n", 81084d15f42SZhanjun Dong buffer_size, spare_size, capture_size); 81184d15f42SZhanjun Dong } 81284d15f42SZhanjun Dong 813*8bfc4963SZhanjun Dong static void 814*8bfc4963SZhanjun Dong guc_capture_add_node_to_list(struct __guc_capture_parsed_output *node, 815*8bfc4963SZhanjun Dong struct list_head *list) 816*8bfc4963SZhanjun Dong { 817*8bfc4963SZhanjun Dong list_add_tail(&node->link, list); 818*8bfc4963SZhanjun Dong } 819*8bfc4963SZhanjun Dong 820*8bfc4963SZhanjun Dong static void 821*8bfc4963SZhanjun Dong guc_capture_add_node_to_outlist(struct xe_guc_state_capture *gc, 822*8bfc4963SZhanjun Dong struct __guc_capture_parsed_output *node) 823*8bfc4963SZhanjun Dong { 824*8bfc4963SZhanjun Dong guc_capture_add_node_to_list(node, &gc->outlist); 825*8bfc4963SZhanjun Dong } 826*8bfc4963SZhanjun Dong 827*8bfc4963SZhanjun Dong static void 828*8bfc4963SZhanjun Dong guc_capture_add_node_to_cachelist(struct xe_guc_state_capture *gc, 829*8bfc4963SZhanjun Dong struct __guc_capture_parsed_output *node) 830*8bfc4963SZhanjun Dong { 831*8bfc4963SZhanjun Dong guc_capture_add_node_to_list(node, &gc->cachelist); 832*8bfc4963SZhanjun Dong } 833*8bfc4963SZhanjun Dong 834*8bfc4963SZhanjun Dong static void 835*8bfc4963SZhanjun Dong guc_capture_init_node(struct xe_guc *guc, struct __guc_capture_parsed_output *node) 836*8bfc4963SZhanjun Dong { 837*8bfc4963SZhanjun Dong struct guc_mmio_reg *tmp[GUC_STATE_CAPTURE_TYPE_MAX]; 838*8bfc4963SZhanjun Dong int i; 839*8bfc4963SZhanjun Dong 840*8bfc4963SZhanjun Dong for (i = 0; i < GUC_STATE_CAPTURE_TYPE_MAX; ++i) { 841*8bfc4963SZhanjun Dong tmp[i] = node->reginfo[i].regs; 842*8bfc4963SZhanjun Dong memset(tmp[i], 0, sizeof(struct guc_mmio_reg) * 843*8bfc4963SZhanjun Dong guc->capture->max_mmio_per_node); 844*8bfc4963SZhanjun Dong } 845*8bfc4963SZhanjun Dong memset(node, 0, sizeof(*node)); 846*8bfc4963SZhanjun Dong for (i = 0; i < GUC_STATE_CAPTURE_TYPE_MAX; ++i) 847*8bfc4963SZhanjun Dong node->reginfo[i].regs = tmp[i]; 848*8bfc4963SZhanjun Dong 849*8bfc4963SZhanjun Dong INIT_LIST_HEAD(&node->link); 850*8bfc4963SZhanjun Dong } 851*8bfc4963SZhanjun Dong 852*8bfc4963SZhanjun Dong /** 853*8bfc4963SZhanjun Dong * DOC: Init, G2H-event and reporting flows for GuC-error-capture 854*8bfc4963SZhanjun Dong * 855*8bfc4963SZhanjun Dong * KMD Init time flows: 856*8bfc4963SZhanjun Dong * -------------------- 857*8bfc4963SZhanjun Dong * --> alloc A: GuC input capture regs lists (registered to GuC via ADS). 858*8bfc4963SZhanjun Dong * xe_guc_ads acquires the register lists by calling 859*8bfc4963SZhanjun Dong * xe_guc_capture_getlistsize and xe_guc_capture_getlist 'n' times, 860*8bfc4963SZhanjun Dong * where n = 1 for global-reg-list + 861*8bfc4963SZhanjun Dong * num_engine_classes for class-reg-list + 862*8bfc4963SZhanjun Dong * num_engine_classes for instance-reg-list 863*8bfc4963SZhanjun Dong * (since all instances of the same engine-class type 864*8bfc4963SZhanjun Dong * have an identical engine-instance register-list). 865*8bfc4963SZhanjun Dong * ADS module also calls separately for PF vs VF. 866*8bfc4963SZhanjun Dong * 867*8bfc4963SZhanjun Dong * --> alloc B: GuC output capture buf (registered via guc_init_params(log_param)) 868*8bfc4963SZhanjun Dong * Size = #define CAPTURE_BUFFER_SIZE (warns if on too-small) 869*8bfc4963SZhanjun Dong * Note2: 'x 3' to hold multiple capture groups 870*8bfc4963SZhanjun Dong * 871*8bfc4963SZhanjun Dong * GUC Runtime notify capture: 872*8bfc4963SZhanjun Dong * -------------------------- 873*8bfc4963SZhanjun Dong * --> G2H STATE_CAPTURE_NOTIFICATION 874*8bfc4963SZhanjun Dong * L--> xe_guc_capture_process 875*8bfc4963SZhanjun Dong * L--> Loop through B (head..tail) and for each engine instance's 876*8bfc4963SZhanjun Dong * err-state-captured register-list we find, we alloc 'C': 877*8bfc4963SZhanjun Dong * --> alloc C: A capture-output-node structure that includes misc capture info along 878*8bfc4963SZhanjun Dong * with 3 register list dumps (global, engine-class and engine-instance) 879*8bfc4963SZhanjun Dong * This node is created from a pre-allocated list of blank nodes in 880*8bfc4963SZhanjun Dong * guc->capture->cachelist and populated with the error-capture 881*8bfc4963SZhanjun Dong * data from GuC and then it's added into guc->capture->outlist linked 882*8bfc4963SZhanjun Dong * list. This list is used for matchup and printout by xe_devcoredump_read 883*8bfc4963SZhanjun Dong * and xe_hw_engine_snapshot_print, (when user invokes the devcoredump sysfs). 884*8bfc4963SZhanjun Dong * 885*8bfc4963SZhanjun Dong * GUC --> notify context reset: 886*8bfc4963SZhanjun Dong * ----------------------------- 887*8bfc4963SZhanjun Dong * --> guc_exec_queue_timedout_job 888*8bfc4963SZhanjun Dong * L--> xe_devcoredump 889*8bfc4963SZhanjun Dong * L--> devcoredump_snapshot 890*8bfc4963SZhanjun Dong * --> xe_hw_engine_snapshot_capture 891*8bfc4963SZhanjun Dong * 892*8bfc4963SZhanjun Dong * User Sysfs / Debugfs 893*8bfc4963SZhanjun Dong * -------------------- 894*8bfc4963SZhanjun Dong * --> xe_devcoredump_read-> 895*8bfc4963SZhanjun Dong * L--> xxx_snapshot_print 896*8bfc4963SZhanjun Dong * L--> xe_hw_engine_snapshot_print 897*8bfc4963SZhanjun Dong * Print register lists values saved at 898*8bfc4963SZhanjun Dong * guc->capture->outlist 899*8bfc4963SZhanjun Dong * 900*8bfc4963SZhanjun Dong */ 901*8bfc4963SZhanjun Dong 902*8bfc4963SZhanjun Dong static int guc_capture_buf_cnt(struct __guc_capture_bufstate *buf) 903*8bfc4963SZhanjun Dong { 904*8bfc4963SZhanjun Dong if (buf->wr >= buf->rd) 905*8bfc4963SZhanjun Dong return (buf->wr - buf->rd); 906*8bfc4963SZhanjun Dong return (buf->size - buf->rd) + buf->wr; 907*8bfc4963SZhanjun Dong } 908*8bfc4963SZhanjun Dong 909*8bfc4963SZhanjun Dong static int guc_capture_buf_cnt_to_end(struct __guc_capture_bufstate *buf) 910*8bfc4963SZhanjun Dong { 911*8bfc4963SZhanjun Dong if (buf->rd > buf->wr) 912*8bfc4963SZhanjun Dong return (buf->size - buf->rd); 913*8bfc4963SZhanjun Dong return (buf->wr - buf->rd); 914*8bfc4963SZhanjun Dong } 915*8bfc4963SZhanjun Dong 916*8bfc4963SZhanjun Dong /* 917*8bfc4963SZhanjun Dong * GuC's error-capture output is a ring buffer populated in a byte-stream fashion: 918*8bfc4963SZhanjun Dong * 919*8bfc4963SZhanjun Dong * The GuC Log buffer region for error-capture is managed like a ring buffer. 920*8bfc4963SZhanjun Dong * The GuC firmware dumps error capture logs into this ring in a byte-stream flow. 921*8bfc4963SZhanjun Dong * Additionally, as per the current and foreseeable future, all packed error- 922*8bfc4963SZhanjun Dong * capture output structures are dword aligned. 923*8bfc4963SZhanjun Dong * 924*8bfc4963SZhanjun Dong * That said, if the GuC firmware is in the midst of writing a structure that is larger 925*8bfc4963SZhanjun Dong * than one dword but the tail end of the err-capture buffer-region has lesser space left, 926*8bfc4963SZhanjun Dong * we would need to extract that structure one dword at a time straddled across the end, 927*8bfc4963SZhanjun Dong * onto the start of the ring. 928*8bfc4963SZhanjun Dong * 929*8bfc4963SZhanjun Dong * Below function, guc_capture_log_remove_bytes is a helper for that. All callers of this 930*8bfc4963SZhanjun Dong * function would typically do a straight-up memcpy from the ring contents and will only 931*8bfc4963SZhanjun Dong * call this helper if their structure-extraction is straddling across the end of the 932*8bfc4963SZhanjun Dong * ring. GuC firmware does not add any padding. The reason for the no-padding is to ease 933*8bfc4963SZhanjun Dong * scalability for future expansion of output data types without requiring a redesign 934*8bfc4963SZhanjun Dong * of the flow controls. 935*8bfc4963SZhanjun Dong */ 936*8bfc4963SZhanjun Dong static int 937*8bfc4963SZhanjun Dong guc_capture_log_remove_bytes(struct xe_guc *guc, struct __guc_capture_bufstate *buf, 938*8bfc4963SZhanjun Dong void *out, int bytes_needed) 939*8bfc4963SZhanjun Dong { 940*8bfc4963SZhanjun Dong #define GUC_CAPTURE_LOG_BUF_COPY_RETRY_MAX 3 941*8bfc4963SZhanjun Dong 942*8bfc4963SZhanjun Dong int fill_size = 0, tries = GUC_CAPTURE_LOG_BUF_COPY_RETRY_MAX; 943*8bfc4963SZhanjun Dong int copy_size, avail; 944*8bfc4963SZhanjun Dong 945*8bfc4963SZhanjun Dong xe_assert(guc_to_xe(guc), bytes_needed % sizeof(u32) == 0); 946*8bfc4963SZhanjun Dong 947*8bfc4963SZhanjun Dong if (bytes_needed > guc_capture_buf_cnt(buf)) 948*8bfc4963SZhanjun Dong return -1; 949*8bfc4963SZhanjun Dong 950*8bfc4963SZhanjun Dong while (bytes_needed > 0 && tries--) { 951*8bfc4963SZhanjun Dong int misaligned; 952*8bfc4963SZhanjun Dong 953*8bfc4963SZhanjun Dong avail = guc_capture_buf_cnt_to_end(buf); 954*8bfc4963SZhanjun Dong misaligned = avail % sizeof(u32); 955*8bfc4963SZhanjun Dong /* wrap if at end */ 956*8bfc4963SZhanjun Dong if (!avail) { 957*8bfc4963SZhanjun Dong /* output stream clipped */ 958*8bfc4963SZhanjun Dong if (!buf->rd) 959*8bfc4963SZhanjun Dong return fill_size; 960*8bfc4963SZhanjun Dong buf->rd = 0; 961*8bfc4963SZhanjun Dong continue; 962*8bfc4963SZhanjun Dong } 963*8bfc4963SZhanjun Dong 964*8bfc4963SZhanjun Dong /* Only copy to u32 aligned data */ 965*8bfc4963SZhanjun Dong copy_size = avail < bytes_needed ? avail - misaligned : bytes_needed; 966*8bfc4963SZhanjun Dong xe_map_memcpy_from(guc_to_xe(guc), out + fill_size, &guc->log.bo->vmap, 967*8bfc4963SZhanjun Dong buf->data_offset + buf->rd, copy_size); 968*8bfc4963SZhanjun Dong buf->rd += copy_size; 969*8bfc4963SZhanjun Dong fill_size += copy_size; 970*8bfc4963SZhanjun Dong bytes_needed -= copy_size; 971*8bfc4963SZhanjun Dong 972*8bfc4963SZhanjun Dong if (misaligned) 973*8bfc4963SZhanjun Dong xe_gt_warn(guc_to_gt(guc), 974*8bfc4963SZhanjun Dong "Bytes extraction not dword aligned, clipping.\n"); 975*8bfc4963SZhanjun Dong } 976*8bfc4963SZhanjun Dong 977*8bfc4963SZhanjun Dong return fill_size; 978*8bfc4963SZhanjun Dong } 979*8bfc4963SZhanjun Dong 980*8bfc4963SZhanjun Dong static int 981*8bfc4963SZhanjun Dong guc_capture_log_get_group_hdr(struct xe_guc *guc, struct __guc_capture_bufstate *buf, 982*8bfc4963SZhanjun Dong struct guc_state_capture_group_header_t *ghdr) 983*8bfc4963SZhanjun Dong { 984*8bfc4963SZhanjun Dong int fullsize = sizeof(struct guc_state_capture_group_header_t); 985*8bfc4963SZhanjun Dong 986*8bfc4963SZhanjun Dong if (guc_capture_log_remove_bytes(guc, buf, ghdr, fullsize) != fullsize) 987*8bfc4963SZhanjun Dong return -1; 988*8bfc4963SZhanjun Dong return 0; 989*8bfc4963SZhanjun Dong } 990*8bfc4963SZhanjun Dong 991*8bfc4963SZhanjun Dong static int 992*8bfc4963SZhanjun Dong guc_capture_log_get_data_hdr(struct xe_guc *guc, struct __guc_capture_bufstate *buf, 993*8bfc4963SZhanjun Dong struct guc_state_capture_header_t *hdr) 994*8bfc4963SZhanjun Dong { 995*8bfc4963SZhanjun Dong int fullsize = sizeof(struct guc_state_capture_header_t); 996*8bfc4963SZhanjun Dong 997*8bfc4963SZhanjun Dong if (guc_capture_log_remove_bytes(guc, buf, hdr, fullsize) != fullsize) 998*8bfc4963SZhanjun Dong return -1; 999*8bfc4963SZhanjun Dong return 0; 1000*8bfc4963SZhanjun Dong } 1001*8bfc4963SZhanjun Dong 1002*8bfc4963SZhanjun Dong static int 1003*8bfc4963SZhanjun Dong guc_capture_log_get_register(struct xe_guc *guc, struct __guc_capture_bufstate *buf, 1004*8bfc4963SZhanjun Dong struct guc_mmio_reg *reg) 1005*8bfc4963SZhanjun Dong { 1006*8bfc4963SZhanjun Dong int fullsize = sizeof(struct guc_mmio_reg); 1007*8bfc4963SZhanjun Dong 1008*8bfc4963SZhanjun Dong if (guc_capture_log_remove_bytes(guc, buf, reg, fullsize) != fullsize) 1009*8bfc4963SZhanjun Dong return -1; 1010*8bfc4963SZhanjun Dong return 0; 1011*8bfc4963SZhanjun Dong } 1012*8bfc4963SZhanjun Dong 1013*8bfc4963SZhanjun Dong static struct __guc_capture_parsed_output * 1014*8bfc4963SZhanjun Dong guc_capture_get_prealloc_node(struct xe_guc *guc) 1015*8bfc4963SZhanjun Dong { 1016*8bfc4963SZhanjun Dong struct __guc_capture_parsed_output *found = NULL; 1017*8bfc4963SZhanjun Dong 1018*8bfc4963SZhanjun Dong if (!list_empty(&guc->capture->cachelist)) { 1019*8bfc4963SZhanjun Dong struct __guc_capture_parsed_output *n, *ntmp; 1020*8bfc4963SZhanjun Dong 1021*8bfc4963SZhanjun Dong /* get first avail node from the cache list */ 1022*8bfc4963SZhanjun Dong list_for_each_entry_safe(n, ntmp, &guc->capture->cachelist, link) { 1023*8bfc4963SZhanjun Dong found = n; 1024*8bfc4963SZhanjun Dong break; 1025*8bfc4963SZhanjun Dong } 1026*8bfc4963SZhanjun Dong } else { 1027*8bfc4963SZhanjun Dong struct __guc_capture_parsed_output *n, *ntmp; 1028*8bfc4963SZhanjun Dong 1029*8bfc4963SZhanjun Dong /* traverse down and steal back the oldest node already allocated */ 1030*8bfc4963SZhanjun Dong list_for_each_entry_safe(n, ntmp, &guc->capture->outlist, link) { 1031*8bfc4963SZhanjun Dong found = n; 1032*8bfc4963SZhanjun Dong } 1033*8bfc4963SZhanjun Dong } 1034*8bfc4963SZhanjun Dong if (found) { 1035*8bfc4963SZhanjun Dong list_del(&found->link); 1036*8bfc4963SZhanjun Dong guc_capture_init_node(guc, found); 1037*8bfc4963SZhanjun Dong } 1038*8bfc4963SZhanjun Dong 1039*8bfc4963SZhanjun Dong return found; 1040*8bfc4963SZhanjun Dong } 1041*8bfc4963SZhanjun Dong 1042*8bfc4963SZhanjun Dong static struct __guc_capture_parsed_output * 1043*8bfc4963SZhanjun Dong guc_capture_clone_node(struct xe_guc *guc, struct __guc_capture_parsed_output *original, 1044*8bfc4963SZhanjun Dong u32 keep_reglist_mask) 1045*8bfc4963SZhanjun Dong { 1046*8bfc4963SZhanjun Dong struct __guc_capture_parsed_output *new; 1047*8bfc4963SZhanjun Dong int i; 1048*8bfc4963SZhanjun Dong 1049*8bfc4963SZhanjun Dong new = guc_capture_get_prealloc_node(guc); 1050*8bfc4963SZhanjun Dong if (!new) 1051*8bfc4963SZhanjun Dong return NULL; 1052*8bfc4963SZhanjun Dong if (!original) 1053*8bfc4963SZhanjun Dong return new; 1054*8bfc4963SZhanjun Dong 1055*8bfc4963SZhanjun Dong new->is_partial = original->is_partial; 1056*8bfc4963SZhanjun Dong 1057*8bfc4963SZhanjun Dong /* copy reg-lists that we want to clone */ 1058*8bfc4963SZhanjun Dong for (i = 0; i < GUC_STATE_CAPTURE_TYPE_MAX; ++i) { 1059*8bfc4963SZhanjun Dong if (keep_reglist_mask & BIT(i)) { 1060*8bfc4963SZhanjun Dong XE_WARN_ON(original->reginfo[i].num_regs > 1061*8bfc4963SZhanjun Dong guc->capture->max_mmio_per_node); 1062*8bfc4963SZhanjun Dong 1063*8bfc4963SZhanjun Dong memcpy(new->reginfo[i].regs, original->reginfo[i].regs, 1064*8bfc4963SZhanjun Dong original->reginfo[i].num_regs * sizeof(struct guc_mmio_reg)); 1065*8bfc4963SZhanjun Dong 1066*8bfc4963SZhanjun Dong new->reginfo[i].num_regs = original->reginfo[i].num_regs; 1067*8bfc4963SZhanjun Dong new->reginfo[i].vfid = original->reginfo[i].vfid; 1068*8bfc4963SZhanjun Dong 1069*8bfc4963SZhanjun Dong if (i == GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS) { 1070*8bfc4963SZhanjun Dong new->eng_class = original->eng_class; 1071*8bfc4963SZhanjun Dong } else if (i == GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE) { 1072*8bfc4963SZhanjun Dong new->eng_inst = original->eng_inst; 1073*8bfc4963SZhanjun Dong new->guc_id = original->guc_id; 1074*8bfc4963SZhanjun Dong new->lrca = original->lrca; 1075*8bfc4963SZhanjun Dong } 1076*8bfc4963SZhanjun Dong } 1077*8bfc4963SZhanjun Dong } 1078*8bfc4963SZhanjun Dong 1079*8bfc4963SZhanjun Dong return new; 1080*8bfc4963SZhanjun Dong } 1081*8bfc4963SZhanjun Dong 1082*8bfc4963SZhanjun Dong static int 1083*8bfc4963SZhanjun Dong guc_capture_extract_reglists(struct xe_guc *guc, struct __guc_capture_bufstate *buf) 1084*8bfc4963SZhanjun Dong { 1085*8bfc4963SZhanjun Dong struct xe_gt *gt = guc_to_gt(guc); 1086*8bfc4963SZhanjun Dong struct guc_state_capture_group_header_t ghdr = {0}; 1087*8bfc4963SZhanjun Dong struct guc_state_capture_header_t hdr = {0}; 1088*8bfc4963SZhanjun Dong struct __guc_capture_parsed_output *node = NULL; 1089*8bfc4963SZhanjun Dong struct guc_mmio_reg *regs = NULL; 1090*8bfc4963SZhanjun Dong int i, numlists, numregs, ret = 0; 1091*8bfc4963SZhanjun Dong enum guc_state_capture_type datatype; 1092*8bfc4963SZhanjun Dong struct guc_mmio_reg tmp; 1093*8bfc4963SZhanjun Dong bool is_partial = false; 1094*8bfc4963SZhanjun Dong 1095*8bfc4963SZhanjun Dong i = guc_capture_buf_cnt(buf); 1096*8bfc4963SZhanjun Dong if (!i) 1097*8bfc4963SZhanjun Dong return -ENODATA; 1098*8bfc4963SZhanjun Dong 1099*8bfc4963SZhanjun Dong if (i % sizeof(u32)) { 1100*8bfc4963SZhanjun Dong xe_gt_warn(gt, "Got mis-aligned register capture entries\n"); 1101*8bfc4963SZhanjun Dong ret = -EIO; 1102*8bfc4963SZhanjun Dong goto bailout; 1103*8bfc4963SZhanjun Dong } 1104*8bfc4963SZhanjun Dong 1105*8bfc4963SZhanjun Dong /* first get the capture group header */ 1106*8bfc4963SZhanjun Dong if (guc_capture_log_get_group_hdr(guc, buf, &ghdr)) { 1107*8bfc4963SZhanjun Dong ret = -EIO; 1108*8bfc4963SZhanjun Dong goto bailout; 1109*8bfc4963SZhanjun Dong } 1110*8bfc4963SZhanjun Dong /* 1111*8bfc4963SZhanjun Dong * we would typically expect a layout as below where n would be expected to be 1112*8bfc4963SZhanjun Dong * anywhere between 3 to n where n > 3 if we are seeing multiple dependent engine 1113*8bfc4963SZhanjun Dong * instances being reset together. 1114*8bfc4963SZhanjun Dong * ____________________________________________ 1115*8bfc4963SZhanjun Dong * | Capture Group | 1116*8bfc4963SZhanjun Dong * | ________________________________________ | 1117*8bfc4963SZhanjun Dong * | | Capture Group Header: | | 1118*8bfc4963SZhanjun Dong * | | - num_captures = 5 | | 1119*8bfc4963SZhanjun Dong * | |______________________________________| | 1120*8bfc4963SZhanjun Dong * | ________________________________________ | 1121*8bfc4963SZhanjun Dong * | | Capture1: | | 1122*8bfc4963SZhanjun Dong * | | Hdr: GLOBAL, numregs=a | | 1123*8bfc4963SZhanjun Dong * | | ____________________________________ | | 1124*8bfc4963SZhanjun Dong * | | | Reglist | | | 1125*8bfc4963SZhanjun Dong * | | | - reg1, reg2, ... rega | | | 1126*8bfc4963SZhanjun Dong * | | |__________________________________| | | 1127*8bfc4963SZhanjun Dong * | |______________________________________| | 1128*8bfc4963SZhanjun Dong * | ________________________________________ | 1129*8bfc4963SZhanjun Dong * | | Capture2: | | 1130*8bfc4963SZhanjun Dong * | | Hdr: CLASS=RENDER/COMPUTE, numregs=b| | 1131*8bfc4963SZhanjun Dong * | | ____________________________________ | | 1132*8bfc4963SZhanjun Dong * | | | Reglist | | | 1133*8bfc4963SZhanjun Dong * | | | - reg1, reg2, ... regb | | | 1134*8bfc4963SZhanjun Dong * | | |__________________________________| | | 1135*8bfc4963SZhanjun Dong * | |______________________________________| | 1136*8bfc4963SZhanjun Dong * | ________________________________________ | 1137*8bfc4963SZhanjun Dong * | | Capture3: | | 1138*8bfc4963SZhanjun Dong * | | Hdr: INSTANCE=RCS, numregs=c | | 1139*8bfc4963SZhanjun Dong * | | ____________________________________ | | 1140*8bfc4963SZhanjun Dong * | | | Reglist | | | 1141*8bfc4963SZhanjun Dong * | | | - reg1, reg2, ... regc | | | 1142*8bfc4963SZhanjun Dong * | | |__________________________________| | | 1143*8bfc4963SZhanjun Dong * | |______________________________________| | 1144*8bfc4963SZhanjun Dong * | ________________________________________ | 1145*8bfc4963SZhanjun Dong * | | Capture4: | | 1146*8bfc4963SZhanjun Dong * | | Hdr: CLASS=RENDER/COMPUTE, numregs=d| | 1147*8bfc4963SZhanjun Dong * | | ____________________________________ | | 1148*8bfc4963SZhanjun Dong * | | | Reglist | | | 1149*8bfc4963SZhanjun Dong * | | | - reg1, reg2, ... regd | | | 1150*8bfc4963SZhanjun Dong * | | |__________________________________| | | 1151*8bfc4963SZhanjun Dong * | |______________________________________| | 1152*8bfc4963SZhanjun Dong * | ________________________________________ | 1153*8bfc4963SZhanjun Dong * | | Capture5: | | 1154*8bfc4963SZhanjun Dong * | | Hdr: INSTANCE=CCS0, numregs=e | | 1155*8bfc4963SZhanjun Dong * | | ____________________________________ | | 1156*8bfc4963SZhanjun Dong * | | | Reglist | | | 1157*8bfc4963SZhanjun Dong * | | | - reg1, reg2, ... rege | | | 1158*8bfc4963SZhanjun Dong * | | |__________________________________| | | 1159*8bfc4963SZhanjun Dong * | |______________________________________| | 1160*8bfc4963SZhanjun Dong * |__________________________________________| 1161*8bfc4963SZhanjun Dong */ 1162*8bfc4963SZhanjun Dong is_partial = FIELD_GET(GUC_STATE_CAPTURE_GROUP_HEADER_CAPTURE_GROUP_TYPE, ghdr.info); 1163*8bfc4963SZhanjun Dong numlists = FIELD_GET(GUC_STATE_CAPTURE_GROUP_HEADER_NUM_CAPTURES, ghdr.info); 1164*8bfc4963SZhanjun Dong 1165*8bfc4963SZhanjun Dong while (numlists--) { 1166*8bfc4963SZhanjun Dong if (guc_capture_log_get_data_hdr(guc, buf, &hdr)) { 1167*8bfc4963SZhanjun Dong ret = -EIO; 1168*8bfc4963SZhanjun Dong break; 1169*8bfc4963SZhanjun Dong } 1170*8bfc4963SZhanjun Dong 1171*8bfc4963SZhanjun Dong datatype = FIELD_GET(GUC_STATE_CAPTURE_HEADER_CAPTURE_TYPE, hdr.info); 1172*8bfc4963SZhanjun Dong if (datatype > GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE) { 1173*8bfc4963SZhanjun Dong /* unknown capture type - skip over to next capture set */ 1174*8bfc4963SZhanjun Dong numregs = FIELD_GET(GUC_STATE_CAPTURE_HEADER_NUM_MMIO_ENTRIES, 1175*8bfc4963SZhanjun Dong hdr.num_mmio_entries); 1176*8bfc4963SZhanjun Dong while (numregs--) { 1177*8bfc4963SZhanjun Dong if (guc_capture_log_get_register(guc, buf, &tmp)) { 1178*8bfc4963SZhanjun Dong ret = -EIO; 1179*8bfc4963SZhanjun Dong break; 1180*8bfc4963SZhanjun Dong } 1181*8bfc4963SZhanjun Dong } 1182*8bfc4963SZhanjun Dong continue; 1183*8bfc4963SZhanjun Dong } else if (node) { 1184*8bfc4963SZhanjun Dong /* 1185*8bfc4963SZhanjun Dong * Based on the current capture type and what we have so far, 1186*8bfc4963SZhanjun Dong * decide if we should add the current node into the internal 1187*8bfc4963SZhanjun Dong * linked list for match-up when xe_devcoredump calls later 1188*8bfc4963SZhanjun Dong * (and alloc a blank node for the next set of reglists) 1189*8bfc4963SZhanjun Dong * or continue with the same node or clone the current node 1190*8bfc4963SZhanjun Dong * but only retain the global or class registers (such as the 1191*8bfc4963SZhanjun Dong * case of dependent engine resets). 1192*8bfc4963SZhanjun Dong */ 1193*8bfc4963SZhanjun Dong if (datatype == GUC_STATE_CAPTURE_TYPE_GLOBAL) { 1194*8bfc4963SZhanjun Dong guc_capture_add_node_to_outlist(guc->capture, node); 1195*8bfc4963SZhanjun Dong node = NULL; 1196*8bfc4963SZhanjun Dong } else if (datatype == GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS && 1197*8bfc4963SZhanjun Dong node->reginfo[GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS].num_regs) { 1198*8bfc4963SZhanjun Dong /* Add to list, clone node and duplicate global list */ 1199*8bfc4963SZhanjun Dong guc_capture_add_node_to_outlist(guc->capture, node); 1200*8bfc4963SZhanjun Dong node = guc_capture_clone_node(guc, node, 1201*8bfc4963SZhanjun Dong GCAP_PARSED_REGLIST_INDEX_GLOBAL); 1202*8bfc4963SZhanjun Dong } else if (datatype == GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE && 1203*8bfc4963SZhanjun Dong node->reginfo[GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE].num_regs) { 1204*8bfc4963SZhanjun Dong /* Add to list, clone node and duplicate global + class lists */ 1205*8bfc4963SZhanjun Dong guc_capture_add_node_to_outlist(guc->capture, node); 1206*8bfc4963SZhanjun Dong node = guc_capture_clone_node(guc, node, 1207*8bfc4963SZhanjun Dong (GCAP_PARSED_REGLIST_INDEX_GLOBAL | 1208*8bfc4963SZhanjun Dong GCAP_PARSED_REGLIST_INDEX_ENGCLASS)); 1209*8bfc4963SZhanjun Dong } 1210*8bfc4963SZhanjun Dong } 1211*8bfc4963SZhanjun Dong 1212*8bfc4963SZhanjun Dong if (!node) { 1213*8bfc4963SZhanjun Dong node = guc_capture_get_prealloc_node(guc); 1214*8bfc4963SZhanjun Dong if (!node) { 1215*8bfc4963SZhanjun Dong ret = -ENOMEM; 1216*8bfc4963SZhanjun Dong break; 1217*8bfc4963SZhanjun Dong } 1218*8bfc4963SZhanjun Dong if (datatype != GUC_STATE_CAPTURE_TYPE_GLOBAL) 1219*8bfc4963SZhanjun Dong xe_gt_dbg(gt, "Register capture missing global dump: %08x!\n", 1220*8bfc4963SZhanjun Dong datatype); 1221*8bfc4963SZhanjun Dong } 1222*8bfc4963SZhanjun Dong node->is_partial = is_partial; 1223*8bfc4963SZhanjun Dong node->reginfo[datatype].vfid = FIELD_GET(GUC_STATE_CAPTURE_HEADER_VFID, hdr.owner); 1224*8bfc4963SZhanjun Dong 1225*8bfc4963SZhanjun Dong switch (datatype) { 1226*8bfc4963SZhanjun Dong case GUC_STATE_CAPTURE_TYPE_ENGINE_INSTANCE: 1227*8bfc4963SZhanjun Dong node->eng_class = FIELD_GET(GUC_STATE_CAPTURE_HEADER_ENGINE_CLASS, 1228*8bfc4963SZhanjun Dong hdr.info); 1229*8bfc4963SZhanjun Dong node->eng_inst = FIELD_GET(GUC_STATE_CAPTURE_HEADER_ENGINE_INSTANCE, 1230*8bfc4963SZhanjun Dong hdr.info); 1231*8bfc4963SZhanjun Dong node->lrca = hdr.lrca; 1232*8bfc4963SZhanjun Dong node->guc_id = hdr.guc_id; 1233*8bfc4963SZhanjun Dong break; 1234*8bfc4963SZhanjun Dong case GUC_STATE_CAPTURE_TYPE_ENGINE_CLASS: 1235*8bfc4963SZhanjun Dong node->eng_class = FIELD_GET(GUC_STATE_CAPTURE_HEADER_ENGINE_CLASS, 1236*8bfc4963SZhanjun Dong hdr.info); 1237*8bfc4963SZhanjun Dong break; 1238*8bfc4963SZhanjun Dong default: 1239*8bfc4963SZhanjun Dong break; 1240*8bfc4963SZhanjun Dong } 1241*8bfc4963SZhanjun Dong 1242*8bfc4963SZhanjun Dong numregs = FIELD_GET(GUC_STATE_CAPTURE_HEADER_NUM_MMIO_ENTRIES, 1243*8bfc4963SZhanjun Dong hdr.num_mmio_entries); 1244*8bfc4963SZhanjun Dong if (numregs > guc->capture->max_mmio_per_node) { 1245*8bfc4963SZhanjun Dong xe_gt_dbg(gt, "Register capture list extraction clipped by prealloc!\n"); 1246*8bfc4963SZhanjun Dong numregs = guc->capture->max_mmio_per_node; 1247*8bfc4963SZhanjun Dong } 1248*8bfc4963SZhanjun Dong node->reginfo[datatype].num_regs = numregs; 1249*8bfc4963SZhanjun Dong regs = node->reginfo[datatype].regs; 1250*8bfc4963SZhanjun Dong i = 0; 1251*8bfc4963SZhanjun Dong while (numregs--) { 1252*8bfc4963SZhanjun Dong if (guc_capture_log_get_register(guc, buf, ®s[i++])) { 1253*8bfc4963SZhanjun Dong ret = -EIO; 1254*8bfc4963SZhanjun Dong break; 1255*8bfc4963SZhanjun Dong } 1256*8bfc4963SZhanjun Dong } 1257*8bfc4963SZhanjun Dong } 1258*8bfc4963SZhanjun Dong 1259*8bfc4963SZhanjun Dong bailout: 1260*8bfc4963SZhanjun Dong if (node) { 1261*8bfc4963SZhanjun Dong /* If we have data, add to linked list for match-up when xe_devcoredump calls */ 1262*8bfc4963SZhanjun Dong for (i = GUC_STATE_CAPTURE_TYPE_GLOBAL; i < GUC_STATE_CAPTURE_TYPE_MAX; ++i) { 1263*8bfc4963SZhanjun Dong if (node->reginfo[i].regs) { 1264*8bfc4963SZhanjun Dong guc_capture_add_node_to_outlist(guc->capture, node); 1265*8bfc4963SZhanjun Dong node = NULL; 1266*8bfc4963SZhanjun Dong break; 1267*8bfc4963SZhanjun Dong } 1268*8bfc4963SZhanjun Dong } 1269*8bfc4963SZhanjun Dong if (node) /* else return it back to cache list */ 1270*8bfc4963SZhanjun Dong guc_capture_add_node_to_cachelist(guc->capture, node); 1271*8bfc4963SZhanjun Dong } 1272*8bfc4963SZhanjun Dong return ret; 1273*8bfc4963SZhanjun Dong } 1274*8bfc4963SZhanjun Dong 1275*8bfc4963SZhanjun Dong static int __guc_capture_flushlog_complete(struct xe_guc *guc) 1276*8bfc4963SZhanjun Dong { 1277*8bfc4963SZhanjun Dong u32 action[] = { 1278*8bfc4963SZhanjun Dong XE_GUC_ACTION_LOG_BUFFER_FILE_FLUSH_COMPLETE, 1279*8bfc4963SZhanjun Dong GUC_LOG_BUFFER_CAPTURE 1280*8bfc4963SZhanjun Dong }; 1281*8bfc4963SZhanjun Dong 1282*8bfc4963SZhanjun Dong return xe_guc_ct_send_g2h_handler(&guc->ct, action, ARRAY_SIZE(action)); 1283*8bfc4963SZhanjun Dong } 1284*8bfc4963SZhanjun Dong 1285*8bfc4963SZhanjun Dong static void __guc_capture_process_output(struct xe_guc *guc) 1286*8bfc4963SZhanjun Dong { 1287*8bfc4963SZhanjun Dong unsigned int buffer_size, read_offset, write_offset, full_count; 1288*8bfc4963SZhanjun Dong struct xe_uc *uc = container_of(guc, typeof(*uc), guc); 1289*8bfc4963SZhanjun Dong struct guc_log_buffer_state log_buf_state_local; 1290*8bfc4963SZhanjun Dong struct __guc_capture_bufstate buf; 1291*8bfc4963SZhanjun Dong bool new_overflow; 1292*8bfc4963SZhanjun Dong int ret, tmp; 1293*8bfc4963SZhanjun Dong u32 log_buf_state_offset; 1294*8bfc4963SZhanjun Dong u32 src_data_offset; 1295*8bfc4963SZhanjun Dong 1296*8bfc4963SZhanjun Dong log_buf_state_offset = sizeof(struct guc_log_buffer_state) * GUC_LOG_BUFFER_CAPTURE; 1297*8bfc4963SZhanjun Dong src_data_offset = xe_guc_get_log_buffer_offset(&guc->log, GUC_LOG_BUFFER_CAPTURE); 1298*8bfc4963SZhanjun Dong 1299*8bfc4963SZhanjun Dong /* 1300*8bfc4963SZhanjun Dong * Make a copy of the state structure, inside GuC log buffer 1301*8bfc4963SZhanjun Dong * (which is uncached mapped), on the stack to avoid reading 1302*8bfc4963SZhanjun Dong * from it multiple times. 1303*8bfc4963SZhanjun Dong */ 1304*8bfc4963SZhanjun Dong xe_map_memcpy_from(guc_to_xe(guc), &log_buf_state_local, &guc->log.bo->vmap, 1305*8bfc4963SZhanjun Dong log_buf_state_offset, sizeof(struct guc_log_buffer_state)); 1306*8bfc4963SZhanjun Dong 1307*8bfc4963SZhanjun Dong buffer_size = xe_guc_get_log_buffer_size(&guc->log, GUC_LOG_BUFFER_CAPTURE); 1308*8bfc4963SZhanjun Dong read_offset = log_buf_state_local.read_ptr; 1309*8bfc4963SZhanjun Dong write_offset = log_buf_state_local.sampled_write_ptr; 1310*8bfc4963SZhanjun Dong full_count = FIELD_GET(GUC_LOG_BUFFER_STATE_BUFFER_FULL_CNT, log_buf_state_local.flags); 1311*8bfc4963SZhanjun Dong 1312*8bfc4963SZhanjun Dong /* Bookkeeping stuff */ 1313*8bfc4963SZhanjun Dong tmp = FIELD_GET(GUC_LOG_BUFFER_STATE_FLUSH_TO_FILE, log_buf_state_local.flags); 1314*8bfc4963SZhanjun Dong guc->log.stats[GUC_LOG_BUFFER_CAPTURE].flush += tmp; 1315*8bfc4963SZhanjun Dong new_overflow = xe_guc_check_log_buf_overflow(&guc->log, GUC_LOG_BUFFER_CAPTURE, 1316*8bfc4963SZhanjun Dong full_count); 1317*8bfc4963SZhanjun Dong 1318*8bfc4963SZhanjun Dong /* Now copy the actual logs. */ 1319*8bfc4963SZhanjun Dong if (unlikely(new_overflow)) { 1320*8bfc4963SZhanjun Dong /* copy the whole buffer in case of overflow */ 1321*8bfc4963SZhanjun Dong read_offset = 0; 1322*8bfc4963SZhanjun Dong write_offset = buffer_size; 1323*8bfc4963SZhanjun Dong } else if (unlikely((read_offset > buffer_size) || 1324*8bfc4963SZhanjun Dong (write_offset > buffer_size))) { 1325*8bfc4963SZhanjun Dong xe_gt_err(guc_to_gt(guc), 1326*8bfc4963SZhanjun Dong "Register capture buffer in invalid state: read = 0x%X, size = 0x%X!\n", 1327*8bfc4963SZhanjun Dong read_offset, buffer_size); 1328*8bfc4963SZhanjun Dong /* copy whole buffer as offsets are unreliable */ 1329*8bfc4963SZhanjun Dong read_offset = 0; 1330*8bfc4963SZhanjun Dong write_offset = buffer_size; 1331*8bfc4963SZhanjun Dong } 1332*8bfc4963SZhanjun Dong 1333*8bfc4963SZhanjun Dong buf.size = buffer_size; 1334*8bfc4963SZhanjun Dong buf.rd = read_offset; 1335*8bfc4963SZhanjun Dong buf.wr = write_offset; 1336*8bfc4963SZhanjun Dong buf.data_offset = src_data_offset; 1337*8bfc4963SZhanjun Dong 1338*8bfc4963SZhanjun Dong if (!xe_guc_read_stopped(guc)) { 1339*8bfc4963SZhanjun Dong do { 1340*8bfc4963SZhanjun Dong ret = guc_capture_extract_reglists(guc, &buf); 1341*8bfc4963SZhanjun Dong if (ret && ret != -ENODATA) 1342*8bfc4963SZhanjun Dong xe_gt_dbg(guc_to_gt(guc), "Capture extraction failed:%d\n", ret); 1343*8bfc4963SZhanjun Dong } while (ret >= 0); 1344*8bfc4963SZhanjun Dong } 1345*8bfc4963SZhanjun Dong 1346*8bfc4963SZhanjun Dong /* Update the state of log buffer err-cap state */ 1347*8bfc4963SZhanjun Dong xe_map_wr(guc_to_xe(guc), &guc->log.bo->vmap, 1348*8bfc4963SZhanjun Dong log_buf_state_offset + offsetof(struct guc_log_buffer_state, read_ptr), u32, 1349*8bfc4963SZhanjun Dong write_offset); 1350*8bfc4963SZhanjun Dong 1351*8bfc4963SZhanjun Dong /* 1352*8bfc4963SZhanjun Dong * Clear the flush_to_file from local first, the local was loaded by above 1353*8bfc4963SZhanjun Dong * xe_map_memcpy_from, then write out the "updated local" through 1354*8bfc4963SZhanjun Dong * xe_map_wr() 1355*8bfc4963SZhanjun Dong */ 1356*8bfc4963SZhanjun Dong log_buf_state_local.flags &= ~GUC_LOG_BUFFER_STATE_FLUSH_TO_FILE; 1357*8bfc4963SZhanjun Dong xe_map_wr(guc_to_xe(guc), &guc->log.bo->vmap, 1358*8bfc4963SZhanjun Dong log_buf_state_offset + offsetof(struct guc_log_buffer_state, flags), u32, 1359*8bfc4963SZhanjun Dong log_buf_state_local.flags); 1360*8bfc4963SZhanjun Dong __guc_capture_flushlog_complete(guc); 1361*8bfc4963SZhanjun Dong } 1362*8bfc4963SZhanjun Dong 1363*8bfc4963SZhanjun Dong /* 1364*8bfc4963SZhanjun Dong * xe_guc_capture_process - Process GuC register captured data 1365*8bfc4963SZhanjun Dong * @guc: The GuC object 1366*8bfc4963SZhanjun Dong * 1367*8bfc4963SZhanjun Dong * When GuC captured data is ready, GuC will send message 1368*8bfc4963SZhanjun Dong * XE_GUC_ACTION_STATE_CAPTURE_NOTIFICATION to host, this function will be 1369*8bfc4963SZhanjun Dong * called to process the data comes with the message. 1370*8bfc4963SZhanjun Dong * 1371*8bfc4963SZhanjun Dong * Returns: None 1372*8bfc4963SZhanjun Dong */ 1373*8bfc4963SZhanjun Dong void xe_guc_capture_process(struct xe_guc *guc) 1374*8bfc4963SZhanjun Dong { 1375*8bfc4963SZhanjun Dong if (guc->capture) 1376*8bfc4963SZhanjun Dong __guc_capture_process_output(guc); 1377*8bfc4963SZhanjun Dong } 1378*8bfc4963SZhanjun Dong 1379*8bfc4963SZhanjun Dong static struct __guc_capture_parsed_output * 1380*8bfc4963SZhanjun Dong guc_capture_alloc_one_node(struct xe_guc *guc) 1381*8bfc4963SZhanjun Dong { 1382*8bfc4963SZhanjun Dong struct drm_device *drm = guc_to_drm(guc); 1383*8bfc4963SZhanjun Dong struct __guc_capture_parsed_output *new; 1384*8bfc4963SZhanjun Dong int i; 1385*8bfc4963SZhanjun Dong 1386*8bfc4963SZhanjun Dong new = drmm_kzalloc(drm, sizeof(*new), GFP_KERNEL); 1387*8bfc4963SZhanjun Dong if (!new) 1388*8bfc4963SZhanjun Dong return NULL; 1389*8bfc4963SZhanjun Dong 1390*8bfc4963SZhanjun Dong for (i = 0; i < GUC_STATE_CAPTURE_TYPE_MAX; ++i) { 1391*8bfc4963SZhanjun Dong new->reginfo[i].regs = drmm_kzalloc(drm, guc->capture->max_mmio_per_node * 1392*8bfc4963SZhanjun Dong sizeof(struct guc_mmio_reg), GFP_KERNEL); 1393*8bfc4963SZhanjun Dong if (!new->reginfo[i].regs) { 1394*8bfc4963SZhanjun Dong while (i) 1395*8bfc4963SZhanjun Dong drmm_kfree(drm, new->reginfo[--i].regs); 1396*8bfc4963SZhanjun Dong drmm_kfree(drm, new); 1397*8bfc4963SZhanjun Dong return NULL; 1398*8bfc4963SZhanjun Dong } 1399*8bfc4963SZhanjun Dong } 1400*8bfc4963SZhanjun Dong guc_capture_init_node(guc, new); 1401*8bfc4963SZhanjun Dong 1402*8bfc4963SZhanjun Dong return new; 1403*8bfc4963SZhanjun Dong } 1404*8bfc4963SZhanjun Dong 1405*8bfc4963SZhanjun Dong static void 1406*8bfc4963SZhanjun Dong __guc_capture_create_prealloc_nodes(struct xe_guc *guc) 1407*8bfc4963SZhanjun Dong { 1408*8bfc4963SZhanjun Dong struct __guc_capture_parsed_output *node = NULL; 1409*8bfc4963SZhanjun Dong int i; 1410*8bfc4963SZhanjun Dong 1411*8bfc4963SZhanjun Dong for (i = 0; i < PREALLOC_NODES_MAX_COUNT; ++i) { 1412*8bfc4963SZhanjun Dong node = guc_capture_alloc_one_node(guc); 1413*8bfc4963SZhanjun Dong if (!node) { 1414*8bfc4963SZhanjun Dong xe_gt_warn(guc_to_gt(guc), "Register capture pre-alloc-cache failure\n"); 1415*8bfc4963SZhanjun Dong /* dont free the priors, use what we got and cleanup at shutdown */ 1416*8bfc4963SZhanjun Dong return; 1417*8bfc4963SZhanjun Dong } 1418*8bfc4963SZhanjun Dong guc_capture_add_node_to_cachelist(guc->capture, node); 1419*8bfc4963SZhanjun Dong } 1420*8bfc4963SZhanjun Dong } 1421*8bfc4963SZhanjun Dong 1422*8bfc4963SZhanjun Dong static int 1423*8bfc4963SZhanjun Dong guc_get_max_reglist_count(struct xe_guc *guc) 1424*8bfc4963SZhanjun Dong { 1425*8bfc4963SZhanjun Dong int i, j, k, tmp, maxregcount = 0; 1426*8bfc4963SZhanjun Dong 1427*8bfc4963SZhanjun Dong for (i = 0; i < GUC_CAPTURE_LIST_INDEX_MAX; ++i) { 1428*8bfc4963SZhanjun Dong for (j = 0; j < GUC_STATE_CAPTURE_TYPE_MAX; ++j) { 1429*8bfc4963SZhanjun Dong for (k = 0; k < GUC_CAPTURE_LIST_CLASS_MAX; ++k) { 1430*8bfc4963SZhanjun Dong const struct __guc_mmio_reg_descr_group *match; 1431*8bfc4963SZhanjun Dong 1432*8bfc4963SZhanjun Dong if (j == GUC_STATE_CAPTURE_TYPE_GLOBAL && k > 0) 1433*8bfc4963SZhanjun Dong continue; 1434*8bfc4963SZhanjun Dong 1435*8bfc4963SZhanjun Dong tmp = 0; 1436*8bfc4963SZhanjun Dong match = guc_capture_get_one_list(guc->capture->reglists, i, j, k); 1437*8bfc4963SZhanjun Dong if (match) 1438*8bfc4963SZhanjun Dong tmp = match->num_regs; 1439*8bfc4963SZhanjun Dong 1440*8bfc4963SZhanjun Dong match = guc_capture_get_one_list(guc->capture->extlists, i, j, k); 1441*8bfc4963SZhanjun Dong if (match) 1442*8bfc4963SZhanjun Dong tmp += match->num_regs; 1443*8bfc4963SZhanjun Dong 1444*8bfc4963SZhanjun Dong if (tmp > maxregcount) 1445*8bfc4963SZhanjun Dong maxregcount = tmp; 1446*8bfc4963SZhanjun Dong } 1447*8bfc4963SZhanjun Dong } 1448*8bfc4963SZhanjun Dong } 1449*8bfc4963SZhanjun Dong if (!maxregcount) 1450*8bfc4963SZhanjun Dong maxregcount = PREALLOC_NODES_DEFAULT_NUMREGS; 1451*8bfc4963SZhanjun Dong 1452*8bfc4963SZhanjun Dong return maxregcount; 1453*8bfc4963SZhanjun Dong } 1454*8bfc4963SZhanjun Dong 1455*8bfc4963SZhanjun Dong static void 1456*8bfc4963SZhanjun Dong guc_capture_create_prealloc_nodes(struct xe_guc *guc) 1457*8bfc4963SZhanjun Dong { 1458*8bfc4963SZhanjun Dong /* skip if we've already done the pre-alloc */ 1459*8bfc4963SZhanjun Dong if (guc->capture->max_mmio_per_node) 1460*8bfc4963SZhanjun Dong return; 1461*8bfc4963SZhanjun Dong 1462*8bfc4963SZhanjun Dong guc->capture->max_mmio_per_node = guc_get_max_reglist_count(guc); 1463*8bfc4963SZhanjun Dong __guc_capture_create_prealloc_nodes(guc); 1464*8bfc4963SZhanjun Dong } 1465*8bfc4963SZhanjun Dong 1466b170d696SZhanjun Dong /* 1467b170d696SZhanjun Dong * xe_guc_capture_steered_list_init - Init steering register list 1468b170d696SZhanjun Dong * @guc: The GuC object 1469b170d696SZhanjun Dong * 1470*8bfc4963SZhanjun Dong * Init steering register list for GuC register capture, create pre-alloc node 1471b170d696SZhanjun Dong */ 1472b170d696SZhanjun Dong void xe_guc_capture_steered_list_init(struct xe_guc *guc) 1473b170d696SZhanjun Dong { 1474b170d696SZhanjun Dong /* 1475b170d696SZhanjun Dong * For certain engine classes, there are slice and subslice 1476b170d696SZhanjun Dong * level registers requiring steering. We allocate and populate 1477b170d696SZhanjun Dong * these based on hw config and add it as an extension list at 1478b170d696SZhanjun Dong * the end of the pre-populated render list. 1479b170d696SZhanjun Dong */ 1480b170d696SZhanjun Dong guc_capture_alloc_steered_lists(guc); 148184d15f42SZhanjun Dong check_guc_capture_size(guc); 1482*8bfc4963SZhanjun Dong guc_capture_create_prealloc_nodes(guc); 1483b170d696SZhanjun Dong } 1484b170d696SZhanjun Dong 148584d15f42SZhanjun Dong /* 14869c8c7a7eSZhanjun Dong * xe_guc_capture_init - Init for GuC register capture 14879c8c7a7eSZhanjun Dong * @guc: The GuC object 14889c8c7a7eSZhanjun Dong * 14899c8c7a7eSZhanjun Dong * Init for GuC register capture, alloc memory for capture data structure. 14909c8c7a7eSZhanjun Dong * 14919c8c7a7eSZhanjun Dong * Returns: 0 if success. 1492b170d696SZhanjun Dong * -ENOMEM if out of memory 14939c8c7a7eSZhanjun Dong */ 14949c8c7a7eSZhanjun Dong int xe_guc_capture_init(struct xe_guc *guc) 14959c8c7a7eSZhanjun Dong { 14969c8c7a7eSZhanjun Dong guc->capture = drmm_kzalloc(guc_to_drm(guc), sizeof(*guc->capture), GFP_KERNEL); 14979c8c7a7eSZhanjun Dong if (!guc->capture) 14989c8c7a7eSZhanjun Dong return -ENOMEM; 14999c8c7a7eSZhanjun Dong 15009c8c7a7eSZhanjun Dong guc->capture->reglists = guc_capture_get_device_reglist(guc_to_xe(guc)); 1501*8bfc4963SZhanjun Dong 1502*8bfc4963SZhanjun Dong INIT_LIST_HEAD(&guc->capture->outlist); 1503*8bfc4963SZhanjun Dong INIT_LIST_HEAD(&guc->capture->cachelist); 1504*8bfc4963SZhanjun Dong 15059c8c7a7eSZhanjun Dong return 0; 15069c8c7a7eSZhanjun Dong } 1507