projects/linux·blob master · refs

1042 lines · 24.2 KiB

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