xref: /linux/drivers/platform/x86/intel/pmt/discovery.c (revision 9669b2499ea377764f8320dd562dd6cd4ea80a5d)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Intel Platform Monitory Technology Discovery driver
4  *
5  * Copyright (c) 2025, Intel Corporation.
6  * All Rights Reserved.
7  */
8 
9 #include <linux/auxiliary_bus.h>
10 #include <linux/bitfield.h>
11 #include <linux/bits.h>
12 #include <linux/bug.h>
13 #include <linux/cleanup.h>
14 #include <linux/container_of.h>
15 #include <linux/device.h>
16 #include <linux/err.h>
17 #include <linux/io.h>
18 #include <linux/ioport.h>
19 #include <linux/kdev_t.h>
20 #include <linux/kobject.h>
21 #include <linux/list.h>
22 #include <linux/module.h>
23 #include <linux/mutex.h>
24 #include <linux/overflow.h>
25 #include <linux/pci.h>
26 #include <linux/slab.h>
27 #include <linux/string_choices.h>
28 #include <linux/sysfs.h>
29 #include <linux/types.h>
30 #include <linux/uaccess.h>
31 
32 #include <linux/intel_pmt_features.h>
33 #include <linux/intel_vsec.h>
34 
35 #include "class.h"
36 
37 #define MAX_FEATURE_VERSION	0
38 #define DT_TBIR			GENMASK(2, 0)
39 #define FEAT_ATTR_SIZE(x)	((x) * sizeof(u32))
40 #define PMT_GUID_SIZE(x)	((x) * sizeof(u32))
41 #define PMT_ACCESS_TYPE_RSVD	0xF
42 #define SKIP_FEATURE		1
43 
44 struct feature_discovery_table {
45 	u32	access_type:4;
46 	u32	version:8;
47 	u32	size:16;
48 	u32	reserved:4;
49 	u32	id;
50 	u32	offset;
51 	u32	reserved2;
52 };
53 
54 /* Common feature table header */
55 struct feature_header {
56 	u32	attr_size:8;
57 	u32	num_guids:8;
58 	u32	reserved:16;
59 };
60 
61 /* Feature attribute fields */
62 struct caps {
63 	u32		caps;
64 };
65 
66 struct command {
67 	u32		max_stream_size:16;
68 	u32		max_command_size:16;
69 };
70 
71 struct watcher {
72 	u32		reserved:21;
73 	u32		period:11;
74 	struct command	command;
75 };
76 
77 struct rmid {
78 	u32		num_rmids:16;	/* Number of Resource Monitoring IDs */
79 	u32		reserved:16;
80 	struct watcher	watcher;
81 };
82 
83 struct feature_table {
84 	struct feature_header	header;
85 	struct caps		caps;
86 	union {
87 		struct command command;
88 		struct watcher watcher;
89 		struct rmid rmid;
90 	};
91 	u32			*guids;
92 };
93 
94 /* For backreference in struct feature */
95 struct pmt_features_priv;
96 
97 struct feature {
98 	struct feature_table		table;
99 	struct kobject			kobj;
100 	struct pmt_features_priv	*priv;
101 	struct list_head		list;
102 	const struct attribute_group	*attr_group;
103 	enum pmt_feature_id		id;
104 };
105 
106 struct pmt_features_priv {
107 	struct device		*parent;
108 	struct device		*dev;
109 	int			count;
110 	u32			mask;
111 	struct feature		feature[];
112 };
113 
114 static LIST_HEAD(pmt_feature_list);
115 static DEFINE_MUTEX(feature_list_lock);
116 
117 #define to_pmt_feature(x) container_of(x, struct feature, kobj)
pmt_feature_release(struct kobject * kobj)118 static void pmt_feature_release(struct kobject *kobj)
119 {
120 }
121 
caps_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)122 static ssize_t caps_show(struct kobject *kobj, struct kobj_attribute *attr,
123 			 char *buf)
124 {
125 	struct feature *feature = to_pmt_feature(kobj);
126 	struct pmt_cap **pmt_caps;
127 	u32 caps = feature->table.caps.caps;
128 	ssize_t ret = 0;
129 
130 	switch (feature->id) {
131 	case FEATURE_PER_CORE_PERF_TELEM:
132 		pmt_caps = pmt_caps_pcpt;
133 		break;
134 	case FEATURE_PER_CORE_ENV_TELEM:
135 		pmt_caps = pmt_caps_pcet;
136 		break;
137 	case FEATURE_PER_RMID_PERF_TELEM:
138 		pmt_caps = pmt_caps_rmid_perf;
139 		break;
140 	case FEATURE_ACCEL_TELEM:
141 		pmt_caps = pmt_caps_accel;
142 		break;
143 	case FEATURE_UNCORE_TELEM:
144 		pmt_caps = pmt_caps_uncore;
145 		break;
146 	case FEATURE_CRASH_LOG:
147 		pmt_caps = pmt_caps_crashlog;
148 		break;
149 	case FEATURE_PETE_LOG:
150 		pmt_caps = pmt_caps_pete;
151 		break;
152 	case FEATURE_TPMI_CTRL:
153 		pmt_caps = pmt_caps_tpmi;
154 		break;
155 	case FEATURE_TRACING:
156 		pmt_caps = pmt_caps_tracing;
157 		break;
158 	case FEATURE_PER_RMID_ENERGY_TELEM:
159 		pmt_caps = pmt_caps_rmid_energy;
160 		break;
161 	default:
162 		return -EINVAL;
163 	}
164 
165 	while (*pmt_caps) {
166 		struct pmt_cap *pmt_cap = *pmt_caps;
167 
168 		while (pmt_cap->name) {
169 			ret += sysfs_emit_at(buf, ret, "%-40s Available: %s\n", pmt_cap->name,
170 					     str_yes_no(pmt_cap->mask & caps));
171 			pmt_cap++;
172 		}
173 		pmt_caps++;
174 	}
175 
176 	return ret;
177 }
178 static struct kobj_attribute caps_attribute = __ATTR_RO(caps);
179 
get_watcher(struct feature * feature)180 static struct watcher *get_watcher(struct feature *feature)
181 {
182 	switch (feature_layout[feature->id]) {
183 	case LAYOUT_RMID:
184 		return &feature->table.rmid.watcher;
185 	case LAYOUT_WATCHER:
186 		return &feature->table.watcher;
187 	default:
188 		return ERR_PTR(-EINVAL);
189 	}
190 }
191 
get_command(struct feature * feature)192 static struct command *get_command(struct feature *feature)
193 {
194 	switch (feature_layout[feature->id]) {
195 	case LAYOUT_RMID:
196 		return &feature->table.rmid.watcher.command;
197 	case LAYOUT_WATCHER:
198 		return &feature->table.watcher.command;
199 	case LAYOUT_COMMAND:
200 		return &feature->table.command;
201 	default:
202 		return ERR_PTR(-EINVAL);
203 	}
204 }
205 
num_rmids_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)206 static ssize_t num_rmids_show(struct kobject *kobj,
207 			      struct kobj_attribute *attr, char *buf)
208 {
209 	struct feature *feature = to_pmt_feature(kobj);
210 
211 	return sysfs_emit(buf, "%u\n", feature->table.rmid.num_rmids);
212 }
213 static struct kobj_attribute num_rmids_attribute = __ATTR_RO(num_rmids);
214 
min_watcher_period_ms_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)215 static ssize_t min_watcher_period_ms_show(struct kobject *kobj,
216 					  struct kobj_attribute *attr, char *buf)
217 {
218 	struct feature *feature = to_pmt_feature(kobj);
219 	struct watcher *watcher = get_watcher(feature);
220 
221 	if (IS_ERR(watcher))
222 		return PTR_ERR(watcher);
223 
224 	return sysfs_emit(buf, "%u\n", watcher->period);
225 }
226 static struct kobj_attribute min_watcher_period_ms_attribute =
227 	__ATTR_RO(min_watcher_period_ms);
228 
max_stream_size_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)229 static ssize_t max_stream_size_show(struct kobject *kobj,
230 				    struct kobj_attribute *attr, char *buf)
231 {
232 	struct feature *feature = to_pmt_feature(kobj);
233 	struct command *command = get_command(feature);
234 
235 	if (IS_ERR(command))
236 		return PTR_ERR(command);
237 
238 	return sysfs_emit(buf, "%u\n", command->max_stream_size);
239 }
240 static struct kobj_attribute max_stream_size_attribute =
241 	__ATTR_RO(max_stream_size);
242 
max_command_size_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)243 static ssize_t max_command_size_show(struct kobject *kobj,
244 				     struct kobj_attribute *attr, char *buf)
245 {
246 	struct feature *feature = to_pmt_feature(kobj);
247 	struct command *command = get_command(feature);
248 
249 	if (IS_ERR(command))
250 		return PTR_ERR(command);
251 
252 	return sysfs_emit(buf, "%u\n", command->max_command_size);
253 }
254 static struct kobj_attribute max_command_size_attribute =
255 	__ATTR_RO(max_command_size);
256 
guids_show(struct kobject * kobj,struct kobj_attribute * attr,char * buf)257 static ssize_t guids_show(struct kobject *kobj, struct kobj_attribute *attr,
258 			  char *buf)
259 {
260 	struct feature *feature = to_pmt_feature(kobj);
261 	int i, count = 0;
262 
263 	for (i = 0; i < feature->table.header.num_guids; i++)
264 		count += sysfs_emit_at(buf, count, "0x%x\n",
265 				       feature->table.guids[i]);
266 
267 	return count;
268 }
269 static struct kobj_attribute guids_attribute = __ATTR_RO(guids);
270 
271 static struct attribute *pmt_feature_rmid_attrs[] = {
272 	&caps_attribute.attr,
273 	&num_rmids_attribute.attr,
274 	&min_watcher_period_ms_attribute.attr,
275 	&max_stream_size_attribute.attr,
276 	&max_command_size_attribute.attr,
277 	&guids_attribute.attr,
278 	NULL
279 };
280 ATTRIBUTE_GROUPS(pmt_feature_rmid);
281 
282 static const struct kobj_type pmt_feature_rmid_ktype = {
283 	.sysfs_ops = &kobj_sysfs_ops,
284 	.release = pmt_feature_release,
285 	.default_groups = pmt_feature_rmid_groups,
286 };
287 
288 static struct attribute *pmt_feature_watcher_attrs[] = {
289 	&caps_attribute.attr,
290 	&min_watcher_period_ms_attribute.attr,
291 	&max_stream_size_attribute.attr,
292 	&max_command_size_attribute.attr,
293 	&guids_attribute.attr,
294 	NULL
295 };
296 ATTRIBUTE_GROUPS(pmt_feature_watcher);
297 
298 static const struct kobj_type pmt_feature_watcher_ktype = {
299 	.sysfs_ops = &kobj_sysfs_ops,
300 	.release = pmt_feature_release,
301 	.default_groups = pmt_feature_watcher_groups,
302 };
303 
304 static struct attribute *pmt_feature_command_attrs[] = {
305 	&caps_attribute.attr,
306 	&max_stream_size_attribute.attr,
307 	&max_command_size_attribute.attr,
308 	&guids_attribute.attr,
309 	NULL
310 };
311 ATTRIBUTE_GROUPS(pmt_feature_command);
312 
313 static const struct kobj_type pmt_feature_command_ktype = {
314 	.sysfs_ops = &kobj_sysfs_ops,
315 	.release = pmt_feature_release,
316 	.default_groups = pmt_feature_command_groups,
317 };
318 
319 static struct attribute *pmt_feature_guids_attrs[] = {
320 	&caps_attribute.attr,
321 	&guids_attribute.attr,
322 	NULL
323 };
324 ATTRIBUTE_GROUPS(pmt_feature_guids);
325 
326 static const struct kobj_type pmt_feature_guids_ktype = {
327 	.sysfs_ops = &kobj_sysfs_ops,
328 	.release = pmt_feature_release,
329 	.default_groups = pmt_feature_guids_groups,
330 };
331 
332 static int
pmt_feature_get_disc_table(struct pmt_features_priv * priv,struct resource * disc_res,struct feature_discovery_table * disc_tbl)333 pmt_feature_get_disc_table(struct pmt_features_priv *priv,
334 			   struct resource *disc_res,
335 			   struct feature_discovery_table *disc_tbl)
336 {
337 	void __iomem *disc_base;
338 
339 	disc_base = devm_ioremap_resource(priv->dev, disc_res);
340 	if (IS_ERR(disc_base))
341 		return PTR_ERR(disc_base);
342 
343 	memcpy_fromio(disc_tbl, disc_base, sizeof(*disc_tbl));
344 
345 	devm_iounmap(priv->dev, disc_base);
346 
347 	if (priv->mask & BIT(disc_tbl->id))
348 		return dev_err_probe(priv->dev, -EINVAL, "Duplicate feature: %s\n",
349 				     pmt_feature_names[disc_tbl->id]);
350 
351 	/*
352 	 * Some devices may expose non-functioning entries that are
353 	 * reserved for future use. They have zero size. Do not fail
354 	 * probe for these. Just ignore them.
355 	 */
356 	if (disc_tbl->size == 0 || disc_tbl->access_type == PMT_ACCESS_TYPE_RSVD)
357 		return SKIP_FEATURE;
358 
359 	if (disc_tbl->version > MAX_FEATURE_VERSION)
360 		return SKIP_FEATURE;
361 
362 	if (!pmt_feature_id_is_valid(disc_tbl->id))
363 		return SKIP_FEATURE;
364 
365 	priv->mask |= BIT(disc_tbl->id);
366 
367 	return 0;
368 }
369 
370 static int
pmt_feature_get_feature_table(struct pmt_features_priv * priv,struct feature * feature,struct feature_discovery_table * disc_tbl,struct resource * disc_res)371 pmt_feature_get_feature_table(struct pmt_features_priv *priv,
372 			      struct feature *feature,
373 			      struct feature_discovery_table *disc_tbl,
374 			      struct resource *disc_res)
375 {
376 	struct feature_table *feat_tbl = &feature->table;
377 	struct feature_header *header;
378 	struct resource res = {};
379 	resource_size_t res_size;
380 	void __iomem *feat_base, *feat_offset;
381 	void *tbl_offset;
382 	size_t size;
383 	u32 *guids;
384 	u8 tbir;
385 
386 	tbir = FIELD_GET(DT_TBIR, disc_tbl->offset);
387 
388 	switch (disc_tbl->access_type) {
389 	case ACCESS_LOCAL:
390 		if (tbir)
391 			return dev_err_probe(priv->dev, -EINVAL,
392 				"Unsupported BAR index %u for access type %u\n",
393 				tbir, disc_tbl->access_type);
394 
395 
396 		/*
397 		 * For access_type LOCAL, the base address is as follows:
398 		 * base address = end of discovery region + base offset + 1
399 		 */
400 		res = DEFINE_RES_MEM(disc_res->end + disc_tbl->offset + 1,
401 				     disc_tbl->size * sizeof(u32));
402 		break;
403 
404 	default:
405 		return dev_err_probe(priv->dev, -EINVAL, "Unrecognized access_type %u\n",
406 				     disc_tbl->access_type);
407 	}
408 
409 	feature->id = disc_tbl->id;
410 
411 	/* Get the feature table */
412 	feat_base = devm_ioremap_resource(priv->dev, &res);
413 	if (IS_ERR(feat_base))
414 		return PTR_ERR(feat_base);
415 
416 	feat_offset = feat_base;
417 	tbl_offset = feat_tbl;
418 
419 	/* Get the header */
420 	header = &feat_tbl->header;
421 	memcpy_fromio(header, feat_offset, sizeof(*header));
422 
423 	/* Validate fields fit within mapped resource */
424 	size = sizeof(*header) + FEAT_ATTR_SIZE(header->attr_size) +
425 	       PMT_GUID_SIZE(header->num_guids);
426 	res_size = resource_size(&res);
427 	if (WARN(size > res_size, "Bad table size %zu > %pa", size, &res_size))
428 		return -EINVAL;
429 
430 	/* Get the feature attributes, including capability fields */
431 	tbl_offset += sizeof(*header);
432 	feat_offset += sizeof(*header);
433 
434 	memcpy_fromio(tbl_offset, feat_offset, FEAT_ATTR_SIZE(header->attr_size));
435 
436 	/* Finally, get the guids */
437 	guids = devm_kmalloc(priv->dev, PMT_GUID_SIZE(header->num_guids), GFP_KERNEL);
438 	if (!guids)
439 		return -ENOMEM;
440 
441 	feat_offset += FEAT_ATTR_SIZE(header->attr_size);
442 
443 	memcpy_fromio(guids, feat_offset, PMT_GUID_SIZE(header->num_guids));
444 
445 	feat_tbl->guids = guids;
446 
447 	devm_iounmap(priv->dev, feat_base);
448 
449 	return 0;
450 }
451 
pmt_features_add_feat(struct feature * feature)452 static void pmt_features_add_feat(struct feature *feature)
453 {
454 	guard(mutex)(&feature_list_lock);
455 	list_add(&feature->list, &pmt_feature_list);
456 }
457 
pmt_features_remove_feat(struct feature * feature)458 static void pmt_features_remove_feat(struct feature *feature)
459 {
460 	guard(mutex)(&feature_list_lock);
461 	list_del(&feature->list);
462 }
463 
464 /* Get the discovery table and use it to get the feature table */
pmt_features_discovery(struct pmt_features_priv * priv,struct feature * feature,struct intel_vsec_device * ivdev,int idx)465 static int pmt_features_discovery(struct pmt_features_priv *priv,
466 				  struct feature *feature,
467 				  struct intel_vsec_device *ivdev,
468 				  int idx)
469 {
470 	struct feature_discovery_table disc_tbl = {}; /* Avoid false warning */
471 	struct resource *disc_res = &ivdev->resource[idx];
472 	const struct kobj_type *ktype;
473 	int ret;
474 
475 	ret = pmt_feature_get_disc_table(priv, disc_res, &disc_tbl);
476 	if (ret)
477 		return ret;
478 
479 	ret = pmt_feature_get_feature_table(priv, feature, &disc_tbl, disc_res);
480 	if (ret)
481 		return ret;
482 
483 	switch (feature_layout[feature->id]) {
484 	case LAYOUT_RMID:
485 		ktype = &pmt_feature_rmid_ktype;
486 		feature->attr_group = &pmt_feature_rmid_group;
487 		break;
488 	case LAYOUT_WATCHER:
489 		ktype = &pmt_feature_watcher_ktype;
490 		feature->attr_group = &pmt_feature_watcher_group;
491 		break;
492 	case LAYOUT_COMMAND:
493 		ktype = &pmt_feature_command_ktype;
494 		feature->attr_group = &pmt_feature_command_group;
495 		break;
496 	case LAYOUT_CAPS_ONLY:
497 		ktype = &pmt_feature_guids_ktype;
498 		feature->attr_group = &pmt_feature_guids_group;
499 		break;
500 	default:
501 		return -EINVAL;
502 	}
503 
504 	ret = kobject_init_and_add(&feature->kobj, ktype, &priv->dev->kobj,
505 				   "%s", pmt_feature_names[feature->id]);
506 	if (ret)
507 		return ret;
508 
509 	kobject_uevent(&feature->kobj, KOBJ_ADD);
510 	pmt_features_add_feat(feature);
511 
512 	return 0;
513 }
514 
pmt_features_remove(struct auxiliary_device * auxdev)515 static void pmt_features_remove(struct auxiliary_device *auxdev)
516 {
517 	struct pmt_features_priv *priv = auxiliary_get_drvdata(auxdev);
518 	int i;
519 
520 	for (i = 0; i < priv->count; i++) {
521 		struct feature *feature = &priv->feature[i];
522 
523 		pmt_features_remove_feat(feature);
524 		sysfs_remove_group(&feature->kobj, feature->attr_group);
525 		kobject_put(&feature->kobj);
526 	}
527 
528 	device_unregister(priv->dev);
529 }
530 
pmt_features_probe(struct auxiliary_device * auxdev,const struct auxiliary_device_id * id)531 static int pmt_features_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
532 {
533 	struct intel_vsec_device *ivdev = auxdev_to_ivdev(auxdev);
534 	struct pmt_features_priv *priv;
535 	size_t size;
536 	int ret, i;
537 
538 	size = struct_size(priv, feature, ivdev->num_resources);
539 	priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL);
540 	if (!priv)
541 		return -ENOMEM;
542 
543 	priv->parent = &ivdev->pcidev->dev;
544 	auxiliary_set_drvdata(auxdev, priv);
545 
546 	priv->dev = device_create(&intel_pmt_class, &auxdev->dev, MKDEV(0, 0), priv,
547 				  "%s-%s", "features", dev_name(priv->parent));
548 	if (IS_ERR(priv->dev))
549 		return dev_err_probe(priv->dev, PTR_ERR(priv->dev),
550 				     "Could not create %s-%s device node\n",
551 				     "features", dev_name(priv->dev));
552 
553 	/* Initialize each feature */
554 	for (i = 0; i < ivdev->num_resources; i++) {
555 		struct feature *feature = &priv->feature[priv->count];
556 
557 		ret = pmt_features_discovery(priv, feature, ivdev, i);
558 		if (ret == SKIP_FEATURE)
559 			continue;
560 		if (ret != 0)
561 			goto abort_probe;
562 
563 		feature->priv = priv;
564 		priv->count++;
565 	}
566 
567 	return 0;
568 
569 abort_probe:
570 	/*
571 	 * Only fully initialized features are tracked in priv->count, which is
572 	 * incremented only after a feature is completely set up (i.e., after
573 	 * discovery and sysfs registration). If feature initialization fails,
574 	 * the failing feature's state is local and does not require rollback.
575 	 *
576 	 * Therefore, on error, we can safely call the driver's remove() routine
577 	 * pmt_features_remove() to clean up only those features that were
578 	 * fully initialized and counted. All other resources are device-managed
579 	 * and will be cleaned up automatically during device_unregister().
580 	 */
581 	pmt_features_remove(auxdev);
582 
583 	return ret;
584 }
585 
pmt_get_features(struct intel_pmt_entry * entry,struct feature * f)586 static void pmt_get_features(struct intel_pmt_entry *entry, struct feature *f)
587 {
588 	int num_guids = f->table.header.num_guids;
589 	int i;
590 
591 	for (i = 0; i < num_guids; i++) {
592 		if (f->table.guids[i] != entry->guid)
593 			continue;
594 
595 		entry->feature_flags |= BIT(f->id);
596 
597 		if (feature_layout[f->id] == LAYOUT_RMID)
598 			entry->num_rmids = f->table.rmid.num_rmids;
599 		else
600 			entry->num_rmids = 0; /* entry is kzalloc but set anyway */
601 	}
602 }
603 
intel_pmt_get_features(struct intel_pmt_entry * entry)604 void intel_pmt_get_features(struct intel_pmt_entry *entry)
605 {
606 	struct feature *feature;
607 
608 	mutex_lock(&feature_list_lock);
609 	list_for_each_entry(feature, &pmt_feature_list, list) {
610 		if (feature->priv->parent != &entry->ep->pcidev->dev)
611 			continue;
612 
613 		pmt_get_features(entry, feature);
614 	}
615 	mutex_unlock(&feature_list_lock);
616 }
617 EXPORT_SYMBOL_NS_GPL(intel_pmt_get_features, "INTEL_PMT");
618 
619 static const struct auxiliary_device_id pmt_features_id_table[] = {
620 	{ .name = "intel_vsec.discovery" },
621 	{}
622 };
623 MODULE_DEVICE_TABLE(auxiliary, pmt_features_id_table);
624 
625 static struct auxiliary_driver pmt_features_aux_driver = {
626 	.id_table	= pmt_features_id_table,
627 	.remove		= pmt_features_remove,
628 	.probe		= pmt_features_probe,
629 };
630 module_auxiliary_driver(pmt_features_aux_driver);
631 
632 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
633 MODULE_DESCRIPTION("Intel PMT Discovery driver");
634 MODULE_LICENSE("GPL");
635 MODULE_IMPORT_NS("INTEL_PMT");
636