1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Intel Platform Monitoring Technology Crashlog driver 4 * 5 * Copyright (c) 2020, Intel Corporation. 6 * All Rights Reserved. 7 * 8 * Author: "Alexander Duyck" <alexander.h.duyck@linux.intel.com> 9 */ 10 11 #include <linux/auxiliary_bus.h> 12 #include <linux/cleanup.h> 13 #include <linux/intel_vsec.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <linux/mutex.h> 17 #include <linux/pci.h> 18 #include <linux/slab.h> 19 #include <linux/uaccess.h> 20 #include <linux/overflow.h> 21 22 #include "class.h" 23 24 /* Crashlog discovery header types */ 25 #define CRASH_TYPE_OOBMSM 1 26 27 /* Control Flags */ 28 #define CRASHLOG_FLAG_DISABLE BIT(28) 29 30 /* 31 * Bits 29 and 30 control the state of bit 31. 32 * 33 * Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured. 34 * Bit 30 will immediately trigger a crashlog to be generated, setting bit 31. 35 * Bit 31 is the read-only status with a 1 indicating log is complete. 36 */ 37 #define CRASHLOG_FLAG_TRIGGER_CLEAR BIT(29) 38 #define CRASHLOG_FLAG_TRIGGER_EXECUTE BIT(30) 39 #define CRASHLOG_FLAG_TRIGGER_COMPLETE BIT(31) 40 #define CRASHLOG_FLAG_TRIGGER_MASK GENMASK(31, 28) 41 42 /* Crashlog Discovery Header */ 43 #define CONTROL_OFFSET 0x0 44 #define GUID_OFFSET 0x4 45 #define BASE_OFFSET 0x8 46 #define SIZE_OFFSET 0xC 47 #define GET_ACCESS(v) ((v) & GENMASK(3, 0)) 48 #define GET_TYPE(v) (((v) & GENMASK(7, 4)) >> 4) 49 #define GET_VERSION(v) (((v) & GENMASK(19, 16)) >> 16) 50 /* size is in bytes */ 51 #define GET_SIZE(v) ((v) * sizeof(u32)) 52 53 struct crashlog_entry { 54 /* entry must be first member of struct */ 55 struct intel_pmt_entry entry; 56 struct mutex control_mutex; 57 }; 58 59 struct pmt_crashlog_priv { 60 int num_entries; 61 struct crashlog_entry entry[]; 62 }; 63 64 /* 65 * I/O 66 */ 67 68 /* Read, modify, write the control register, setting or clearing @bit based on @set */ 69 static void pmt_crashlog_rmw(struct crashlog_entry *crashlog, u32 bit, bool set) 70 { 71 struct intel_pmt_entry *entry = &crashlog->entry; 72 u32 reg = readl(entry->disc_table + CONTROL_OFFSET); 73 74 reg &= ~CRASHLOG_FLAG_TRIGGER_MASK; 75 76 if (set) 77 reg |= bit; 78 else 79 reg &= ~bit; 80 81 writel(reg, entry->disc_table + CONTROL_OFFSET); 82 } 83 84 /* Read the status register and see if the specified @bit is set */ 85 static bool pmt_crashlog_rc(struct crashlog_entry *crashlog, u32 bit) 86 { 87 struct intel_pmt_entry *entry = &crashlog->entry; 88 u32 reg = readl(entry->disc_table + CONTROL_OFFSET); 89 90 return !!(reg & bit); 91 } 92 93 static bool pmt_crashlog_complete(struct crashlog_entry *crashlog) 94 { 95 /* return current value of the crashlog complete flag */ 96 return pmt_crashlog_rc(crashlog, CRASHLOG_FLAG_TRIGGER_COMPLETE); 97 } 98 99 static bool pmt_crashlog_disabled(struct crashlog_entry *crashlog) 100 { 101 /* return current value of the crashlog disabled flag */ 102 return pmt_crashlog_rc(crashlog, CRASHLOG_FLAG_DISABLE); 103 } 104 105 static bool pmt_crashlog_supported(struct intel_pmt_entry *entry) 106 { 107 u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET); 108 u32 crash_type, version; 109 110 crash_type = GET_TYPE(discovery_header); 111 version = GET_VERSION(discovery_header); 112 113 /* 114 * Currently we only recognize OOBMSM version 0 devices. 115 * We can ignore all other crashlog devices in the system. 116 */ 117 return crash_type == CRASH_TYPE_OOBMSM && version == 0; 118 } 119 120 static void pmt_crashlog_set_disable(struct crashlog_entry *crashlog, 121 bool disable) 122 { 123 pmt_crashlog_rmw(crashlog, CRASHLOG_FLAG_DISABLE, disable); 124 } 125 126 static void pmt_crashlog_set_clear(struct crashlog_entry *crashlog) 127 { 128 pmt_crashlog_rmw(crashlog, CRASHLOG_FLAG_TRIGGER_CLEAR, true); 129 } 130 131 static void pmt_crashlog_set_execute(struct crashlog_entry *crashlog) 132 { 133 pmt_crashlog_rmw(crashlog, CRASHLOG_FLAG_TRIGGER_EXECUTE, true); 134 } 135 136 /* 137 * sysfs 138 */ 139 static ssize_t 140 enable_show(struct device *dev, struct device_attribute *attr, char *buf) 141 { 142 struct crashlog_entry *crashlog = dev_get_drvdata(dev); 143 bool enabled = !pmt_crashlog_disabled(crashlog); 144 145 return sprintf(buf, "%d\n", enabled); 146 } 147 148 static ssize_t 149 enable_store(struct device *dev, struct device_attribute *attr, 150 const char *buf, size_t count) 151 { 152 struct crashlog_entry *crashlog; 153 bool enabled; 154 int result; 155 156 crashlog = dev_get_drvdata(dev); 157 158 result = kstrtobool(buf, &enabled); 159 if (result) 160 return result; 161 162 guard(mutex)(&crashlog->control_mutex); 163 164 pmt_crashlog_set_disable(crashlog, !enabled); 165 166 return count; 167 } 168 static DEVICE_ATTR_RW(enable); 169 170 static ssize_t 171 trigger_show(struct device *dev, struct device_attribute *attr, char *buf) 172 { 173 struct crashlog_entry *crashlog; 174 bool trigger; 175 176 crashlog = dev_get_drvdata(dev); 177 trigger = pmt_crashlog_complete(crashlog); 178 179 return sprintf(buf, "%d\n", trigger); 180 } 181 182 static ssize_t 183 trigger_store(struct device *dev, struct device_attribute *attr, 184 const char *buf, size_t count) 185 { 186 struct crashlog_entry *crashlog; 187 bool trigger; 188 int result; 189 190 crashlog = dev_get_drvdata(dev); 191 192 result = kstrtobool(buf, &trigger); 193 if (result) 194 return result; 195 196 guard(mutex)(&crashlog->control_mutex); 197 198 /* if device is currently disabled, return busy */ 199 if (pmt_crashlog_disabled(crashlog)) 200 return -EBUSY; 201 202 if (!trigger) { 203 pmt_crashlog_set_clear(crashlog); 204 return count; 205 } 206 207 /* we cannot trigger a new crash if one is still pending */ 208 if (pmt_crashlog_complete(crashlog)) 209 return -EEXIST; 210 211 pmt_crashlog_set_execute(crashlog); 212 213 return count; 214 } 215 static DEVICE_ATTR_RW(trigger); 216 217 static struct attribute *pmt_crashlog_attrs[] = { 218 &dev_attr_enable.attr, 219 &dev_attr_trigger.attr, 220 NULL 221 }; 222 223 static const struct attribute_group pmt_crashlog_group = { 224 .attrs = pmt_crashlog_attrs, 225 }; 226 227 static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, 228 struct device *dev) 229 { 230 void __iomem *disc_table = entry->disc_table; 231 struct intel_pmt_header *header = &entry->header; 232 struct crashlog_entry *crashlog; 233 234 if (!pmt_crashlog_supported(entry)) 235 return 1; 236 237 /* initialize control mutex */ 238 crashlog = container_of(entry, struct crashlog_entry, entry); 239 mutex_init(&crashlog->control_mutex); 240 241 header->access_type = GET_ACCESS(readl(disc_table)); 242 header->guid = readl(disc_table + GUID_OFFSET); 243 header->base_offset = readl(disc_table + BASE_OFFSET); 244 245 /* Size is measured in DWORDS, but accessor returns bytes */ 246 header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET)); 247 248 entry->attr_grp = &pmt_crashlog_group; 249 250 return 0; 251 } 252 253 static DEFINE_XARRAY_ALLOC(crashlog_array); 254 static struct intel_pmt_namespace pmt_crashlog_ns = { 255 .name = "crashlog", 256 .xa = &crashlog_array, 257 .pmt_header_decode = pmt_crashlog_header_decode, 258 }; 259 260 /* 261 * initialization 262 */ 263 static void pmt_crashlog_remove(struct auxiliary_device *auxdev) 264 { 265 struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev); 266 int i; 267 268 for (i = 0; i < priv->num_entries; i++) { 269 struct crashlog_entry *crashlog = &priv->entry[i]; 270 271 intel_pmt_dev_destroy(&crashlog->entry, &pmt_crashlog_ns); 272 mutex_destroy(&crashlog->control_mutex); 273 } 274 } 275 276 static int pmt_crashlog_probe(struct auxiliary_device *auxdev, 277 const struct auxiliary_device_id *id) 278 { 279 struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev); 280 struct pmt_crashlog_priv *priv; 281 size_t size; 282 int i, ret; 283 284 size = struct_size(priv, entry, intel_vsec_dev->num_resources); 285 priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); 286 if (!priv) 287 return -ENOMEM; 288 289 auxiliary_set_drvdata(auxdev, priv); 290 291 for (i = 0; i < intel_vsec_dev->num_resources; i++) { 292 struct intel_pmt_entry *entry = &priv->entry[priv->num_entries].entry; 293 294 ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, intel_vsec_dev, i); 295 if (ret < 0) 296 goto abort_probe; 297 if (ret) 298 continue; 299 300 priv->num_entries++; 301 } 302 303 return 0; 304 abort_probe: 305 pmt_crashlog_remove(auxdev); 306 return ret; 307 } 308 309 static const struct auxiliary_device_id pmt_crashlog_id_table[] = { 310 { .name = "intel_vsec.crashlog" }, 311 {} 312 }; 313 MODULE_DEVICE_TABLE(auxiliary, pmt_crashlog_id_table); 314 315 static struct auxiliary_driver pmt_crashlog_aux_driver = { 316 .id_table = pmt_crashlog_id_table, 317 .remove = pmt_crashlog_remove, 318 .probe = pmt_crashlog_probe, 319 }; 320 321 static int __init pmt_crashlog_init(void) 322 { 323 return auxiliary_driver_register(&pmt_crashlog_aux_driver); 324 } 325 326 static void __exit pmt_crashlog_exit(void) 327 { 328 auxiliary_driver_unregister(&pmt_crashlog_aux_driver); 329 xa_destroy(&crashlog_array); 330 } 331 332 module_init(pmt_crashlog_init); 333 module_exit(pmt_crashlog_exit); 334 335 MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>"); 336 MODULE_DESCRIPTION("Intel PMT Crashlog driver"); 337 MODULE_LICENSE("GPL v2"); 338 MODULE_IMPORT_NS("INTEL_PMT"); 339