Greg Kroah-Hartman | b244131 | 2017-11-01 15:07:57 +0100 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 2 | #include "amd64_edac.h" |
| 3 | |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 4 | static ssize_t amd64_inject_section_show(struct device *dev, |
| 5 | struct device_attribute *mattr, |
| 6 | char *buf) |
Borislav Petkov | 94baaee | 2009-09-24 11:05:30 +0200 | [diff] [blame] | 7 | { |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 8 | struct mem_ctl_info *mci = to_mci(dev); |
Borislav Petkov | 94baaee | 2009-09-24 11:05:30 +0200 | [diff] [blame] | 9 | struct amd64_pvt *pvt = mci->pvt_info; |
| 10 | return sprintf(buf, "0x%x\n", pvt->injection.section); |
| 11 | } |
| 12 | |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 13 | /* |
| 14 | * store error injection section value which refers to one of 4 16-byte sections |
| 15 | * within a 64-byte cacheline |
| 16 | * |
| 17 | * range: 0..3 |
| 18 | */ |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 19 | static ssize_t amd64_inject_section_store(struct device *dev, |
| 20 | struct device_attribute *mattr, |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 21 | const char *data, size_t count) |
| 22 | { |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 23 | struct mem_ctl_info *mci = to_mci(dev); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 24 | struct amd64_pvt *pvt = mci->pvt_info; |
| 25 | unsigned long value; |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 26 | int ret; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 27 | |
Jingoo Han | c7f62fc | 2013-06-01 16:08:22 +0900 | [diff] [blame] | 28 | ret = kstrtoul(data, 10, &value); |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 29 | if (ret < 0) |
| 30 | return ret; |
Borislav Petkov | 94baaee | 2009-09-24 11:05:30 +0200 | [diff] [blame] | 31 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 32 | if (value > 3) { |
| 33 | amd64_warn("%s: invalid section 0x%lx\n", __func__, value); |
| 34 | return -EINVAL; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 35 | } |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 36 | |
| 37 | pvt->injection.section = (u32) value; |
| 38 | return count; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 39 | } |
| 40 | |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 41 | static ssize_t amd64_inject_word_show(struct device *dev, |
| 42 | struct device_attribute *mattr, |
| 43 | char *buf) |
Borislav Petkov | 94baaee | 2009-09-24 11:05:30 +0200 | [diff] [blame] | 44 | { |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 45 | struct mem_ctl_info *mci = to_mci(dev); |
Borislav Petkov | 94baaee | 2009-09-24 11:05:30 +0200 | [diff] [blame] | 46 | struct amd64_pvt *pvt = mci->pvt_info; |
| 47 | return sprintf(buf, "0x%x\n", pvt->injection.word); |
| 48 | } |
| 49 | |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 50 | /* |
| 51 | * store error injection word value which refers to one of 9 16-bit word of the |
| 52 | * 16-byte (128-bit + ECC bits) section |
| 53 | * |
| 54 | * range: 0..8 |
| 55 | */ |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 56 | static ssize_t amd64_inject_word_store(struct device *dev, |
| 57 | struct device_attribute *mattr, |
| 58 | const char *data, size_t count) |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 59 | { |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 60 | struct mem_ctl_info *mci = to_mci(dev); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 61 | struct amd64_pvt *pvt = mci->pvt_info; |
| 62 | unsigned long value; |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 63 | int ret; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 64 | |
Jingoo Han | c7f62fc | 2013-06-01 16:08:22 +0900 | [diff] [blame] | 65 | ret = kstrtoul(data, 10, &value); |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 66 | if (ret < 0) |
| 67 | return ret; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 68 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 69 | if (value > 8) { |
| 70 | amd64_warn("%s: invalid word 0x%lx\n", __func__, value); |
| 71 | return -EINVAL; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 72 | } |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 73 | |
| 74 | pvt->injection.word = (u32) value; |
| 75 | return count; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 76 | } |
| 77 | |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 78 | static ssize_t amd64_inject_ecc_vector_show(struct device *dev, |
| 79 | struct device_attribute *mattr, |
| 80 | char *buf) |
Borislav Petkov | 94baaee | 2009-09-24 11:05:30 +0200 | [diff] [blame] | 81 | { |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 82 | struct mem_ctl_info *mci = to_mci(dev); |
Borislav Petkov | 94baaee | 2009-09-24 11:05:30 +0200 | [diff] [blame] | 83 | struct amd64_pvt *pvt = mci->pvt_info; |
| 84 | return sprintf(buf, "0x%x\n", pvt->injection.bit_map); |
| 85 | } |
| 86 | |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 87 | /* |
| 88 | * store 16 bit error injection vector which enables injecting errors to the |
| 89 | * corresponding bit within the error injection word above. When used during a |
| 90 | * DRAM ECC read, it holds the contents of the of the DRAM ECC bits. |
| 91 | */ |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 92 | static ssize_t amd64_inject_ecc_vector_store(struct device *dev, |
| 93 | struct device_attribute *mattr, |
| 94 | const char *data, size_t count) |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 95 | { |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 96 | struct mem_ctl_info *mci = to_mci(dev); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 97 | struct amd64_pvt *pvt = mci->pvt_info; |
| 98 | unsigned long value; |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 99 | int ret; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 100 | |
Jingoo Han | c7f62fc | 2013-06-01 16:08:22 +0900 | [diff] [blame] | 101 | ret = kstrtoul(data, 16, &value); |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 102 | if (ret < 0) |
| 103 | return ret; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 104 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 105 | if (value & 0xFFFF0000) { |
| 106 | amd64_warn("%s: invalid EccVector: 0x%lx\n", __func__, value); |
| 107 | return -EINVAL; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 108 | } |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 109 | |
| 110 | pvt->injection.bit_map = (u32) value; |
| 111 | return count; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 112 | } |
| 113 | |
| 114 | /* |
| 115 | * Do a DRAM ECC read. Assemble staged values in the pvt area, format into |
| 116 | * fields needed by the injection registers and read the NB Array Data Port. |
| 117 | */ |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 118 | static ssize_t amd64_inject_read_store(struct device *dev, |
| 119 | struct device_attribute *mattr, |
| 120 | const char *data, size_t count) |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 121 | { |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 122 | struct mem_ctl_info *mci = to_mci(dev); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 123 | struct amd64_pvt *pvt = mci->pvt_info; |
| 124 | unsigned long value; |
| 125 | u32 section, word_bits; |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 126 | int ret; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 127 | |
Jingoo Han | c7f62fc | 2013-06-01 16:08:22 +0900 | [diff] [blame] | 128 | ret = kstrtoul(data, 10, &value); |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 129 | if (ret < 0) |
| 130 | return ret; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 131 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 132 | /* Form value to choose 16-byte section of cacheline */ |
| 133 | section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 134 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 135 | amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 136 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 137 | word_bits = SET_NB_DRAM_INJECTION_READ(pvt->injection); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 138 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 139 | /* Issue 'word' and 'bit' along with the READ request */ |
| 140 | amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 141 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 142 | edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); |
| 143 | |
| 144 | return count; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 145 | } |
| 146 | |
| 147 | /* |
| 148 | * Do a DRAM ECC write. Assemble staged values in the pvt area and format into |
| 149 | * fields needed by the injection registers. |
| 150 | */ |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 151 | static ssize_t amd64_inject_write_store(struct device *dev, |
| 152 | struct device_attribute *mattr, |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 153 | const char *data, size_t count) |
| 154 | { |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 155 | struct mem_ctl_info *mci = to_mci(dev); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 156 | struct amd64_pvt *pvt = mci->pvt_info; |
Borislav Petkov | 66fed2d | 2012-08-09 18:41:07 +0200 | [diff] [blame] | 157 | u32 section, word_bits, tmp; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 158 | unsigned long value; |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 159 | int ret; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 160 | |
Jingoo Han | c7f62fc | 2013-06-01 16:08:22 +0900 | [diff] [blame] | 161 | ret = kstrtoul(data, 10, &value); |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 162 | if (ret < 0) |
| 163 | return ret; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 164 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 165 | /* Form value to choose 16-byte section of cacheline */ |
| 166 | section = F10_NB_ARRAY_DRAM | SET_NB_ARRAY_ADDR(pvt->injection.section); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 167 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 168 | amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_ADDR, section); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 169 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 170 | word_bits = SET_NB_DRAM_INJECTION_WRITE(pvt->injection); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 171 | |
Borislav Petkov | 66fed2d | 2012-08-09 18:41:07 +0200 | [diff] [blame] | 172 | pr_notice_once("Don't forget to decrease MCE polling interval in\n" |
| 173 | "/sys/bus/machinecheck/devices/machinecheck<CPUNUM>/check_interval\n" |
| 174 | "so that you can get the error report faster.\n"); |
| 175 | |
| 176 | on_each_cpu(disable_caches, NULL, 1); |
| 177 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 178 | /* Issue 'word' and 'bit' along with the READ request */ |
| 179 | amd64_write_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, word_bits); |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 180 | |
Borislav Petkov | 66fed2d | 2012-08-09 18:41:07 +0200 | [diff] [blame] | 181 | retry: |
| 182 | /* wait until injection happens */ |
| 183 | amd64_read_pci_cfg(pvt->F3, F10_NB_ARRAY_DATA, &tmp); |
| 184 | if (tmp & F10_NB_ARR_ECC_WR_REQ) { |
| 185 | cpu_relax(); |
| 186 | goto retry; |
| 187 | } |
| 188 | |
| 189 | on_each_cpu(enable_caches, NULL, 1); |
| 190 | |
Borislav Petkov | 6e71a87 | 2012-08-09 18:23:53 +0200 | [diff] [blame] | 191 | edac_dbg(0, "section=0x%x word_bits=0x%x\n", section, word_bits); |
| 192 | |
| 193 | return count; |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 194 | } |
| 195 | |
| 196 | /* |
| 197 | * update NUM_INJ_ATTRS in case you add new members |
| 198 | */ |
Doug Thompson | eb91969 | 2009-05-05 20:07:11 +0200 | [diff] [blame] | 199 | |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 200 | static DEVICE_ATTR(inject_section, S_IRUGO | S_IWUSR, |
| 201 | amd64_inject_section_show, amd64_inject_section_store); |
| 202 | static DEVICE_ATTR(inject_word, S_IRUGO | S_IWUSR, |
| 203 | amd64_inject_word_show, amd64_inject_word_store); |
| 204 | static DEVICE_ATTR(inject_ecc_vector, S_IRUGO | S_IWUSR, |
| 205 | amd64_inject_ecc_vector_show, amd64_inject_ecc_vector_store); |
Borislav Petkov | bbb013b | 2013-05-12 13:03:56 +0200 | [diff] [blame] | 206 | static DEVICE_ATTR(inject_write, S_IWUSR, |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 207 | NULL, amd64_inject_write_store); |
Borislav Petkov | bbb013b | 2013-05-12 13:03:56 +0200 | [diff] [blame] | 208 | static DEVICE_ATTR(inject_read, S_IWUSR, |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 209 | NULL, amd64_inject_read_store); |
| 210 | |
Takashi Iwai | e339f1e | 2015-02-04 11:48:53 +0100 | [diff] [blame] | 211 | static struct attribute *amd64_edac_inj_attrs[] = { |
| 212 | &dev_attr_inject_section.attr, |
| 213 | &dev_attr_inject_word.attr, |
| 214 | &dev_attr_inject_ecc_vector.attr, |
| 215 | &dev_attr_inject_write.attr, |
| 216 | &dev_attr_inject_read.attr, |
| 217 | NULL |
| 218 | }; |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 219 | |
Takashi Iwai | e339f1e | 2015-02-04 11:48:53 +0100 | [diff] [blame] | 220 | static umode_t amd64_edac_inj_is_visible(struct kobject *kobj, |
| 221 | struct attribute *attr, int idx) |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 222 | { |
Takashi Iwai | e339f1e | 2015-02-04 11:48:53 +0100 | [diff] [blame] | 223 | struct device *dev = kobj_to_dev(kobj); |
| 224 | struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); |
| 225 | struct amd64_pvt *pvt = mci->pvt_info; |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 226 | |
Takashi Iwai | e339f1e | 2015-02-04 11:48:53 +0100 | [diff] [blame] | 227 | if (pvt->fam < 0x10) |
| 228 | return 0; |
| 229 | return attr->mode; |
Mauro Carvalho Chehab | c560875 | 2012-03-21 14:00:44 -0300 | [diff] [blame] | 230 | } |
| 231 | |
Takashi Iwai | e339f1e | 2015-02-04 11:48:53 +0100 | [diff] [blame] | 232 | const struct attribute_group amd64_edac_inj_group = { |
| 233 | .attrs = amd64_edac_inj_attrs, |
| 234 | .is_visible = amd64_edac_inj_is_visible, |
| 235 | }; |