0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #include <linux/auxiliary_bus.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/pci.h>
0015 #include <linux/slab.h>
0016 #include <linux/uaccess.h>
0017 #include <linux/overflow.h>
0018
0019 #include "../vsec.h"
0020 #include "class.h"
0021
0022
0023 #define CRASH_TYPE_OOBMSM 1
0024
0025
0026 #define CRASHLOG_FLAG_DISABLE BIT(28)
0027
0028
0029
0030
0031
0032
0033
0034
0035 #define CRASHLOG_FLAG_TRIGGER_CLEAR BIT(29)
0036 #define CRASHLOG_FLAG_TRIGGER_EXECUTE BIT(30)
0037 #define CRASHLOG_FLAG_TRIGGER_COMPLETE BIT(31)
0038 #define CRASHLOG_FLAG_TRIGGER_MASK GENMASK(31, 28)
0039
0040
0041 #define CONTROL_OFFSET 0x0
0042 #define GUID_OFFSET 0x4
0043 #define BASE_OFFSET 0x8
0044 #define SIZE_OFFSET 0xC
0045 #define GET_ACCESS(v) ((v) & GENMASK(3, 0))
0046 #define GET_TYPE(v) (((v) & GENMASK(7, 4)) >> 4)
0047 #define GET_VERSION(v) (((v) & GENMASK(19, 16)) >> 16)
0048
0049 #define GET_SIZE(v) ((v) * sizeof(u32))
0050
0051 struct crashlog_entry {
0052
0053 struct intel_pmt_entry entry;
0054 struct mutex control_mutex;
0055 };
0056
0057 struct pmt_crashlog_priv {
0058 int num_entries;
0059 struct crashlog_entry entry[];
0060 };
0061
0062
0063
0064
0065 static bool pmt_crashlog_complete(struct intel_pmt_entry *entry)
0066 {
0067 u32 control = readl(entry->disc_table + CONTROL_OFFSET);
0068
0069
0070 return !!(control & CRASHLOG_FLAG_TRIGGER_COMPLETE);
0071 }
0072
0073 static bool pmt_crashlog_disabled(struct intel_pmt_entry *entry)
0074 {
0075 u32 control = readl(entry->disc_table + CONTROL_OFFSET);
0076
0077
0078 return !!(control & CRASHLOG_FLAG_DISABLE);
0079 }
0080
0081 static bool pmt_crashlog_supported(struct intel_pmt_entry *entry)
0082 {
0083 u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET);
0084 u32 crash_type, version;
0085
0086 crash_type = GET_TYPE(discovery_header);
0087 version = GET_VERSION(discovery_header);
0088
0089
0090
0091
0092
0093 return crash_type == CRASH_TYPE_OOBMSM && version == 0;
0094 }
0095
0096 static void pmt_crashlog_set_disable(struct intel_pmt_entry *entry,
0097 bool disable)
0098 {
0099 u32 control = readl(entry->disc_table + CONTROL_OFFSET);
0100
0101
0102 control &= ~CRASHLOG_FLAG_TRIGGER_MASK;
0103
0104 if (disable)
0105 control |= CRASHLOG_FLAG_DISABLE;
0106 else
0107 control &= ~CRASHLOG_FLAG_DISABLE;
0108
0109 writel(control, entry->disc_table + CONTROL_OFFSET);
0110 }
0111
0112 static void pmt_crashlog_set_clear(struct intel_pmt_entry *entry)
0113 {
0114 u32 control = readl(entry->disc_table + CONTROL_OFFSET);
0115
0116 control &= ~CRASHLOG_FLAG_TRIGGER_MASK;
0117 control |= CRASHLOG_FLAG_TRIGGER_CLEAR;
0118
0119 writel(control, entry->disc_table + CONTROL_OFFSET);
0120 }
0121
0122 static void pmt_crashlog_set_execute(struct intel_pmt_entry *entry)
0123 {
0124 u32 control = readl(entry->disc_table + CONTROL_OFFSET);
0125
0126 control &= ~CRASHLOG_FLAG_TRIGGER_MASK;
0127 control |= CRASHLOG_FLAG_TRIGGER_EXECUTE;
0128
0129 writel(control, entry->disc_table + CONTROL_OFFSET);
0130 }
0131
0132
0133
0134
0135 static ssize_t
0136 enable_show(struct device *dev, struct device_attribute *attr, char *buf)
0137 {
0138 struct intel_pmt_entry *entry = dev_get_drvdata(dev);
0139 int enabled = !pmt_crashlog_disabled(entry);
0140
0141 return sprintf(buf, "%d\n", enabled);
0142 }
0143
0144 static ssize_t
0145 enable_store(struct device *dev, struct device_attribute *attr,
0146 const char *buf, size_t count)
0147 {
0148 struct crashlog_entry *entry;
0149 bool enabled;
0150 int result;
0151
0152 entry = dev_get_drvdata(dev);
0153
0154 result = kstrtobool(buf, &enabled);
0155 if (result)
0156 return result;
0157
0158 mutex_lock(&entry->control_mutex);
0159 pmt_crashlog_set_disable(&entry->entry, !enabled);
0160 mutex_unlock(&entry->control_mutex);
0161
0162 return count;
0163 }
0164 static DEVICE_ATTR_RW(enable);
0165
0166 static ssize_t
0167 trigger_show(struct device *dev, struct device_attribute *attr, char *buf)
0168 {
0169 struct intel_pmt_entry *entry;
0170 int trigger;
0171
0172 entry = dev_get_drvdata(dev);
0173 trigger = pmt_crashlog_complete(entry);
0174
0175 return sprintf(buf, "%d\n", trigger);
0176 }
0177
0178 static ssize_t
0179 trigger_store(struct device *dev, struct device_attribute *attr,
0180 const char *buf, size_t count)
0181 {
0182 struct crashlog_entry *entry;
0183 bool trigger;
0184 int result;
0185
0186 entry = dev_get_drvdata(dev);
0187
0188 result = kstrtobool(buf, &trigger);
0189 if (result)
0190 return result;
0191
0192 mutex_lock(&entry->control_mutex);
0193
0194 if (!trigger) {
0195 pmt_crashlog_set_clear(&entry->entry);
0196 } else if (pmt_crashlog_complete(&entry->entry)) {
0197
0198 result = -EEXIST;
0199 goto err;
0200 } else if (pmt_crashlog_disabled(&entry->entry)) {
0201
0202 result = -EBUSY;
0203 goto err;
0204 } else {
0205 pmt_crashlog_set_execute(&entry->entry);
0206 }
0207
0208 result = count;
0209 err:
0210 mutex_unlock(&entry->control_mutex);
0211 return result;
0212 }
0213 static DEVICE_ATTR_RW(trigger);
0214
0215 static struct attribute *pmt_crashlog_attrs[] = {
0216 &dev_attr_enable.attr,
0217 &dev_attr_trigger.attr,
0218 NULL
0219 };
0220
0221 static const struct attribute_group pmt_crashlog_group = {
0222 .attrs = pmt_crashlog_attrs,
0223 };
0224
0225 static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
0226 struct intel_pmt_header *header,
0227 struct device *dev)
0228 {
0229 void __iomem *disc_table = entry->disc_table;
0230 struct crashlog_entry *crashlog;
0231
0232 if (!pmt_crashlog_supported(entry))
0233 return 1;
0234
0235
0236 crashlog = container_of(entry, struct crashlog_entry, entry);
0237 mutex_init(&crashlog->control_mutex);
0238
0239 header->access_type = GET_ACCESS(readl(disc_table));
0240 header->guid = readl(disc_table + GUID_OFFSET);
0241 header->base_offset = readl(disc_table + BASE_OFFSET);
0242
0243
0244 header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET));
0245
0246 return 0;
0247 }
0248
0249 static DEFINE_XARRAY_ALLOC(crashlog_array);
0250 static struct intel_pmt_namespace pmt_crashlog_ns = {
0251 .name = "crashlog",
0252 .xa = &crashlog_array,
0253 .attr_grp = &pmt_crashlog_group,
0254 .pmt_header_decode = pmt_crashlog_header_decode,
0255 };
0256
0257
0258
0259
0260 static void pmt_crashlog_remove(struct auxiliary_device *auxdev)
0261 {
0262 struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev);
0263 int i;
0264
0265 for (i = 0; i < priv->num_entries; i++)
0266 intel_pmt_dev_destroy(&priv->entry[i].entry, &pmt_crashlog_ns);
0267 }
0268
0269 static int pmt_crashlog_probe(struct auxiliary_device *auxdev,
0270 const struct auxiliary_device_id *id)
0271 {
0272 struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev);
0273 struct pmt_crashlog_priv *priv;
0274 size_t size;
0275 int i, ret;
0276
0277 size = struct_size(priv, entry, intel_vsec_dev->num_resources);
0278 priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL);
0279 if (!priv)
0280 return -ENOMEM;
0281
0282 auxiliary_set_drvdata(auxdev, priv);
0283
0284 for (i = 0; i < intel_vsec_dev->num_resources; i++) {
0285 struct intel_pmt_entry *entry = &priv->entry[priv->num_entries].entry;
0286
0287 ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, intel_vsec_dev, i);
0288 if (ret < 0)
0289 goto abort_probe;
0290 if (ret)
0291 continue;
0292
0293 priv->num_entries++;
0294 }
0295
0296 return 0;
0297 abort_probe:
0298 pmt_crashlog_remove(auxdev);
0299 return ret;
0300 }
0301
0302 static const struct auxiliary_device_id pmt_crashlog_id_table[] = {
0303 { .name = "intel_vsec.crashlog" },
0304 {}
0305 };
0306 MODULE_DEVICE_TABLE(auxiliary, pmt_crashlog_id_table);
0307
0308 static struct auxiliary_driver pmt_crashlog_aux_driver = {
0309 .id_table = pmt_crashlog_id_table,
0310 .remove = pmt_crashlog_remove,
0311 .probe = pmt_crashlog_probe,
0312 };
0313
0314 static int __init pmt_crashlog_init(void)
0315 {
0316 return auxiliary_driver_register(&pmt_crashlog_aux_driver);
0317 }
0318
0319 static void __exit pmt_crashlog_exit(void)
0320 {
0321 auxiliary_driver_unregister(&pmt_crashlog_aux_driver);
0322 xa_destroy(&crashlog_array);
0323 }
0324
0325 module_init(pmt_crashlog_init);
0326 module_exit(pmt_crashlog_exit);
0327
0328 MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>");
0329 MODULE_DESCRIPTION("Intel PMT Crashlog driver");
0330 MODULE_LICENSE("GPL v2");