blob: 1ebc6488d2eb6afc3faed2d05b0978ab04578965 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "msd_intel_device.h"
#include "registers.h"
#include <memory>
#include <string>
class InstructionDecoder {
public:
enum Id {
NOOP = 0x0,
MI_BATCH_BUFFER_END = 0x0500,
LOAD_REGISTER_IMM = 0x1100,
_3DSTATE_CLEAR_PARAMS = 0x7804,
_3DSTATE_DEPTH_BUFFER = 0x7805,
_3DSTATE_STENCIL_BUFFER = 0x7806,
_3DSTATE_HIER_DEPTH_BUFFER = 0x7807,
_3DSTATE_VERTEX_BUFFERS = 0x7808,
_3DSTATE_VERTEX_ELEMENTS = 0x7809,
_3DSTATE_MULTISAMPLE = 0x780d,
_3DSTATE_INDEX_BUFFER = 0x780a,
_3DSTATE_VF = 0x780c,
_3DSTATE_SCISSOR_STATE_POINTERS = 0x780f,
_3DSTATE_VS = 0x7810,
_3DSTATE_GS = 0x7811,
_3DSTATE_CLIP = 0x7812,
_3DSTATE_SF = 0x7813,
_3DSTATE_WM = 0x7814,
_3DSTATE_CONSTANT_VS = 0x7815,
_3DSTATE_CONSTANT_GS = 0x7816,
_3DSTATE_CONSTANT_PS = 0x7817,
_3DSTATE_SAMPLE_MASK = 0x7818,
_3DSTATE_CONSTANT_HS = 0x7819,
_3DSTATE_CONSTANT_DS = 0x781a,
_3DSTATE_HS = 0x781b,
_3DSTATE_TE = 0x781c,
_3DSTATE_DS = 0x781d,
_3DSTATE_STREAMOUT = 0x781e,
_3DSTATE_SBE = 0x781f,
_3DSTATE_PS = 0x7820,
_3DSTATE_VIEWPORT_STATE_POINTERS_SF_CLIP = 0x7821,
_3DSTATE_VIEWPORT_STATE_POINTERS_CC = 0x7823,
_3DSTATE_BINDING_TABLE_POINTERS_VS = 0x7826,
_3DSTATE_BINDING_TABLE_POINTERS_HS = 0x7827,
_3DSTATE_BINDING_TABLE_POINTERS_DS = 0x7828,
_3DSTATE_BINDING_TABLE_POINTERS_GS = 0x7829,
_3DSTATE_BINDING_TABLE_POINTERS_PS = 0x782a,
_3DSTATE_SAMPLER_STATE_POINTERS_PS = 0x782f,
_3DSTATE_CC_STATE_POINTERS = 0x780e,
_3DSTATE_BLEND_STATE_POINTERS = 0x7824,
_3DSTATE_URB_VS = 0x7830,
_3DSTATE_URB_HS = 0x7831,
_3DSTATE_URB_DS = 0x7832,
_3DSTATE_URB_GS = 0x7833,
_3DSTATE_VF_INSTANCING = 0x7849,
_3DSTATE_VF_SGVS = 0x784a,
_3DSTATE_VF_TOPOLOGY = 0x784b,
_3DSTATE_PS_BLEND = 0x784d,
_3DSTATE_WM_DEPTH_STENCIL = 0x784e,
_3DSTATE_PS_EXTRA = 0x784f,
_3DSTATE_RASTER = 0x7850,
_3DSTATE_SBE_SWIZ = 0x7851,
_3DSTATE_WM_HZ_OP = 0x7852,
_3DSTATE_PUSH_CONSTANT_ALLOC_VS = 0x7912,
_3DSTATE_PUSH_CONSTANT_ALLOC_HS = 0x7913,
_3DSTATE_PUSH_CONSTANT_ALLOC_DS = 0x7914,
_3DSTATE_PUSH_CONSTANT_ALLOC_GS = 0x7915,
_3DSTATE_PUSH_CONSTANT_ALLOC_PS = 0x7916,
PIPE_CONTROL = 0x7a00,
_3DPRIMITIVE = 0x7b00,
STATE_BASE_ADDRESS = 0x6101,
PIPELINE_SELECT = 0x6904,
};
static const char* name(Id id)
{
switch (id) {
case _3DSTATE_VERTEX_BUFFERS:
return "3DSTATE_VERTEX_BUFFERS";
case _3DSTATE_VERTEX_ELEMENTS:
return "3DSTATE_VERTEX_ELEMENTS";
case LOAD_REGISTER_IMM:
return "LOAD_REGISTER_IMM";
case PIPE_CONTROL:
return "PIPE_CONTROL";
case PIPELINE_SELECT:
return "PIPELINE_SELECT";
case STATE_BASE_ADDRESS:
return "STATE_BASE_ADDRESS";
case _3DSTATE_VF_SGVS:
return "3DSTATE_VF_SGVS";
case _3DSTATE_VF_INSTANCING:
return "3DSTATE_VF_INSTANCING";
case _3DSTATE_VF_TOPOLOGY:
return "3DSTATE_VF_TOPOLOGY";
case _3DSTATE_URB_VS:
return "3DSTATE_URB_VS";
case _3DSTATE_URB_HS:
return "3DSTATE_URB_HS";
case _3DSTATE_URB_DS:
return "3DSTATE_URB_DS";
case _3DSTATE_URB_GS:
return "3DSTATE_URB_GS";
case _3DSTATE_BLEND_STATE_POINTERS:
return "3DSTATE_BLEND_STATE_POINTERS";
case _3DSTATE_PS_BLEND:
return "3DSTATE_PS_BLEND";
case _3DSTATE_CC_STATE_POINTERS:
return "3DSTATE_CC_STATE_POINTERS";
case _3DSTATE_WM_DEPTH_STENCIL:
return "3DSTATE_WM_DEPTH_STENCIL";
case _3DSTATE_CONSTANT_VS:
return "3DSTATE_CONSTANT_VS";
case _3DSTATE_CONSTANT_HS:
return "3DSTATE_CONSTANT_HS";
case _3DSTATE_CONSTANT_DS:
return "3DSTATE_CONSTANT_DS";
case _3DSTATE_CONSTANT_GS:
return "3DSTATE_CONSTANT_GS";
case _3DSTATE_CONSTANT_PS:
return "3DSTATE_CONSTANT_PS";
case _3DSTATE_BINDING_TABLE_POINTERS_VS:
return "3DSTATE_BINDING_TABLE_POINTERS_VS";
case _3DSTATE_BINDING_TABLE_POINTERS_HS:
return "3DSTATE_BINDING_TABLE_POINTERS_HS";
case _3DSTATE_BINDING_TABLE_POINTERS_DS:
return "3DSTATE_BINDING_TABLE_POINTERS_DS";
case _3DSTATE_BINDING_TABLE_POINTERS_GS:
return "3DSTATE_BINDING_TABLE_POINTERS_GS";
case _3DSTATE_BINDING_TABLE_POINTERS_PS:
return "3DSTATE_BINDING_TABLE_POINTERS_PS";
case _3DSTATE_SAMPLER_STATE_POINTERS_PS:
return "3DSTATE_SAMPLER_STATE_POINTERS_PS";
case _3DSTATE_MULTISAMPLE:
return "3DSTATE_MULTISAMPLE";
case _3DSTATE_SAMPLE_MASK:
return "3DSTATE_SAMPLE_MASK";
case _3DSTATE_VS:
return "3DSTATE_VS";
case _3DSTATE_HS:
return "3DSTATE_HS";
case _3DSTATE_TE:
return "3DSTATE_TE";
case _3DSTATE_DS:
return "3DSTATE_DS";
case _3DSTATE_STREAMOUT:
return "3DSTATE_STREAMOUT";
case _3DSTATE_GS:
return "3DSTATE_GS";
case _3DSTATE_CLIP:
return "3DSTATE_CLIP";
case _3DSTATE_SF:
return "3DSTATE_SF";
case _3DSTATE_RASTER:
return "3DSTATE_RASTER";
case _3DSTATE_SBE:
return "3DSTATE_SBE";
case _3DSTATE_WM:
return "3DSTATE_WM";
case _3DSTATE_PS:
return "3DSTATE_PS";
case _3DSTATE_PS_EXTRA:
return "3DSTATE_PS_EXTRA";
case _3DSTATE_VIEWPORT_STATE_POINTERS_CC:
return "3DSTATE_VIEWPORT_STATE_POINTERS_CC";
case _3DSTATE_DEPTH_BUFFER:
return "3DSTATE_DEPTH_BUFFER";
case _3DSTATE_HIER_DEPTH_BUFFER:
return "3DSTATE_HIER_DEPTH_BUFFER";
case _3DSTATE_STENCIL_BUFFER:
return "3DSTATE_STENCIL_BUFFER";
case _3DSTATE_CLEAR_PARAMS:
return "3DSTATE_CLEAR_PARAMS";
case _3DPRIMITIVE:
return "3DPRIMITIVE";
case _3DSTATE_INDEX_BUFFER:
return "3DSTATE_INDEX_BUFFER";
case _3DSTATE_SBE_SWIZ:
return "3DSTATE_SBE_SWIZ";
case _3DSTATE_PUSH_CONSTANT_ALLOC_VS:
return "3DSTATE_PUSH_CONSTANT_ALLOC_VS";
case _3DSTATE_PUSH_CONSTANT_ALLOC_HS:
return "3DSTATE_PUSH_CONSTANT_ALLOC_HS";
case _3DSTATE_PUSH_CONSTANT_ALLOC_DS:
return "3DSTATE_PUSH_CONSTANT_ALLOC_DS";
case _3DSTATE_PUSH_CONSTANT_ALLOC_GS:
return "3DSTATE_PUSH_CONSTANT_ALLOC_GS";
case _3DSTATE_PUSH_CONSTANT_ALLOC_PS:
return "3DSTATE_PUSH_CONSTANT_ALLOC_PS";
case _3DSTATE_WM_HZ_OP:
return "3DSTATE_WM_HZ_OP";
case _3DSTATE_VIEWPORT_STATE_POINTERS_SF_CLIP:
return "3DSTATE_VIEWPORT_STATE_POINTERS_SF_CLIP";
case _3DSTATE_SCISSOR_STATE_POINTERS:
return "3DSTATE_SCISSOR_STATE_POINTERS";
case _3DSTATE_VF:
return "3DSTATE_VF";
case MI_BATCH_BUFFER_END:
return "MI_BATCH_BUFFER_END";
case NOOP:
return "NOOP";
}
return "UNKNOWN";
}
static bool Decode(uint32_t dword, Id* id_out, uint32_t* dword_count_out)
{
if (dword == 0) {
*id_out = NOOP;
*dword_count_out = 1;
return true;
}
uint16_t id = dword >> 16;
switch (id) {
case PIPELINE_SELECT:
case MI_BATCH_BUFFER_END:
*dword_count_out = 1;
break;
case LOAD_REGISTER_IMM:
*dword_count_out = 3;
break;
case _3DSTATE_BLEND_STATE_POINTERS:
case _3DSTATE_CC_STATE_POINTERS:
case _3DSTATE_VIEWPORT_STATE_POINTERS_CC:
*dword_count_out = 2;
break;
case _3DSTATE_VF:
case _3DSTATE_SCISSOR_STATE_POINTERS:
case _3DSTATE_VIEWPORT_STATE_POINTERS_SF_CLIP:
case _3DSTATE_PUSH_CONSTANT_ALLOC_PS:
case _3DSTATE_PUSH_CONSTANT_ALLOC_GS:
case _3DSTATE_PUSH_CONSTANT_ALLOC_DS:
case _3DSTATE_PUSH_CONSTANT_ALLOC_HS:
case _3DSTATE_PUSH_CONSTANT_ALLOC_VS:
case _3DSTATE_SBE_SWIZ:
case _3DSTATE_INDEX_BUFFER:
case _3DPRIMITIVE:
case _3DSTATE_CLEAR_PARAMS:
case _3DSTATE_STENCIL_BUFFER:
case _3DSTATE_HIER_DEPTH_BUFFER:
case _3DSTATE_DEPTH_BUFFER:
case _3DSTATE_PS_EXTRA:
case _3DSTATE_PS:
case _3DSTATE_WM:
case _3DSTATE_SBE:
case _3DSTATE_RASTER:
case _3DSTATE_SF:
case _3DSTATE_CLIP:
case _3DSTATE_GS:
case _3DSTATE_STREAMOUT:
case _3DSTATE_DS:
case _3DSTATE_TE:
case _3DSTATE_VS:
case _3DSTATE_HS:
case _3DSTATE_SAMPLE_MASK:
case _3DSTATE_MULTISAMPLE:
case _3DSTATE_SAMPLER_STATE_POINTERS_PS:
case _3DSTATE_BINDING_TABLE_POINTERS_PS:
case _3DSTATE_BINDING_TABLE_POINTERS_GS:
case _3DSTATE_BINDING_TABLE_POINTERS_DS:
case _3DSTATE_BINDING_TABLE_POINTERS_HS:
case _3DSTATE_BINDING_TABLE_POINTERS_VS:
case _3DSTATE_CONSTANT_PS:
case _3DSTATE_CONSTANT_GS:
case _3DSTATE_CONSTANT_DS:
case _3DSTATE_CONSTANT_HS:
case _3DSTATE_CONSTANT_VS:
case _3DSTATE_WM_DEPTH_STENCIL:
case _3DSTATE_PS_BLEND:
case _3DSTATE_URB_GS:
case _3DSTATE_URB_DS:
case _3DSTATE_URB_HS:
case _3DSTATE_URB_VS:
case _3DSTATE_VF_TOPOLOGY:
case _3DSTATE_VF_INSTANCING:
case _3DSTATE_VF_SGVS:
case _3DSTATE_VERTEX_BUFFERS:
case _3DSTATE_VERTEX_ELEMENTS:
case _3DSTATE_WM_HZ_OP:
case PIPE_CONTROL:
case STATE_BASE_ADDRESS:
*dword_count_out = (dword & 0xFF) + 2;
break;
}
*id_out = static_cast<Id>(id);
return true;
}
};
void MsdIntelDevice::Dump(DumpState* dump_out)
{
dump_out->render_cs.sequence_number =
global_context_->hardware_status_page(render_engine_cs_->id())->read_sequence_number();
dump_out->render_cs.active_head_pointer = render_engine_cs_->GetActiveHeadPointer();
dump_out->render_cs.inflight_batches = render_engine_cs_->GetInflightBatches();
DumpFault(dump_out, registers::AllEngineFault::read(register_io_.get()));
dump_out->fault_gpu_address = kInvalidGpuAddr;
dump_out->global = false;
if (dump_out->fault_present)
DumpFaultAddress(dump_out, register_io_.get());
}
void MsdIntelDevice::DumpFault(DumpState* dump_out, uint32_t fault)
{
dump_out->fault_present = registers::AllEngineFault::valid(fault);
dump_out->fault_engine = registers::AllEngineFault::engine(fault);
dump_out->fault_src = registers::AllEngineFault::src(fault);
dump_out->fault_type = registers::AllEngineFault::type(fault);
}
void MsdIntelDevice::DumpFaultAddress(DumpState* dump_out, magma::RegisterIo* register_io)
{
uint64_t val = registers::FaultTlbReadData::read(register_io);
dump_out->fault_gpu_address = registers::FaultTlbReadData::addr(val);
dump_out->global = registers::FaultTlbReadData::is_ggtt(val);
}
void MsdIntelDevice::DumpToString(std::string& dump_out)
{
DumpState dump_state;
Dump(&dump_state);
FormatDump(dump_state, dump_out);
}
void MsdIntelDevice::FormatDump(DumpState& dump_state, std::string& dump_out)
{
const char* build = magma::kDebug ? "DEBUG" : "RELEASE";
const char* fmt = "---- device dump begin ----\n"
"%s build\n"
"Device id: 0x%x Revision: 0x%x\n"
"RENDER_COMMAND_STREAMER\n"
"sequence_number 0x%x\n"
"active head pointer: 0x%llx\n";
int size = std::snprintf(nullptr, 0, fmt, build, device_id(), revision(),
dump_state.render_cs.sequence_number,
dump_state.render_cs.active_head_pointer);
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, build, device_id(), revision(),
dump_state.render_cs.sequence_number, dump_state.render_cs.active_head_pointer);
dump_out.append(&buf[0]);
if (dump_state.fault_present) {
fmt = "ENGINE FAULT DETECTED\n"
"engine 0x%x src 0x%x type 0x%x gpu_address 0x%lx global %d\n";
size =
std::snprintf(nullptr, 0, fmt, dump_state.fault_engine, dump_state.fault_src,
dump_state.fault_type, dump_state.fault_gpu_address, dump_state.global);
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, dump_state.fault_engine, dump_state.fault_src,
dump_state.fault_type, dump_state.fault_gpu_address, dump_state.global);
dump_out.append(&buf[0]);
} else {
dump_out.append("No engine faults detected.\n");
}
bool is_mapped = false;
std::shared_ptr<GpuMapping> fault_mapping;
std::shared_ptr<GpuMapping> closest_mapping;
GpuMapping* faulted_batch_mapping = nullptr;
uint64_t closest_mapping_distance = UINT64_MAX;
if (!dump_state.render_cs.inflight_batches.empty()) {
dump_out.append("Inflight Batches:\n");
for (auto batch : dump_state.render_cs.inflight_batches) {
fmt = " Batch %p, context %p, connection client_id %lu\n";
auto context = batch->GetContext().lock().get();
auto connection = context ? context->connection().lock() : nullptr;
size = std::snprintf(nullptr, 0, fmt, batch, context,
connection ? connection->client_id() : 0u);
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, batch, context,
connection ? connection->client_id() : 0u);
dump_out.append(&buf[0]);
auto batch_mapping = batch->GetBatchMapping();
if (!batch_mapping)
continue;
if (dump_state.render_cs.active_head_pointer >= batch_mapping->gpu_addr() &&
dump_state.render_cs.active_head_pointer <
batch_mapping->gpu_addr() + batch_mapping->length()) {
dump_out.append(" FAULTING BATCH (active head ptr within this batch)\n");
faulted_batch_mapping = batch_mapping;
}
if (!batch->IsCommandBuffer())
continue;
auto cmd_buf = static_cast<CommandBuffer*>(batch);
for (auto mapping : cmd_buf->exec_resource_mappings()) {
fmt = " Mapping %p, aspace %p, buffer 0x%lx, gpu addr range [0x%lx, 0x%lx), "
"offset 0x%lx, mapping length 0x%lx\n";
gpu_addr_t mapping_start = mapping->gpu_addr();
gpu_addr_t mapping_end = mapping->gpu_addr() + mapping->length();
size = std::snprintf(nullptr, 0, fmt, mapping.get(),
mapping->address_space().lock().get(),
mapping->buffer()->platform_buffer()->id(), mapping_start,
mapping_end, mapping->offset(), mapping->length());
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, mapping.get(),
mapping->address_space().lock().get(),
mapping->buffer()->platform_buffer()->id(), mapping_start,
mapping_end, mapping->offset(), mapping->length());
dump_out.append(&buf[0]);
if (dump_state.fault_gpu_address >= mapping_start &&
dump_state.fault_gpu_address < mapping_end) {
is_mapped = true;
fault_mapping = mapping;
} else if (dump_state.fault_gpu_address > mapping_end &&
dump_state.fault_gpu_address - mapping_end < closest_mapping_distance) {
closest_mapping_distance = dump_state.fault_gpu_address - mapping_end;
closest_mapping = mapping;
}
}
}
}
if (is_mapped) {
fmt = "Fault address appears to be within mapping %p addr [0x%lx, 0x%lx)\n";
size = std::snprintf(nullptr, 0, fmt, fault_mapping.get(), fault_mapping->gpu_addr(),
fault_mapping->gpu_addr() + fault_mapping->length());
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, fault_mapping.get(), fault_mapping->gpu_addr(),
fault_mapping->gpu_addr() + fault_mapping->length());
dump_out.append(&buf[0]);
} else {
dump_out.append("Fault address does not appear to be mapped for any outstanding batch\n");
if (closest_mapping_distance < UINT64_MAX) {
fmt = "Fault address is 0x%lx past the end of mapping %p addr [0x%08lx, 0x%08lx), size "
"0x%lx, buffer size 0x%lx\n";
size = std::snprintf(nullptr, 0, fmt, closest_mapping_distance, closest_mapping.get(),
closest_mapping->gpu_addr(),
closest_mapping->gpu_addr() + closest_mapping->length(),
closest_mapping->length(),
closest_mapping->buffer()->platform_buffer()->size());
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, closest_mapping_distance, closest_mapping.get(),
closest_mapping->gpu_addr(),
closest_mapping->gpu_addr() + closest_mapping->length(),
closest_mapping->length(),
closest_mapping->buffer()->platform_buffer()->size());
dump_out.append(&buf[0]);
}
}
if (faulted_batch_mapping) {
dump_out.append("Batch instructions immediately surrounding the active head:\n");
uint32_t* batch_data = nullptr;
// dont early out because we always want to print the "dump end" line
if (faulted_batch_mapping->buffer()->platform_buffer()->MapCpu(
reinterpret_cast<void**>(&batch_data))) {
uint64_t active_head_offset =
dump_state.render_cs.active_head_pointer - faulted_batch_mapping->gpu_addr();
DASSERT(active_head_offset <= faulted_batch_mapping->length());
DASSERT(active_head_offset % sizeof(uint32_t) == 0);
uint64_t total_dwords = faulted_batch_mapping->length() / sizeof(uint32_t);
uint64_t active_head_dword = active_head_offset / sizeof(uint32_t);
uint64_t start_dword = faulted_batch_mapping->offset();
uint64_t end_dword = total_dwords - 1;
uint32_t dwords_remaining = 0;
bool end_of_batch = false;
for (uint64_t i = start_dword; i < end_dword; i++) {
if (dwords_remaining == 0) {
InstructionDecoder::Id id;
bool decoded =
InstructionDecoder::Decode(batch_data[i], &id, &dwords_remaining);
if (decoded) {
fmt = "\n%s: ";
size = std::snprintf(nullptr, 0, fmt, InstructionDecoder::name(id));
buf = std::vector<char>(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, InstructionDecoder::name(id));
dump_out.append(&buf[0]);
end_of_batch = id == InstructionDecoder::Id::MI_BATCH_BUFFER_END;
}
}
const char* prefix = "";
const char* suffix = ",";
if (i == active_head_dword) {
prefix = "===>";
suffix = "<===,";
}
if (dwords_remaining)
--dwords_remaining;
fmt = "%s0x%08lx%s";
size = std::snprintf(nullptr, 0, fmt, prefix, batch_data[i], suffix);
std::vector<char> buf(size + 1);
std::snprintf(&buf[0], buf.size(), fmt, prefix, batch_data[i], suffix);
dump_out.append(&buf[0]);
if (end_of_batch)
break;
}
dump_out.append("\n\n");
} else {
dump_out.append("Failed to map batch data\n");
}
}
#if MSD_INTEL_ENABLE_MAPPING_CACHE
dump_out.append("mapping cache: ENABLED\n");
#else
dump_out.append("mapping cache: DISABLED\n");
#endif
dump_out.append("---- device dump end ----");
}