/* * Copyright © 2022 Imagination Technologies Ltd. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include #include #include "pvr_bo.h" #include "pvr_csb.h" #include "pvr_csb_enum_helpers.h" #include "pvr_device_info.h" #include "pvr_dump.h" #include "pvr_dump_bo.h" #include "pvr_private.h" #include "pvr_util.h" #include "util/list.h" #include "util/macros.h" #include "util/u_math.h" #include "vulkan/util/vk_enum_to_str.h" /***************************************************************************** Utilities ******************************************************************************/ #define PVR_DUMP_CSB_WORD_SIZE ((unsigned)sizeof(uint32_t)) enum buffer_type { BUFFER_TYPE_NONE = 0, BUFFER_TYPE_CDMCTRL, BUFFER_TYPE_VDMCTRL, BUFFER_TYPE_PPP, BUFFER_TYPE_INVALID, /* Must be last. */ }; struct pvr_dump_csb_ctx { struct pvr_dump_buffer_ctx base; /* User-modifiable values */ uint32_t next_block_idx; }; static inline bool pvr_dump_csb_ctx_push(struct pvr_dump_csb_ctx *const ctx, struct pvr_dump_buffer_ctx *const parent_ctx) { if (!pvr_dump_buffer_ctx_push(&ctx->base, &parent_ctx->base, parent_ctx->ptr, parent_ctx->remaining_size)) { return false; } ctx->next_block_idx = 0; return true; } static inline struct pvr_dump_buffer_ctx * pvr_dump_csb_ctx_pop(struct pvr_dump_csb_ctx *const ctx, bool advance_parent) { struct pvr_dump_buffer_ctx *parent; struct pvr_dump_ctx *parent_base; const uint64_t unused_words = ctx->base.remaining_size / PVR_DUMP_CSB_WORD_SIZE; if (unused_words) { pvr_dump_buffer_print_header_line(&ctx->base, "<%" PRIu64 " unused word%s (%" PRIu64 " bytes)>", unused_words, unused_words == 1 ? "" : "s", unused_words * PVR_DUMP_CSB_WORD_SIZE); pvr_dump_buffer_advance(&ctx->base, unused_words * PVR_DUMP_CSB_WORD_SIZE); } pvr_dump_buffer_print_header_line(&ctx->base, ""); parent_base = pvr_dump_buffer_ctx_pop(&ctx->base); if (!parent_base) return NULL; parent = container_of(parent_base, struct pvr_dump_buffer_ctx, base); if (advance_parent) pvr_dump_buffer_advance(parent, ctx->base.capacity); return parent; } struct pvr_dump_csb_block_ctx { struct pvr_dump_buffer_ctx base; }; #define pvr_dump_csb_block_ctx_push(ctx, \ parent_ctx, \ header_format, \ header_args...) \ ({ \ struct pvr_dump_csb_ctx *const _csb_ctx = (parent_ctx); \ pvr_dump_buffer_print_header_line(&_csb_ctx->base, \ "%" PRIu32 ": " header_format, \ _csb_ctx->next_block_idx, \ ##header_args); \ __pvr_dump_csb_block_ctx_push(ctx, _csb_ctx); \ }) static inline bool __pvr_dump_csb_block_ctx_push(struct pvr_dump_csb_block_ctx *const ctx, struct pvr_dump_csb_ctx *const parent_ctx) { pvr_dump_indent(&parent_ctx->base.base); if (!pvr_dump_buffer_ctx_push(&ctx->base, &parent_ctx->base.base, parent_ctx->base.ptr, parent_ctx->base.remaining_size)) { return false; } parent_ctx->next_block_idx++; return true; } static inline struct pvr_dump_csb_ctx * pvr_dump_csb_block_ctx_pop(struct pvr_dump_csb_block_ctx *const ctx) { const uint64_t used_size = ctx->base.capacity - ctx->base.remaining_size; struct pvr_dump_csb_ctx *parent_ctx; struct pvr_dump_ctx *parent_base; parent_base = pvr_dump_buffer_ctx_pop(&ctx->base); if (!parent_base) return NULL; parent_ctx = container_of(parent_base, struct pvr_dump_csb_ctx, base.base); /* No need to check this since it can never fail. */ pvr_dump_buffer_advance(&parent_ctx->base, used_size); pvr_dump_dedent(parent_base); return parent_ctx; } static inline const uint32_t * pvr_dump_csb_block_take(struct pvr_dump_csb_block_ctx *const restrict ctx, const uint32_t nr_words) { return pvr_dump_buffer_take(&ctx->base, nr_words * PVR_DUMP_CSB_WORD_SIZE); } #define pvr_dump_csb_block_take_packed(ctx, cmd, dest) \ ({ \ struct pvr_dump_csb_block_ctx *const _block_ctx = (ctx); \ struct PVRX(cmd) *const _dest = (dest); \ const void *const _ptr = \ pvr_dump_csb_block_take(_block_ctx, pvr_cmd_length(cmd)); \ if (_ptr) { \ pvr_cmd_unpack(cmd)(_ptr, _dest); \ } else { \ pvr_dump_field_error(&_block_ctx->base.base, \ "failed to unpack word(s)"); \ } \ !!_ptr; \ }) /***************************************************************************** Feature dumping ******************************************************************************/ static inline void __pvr_dump_field_needs_feature(struct pvr_dump_ctx *const ctx, const char *const name, const char *const feature) { pvr_dump_field(ctx, name, "", feature); } #define pvr_dump_field_needs_feature(ctx, name, feature) \ do { \ (void)PVR_HAS_FEATURE((struct pvr_device_info *)NULL, feature); \ __pvr_dump_field_needs_feature(ctx, name, #feature); \ } while (0) #define pvr_dump_field_member_needs_feature(ctx, compound, member, feature) \ do { \ (void)&(compound)->member; \ pvr_dump_field_needs_feature(ctx, #member, feature); \ } while (0) /****************************************************************************** Sub buffer printer declaration ******************************************************************************/ static bool print_sub_buffer(struct pvr_dump_ctx *ctx, struct pvr_device *device, enum buffer_type type, pvr_dev_addr_t addr, uint64_t expected_size, char const *size_src); /****************************************************************************** Block printers ******************************************************************************/ static uint32_t print_block_cdmctrl_kernel(struct pvr_dump_csb_ctx *const csb_ctx, struct pvr_device *const device) { const pvr_dev_addr_t pds_heap_base = device->heaps.pds_heap->base_addr; struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(CDMCTRL_KERNEL0) kernel0 = { 0 }; struct PVRX(CDMCTRL_KERNEL1) kernel1 = { 0 }; struct PVRX(CDMCTRL_KERNEL2) kernel2 = { 0 }; struct PVRX(CDMCTRL_KERNEL3) kernel3 = { 0 }; struct PVRX(CDMCTRL_KERNEL4) kernel4 = { 0 }; struct PVRX(CDMCTRL_KERNEL5) kernel5 = { 0 }; struct PVRX(CDMCTRL_KERNEL6) kernel6 = { 0 }; struct PVRX(CDMCTRL_KERNEL7) kernel7 = { 0 }; struct PVRX(CDMCTRL_KERNEL8) kernel8 = { 0 }; struct PVRX(CDMCTRL_KERNEL9) kernel9 = { 0 }; struct PVRX(CDMCTRL_KERNEL10) kernel10 = { 0 }; struct PVRX(CDMCTRL_KERNEL11) kernel11 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "KERNEL")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL0, &kernel0) || !pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL1, &kernel1) || !pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL2, &kernel2)) { goto end_pop_ctx; } words_read += 3; if (!kernel0.indirect_present) { if (!pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL3, &kernel3) || !pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL4, &kernel4) || !pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL5, &kernel5)) { goto end_pop_ctx; } words_read += 3; } else { if (!pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL6, &kernel6) || !pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL7, &kernel7)) { goto end_pop_ctx; } words_read += 2; } if (!pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL8, &kernel8)) goto end_pop_ctx; words_read += 1; if (!pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL9, &kernel9) || !pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL10, &kernel10) || !pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_KERNEL11, &kernel11)) { goto end_pop_ctx; } words_read += 3; pvr_dump_field_member_bool(base_ctx, &kernel0, indirect_present); pvr_dump_field_member_bool(base_ctx, &kernel0, global_offsets_present); pvr_dump_field_member_bool(base_ctx, &kernel0, event_object_present); pvr_dump_field_member_u32_scaled_units( base_ctx, &kernel0, usc_common_size, PVRX(CDMCTRL_KERNEL0_USC_COMMON_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_scaled_units( base_ctx, &kernel0, usc_unified_size, PVRX(CDMCTRL_KERNEL0_USC_UNIFIED_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_scaled_units( base_ctx, &kernel0, pds_temp_size, PVRX(CDMCTRL_KERNEL0_PDS_TEMP_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_scaled_units( base_ctx, &kernel0, pds_data_size, PVRX(CDMCTRL_KERNEL0_PDS_DATA_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_enum(base_ctx, &kernel0, usc_target, pvr_cmd_enum_to_str(CDMCTRL_USC_TARGET)); pvr_dump_field_member_bool(base_ctx, &kernel0, fence); pvr_dump_field_member_addr_offset(base_ctx, &kernel1, data_addr, pds_heap_base); ret = print_sub_buffer( base_ctx, device, BUFFER_TYPE_NONE, PVR_DEV_ADDR_OFFSET(pds_heap_base, kernel1.data_addr.addr), kernel0.pds_data_size * PVRX(CDMCTRL_KERNEL0_PDS_DATA_SIZE_UNIT_SIZE), "pds_data_size"); if (!ret) goto end_pop_ctx; pvr_dump_field_member_enum(base_ctx, &kernel1, sd_type, pvr_cmd_enum_to_str(CDMCTRL_SD_TYPE)); pvr_dump_field_member_bool(base_ctx, &kernel1, usc_common_shared); pvr_dump_field_member_addr_offset(base_ctx, &kernel2, code_addr, pds_heap_base); /* FIXME: Determine the exact size of the PDS code section once disassembly * is implemented. */ ret = print_sub_buffer(base_ctx, device, BUFFER_TYPE_NONE, PVR_DEV_ADDR_OFFSET(pds_heap_base, kernel2.code_addr.addr), 0, NULL); if (!ret) goto end_pop_ctx; pvr_dump_field_member_bool(base_ctx, &kernel2, one_wg_per_task); if (!kernel0.indirect_present) { pvr_dump_field_member_u32_offset(base_ctx, &kernel3, workgroup_x, 1); pvr_dump_field_member_u32_offset(base_ctx, &kernel4, workgroup_y, 1); pvr_dump_field_member_u32_offset(base_ctx, &kernel5, workgroup_z, 1); pvr_dump_field_not_present(base_ctx, "indirect_addr"); } else { pvr_dump_field_member_not_present(base_ctx, &kernel3, workgroup_x); pvr_dump_field_member_not_present(base_ctx, &kernel4, workgroup_y); pvr_dump_field_member_not_present(base_ctx, &kernel5, workgroup_z); pvr_dump_field_addr_split(base_ctx, "indirect_addr", kernel6.indirect_addrmsb, kernel7.indirect_addrlsb); } pvr_dump_field_member_u32_zero(base_ctx, &kernel8, max_instances, 32); pvr_dump_field_member_u32_offset(base_ctx, &kernel8, workgroup_size_x, 1); pvr_dump_field_member_u32_offset(base_ctx, &kernel8, workgroup_size_y, 1); pvr_dump_field_member_u32_offset(base_ctx, &kernel8, workgroup_size_z, 1); if (kernel0.event_object_present) { pvr_dump_field_member_u32(base_ctx, &kernel9, global_offset_x); pvr_dump_field_member_u32(base_ctx, &kernel10, global_offset_y); pvr_dump_field_member_u32(base_ctx, &kernel11, global_offset_z); } else { pvr_dump_field_member_not_present(base_ctx, &kernel9, global_offset_x); pvr_dump_field_member_not_present(base_ctx, &kernel10, global_offset_y); pvr_dump_field_member_not_present(base_ctx, &kernel11, global_offset_z); } ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_cdmctrl_stream_link(struct pvr_dump_csb_ctx *const csb_ctx) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(CDMCTRL_STREAM_LINK0) link0 = { 0 }; struct PVRX(CDMCTRL_STREAM_LINK1) link1 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "STREAM_LINK")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_STREAM_LINK0, &link0) || !pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_STREAM_LINK1, &link1)) { goto end_pop_ctx; } words_read += 2; pvr_dump_field_addr_split(base_ctx, "link_addr", link0.link_addrmsb, link1.link_addrlsb); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_cdmctrl_stream_terminate(struct pvr_dump_csb_ctx *const csb_ctx) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(CDMCTRL_STREAM_TERMINATE) terminate = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "TERMINATE")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, CDMCTRL_STREAM_TERMINATE, &terminate)) { goto end_pop_ctx; } words_read += 1; pvr_dump_field_no_fields(base_ctx); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_vdmctrl_ppp_state_update(struct pvr_dump_csb_ctx *const csb_ctx, struct pvr_device *const device) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(VDMCTRL_PPP_STATE0) state0 = { 0 }; struct PVRX(VDMCTRL_PPP_STATE1) state1 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "PPP_STATE_UPDATE")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_PPP_STATE0, &state0) || !pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_PPP_STATE1, &state1)) { goto end_pop_ctx; } words_read += 2; pvr_dump_field_member_u32_zero(base_ctx, &state0, word_count, 256); pvr_dump_field_addr_split(base_ctx, "addr", state0.addrmsb, state1.addrlsb); ret = print_sub_buffer( base_ctx, device, BUFFER_TYPE_PPP, PVR_DEV_ADDR(state0.addrmsb.addr | state1.addrlsb.addr), (state0.word_count ? state0.word_count : 256) * PVR_DUMP_CSB_WORD_SIZE, "word_count"); if (!ret) goto end_pop_ctx; ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_vdmctrl_pds_state_update(struct pvr_dump_csb_ctx *const csb_ctx, struct pvr_device *const device) { const pvr_dev_addr_t pds_heap_base = device->heaps.pds_heap->base_addr; struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(VDMCTRL_PDS_STATE0) state0 = { 0 }; struct PVRX(VDMCTRL_PDS_STATE1) state1 = { 0 }; struct PVRX(VDMCTRL_PDS_STATE2) state2 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "PDS_STATE_UPDATE")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_PDS_STATE0, &state0) || !pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_PDS_STATE1, &state1) || !pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_PDS_STATE2, &state2)) { goto end_pop_ctx; } words_read += 3; pvr_dump_field_member_enum(base_ctx, &state0, dm_target, pvr_cmd_enum_to_str(VDMCTRL_DM_TARGET)); pvr_dump_field_member_enum(base_ctx, &state0, usc_target, pvr_cmd_enum_to_str(VDMCTRL_USC_TARGET)); pvr_dump_field_member_u32_scaled_units( base_ctx, &state0, usc_common_size, PVRX(VDMCTRL_PDS_STATE0_USC_COMMON_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_scaled_units( base_ctx, &state0, usc_unified_size, PVRX(VDMCTRL_PDS_STATE0_USC_UNIFIED_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_scaled_units( base_ctx, &state0, pds_temp_size, PVRX(VDMCTRL_PDS_STATE0_PDS_TEMP_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_scaled_units( base_ctx, &state0, pds_data_size, PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_addr_offset(base_ctx, &state1, pds_data_addr, pds_heap_base); ret = print_sub_buffer( base_ctx, device, BUFFER_TYPE_NONE, PVR_DEV_ADDR_OFFSET(pds_heap_base, state1.pds_data_addr.addr), state0.pds_data_size * PVRX(VDMCTRL_PDS_STATE0_PDS_DATA_SIZE_UNIT_SIZE), "pds_data_size"); if (!ret) goto end_pop_ctx; pvr_dump_field_member_enum(base_ctx, &state1, sd_type, pvr_cmd_enum_to_str(VDMCTRL_SD_TYPE)); pvr_dump_field_member_enum(base_ctx, &state1, sd_next_type, pvr_cmd_enum_to_str(VDMCTRL_SD_TYPE)); pvr_dump_field_member_addr_offset(base_ctx, &state2, pds_code_addr, pds_heap_base); /* FIXME: Determine the exact size of the PDS code section once disassembly * is implemented. */ ret = print_sub_buffer(base_ctx, device, BUFFER_TYPE_NONE, PVR_DEV_ADDR_OFFSET(pds_heap_base, state2.pds_code_addr.addr), 0, NULL); if (!ret) goto end_pop_ctx; ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_vdmctrl_vdm_state_update(struct pvr_dump_csb_ctx *const csb_ctx, struct pvr_device *const device) { const pvr_dev_addr_t pds_heap_base = device->heaps.pds_heap->base_addr; struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(VDMCTRL_VDM_STATE0) state0 = { 0 }; struct PVRX(VDMCTRL_VDM_STATE1) state1 = { 0 }; struct PVRX(VDMCTRL_VDM_STATE2) state2 = { 0 }; struct PVRX(VDMCTRL_VDM_STATE3) state3 = { 0 }; struct PVRX(VDMCTRL_VDM_STATE4) state4 = { 0 }; struct PVRX(VDMCTRL_VDM_STATE5) state5 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "VDM_STATE_UPDATE")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_VDM_STATE0, &state0)) goto end_pop_ctx; words_read += 1; if (state0.cut_index_present) { if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_VDM_STATE1, &state1)) goto end_pop_ctx; words_read += 1; } if (state0.vs_data_addr_present) { if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_VDM_STATE2, &state2)) goto end_pop_ctx; words_read += 1; } if (state0.vs_other_present) { if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_VDM_STATE3, &state3) || !pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_VDM_STATE4, &state4) || !pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_VDM_STATE5, &state5)) { goto end_pop_ctx; } words_read += 3; } if (state0.cut_index_present) { pvr_dump_field_member_x32(base_ctx, &state1, cut_index, 8); } else { pvr_dump_field_member_not_present(base_ctx, &state1, cut_index); } if (state0.vs_data_addr_present) { pvr_dump_field_member_addr_offset(base_ctx, &state2, vs_pds_data_base_addr, pds_heap_base); if (state0.vs_other_present) { ret = print_sub_buffer( base_ctx, device, BUFFER_TYPE_NONE, PVR_DEV_ADDR_OFFSET(pds_heap_base, state2.vs_pds_data_base_addr.addr), state5.vs_pds_data_size * PVRX(VDMCTRL_VDM_STATE5_VS_PDS_DATA_SIZE_UNIT_SIZE), "pds_data_size"); } else { /* FIXME: Determine the exact size of the PDS data section when no * code section is present once disassembly is implemented. */ ret = print_sub_buffer( base_ctx, device, BUFFER_TYPE_NONE, PVR_DEV_ADDR_OFFSET(pds_heap_base, state2.vs_pds_data_base_addr.addr), 0, NULL); } if (!ret) goto end_pop_ctx; } else { pvr_dump_field_member_not_present(base_ctx, &state2, vs_pds_data_base_addr); } if (state0.vs_other_present) { pvr_dump_field_member_addr_offset(base_ctx, &state3, vs_pds_code_base_addr, pds_heap_base); /* FIXME: Determine the exact size of the PDS code section once * disassembly is implemented. */ ret = print_sub_buffer( base_ctx, device, BUFFER_TYPE_NONE, PVR_DEV_ADDR_OFFSET(pds_heap_base, state3.vs_pds_code_base_addr.addr), 0, NULL); if (!ret) goto end_pop_ctx; pvr_dump_field_member_u32_scaled_units( base_ctx, &state4, vs_output_size, PVRX(VDMCTRL_VDM_STATE4_VS_OUTPUT_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_zero(base_ctx, &state5, vs_max_instances, 32); pvr_dump_field_member_u32_scaled_units( base_ctx, &state5, vs_usc_common_size, PVRX(VDMCTRL_VDM_STATE5_VS_USC_COMMON_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_scaled_units( base_ctx, &state5, vs_usc_unified_size, PVRX(VDMCTRL_VDM_STATE5_VS_USC_UNIFIED_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_scaled_units( base_ctx, &state5, vs_pds_temp_size, PVRX(VDMCTRL_VDM_STATE5_VS_PDS_TEMP_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_scaled_units( base_ctx, &state5, vs_pds_data_size, PVRX(VDMCTRL_VDM_STATE5_VS_PDS_DATA_SIZE_UNIT_SIZE), "bytes"); } else { pvr_dump_field_member_not_present(base_ctx, &state3, vs_pds_code_base_addr); pvr_dump_field_member_not_present(base_ctx, &state4, vs_output_size); pvr_dump_field_member_not_present(base_ctx, &state5, vs_max_instances); pvr_dump_field_member_not_present(base_ctx, &state5, vs_usc_common_size); pvr_dump_field_member_not_present(base_ctx, &state5, vs_usc_unified_size); pvr_dump_field_member_not_present(base_ctx, &state5, vs_pds_temp_size); pvr_dump_field_member_not_present(base_ctx, &state5, vs_pds_data_size); } pvr_dump_field_member_bool(base_ctx, &state0, ds_present); pvr_dump_field_member_bool(base_ctx, &state0, gs_present); pvr_dump_field_member_bool(base_ctx, &state0, hs_present); pvr_dump_field_member_u32_offset(base_ctx, &state0, cam_size, 1); pvr_dump_field_member_enum( base_ctx, &state0, uvs_scratch_size_select, pvr_cmd_enum_to_str(VDMCTRL_UVS_SCRATCH_SIZE_SELECT)); pvr_dump_field_member_bool(base_ctx, &state0, cut_index_enable); pvr_dump_field_member_bool(base_ctx, &state0, tess_enable); pvr_dump_field_member_bool(base_ctx, &state0, gs_enable); pvr_dump_field_member_enum(base_ctx, &state0, flatshade_control, pvr_cmd_enum_to_str(VDMCTRL_FLATSHADE_CONTROL)); pvr_dump_field_member_bool(base_ctx, &state0, generate_primitive_id); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_vdmctrl_index_list(struct pvr_dump_csb_ctx *const csb_ctx, struct pvr_device *const device) { const struct pvr_device_info *const dev_info = &device->pdevice->dev_info; struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(VDMCTRL_INDEX_LIST0) index_list0 = { 0 }; struct PVRX(VDMCTRL_INDEX_LIST1) index_list1 = { 0 }; struct PVRX(VDMCTRL_INDEX_LIST2) index_list2 = { 0 }; struct PVRX(VDMCTRL_INDEX_LIST3) index_list3 = { 0 }; struct PVRX(VDMCTRL_INDEX_LIST4) index_list4 = { 0 }; struct PVRX(VDMCTRL_INDEX_LIST5) index_list5 = { 0 }; struct PVRX(VDMCTRL_INDEX_LIST6) index_list6 = { 0 }; struct PVRX(VDMCTRL_INDEX_LIST7) index_list7 = { 0 }; struct PVRX(VDMCTRL_INDEX_LIST8) index_list8 = { 0 }; struct PVRX(VDMCTRL_INDEX_LIST9) index_list9 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "INDEX_LIST")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_INDEX_LIST0, &index_list0)) goto end_pop_ctx; words_read += 1; if (index_list0.index_addr_present) { if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_INDEX_LIST1, &index_list1)) { goto end_pop_ctx; } words_read += 1; } if (index_list0.index_count_present) { if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_INDEX_LIST2, &index_list2)) { goto end_pop_ctx; } words_read += 1; } if (index_list0.index_instance_count_present) { if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_INDEX_LIST3, &index_list3)) { goto end_pop_ctx; } words_read += 1; } if (index_list0.index_offset_present) { if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_INDEX_LIST4, &index_list4)) { goto end_pop_ctx; } words_read += 1; } if (index_list0.start_present) { if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_INDEX_LIST5, &index_list5) || !pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_INDEX_LIST6, &index_list6)) { goto end_pop_ctx; } words_read += 2; } if (index_list0.indirect_addr_present) { if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_INDEX_LIST7, &index_list7) || !pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_INDEX_LIST8, &index_list8)) { goto end_pop_ctx; } words_read += 2; } if (index_list0.split_count_present) { if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_INDEX_LIST9, &index_list9)) goto end_pop_ctx; words_read += 1; } if (PVR_HAS_FEATURE(dev_info, vdm_degenerate_culling)) { pvr_dump_field_member_bool(base_ctx, &index_list0, degen_cull_enable); } else { pvr_dump_field_member_needs_feature(base_ctx, &index_list0, degen_cull_enable, vdm_degenerate_culling); } pvr_dump_field_member_enum(base_ctx, &index_list0, index_size, pvr_cmd_enum_to_str(VDMCTRL_INDEX_SIZE)); pvr_dump_field_member_u32_offset(base_ctx, &index_list0, patch_count, 1); pvr_dump_field_member_enum(base_ctx, &index_list0, primitive_topology, pvr_cmd_enum_to_str(VDMCTRL_PRIMITIVE_TOPOLOGY)); if (index_list0.index_addr_present) { pvr_dump_field_addr_split(base_ctx, "index_base_addr", index_list0.index_base_addrmsb, index_list1.index_base_addrlsb); const uint32_t index_size = pvr_vdmctrl_index_size_nr_bytes(index_list0.index_size); if (!index_list0.index_count_present) { ret = pvr_dump_error(base_ctx, "index_addr requires index_count"); goto end_pop_ctx; } ret = print_sub_buffer(base_ctx, device, BUFFER_TYPE_NONE, PVR_DEV_ADDR(index_list0.index_base_addrmsb.addr | index_list1.index_base_addrlsb.addr), index_list2.index_count * index_size, "index_count * index_size"); if (!ret) goto end_pop_ctx; } else { pvr_dump_field_not_present(base_ctx, "index_base_addr"); } if (index_list0.index_count_present) { pvr_dump_field_member_u32(base_ctx, &index_list2, index_count); } else { pvr_dump_field_member_not_present(base_ctx, &index_list2, index_count); } if (index_list0.index_instance_count_present) { pvr_dump_field_member_u32_offset(base_ctx, &index_list3, instance_count, 1); } else { pvr_dump_field_member_not_present(base_ctx, &index_list3, instance_count); } if (index_list0.index_offset_present) { pvr_dump_field_member_u32(base_ctx, &index_list4, index_offset); } else { pvr_dump_field_member_not_present(base_ctx, &index_list4, index_offset); } if (index_list0.start_present) { pvr_dump_field_member_u32(base_ctx, &index_list5, start_index); pvr_dump_field_member_u32(base_ctx, &index_list6, start_instance); } else { pvr_dump_field_member_not_present(base_ctx, &index_list5, start_index); pvr_dump_field_member_not_present(base_ctx, &index_list6, start_instance); } if (index_list0.indirect_addr_present) { pvr_dump_field_addr_split(base_ctx, "indirect_base_addr", index_list7.indirect_base_addrmsb, index_list8.indirect_base_addrlsb); ret = print_sub_buffer(base_ctx, device, BUFFER_TYPE_NONE, PVR_DEV_ADDR(index_list7.indirect_base_addrmsb.addr | index_list8.indirect_base_addrlsb.addr), 0, NULL); if (!ret) goto end_pop_ctx; } else { pvr_dump_field_not_present(base_ctx, "indirect_base_addr"); } if (index_list0.split_count_present) { pvr_dump_field_member_u32(base_ctx, &index_list9, split_count); } else { pvr_dump_field_member_not_present(base_ctx, &index_list9, split_count); } ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_vdmctrl_stream_link(struct pvr_dump_csb_ctx *const csb_ctx) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(VDMCTRL_STREAM_LINK0) link0 = { 0 }; struct PVRX(VDMCTRL_STREAM_LINK1) link1 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "STREAM_LINK")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_STREAM_LINK0, &link0) || !pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_STREAM_LINK1, &link1)) { goto end_pop_ctx; } words_read += 2; pvr_dump_field_member_bool(base_ctx, &link0, with_return); if (link0.compare_present) { pvr_dump_field_member_u32(base_ctx, &link0, compare_mode); pvr_dump_field_member_u32(base_ctx, &link0, compare_data); } else { pvr_dump_field_member_not_present(base_ctx, &link0, compare_mode); pvr_dump_field_member_not_present(base_ctx, &link0, compare_data); } pvr_dump_field_addr_split(base_ctx, "link_addr", link0.link_addrmsb, link1.link_addrlsb); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_vdmctrl_stream_return(struct pvr_dump_csb_ctx *const csb_ctx) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(VDMCTRL_STREAM_RETURN) return_ = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "STREAM_RETURN")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_STREAM_RETURN, &return_)) goto end_pop_ctx; words_read += 1; pvr_dump_field_no_fields(base_ctx); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_vdmctrl_stream_terminate(struct pvr_dump_csb_ctx *const csb_ctx) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(VDMCTRL_STREAM_TERMINATE) terminate = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "TERMINATE")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, VDMCTRL_STREAM_TERMINATE, &terminate)) { goto end_pop_ctx; } words_read += 1; pvr_dump_field_member_bool(base_ctx, &terminate, context); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_ppp_state_header(struct pvr_dump_csb_ctx *const csb_ctx, struct PVRX(TA_STATE_HEADER) *const header_out) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(TA_STATE_HEADER) header = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "STATE_HEADER")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_HEADER, &header)) goto end_pop_ctx; words_read += 1; pvr_dump_field_member_bool(base_ctx, &header, pres_ispctl); pvr_dump_field_member_bool(base_ctx, &header, pres_ispctl_fa); pvr_dump_field_member_bool(base_ctx, &header, pres_ispctl_fb); pvr_dump_field_member_bool(base_ctx, &header, pres_ispctl_ba); pvr_dump_field_member_bool(base_ctx, &header, pres_ispctl_bb); pvr_dump_field_member_bool(base_ctx, &header, pres_ispctl_dbsc); pvr_dump_field_member_bool(base_ctx, &header, pres_pds_state_ptr0); pvr_dump_field_member_bool(base_ctx, &header, pres_pds_state_ptr1); pvr_dump_field_member_bool(base_ctx, &header, pres_pds_state_ptr2); pvr_dump_field_member_bool(base_ctx, &header, pres_pds_state_ptr3); pvr_dump_field_member_bool(base_ctx, &header, pres_region_clip); pvr_dump_field_member_bool(base_ctx, &header, pres_viewport); pvr_dump_field_member_u32_offset(base_ctx, &header, view_port_count, 1); pvr_dump_field_member_bool(base_ctx, &header, pres_wclamp); pvr_dump_field_member_bool(base_ctx, &header, pres_outselects); pvr_dump_field_member_bool(base_ctx, &header, pres_varying_word0); pvr_dump_field_member_bool(base_ctx, &header, pres_varying_word1); pvr_dump_field_member_bool(base_ctx, &header, pres_varying_word2); pvr_dump_field_member_bool(base_ctx, &header, pres_ppp_ctrl); pvr_dump_field_member_bool(base_ctx, &header, pres_stream_out_size); pvr_dump_field_member_bool(base_ctx, &header, pres_stream_out_program); pvr_dump_field_member_bool(base_ctx, &header, context_switch); pvr_dump_field_member_bool(base_ctx, &header, pres_terminate); pvr_dump_field_member_bool(base_ctx, &header, not_final_term); if (header_out) *header_out = header; ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static void print_block_ppp_state_isp_one_side( struct pvr_dump_csb_block_ctx *const ctx, const struct PVRX(TA_STATE_ISPA) *const isp_a, const struct PVRX(TA_STATE_ISPB) *const isp_b, const bool has_b) { struct pvr_dump_ctx *const base_ctx = &ctx->base.base; pvr_dump_indent(base_ctx); pvr_dump_field_member_enum(base_ctx, isp_a, objtype, pvr_cmd_enum_to_str(TA_OBJTYPE)); pvr_dump_field_member_enum(base_ctx, isp_a, passtype, pvr_cmd_enum_to_str(TA_PASSTYPE)); pvr_dump_field_member_bool(base_ctx, isp_a, ovgvispassmaskop); pvr_dump_field_member_bool(base_ctx, isp_a, maskval); pvr_dump_field_member_bool(base_ctx, isp_a, dwritedisable); pvr_dump_field_member_bool(base_ctx, isp_a, dfbztestenable); pvr_dump_field_member_enum(base_ctx, isp_a, dcmpmode, pvr_cmd_enum_to_str(TA_CMPMODE)); pvr_dump_field_member_bool(base_ctx, isp_a, linefilllastpixel); pvr_dump_field_member_uq4_4_offset(base_ctx, isp_a, pointlinewidth, 0x01); pvr_dump_field_member_u32(base_ctx, isp_a, sref); if (has_b) { pvr_dump_field_member_enum(base_ctx, isp_b, scmpmode, pvr_cmd_enum_to_str(TA_CMPMODE)); pvr_dump_field_member_enum(base_ctx, isp_b, sop1, pvr_cmd_enum_to_str(TA_ISPB_STENCILOP)); pvr_dump_field_member_enum(base_ctx, isp_b, sop2, pvr_cmd_enum_to_str(TA_ISPB_STENCILOP)); pvr_dump_field_member_enum(base_ctx, isp_b, sop3, pvr_cmd_enum_to_str(TA_ISPB_STENCILOP)); pvr_dump_field_member_x32(base_ctx, isp_b, scmpmask, 2); pvr_dump_field_member_x32(base_ctx, isp_b, swmask, 2); } else { pvr_dump_field_member_not_present(base_ctx, isp_b, scmpmode); pvr_dump_field_member_not_present(base_ctx, isp_b, sop1); pvr_dump_field_member_not_present(base_ctx, isp_b, sop2); pvr_dump_field_member_not_present(base_ctx, isp_b, sop3); pvr_dump_field_member_not_present(base_ctx, isp_b, scmpmask); pvr_dump_field_member_not_present(base_ctx, isp_b, swmask); } pvr_dump_dedent(base_ctx); } static uint32_t print_block_ppp_state_isp(struct pvr_dump_csb_ctx *const csb_ctx, const bool has_fa, const bool has_fb, const bool has_ba, const bool has_bb, const bool has_dbsc) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(TA_STATE_ISPCTL) isp_ctl = { 0 }; struct PVRX(TA_STATE_ISPA) isp_fa = { 0 }; struct PVRX(TA_STATE_ISPB) isp_fb = { 0 }; struct PVRX(TA_STATE_ISPA) isp_ba = { 0 }; struct PVRX(TA_STATE_ISPB) isp_bb = { 0 }; struct PVRX(TA_STATE_ISPDBSC) isp_dbsc = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "STATE_ISP")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_ISPCTL, &isp_ctl)) goto end_pop_ctx; words_read += 1; /* In most blocks, we try to read all words before printing anything. In * this case, there can be ambiguity in which words to parse (which results * in an error from the conditional below). To aid in debugging when this * ambiguity is present, print the control word's contents before continuing * so the fields which create the ambiguity are dumped even when the rest of * the block isn't. */ pvr_dump_field_member_u32(base_ctx, &isp_ctl, visreg); pvr_dump_field_member_bool(base_ctx, &isp_ctl, visbool); pvr_dump_field_member_bool(base_ctx, &isp_ctl, vistest); pvr_dump_field_member_bool(base_ctx, &isp_ctl, scenable); pvr_dump_field_member_bool(base_ctx, &isp_ctl, dbenable); pvr_dump_field_member_bool(base_ctx, &isp_ctl, bpres); pvr_dump_field_member_bool(base_ctx, &isp_ctl, two_sided); pvr_dump_field_member_bool(base_ctx, &isp_ctl, ovgmtestdisable); pvr_dump_field_member_bool(base_ctx, &isp_ctl, tagwritedisable); pvr_dump_field_member_u32(base_ctx, &isp_ctl, upass); pvr_dump_field_member_u32(base_ctx, &isp_ctl, validid); if (!has_fa || has_fb != isp_ctl.bpres || has_ba != isp_ctl.two_sided || has_bb != (isp_ctl.bpres && isp_ctl.two_sided) || has_dbsc != (isp_ctl.dbenable || isp_ctl.scenable)) { pvr_dump_error( base_ctx, "words declared by ppp header do not match requirements of ispctl word"); goto end_pop_ctx; } if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_ISPA, &isp_fa)) return false; words_read += 1; if (has_fb) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_ISPB, &isp_fb)) return false; words_read += 1; } if (has_ba) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_ISPA, &isp_ba)) return false; words_read += 1; } if (has_bb) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_ISPB, &isp_bb)) return false; words_read += 1; } if (has_dbsc) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_ISPDBSC, &isp_dbsc)) goto end_pop_ctx; words_read += 1; } pvr_dump_println(base_ctx, "front"); print_block_ppp_state_isp_one_side(&ctx, &isp_fa, &isp_fb, isp_ctl.bpres); if (isp_ctl.two_sided) { pvr_dump_println(base_ctx, "back"); print_block_ppp_state_isp_one_side(&ctx, &isp_ba, &isp_bb, isp_ctl.bpres); } else { pvr_dump_field_not_present(base_ctx, "back"); } if (has_dbsc) { pvr_dump_field_member_u32(base_ctx, &isp_dbsc, dbindex); pvr_dump_field_member_u32(base_ctx, &isp_dbsc, scindex); } else { pvr_dump_field_member_not_present(base_ctx, &isp_dbsc, dbindex); pvr_dump_field_member_not_present(base_ctx, &isp_dbsc, scindex); } ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_ppp_state_pds(struct pvr_dump_csb_ctx *const csb_ctx, struct pvr_device *const device, const bool has_initial_words, const bool has_varying, const bool has_texturedata, const bool has_uniformdata) { const pvr_dev_addr_t pds_heap_base = device->heaps.pds_heap->base_addr; struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(TA_STATE_PDS_SHADERBASE) shader_base = { 0 }; struct PVRX(TA_STATE_PDS_TEXUNICODEBASE) tex_unicode_base = { 0 }; struct PVRX(TA_STATE_PDS_SIZEINFO1) size_info1 = { 0 }; struct PVRX(TA_STATE_PDS_SIZEINFO2) size_info2 = { 0 }; struct PVRX(TA_STATE_PDS_VARYINGBASE) varying_base = { 0 }; struct PVRX(TA_STATE_PDS_TEXTUREDATABASE) texture_data_base = { 0 }; struct PVRX(TA_STATE_PDS_UNIFORMDATABASE) uniform_data_base = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "STATE_PDS")) goto end_out; if (has_initial_words) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_PDS_SHADERBASE, &shader_base) || !pvr_dump_csb_block_take_packed(&ctx, TA_STATE_PDS_TEXUNICODEBASE, &tex_unicode_base) || !pvr_dump_csb_block_take_packed(&ctx, TA_STATE_PDS_SIZEINFO1, &size_info1) || !pvr_dump_csb_block_take_packed(&ctx, TA_STATE_PDS_SIZEINFO2, &size_info2)) { goto end_pop_ctx; } words_read += 4; } if (has_varying) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_PDS_VARYINGBASE, &varying_base)) { goto end_pop_ctx; } words_read += 1; } if (has_texturedata) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_PDS_TEXTUREDATABASE, &texture_data_base)) { goto end_pop_ctx; } words_read += 1; } if (has_uniformdata) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_PDS_UNIFORMDATABASE, &uniform_data_base)) { goto end_pop_ctx; } words_read += 1; } if (has_initial_words) { pvr_dump_field_addr_offset(base_ctx, "shaderbase", shader_base.addr, pds_heap_base); pvr_dump_field_addr_offset(base_ctx, "texunicodebase", tex_unicode_base.addr, pds_heap_base); pvr_dump_field_member_u32_scaled_units( base_ctx, &size_info1, pds_uniformsize, PVRX(TA_STATE_PDS_SIZEINFO1_PDS_UNIFORMSIZE_UNIT_SIZE), "words"); pvr_dump_field_member_u32_scaled_units( base_ctx, &size_info1, pds_texturestatesize, PVRX(TA_STATE_PDS_SIZEINFO1_PDS_TEXTURESTATESIZE_UNIT_SIZE), "words"); pvr_dump_field_member_u32_scaled_units( base_ctx, &size_info1, pds_varyingsize, PVRX(TA_STATE_PDS_SIZEINFO1_PDS_VARYINGSIZE_UNIT_SIZE), "words"); pvr_dump_field_member_u32_scaled_units( base_ctx, &size_info1, usc_varyingsize, PVRX(TA_STATE_PDS_SIZEINFO1_USC_VARYINGSIZE_UNIT_SIZE), "words"); pvr_dump_field_member_u32_scaled_units( base_ctx, &size_info1, pds_tempsize, PVRX(TA_STATE_PDS_SIZEINFO1_PDS_TEMPSIZE_UNIT_SIZE), "words"); pvr_dump_field_member_u32_scaled_units( base_ctx, &size_info2, usc_sharedsize, PVRX(TA_STATE_PDS_SIZEINFO2_USC_SHAREDSIZE_UNIT_SIZE), "words"); pvr_dump_field_member_bool(base_ctx, &size_info2, pds_tri_merge_disable); pvr_dump_field_member_u32(base_ctx, &size_info2, pds_batchnum); } else { pvr_dump_field_not_present(base_ctx, "shaderbase"); pvr_dump_field_not_present(base_ctx, "texunicodebase"); pvr_dump_field_member_not_present(base_ctx, &size_info1, pds_uniformsize); pvr_dump_field_member_not_present(base_ctx, &size_info1, pds_texturestatesize); pvr_dump_field_member_not_present(base_ctx, &size_info1, pds_varyingsize); pvr_dump_field_member_not_present(base_ctx, &size_info1, usc_varyingsize); pvr_dump_field_member_not_present(base_ctx, &size_info1, pds_tempsize); pvr_dump_field_member_not_present(base_ctx, &size_info2, usc_sharedsize); pvr_dump_field_member_not_present(base_ctx, &size_info2, pds_tri_merge_disable); pvr_dump_field_member_not_present(base_ctx, &size_info2, pds_batchnum); } if (has_varying) { pvr_dump_field_addr_offset(base_ctx, "varyingbase", varying_base.addr, pds_heap_base); } else { pvr_dump_field_not_present(base_ctx, "varyingbase"); } if (has_texturedata) { pvr_dump_field_addr_offset(base_ctx, "texturedatabase", texture_data_base.addr, pds_heap_base); } else { pvr_dump_field_not_present(base_ctx, "texturedatabase"); } if (has_uniformdata) { pvr_dump_field_addr_offset(base_ctx, "uniformdatabase", uniform_data_base.addr, pds_heap_base); } else { pvr_dump_field_not_present(base_ctx, "uniformdatabase"); } ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_ppp_region_clip(struct pvr_dump_csb_ctx *const csb_ctx) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(TA_REGION_CLIP0) clip0 = { 0 }; struct PVRX(TA_REGION_CLIP1) clip1 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "REGION_CLIP")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, TA_REGION_CLIP0, &clip0) || !pvr_dump_csb_block_take_packed(&ctx, TA_REGION_CLIP1, &clip1)) { goto end_pop_ctx; } words_read += 2; pvr_dump_field_member_enum(base_ctx, &clip0, mode, pvr_cmd_enum_to_str(TA_REGION_CLIP_MODE)); pvr_dump_field_member_u32_scaled_units(base_ctx, &clip0, left, 32, "pixels"); pvr_dump_field_member_u32_scaled_units(base_ctx, &clip0, right, 32, "pixels"); pvr_dump_field_member_u32_scaled_units(base_ctx, &clip1, top, 32, "pixels"); pvr_dump_field_member_u32_scaled_units(base_ctx, &clip1, bottom, 32, "pixels"); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_ppp_viewport(struct pvr_dump_csb_ctx *const csb_ctx, const uint32_t idx) { static char const *const field_names[] = { "a0", "m0", "a1", "m1", "a2", "m2" }; struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; STATIC_ASSERT(sizeof(float) == 4); if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "VIEWPORT %" PRIu32, idx)) goto end_out; for (uint32_t i = 0; i < ARRAY_SIZE(field_names); i++) { const uint32_t *const value = pvr_dump_csb_block_take(&ctx, 1); if (!value) goto end_pop_ctx; words_read += 1; pvr_dump_field_f32(base_ctx, field_names[i], uif(*value)); } ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_ppp_wclamp(struct pvr_dump_csb_ctx *const csb_ctx) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; STATIC_ASSERT(sizeof(float) == 4); if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "WCLAMP")) goto end_out; const uint32_t *const value = pvr_dump_csb_block_take(&ctx, 1); if (!value) goto end_pop_ctx; words_read += 1; pvr_dump_field_f32(base_ctx, "value", uif(*value)); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_ppp_output_sel(struct pvr_dump_csb_ctx *const csb_ctx) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(TA_OUTPUT_SEL) output_sel = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "OUTPUT_SEL")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, TA_OUTPUT_SEL, &output_sel)) goto end_pop_ctx; words_read += 1; pvr_dump_field_member_bool(base_ctx, &output_sel, plane0); pvr_dump_field_member_bool(base_ctx, &output_sel, plane1); pvr_dump_field_member_bool(base_ctx, &output_sel, plane2); pvr_dump_field_member_bool(base_ctx, &output_sel, plane3); pvr_dump_field_member_bool(base_ctx, &output_sel, plane4); pvr_dump_field_member_bool(base_ctx, &output_sel, plane5); pvr_dump_field_member_bool(base_ctx, &output_sel, plane6); pvr_dump_field_member_bool(base_ctx, &output_sel, plane7); pvr_dump_field_member_bool(base_ctx, &output_sel, cullplane0); pvr_dump_field_member_bool(base_ctx, &output_sel, cullplane1); pvr_dump_field_member_bool(base_ctx, &output_sel, cullplane2); pvr_dump_field_member_bool(base_ctx, &output_sel, cullplane3); pvr_dump_field_member_bool(base_ctx, &output_sel, cullplane4); pvr_dump_field_member_bool(base_ctx, &output_sel, cullplane5); pvr_dump_field_member_bool(base_ctx, &output_sel, cullplane6); pvr_dump_field_member_bool(base_ctx, &output_sel, cullplane7); pvr_dump_field_member_bool(base_ctx, &output_sel, rhw_pres); pvr_dump_field_member_bool(base_ctx, &output_sel, isp_position_depth_clamp_z); pvr_dump_field_member_bool(base_ctx, &output_sel, psprite_size_pres); pvr_dump_field_member_bool(base_ctx, &output_sel, vpt_tgt_pres); pvr_dump_field_member_bool(base_ctx, &output_sel, render_tgt_pres); pvr_dump_field_member_bool(base_ctx, &output_sel, tsp_unclamped_z_pres); pvr_dump_field_member_u32(base_ctx, &output_sel, vtxsize); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_ppp_state_varying(struct pvr_dump_csb_ctx *const csb_ctx, const bool has_word0, const bool has_word1, const bool has_word2) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(TA_STATE_VARYING0) varying0 = { 0 }; struct PVRX(TA_STATE_VARYING1) varying1 = { 0 }; struct PVRX(TA_STATE_VARYING2) varying2 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "STATE_VARYING")) goto end_out; if (has_word0) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_VARYING0, &varying0)) goto end_pop_ctx; words_read += 1; } if (has_word1) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_VARYING1, &varying1)) goto end_pop_ctx; words_read += 1; } if (has_word2) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_VARYING2, &varying2)) goto end_pop_ctx; words_read += 1; } if (has_word0) { pvr_dump_field_member_u32(base_ctx, &varying0, f32_linear); pvr_dump_field_member_u32(base_ctx, &varying0, f32_flat); pvr_dump_field_member_u32(base_ctx, &varying0, f32_npc); } else { pvr_dump_field_member_not_present(base_ctx, &varying0, f32_linear); pvr_dump_field_member_not_present(base_ctx, &varying0, f32_flat); pvr_dump_field_member_not_present(base_ctx, &varying0, f32_npc); } if (has_word1) { pvr_dump_field_member_u32(base_ctx, &varying1, f16_linear); pvr_dump_field_member_u32(base_ctx, &varying1, f16_flat); pvr_dump_field_member_u32(base_ctx, &varying1, f16_npc); } else { pvr_dump_field_member_not_present(base_ctx, &varying1, f16_linear); pvr_dump_field_member_not_present(base_ctx, &varying1, f16_flat); pvr_dump_field_member_not_present(base_ctx, &varying1, f16_npc); } if (has_word2) { pvr_dump_field_member_u32(base_ctx, &varying2, output_clip_planes); } else { pvr_dump_field_member_not_present(base_ctx, &varying2, output_clip_planes); } ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_ppp_state_ppp_ctrl(struct pvr_dump_csb_ctx *const csb_ctx) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(TA_STATE_PPP_CTRL) ppp_ctrl = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "STATE_PPP_CTRL")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_PPP_CTRL, &ppp_ctrl)) goto end_pop_ctx; words_read += 1; pvr_dump_field_member_enum(base_ctx, &ppp_ctrl, cullmode, pvr_cmd_enum_to_str(TA_CULLMODE)); pvr_dump_field_member_bool(base_ctx, &ppp_ctrl, updatebbox); pvr_dump_field_member_bool(base_ctx, &ppp_ctrl, resetbbox); pvr_dump_field_member_bool(base_ctx, &ppp_ctrl, wbuffen); pvr_dump_field_member_bool(base_ctx, &ppp_ctrl, wclampen); pvr_dump_field_member_bool(base_ctx, &ppp_ctrl, pretransform); pvr_dump_field_member_enum(base_ctx, &ppp_ctrl, flatshade_vtx, pvr_cmd_enum_to_str(TA_FLATSHADE)); pvr_dump_field_member_bool(base_ctx, &ppp_ctrl, drawclippededges); pvr_dump_field_member_enum(base_ctx, &ppp_ctrl, clip_mode, pvr_cmd_enum_to_str(TA_CLIP_MODE)); pvr_dump_field_member_bool(base_ctx, &ppp_ctrl, pres_prim_id); pvr_dump_field_member_enum(base_ctx, &ppp_ctrl, gs_output_topology, pvr_cmd_enum_to_str(TA_GS_OUTPUT_TOPOLOGY)); pvr_dump_field_member_bool(base_ctx, &ppp_ctrl, prim_msaa); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_ppp_state_stream_out(struct pvr_dump_csb_ctx *const csb_ctx, struct pvr_device *const device, const bool has_word0, const bool has_words12) { const pvr_dev_addr_t pds_heap_base = device->heaps.pds_heap->base_addr; struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(TA_STATE_STREAM_OUT0) stream_out0 = { 0 }; struct PVRX(TA_STATE_STREAM_OUT1) stream_out1 = { 0 }; struct PVRX(TA_STATE_STREAM_OUT2) stream_out2 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "STATE_STREAM_OUT")) goto end_out; if (has_word0) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_STREAM_OUT0, &stream_out0)) { goto end_pop_ctx; } words_read += 1; } if (has_words12) { if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_STREAM_OUT1, &stream_out1) || !pvr_dump_csb_block_take_packed(&ctx, TA_STATE_STREAM_OUT2, &stream_out2)) { goto end_pop_ctx; } words_read += 2; } if (has_word0) { pvr_dump_field_member_bool(base_ctx, &stream_out0, stream0_ta_output); pvr_dump_field_member_bool(base_ctx, &stream_out0, stream0_mem_output); pvr_dump_field_member_u32_units(base_ctx, &stream_out0, stream1_size, "words"); pvr_dump_field_member_u32_units(base_ctx, &stream_out0, stream2_size, "words"); pvr_dump_field_member_u32_units(base_ctx, &stream_out0, stream3_size, "words"); } else { pvr_dump_field_member_not_present(base_ctx, &stream_out0, stream0_ta_output); pvr_dump_field_member_not_present(base_ctx, &stream_out0, stream0_mem_output); pvr_dump_field_member_not_present(base_ctx, &stream_out0, stream1_size); pvr_dump_field_member_not_present(base_ctx, &stream_out0, stream2_size); pvr_dump_field_member_not_present(base_ctx, &stream_out0, stream3_size); } if (has_words12) { pvr_dump_field_member_u32_scaled_units( base_ctx, &stream_out1, pds_temp_size, PVRX(TA_STATE_STREAM_OUT1_PDS_TEMP_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_u32_scaled_units( base_ctx, &stream_out1, pds_data_size, PVRX(TA_STATE_STREAM_OUT1_PDS_DATA_SIZE_UNIT_SIZE), "bytes"); pvr_dump_field_member_bool(base_ctx, &stream_out1, sync); pvr_dump_field_member_addr_offset(base_ctx, &stream_out2, pds_data_addr, pds_heap_base); ret = print_sub_buffer( base_ctx, device, BUFFER_TYPE_NONE, PVR_DEV_ADDR_OFFSET(pds_heap_base, stream_out2.pds_data_addr.addr), stream_out1.pds_data_size, "pds_data_size"); if (!ret) goto end_pop_ctx; } else { pvr_dump_field_member_not_present(base_ctx, &stream_out1, pds_temp_size); pvr_dump_field_member_not_present(base_ctx, &stream_out1, pds_data_size); pvr_dump_field_member_not_present(base_ctx, &stream_out1, sync); pvr_dump_field_member_not_present(base_ctx, &stream_out2, pds_data_addr); } ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } static uint32_t print_block_ppp_state_terminate(struct pvr_dump_csb_ctx *const csb_ctx) { struct pvr_dump_csb_block_ctx ctx; struct pvr_dump_ctx *const base_ctx = &ctx.base.base; uint32_t words_read = 0; bool ret = false; struct PVRX(TA_STATE_TERMINATE0) terminate0 = { 0 }; struct PVRX(TA_STATE_TERMINATE1) terminate1 = { 0 }; if (!pvr_dump_csb_block_ctx_push(&ctx, csb_ctx, "STATE_TERMINATE")) goto end_out; if (!pvr_dump_csb_block_take_packed(&ctx, TA_STATE_TERMINATE0, &terminate0) || !pvr_dump_csb_block_take_packed(&ctx, TA_STATE_TERMINATE1, &terminate1)) { goto end_pop_ctx; } words_read += 2; pvr_dump_field_member_u32_scaled_units(base_ctx, &terminate0, clip_right, 32, "pixels"); pvr_dump_field_member_u32_scaled_units(base_ctx, &terminate0, clip_top, 32, "pixels"); pvr_dump_field_member_u32_scaled_units(base_ctx, &terminate0, clip_bottom, 32, "pixels"); pvr_dump_field_member_u32_scaled_units(base_ctx, &terminate1, clip_left, 32, "pixels"); pvr_dump_field_member_u32(base_ctx, &terminate1, render_target); ret = true; end_pop_ctx: pvr_dump_csb_block_ctx_pop(&ctx); end_out: return ret ? words_read : 0; } /****************************************************************************** Buffer printers ******************************************************************************/ static bool print_block_hex(struct pvr_dump_buffer_ctx *const ctx, const uint32_t nr_words) { const uint32_t nr_bytes = nr_words * PVR_DUMP_CSB_WORD_SIZE; if (!nr_words) return false; pvr_dump_indent(&ctx->base); pvr_dump_field_u32_units(&ctx->base, "", nr_bytes, "bytes"); pvr_dump_indent(&ctx->base); pvr_dump_buffer_rewind(ctx, nr_bytes); pvr_dump_buffer_hex(ctx, nr_bytes); pvr_dump_dedent(&ctx->base); pvr_dump_dedent(&ctx->base); return true; } static bool print_cdmctrl_buffer(struct pvr_dump_buffer_ctx *const parent_ctx, struct pvr_device *const device) { struct pvr_dump_csb_ctx ctx; bool ret = true; /* All blocks contain a block_type member in the first word at the same * position. We could unpack any block to pick out this discriminant field, * but this one has been chosen because it's only one word long. */ STATIC_ASSERT(pvr_cmd_length(CDMCTRL_STREAM_TERMINATE) == 1); if (!pvr_dump_csb_ctx_push(&ctx, parent_ctx)) return false; do { enum PVRX(CDMCTRL_BLOCK_TYPE) block_type; const uint32_t *next_word; uint32_t words_read = 0; next_word = pvr_dump_buffer_peek(&ctx.base, sizeof(*next_word)); if (!next_word) { ret = false; goto end_pop_ctx; } block_type = pvr_csb_unpack(next_word, CDMCTRL_STREAM_TERMINATE).block_type; switch (block_type) { case PVRX(CDMCTRL_BLOCK_TYPE_COMPUTE_KERNEL): words_read = print_block_cdmctrl_kernel(&ctx, device); break; case PVRX(CDMCTRL_BLOCK_TYPE_STREAM_LINK): words_read = print_block_cdmctrl_stream_link(&ctx); break; case PVRX(CDMCTRL_BLOCK_TYPE_STREAM_TERMINATE): words_read = print_block_cdmctrl_stream_terminate(&ctx); break; default: pvr_dump_buffer_print_header_line( &ctx.base, "", block_type); break; } if (!print_block_hex(&ctx.base, words_read)) ret = false; if (block_type == PVRX(CDMCTRL_BLOCK_TYPE_STREAM_TERMINATE)) break; } while (ret); end_pop_ctx: pvr_dump_csb_ctx_pop(&ctx, true); return ret; } static bool print_vdmctrl_buffer(struct pvr_dump_buffer_ctx *const parent_ctx, struct pvr_device *const device) { struct pvr_dump_csb_ctx ctx; bool ret = true; /* All blocks contain a block_type member in the first word at the same * position. We could unpack any block to pick out this discriminant field, * but this one has been chosen because it's only one word long. */ STATIC_ASSERT(pvr_cmd_length(VDMCTRL_STREAM_RETURN) == 1); if (!pvr_dump_csb_ctx_push(&ctx, parent_ctx)) return false; do { enum PVRX(VDMCTRL_BLOCK_TYPE) block_type; const uint32_t *next_word; uint32_t words_read = 0; next_word = pvr_dump_buffer_peek(&ctx.base, sizeof(*next_word)); if (!next_word) { ret = false; goto end_pop_ctx; } block_type = pvr_csb_unpack(next_word, VDMCTRL_STREAM_RETURN).block_type; switch (block_type) { case PVRX(VDMCTRL_BLOCK_TYPE_PPP_STATE_UPDATE): words_read = print_block_vdmctrl_ppp_state_update(&ctx, device); break; case PVRX(VDMCTRL_BLOCK_TYPE_PDS_STATE_UPDATE): words_read = print_block_vdmctrl_pds_state_update(&ctx, device); break; case PVRX(VDMCTRL_BLOCK_TYPE_VDM_STATE_UPDATE): words_read = print_block_vdmctrl_vdm_state_update(&ctx, device); break; case PVRX(VDMCTRL_BLOCK_TYPE_INDEX_LIST): words_read = print_block_vdmctrl_index_list(&ctx, device); break; case PVRX(VDMCTRL_BLOCK_TYPE_STREAM_LINK): words_read = print_block_vdmctrl_stream_link(&ctx); break; case PVRX(VDMCTRL_BLOCK_TYPE_STREAM_RETURN): words_read = print_block_vdmctrl_stream_return(&ctx); break; case PVRX(VDMCTRL_BLOCK_TYPE_STREAM_TERMINATE): words_read = print_block_vdmctrl_stream_terminate(&ctx); break; default: pvr_dump_buffer_print_header_line( &ctx.base, "", block_type); break; } if (!print_block_hex(&ctx.base, words_read)) ret = false; if (block_type == PVRX(VDMCTRL_BLOCK_TYPE_STREAM_TERMINATE)) break; } while (ret); end_pop_ctx: pvr_dump_csb_ctx_pop(&ctx, true); return ret; } static bool print_ppp_buffer(struct pvr_dump_buffer_ctx *const parent_ctx, struct pvr_device *const device) { struct pvr_dump_csb_ctx ctx; uint32_t words_read; bool ret = false; struct PVRX(TA_STATE_HEADER) header = { 0 }; if (!pvr_dump_csb_ctx_push(&ctx, parent_ctx)) goto end_out; words_read = print_block_ppp_state_header(&ctx, &header); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; if (header.pres_ispctl_fa || header.pres_ispctl_fb || header.pres_ispctl_ba || header.pres_ispctl_bb || header.pres_ispctl_dbsc) { if (!header.pres_ispctl) { ret = pvr_dump_field_error(&ctx.base.base, "missing ispctl control word"); goto end_pop_ctx; } words_read = print_block_ppp_state_isp(&ctx, header.pres_ispctl_fa, header.pres_ispctl_fb, header.pres_ispctl_ba, header.pres_ispctl_bb, header.pres_ispctl_dbsc); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; } if (header.pres_pds_state_ptr0 || header.pres_pds_state_ptr1 || header.pres_pds_state_ptr2 || header.pres_pds_state_ptr3) { words_read = print_block_ppp_state_pds(&ctx, device, header.pres_pds_state_ptr0, header.pres_pds_state_ptr1, header.pres_pds_state_ptr2, header.pres_pds_state_ptr3); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; } if (header.pres_region_clip) { words_read = print_block_ppp_region_clip(&ctx); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; } if (header.pres_viewport) { for (uint32_t i = 0; i < header.view_port_count + 1; i++) { words_read = print_block_ppp_viewport(&ctx, i); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; } } if (header.pres_wclamp) { words_read = print_block_ppp_wclamp(&ctx); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; } if (header.pres_outselects) { words_read = print_block_ppp_output_sel(&ctx); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; } if (header.pres_varying_word0 || header.pres_varying_word1 || header.pres_varying_word2) { words_read = print_block_ppp_state_varying(&ctx, header.pres_varying_word0, header.pres_varying_word1, header.pres_varying_word2); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; } if (header.pres_ppp_ctrl) { words_read = print_block_ppp_state_ppp_ctrl(&ctx); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; } if (header.pres_stream_out_size || header.pres_stream_out_program) { words_read = print_block_ppp_state_stream_out(&ctx, device, header.pres_stream_out_size, header.pres_stream_out_program); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; } if (header.pres_terminate) { words_read = print_block_ppp_state_terminate(&ctx); if (!print_block_hex(&ctx.base, words_read)) goto end_pop_ctx; } ret = true; end_pop_ctx: pvr_dump_csb_ctx_pop(&ctx, true); end_out: return ret; } /****************************************************************************** Sub buffer printer definition ******************************************************************************/ static bool print_sub_buffer(struct pvr_dump_ctx *const ctx, struct pvr_device *const device, const enum buffer_type type, const pvr_dev_addr_t addr, const uint64_t expected_size, const char *const size_src) { struct pvr_dump_bo_ctx sub_ctx; struct pvr_dump_ctx *base_ctx; struct pvr_bo *bo; uint64_t real_size; uint64_t offset; bool ret = false; pvr_dump_indent(ctx); bo = pvr_bo_store_lookup(device, addr); if (!bo) { if (expected_size) { pvr_dump_field(ctx, "", "%" PRIu64 " bytes (from %s)", expected_size, size_src); } else { pvr_dump_field(ctx, "", ""); } /* FIXME: Trace pvr_buffer allocations with pvr_bo_store. */ pvr_dump_warn(ctx, "no mapping found at " PVR_DEV_ADDR_FMT, addr.addr); /* Not a fatal error; don't let a single bad address halt the dump. */ ret = true; goto end_out; } offset = addr.addr - bo->vma->dev_addr.addr; if (!pvr_dump_bo_ctx_push(&sub_ctx, ctx, device, bo)) { pvr_dump_println(&sub_ctx.base.base, ""); goto end_out; } base_ctx = &sub_ctx.base.base; if (!pvr_dump_buffer_advance(&sub_ctx.base, offset)) goto end_pop_ctx; real_size = sub_ctx.base.remaining_size; if (!expected_size) { pvr_dump_field(base_ctx, "", "%" PRIu64 " bytes mapped", real_size); } else if (expected_size > real_size) { pvr_dump_field(base_ctx, "", "%" PRIu64 " bytes mapped, expected %" PRIu64 " bytes (from %s)", real_size, expected_size, size_src); } else { pvr_dump_field(base_ctx, "", "%" PRIu64 " bytes (from %s)", expected_size, size_src); pvr_dump_buffer_truncate(&sub_ctx.base, expected_size); } if (sub_ctx.bo_mapped_in_ctx) pvr_dump_field(base_ctx, "", ""); else pvr_dump_field(base_ctx, "", "%p", sub_ctx.base.ptr); switch (type) { case BUFFER_TYPE_NONE: pvr_dump_field(base_ctx, "", ""); ret = true; break; case BUFFER_TYPE_PPP: pvr_dump_field(base_ctx, "", ""); ret = print_ppp_buffer(&sub_ctx.base, device); break; default: pvr_dump_field(base_ctx, "", ""); ret = false; } pvr_dump_field_u32_units(&sub_ctx.base.base, "", sub_ctx.base.capacity, "bytes"); pvr_dump_indent(&sub_ctx.base.base); pvr_dump_buffer_restart(&sub_ctx.base); pvr_dump_buffer_hex(&sub_ctx.base, 0); pvr_dump_dedent(&sub_ctx.base.base); end_pop_ctx: pvr_dump_bo_ctx_pop(&sub_ctx); end_out: pvr_dump_dedent(ctx); return ret; } /****************************************************************************** Top-level dumping ******************************************************************************/ static bool dump_first_buffer(struct pvr_dump_buffer_ctx *const ctx, const enum pvr_cmd_stream_type stream_type, struct pvr_device *const device) { bool ret = false; pvr_dump_mark_section(&ctx->base, "First buffer content"); switch (stream_type) { case PVR_CMD_STREAM_TYPE_GRAPHICS: ret = print_vdmctrl_buffer(ctx, device); break; case PVR_CMD_STREAM_TYPE_COMPUTE: ret = print_cdmctrl_buffer(ctx, device); break; default: unreachable("Unknown stream type"); } if (!ret) pvr_dump_println(&ctx->base, "", ctx->ptr - ctx->initial_ptr); pvr_dump_buffer_restart(ctx); pvr_dump_mark_section(&ctx->base, "First buffer hexdump"); return pvr_dump_buffer_hex(ctx, 0); } /****************************************************************************** Public functions ******************************************************************************/ void pvr_csb_dump(const struct pvr_csb *const csb, const uint32_t frame_num, const uint32_t job_num) { const uint32_t nr_bos = list_length(&csb->pvr_bo_list); struct pvr_device *const device = csb->device; struct pvr_dump_bo_ctx first_bo_ctx; struct pvr_dump_ctx root_ctx; pvr_bo_store_dump(device); pvr_dump_begin(&root_ctx, stderr, "CONTROL STREAM DUMP", 6); pvr_dump_field_u32(&root_ctx, "Frame num", frame_num); pvr_dump_field_u32(&root_ctx, "Job num", job_num); pvr_dump_field_enum(&root_ctx, "Status", csb->status, vk_Result_to_str); pvr_dump_field_enum(&root_ctx, "Stream type", csb->stream_type, pvr_cmd_stream_type_to_str); if (nr_bos <= 1) { pvr_dump_field_u32(&root_ctx, "Nr of BOs", nr_bos); } else { /* TODO: Implement multi-buffer dumping. */ pvr_dump_field_computed(&root_ctx, "Nr of BOs", "%" PRIu32, "only the first buffer will be dumped", nr_bos); } if (nr_bos == 0) goto end_dump; pvr_dump_mark_section(&root_ctx, "Buffer objects"); pvr_bo_list_dump(&root_ctx, &csb->pvr_bo_list, nr_bos); if (!pvr_dump_bo_ctx_push( &first_bo_ctx, &root_ctx, device, list_first_entry(&csb->pvr_bo_list, struct pvr_bo, link))) { pvr_dump_mark_section(&root_ctx, "First buffer"); pvr_dump_println(&root_ctx, ""); goto end_dump; } dump_first_buffer(&first_bo_ctx.base, csb->stream_type, device); pvr_dump_bo_ctx_pop(&first_bo_ctx); end_dump: pvr_dump_end(&root_ctx); }