Commit 18737353 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'edac_urgent_for_v6.9_rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras

Pull EDAC fixes from Borislav Petkov:

 - Fix more issues in the AMD FMPM driver

* tag 'edac_urgent_for_v6.9_rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/ras/ras:
  RAS: Avoid build errors when CONFIG_DEBUG_FS=n
  RAS/AMD/FMPM: Safely handle saved records of various sizes
  RAS/AMD/FMPM: Avoid NULL ptr deref in get_saved_records()
parents 5dad2623 a6b227d7
...@@ -150,6 +150,8 @@ static unsigned int max_nr_fru; ...@@ -150,6 +150,8 @@ static unsigned int max_nr_fru;
/* Total length of record including headers and list of descriptor entries. */ /* Total length of record including headers and list of descriptor entries. */
static size_t max_rec_len; static size_t max_rec_len;
#define FMPM_MAX_REC_LEN (sizeof(struct fru_rec) + (sizeof(struct cper_fru_poison_desc) * 255))
/* Total number of SPA entries across all FRUs. */ /* Total number of SPA entries across all FRUs. */
static unsigned int spa_nr_entries; static unsigned int spa_nr_entries;
...@@ -475,6 +477,16 @@ static void set_rec_fields(struct fru_rec *rec) ...@@ -475,6 +477,16 @@ static void set_rec_fields(struct fru_rec *rec)
struct cper_section_descriptor *sec_desc = &rec->sec_desc; struct cper_section_descriptor *sec_desc = &rec->sec_desc;
struct cper_record_header *hdr = &rec->hdr; struct cper_record_header *hdr = &rec->hdr;
/*
* This is a saved record created with fewer max_nr_entries.
* Update the record lengths and keep everything else as-is.
*/
if (hdr->record_length && hdr->record_length < max_rec_len) {
pr_debug("Growing record 0x%016llx from %u to %zu bytes\n",
hdr->record_id, hdr->record_length, max_rec_len);
goto update_lengths;
}
memcpy(hdr->signature, CPER_SIG_RECORD, CPER_SIG_SIZE); memcpy(hdr->signature, CPER_SIG_RECORD, CPER_SIG_SIZE);
hdr->revision = CPER_RECORD_REV; hdr->revision = CPER_RECORD_REV;
hdr->signature_end = CPER_SIG_END; hdr->signature_end = CPER_SIG_END;
...@@ -489,19 +501,21 @@ static void set_rec_fields(struct fru_rec *rec) ...@@ -489,19 +501,21 @@ static void set_rec_fields(struct fru_rec *rec)
hdr->error_severity = CPER_SEV_RECOVERABLE; hdr->error_severity = CPER_SEV_RECOVERABLE;
hdr->validation_bits = 0; hdr->validation_bits = 0;
hdr->record_length = max_rec_len;
hdr->creator_id = CPER_CREATOR_FMP; hdr->creator_id = CPER_CREATOR_FMP;
hdr->notification_type = CPER_NOTIFY_MCE; hdr->notification_type = CPER_NOTIFY_MCE;
hdr->record_id = cper_next_record_id(); hdr->record_id = cper_next_record_id();
hdr->flags = CPER_HW_ERROR_FLAGS_PREVERR; hdr->flags = CPER_HW_ERROR_FLAGS_PREVERR;
sec_desc->section_offset = sizeof(struct cper_record_header); sec_desc->section_offset = sizeof(struct cper_record_header);
sec_desc->section_length = max_rec_len - sizeof(struct cper_record_header);
sec_desc->revision = CPER_SEC_REV; sec_desc->revision = CPER_SEC_REV;
sec_desc->validation_bits = 0; sec_desc->validation_bits = 0;
sec_desc->flags = CPER_SEC_PRIMARY; sec_desc->flags = CPER_SEC_PRIMARY;
sec_desc->section_type = CPER_SECTION_TYPE_FMP; sec_desc->section_type = CPER_SECTION_TYPE_FMP;
sec_desc->section_severity = CPER_SEV_RECOVERABLE; sec_desc->section_severity = CPER_SEV_RECOVERABLE;
update_lengths:
hdr->record_length = max_rec_len;
sec_desc->section_length = max_rec_len - sizeof(struct cper_record_header);
} }
static int save_new_records(void) static int save_new_records(void)
...@@ -512,16 +526,18 @@ static int save_new_records(void) ...@@ -512,16 +526,18 @@ static int save_new_records(void)
int ret = 0; int ret = 0;
for_each_fru(i, rec) { for_each_fru(i, rec) {
if (rec->hdr.record_length) /* No need to update saved records that match the current record size. */
if (rec->hdr.record_length == max_rec_len)
continue; continue;
if (!rec->hdr.record_length)
set_bit(i, new_records);
set_rec_fields(rec); set_rec_fields(rec);
ret = update_record_on_storage(rec); ret = update_record_on_storage(rec);
if (ret) if (ret)
goto out_clear; goto out_clear;
set_bit(i, new_records);
} }
return ret; return ret;
...@@ -641,12 +657,7 @@ static int get_saved_records(void) ...@@ -641,12 +657,7 @@ static int get_saved_records(void)
int ret, pos; int ret, pos;
ssize_t len; ssize_t len;
/* old = kmalloc(FMPM_MAX_REC_LEN, GFP_KERNEL);
* Assume saved records match current max size.
*
* However, this may not be true depending on module parameters.
*/
old = kmalloc(max_rec_len, GFP_KERNEL);
if (!old) { if (!old) {
ret = -ENOMEM; ret = -ENOMEM;
goto out; goto out;
...@@ -663,21 +674,31 @@ static int get_saved_records(void) ...@@ -663,21 +674,31 @@ static int get_saved_records(void)
* Make sure to clear temporary buffer between reads to avoid * Make sure to clear temporary buffer between reads to avoid
* leftover data from records of various sizes. * leftover data from records of various sizes.
*/ */
memset(old, 0, max_rec_len); memset(old, 0, FMPM_MAX_REC_LEN);
len = erst_read_record(record_id, &old->hdr, max_rec_len, len = erst_read_record(record_id, &old->hdr, FMPM_MAX_REC_LEN,
sizeof(struct fru_rec), &CPER_CREATOR_FMP); sizeof(struct fru_rec), &CPER_CREATOR_FMP);
if (len < 0) if (len < 0)
continue; continue;
if (len > max_rec_len) { new = get_valid_record(old);
pr_debug("Found record larger than max_rec_len\n"); if (!new) {
erst_clear(record_id);
continue; continue;
} }
new = get_valid_record(old); if (len > max_rec_len) {
if (!new) unsigned int saved_nr_entries;
erst_clear(record_id);
saved_nr_entries = len - sizeof(struct fru_rec);
saved_nr_entries /= sizeof(struct cper_fru_poison_desc);
pr_warn("Saved record found with %u entries.\n", saved_nr_entries);
pr_warn("Please increase max_nr_entries to %u.\n", saved_nr_entries);
ret = -EINVAL;
goto out_end;
}
/* Restore the record */ /* Restore the record */
memcpy(new, old, len); memcpy(new, old, len);
......
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
#include <linux/debugfs.h> #include <linux/debugfs.h>
#if IS_ENABLED(CONFIG_DEBUG_FS)
struct dentry *ras_get_debugfs_root(void); struct dentry *ras_get_debugfs_root(void);
#else
static inline struct dentry *ras_get_debugfs_root(void) { return NULL; }
#endif /* DEBUG_FS */
#endif /* __RAS_DEBUGFS_H__ */ #endif /* __RAS_DEBUGFS_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment