From patchwork Tue Dec 15 11:05:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Borislav Petkov X-Patchwork-Id: 11974555 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 502C3C4361B for ; Tue, 15 Dec 2020 11:06:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0420222252 for ; Tue, 15 Dec 2020 11:06:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727029AbgLOLGF (ORCPT ); Tue, 15 Dec 2020 06:06:05 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39024 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726176AbgLOLGF (ORCPT ); Tue, 15 Dec 2020 06:06:05 -0500 Received: from mail.skyhub.de (mail.skyhub.de [IPv6:2a01:4f8:190:11c2::b:1457]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C0DB8C06179C; Tue, 15 Dec 2020 03:05:24 -0800 (PST) Received: from zn.tnic (p200300ec2f0f9e0004c09a106cc893ab.dip0.t-ipconnect.de [IPv6:2003:ec:2f0f:9e00:4c0:9a10:6cc8:93ab]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.skyhub.de (SuperMail on ZX Spectrum 128k) with ESMTPSA id 0B0C71EC0531; Tue, 15 Dec 2020 12:05:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alien8.de; s=dkim; t=1608030323; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type: content-transfer-encoding:content-transfer-encoding:in-reply-to: references; bh=ik/SYb2l7svBOZl65tmQV1OhmQBvNT4E7Nsqu0rZpng=; b=MjcmQ8wo60+U5HPWNaDDAbEN5U+tf8P8zCMMD6pWQYTxnPur9rjcUjbsNOYlx6x0MxPQop DeflbWidlouIRvZoOKXOCM83VNlaWh0cjWWuIm+jb94PiFC1gWtqAnt8WW+PIuH6UsdV9C Kh1SFKRlONE98xcgekVyaUq5oCQfM/A= From: Borislav Petkov To: Yazen Ghannam Cc: linux-edac , LKML Subject: [PATCH 1/2] EDAC/amd64: Merge sysfs debugging attributes setup code Date: Tue, 15 Dec 2020 12:05:16 +0100 Message-Id: <20201215110517.5215-1-bp@alien8.de> X-Mailer: git-send-email 2.29.2 MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-edac@vger.kernel.org From: Borislav Petkov There's no need for them to be in a separate file so merge them into the main driver compilation unit like the other EDAC drivers do. Drop now-unneeded function export, make the function static and shorten static function names. No functional changes. Signed-off-by: Borislav Petkov Reviewed-by: Yazen Ghannam --- drivers/edac/Makefile | 1 - drivers/edac/amd64_edac.c | 65 +++++++++++++++++++++++++++++++---- drivers/edac/amd64_edac.h | 3 -- drivers/edac/amd64_edac_dbg.c | 55 ----------------------------- 4 files changed, 59 insertions(+), 65 deletions(-) delete mode 100644 drivers/edac/amd64_edac_dbg.c diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 3a849168780d..195e851652b6 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -45,7 +45,6 @@ obj-$(CONFIG_EDAC_I82860) += i82860_edac.o obj-$(CONFIG_EDAC_R82600) += r82600_edac.o amd64_edac_mod-y := amd64_edac.o -amd64_edac_mod-$(CONFIG_EDAC_DEBUG) += amd64_edac_dbg.o amd64_edac_mod-$(CONFIG_EDAC_AMD64_ERROR_INJECTION) += amd64_edac_inj.o obj-$(CONFIG_EDAC_AMD64) += amd64_edac_mod.o diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 1362274d840b..b793ccd6c6bd 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -497,8 +497,8 @@ static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr) * complete 32-bit values despite the fact that the bitfields in the DHAR * only represent bits 31-24 of the base and offset values. */ -int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, - u64 *hole_offset, u64 *hole_size) +static int get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, + u64 *hole_offset, u64 *hole_size) { struct amd64_pvt *pvt = mci->pvt_info; @@ -551,7 +551,61 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, return 0; } -EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info); + +#ifdef CONFIG_EDAC_DEBUG +#define EDAC_DCT_ATTR_SHOW(reg) \ +static ssize_t reg##_show(struct device *dev, \ + struct device_attribute *mattr, char *data) \ +{ \ + struct mem_ctl_info *mci = to_mci(dev); \ + struct amd64_pvt *pvt = mci->pvt_info; \ + \ + return sprintf(data, "0x%016llx\n", (u64)pvt->reg); \ +} + +EDAC_DCT_ATTR_SHOW(dhar); +EDAC_DCT_ATTR_SHOW(dbam0); +EDAC_DCT_ATTR_SHOW(top_mem); +EDAC_DCT_ATTR_SHOW(top_mem2); + +static ssize_t hole_show(struct device *dev, struct device_attribute *mattr, + char *data) +{ + struct mem_ctl_info *mci = to_mci(dev); + + u64 hole_base = 0; + u64 hole_offset = 0; + u64 hole_size = 0; + + get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size); + + return sprintf(data, "%llx %llx %llx\n", hole_base, hole_offset, + hole_size); +} + +/* + * update NUM_DBG_ATTRS in case you add new members + */ +static DEVICE_ATTR(dhar, S_IRUGO, dhar_show, NULL); +static DEVICE_ATTR(dbam, S_IRUGO, dbam0_show, NULL); +static DEVICE_ATTR(topmem, S_IRUGO, top_mem_show, NULL); +static DEVICE_ATTR(topmem2, S_IRUGO, top_mem2_show, NULL); +static DEVICE_ATTR(dram_hole, S_IRUGO, hole_show, NULL); + +static struct attribute *dbg_attrs[] = { + &dev_attr_dhar.attr, + &dev_attr_dbam.attr, + &dev_attr_topmem.attr, + &dev_attr_topmem2.attr, + &dev_attr_dram_hole.attr, + NULL +}; + +const struct attribute_group dbg_group = { + .attrs = dbg_attrs, +}; +#endif /* CONFIG_EDAC_DEBUG */ + /* * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is @@ -590,8 +644,7 @@ static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr) dram_base = get_dram_base(pvt, pvt->mc_node_id); - ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, - &hole_size); + ret = get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size); if (!ret) { if ((sys_addr >= (1ULL << 32)) && (sys_addr < ((1ULL << 32) + hole_size))) { @@ -3411,7 +3464,7 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) static const struct attribute_group *amd64_edac_attr_groups[] = { #ifdef CONFIG_EDAC_DEBUG - &amd64_edac_dbg_group, + &dbg_group, #endif #ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION &amd64_edac_inj_group, diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index 52b5d03eeba0..7c9f8c0b46d7 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -501,9 +501,6 @@ int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, #define amd64_write_pci_cfg(pdev, offset, val) \ __amd64_write_pci_cfg_dword(pdev, offset, val, __func__) -int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, - u64 *hole_offset, u64 *hole_size); - #define to_mci(k) container_of(k, struct mem_ctl_info, dev) /* Injection helpers */ diff --git a/drivers/edac/amd64_edac_dbg.c b/drivers/edac/amd64_edac_dbg.c deleted file mode 100644 index 393be3351493..000000000000 --- a/drivers/edac/amd64_edac_dbg.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "amd64_edac.h" - -#define EDAC_DCT_ATTR_SHOW(reg) \ -static ssize_t amd64_##reg##_show(struct device *dev, \ - struct device_attribute *mattr, \ - char *data) \ -{ \ - struct mem_ctl_info *mci = to_mci(dev); \ - struct amd64_pvt *pvt = mci->pvt_info; \ - return sprintf(data, "0x%016llx\n", (u64)pvt->reg); \ -} - -EDAC_DCT_ATTR_SHOW(dhar); -EDAC_DCT_ATTR_SHOW(dbam0); -EDAC_DCT_ATTR_SHOW(top_mem); -EDAC_DCT_ATTR_SHOW(top_mem2); - -static ssize_t amd64_hole_show(struct device *dev, - struct device_attribute *mattr, - char *data) -{ - struct mem_ctl_info *mci = to_mci(dev); - - u64 hole_base = 0; - u64 hole_offset = 0; - u64 hole_size = 0; - - amd64_get_dram_hole_info(mci, &hole_base, &hole_offset, &hole_size); - - return sprintf(data, "%llx %llx %llx\n", hole_base, hole_offset, - hole_size); -} - -/* - * update NUM_DBG_ATTRS in case you add new members - */ -static DEVICE_ATTR(dhar, S_IRUGO, amd64_dhar_show, NULL); -static DEVICE_ATTR(dbam, S_IRUGO, amd64_dbam0_show, NULL); -static DEVICE_ATTR(topmem, S_IRUGO, amd64_top_mem_show, NULL); -static DEVICE_ATTR(topmem2, S_IRUGO, amd64_top_mem2_show, NULL); -static DEVICE_ATTR(dram_hole, S_IRUGO, amd64_hole_show, NULL); - -static struct attribute *amd64_edac_dbg_attrs[] = { - &dev_attr_dhar.attr, - &dev_attr_dbam.attr, - &dev_attr_topmem.attr, - &dev_attr_topmem2.attr, - &dev_attr_dram_hole.attr, - NULL -}; - -const struct attribute_group amd64_edac_dbg_group = { - .attrs = amd64_edac_dbg_attrs, -}; From patchwork Tue Dec 15 11:05:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Borislav Petkov X-Patchwork-Id: 11974557 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-18.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER, INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED, USER_AGENT_GIT autolearn=unavailable autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0E0B6C4361B for ; Tue, 15 Dec 2020 11:06:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A20D22224C for ; Tue, 15 Dec 2020 11:06:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727303AbgLOLGG (ORCPT ); Tue, 15 Dec 2020 06:06:06 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39028 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726176AbgLOLGF (ORCPT ); Tue, 15 Dec 2020 06:06:05 -0500 Received: from mail.skyhub.de (mail.skyhub.de [IPv6:2a01:4f8:190:11c2::b:1457]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8243FC0617A7; Tue, 15 Dec 2020 03:05:25 -0800 (PST) Received: from zn.tnic (p200300ec2f0f9e0004c09a106cc893ab.dip0.t-ipconnect.de [IPv6:2003:ec:2f0f:9e00:4c0:9a10:6cc8:93ab]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.skyhub.de (SuperMail on ZX Spectrum 128k) with ESMTPSA id D783E1EC04EF; Tue, 15 Dec 2020 12:05:23 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=alien8.de; s=dkim; t=1608030324; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=ar+S7KklmEqRohSxM8GvqPlOqKI8x3eVADd4paee/cM=; b=EoHFpb/BYOIvqyKvubo+YwNtXP3jO8vm5AhhnBzkor+dEmNR0Keg28OxwMJ98HKhVTK2NB MsnSZbJyEQ/ehpR/X3funLAnMsYGiQeKXa8+8Js3ghXTjLDkT0cMqNsInKE1tMzxuH1xXK dD5AGOKLG4jN3ElvvnURbJ6NHH95G2I= From: Borislav Petkov To: Yazen Ghannam Cc: linux-edac , LKML Subject: [PATCH 2/2] EDAC/amd64: Merge error injection sysfs facilities Date: Tue, 15 Dec 2020 12:05:17 +0100 Message-Id: <20201215110517.5215-2-bp@alien8.de> X-Mailer: git-send-email 2.29.2 In-Reply-To: <20201215110517.5215-1-bp@alien8.de> References: <20201215110517.5215-1-bp@alien8.de> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-edac@vger.kernel.org From: Borislav Petkov Merge them into the main driver and put them inside an EDAC_DEBUG ifdeffery to simplify the driver and have all debugging/injection stuff behind a debug build-time switch. No functional changes. Signed-off-by: Borislav Petkov Reviewed-by: Yazen Ghannam Signed-off-by: Borislav Petkov --- drivers/edac/Kconfig | 7 +- drivers/edac/Makefile | 6 +- drivers/edac/amd64_edac.c | 237 +++++++++++++++++++++++++++++++++- drivers/edac/amd64_edac.h | 8 -- drivers/edac/amd64_edac_inj.c | 235 --------------------------------- 5 files changed, 236 insertions(+), 257 deletions(-) delete mode 100644 drivers/edac/amd64_edac_inj.c diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 7a47680d6f07..9c2e719cb86a 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -81,10 +81,9 @@ config EDAC_AMD64 Support for error detection and correction of DRAM ECC errors on the AMD64 families (>= K8) of memory controllers. -config EDAC_AMD64_ERROR_INJECTION - bool "Sysfs HW Error injection facilities" - depends on EDAC_AMD64 - help + When EDAC_DEBUG is enabled, hardware error injection facilities + through sysfs are available: + Recent Opterons (Family 10h and later) provide for Memory Error Injection into the ECC detection circuits. The amd64_edac module allows the operator/user to inject Uncorrectable and Correctable diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index 195e851652b6..b8133cd32059 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile @@ -43,11 +43,7 @@ obj-$(CONFIG_EDAC_IE31200) += ie31200_edac.o obj-$(CONFIG_EDAC_X38) += x38_edac.o obj-$(CONFIG_EDAC_I82860) += i82860_edac.o obj-$(CONFIG_EDAC_R82600) += r82600_edac.o - -amd64_edac_mod-y := amd64_edac.o -amd64_edac_mod-$(CONFIG_EDAC_AMD64_ERROR_INJECTION) += amd64_edac_inj.o - -obj-$(CONFIG_EDAC_AMD64) += amd64_edac_mod.o +obj-$(CONFIG_EDAC_AMD64) += amd64_edac.o obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index b793ccd6c6bd..f5de5857a84e 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -601,11 +601,240 @@ static struct attribute *dbg_attrs[] = { NULL }; -const struct attribute_group dbg_group = { +static const struct attribute_group dbg_group = { .attrs = dbg_attrs, }; -#endif /* CONFIG_EDAC_DEBUG */ +static ssize_t inject_section_show(struct device *dev, + struct device_attribute *mattr, char *buf) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct amd64_pvt *pvt = mci->pvt_info; + return sprintf(buf, "0x%x\n", pvt->injection.section); +} + +/* + * store error injection section value which refers to one of 4 16-byte sections + * within a 64-byte cacheline + * + * range: 0..3 + */ +static ssize_t inject_section_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct amd64_pvt *pvt = mci->pvt_info; + unsigned long value; + int ret; + + ret = kstrtoul(data, 10, &value); + if (ret < 0) + return ret; + + if (value > 3) { + amd64_warn("%s: invalid section 0x%lx\n", __func__, value); + return -EINVAL; + } + + pvt->injection.section = (u32) value; + return count; +} + +static ssize_t inject_word_show(struct device *dev, + struct device_attribute *mattr, char *buf) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct amd64_pvt *pvt = mci->pvt_info; + return sprintf(buf, "0x%x\n", pvt->injection.word); +} + +/* + * store error injection word value which refers to one of 9 16-bit word of the + * 16-byte (128-bit + ECC bits) section + * + * range: 0..8 + */ +static ssize_t inject_word_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct amd64_pvt *pvt = mci->pvt_info; + unsigned long value; + int ret; + + ret = kstrtoul(data, 10, &value); + if (ret < 0) + return ret; + + if (value > 8) { + amd64_warn("%s: invalid word 0x%lx\n", __func__, value); + return -EINVAL; + } + + pvt->injection.word = (u32) value; + return count; +} + +static ssize_t inject_ecc_vector_show(struct device *dev, + struct device_attribute *mattr, + char *buf) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct amd64_pvt *pvt = mci->pvt_info; + return sprintf(buf, "0x%x\n", pvt->injection.bit_map); +} + +/* + * store 16 bit error injection vector which enables injecting errors to the + * corresponding bit within the error injection word above. When used during a + * DRAM ECC read, it holds the contents of the of the DRAM ECC bits. + */ +static ssize_t inject_ecc_vector_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct amd64_pvt *pvt = mci->pvt_info; + unsigned long value; + int ret; + + ret = kstrtoul(data, 16, &value); + if (ret < 0) + return ret; + + if (value & 0xFFFF0000) { + amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value); + return -EINVAL; + } + + pvt->injection.bit_map = (u32) value; + return count; +} + +/* + * Do a DRAM ECC read. Assemble staged values in the pvt area, format into + * fields needed by the injection registers and read the NB Array Data Port. + */ +static ssize_t inject_read_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct amd64_pvt *pvt = mci->pvt_info; + unsigned long value; + u32 section, word_bits; + int ret; + + ret = kstrtoul(data, 10, &value); + if (ret < 0) + return ret; + + /* Form value to choose 16-byte section of cacheline */ + section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); + + amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); + + word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection); + + /* Issue 'word' and 'bit' along with the READ request */ + amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); + + edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); + + return count; +} + +/* + * Do a DRAM ECC write. Assemble staged values in the pvt area and format into + * fields needed by the injection registers. + */ +static ssize_t inject_write_store(struct device *dev, + struct device_attribute *mattr, + const char *data, size_t count) +{ + struct mem_ctl_info *mci = to_mci(dev); + struct amd64_pvt *pvt = mci->pvt_info; + u32 section, word_bits, tmp; + unsigned long value; + int ret; + + ret = kstrtoul(data, 10, &value); + if (ret < 0) + return ret; + + /* Form value to choose 16-byte section of cacheline */ + section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); + + amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); + + word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection); + + pr_notice_once("Don't forget to decrease MCE polling interval in\n" + "/sys/bus/machinecheck/devices/machinecheck/check_interval\n" + "so that you can get the error report faster.\n"); + + on_each_cpu(disable_caches, NULL, 1); + + /* Issue 'word' and 'bit' along with the READ request */ + amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); + + retry: + /* wait until injection happens */ + amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp); + if (tmp & F10_NB_ARR_ECC_WR_REQ) { + cpu_relax(); + goto retry; + } + + on_each_cpu(enable_caches, NULL, 1); + + edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); + + return count; +} + +/* + * update NUM_INJ_ATTRS in case you add new members + */ + +static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR, + inject_section_show, inject_section_store); +static DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR, + inject_word_show, inject_word_store); +static DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR, + inject_ecc_vector_show, inject_ecc_vector_store); +static DEVICE_ATTR(inject_write, S_IWUSR, + NULL, inject_write_store); +static DEVICE_ATTR(inject_read, S_IWUSR, + NULL, inject_read_store); + +static struct attribute *inj_attrs[] = { + &dev_attr_inject_section.attr, + &dev_attr_inject_word.attr, + &dev_attr_inject_ecc_vector.attr, + &dev_attr_inject_write.attr, + &dev_attr_inject_read.attr, + NULL +}; + +static umode_t inj_is_visible(struct kobject *kobj, struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); + struct amd64_pvt *pvt = mci->pvt_info; + + if (pvt->fam < 0x10) + return 0; + return attr->mode; +} + +static const struct attribute_group inj_group = { + .attrs = inj_attrs, + .is_visible = inj_is_visible, +}; +#endif /* CONFIG_EDAC_DEBUG */ /* * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is @@ -3465,9 +3694,7 @@ static struct amd64_family_type *per_family_init(struct amd64_pvt *pvt) static const struct attribute_group *amd64_edac_attr_groups[] = { #ifdef CONFIG_EDAC_DEBUG &dbg_group, -#endif -#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION - &amd64_edac_inj_group, + &inj_group, #endif NULL }; diff --git a/drivers/edac/amd64_edac.h b/drivers/edac/amd64_edac.h index 7c9f8c0b46d7..85aa820bc165 100644 --- a/drivers/edac/amd64_edac.h +++ b/drivers/edac/amd64_edac.h @@ -462,14 +462,6 @@ struct ecc_settings { } flags; }; -#ifdef CONFIG_EDAC_DEBUG -extern const struct attribute_group amd64_edac_dbg_group; -#endif - -#ifdef CONFIG_EDAC_AMD64_ERROR_INJECTION -extern const struct attribute_group amd64_edac_inj_group; -#endif - /* * Each of the PCI Device IDs types have their own set of hardware accessor * functions and per device encoding/decoding logic. diff --git a/drivers/edac/amd64_edac_inj.c b/drivers/edac/amd64_edac_inj.c deleted file mode 100644 index d96d6116f0fb..000000000000 --- a/drivers/edac/amd64_edac_inj.c +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include "amd64_edac.h" - -static ssize_t amd64_inject_section_show(struct device *dev, - struct device_attribute *mattr, - char *buf) -{ - struct mem_ctl_info *mci = to_mci(dev); - struct amd64_pvt *pvt = mci->pvt_info; - return sprintf(buf, "0x%x\n", pvt->injection.section); -} - -/* - * store error injection section value which refers to one of 4 16-byte sections - * within a 64-byte cacheline - * - * range: 0..3 - */ -static ssize_t amd64_inject_section_store(struct device *dev, - struct device_attribute *mattr, - const char *data, size_t count) -{ - struct mem_ctl_info *mci = to_mci(dev); - struct amd64_pvt *pvt = mci->pvt_info; - unsigned long value; - int ret; - - ret = kstrtoul(data, 10, &value); - if (ret < 0) - return ret; - - if (value > 3) { - amd64_warn("%s: invalid section 0x%lx\n", __func__, value); - return -EINVAL; - } - - pvt->injection.section = (u32) value; - return count; -} - -static ssize_t amd64_inject_word_show(struct device *dev, - struct device_attribute *mattr, - char *buf) -{ - struct mem_ctl_info *mci = to_mci(dev); - struct amd64_pvt *pvt = mci->pvt_info; - return sprintf(buf, "0x%x\n", pvt->injection.word); -} - -/* - * store error injection word value which refers to one of 9 16-bit word of the - * 16-byte (128-bit + ECC bits) section - * - * range: 0..8 - */ -static ssize_t amd64_inject_word_store(struct device *dev, - struct device_attribute *mattr, - const char *data, size_t count) -{ - struct mem_ctl_info *mci = to_mci(dev); - struct amd64_pvt *pvt = mci->pvt_info; - unsigned long value; - int ret; - - ret = kstrtoul(data, 10, &value); - if (ret < 0) - return ret; - - if (value > 8) { - amd64_warn("%s: invalid word 0x%lx\n", __func__, value); - return -EINVAL; - } - - pvt->injection.word = (u32) value; - return count; -} - -static ssize_t amd64_inject_ecc_vector_show(struct device *dev, - struct device_attribute *mattr, - char *buf) -{ - struct mem_ctl_info *mci = to_mci(dev); - struct amd64_pvt *pvt = mci->pvt_info; - return sprintf(buf, "0x%x\n", pvt->injection.bit_map); -} - -/* - * store 16 bit error injection vector which enables injecting errors to the - * corresponding bit within the error injection word above. When used during a - * DRAM ECC read, it holds the contents of the of the DRAM ECC bits. - */ -static ssize_t amd64_inject_ecc_vector_store(struct device *dev, - struct device_attribute *mattr, - const char *data, size_t count) -{ - struct mem_ctl_info *mci = to_mci(dev); - struct amd64_pvt *pvt = mci->pvt_info; - unsigned long value; - int ret; - - ret = kstrtoul(data, 16, &value); - if (ret < 0) - return ret; - - if (value & 0xFFFF0000) { - amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value); - return -EINVAL; - } - - pvt->injection.bit_map = (u32) value; - return count; -} - -/* - * Do a DRAM ECC read. Assemble staged values in the pvt area, format into - * fields needed by the injection registers and read the NB Array Data Port. - */ -static ssize_t amd64_inject_read_store(struct device *dev, - struct device_attribute *mattr, - const char *data, size_t count) -{ - struct mem_ctl_info *mci = to_mci(dev); - struct amd64_pvt *pvt = mci->pvt_info; - unsigned long value; - u32 section, word_bits; - int ret; - - ret = kstrtoul(data, 10, &value); - if (ret < 0) - return ret; - - /* Form value to choose 16-byte section of cacheline */ - section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); - - amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); - - word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection); - - /* Issue 'word' and 'bit' along with the READ request */ - amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); - - edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); - - return count; -} - -/* - * Do a DRAM ECC write. Assemble staged values in the pvt area and format into - * fields needed by the injection registers. - */ -static ssize_t amd64_inject_write_store(struct device *dev, - struct device_attribute *mattr, - const char *data, size_t count) -{ - struct mem_ctl_info *mci = to_mci(dev); - struct amd64_pvt *pvt = mci->pvt_info; - u32 section, word_bits, tmp; - unsigned long value; - int ret; - - ret = kstrtoul(data, 10, &value); - if (ret < 0) - return ret; - - /* Form value to choose 16-byte section of cacheline */ - section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); - - amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); - - word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection); - - pr_notice_once("Don't forget to decrease MCE polling interval in\n" - "/sys/bus/machinecheck/devices/machinecheck/check_interval\n" - "so that you can get the error report faster.\n"); - - on_each_cpu(disable_caches, NULL, 1); - - /* Issue 'word' and 'bit' along with the READ request */ - amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); - - retry: - /* wait until injection happens */ - amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp); - if (tmp & F10_NB_ARR_ECC_WR_REQ) { - cpu_relax(); - goto retry; - } - - on_each_cpu(enable_caches, NULL, 1); - - edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); - - return count; -} - -/* - * update NUM_INJ_ATTRS in case you add new members - */ - -static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR, - amd64_inject_section_show, amd64_inject_section_store); -static DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR, - amd64_inject_word_show, amd64_inject_word_store); -static DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR, - amd64_inject_ecc_vector_show, amd64_inject_ecc_vector_store); -static DEVICE_ATTR(inject_write, S_IWUSR, - NULL, amd64_inject_write_store); -static DEVICE_ATTR(inject_read, S_IWUSR, - NULL, amd64_inject_read_store); - -static struct attribute *amd64_edac_inj_attrs[] = { - &dev_attr_inject_section.attr, - &dev_attr_inject_word.attr, - &dev_attr_inject_ecc_vector.attr, - &dev_attr_inject_write.attr, - &dev_attr_inject_read.attr, - NULL -}; - -static umode_t amd64_edac_inj_is_visible(struct kobject *kobj, - struct attribute *attr, int idx) -{ - struct device *dev = kobj_to_dev(kobj); - struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); - struct amd64_pvt *pvt = mci->pvt_info; - - if (pvt->fam < 0x10) - return 0; - return attr->mode; -} - -const struct attribute_group amd64_edac_inj_group = { - .attrs = amd64_edac_inj_attrs, - .is_visible = amd64_edac_inj_is_visible, -};