]> Git Repo - J-linux.git/blob - drivers/ras/amd/fmpm.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / ras / amd / fmpm.c
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/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 */
78 struct 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 */
95 struct 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. */
104 struct 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  */
116 static struct fru_rec **fru_records;
117
118 /* system physical addresses array */
119 static u64 *spa_entries;
120
121 static struct dentry *fmpm_dfs_dir;
122 static 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  */
139 static u8 max_nr_entries;
140 module_param(max_nr_entries, byte, 0644);
141 MODULE_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
148 static unsigned int max_nr_fru;
149
150 /* Total length of record including headers and list of descriptor entries. */
151 static 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. */
156 static 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  */
168 static 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
173 static inline u32 get_fmp_len(struct fru_rec *rec)
174 {
175         return rec->sec_desc.section_length - sizeof(struct cper_section_descriptor);
176 }
177
178 static 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  */
200 static 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
215 static 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
242 static 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
253 static 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
276 static 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
292 static 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
340 static 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
371 save_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
384 out_unlock:
385         mutex_unlock(&fmpm_update_mutex);
386 }
387
388 static 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
401 static 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
424 static struct notifier_block fru_mem_poison_nb = {
425         .notifier_call  = fru_handle_mem_poison,
426         .priority       = MCE_PRIO_LOWEST,
427 };
428
429 static 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
461 static 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. */
475 static 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
516 update_lengths:
517         hdr->record_length              = max_rec_len;
518         sec_desc->section_length        = max_rec_len - sizeof(struct cper_record_header);
519 }
520
521 static 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
545 out_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.*/
557 static 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
604 static 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
629 static 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  */
653 static 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
707 out_end:
708         erst_get_record_id_end();
709         kfree(old);
710 out:
711         return ret;
712 }
713
714 static 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
732 static 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
762 static 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
799 static 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
811 static 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
840 out_free:
841         while (--i >= 0)
842                 kfree(fru_records[i]);
843
844         kfree(fru_records);
845 out:
846         return ret;
847 }
848
849 static 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
856 static 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
863 static 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)
873 static 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
924 out_unlock:
925         mutex_unlock(&fmpm_update_mutex);
926 out_newline:
927         seq_putc(f, '\n');
928 out:
929         return 0;
930 }
931
932 static 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
939 static int fmpm_open(struct inode *inode, struct file *file)
940 {
941         return seq_open(file, &fmpm_seq_ops);
942 }
943
944 static const struct file_operations fmpm_fops = {
945         .open           = fmpm_open,
946         .release        = seq_release,
947         .read           = seq_read,
948         .llseek         = seq_lseek,
949 };
950
951 static 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
967 static const struct x86_cpu_id fmpm_cpuids[] = {
968         X86_MATCH_VENDOR_FAM(AMD, 0x19, NULL),
969         { }
970 };
971 MODULE_DEVICE_TABLE(x86cpu, fmpm_cpuids);
972
973 static 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
1017 out_free:
1018         free_records();
1019 out:
1020         return ret;
1021 }
1022
1023 static 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
1030 module_init(fru_mem_poison_init);
1031 module_exit(fru_mem_poison_exit);
1032
1033 MODULE_LICENSE("GPL");
1034 MODULE_DESCRIPTION("FRU Memory Poison Manager");
This page took 0.086175 seconds and 4 git commands to generate.