crashlog.c 8.1 KB

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