[PATCH 104/109] drm/amd/ras: generate processor CPER records

Alex Deucher <[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
From: Xiang Liu <[email protected]>

Encode runtime CPU machine checks with the standard IA Processor
section, including APIC ID, machine-check status, bank MSR address, and
SMCA register context.

Also preserve IA Processor sections recovered from BERT as
boot-notified records with their original context metadata and variable
payload size.

Route both forms through a processor-specific generator so they remain
separate from AMD crashdump records.

Signed-off-by: Xiang Liu <[email protected]>
Signed-off-by: Alex Deucher <[email protected]>
---
 drivers/gpu/drm/amd/ras/core/ras_cper.c | 212 ++++++++++++++++++++++++
 1 file changed, 212 insertions(+)

diff --git a/drivers/gpu/drm/amd/ras/core/ras_cper.c b/drivers/gpu/drm/amd/ras/core/ras_cper.c
index eee4e0373c3bd..1bbc2c3247c25 100644
--- a/drivers/gpu/drm/amd/ras/core/ras_cper.c
+++ b/drivers/gpu/drm/amd/ras/core/ras_cper.c
@@ -27,6 +27,9 @@
 #include "ras_cper.h"
 #define ns_to_seconds(ns)   div_u64(ns, NSEC_PER_SEC)
 
+static const struct ras_cper_guid processor_section_type =
+	PROC_ERR__SECTION_TYPE;
+
 static int ras_cper_get_profile(struct ras_core_context *ras_core,
 		enum ras_log_event event, struct ras_cper_profile **profile);
 
@@ -183,6 +186,79 @@ static int fill_section_boot(struct ras_core_context *ras_core, void *section,
 	return 0;
 }
 
+static void fill_processor_error_info(struct cper_processor_error_info *error_info,
+		u64 status)
+{
+	u64 check_info = CPER_MS_CHECK_VALID_ERR_TYPE |
+		CPER_MS_CHECK_ERR_TYPE_INTERNAL;
+
+	error_info->error_type = PROC_ERR__MS_CHECK_TYPE;
+	error_info->valid_bits = CPER_PROC_INFO_VALID_CHECK_INFO;
+
+	if (status & CPER_MCA_STATUS_PCC)
+		check_info |= CPER_MS_CHECK_VALID_PCC | CPER_MS_CHECK_PCC;
+	if (status & CPER_MCA_STATUS_UNCORRECTED)
+		check_info |= CPER_MS_CHECK_VALID_UNCORRECTED |
+			CPER_MS_CHECK_UNCORRECTED;
+	if (status & CPER_MCA_STATUS_OVERFLOW)
+		check_info |= CPER_MS_CHECK_VALID_OVERFLOW |
+			CPER_MS_CHECK_OVERFLOW;
+
+	error_info->check_info = check_info;
+}
+
+static int fill_section_processor(struct cper_section_processor *processor,
+		struct ras_log_info *log)
+{
+	const struct ras_cpu_mce *mce = &log->body.cpu_mce;
+	u64 status = mce->regs[RAS_CPER_ACA_REG_STATUS];
+	u64 *reg_dump = processor->context.reg_dump;
+
+	processor->processor.valid_bits = CPER_PROC_VALID_APIC_ID |
+		CPER_PROC_ERR_INFO_COUNT(1) | CPER_PROC_CONTEXT_COUNT(1);
+	processor->processor.apic_id = mce->apic_id;
+	fill_processor_error_info(&processor->error_info, status);
+	processor->context.reg_ctx_type = CPER_CTX_TYPE__CRASH;
+	processor->context.reg_arr_size = sizeof(processor->context.reg_dump);
+	processor->context.msr_addr = CPER_SMCA_MC0_STATUS_MSR +
+		(mce->bank * CPER_SMCA_BANK_STRIDE);
+	/* The IA processor register array starts at MCA_STATUS. */
+	reg_dump[0] = mce->regs[ACA_REG_IDX__STATUS];
+	reg_dump[1] = mce->regs[ACA_REG_IDX__ADDR];
+	reg_dump[2] = mce->regs[ACA_REG_IDX__MISC0];
+	reg_dump[3] = mce->regs[ACA_REG_IDX__CONFG];
+	reg_dump[4] = mce->regs[ACA_REG_IDX__IPID];
+	reg_dump[5] = mce->regs[ACA_REG_IDX__SYND];
+	reg_dump[7] = mce->regs[ACA_REG_IDX__DESTAT];
+	reg_dump[8] = mce->regs[ACA_REG_IDX__DEADDR];
+	reg_dump[9] = mce->regs[ACA_REG_IDX__CTL_MASK];
+
+	return 0;
+}
+
+static int fill_section_boot_processor(struct cper_section_processor *processor,
+		struct ras_log_info *log)
+{
+	const struct ras_boot_err_ctx *ctx = &log->body.boot_err_ctx;
+	u64 status = ctx->regs[0];
+
+	processor->processor.valid_bits = CPER_PROC_ERR_INFO_COUNT(1) |
+		CPER_PROC_CONTEXT_COUNT(1);
+	if (ctx->flags & RAS_BOOT_CTX_VALID_APIC_ID)
+		processor->processor.valid_bits |= CPER_PROC_VALID_APIC_ID;
+	processor->processor.apic_id = ctx->apic_id;
+	fill_processor_error_info(&processor->error_info, status);
+	processor->context.reg_ctx_type = ctx->reg_ctx_type;
+	processor->context.reg_arr_size = ctx->reg_arr_size;
+	processor->context.msr_addr = ctx->msr_addr;
+	processor->context.mm_reg_addr = ctx->mm_reg_addr;
+	memcpy(processor->context.reg_dump, ctx->regs,
+		min_t(size_t, ctx->reg_arr_size,
+		      sizeof(processor->context.reg_dump)));
+
+	return 0;
+}
+
 static int cper_boot_get_severity(struct ras_log_info *log,
 			enum ras_cper_severity *sev)
 {
@@ -293,6 +369,139 @@ static int cper_build_multiple_records(struct ras_core_context *ras_core,
 	return 0;
 }
 
+static int cper_generate_boot_processor_records(struct ras_core_context *ras_core,
+		u8 *buffer, struct ras_log_info *batch_logs, u32 nr_batch_logs)
+{
+	u32 offset = 0;
+	u32 i;
+
+	for (i = 0; i < nr_batch_logs; i++) {
+		const struct ras_boot_err_ctx *ctx = &batch_logs[i].body.boot_err_ctx;
+		struct ras_cper_processor_record record = { 0 };
+		u16 data_size = min_t(u16, ctx->reg_arr_size,
+					 sizeof(record.processor.context.reg_dump));
+		u32 section_size = offsetof(struct cper_section_processor,
+					    context.reg_dump) + data_size;
+		u32 record_size = RAS_HDR_LEN + RAS_SEC_DESC_LEN + section_size;
+		enum ras_cper_severity sev = ctx->error_severity;
+
+		fill_section_hdr(ras_core, &record.hdr, BOOT__TYPE, sev,
+				 &batch_logs[i]);
+		record.hdr.record_length = record_size;
+		record.hdr.sec_cnt = 1;
+		fill_section_descriptor(ras_core, &record.descriptor, sev,
+				PROC_ERR__SECTION_TYPE,
+				offsetof(struct ras_cper_processor_record, processor),
+				section_size);
+		fill_section_boot_processor(&record.processor, &batch_logs[i]);
+		memcpy(buffer + offset, &record, record_size);
+		offset += record_size;
+	}
+
+	return offset;
+}
+
+static int cper_generate_runtime_processor_records(struct ras_core_context *ras_core,
+		u8 *buffer, struct ras_log_info *batch_logs, u32 nr_batch_logs)
+{
+	u32 i;
+
+	for (i = 0; i < nr_batch_logs; i++) {
+		struct ras_cper_processor_record record = { 0 };
+		u64 status = batch_logs[i].body.cpu_mce.regs[ACA_REG_IDX__STATUS];
+		enum ras_cper_severity sev;
+
+		if (!(status & CPER_MCA_STATUS_UNCORRECTED) &&
+		    !(status & CPER_MCA_STATUS_DEFERRED))
+			sev = RAS_CPER_SEV_NON_FATAL_CE;
+		else if ((status & CPER_MCA_STATUS_UNCORRECTED) &&
+			 (status & CPER_MCA_STATUS_PCC))
+			sev = RAS_CPER_SEV_FATAL_UE;
+		else
+			sev = RAS_CPER_SEV_NON_FATAL_UE;
+
+		fill_section_hdr(ras_core, &record.hdr, CPER_NOTIFY__MCE, sev,
+				 &batch_logs[i]);
+		record.hdr.revision = CPER_HDR__REV_AMD_CPU;
+		record.hdr.record_length = sizeof(record);
+		record.hdr.sec_cnt = 1;
+		fill_section_descriptor(ras_core, &record.descriptor, sev,
+				PROC_ERR__SECTION_TYPE,
+				offsetof(struct ras_cper_processor_record, processor),
+				RAS_PROC_SEC_LEN);
+		record.descriptor.flag_bits.latent_err =
+			!!(status & CPER_MCA_STATUS_DEFERRED);
+		record.descriptor.revision_minor = CPER_SEC__REV_AMD_CPU & 0xff;
+		record.descriptor.revision_major = CPER_SEC__REV_AMD_CPU >> 8;
+		fill_section_processor(&record.processor, &batch_logs[i]);
+		memcpy(buffer + (i * sizeof(record)), &record, sizeof(record));
+	}
+
+	return 0;
+}
+
+static bool cper_is_processor_record(struct ras_log_info *log)
+{
+	return log->event == RAS_LOG_EVENT_CPU_RAS ||
+		(log->event == RAS_LOG_EVENT_BOOT &&
+		 !memcmp(log->body.boot_err_ctx.section_type,
+			 processor_section_type.b, CPER_UUID_MAX_SIZE));
+}
+
+static int cper_generate_processor_records(struct ras_core_context *ras_core,
+		struct ras_log_info *batch_logs, u32 nr_batch_logs,
+		u8 *buffer, u32 buf_len, u32 *real_data_len)
+{
+	u32 record_size = 0;
+	u32 i;
+	int ret;
+
+	if (batch_logs[0].event == RAS_LOG_EVENT_CPU_RAS) {
+		if (nr_batch_logs > U32_MAX / sizeof(struct ras_cper_processor_record))
+			return -EOVERFLOW;
+		record_size = sizeof(struct ras_cper_processor_record) * nr_batch_logs;
+		if (record_size > buf_len)
+			return -ENOMEM;
+
+		ret = cper_generate_runtime_processor_records(ras_core, buffer,
+				batch_logs, nr_batch_logs);
+		if (ret)
+			return ret;
+		*real_data_len = record_size;
+		return 0;
+	}
+
+	for (i = 0; i < nr_batch_logs; i++) {
+		const struct ras_boot_err_ctx *ctx = &batch_logs[i].body.boot_err_ctx;
+		u16 data_size;
+		u32 size;
+
+		if (batch_logs[i].event != RAS_LOG_EVENT_BOOT ||
+		    !cper_is_processor_record(&batch_logs[i]))
+			return -EINVAL;
+
+		data_size = min_t(u16, ctx->reg_arr_size,
+				  sizeof(struct cper_processor_context) -
+				  offsetof(struct cper_processor_context, reg_dump));
+		size = RAS_HDR_LEN + RAS_SEC_DESC_LEN +
+			offsetof(struct cper_section_processor, context.reg_dump) +
+			data_size;
+		if (record_size > U32_MAX - size)
+			return -EOVERFLOW;
+		record_size += size;
+	}
+	if (record_size > buf_len)
+		return -ENOMEM;
+
+	ret = cper_generate_boot_processor_records(ras_core, buffer,
+			batch_logs, nr_batch_logs);
+	if (ret < 0)
+		return ret;
+	*real_data_len = ret;
+
+	return 0;
+}
+
 static enum ras_log_event cper_mce_parse_err_type(struct ras_core_context *ras_core,
 						  struct aca_bank_reg *bank)
 {
@@ -327,6 +536,9 @@ int ras_cper_generate_batch_cper(struct ras_core_context *ras_core,
 		return -EINVAL;
 
 	*real_data_len = 0;
+	if (cper_is_processor_record(&batch_logs[0]))
+		return cper_generate_processor_records(ras_core, batch_logs,
+			nr_batch_logs, buf, buf_len, real_data_len);
 
 	event = batch_logs[0].event;
 	if (event == RAS_LOG_EVENT_MCE) {
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.