Linux Audio

Check our new training course

Open-source upstreaming

Need help get the support for your hardware in upstream Linux?
Loading...
v6.13.7
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * FRU (Field-Replaceable Unit) Memory Poison Manager
   4 *
   5 * Copyright (c) 2024, Advanced Micro Devices, Inc.
   6 * All Rights Reserved.
   7 *
   8 * Authors:
   9 *	Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
  10 *	Muralidhara M K <muralidhara.mk@amd.com>
  11 *	Yazen Ghannam <Yazen.Ghannam@amd.com>
  12 *
  13 * Implementation notes, assumptions, and limitations:
  14 *
  15 * - FRU memory poison section and memory poison descriptor definitions are not yet
  16 *   included in the UEFI specification. So they are defined here. Afterwards, they
  17 *   may be moved to linux/cper.h, if appropriate.
  18 *
  19 * - Platforms based on AMD MI300 systems will be the first to use these structures.
  20 *   There are a number of assumptions made here that will need to be generalized
  21 *   to support other platforms.
  22 *
  23 *   AMD MI300-based platform(s) assumptions:
  24 *   - Memory errors are reported through x86 MCA.
  25 *   - The entire DRAM row containing a memory error should be retired.
  26 *   - There will be (1) FRU memory poison section per CPER.
  27 *   - The FRU will be the CPU package (processor socket).
  28 *   - The default number of memory poison descriptor entries should be (8).
  29 *   - The platform will use ACPI ERST for persistent storage.
  30 *   - All FRU records should be saved to persistent storage. Module init will
  31 *     fail if any FRU record is not successfully written.
  32 *
  33 * - Boot time memory retirement may occur later than ideal due to dependencies
  34 *   on other libraries and drivers. This leaves a gap where bad memory may be
  35 *   accessed during early boot stages.
  36 *
  37 * - Enough memory should be pre-allocated for each FRU record to be able to hold
  38 *   the expected number of descriptor entries. This, mostly empty, record is
  39 *   written to storage during init time. Subsequent writes to the same record
  40 *   should allow the Platform to update the stored record in-place. Otherwise,
  41 *   if the record is extended, then the Platform may need to perform costly memory
  42 *   management operations on the storage. For example, the Platform may spend time
  43 *   in Firmware copying and invalidating memory on a relatively slow SPI ROM.
  44 */
  45
  46#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  47
  48#include <linux/cper.h>
  49#include <linux/ras.h>
  50#include <linux/cpu.h>
  51
  52#include <acpi/apei.h>
  53
  54#include <asm/cpu_device_id.h>
  55#include <asm/mce.h>
  56
  57#include "../debugfs.h"
  58
  59#include "atl/internal.h"
  60
  61#define INVALID_CPU			UINT_MAX
  62
  63/* Validation Bits */
  64#define FMP_VALID_ARCH_TYPE		BIT_ULL(0)
  65#define FMP_VALID_ARCH			BIT_ULL(1)
  66#define FMP_VALID_ID_TYPE		BIT_ULL(2)
  67#define FMP_VALID_ID			BIT_ULL(3)
  68#define FMP_VALID_LIST_ENTRIES		BIT_ULL(4)
  69#define FMP_VALID_LIST			BIT_ULL(5)
  70
  71/* FRU Architecture Types */
  72#define FMP_ARCH_TYPE_X86_CPUID_1_EAX	0
  73
  74/* FRU ID Types */
  75#define FMP_ID_TYPE_X86_PPIN		0
  76
  77/* FRU Memory Poison Section */
  78struct cper_sec_fru_mem_poison {
  79	u32 checksum;
  80	u64 validation_bits;
  81	u32 fru_arch_type;
  82	u64 fru_arch;
  83	u32 fru_id_type;
  84	u64 fru_id;
  85	u32 nr_entries;
  86} __packed;
  87
  88/* FRU Descriptor ID Types */
  89#define FPD_HW_ID_TYPE_MCA_IPID		0
  90
  91/* FRU Descriptor Address Types */
  92#define FPD_ADDR_TYPE_MCA_ADDR		0
  93
  94/* Memory Poison Descriptor */
  95struct cper_fru_poison_desc {
  96	u64 timestamp;
  97	u32 hw_id_type;
  98	u64 hw_id;
  99	u32 addr_type;
 100	u64 addr;
 101} __packed;
 102
 103/* Collection of headers and sections for easy pointer use. */
 104struct fru_rec {
 105	struct cper_record_header	hdr;
 106	struct cper_section_descriptor	sec_desc;
 107	struct cper_sec_fru_mem_poison	fmp;
 108	struct cper_fru_poison_desc	entries[];
 109} __packed;
 110
 111/*
 112 * Pointers to the complete CPER record of each FRU.
 113 *
 114 * Memory allocation will include padded space for descriptor entries.
 115 */
 116static struct fru_rec **fru_records;
 117
 118/* system physical addresses array */
 119static u64 *spa_entries;
 
 
 120
 121static struct dentry *fmpm_dfs_dir;
 122static struct dentry *fmpm_dfs_entries;
 123
 124#define CPER_CREATOR_FMP						\
 125	GUID_INIT(0xcd5c2993, 0xf4b2, 0x41b2, 0xb5, 0xd4, 0xf9, 0xc3,	\
 126		  0xa0, 0x33, 0x08, 0x75)
 127
 128#define CPER_SECTION_TYPE_FMP						\
 129	GUID_INIT(0x5e4706c1, 0x5356, 0x48c6, 0x93, 0x0b, 0x52, 0xf2,	\
 130		  0x12, 0x0a, 0x44, 0x58)
 131
 132/**
 133 * DOC: max_nr_entries (byte)
 134 * Maximum number of descriptor entries possible for each FRU.
 135 *
 136 * Values between '1' and '255' are valid.
 137 * No input or '0' will default to FMPM_DEFAULT_MAX_NR_ENTRIES.
 138 */
 139static u8 max_nr_entries;
 140module_param(max_nr_entries, byte, 0644);
 141MODULE_PARM_DESC(max_nr_entries,
 142		 "Maximum number of memory poison descriptor entries per FRU");
 143
 144#define FMPM_DEFAULT_MAX_NR_ENTRIES	8
 145
 146/* Maximum number of FRUs in the system. */
 147#define FMPM_MAX_NR_FRU			256
 148static unsigned int max_nr_fru;
 149
 150/* Total length of record including headers and list of descriptor entries. */
 151static size_t max_rec_len;
 152
 153#define FMPM_MAX_REC_LEN (sizeof(struct fru_rec) + (sizeof(struct cper_fru_poison_desc) * 255))
 154
 155/* Total number of SPA entries across all FRUs. */
 156static unsigned int spa_nr_entries;
 157
 158/*
 159 * Protect the local records cache in fru_records and prevent concurrent
 160 * writes to storage. This is only needed after init once notifier block
 161 * registration is done.
 162 *
 163 * The majority of a record is fixed at module init and will not change
 164 * during run time. The entries within a record will be updated as new
 165 * errors are reported. The mutex should be held whenever the entries are
 166 * accessed during run time.
 167 */
 168static DEFINE_MUTEX(fmpm_update_mutex);
 169
 170#define for_each_fru(i, rec) \
 171	for (i = 0; rec = fru_records[i], i < max_nr_fru; i++)
 172
 173static inline u32 get_fmp_len(struct fru_rec *rec)
 174{
 175	return rec->sec_desc.section_length - sizeof(struct cper_section_descriptor);
 176}
 177
 178static struct fru_rec *get_fru_record(u64 fru_id)
 179{
 180	struct fru_rec *rec;
 181	unsigned int i;
 182
 183	for_each_fru(i, rec) {
 184		if (rec->fmp.fru_id == fru_id)
 185			return rec;
 186	}
 187
 188	pr_debug("Record not found for FRU 0x%016llx\n", fru_id);
 189
 190	return NULL;
 191}
 192
 193/*
 194 * Sum up all bytes within the FRU Memory Poison Section including the Memory
 195 * Poison Descriptor entries.
 196 *
 197 * Don't include the old checksum here. It's a u32 value, so summing each of its
 198 * bytes will give the wrong total.
 199 */
 200static u32 do_fmp_checksum(struct cper_sec_fru_mem_poison *fmp, u32 len)
 201{
 202	u32 checksum = 0;
 203	u8 *buf, *end;
 204
 205	/* Skip old checksum. */
 206	buf = (u8 *)fmp + sizeof(u32);
 207	end = buf + len;
 208
 209	while (buf < end)
 210		checksum += (u8)(*(buf++));
 211
 212	return checksum;
 213}
 214
 215static int update_record_on_storage(struct fru_rec *rec)
 216{
 217	u32 len, checksum;
 218	int ret;
 219
 220	/* Calculate a new checksum. */
 221	len = get_fmp_len(rec);
 222
 223	/* Get the current total. */
 224	checksum = do_fmp_checksum(&rec->fmp, len);
 225
 226	/* Use the complement value. */
 227	rec->fmp.checksum = -checksum;
 228
 229	pr_debug("Writing to storage\n");
 230
 231	ret = erst_write(&rec->hdr);
 232	if (ret) {
 233		pr_warn("Storage update failed for FRU 0x%016llx\n", rec->fmp.fru_id);
 234
 235		if (ret == -ENOSPC)
 236			pr_warn("Not enough space on storage\n");
 237	}
 238
 239	return ret;
 240}
 241
 242static bool rec_has_valid_entries(struct fru_rec *rec)
 243{
 244	if (!(rec->fmp.validation_bits & FMP_VALID_LIST_ENTRIES))
 245		return false;
 246
 247	if (!(rec->fmp.validation_bits & FMP_VALID_LIST))
 248		return false;
 249
 250	return true;
 251}
 252
 253static bool fpds_equal(struct cper_fru_poison_desc *old, struct cper_fru_poison_desc *new)
 254{
 255	/*
 256	 * Ignore timestamp field.
 257	 * The same physical error may be reported multiple times due to stuck bits, etc.
 258	 *
 259	 * Also, order the checks from most->least likely to fail to shortcut the code.
 260	 */
 261	if (old->addr != new->addr)
 262		return false;
 263
 264	if (old->hw_id != new->hw_id)
 265		return false;
 266
 267	if (old->addr_type != new->addr_type)
 268		return false;
 269
 270	if (old->hw_id_type != new->hw_id_type)
 271		return false;
 272
 273	return true;
 274}
 275
 276static bool rec_has_fpd(struct fru_rec *rec, struct cper_fru_poison_desc *fpd)
 277{
 278	unsigned int i;
 279
 280	for (i = 0; i < rec->fmp.nr_entries; i++) {
 281		struct cper_fru_poison_desc *fpd_i = &rec->entries[i];
 282
 283		if (fpds_equal(fpd_i, fpd)) {
 284			pr_debug("Found duplicate record\n");
 285			return true;
 286		}
 287	}
 288
 289	return false;
 290}
 291
 292static void save_spa(struct fru_rec *rec, unsigned int entry,
 293		     u64 addr, u64 id, unsigned int cpu)
 294{
 295	unsigned int i, fru_idx, spa_entry;
 296	struct atl_err a_err;
 297	unsigned long spa;
 298
 299	if (entry >= max_nr_entries) {
 300		pr_warn_once("FRU descriptor entry %d out-of-bounds (max: %d)\n",
 301			     entry, max_nr_entries);
 302		return;
 303	}
 304
 305	/* spa_nr_entries is always multiple of max_nr_entries */
 306	for (i = 0; i < spa_nr_entries; i += max_nr_entries) {
 307		fru_idx = i / max_nr_entries;
 308		if (fru_records[fru_idx] == rec)
 309			break;
 310	}
 311
 312	if (i >= spa_nr_entries) {
 313		pr_warn_once("FRU record %d not found\n", i);
 314		return;
 315	}
 316
 317	spa_entry = i + entry;
 318	if (spa_entry >= spa_nr_entries) {
 319		pr_warn_once("spa_entries[] index out-of-bounds\n");
 320		return;
 321	}
 322
 323	memset(&a_err, 0, sizeof(struct atl_err));
 324
 325	a_err.addr = addr;
 326	a_err.ipid = id;
 327	a_err.cpu  = cpu;
 328
 329	spa = amd_convert_umc_mca_addr_to_sys_addr(&a_err);
 330	if (IS_ERR_VALUE(spa)) {
 331		pr_debug("Failed to get system address\n");
 332		return;
 333	}
 334
 335	spa_entries[spa_entry] = spa;
 336	pr_debug("fru_idx: %u, entry: %u, spa_entry: %u, spa: 0x%016llx\n",
 337		 fru_idx, entry, spa_entry, spa_entries[spa_entry]);
 338}
 339
 340static void update_fru_record(struct fru_rec *rec, struct mce *m)
 341{
 342	struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
 343	struct cper_fru_poison_desc fpd, *fpd_dest;
 344	u32 entry = 0;
 345
 346	mutex_lock(&fmpm_update_mutex);
 347
 348	memset(&fpd, 0, sizeof(struct cper_fru_poison_desc));
 349
 350	fpd.timestamp	= m->time;
 351	fpd.hw_id_type = FPD_HW_ID_TYPE_MCA_IPID;
 352	fpd.hw_id	= m->ipid;
 353	fpd.addr_type	= FPD_ADDR_TYPE_MCA_ADDR;
 354	fpd.addr	= m->addr;
 355
 356	/* This is the first entry, so just save it. */
 357	if (!rec_has_valid_entries(rec))
 358		goto save_fpd;
 359
 360	/* Ignore already recorded errors. */
 361	if (rec_has_fpd(rec, &fpd))
 362		goto out_unlock;
 363
 364	if (rec->fmp.nr_entries >= max_nr_entries) {
 365		pr_warn("Exceeded number of entries for FRU 0x%016llx\n", rec->fmp.fru_id);
 366		goto out_unlock;
 367	}
 368
 369	entry  = fmp->nr_entries;
 370
 371save_fpd:
 372	save_spa(rec, entry, m->addr, m->ipid, m->extcpu);
 373	fpd_dest  = &rec->entries[entry];
 374	memcpy(fpd_dest, &fpd, sizeof(struct cper_fru_poison_desc));
 375
 376	fmp->nr_entries		 = entry + 1;
 377	fmp->validation_bits	|= FMP_VALID_LIST_ENTRIES;
 378	fmp->validation_bits	|= FMP_VALID_LIST;
 379
 380	pr_debug("Updated FRU 0x%016llx entry #%u\n", fmp->fru_id, entry);
 381
 382	update_record_on_storage(rec);
 383
 384out_unlock:
 385	mutex_unlock(&fmpm_update_mutex);
 386}
 387
 388static void retire_dram_row(u64 addr, u64 id, u32 cpu)
 389{
 390	struct atl_err a_err;
 391
 392	memset(&a_err, 0, sizeof(struct atl_err));
 393
 394	a_err.addr = addr;
 395	a_err.ipid = id;
 396	a_err.cpu  = cpu;
 397
 398	amd_retire_dram_row(&a_err);
 399}
 400
 401static int fru_handle_mem_poison(struct notifier_block *nb, unsigned long val, void *data)
 402{
 403	struct mce *m = (struct mce *)data;
 404	struct fru_rec *rec;
 405
 406	if (!mce_is_memory_error(m))
 407		return NOTIFY_DONE;
 408
 409	retire_dram_row(m->addr, m->ipid, m->extcpu);
 410
 411	/*
 412	 * An invalid FRU ID should not happen on real errors. But it
 413	 * could happen from software error injection, etc.
 414	 */
 415	rec = get_fru_record(m->ppin);
 416	if (!rec)
 417		return NOTIFY_DONE;
 418
 419	update_fru_record(rec, m);
 420
 421	return NOTIFY_OK;
 422}
 423
 424static struct notifier_block fru_mem_poison_nb = {
 425	.notifier_call  = fru_handle_mem_poison,
 426	.priority	= MCE_PRIO_LOWEST,
 427};
 428
 429static void retire_mem_fmp(struct fru_rec *rec)
 430{
 431	struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
 432	unsigned int i, cpu;
 433
 434	for (i = 0; i < fmp->nr_entries; i++) {
 435		struct cper_fru_poison_desc *fpd = &rec->entries[i];
 436		unsigned int err_cpu = INVALID_CPU;
 437
 438		if (fpd->hw_id_type != FPD_HW_ID_TYPE_MCA_IPID)
 439			continue;
 440
 441		if (fpd->addr_type != FPD_ADDR_TYPE_MCA_ADDR)
 442			continue;
 443
 444		cpus_read_lock();
 445		for_each_online_cpu(cpu) {
 446			if (topology_ppin(cpu) == fmp->fru_id) {
 447				err_cpu = cpu;
 448				break;
 449			}
 450		}
 451		cpus_read_unlock();
 452
 453		if (err_cpu == INVALID_CPU)
 454			continue;
 455
 456		retire_dram_row(fpd->addr, fpd->hw_id, err_cpu);
 457		save_spa(rec, i, fpd->addr, fpd->hw_id, err_cpu);
 458	}
 459}
 460
 461static void retire_mem_records(void)
 462{
 463	struct fru_rec *rec;
 464	unsigned int i;
 465
 466	for_each_fru(i, rec) {
 467		if (!rec_has_valid_entries(rec))
 468			continue;
 469
 470		retire_mem_fmp(rec);
 471	}
 472}
 473
 474/* Set the CPER Record Header and CPER Section Descriptor fields. */
 475static void set_rec_fields(struct fru_rec *rec)
 476{
 477	struct cper_section_descriptor	*sec_desc = &rec->sec_desc;
 478	struct cper_record_header	*hdr	  = &rec->hdr;
 479
 480	/*
 481	 * This is a saved record created with fewer max_nr_entries.
 482	 * Update the record lengths and keep everything else as-is.
 483	 */
 484	if (hdr->record_length && hdr->record_length < max_rec_len) {
 485		pr_debug("Growing record 0x%016llx from %u to %zu bytes\n",
 486			 hdr->record_id, hdr->record_length, max_rec_len);
 487		goto update_lengths;
 488	}
 489
 490	memcpy(hdr->signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
 491	hdr->revision			= CPER_RECORD_REV;
 492	hdr->signature_end		= CPER_SIG_END;
 493
 494	/*
 495	 * Currently, it is assumed that there is one FRU Memory Poison
 496	 * section per CPER. But this may change for other implementations.
 497	 */
 498	hdr->section_count		= 1;
 499
 500	/* The logged errors are recoverable. Otherwise, they'd never make it here. */
 501	hdr->error_severity		= CPER_SEV_RECOVERABLE;
 502
 503	hdr->validation_bits		= 0;
 504	hdr->creator_id			= CPER_CREATOR_FMP;
 505	hdr->notification_type		= CPER_NOTIFY_MCE;
 506	hdr->record_id			= cper_next_record_id();
 507	hdr->flags			= CPER_HW_ERROR_FLAGS_PREVERR;
 508
 509	sec_desc->section_offset	= sizeof(struct cper_record_header);
 510	sec_desc->revision		= CPER_SEC_REV;
 511	sec_desc->validation_bits	= 0;
 512	sec_desc->flags			= CPER_SEC_PRIMARY;
 513	sec_desc->section_type		= CPER_SECTION_TYPE_FMP;
 514	sec_desc->section_severity	= CPER_SEV_RECOVERABLE;
 515
 516update_lengths:
 517	hdr->record_length		= max_rec_len;
 518	sec_desc->section_length	= max_rec_len - sizeof(struct cper_record_header);
 519}
 520
 521static int save_new_records(void)
 522{
 523	DECLARE_BITMAP(new_records, FMPM_MAX_NR_FRU);
 524	struct fru_rec *rec;
 525	unsigned int i;
 526	int ret = 0;
 527
 528	for_each_fru(i, rec) {
 529		/* No need to update saved records that match the current record size. */
 530		if (rec->hdr.record_length == max_rec_len)
 531			continue;
 532
 533		if (!rec->hdr.record_length)
 534			set_bit(i, new_records);
 535
 536		set_rec_fields(rec);
 537
 538		ret = update_record_on_storage(rec);
 539		if (ret)
 540			goto out_clear;
 541	}
 542
 543	return ret;
 544
 545out_clear:
 546	for_each_fru(i, rec) {
 547		if (!test_bit(i, new_records))
 548			continue;
 549
 550		erst_clear(rec->hdr.record_id);
 551	}
 552
 553	return ret;
 554}
 555
 556/* Check that the record matches expected types for the current system.*/
 557static bool fmp_is_usable(struct fru_rec *rec)
 558{
 559	struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
 560	u64 cpuid;
 561
 562	pr_debug("Validation bits: 0x%016llx\n", fmp->validation_bits);
 563
 564	if (!(fmp->validation_bits & FMP_VALID_ARCH_TYPE)) {
 565		pr_debug("Arch type unknown\n");
 566		return false;
 567	}
 568
 569	if (fmp->fru_arch_type != FMP_ARCH_TYPE_X86_CPUID_1_EAX) {
 570		pr_debug("Arch type not 'x86 Family/Model/Stepping'\n");
 571		return false;
 572	}
 573
 574	if (!(fmp->validation_bits & FMP_VALID_ARCH)) {
 575		pr_debug("Arch value unknown\n");
 576		return false;
 577	}
 578
 579	cpuid = cpuid_eax(1);
 580	if (fmp->fru_arch != cpuid) {
 581		pr_debug("Arch value mismatch: record = 0x%016llx, system = 0x%016llx\n",
 582			 fmp->fru_arch, cpuid);
 583		return false;
 584	}
 585
 586	if (!(fmp->validation_bits & FMP_VALID_ID_TYPE)) {
 587		pr_debug("FRU ID type unknown\n");
 588		return false;
 589	}
 590
 591	if (fmp->fru_id_type != FMP_ID_TYPE_X86_PPIN) {
 592		pr_debug("FRU ID type is not 'x86 PPIN'\n");
 593		return false;
 594	}
 595
 596	if (!(fmp->validation_bits & FMP_VALID_ID)) {
 597		pr_debug("FRU ID value unknown\n");
 598		return false;
 599	}
 600
 601	return true;
 602}
 603
 604static bool fmp_is_valid(struct fru_rec *rec)
 605{
 606	struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
 607	u32 checksum, len;
 608
 609	len = get_fmp_len(rec);
 610	if (len < sizeof(struct cper_sec_fru_mem_poison)) {
 611		pr_debug("fmp length is too small\n");
 612		return false;
 613	}
 614
 615	/* Checksum must sum to zero for the entire section. */
 616	checksum = do_fmp_checksum(fmp, len) + fmp->checksum;
 617	if (checksum) {
 618		pr_debug("fmp checksum failed: sum = 0x%x\n", checksum);
 619		print_hex_dump_debug("fmp record: ", DUMP_PREFIX_NONE, 16, 1, fmp, len, false);
 620		return false;
 621	}
 622
 623	if (!fmp_is_usable(rec))
 624		return false;
 625
 626	return true;
 627}
 628
 629static struct fru_rec *get_valid_record(struct fru_rec *old)
 630{
 631	struct fru_rec *new;
 632
 633	if (!fmp_is_valid(old)) {
 634		pr_debug("Ignoring invalid record\n");
 635		return NULL;
 636	}
 637
 638	new = get_fru_record(old->fmp.fru_id);
 639	if (!new)
 640		pr_debug("Ignoring record for absent FRU\n");
 641
 642	return new;
 643}
 644
 645/*
 646 * Fetch saved records from persistent storage.
 647 *
 648 * For each found record:
 649 * - If it was not created by this module, then ignore it.
 650 * - If it is valid, then copy its data to the local cache.
 651 * - If it is not valid, then erase it.
 652 */
 653static int get_saved_records(void)
 654{
 655	struct fru_rec *old, *new;
 656	u64 record_id;
 657	int ret, pos;
 658	ssize_t len;
 659
 660	old = kmalloc(FMPM_MAX_REC_LEN, GFP_KERNEL);
 661	if (!old) {
 662		ret = -ENOMEM;
 663		goto out;
 664	}
 665
 666	ret = erst_get_record_id_begin(&pos);
 667	if (ret < 0)
 668		goto out_end;
 669
 670	while (!erst_get_record_id_next(&pos, &record_id)) {
 671		if (record_id == APEI_ERST_INVALID_RECORD_ID)
 672			goto out_end;
 673		/*
 674		 * Make sure to clear temporary buffer between reads to avoid
 675		 * leftover data from records of various sizes.
 676		 */
 677		memset(old, 0, FMPM_MAX_REC_LEN);
 678
 679		len = erst_read_record(record_id, &old->hdr, FMPM_MAX_REC_LEN,
 680				       sizeof(struct fru_rec), &CPER_CREATOR_FMP);
 681		if (len < 0)
 682			continue;
 683
 684		new = get_valid_record(old);
 685		if (!new) {
 686			erst_clear(record_id);
 687			continue;
 688		}
 689
 690		if (len > max_rec_len) {
 691			unsigned int saved_nr_entries;
 692
 693			saved_nr_entries  = len - sizeof(struct fru_rec);
 694			saved_nr_entries /= sizeof(struct cper_fru_poison_desc);
 695
 696			pr_warn("Saved record found with %u entries.\n", saved_nr_entries);
 697			pr_warn("Please increase max_nr_entries to %u.\n", saved_nr_entries);
 698
 699			ret = -EINVAL;
 700			goto out_end;
 701		}
 702
 703		/* Restore the record */
 704		memcpy(new, old, len);
 705	}
 706
 707out_end:
 708	erst_get_record_id_end();
 709	kfree(old);
 710out:
 711	return ret;
 712}
 713
 714static void set_fmp_fields(struct fru_rec *rec, unsigned int cpu)
 715{
 716	struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
 717
 718	fmp->fru_arch_type    = FMP_ARCH_TYPE_X86_CPUID_1_EAX;
 719	fmp->validation_bits |= FMP_VALID_ARCH_TYPE;
 720
 721	/* Assume all CPUs in the system have the same value for now. */
 722	fmp->fru_arch	      = cpuid_eax(1);
 723	fmp->validation_bits |= FMP_VALID_ARCH;
 724
 725	fmp->fru_id_type      = FMP_ID_TYPE_X86_PPIN;
 726	fmp->validation_bits |= FMP_VALID_ID_TYPE;
 727
 728	fmp->fru_id	      = topology_ppin(cpu);
 729	fmp->validation_bits |= FMP_VALID_ID;
 730}
 731
 732static int init_fmps(void)
 733{
 734	struct fru_rec *rec;
 735	unsigned int i, cpu;
 736	int ret = 0;
 737
 738	for_each_fru(i, rec) {
 739		unsigned int fru_cpu = INVALID_CPU;
 740
 741		cpus_read_lock();
 742		for_each_online_cpu(cpu) {
 743			if (topology_physical_package_id(cpu) == i) {
 744				fru_cpu = cpu;
 745				break;
 746			}
 747		}
 748		cpus_read_unlock();
 749
 750		if (fru_cpu == INVALID_CPU) {
 751			pr_debug("Failed to find matching CPU for FRU #%u\n", i);
 752			ret = -ENODEV;
 753			break;
 754		}
 755
 756		set_fmp_fields(rec, fru_cpu);
 757	}
 758
 759	return ret;
 760}
 761
 762static int get_system_info(void)
 763{
 764	/* Only load on MI300A systems for now. */
 765	if (!(boot_cpu_data.x86_model >= 0x90 &&
 766	      boot_cpu_data.x86_model <= 0x9f))
 767		return -ENODEV;
 768
 769	if (!cpu_feature_enabled(X86_FEATURE_AMD_PPIN)) {
 770		pr_debug("PPIN feature not available\n");
 771		return -ENODEV;
 772	}
 773
 774	/* Use CPU socket as FRU for MI300 systems. */
 775	max_nr_fru = topology_max_packages();
 776	if (!max_nr_fru)
 777		return -ENODEV;
 778
 779	if (max_nr_fru > FMPM_MAX_NR_FRU) {
 780		pr_warn("Too many FRUs to manage: found: %u, max: %u\n",
 781			max_nr_fru, FMPM_MAX_NR_FRU);
 782		return -ENODEV;
 783	}
 784
 785	if (!max_nr_entries)
 786		max_nr_entries = FMPM_DEFAULT_MAX_NR_ENTRIES;
 787
 788	spa_nr_entries = max_nr_fru * max_nr_entries;
 789
 790	max_rec_len  = sizeof(struct fru_rec);
 791	max_rec_len += sizeof(struct cper_fru_poison_desc) * max_nr_entries;
 792
 793	pr_info("max FRUs: %u, max entries: %u, max record length: %lu\n",
 794		 max_nr_fru, max_nr_entries, max_rec_len);
 795
 796	return 0;
 797}
 798
 799static void free_records(void)
 800{
 801	struct fru_rec *rec;
 802	int i;
 803
 804	for_each_fru(i, rec)
 805		kfree(rec);
 806
 807	kfree(fru_records);
 808	kfree(spa_entries);
 809}
 810
 811static int allocate_records(void)
 812{
 813	int i, ret = 0;
 814
 815	fru_records = kcalloc(max_nr_fru, sizeof(struct fru_rec *), GFP_KERNEL);
 816	if (!fru_records) {
 817		ret = -ENOMEM;
 818		goto out;
 819	}
 820
 821	for (i = 0; i < max_nr_fru; i++) {
 822		fru_records[i] = kzalloc(max_rec_len, GFP_KERNEL);
 823		if (!fru_records[i]) {
 824			ret = -ENOMEM;
 825			goto out_free;
 826		}
 827	}
 828
 829	spa_entries = kcalloc(spa_nr_entries, sizeof(u64), GFP_KERNEL);
 830	if (!spa_entries) {
 831		ret = -ENOMEM;
 832		goto out_free;
 833	}
 834
 835	for (i = 0; i < spa_nr_entries; i++)
 836		spa_entries[i] = INVALID_SPA;
 837
 838	return ret;
 839
 840out_free:
 841	while (--i >= 0)
 842		kfree(fru_records[i]);
 843
 844	kfree(fru_records);
 845out:
 846	return ret;
 847}
 848
 849static void *fmpm_start(struct seq_file *f, loff_t *pos)
 850{
 851	if (*pos >= (spa_nr_entries + 1))
 852		return NULL;
 853	return pos;
 854}
 855
 856static void *fmpm_next(struct seq_file *f, void *data, loff_t *pos)
 857{
 858	if (++(*pos) >= (spa_nr_entries + 1))
 859		return NULL;
 860	return pos;
 861}
 862
 863static void fmpm_stop(struct seq_file *f, void *data)
 864{
 865}
 866
 867#define SHORT_WIDTH	8
 868#define U64_WIDTH	18
 869#define TIMESTAMP_WIDTH	19
 870#define LONG_WIDTH	24
 871#define U64_PAD		(LONG_WIDTH - U64_WIDTH)
 872#define TS_PAD		(LONG_WIDTH - TIMESTAMP_WIDTH)
 873static int fmpm_show(struct seq_file *f, void *data)
 874{
 875	unsigned int fru_idx, entry, spa_entry, line;
 876	struct cper_fru_poison_desc *fpd;
 877	struct fru_rec *rec;
 878
 879	line = *(loff_t *)data;
 880	if (line == 0) {
 881		seq_printf(f, "%-*s", SHORT_WIDTH, "fru_idx");
 882		seq_printf(f, "%-*s", LONG_WIDTH,  "fru_id");
 883		seq_printf(f, "%-*s", SHORT_WIDTH, "entry");
 884		seq_printf(f, "%-*s", LONG_WIDTH,  "timestamp");
 885		seq_printf(f, "%-*s", LONG_WIDTH,  "hw_id");
 886		seq_printf(f, "%-*s", LONG_WIDTH,  "addr");
 887		seq_printf(f, "%-*s", LONG_WIDTH,  "spa");
 888		goto out_newline;
 889	}
 890
 891	spa_entry = line - 1;
 892	fru_idx	  = spa_entry / max_nr_entries;
 893	entry	  = spa_entry % max_nr_entries;
 894
 895	rec = fru_records[fru_idx];
 896	if (!rec)
 897		goto out;
 898
 899	seq_printf(f, "%-*u",		SHORT_WIDTH, fru_idx);
 900	seq_printf(f, "0x%016llx%-*s",	rec->fmp.fru_id, U64_PAD, "");
 901	seq_printf(f, "%-*u",		SHORT_WIDTH, entry);
 902
 903	mutex_lock(&fmpm_update_mutex);
 904
 905	if (entry >= rec->fmp.nr_entries) {
 906		seq_printf(f, "%-*s", LONG_WIDTH, "*");
 907		seq_printf(f, "%-*s", LONG_WIDTH, "*");
 908		seq_printf(f, "%-*s", LONG_WIDTH, "*");
 909		seq_printf(f, "%-*s", LONG_WIDTH, "*");
 910		goto out_unlock;
 911	}
 912
 913	fpd = &rec->entries[entry];
 914
 915	seq_printf(f, "%ptT%-*s",	&fpd->timestamp, TS_PAD,  "");
 916	seq_printf(f, "0x%016llx%-*s",	fpd->hw_id,	 U64_PAD, "");
 917	seq_printf(f, "0x%016llx%-*s",	fpd->addr,	 U64_PAD, "");
 918
 919	if (spa_entries[spa_entry] == INVALID_SPA)
 920		seq_printf(f, "%-*s", LONG_WIDTH, "*");
 921	else
 922		seq_printf(f, "0x%016llx%-*s", spa_entries[spa_entry], U64_PAD, "");
 923
 924out_unlock:
 925	mutex_unlock(&fmpm_update_mutex);
 926out_newline:
 927	seq_putc(f, '\n');
 928out:
 929	return 0;
 930}
 931
 932static const struct seq_operations fmpm_seq_ops = {
 933	.start	= fmpm_start,
 934	.next	= fmpm_next,
 935	.stop	= fmpm_stop,
 936	.show	= fmpm_show,
 937};
 938
 939static int fmpm_open(struct inode *inode, struct file *file)
 940{
 941	return seq_open(file, &fmpm_seq_ops);
 942}
 943
 944static const struct file_operations fmpm_fops = {
 945	.open		= fmpm_open,
 946	.release	= seq_release,
 947	.read		= seq_read,
 948	.llseek		= seq_lseek,
 949};
 950
 951static void setup_debugfs(void)
 952{
 953	struct dentry *dfs = ras_get_debugfs_root();
 954
 955	if (!dfs)
 956		return;
 957
 958	fmpm_dfs_dir = debugfs_create_dir("fmpm", dfs);
 959	if (!fmpm_dfs_dir)
 960		return;
 961
 962	fmpm_dfs_entries = debugfs_create_file("entries", 0400, fmpm_dfs_dir, NULL, &fmpm_fops);
 963	if (!fmpm_dfs_entries)
 964		debugfs_remove(fmpm_dfs_dir);
 965}
 966
 967static const struct x86_cpu_id fmpm_cpuids[] = {
 968	X86_MATCH_VENDOR_FAM(AMD, 0x19, NULL),
 969	{ }
 970};
 971MODULE_DEVICE_TABLE(x86cpu, fmpm_cpuids);
 972
 973static int __init fru_mem_poison_init(void)
 974{
 975	int ret;
 976
 977	if (!x86_match_cpu(fmpm_cpuids)) {
 978		ret = -ENODEV;
 979		goto out;
 980	}
 981
 982	if (erst_disable) {
 983		pr_debug("ERST not available\n");
 984		ret = -ENODEV;
 985		goto out;
 986	}
 987
 988	ret = get_system_info();
 989	if (ret)
 990		goto out;
 991
 992	ret = allocate_records();
 993	if (ret)
 994		goto out;
 995
 996	ret = init_fmps();
 997	if (ret)
 998		goto out_free;
 999
1000	ret = get_saved_records();
1001	if (ret)
1002		goto out_free;
1003
1004	ret = save_new_records();
1005	if (ret)
1006		goto out_free;
1007
1008	setup_debugfs();
1009
1010	retire_mem_records();
1011
1012	mce_register_decode_chain(&fru_mem_poison_nb);
1013
1014	pr_info("FRU Memory Poison Manager initialized\n");
1015	return 0;
1016
1017out_free:
1018	free_records();
1019out:
1020	return ret;
1021}
1022
1023static void __exit fru_mem_poison_exit(void)
1024{
1025	mce_unregister_decode_chain(&fru_mem_poison_nb);
1026	debugfs_remove(fmpm_dfs_dir);
1027	free_records();
1028}
1029
1030module_init(fru_mem_poison_init);
1031module_exit(fru_mem_poison_exit);
1032
1033MODULE_LICENSE("GPL");
1034MODULE_DESCRIPTION("FRU Memory Poison Manager");
v6.9.4
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * FRU (Field-Replaceable Unit) Memory Poison Manager
   4 *
   5 * Copyright (c) 2024, Advanced Micro Devices, Inc.
   6 * All Rights Reserved.
   7 *
   8 * Authors:
   9 *	Naveen Krishna Chatradhi <naveenkrishna.chatradhi@amd.com>
  10 *	Muralidhara M K <muralidhara.mk@amd.com>
  11 *	Yazen Ghannam <Yazen.Ghannam@amd.com>
  12 *
  13 * Implementation notes, assumptions, and limitations:
  14 *
  15 * - FRU memory poison section and memory poison descriptor definitions are not yet
  16 *   included in the UEFI specification. So they are defined here. Afterwards, they
  17 *   may be moved to linux/cper.h, if appropriate.
  18 *
  19 * - Platforms based on AMD MI300 systems will be the first to use these structures.
  20 *   There are a number of assumptions made here that will need to be generalized
  21 *   to support other platforms.
  22 *
  23 *   AMD MI300-based platform(s) assumptions:
  24 *   - Memory errors are reported through x86 MCA.
  25 *   - The entire DRAM row containing a memory error should be retired.
  26 *   - There will be (1) FRU memory poison section per CPER.
  27 *   - The FRU will be the CPU package (processor socket).
  28 *   - The default number of memory poison descriptor entries should be (8).
  29 *   - The platform will use ACPI ERST for persistent storage.
  30 *   - All FRU records should be saved to persistent storage. Module init will
  31 *     fail if any FRU record is not successfully written.
  32 *
  33 * - Boot time memory retirement may occur later than ideal due to dependencies
  34 *   on other libraries and drivers. This leaves a gap where bad memory may be
  35 *   accessed during early boot stages.
  36 *
  37 * - Enough memory should be pre-allocated for each FRU record to be able to hold
  38 *   the expected number of descriptor entries. This, mostly empty, record is
  39 *   written to storage during init time. Subsequent writes to the same record
  40 *   should allow the Platform to update the stored record in-place. Otherwise,
  41 *   if the record is extended, then the Platform may need to perform costly memory
  42 *   management operations on the storage. For example, the Platform may spend time
  43 *   in Firmware copying and invalidating memory on a relatively slow SPI ROM.
  44 */
  45
  46#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  47
  48#include <linux/cper.h>
  49#include <linux/ras.h>
  50#include <linux/cpu.h>
  51
  52#include <acpi/apei.h>
  53
  54#include <asm/cpu_device_id.h>
  55#include <asm/mce.h>
  56
  57#include "../debugfs.h"
  58
 
 
  59#define INVALID_CPU			UINT_MAX
  60
  61/* Validation Bits */
  62#define FMP_VALID_ARCH_TYPE		BIT_ULL(0)
  63#define FMP_VALID_ARCH			BIT_ULL(1)
  64#define FMP_VALID_ID_TYPE		BIT_ULL(2)
  65#define FMP_VALID_ID			BIT_ULL(3)
  66#define FMP_VALID_LIST_ENTRIES		BIT_ULL(4)
  67#define FMP_VALID_LIST			BIT_ULL(5)
  68
  69/* FRU Architecture Types */
  70#define FMP_ARCH_TYPE_X86_CPUID_1_EAX	0
  71
  72/* FRU ID Types */
  73#define FMP_ID_TYPE_X86_PPIN		0
  74
  75/* FRU Memory Poison Section */
  76struct cper_sec_fru_mem_poison {
  77	u32 checksum;
  78	u64 validation_bits;
  79	u32 fru_arch_type;
  80	u64 fru_arch;
  81	u32 fru_id_type;
  82	u64 fru_id;
  83	u32 nr_entries;
  84} __packed;
  85
  86/* FRU Descriptor ID Types */
  87#define FPD_HW_ID_TYPE_MCA_IPID		0
  88
  89/* FRU Descriptor Address Types */
  90#define FPD_ADDR_TYPE_MCA_ADDR		0
  91
  92/* Memory Poison Descriptor */
  93struct cper_fru_poison_desc {
  94	u64 timestamp;
  95	u32 hw_id_type;
  96	u64 hw_id;
  97	u32 addr_type;
  98	u64 addr;
  99} __packed;
 100
 101/* Collection of headers and sections for easy pointer use. */
 102struct fru_rec {
 103	struct cper_record_header	hdr;
 104	struct cper_section_descriptor	sec_desc;
 105	struct cper_sec_fru_mem_poison	fmp;
 106	struct cper_fru_poison_desc	entries[];
 107} __packed;
 108
 109/*
 110 * Pointers to the complete CPER record of each FRU.
 111 *
 112 * Memory allocation will include padded space for descriptor entries.
 113 */
 114static struct fru_rec **fru_records;
 115
 116/* system physical addresses array */
 117static u64 *spa_entries;
 118
 119#define INVALID_SPA	~0ULL
 120
 121static struct dentry *fmpm_dfs_dir;
 122static struct dentry *fmpm_dfs_entries;
 123
 124#define CPER_CREATOR_FMP						\
 125	GUID_INIT(0xcd5c2993, 0xf4b2, 0x41b2, 0xb5, 0xd4, 0xf9, 0xc3,	\
 126		  0xa0, 0x33, 0x08, 0x75)
 127
 128#define CPER_SECTION_TYPE_FMP						\
 129	GUID_INIT(0x5e4706c1, 0x5356, 0x48c6, 0x93, 0x0b, 0x52, 0xf2,	\
 130		  0x12, 0x0a, 0x44, 0x58)
 131
 132/**
 133 * DOC: max_nr_entries (byte)
 134 * Maximum number of descriptor entries possible for each FRU.
 135 *
 136 * Values between '1' and '255' are valid.
 137 * No input or '0' will default to FMPM_DEFAULT_MAX_NR_ENTRIES.
 138 */
 139static u8 max_nr_entries;
 140module_param(max_nr_entries, byte, 0644);
 141MODULE_PARM_DESC(max_nr_entries,
 142		 "Maximum number of memory poison descriptor entries per FRU");
 143
 144#define FMPM_DEFAULT_MAX_NR_ENTRIES	8
 145
 146/* Maximum number of FRUs in the system. */
 147#define FMPM_MAX_NR_FRU			256
 148static unsigned int max_nr_fru;
 149
 150/* Total length of record including headers and list of descriptor entries. */
 151static size_t max_rec_len;
 152
 153#define FMPM_MAX_REC_LEN (sizeof(struct fru_rec) + (sizeof(struct cper_fru_poison_desc) * 255))
 154
 155/* Total number of SPA entries across all FRUs. */
 156static unsigned int spa_nr_entries;
 157
 158/*
 159 * Protect the local records cache in fru_records and prevent concurrent
 160 * writes to storage. This is only needed after init once notifier block
 161 * registration is done.
 162 *
 163 * The majority of a record is fixed at module init and will not change
 164 * during run time. The entries within a record will be updated as new
 165 * errors are reported. The mutex should be held whenever the entries are
 166 * accessed during run time.
 167 */
 168static DEFINE_MUTEX(fmpm_update_mutex);
 169
 170#define for_each_fru(i, rec) \
 171	for (i = 0; rec = fru_records[i], i < max_nr_fru; i++)
 172
 173static inline u32 get_fmp_len(struct fru_rec *rec)
 174{
 175	return rec->sec_desc.section_length - sizeof(struct cper_section_descriptor);
 176}
 177
 178static struct fru_rec *get_fru_record(u64 fru_id)
 179{
 180	struct fru_rec *rec;
 181	unsigned int i;
 182
 183	for_each_fru(i, rec) {
 184		if (rec->fmp.fru_id == fru_id)
 185			return rec;
 186	}
 187
 188	pr_debug("Record not found for FRU 0x%016llx\n", fru_id);
 189
 190	return NULL;
 191}
 192
 193/*
 194 * Sum up all bytes within the FRU Memory Poison Section including the Memory
 195 * Poison Descriptor entries.
 196 *
 197 * Don't include the old checksum here. It's a u32 value, so summing each of its
 198 * bytes will give the wrong total.
 199 */
 200static u32 do_fmp_checksum(struct cper_sec_fru_mem_poison *fmp, u32 len)
 201{
 202	u32 checksum = 0;
 203	u8 *buf, *end;
 204
 205	/* Skip old checksum. */
 206	buf = (u8 *)fmp + sizeof(u32);
 207	end = buf + len;
 208
 209	while (buf < end)
 210		checksum += (u8)(*(buf++));
 211
 212	return checksum;
 213}
 214
 215static int update_record_on_storage(struct fru_rec *rec)
 216{
 217	u32 len, checksum;
 218	int ret;
 219
 220	/* Calculate a new checksum. */
 221	len = get_fmp_len(rec);
 222
 223	/* Get the current total. */
 224	checksum = do_fmp_checksum(&rec->fmp, len);
 225
 226	/* Use the complement value. */
 227	rec->fmp.checksum = -checksum;
 228
 229	pr_debug("Writing to storage\n");
 230
 231	ret = erst_write(&rec->hdr);
 232	if (ret) {
 233		pr_warn("Storage update failed for FRU 0x%016llx\n", rec->fmp.fru_id);
 234
 235		if (ret == -ENOSPC)
 236			pr_warn("Not enough space on storage\n");
 237	}
 238
 239	return ret;
 240}
 241
 242static bool rec_has_valid_entries(struct fru_rec *rec)
 243{
 244	if (!(rec->fmp.validation_bits & FMP_VALID_LIST_ENTRIES))
 245		return false;
 246
 247	if (!(rec->fmp.validation_bits & FMP_VALID_LIST))
 248		return false;
 249
 250	return true;
 251}
 252
 253static bool fpds_equal(struct cper_fru_poison_desc *old, struct cper_fru_poison_desc *new)
 254{
 255	/*
 256	 * Ignore timestamp field.
 257	 * The same physical error may be reported multiple times due to stuck bits, etc.
 258	 *
 259	 * Also, order the checks from most->least likely to fail to shortcut the code.
 260	 */
 261	if (old->addr != new->addr)
 262		return false;
 263
 264	if (old->hw_id != new->hw_id)
 265		return false;
 266
 267	if (old->addr_type != new->addr_type)
 268		return false;
 269
 270	if (old->hw_id_type != new->hw_id_type)
 271		return false;
 272
 273	return true;
 274}
 275
 276static bool rec_has_fpd(struct fru_rec *rec, struct cper_fru_poison_desc *fpd)
 277{
 278	unsigned int i;
 279
 280	for (i = 0; i < rec->fmp.nr_entries; i++) {
 281		struct cper_fru_poison_desc *fpd_i = &rec->entries[i];
 282
 283		if (fpds_equal(fpd_i, fpd)) {
 284			pr_debug("Found duplicate record\n");
 285			return true;
 286		}
 287	}
 288
 289	return false;
 290}
 291
 292static void save_spa(struct fru_rec *rec, unsigned int entry,
 293		     u64 addr, u64 id, unsigned int cpu)
 294{
 295	unsigned int i, fru_idx, spa_entry;
 296	struct atl_err a_err;
 297	unsigned long spa;
 298
 299	if (entry >= max_nr_entries) {
 300		pr_warn_once("FRU descriptor entry %d out-of-bounds (max: %d)\n",
 301			     entry, max_nr_entries);
 302		return;
 303	}
 304
 305	/* spa_nr_entries is always multiple of max_nr_entries */
 306	for (i = 0; i < spa_nr_entries; i += max_nr_entries) {
 307		fru_idx = i / max_nr_entries;
 308		if (fru_records[fru_idx] == rec)
 309			break;
 310	}
 311
 312	if (i >= spa_nr_entries) {
 313		pr_warn_once("FRU record %d not found\n", i);
 314		return;
 315	}
 316
 317	spa_entry = i + entry;
 318	if (spa_entry >= spa_nr_entries) {
 319		pr_warn_once("spa_entries[] index out-of-bounds\n");
 320		return;
 321	}
 322
 323	memset(&a_err, 0, sizeof(struct atl_err));
 324
 325	a_err.addr = addr;
 326	a_err.ipid = id;
 327	a_err.cpu  = cpu;
 328
 329	spa = amd_convert_umc_mca_addr_to_sys_addr(&a_err);
 330	if (IS_ERR_VALUE(spa)) {
 331		pr_debug("Failed to get system address\n");
 332		return;
 333	}
 334
 335	spa_entries[spa_entry] = spa;
 336	pr_debug("fru_idx: %u, entry: %u, spa_entry: %u, spa: 0x%016llx\n",
 337		 fru_idx, entry, spa_entry, spa_entries[spa_entry]);
 338}
 339
 340static void update_fru_record(struct fru_rec *rec, struct mce *m)
 341{
 342	struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
 343	struct cper_fru_poison_desc fpd, *fpd_dest;
 344	u32 entry = 0;
 345
 346	mutex_lock(&fmpm_update_mutex);
 347
 348	memset(&fpd, 0, sizeof(struct cper_fru_poison_desc));
 349
 350	fpd.timestamp	= m->time;
 351	fpd.hw_id_type = FPD_HW_ID_TYPE_MCA_IPID;
 352	fpd.hw_id	= m->ipid;
 353	fpd.addr_type	= FPD_ADDR_TYPE_MCA_ADDR;
 354	fpd.addr	= m->addr;
 355
 356	/* This is the first entry, so just save it. */
 357	if (!rec_has_valid_entries(rec))
 358		goto save_fpd;
 359
 360	/* Ignore already recorded errors. */
 361	if (rec_has_fpd(rec, &fpd))
 362		goto out_unlock;
 363
 364	if (rec->fmp.nr_entries >= max_nr_entries) {
 365		pr_warn("Exceeded number of entries for FRU 0x%016llx\n", rec->fmp.fru_id);
 366		goto out_unlock;
 367	}
 368
 369	entry  = fmp->nr_entries;
 370
 371save_fpd:
 372	save_spa(rec, entry, m->addr, m->ipid, m->extcpu);
 373	fpd_dest  = &rec->entries[entry];
 374	memcpy(fpd_dest, &fpd, sizeof(struct cper_fru_poison_desc));
 375
 376	fmp->nr_entries		 = entry + 1;
 377	fmp->validation_bits	|= FMP_VALID_LIST_ENTRIES;
 378	fmp->validation_bits	|= FMP_VALID_LIST;
 379
 380	pr_debug("Updated FRU 0x%016llx entry #%u\n", fmp->fru_id, entry);
 381
 382	update_record_on_storage(rec);
 383
 384out_unlock:
 385	mutex_unlock(&fmpm_update_mutex);
 386}
 387
 388static void retire_dram_row(u64 addr, u64 id, u32 cpu)
 389{
 390	struct atl_err a_err;
 391
 392	memset(&a_err, 0, sizeof(struct atl_err));
 393
 394	a_err.addr = addr;
 395	a_err.ipid = id;
 396	a_err.cpu  = cpu;
 397
 398	amd_retire_dram_row(&a_err);
 399}
 400
 401static int fru_handle_mem_poison(struct notifier_block *nb, unsigned long val, void *data)
 402{
 403	struct mce *m = (struct mce *)data;
 404	struct fru_rec *rec;
 405
 406	if (!mce_is_memory_error(m))
 407		return NOTIFY_DONE;
 408
 409	retire_dram_row(m->addr, m->ipid, m->extcpu);
 410
 411	/*
 412	 * An invalid FRU ID should not happen on real errors. But it
 413	 * could happen from software error injection, etc.
 414	 */
 415	rec = get_fru_record(m->ppin);
 416	if (!rec)
 417		return NOTIFY_DONE;
 418
 419	update_fru_record(rec, m);
 420
 421	return NOTIFY_OK;
 422}
 423
 424static struct notifier_block fru_mem_poison_nb = {
 425	.notifier_call  = fru_handle_mem_poison,
 426	.priority	= MCE_PRIO_LOWEST,
 427};
 428
 429static void retire_mem_fmp(struct fru_rec *rec)
 430{
 431	struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
 432	unsigned int i, cpu;
 433
 434	for (i = 0; i < fmp->nr_entries; i++) {
 435		struct cper_fru_poison_desc *fpd = &rec->entries[i];
 436		unsigned int err_cpu = INVALID_CPU;
 437
 438		if (fpd->hw_id_type != FPD_HW_ID_TYPE_MCA_IPID)
 439			continue;
 440
 441		if (fpd->addr_type != FPD_ADDR_TYPE_MCA_ADDR)
 442			continue;
 443
 444		cpus_read_lock();
 445		for_each_online_cpu(cpu) {
 446			if (topology_ppin(cpu) == fmp->fru_id) {
 447				err_cpu = cpu;
 448				break;
 449			}
 450		}
 451		cpus_read_unlock();
 452
 453		if (err_cpu == INVALID_CPU)
 454			continue;
 455
 456		retire_dram_row(fpd->addr, fpd->hw_id, err_cpu);
 457		save_spa(rec, i, fpd->addr, fpd->hw_id, err_cpu);
 458	}
 459}
 460
 461static void retire_mem_records(void)
 462{
 463	struct fru_rec *rec;
 464	unsigned int i;
 465
 466	for_each_fru(i, rec) {
 467		if (!rec_has_valid_entries(rec))
 468			continue;
 469
 470		retire_mem_fmp(rec);
 471	}
 472}
 473
 474/* Set the CPER Record Header and CPER Section Descriptor fields. */
 475static void set_rec_fields(struct fru_rec *rec)
 476{
 477	struct cper_section_descriptor	*sec_desc = &rec->sec_desc;
 478	struct cper_record_header	*hdr	  = &rec->hdr;
 479
 480	/*
 481	 * This is a saved record created with fewer max_nr_entries.
 482	 * Update the record lengths and keep everything else as-is.
 483	 */
 484	if (hdr->record_length && hdr->record_length < max_rec_len) {
 485		pr_debug("Growing record 0x%016llx from %u to %zu bytes\n",
 486			 hdr->record_id, hdr->record_length, max_rec_len);
 487		goto update_lengths;
 488	}
 489
 490	memcpy(hdr->signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
 491	hdr->revision			= CPER_RECORD_REV;
 492	hdr->signature_end		= CPER_SIG_END;
 493
 494	/*
 495	 * Currently, it is assumed that there is one FRU Memory Poison
 496	 * section per CPER. But this may change for other implementations.
 497	 */
 498	hdr->section_count		= 1;
 499
 500	/* The logged errors are recoverable. Otherwise, they'd never make it here. */
 501	hdr->error_severity		= CPER_SEV_RECOVERABLE;
 502
 503	hdr->validation_bits		= 0;
 504	hdr->creator_id			= CPER_CREATOR_FMP;
 505	hdr->notification_type		= CPER_NOTIFY_MCE;
 506	hdr->record_id			= cper_next_record_id();
 507	hdr->flags			= CPER_HW_ERROR_FLAGS_PREVERR;
 508
 509	sec_desc->section_offset	= sizeof(struct cper_record_header);
 510	sec_desc->revision		= CPER_SEC_REV;
 511	sec_desc->validation_bits	= 0;
 512	sec_desc->flags			= CPER_SEC_PRIMARY;
 513	sec_desc->section_type		= CPER_SECTION_TYPE_FMP;
 514	sec_desc->section_severity	= CPER_SEV_RECOVERABLE;
 515
 516update_lengths:
 517	hdr->record_length		= max_rec_len;
 518	sec_desc->section_length	= max_rec_len - sizeof(struct cper_record_header);
 519}
 520
 521static int save_new_records(void)
 522{
 523	DECLARE_BITMAP(new_records, FMPM_MAX_NR_FRU);
 524	struct fru_rec *rec;
 525	unsigned int i;
 526	int ret = 0;
 527
 528	for_each_fru(i, rec) {
 529		/* No need to update saved records that match the current record size. */
 530		if (rec->hdr.record_length == max_rec_len)
 531			continue;
 532
 533		if (!rec->hdr.record_length)
 534			set_bit(i, new_records);
 535
 536		set_rec_fields(rec);
 537
 538		ret = update_record_on_storage(rec);
 539		if (ret)
 540			goto out_clear;
 541	}
 542
 543	return ret;
 544
 545out_clear:
 546	for_each_fru(i, rec) {
 547		if (!test_bit(i, new_records))
 548			continue;
 549
 550		erst_clear(rec->hdr.record_id);
 551	}
 552
 553	return ret;
 554}
 555
 556/* Check that the record matches expected types for the current system.*/
 557static bool fmp_is_usable(struct fru_rec *rec)
 558{
 559	struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
 560	u64 cpuid;
 561
 562	pr_debug("Validation bits: 0x%016llx\n", fmp->validation_bits);
 563
 564	if (!(fmp->validation_bits & FMP_VALID_ARCH_TYPE)) {
 565		pr_debug("Arch type unknown\n");
 566		return false;
 567	}
 568
 569	if (fmp->fru_arch_type != FMP_ARCH_TYPE_X86_CPUID_1_EAX) {
 570		pr_debug("Arch type not 'x86 Family/Model/Stepping'\n");
 571		return false;
 572	}
 573
 574	if (!(fmp->validation_bits & FMP_VALID_ARCH)) {
 575		pr_debug("Arch value unknown\n");
 576		return false;
 577	}
 578
 579	cpuid = cpuid_eax(1);
 580	if (fmp->fru_arch != cpuid) {
 581		pr_debug("Arch value mismatch: record = 0x%016llx, system = 0x%016llx\n",
 582			 fmp->fru_arch, cpuid);
 583		return false;
 584	}
 585
 586	if (!(fmp->validation_bits & FMP_VALID_ID_TYPE)) {
 587		pr_debug("FRU ID type unknown\n");
 588		return false;
 589	}
 590
 591	if (fmp->fru_id_type != FMP_ID_TYPE_X86_PPIN) {
 592		pr_debug("FRU ID type is not 'x86 PPIN'\n");
 593		return false;
 594	}
 595
 596	if (!(fmp->validation_bits & FMP_VALID_ID)) {
 597		pr_debug("FRU ID value unknown\n");
 598		return false;
 599	}
 600
 601	return true;
 602}
 603
 604static bool fmp_is_valid(struct fru_rec *rec)
 605{
 606	struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
 607	u32 checksum, len;
 608
 609	len = get_fmp_len(rec);
 610	if (len < sizeof(struct cper_sec_fru_mem_poison)) {
 611		pr_debug("fmp length is too small\n");
 612		return false;
 613	}
 614
 615	/* Checksum must sum to zero for the entire section. */
 616	checksum = do_fmp_checksum(fmp, len) + fmp->checksum;
 617	if (checksum) {
 618		pr_debug("fmp checksum failed: sum = 0x%x\n", checksum);
 619		print_hex_dump_debug("fmp record: ", DUMP_PREFIX_NONE, 16, 1, fmp, len, false);
 620		return false;
 621	}
 622
 623	if (!fmp_is_usable(rec))
 624		return false;
 625
 626	return true;
 627}
 628
 629static struct fru_rec *get_valid_record(struct fru_rec *old)
 630{
 631	struct fru_rec *new;
 632
 633	if (!fmp_is_valid(old)) {
 634		pr_debug("Ignoring invalid record\n");
 635		return NULL;
 636	}
 637
 638	new = get_fru_record(old->fmp.fru_id);
 639	if (!new)
 640		pr_debug("Ignoring record for absent FRU\n");
 641
 642	return new;
 643}
 644
 645/*
 646 * Fetch saved records from persistent storage.
 647 *
 648 * For each found record:
 649 * - If it was not created by this module, then ignore it.
 650 * - If it is valid, then copy its data to the local cache.
 651 * - If it is not valid, then erase it.
 652 */
 653static int get_saved_records(void)
 654{
 655	struct fru_rec *old, *new;
 656	u64 record_id;
 657	int ret, pos;
 658	ssize_t len;
 659
 660	old = kmalloc(FMPM_MAX_REC_LEN, GFP_KERNEL);
 661	if (!old) {
 662		ret = -ENOMEM;
 663		goto out;
 664	}
 665
 666	ret = erst_get_record_id_begin(&pos);
 667	if (ret < 0)
 668		goto out_end;
 669
 670	while (!erst_get_record_id_next(&pos, &record_id)) {
 671		if (record_id == APEI_ERST_INVALID_RECORD_ID)
 672			goto out_end;
 673		/*
 674		 * Make sure to clear temporary buffer between reads to avoid
 675		 * leftover data from records of various sizes.
 676		 */
 677		memset(old, 0, FMPM_MAX_REC_LEN);
 678
 679		len = erst_read_record(record_id, &old->hdr, FMPM_MAX_REC_LEN,
 680				       sizeof(struct fru_rec), &CPER_CREATOR_FMP);
 681		if (len < 0)
 682			continue;
 683
 684		new = get_valid_record(old);
 685		if (!new) {
 686			erst_clear(record_id);
 687			continue;
 688		}
 689
 690		if (len > max_rec_len) {
 691			unsigned int saved_nr_entries;
 692
 693			saved_nr_entries  = len - sizeof(struct fru_rec);
 694			saved_nr_entries /= sizeof(struct cper_fru_poison_desc);
 695
 696			pr_warn("Saved record found with %u entries.\n", saved_nr_entries);
 697			pr_warn("Please increase max_nr_entries to %u.\n", saved_nr_entries);
 698
 699			ret = -EINVAL;
 700			goto out_end;
 701		}
 702
 703		/* Restore the record */
 704		memcpy(new, old, len);
 705	}
 706
 707out_end:
 708	erst_get_record_id_end();
 709	kfree(old);
 710out:
 711	return ret;
 712}
 713
 714static void set_fmp_fields(struct fru_rec *rec, unsigned int cpu)
 715{
 716	struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
 717
 718	fmp->fru_arch_type    = FMP_ARCH_TYPE_X86_CPUID_1_EAX;
 719	fmp->validation_bits |= FMP_VALID_ARCH_TYPE;
 720
 721	/* Assume all CPUs in the system have the same value for now. */
 722	fmp->fru_arch	      = cpuid_eax(1);
 723	fmp->validation_bits |= FMP_VALID_ARCH;
 724
 725	fmp->fru_id_type      = FMP_ID_TYPE_X86_PPIN;
 726	fmp->validation_bits |= FMP_VALID_ID_TYPE;
 727
 728	fmp->fru_id	      = topology_ppin(cpu);
 729	fmp->validation_bits |= FMP_VALID_ID;
 730}
 731
 732static int init_fmps(void)
 733{
 734	struct fru_rec *rec;
 735	unsigned int i, cpu;
 736	int ret = 0;
 737
 738	for_each_fru(i, rec) {
 739		unsigned int fru_cpu = INVALID_CPU;
 740
 741		cpus_read_lock();
 742		for_each_online_cpu(cpu) {
 743			if (topology_physical_package_id(cpu) == i) {
 744				fru_cpu = cpu;
 745				break;
 746			}
 747		}
 748		cpus_read_unlock();
 749
 750		if (fru_cpu == INVALID_CPU) {
 751			pr_debug("Failed to find matching CPU for FRU #%u\n", i);
 752			ret = -ENODEV;
 753			break;
 754		}
 755
 756		set_fmp_fields(rec, fru_cpu);
 757	}
 758
 759	return ret;
 760}
 761
 762static int get_system_info(void)
 763{
 764	/* Only load on MI300A systems for now. */
 765	if (!(boot_cpu_data.x86_model >= 0x90 &&
 766	      boot_cpu_data.x86_model <= 0x9f))
 767		return -ENODEV;
 768
 769	if (!cpu_feature_enabled(X86_FEATURE_AMD_PPIN)) {
 770		pr_debug("PPIN feature not available\n");
 771		return -ENODEV;
 772	}
 773
 774	/* Use CPU socket as FRU for MI300 systems. */
 775	max_nr_fru = topology_max_packages();
 776	if (!max_nr_fru)
 777		return -ENODEV;
 778
 779	if (max_nr_fru > FMPM_MAX_NR_FRU) {
 780		pr_warn("Too many FRUs to manage: found: %u, max: %u\n",
 781			max_nr_fru, FMPM_MAX_NR_FRU);
 782		return -ENODEV;
 783	}
 784
 785	if (!max_nr_entries)
 786		max_nr_entries = FMPM_DEFAULT_MAX_NR_ENTRIES;
 787
 788	spa_nr_entries = max_nr_fru * max_nr_entries;
 789
 790	max_rec_len  = sizeof(struct fru_rec);
 791	max_rec_len += sizeof(struct cper_fru_poison_desc) * max_nr_entries;
 792
 793	pr_info("max FRUs: %u, max entries: %u, max record length: %lu\n",
 794		 max_nr_fru, max_nr_entries, max_rec_len);
 795
 796	return 0;
 797}
 798
 799static void free_records(void)
 800{
 801	struct fru_rec *rec;
 802	int i;
 803
 804	for_each_fru(i, rec)
 805		kfree(rec);
 806
 807	kfree(fru_records);
 808	kfree(spa_entries);
 809}
 810
 811static int allocate_records(void)
 812{
 813	int i, ret = 0;
 814
 815	fru_records = kcalloc(max_nr_fru, sizeof(struct fru_rec *), GFP_KERNEL);
 816	if (!fru_records) {
 817		ret = -ENOMEM;
 818		goto out;
 819	}
 820
 821	for (i = 0; i < max_nr_fru; i++) {
 822		fru_records[i] = kzalloc(max_rec_len, GFP_KERNEL);
 823		if (!fru_records[i]) {
 824			ret = -ENOMEM;
 825			goto out_free;
 826		}
 827	}
 828
 829	spa_entries = kcalloc(spa_nr_entries, sizeof(u64), GFP_KERNEL);
 830	if (!spa_entries) {
 831		ret = -ENOMEM;
 832		goto out_free;
 833	}
 834
 835	for (i = 0; i < spa_nr_entries; i++)
 836		spa_entries[i] = INVALID_SPA;
 837
 838	return ret;
 839
 840out_free:
 841	while (--i >= 0)
 842		kfree(fru_records[i]);
 843
 844	kfree(fru_records);
 845out:
 846	return ret;
 847}
 848
 849static void *fmpm_start(struct seq_file *f, loff_t *pos)
 850{
 851	if (*pos >= (spa_nr_entries + 1))
 852		return NULL;
 853	return pos;
 854}
 855
 856static void *fmpm_next(struct seq_file *f, void *data, loff_t *pos)
 857{
 858	if (++(*pos) >= (spa_nr_entries + 1))
 859		return NULL;
 860	return pos;
 861}
 862
 863static void fmpm_stop(struct seq_file *f, void *data)
 864{
 865}
 866
 867#define SHORT_WIDTH	8
 868#define U64_WIDTH	18
 869#define TIMESTAMP_WIDTH	19
 870#define LONG_WIDTH	24
 871#define U64_PAD		(LONG_WIDTH - U64_WIDTH)
 872#define TS_PAD		(LONG_WIDTH - TIMESTAMP_WIDTH)
 873static int fmpm_show(struct seq_file *f, void *data)
 874{
 875	unsigned int fru_idx, entry, spa_entry, line;
 876	struct cper_fru_poison_desc *fpd;
 877	struct fru_rec *rec;
 878
 879	line = *(loff_t *)data;
 880	if (line == 0) {
 881		seq_printf(f, "%-*s", SHORT_WIDTH, "fru_idx");
 882		seq_printf(f, "%-*s", LONG_WIDTH,  "fru_id");
 883		seq_printf(f, "%-*s", SHORT_WIDTH, "entry");
 884		seq_printf(f, "%-*s", LONG_WIDTH,  "timestamp");
 885		seq_printf(f, "%-*s", LONG_WIDTH,  "hw_id");
 886		seq_printf(f, "%-*s", LONG_WIDTH,  "addr");
 887		seq_printf(f, "%-*s", LONG_WIDTH,  "spa");
 888		goto out_newline;
 889	}
 890
 891	spa_entry = line - 1;
 892	fru_idx	  = spa_entry / max_nr_entries;
 893	entry	  = spa_entry % max_nr_entries;
 894
 895	rec = fru_records[fru_idx];
 896	if (!rec)
 897		goto out;
 898
 899	seq_printf(f, "%-*u",		SHORT_WIDTH, fru_idx);
 900	seq_printf(f, "0x%016llx%-*s",	rec->fmp.fru_id, U64_PAD, "");
 901	seq_printf(f, "%-*u",		SHORT_WIDTH, entry);
 902
 903	mutex_lock(&fmpm_update_mutex);
 904
 905	if (entry >= rec->fmp.nr_entries) {
 906		seq_printf(f, "%-*s", LONG_WIDTH, "*");
 907		seq_printf(f, "%-*s", LONG_WIDTH, "*");
 908		seq_printf(f, "%-*s", LONG_WIDTH, "*");
 909		seq_printf(f, "%-*s", LONG_WIDTH, "*");
 910		goto out_unlock;
 911	}
 912
 913	fpd = &rec->entries[entry];
 914
 915	seq_printf(f, "%ptT%-*s",	&fpd->timestamp, TS_PAD,  "");
 916	seq_printf(f, "0x%016llx%-*s",	fpd->hw_id,	 U64_PAD, "");
 917	seq_printf(f, "0x%016llx%-*s",	fpd->addr,	 U64_PAD, "");
 918
 919	if (spa_entries[spa_entry] == INVALID_SPA)
 920		seq_printf(f, "%-*s", LONG_WIDTH, "*");
 921	else
 922		seq_printf(f, "0x%016llx%-*s", spa_entries[spa_entry], U64_PAD, "");
 923
 924out_unlock:
 925	mutex_unlock(&fmpm_update_mutex);
 926out_newline:
 927	seq_putc(f, '\n');
 928out:
 929	return 0;
 930}
 931
 932static const struct seq_operations fmpm_seq_ops = {
 933	.start	= fmpm_start,
 934	.next	= fmpm_next,
 935	.stop	= fmpm_stop,
 936	.show	= fmpm_show,
 937};
 938
 939static int fmpm_open(struct inode *inode, struct file *file)
 940{
 941	return seq_open(file, &fmpm_seq_ops);
 942}
 943
 944static const struct file_operations fmpm_fops = {
 945	.open		= fmpm_open,
 946	.release	= seq_release,
 947	.read		= seq_read,
 948	.llseek		= seq_lseek,
 949};
 950
 951static void setup_debugfs(void)
 952{
 953	struct dentry *dfs = ras_get_debugfs_root();
 954
 955	if (!dfs)
 956		return;
 957
 958	fmpm_dfs_dir = debugfs_create_dir("fmpm", dfs);
 959	if (!fmpm_dfs_dir)
 960		return;
 961
 962	fmpm_dfs_entries = debugfs_create_file("entries", 0400, fmpm_dfs_dir, NULL, &fmpm_fops);
 963	if (!fmpm_dfs_entries)
 964		debugfs_remove(fmpm_dfs_dir);
 965}
 966
 967static const struct x86_cpu_id fmpm_cpuids[] = {
 968	X86_MATCH_VENDOR_FAM(AMD, 0x19, NULL),
 969	{ }
 970};
 971MODULE_DEVICE_TABLE(x86cpu, fmpm_cpuids);
 972
 973static int __init fru_mem_poison_init(void)
 974{
 975	int ret;
 976
 977	if (!x86_match_cpu(fmpm_cpuids)) {
 978		ret = -ENODEV;
 979		goto out;
 980	}
 981
 982	if (erst_disable) {
 983		pr_debug("ERST not available\n");
 984		ret = -ENODEV;
 985		goto out;
 986	}
 987
 988	ret = get_system_info();
 989	if (ret)
 990		goto out;
 991
 992	ret = allocate_records();
 993	if (ret)
 994		goto out;
 995
 996	ret = init_fmps();
 997	if (ret)
 998		goto out_free;
 999
1000	ret = get_saved_records();
1001	if (ret)
1002		goto out_free;
1003
1004	ret = save_new_records();
1005	if (ret)
1006		goto out_free;
1007
1008	setup_debugfs();
1009
1010	retire_mem_records();
1011
1012	mce_register_decode_chain(&fru_mem_poison_nb);
1013
1014	pr_info("FRU Memory Poison Manager initialized\n");
1015	return 0;
1016
1017out_free:
1018	free_records();
1019out:
1020	return ret;
1021}
1022
1023static void __exit fru_mem_poison_exit(void)
1024{
1025	mce_unregister_decode_chain(&fru_mem_poison_nb);
1026	debugfs_remove(fmpm_dfs_dir);
1027	free_records();
1028}
1029
1030module_init(fru_mem_poison_init);
1031module_exit(fru_mem_poison_exit);
1032
1033MODULE_LICENSE("GPL");
1034MODULE_DESCRIPTION("FRU Memory Poison Manager");