Path: blob/main/sys/dev/amd_ecc_inject/ecc_inject.c
39534 views
/*-1* Copyright (c) 2017 Andriy Gapon2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <sys/systm.h>28#include <sys/bus.h>29#include <sys/kernel.h>30#include <sys/conf.h>31#include <sys/malloc.h>32#include <sys/module.h>33#include <sys/sysctl.h>34#include <sys/types.h>3536#include <dev/pci/pcivar.h>3738#include <vm/vm.h>39#include <vm/vm_extern.h>40#include <vm/vm_kern.h>4142#include <machine/cputypes.h>43#include <machine/md_var.h>4445/*46* See BKDG for AMD Family 15h Models 00h-0Fh Processors47* (publication 42301 Rev 3.08 - March 12, 2012):48* - 2.13.3.1 DRAM Error Injection49* - D18F3xB8 NB Array Address50* - D18F3xBC NB Array Data Port51* - D18F3xBC_x8 DRAM ECC52*/53#define NB_MCA_CFG 0x4454#define DRAM_ECC_EN (1 << 22)55#define NB_MCA_EXTCFG 0x18056#define ECC_SYMB_SZ (1 << 25)57#define NB_ARRAY_ADDR 0xb858#define DRAM_ECC_SEL (0x8 << 28)59#define QUADRANT_SHIFT 160#define QUADRANT_MASK 0x361#define NB_ARRAY_PORT 0xbc62#define INJ_WORD_SHIFT 2063#define INJ_WORD_MASK 0x1ff64#define DRAM_ERR_EN (1 << 18)65#define DRAM_WR_REQ (1 << 17)66#define DRAM_RD_REQ (1 << 16)67#define INJ_VECTOR_MASK 0xffff6869static void ecc_ei_inject(int);7071static device_t nbdev;72static int delay_ms = 0;73static int quadrant = 0; /* 0 - 3 */74static int word_mask = 0x001; /* 9 bits: 8 + 1 for ECC */75static int bit_mask = 0x0001; /* 16 bits */7677static int78sysctl_int_with_max(SYSCTL_HANDLER_ARGS)79{80u_int value;81int error;8283value = *(u_int *)arg1;84error = sysctl_handle_int(oidp, &value, 0, req);85if (error || req->newptr == NULL)86return (error);87if (value > arg2)88return (EINVAL);89*(u_int *)arg1 = value;90return (0);91}9293static int94sysctl_nonzero_int_with_max(SYSCTL_HANDLER_ARGS)95{96u_int value;97int error;9899value = *(u_int *)arg1;100error = sysctl_int_with_max(oidp, &value, arg2, req);101if (error || req->newptr == NULL)102return (error);103if (value == 0)104return (EINVAL);105*(u_int *)arg1 = value;106return (0);107}108109static int110sysctl_proc_inject(SYSCTL_HANDLER_ARGS)111{112int error;113int i;114115i = 0;116error = sysctl_handle_int(oidp, &i, 0, req);117if (error)118return (error);119if (i != 0)120ecc_ei_inject(i);121return (0);122}123124static SYSCTL_NODE(_hw, OID_AUTO, error_injection,125CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,126"Hardware error injection");127static SYSCTL_NODE(_hw_error_injection, OID_AUTO, dram_ecc,128CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,129"DRAM ECC error injection");130SYSCTL_UINT(_hw_error_injection_dram_ecc, OID_AUTO, delay,131CTLTYPE_UINT | CTLFLAG_RW, &delay_ms, 0,132"Delay in milliseconds between error injections");133SYSCTL_PROC(_hw_error_injection_dram_ecc, OID_AUTO, quadrant,134CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &quadrant, QUADRANT_MASK,135sysctl_int_with_max, "IU",136"Index of 16-byte quadrant within 64-byte line where errors "137"should be injected");138SYSCTL_PROC(_hw_error_injection_dram_ecc, OID_AUTO, word_mask,139CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &word_mask, INJ_WORD_MASK,140sysctl_nonzero_int_with_max, "IU",141"9-bit mask of words where errors should be injected (8 data + 1 ECC)");142SYSCTL_PROC(_hw_error_injection_dram_ecc, OID_AUTO, bit_mask,143CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &bit_mask, INJ_VECTOR_MASK,144sysctl_nonzero_int_with_max, "IU",145"16-bit mask of bits within each selected word where errors "146"should be injected");147SYSCTL_PROC(_hw_error_injection_dram_ecc, OID_AUTO, inject,148CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, NULL, 0, sysctl_proc_inject, "I",149"Inject a number of errors according to configured parameters");150151static void152ecc_ei_inject_one(void *arg, size_t size)153{154volatile uint64_t *memory = arg;155uint32_t val;156int i;157158val = DRAM_ECC_SEL | (quadrant << QUADRANT_SHIFT);159pci_write_config(nbdev, NB_ARRAY_ADDR, val, 4);160161val = (word_mask << INJ_WORD_SHIFT) | DRAM_WR_REQ | bit_mask;162pci_write_config(nbdev, NB_ARRAY_PORT, val, 4);163164for (i = 0; i < size / sizeof(uint64_t); i++) {165memory[i] = 0;166val = pci_read_config(nbdev, NB_ARRAY_PORT, 4);167if ((val & DRAM_WR_REQ) == 0)168break;169}170for (i = 0; i < size / sizeof(uint64_t); i++)171memory[0] = memory[i];172}173174static void175ecc_ei_inject(int count)176{177void *memory;178int injected;179180KASSERT((quadrant & ~QUADRANT_MASK) == 0,181("quadrant value is outside of range: %u", quadrant));182KASSERT(word_mask != 0 && (word_mask & ~INJ_WORD_MASK) == 0,183("word mask value is outside of range: 0x%x", word_mask));184KASSERT(bit_mask != 0 && (bit_mask & ~INJ_VECTOR_MASK) == 0,185("bit mask value is outside of range: 0x%x", bit_mask));186187memory = kmem_alloc_attr(PAGE_SIZE, M_WAITOK, 0, ~0,188VM_MEMATTR_UNCACHEABLE);189190for (injected = 0; injected < count; injected++) {191ecc_ei_inject_one(memory, PAGE_SIZE);192if (delay_ms != 0 && injected != count - 1)193pause_sbt("ecc_ei_inject", delay_ms * SBT_1MS, 0, 0);194}195196kmem_free(memory, PAGE_SIZE);197}198199static int200ecc_ei_load(void)201{202uint32_t val;203204if ((cpu_vendor_id != CPU_VENDOR_AMD || CPUID_TO_FAMILY(cpu_id) < 0x10) &&205cpu_vendor_id != CPU_VENDOR_HYGON) {206printf("DRAM ECC error injection is not supported\n");207return (ENXIO);208}209nbdev = pci_find_bsf(0, 24, 3);210if (nbdev == NULL) {211printf("Couldn't find NB PCI device\n");212return (ENXIO);213}214val = pci_read_config(nbdev, NB_MCA_CFG, 4);215if ((val & DRAM_ECC_EN) == 0) {216printf("DRAM ECC is not supported or disabled\n");217return (ENXIO);218}219printf("DRAM ECC error injection support loaded\n");220return (0);221}222223static int224tsc_modevent(module_t mod __unused, int type, void *data __unused)225{226int error;227228error = 0;229switch (type) {230case MOD_LOAD:231error = ecc_ei_load();232break;233case MOD_UNLOAD:234case MOD_SHUTDOWN:235break;236default:237error = EOPNOTSUPP;238}239return (error);240}241242DEV_MODULE(tsc, tsc_modevent, NULL);243244245