summaryrefslogtreecommitdiff
path: root/drivers/ras
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ras')
-rw-r--r--drivers/ras/amd/fmpm.c72
1 files changed, 71 insertions, 1 deletions
diff --git a/drivers/ras/amd/fmpm.c b/drivers/ras/amd/fmpm.c
index 80dd112b720a..8c3188488673 100644
--- a/drivers/ras/amd/fmpm.c
+++ b/drivers/ras/amd/fmpm.c
@@ -111,6 +111,11 @@ struct fru_rec {
*/
static struct fru_rec **fru_records;
+/* system physical addresses array */
+static u64 *spa_entries;
+
+#define INVALID_SPA ~0ULL
+
#define CPER_CREATOR_FMP \
GUID_INIT(0xcd5c2993, 0xf4b2, 0x41b2, 0xb5, 0xd4, 0xf9, 0xc3, \
0xa0, 0x33, 0x08, 0x75)
@@ -120,7 +125,7 @@ static struct fru_rec **fru_records;
0x12, 0x0a, 0x44, 0x58)
/**
- * DOC: fru_poison_entries (byte)
+ * DOC: max_nr_entries (byte)
* Maximum number of descriptor entries possible for each FRU.
*
* Values between '1' and '255' are valid.
@@ -140,6 +145,9 @@ static unsigned int max_nr_fru;
/* Total length of record including headers and list of descriptor entries. */
static size_t max_rec_len;
+/* Total number of SPA entries across all FRUs. */
+static unsigned int spa_nr_entries;
+
/*
* Protect the local records cache in fru_records and prevent concurrent
* writes to storage. This is only needed after init once notifier block
@@ -269,6 +277,54 @@ static bool rec_has_fpd(struct fru_rec *rec, struct cper_fru_poison_desc *fpd)
return false;
}
+static void save_spa(struct fru_rec *rec, unsigned int entry,
+ u64 addr, u64 id, unsigned int cpu)
+{
+ unsigned int i, fru_idx, spa_entry;
+ struct atl_err a_err;
+ unsigned long spa;
+
+ if (entry >= max_nr_entries) {
+ pr_warn_once("FRU descriptor entry %d out-of-bounds (max: %d)\n",
+ entry, max_nr_entries);
+ return;
+ }
+
+ /* spa_nr_entries is always multiple of max_nr_entries */
+ for (i = 0; i < spa_nr_entries; i += max_nr_entries) {
+ fru_idx = i / max_nr_entries;
+ if (fru_records[fru_idx] == rec)
+ break;
+ }
+
+ if (i >= spa_nr_entries) {
+ pr_warn_once("FRU record %d not found\n", i);
+ return;
+ }
+
+ spa_entry = i + entry;
+ if (spa_entry >= spa_nr_entries) {
+ pr_warn_once("spa_entries[] index out-of-bounds\n");
+ return;
+ }
+
+ memset(&a_err, 0, sizeof(struct atl_err));
+
+ a_err.addr = addr;
+ a_err.ipid = id;
+ a_err.cpu = cpu;
+
+ spa = amd_convert_umc_mca_addr_to_sys_addr(&a_err);
+ if (IS_ERR_VALUE(spa)) {
+ pr_debug("Failed to get system address\n");
+ return;
+ }
+
+ spa_entries[spa_entry] = spa;
+ pr_debug("fru_idx: %u, entry: %u, spa_entry: %u, spa: 0x%016llx\n",
+ fru_idx, entry, spa_entry, spa_entries[spa_entry]);
+}
+
static void update_fru_record(struct fru_rec *rec, struct mce *m)
{
struct cper_sec_fru_mem_poison *fmp = &rec->fmp;
@@ -301,6 +357,7 @@ static void update_fru_record(struct fru_rec *rec, struct mce *m)
entry = fmp->nr_entries;
save_fpd:
+ save_spa(rec, entry, m->addr, m->ipid, m->extcpu);
fpd_dest = &rec->entries[entry];
memcpy(fpd_dest, &fpd, sizeof(struct cper_fru_poison_desc));
@@ -385,6 +442,7 @@ static void retire_mem_fmp(struct fru_rec *rec)
continue;
retire_dram_row(fpd->addr, fpd->hw_id, err_cpu);
+ save_spa(rec, i, fpd->addr, fpd->hw_id, err_cpu);
}
}
@@ -696,6 +754,8 @@ static int get_system_info(void)
if (!max_nr_entries)
max_nr_entries = FMPM_DEFAULT_MAX_NR_ENTRIES;
+ spa_nr_entries = max_nr_fru * max_nr_entries;
+
max_rec_len = sizeof(struct fru_rec);
max_rec_len += sizeof(struct cper_fru_poison_desc) * max_nr_entries;
@@ -714,6 +774,7 @@ static void free_records(void)
kfree(rec);
kfree(fru_records);
+ kfree(spa_entries);
}
static int allocate_records(void)
@@ -734,6 +795,15 @@ static int allocate_records(void)
}
}
+ spa_entries = kcalloc(spa_nr_entries, sizeof(u64), GFP_KERNEL);
+ if (!spa_entries) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ for (i = 0; i < spa_nr_entries; i++)
+ spa_entries[i] = INVALID_SPA;
+
return ret;
out_free: