File Index
Files and directories
Sign in
FILE

crashlog.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/intel/pmt/crashlog.c C
  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/* Crashlog Discovery Header */
 28#define CONTROL_OFFSET		0x0
 29#define GUID_OFFSET		0x4
 30#define BASE_OFFSET		0x8
 31#define SIZE_OFFSET		0xC
 32#define GET_ACCESS(v)		((v) & GENMASK(3, 0))
 33#define GET_TYPE(v)		(((v) & GENMASK(7, 4)) >> 4)
 34#define GET_VERSION(v)		(((v) & GENMASK(19, 16)) >> 16)
 35/* size is in bytes */
 36#define GET_SIZE(v)		((v) * sizeof(u32))
 37
 38/*
 39 * Type 1 Version 0
 40 * status and control registers are combined.
 41 *
 42 * Bits 29 and 30 control the state of bit 31.
 43 * Bit 29 will clear bit 31, if set, allowing a new crashlog to be captured.
 44 * Bit 30 will immediately trigger a crashlog to be generated, setting bit 31.
 45 * Bit 31 is the read-only status with a 1 indicating log is complete.
 46 */
 47#define TYPE1_VER0_STATUS_OFFSET	0x00
 48#define TYPE1_VER0_CONTROL_OFFSET	0x00
 49
 50#define TYPE1_VER0_DISABLE		BIT(28)
 51#define TYPE1_VER0_CLEAR		BIT(29)
 52#define TYPE1_VER0_EXECUTE		BIT(30)
 53#define TYPE1_VER0_COMPLETE		BIT(31)
 54#define TYPE1_VER0_TRIGGER_MASK		GENMASK(31, 28)
 55
 56/*
 57 * Type 1 Version 2
 58 * status and control are different registers
 59 */
 60#define TYPE1_VER2_STATUS_OFFSET	0x00
 61#define TYPE1_VER2_CONTROL_OFFSET	0x14
 62
 63/* status register */
 64#define TYPE1_VER2_CLEAR_SUPPORT	BIT(20)
 65#define TYPE1_VER2_REARMED		BIT(25)
 66#define TYPE1_VER2_ERROR		BIT(26)
 67#define TYPE1_VER2_CONSUMED		BIT(27)
 68#define TYPE1_VER2_DISABLED		BIT(28)
 69#define TYPE1_VER2_CLEARED		BIT(29)
 70#define TYPE1_VER2_IN_PROGRESS		BIT(30)
 71#define TYPE1_VER2_COMPLETE		BIT(31)
 72
 73/* control register */
 74#define TYPE1_VER2_CONSUME		BIT(25)
 75#define TYPE1_VER2_REARM		BIT(28)
 76#define TYPE1_VER2_EXECUTE		BIT(29)
 77#define TYPE1_VER2_CLEAR		BIT(30)
 78#define TYPE1_VER2_DISABLE		BIT(31)
 79#define TYPE1_VER2_TRIGGER_MASK	\
 80	(TYPE1_VER2_EXECUTE | TYPE1_VER2_CLEAR | TYPE1_VER2_DISABLE)
 81
 82/* After offset, order alphabetically, not bit ordered */
 83struct crashlog_status {
 84	u32 offset;
 85	u32 clear_supported;
 86	u32 cleared;
 87	u32 complete;
 88	u32 consumed;
 89	u32 disabled;
 90	u32 error;
 91	u32 in_progress;
 92	u32 rearmed;
 93};
 94
 95struct crashlog_control {
 96	u32 offset;
 97	u32 trigger_mask;
 98	u32 clear;
 99	u32 consume;
100	u32 disable;
101	u32 manual;
102	u32 rearm;
103};
104
105struct crashlog_info {
106	const struct crashlog_status status;
107	const struct crashlog_control control;
108	const struct attribute_group *attr_grp;
109};
110
111struct crashlog_entry {
112	/* entry must be first member of struct */
113	struct intel_pmt_entry		entry;
114	struct mutex			control_mutex;
115	const struct crashlog_info	*info;
116};
117
118struct pmt_crashlog_priv {
119	int			num_entries;
120	struct crashlog_entry	entry[];
121};
122
123/*
124 * I/O
125 */
126
127/* Read, modify, write the control register, setting or clearing @bit based on @set */
128static void pmt_crashlog_rmw(struct crashlog_entry *crashlog, u32 bit, bool set)
129{
130	const struct crashlog_control *control = &crashlog->info->control;
131	struct intel_pmt_entry *entry = &crashlog->entry;
132	u32 reg = readl(entry->disc_table + control->offset);
133
134	reg &= ~control->trigger_mask;
135
136	if (set)
137		reg |= bit;
138	else
139		reg &= ~bit;
140
141	writel(reg, entry->disc_table + control->offset);
142}
143
144/* Read the status register and see if the specified @bit is set */
145static bool pmt_crashlog_rc(struct crashlog_entry *crashlog, u32 bit)
146{
147	const struct crashlog_status *status = &crashlog->info->status;
148	u32 reg = readl(crashlog->entry.disc_table + status->offset);
149
150	return !!(reg & bit);
151}
152
153static bool pmt_crashlog_complete(struct crashlog_entry *crashlog)
154{
155	/* return current value of the crashlog complete flag */
156	return pmt_crashlog_rc(crashlog, crashlog->info->status.complete);
157}
158
159static bool pmt_crashlog_disabled(struct crashlog_entry *crashlog)
160{
161	/* return current value of the crashlog disabled flag */
162	return pmt_crashlog_rc(crashlog, crashlog->info->status.disabled);
163}
164
165static bool pmt_crashlog_supported(struct intel_pmt_entry *entry, u32 *crash_type, u32 *version)
166{
167	u32 discovery_header = readl(entry->disc_table + CONTROL_OFFSET);
168
169	*crash_type = GET_TYPE(discovery_header);
170	*version = GET_VERSION(discovery_header);
171
172	/*
173	 * Currently we only recognize OOBMSM (type 1) and version 0 or 2
174	 * devices.
175	 *
176	 * Ignore all other crashlog devices in the system.
177	 */
178	if (*crash_type == CRASH_TYPE_OOBMSM && (*version == 0 || *version == 2))
179		return true;
180
181	return false;
182}
183
184static void pmt_crashlog_set_disable(struct crashlog_entry *crashlog,
185				     bool disable)
186{
187	pmt_crashlog_rmw(crashlog, crashlog->info->control.disable, disable);
188}
189
190static void pmt_crashlog_set_clear(struct crashlog_entry *crashlog)
191{
192	pmt_crashlog_rmw(crashlog, crashlog->info->control.clear, true);
193}
194
195static void pmt_crashlog_set_execute(struct crashlog_entry *crashlog)
196{
197	pmt_crashlog_rmw(crashlog, crashlog->info->control.manual, true);
198}
199
200static bool pmt_crashlog_cleared(struct crashlog_entry *crashlog)
201{
202	return pmt_crashlog_rc(crashlog, crashlog->info->status.cleared);
203}
204
205static bool pmt_crashlog_consumed(struct crashlog_entry *crashlog)
206{
207	return pmt_crashlog_rc(crashlog, crashlog->info->status.consumed);
208}
209
210static void pmt_crashlog_set_consumed(struct crashlog_entry *crashlog)
211{
212	pmt_crashlog_rmw(crashlog, crashlog->info->control.consume, true);
213}
214
215static bool pmt_crashlog_error(struct crashlog_entry *crashlog)
216{
217	return pmt_crashlog_rc(crashlog, crashlog->info->status.error);
218}
219
220static bool pmt_crashlog_rearm(struct crashlog_entry *crashlog)
221{
222	return pmt_crashlog_rc(crashlog, crashlog->info->status.rearmed);
223}
224
225static void pmt_crashlog_set_rearm(struct crashlog_entry *crashlog)
226{
227	pmt_crashlog_rmw(crashlog, crashlog->info->control.rearm, true);
228}
229
230/*
231 * sysfs
232 */
233static ssize_t
234clear_show(struct device *dev, struct device_attribute *attr, char *buf)
235{
236	struct crashlog_entry *crashlog = dev_get_drvdata(dev);
237	bool cleared = pmt_crashlog_cleared(crashlog);
238
239	return sysfs_emit(buf, "%d\n", cleared);
240}
241
242static ssize_t
243clear_store(struct device *dev, struct device_attribute *attr,
244	    const char *buf, size_t count)
245{
246	struct crashlog_entry *crashlog;
247	bool clear;
248	int result;
249
250	crashlog = dev_get_drvdata(dev);
251
252	result = kstrtobool(buf, &clear);
253	if (result)
254		return result;
255
256	/* set bit only */
257	if (!clear)
258		return -EINVAL;
259
260	guard(mutex)(&crashlog->control_mutex);
261
262	pmt_crashlog_set_clear(crashlog);
263
264	return count;
265}
266static DEVICE_ATTR_RW(clear);
267
268static ssize_t
269consumed_show(struct device *dev, struct device_attribute *attr, char *buf)
270{
271	struct crashlog_entry *crashlog = dev_get_drvdata(dev);
272	bool consumed = pmt_crashlog_consumed(crashlog);
273
274	return sysfs_emit(buf, "%d\n", consumed);
275}
276
277static ssize_t
278consumed_store(struct device *dev, struct device_attribute *attr, const char *buf,
279	       size_t count)
280{
281	struct crashlog_entry *crashlog;
282	bool consumed;
283	int result;
284
285	crashlog = dev_get_drvdata(dev);
286
287	result = kstrtobool(buf, &consumed);
288	if (result)
289		return result;
290
291	/* set bit only */
292	if (!consumed)
293		return -EINVAL;
294
295	guard(mutex)(&crashlog->control_mutex);
296
297	if (pmt_crashlog_disabled(crashlog))
298		return -EBUSY;
299
300	if (!pmt_crashlog_complete(crashlog))
301		return -EEXIST;
302
303	pmt_crashlog_set_consumed(crashlog);
304
305	return count;
306}
307static DEVICE_ATTR_RW(consumed);
308
309static ssize_t
310enable_show(struct device *dev, struct device_attribute *attr, char *buf)
311{
312	struct crashlog_entry *crashlog = dev_get_drvdata(dev);
313	bool enabled = !pmt_crashlog_disabled(crashlog);
314
315	return sprintf(buf, "%d\n", enabled);
316}
317
318static ssize_t
319enable_store(struct device *dev, struct device_attribute *attr,
320	     const char *buf, size_t count)
321{
322	struct crashlog_entry *crashlog;
323	bool enabled;
324	int result;
325
326	crashlog = dev_get_drvdata(dev);
327
328	result = kstrtobool(buf, &enabled);
329	if (result)
330		return result;
331
332	guard(mutex)(&crashlog->control_mutex);
333
334	pmt_crashlog_set_disable(crashlog, !enabled);
335
336	return count;
337}
338static DEVICE_ATTR_RW(enable);
339
340static ssize_t
341error_show(struct device *dev, struct device_attribute *attr, char *buf)
342{
343	struct crashlog_entry *crashlog = dev_get_drvdata(dev);
344	bool error = pmt_crashlog_error(crashlog);
345
346	return sysfs_emit(buf, "%d\n", error);
347}
348static DEVICE_ATTR_RO(error);
349
350static ssize_t
351rearm_show(struct device *dev, struct device_attribute *attr, char *buf)
352{
353	struct crashlog_entry *crashlog = dev_get_drvdata(dev);
354	int rearmed = pmt_crashlog_rearm(crashlog);
355
356	return sysfs_emit(buf, "%d\n", rearmed);
357}
358
359static ssize_t
360rearm_store(struct device *dev, struct device_attribute *attr, const char *buf,
361	    size_t count)
362{
363	struct crashlog_entry *crashlog;
364	bool rearm;
365	int result;
366
367	crashlog = dev_get_drvdata(dev);
368
369	result = kstrtobool(buf, &rearm);
370	if (result)
371		return result;
372
373	/* set only */
374	if (!rearm)
375		return -EINVAL;
376
377	guard(mutex)(&crashlog->control_mutex);
378
379	pmt_crashlog_set_rearm(crashlog);
380
381	return count;
382}
383static DEVICE_ATTR_RW(rearm);
384
385static ssize_t
386trigger_show(struct device *dev, struct device_attribute *attr, char *buf)
387{
388	struct crashlog_entry *crashlog;
389	bool trigger;
390
391	crashlog = dev_get_drvdata(dev);
392	trigger = pmt_crashlog_complete(crashlog);
393
394	return sprintf(buf, "%d\n", trigger);
395}
396
397static ssize_t
398trigger_store(struct device *dev, struct device_attribute *attr,
399	      const char *buf, size_t count)
400{
401	struct crashlog_entry *crashlog;
402	bool trigger;
403	int result;
404
405	crashlog = dev_get_drvdata(dev);
406
407	result = kstrtobool(buf, &trigger);
408	if (result)
409		return result;
410
411	guard(mutex)(&crashlog->control_mutex);
412
413	/* if device is currently disabled, return busy */
414	if (pmt_crashlog_disabled(crashlog))
415		return -EBUSY;
416
417	if (!trigger) {
418		pmt_crashlog_set_clear(crashlog);
419		return count;
420	}
421
422	/* we cannot trigger a new crash if one is still pending */
423	if (pmt_crashlog_complete(crashlog))
424		return -EEXIST;
425
426	pmt_crashlog_set_execute(crashlog);
427
428	return count;
429}
430static DEVICE_ATTR_RW(trigger);
431
432static struct attribute *pmt_crashlog_type1_ver0_attrs[] = {
433	&dev_attr_enable.attr,
434	&dev_attr_trigger.attr,
435	NULL
436};
437
438static struct attribute *pmt_crashlog_type1_ver2_attrs[] = {
439	&dev_attr_clear.attr,
440	&dev_attr_consumed.attr,
441	&dev_attr_enable.attr,
442	&dev_attr_error.attr,
443	&dev_attr_rearm.attr,
444	&dev_attr_trigger.attr,
445	NULL
446};
447
448static const struct attribute_group pmt_crashlog_type1_ver0_group = {
449	.attrs	= pmt_crashlog_type1_ver0_attrs,
450};
451
452static const struct attribute_group pmt_crashlog_type1_ver2_group = {
453	.attrs = pmt_crashlog_type1_ver2_attrs,
454};
455
456static const struct crashlog_info crashlog_type1_ver0 = {
457	.status.offset = TYPE1_VER0_STATUS_OFFSET,
458	.status.cleared = TYPE1_VER0_CLEAR,
459	.status.complete = TYPE1_VER0_COMPLETE,
460	.status.disabled = TYPE1_VER0_DISABLE,
461
462	.control.offset = TYPE1_VER0_CONTROL_OFFSET,
463	.control.trigger_mask = TYPE1_VER0_TRIGGER_MASK,
464	.control.clear = TYPE1_VER0_CLEAR,
465	.control.disable = TYPE1_VER0_DISABLE,
466	.control.manual = TYPE1_VER0_EXECUTE,
467	.attr_grp = &pmt_crashlog_type1_ver0_group,
468};
469
470static const struct crashlog_info crashlog_type1_ver2 = {
471	.status.offset = TYPE1_VER2_STATUS_OFFSET,
472	.status.clear_supported = TYPE1_VER2_CLEAR_SUPPORT,
473	.status.cleared = TYPE1_VER2_CLEARED,
474	.status.complete = TYPE1_VER2_COMPLETE,
475	.status.consumed = TYPE1_VER2_CONSUMED,
476	.status.disabled = TYPE1_VER2_DISABLED,
477	.status.error = TYPE1_VER2_ERROR,
478	.status.in_progress = TYPE1_VER2_IN_PROGRESS,
479	.status.rearmed = TYPE1_VER2_REARMED,
480
481	.control.offset = TYPE1_VER2_CONTROL_OFFSET,
482	.control.trigger_mask = TYPE1_VER2_TRIGGER_MASK,
483	.control.clear = TYPE1_VER2_CLEAR,
484	.control.consume = TYPE1_VER2_CONSUME,
485	.control.disable = TYPE1_VER2_DISABLE,
486	.control.manual = TYPE1_VER2_EXECUTE,
487	.control.rearm = TYPE1_VER2_REARM,
488	.attr_grp = &pmt_crashlog_type1_ver2_group,
489};
490
491static const struct crashlog_info *select_crashlog_info(u32 type, u32 version)
492{
493	if (version == 0)
494		return &crashlog_type1_ver0;
495
496	return &crashlog_type1_ver2;
497}
498
499static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry,
500				      struct device *dev)
501{
502	void __iomem *disc_table = entry->disc_table;
503	struct intel_pmt_header *header = &entry->header;
504	struct crashlog_entry *crashlog;
505	u32 version;
506	u32 type;
507
508	if (!pmt_crashlog_supported(entry, &type, &version))
509		return 1;
510
511	/* initialize the crashlog struct */
512	crashlog = container_of(entry, struct crashlog_entry, entry);
513	mutex_init(&crashlog->control_mutex);
514
515	crashlog->info = select_crashlog_info(type, version);
516
517	header->access_type = GET_ACCESS(readl(disc_table));
518	header->guid = readl(disc_table + GUID_OFFSET);
519	header->base_offset = readl(disc_table + BASE_OFFSET);
520
521	/* Size is measured in DWORDS, but accessor returns bytes */
522	header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET));
523
524	entry->attr_grp = crashlog->info->attr_grp;
525
526	return 0;
527}
528
529static DEFINE_XARRAY_ALLOC(crashlog_array);
530static struct intel_pmt_namespace pmt_crashlog_ns = {
531	.name = "crashlog",
532	.xa = &crashlog_array,
533	.pmt_header_decode = pmt_crashlog_header_decode,
534};
535
536/*
537 * initialization
538 */
539static void pmt_crashlog_remove(struct auxiliary_device *auxdev)
540{
541	struct pmt_crashlog_priv *priv = auxiliary_get_drvdata(auxdev);
542	int i;
543
544	for (i = 0; i < priv->num_entries; i++) {
545		struct crashlog_entry *crashlog = &priv->entry[i];
546
547		intel_pmt_dev_destroy(&crashlog->entry, &pmt_crashlog_ns);
548		mutex_destroy(&crashlog->control_mutex);
549	}
550}
551
552static int pmt_crashlog_probe(struct auxiliary_device *auxdev,
553			      const struct auxiliary_device_id *id)
554{
555	struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev);
556	struct pmt_crashlog_priv *priv;
557	size_t size;
558	int i, ret;
559
560	size = struct_size(priv, entry, intel_vsec_dev->num_resources);
561	priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL);
562	if (!priv)
563		return -ENOMEM;
564
565	auxiliary_set_drvdata(auxdev, priv);
566
567	for (i = 0; i < intel_vsec_dev->num_resources; i++) {
568		struct intel_pmt_entry *entry = &priv->entry[priv->num_entries].entry;
569
570		ret = intel_pmt_dev_create(entry, &pmt_crashlog_ns, intel_vsec_dev, i);
571		if (ret < 0)
572			goto abort_probe;
573		if (ret)
574			continue;
575
576		priv->num_entries++;
577	}
578
579	return 0;
580abort_probe:
581	pmt_crashlog_remove(auxdev);
582	return ret;
583}
584
585static const struct auxiliary_device_id pmt_crashlog_id_table[] = {
586	{ .name = "intel_vsec.crashlog" },
587	{}
588};
589MODULE_DEVICE_TABLE(auxiliary, pmt_crashlog_id_table);
590
591static struct auxiliary_driver pmt_crashlog_aux_driver = {
592	.id_table	= pmt_crashlog_id_table,
593	.remove		= pmt_crashlog_remove,
594	.probe		= pmt_crashlog_probe,
595};
596
597static int __init pmt_crashlog_init(void)
598{
599	return auxiliary_driver_register(&pmt_crashlog_aux_driver);
600}
601
602static void __exit pmt_crashlog_exit(void)
603{
604	auxiliary_driver_unregister(&pmt_crashlog_aux_driver);
605	xa_destroy(&crashlog_array);
606}
607
608module_init(pmt_crashlog_init);
609module_exit(pmt_crashlog_exit);
610
611MODULE_AUTHOR("Alexander Duyck <alexander.h.duyck@linux.intel.com>");
612MODULE_DESCRIPTION("Intel PMT Crashlog driver");
613MODULE_LICENSE("GPL v2");
614MODULE_IMPORT_NS("INTEL_PMT");
~/ Read-only file preview.