[PATCH 107/109] drm/amd/ras: parse and route BERT sections
Alex Deucher <[email protected]>
| Newsgroups | org.freedesktop.lists.amd-gfx |
|---|---|
| Message-ID | <[email protected]> |
From: Xiang Liu <[email protected]> BERT may contain structured AMD crashdump, IA processor, Platform Memory, PCIe, and SMN sections. Replaying all sections for every GPU either duplicates GPU records or drops CPU and platform records when their topology IDs are compared with GPU OAM IDs. Parse the supported section formats in rascore. Route GPU records using the OAM identity and gate CPU and platform sections through an explicit owner selected by the operating-system integration layer. Signed-off-by: Xiang Liu <[email protected]> Signed-off-by: Alex Deucher <[email protected]> --- drivers/gpu/drm/amd/ras/core/ras.h | 1 + drivers/gpu/drm/amd/ras/core/ras_bert.c | 337 +++++++++++++++++++++++- 2 files changed, 331 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/ras/core/ras.h b/drivers/gpu/drm/amd/ras/core/ras.h index 6509bd798e48e..49f9dd1b212d3 100644 --- a/drivers/gpu/drm/amd/ras/core/ras.h +++ b/drivers/gpu/drm/amd/ras/core/ras.h @@ -384,6 +384,7 @@ struct ras_core_config { struct ras_core_context { void *dev; struct ras_core_config *config; + bool bert_platform_owner; u32 socket_num_per_node; u32 aid_num_per_socket; u32 xcd_num_per_aid; diff --git a/drivers/gpu/drm/amd/ras/core/ras_bert.c b/drivers/gpu/drm/amd/ras/core/ras_bert.c index 474b2836dee77..cc4140bb97612 100644 --- a/drivers/gpu/drm/amd/ras/core/ras_bert.c +++ b/drivers/gpu/drm/amd/ras/core/ras_bert.c @@ -25,6 +25,20 @@ #include "ras.h" #if defined(CONFIG_X86_MCE_AMD) && defined(CONFIG_ACPI_APEI) +#define RAS_BERT_ERR_INFO_COUNT(bits) (((bits) & GENMASK_ULL(7, 2)) >> 2) +#define RAS_BERT_CONTEXT_COUNT(bits) (((bits) & GENMASK_ULL(13, 8)) >> 8) +#define CPER_MEM_VALID_NODE 0x0008 +#define CPER_MEM_VALID_ERROR_STATUS 0x0001 +#define CPER_MEM_VALID_PA 0x0002 +#define CPER_MEM_VALID_PA_MASK 0x0004 +#define CPER_MEM_VALID_REQUESTOR_ID 0x0800 +#define CPER_MEM_VALID_RESPONDER_ID 0x1000 +#define CPER_MEM_VALID_TARGET_ID 0x2000 +#define CPER_MEM_VALID_ERROR_TYPE 0x4000 +#define AMD_CPER_BODY_VALID_MASK GENMASK_ULL(15, 1) +#define RAS_BERT_VALID_FRU_STRING BIT(1) +#define RAS_BERT_PROC_CTX_TYPE_MSR 1 + struct ras_bert_hest_generic_status { u32 block_status; u32 raw_data_offset; @@ -56,6 +70,71 @@ struct ras_bert_hest_generic_data_v300 { u64 time_stamp; }; +struct ras_bert_sec_proc_ia { + u64 validation_bits; + u64 lapic_id; + u8 cpuid[48]; +}; + +struct ras_bert_ia_err_info { + u8 err_type[16]; + u64 validation_bits; + u64 check_info; + u64 target_id; + u64 requestor_id; + u64 responder_id; + u64 ip; +}; + +struct ras_bert_ia_proc_ctx { + u16 reg_ctx_type; + u16 reg_arr_size; + u32 msr_addr; + u64 mm_reg_addr; +}; + +struct ras_bert_sec_mem_err_old { + u64 validation_bits; + u64 error_status; + u64 physical_addr; + u64 physical_addr_mask; + u16 node; + u16 card; + u16 module; + u16 bank; + u16 device; + u16 row; + u16 column; + u16 bit_pos; + u64 requestor_id; + u64 responder_id; + u64 target_id; + u8 error_type; +} __packed; + +struct ras_bert_sec_mem_err { + u64 validation_bits; + u64 error_status; + u64 physical_addr; + u64 physical_addr_mask; + u16 node; + u16 card; + u16 module; + u16 bank; + u16 device; + u16 row; + u16 column; + u16 bit_pos; + u64 requestor_id; + u64 responder_id; + u64 target_id; + u8 error_type; + u8 extended; + u16 rank; + u16 mem_array_handle; + u16 mem_dev_handle; +} __packed; + static inline int ras_bert_get_version(struct ras_bert_hest_generic_data *gdata) { return gdata->revision >> 8; @@ -121,29 +200,273 @@ struct ras_bert_section_dispatch { ras_bert_section_parser parser; }; -static int ras_bert_record_section(struct ras_core_context *ras_core, +static const struct ras_cper_guid ras_bert_guid_crashdump = GPU__CRASHDUMP; +static const struct ras_cper_guid ras_bert_guid_proc_ia = PROC_ERR__SECTION_TYPE; +static const struct ras_cper_guid ras_bert_guid_platform_mem = PLATFORM_MEM__SECTION_TYPE; +static const struct ras_cper_guid ras_bert_guid_pcie = PCIE_ERR__SECTION_TYPE; +static const struct ras_cper_guid ras_bert_guid_smn = SMN_ERR__SECTION_TYPE; + +static u64 ras_bert_read_le64(const u8 *data) +{ + u64 value = 0; + size_t i; + + for (i = 0; i < sizeof(value); i++) + value |= (u64)data[i] << (i * 8); + + return value; +} + +static void ras_bert_fill_boot_ctx(struct ras_boot_err_ctx *ctx, struct ras_bert_hest_generic_data *gdata) +{ + memcpy(ctx->section_type, gdata->section_type, sizeof(ctx->section_type)); + ctx->error_severity = gdata->error_severity; +} + +static bool ras_bert_amd_body_valid(const u8 *body, u32 body_len) +{ + u64 valid_bits; + u32 context_count; + u32 error_count; + u32 min_size; + + if (body_len < sizeof(struct crashdump_hdr)) + return false; + + valid_bits = ras_bert_read_le64(body); + if (valid_bits & ~AMD_CPER_BODY_VALID_MASK) + return false; + error_count = RAS_BERT_ERR_INFO_COUNT(valid_bits); + context_count = RAS_BERT_CONTEXT_COUNT(valid_bits); + if (!error_count && !context_count) + return false; + + min_size = sizeof(struct crashdump_hdr) + + (error_count * sizeof(struct crashdump_error_info)) + + (context_count * sizeof(struct ras_bert_ia_proc_ctx)); + + return min_size <= body_len; +} + +static bool ras_bert_fru_matches_device(struct ras_core_context *ras_core, + struct ras_bert_hest_generic_data *gdata) +{ + struct device_system_info dev_info = { 0 }; + char fru_text[sizeof(gdata->fru_text) + 1] = { 0 }; + unsigned int socket_id; + + if (!(gdata->validation_bits & RAS_BERT_VALID_FRU_STRING)) + return false; + if (ras_core_get_device_system_info(ras_core, &dev_info)) + return false; + + memcpy(fru_text, gdata->fru_text, sizeof(gdata->fru_text)); + if (sscanf(fru_text, "OAM%u", &socket_id) != 1 && + sscanf(fru_text, "EAM%u", &socket_id) != 1) + return false; + + return socket_id == dev_info.socket_id; +} + +static void ras_bert_log_processor_section(struct ras_core_context *ras_core, + struct ras_bert_hest_generic_data *gdata, + const struct ras_bert_sec_proc_ia *proc, + const struct ras_bert_ia_proc_ctx *ctx_info) { struct ras_boot_err_ctx ctx = { 0 }; u16 data_size; - memcpy(ctx.section_type, gdata->section_type, - min(sizeof(ctx.section_type), sizeof(gdata->section_type))); - ctx.error_severity = gdata->error_severity; + ras_bert_fill_boot_ctx(&ctx, gdata); + ctx.apic_id = proc->lapic_id; + if (proc->validation_bits & CPER_PROC_VALID_APIC_ID) + ctx.flags |= RAS_BOOT_CTX_VALID_APIC_ID; + ctx.reg_ctx_type = ctx_info->reg_ctx_type; + ctx.msr_addr = ctx_info->msr_addr; + ctx.mm_reg_addr = ctx_info->mm_reg_addr; + data_size = min_t(u16, ctx_info->reg_arr_size, sizeof(ctx.regs)); + ctx.reg_arr_size = data_size; + memcpy(ctx.regs, ctx_info + 1, data_size); + + ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_BOOT, &ctx, sizeof(ctx), NULL); +} + +static int ras_bert_record_amd_contexts(struct ras_core_context *ras_core, + struct ras_bert_hest_generic_data *gdata, + const u8 *body) +{ + const struct ras_bert_ia_proc_ctx *context; + u64 valid_bits = ras_bert_read_le64(body); + u32 error_count = RAS_BERT_ERR_INFO_COUNT(valid_bits); + u32 context_count = RAS_BERT_CONTEXT_COUNT(valid_bits); + u32 offset = sizeof(struct crashdump_hdr) + + (error_count * sizeof(struct crashdump_error_info)); + u32 i; + + for (i = 0; i < context_count; i++) { + struct ras_boot_err_ctx ctx = { 0 }; + u32 context_size; + u16 data_size; + + if (offset > gdata->error_data_length || + sizeof(*context) > gdata->error_data_length - offset) + return -EINVAL; + + context = (const struct ras_bert_ia_proc_ctx *)(body + offset); + context_size = ALIGN(sizeof(*context) + context->reg_arr_size, 16); + if (context_size > gdata->error_data_length - offset) + return -EINVAL; + + ras_bert_fill_boot_ctx(&ctx, gdata); + ctx.reg_ctx_type = context->reg_ctx_type; + ctx.msr_addr = context->msr_addr; + ctx.mm_reg_addr = context->mm_reg_addr; + data_size = min_t(u16, context->reg_arr_size, sizeof(ctx.regs)); + ctx.reg_arr_size = data_size; + memcpy(ctx.regs, context + 1, data_size); + ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_BOOT, + &ctx, sizeof(ctx), NULL); + + offset += context_size; + } + + return 0; +} + +static int ras_bert_record_section(struct ras_core_context *ras_core, + struct ras_bert_hest_generic_data *gdata) +{ + struct aca_bank_ecc err = { 0 }; + struct aca_bank_reg bank = { 0 }; + struct device_system_info dev_info = { 0 }; + struct ras_boot_err_ctx ctx = { 0 }; + u64 *regs = ras_bert_get_payload(gdata); + u32 data_size; + int ret; + + if (ras_bert_amd_body_valid((u8 *)regs, gdata->error_data_length)) { + if (!ras_bert_fru_matches_device(ras_core, gdata)) + return 0; + + return ras_bert_record_amd_contexts(ras_core, gdata, + (const u8 *)regs); + } + + if (gdata->error_data_length < sizeof(u64) * (RAS_CPER_ACA_REG_IPID + 1)) + return 0; + + bank.bank_type = MCE_BANK_TYPE_GPU; + data_size = min_t(u32, gdata->error_data_length, sizeof(bank.regs)); + memcpy(bank.regs, regs, data_size); + ret = ras_aca_parse_bank(ras_core, &bank, &err); + if (ret || ras_core_get_device_system_info(ras_core, &dev_info) || + err.bank_info.socket_id != dev_info.socket_id) + return 0; + + ras_bert_fill_boot_ctx(&ctx, gdata); ctx.reg_ctx_type = CPER_CTX_TYPE__BOOT; - data_size = min_t(u32, gdata->error_data_length, sizeof(ctx.regs)); + data_size = min_t(u32, data_size, sizeof(ctx.regs)); ctx.reg_arr_size = data_size; - memcpy(ctx.regs, ras_bert_get_payload(gdata), data_size); + memcpy(ctx.regs, regs, data_size); ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_BOOT, &ctx, sizeof(ctx), NULL); return 0; } -static const struct ras_cper_guid ras_bert_guid_crashdump = GPU__CRASHDUMP; +static int ras_bert_record_proc_ia_section(struct ras_core_context *ras_core, + struct ras_bert_hest_generic_data *gdata) +{ + struct ras_bert_ia_err_info *err_info; + struct ras_bert_ia_proc_ctx *ctx_info; + struct ras_bert_sec_proc_ia *proc; + u8 *section_end; + int ctx_count; + int err_count; + int i; + + if (gdata->error_data_length < sizeof(*proc)) + return -EINVAL; + + if (!ras_core->bert_platform_owner) + return 0; + + proc = ras_bert_get_payload(gdata); + section_end = (u8 *)proc + gdata->error_data_length; + err_count = RAS_BERT_ERR_INFO_COUNT(proc->validation_bits); + ctx_count = RAS_BERT_CONTEXT_COUNT(proc->validation_bits); + err_info = (struct ras_bert_ia_err_info *)(proc + 1); + if ((u8 *)err_info > section_end || + err_count * sizeof(*err_info) > section_end - (u8 *)err_info) + return -EINVAL; + + err_info += err_count; + ctx_info = (struct ras_bert_ia_proc_ctx *)err_info; + for (i = 0; i < ctx_count; i++) { + size_t remaining; + size_t ctx_size; + + if ((u8 *)ctx_info > section_end || + sizeof(*ctx_info) > section_end - (u8 *)ctx_info) + return -EINVAL; + + remaining = section_end - (u8 *)ctx_info; + if (ctx_info->reg_arr_size > remaining - sizeof(*ctx_info)) + return -EINVAL; + ctx_size = ALIGN(sizeof(*ctx_info) + ctx_info->reg_arr_size, 16); + if (ctx_size > remaining) + return -EINVAL; + if (ctx_info->reg_ctx_type != RAS_BERT_PROC_CTX_TYPE_MSR || + ctx_info->msr_addr < CPER_SMCA_MC0_STATUS_MSR || + (ctx_info->msr_addr - CPER_SMCA_MC0_STATUS_MSR) % + CPER_SMCA_BANK_STRIDE) + goto next_ctx; + if (ctx_info->reg_arr_size < sizeof(u64)) + goto next_ctx; + + ras_bert_log_processor_section(ras_core, gdata, proc, ctx_info); + +next_ctx: + ctx_info = (struct ras_bert_ia_proc_ctx *)((u8 *)ctx_info + ctx_size); + } + + return 0; +} + +static int ras_bert_record_platform_mem_section(struct ras_core_context *ras_core, + struct ras_bert_hest_generic_data *gdata) +{ + struct ras_boot_err_ctx ctx = { 0 }; + struct ras_bert_sec_mem_err *mem; + u16 data_size; + + if (gdata->error_data_length != sizeof(struct ras_bert_sec_mem_err_old) && + gdata->error_data_length != sizeof(struct ras_bert_sec_mem_err)) + return -EINVAL; + + if (!ras_core->bert_platform_owner) + return 0; + + mem = ras_bert_get_payload(gdata); + if (gdata->error_data_length > sizeof(ctx.raw_data)) + return -EOVERFLOW; + + ras_bert_fill_boot_ctx(&ctx, gdata); + data_size = gdata->error_data_length; + ctx.raw_data_size = data_size; + memcpy(ctx.raw_data, mem, data_size); + + ras_log_ring_add_log_event(ras_core, RAS_LOG_EVENT_BOOT, &ctx, sizeof(ctx), NULL); + + return 0; +} static const struct ras_bert_section_dispatch ras_bert_section_dispatch[] = { { ras_bert_guid_crashdump.b, ras_bert_record_section }, + { ras_bert_guid_pcie.b, ras_bert_record_section }, + { ras_bert_guid_smn.b, ras_bert_record_section }, + { ras_bert_guid_proc_ia.b, ras_bert_record_proc_ia_section }, + { ras_bert_guid_platform_mem.b, ras_bert_record_platform_mem_section }, }; static const struct ras_bert_section_dispatch * -- 2.55.0