Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

platform/x86/intel/tpmi: Add debugfs interface

Add debugfs interface for debugging TPMI configuration and register
contents. This shows PFS (PM Feature structure) for each TPMI device.

For each feature, show full register contents and allow to modify
register at an offset.

This debugfs interface is not present on locked down kernel with no
DEVMEM access and without CAP_SYS_RAWIO permission.

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://lore.kernel.org/r/20230712225950.171326-3-srinivas.pandruvada@linux.intel.com
Signed-off-by: Hans de Goede <hdegoede@redhat.com>

authored by

Srinivas Pandruvada and committed by
Hans de Goede
b326c1bb 61457949

+212 -7
+212 -7
drivers/platform/x86/intel/tpmi.c
··· 48 48 49 49 #include <linux/auxiliary_bus.h> 50 50 #include <linux/bitfield.h> 51 + #include <linux/debugfs.h> 51 52 #include <linux/delay.h> 52 53 #include <linux/intel_tpmi.h> 53 54 #include <linux/io.h> 54 55 #include <linux/iopoll.h> 55 56 #include <linux/module.h> 56 57 #include <linux/pci.h> 58 + #include <linux/security.h> 57 59 #include <linux/sizes.h> 60 + #include <linux/string_helpers.h> 58 61 59 62 #include "vsec.h" 60 63 ··· 90 87 * @vsec_offset: Starting MMIO address for this feature in bytes. Essentially 91 88 * this offset = "Address" from VSEC header + PFS Capability 92 89 * offset for this feature entry. 90 + * @vsec_dev: Pointer to intel_vsec_device structure for this TPMI device 93 91 * 94 92 * Represents TPMI instance information for one TPMI ID. 95 93 */ 96 94 struct intel_tpmi_pm_feature { 97 95 struct intel_tpmi_pfs_entry pfs_header; 98 96 unsigned int vsec_offset; 97 + struct intel_vsec_device *vsec_dev; 99 98 }; 100 99 101 100 /** ··· 108 103 * @pfs_start: Start of PFS offset for the TPMI instances in this device 109 104 * @plat_info: Stores platform info which can be used by the client drivers 110 105 * @tpmi_control_mem: Memory mapped IO for getting control information 106 + * @dbgfs_dir: debugfs entry pointer 111 107 * 112 108 * Stores the information for all TPMI devices enumerated from a single PCI device. 113 109 */ ··· 119 113 u64 pfs_start; 120 114 struct intel_tpmi_plat_info plat_info; 121 115 void __iomem *tpmi_control_mem; 116 + struct dentry *dbgfs_dir; 122 117 }; 123 118 124 119 /** ··· 340 333 } 341 334 EXPORT_SYMBOL_NS_GPL(tpmi_get_feature_status, INTEL_TPMI); 342 335 336 + static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused) 337 + { 338 + struct intel_tpmi_info *tpmi_info = s->private; 339 + struct intel_tpmi_pm_feature *pfs; 340 + int locked, disabled, ret, i; 341 + 342 + seq_printf(s, "tpmi PFS start offset 0x:%llx\n", tpmi_info->pfs_start); 343 + seq_puts(s, "tpmi_id\t\tentries\t\tsize\t\tcap_offset\tattribute\tvsec_offset\tlocked\tdisabled\n"); 344 + for (i = 0; i < tpmi_info->feature_count; ++i) { 345 + pfs = &tpmi_info->tpmi_features[i]; 346 + ret = tpmi_read_feature_status(tpmi_info, pfs->pfs_header.tpmi_id, &locked, 347 + &disabled); 348 + if (ret) { 349 + locked = 'U'; 350 + disabled = 'U'; 351 + } else { 352 + disabled = disabled ? 'Y' : 'N'; 353 + locked = locked ? 'Y' : 'N'; 354 + } 355 + seq_printf(s, "0x%02x\t\t0x%02x\t\t0x%04x\t\t0x%04x\t\t0x%02x\t\t0x%08x\t%c\t%c\n", 356 + pfs->pfs_header.tpmi_id, pfs->pfs_header.num_entries, 357 + pfs->pfs_header.entry_size, pfs->pfs_header.cap_offset, 358 + pfs->pfs_header.attribute, pfs->vsec_offset, locked, disabled); 359 + } 360 + 361 + return 0; 362 + } 363 + DEFINE_SHOW_ATTRIBUTE(tpmi_pfs_dbg); 364 + 365 + #define MEM_DUMP_COLUMN_COUNT 8 366 + 367 + static int tpmi_mem_dump_show(struct seq_file *s, void *unused) 368 + { 369 + size_t row_size = MEM_DUMP_COLUMN_COUNT * sizeof(u32); 370 + struct intel_tpmi_pm_feature *pfs = s->private; 371 + int count, ret = 0; 372 + void __iomem *mem; 373 + u32 off, size; 374 + u8 *buffer; 375 + 376 + size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs); 377 + if (!size) 378 + return -EIO; 379 + 380 + buffer = kmalloc(size, GFP_KERNEL); 381 + if (!buffer) 382 + return -ENOMEM; 383 + 384 + off = pfs->vsec_offset; 385 + 386 + mutex_lock(&tpmi_dev_lock); 387 + 388 + for (count = 0; count < pfs->pfs_header.num_entries; ++count) { 389 + seq_printf(s, "TPMI Instance:%d offset:0x%x\n", count, off); 390 + 391 + mem = ioremap(off, size); 392 + if (!mem) { 393 + ret = -ENOMEM; 394 + break; 395 + } 396 + 397 + memcpy_fromio(buffer, mem, size); 398 + 399 + seq_hex_dump(s, " ", DUMP_PREFIX_OFFSET, row_size, sizeof(u32), buffer, size, 400 + false); 401 + 402 + iounmap(mem); 403 + 404 + off += size; 405 + } 406 + 407 + mutex_unlock(&tpmi_dev_lock); 408 + 409 + kfree(buffer); 410 + 411 + return ret; 412 + } 413 + DEFINE_SHOW_ATTRIBUTE(tpmi_mem_dump); 414 + 415 + static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t len, loff_t *ppos) 416 + { 417 + struct seq_file *m = file->private_data; 418 + struct intel_tpmi_pm_feature *pfs = m->private; 419 + u32 addr, value, punit, size; 420 + u32 num_elems, *array; 421 + void __iomem *mem; 422 + int ret; 423 + 424 + size = TPMI_GET_SINGLE_ENTRY_SIZE(pfs); 425 + if (!size) 426 + return -EIO; 427 + 428 + ret = parse_int_array_user(userbuf, len, (int **)&array); 429 + if (ret < 0) 430 + return ret; 431 + 432 + num_elems = *array; 433 + if (num_elems != 3) { 434 + ret = -EINVAL; 435 + goto exit_write; 436 + } 437 + 438 + punit = array[1]; 439 + addr = array[2]; 440 + value = array[3]; 441 + 442 + if (punit >= pfs->pfs_header.num_entries) { 443 + ret = -EINVAL; 444 + goto exit_write; 445 + } 446 + 447 + if (addr >= size) { 448 + ret = -EINVAL; 449 + goto exit_write; 450 + } 451 + 452 + mutex_lock(&tpmi_dev_lock); 453 + 454 + mem = ioremap(pfs->vsec_offset + punit * size, size); 455 + if (!mem) { 456 + ret = -ENOMEM; 457 + goto unlock_mem_write; 458 + } 459 + 460 + writel(value, mem + addr); 461 + 462 + iounmap(mem); 463 + 464 + ret = len; 465 + 466 + unlock_mem_write: 467 + mutex_unlock(&tpmi_dev_lock); 468 + 469 + exit_write: 470 + kfree(array); 471 + 472 + return ret; 473 + } 474 + 475 + static int mem_write_show(struct seq_file *s, void *unused) 476 + { 477 + return 0; 478 + } 479 + 480 + static int mem_write_open(struct inode *inode, struct file *file) 481 + { 482 + return single_open(file, mem_write_show, inode->i_private); 483 + } 484 + 485 + static const struct file_operations mem_write_ops = { 486 + .open = mem_write_open, 487 + .read = seq_read, 488 + .write = mem_write, 489 + .llseek = seq_lseek, 490 + .release = single_release, 491 + }; 492 + 493 + #define tpmi_to_dev(info) (&info->vsec_dev->pcidev->dev) 494 + 495 + static void tpmi_dbgfs_register(struct intel_tpmi_info *tpmi_info) 496 + { 497 + char name[64]; 498 + int i; 499 + 500 + snprintf(name, sizeof(name), "tpmi-%s", dev_name(tpmi_to_dev(tpmi_info))); 501 + tpmi_info->dbgfs_dir = debugfs_create_dir(name, NULL); 502 + 503 + debugfs_create_file("pfs_dump", 0444, tpmi_info->dbgfs_dir, tpmi_info, &tpmi_pfs_dbg_fops); 504 + 505 + for (i = 0; i < tpmi_info->feature_count; ++i) { 506 + struct intel_tpmi_pm_feature *pfs; 507 + struct dentry *dir; 508 + 509 + pfs = &tpmi_info->tpmi_features[i]; 510 + snprintf(name, sizeof(name), "tpmi-id-%02x", pfs->pfs_header.tpmi_id); 511 + dir = debugfs_create_dir(name, tpmi_info->dbgfs_dir); 512 + 513 + debugfs_create_file("mem_dump", 0444, dir, pfs, &tpmi_mem_dump_fops); 514 + debugfs_create_file("mem_write", 0644, dir, pfs, &mem_write_ops); 515 + } 516 + } 517 + 343 518 static void tpmi_set_control_base(struct auxiliary_device *auxdev, 344 519 struct intel_tpmi_info *tpmi_info, 345 520 struct intel_tpmi_pm_feature *pfs) ··· 682 493 struct pci_dev *pci_dev = vsec_dev->pcidev; 683 494 struct intel_tpmi_info *tpmi_info; 684 495 u64 pfs_start = 0; 685 - int i; 496 + int ret, i; 686 497 687 498 tpmi_info = devm_kzalloc(&auxdev->dev, sizeof(*tpmi_info), GFP_KERNEL); 688 499 if (!tpmi_info) ··· 705 516 int size, ret; 706 517 707 518 pfs = &tpmi_info->tpmi_features[i]; 519 + pfs->vsec_dev = vsec_dev; 708 520 709 521 res = &vsec_dev->resource[i]; 710 522 if (!res) ··· 745 555 746 556 auxiliary_set_drvdata(auxdev, tpmi_info); 747 557 748 - return tpmi_create_devices(tpmi_info); 558 + ret = tpmi_create_devices(tpmi_info); 559 + if (ret) 560 + return ret; 561 + 562 + /* 563 + * Allow debugfs when security policy allows. Everything this debugfs 564 + * interface provides, can also be done via /dev/mem access. If 565 + * /dev/mem interface is locked, don't allow debugfs to present any 566 + * information. Also check for CAP_SYS_RAWIO as /dev/mem interface. 567 + */ 568 + if (!security_locked_down(LOCKDOWN_DEV_MEM) && capable(CAP_SYS_RAWIO)) 569 + tpmi_dbgfs_register(tpmi_info); 570 + 571 + return 0; 749 572 } 750 573 751 574 static int tpmi_probe(struct auxiliary_device *auxdev, ··· 767 564 return intel_vsec_tpmi_init(auxdev); 768 565 } 769 566 770 - /* 771 - * Remove callback is not needed currently as there is no 772 - * cleanup required. All memory allocs are device managed. All 773 - * devices created by this modules are also device managed. 774 - */ 567 + static void tpmi_remove(struct auxiliary_device *auxdev) 568 + { 569 + struct intel_tpmi_info *tpmi_info = auxiliary_get_drvdata(auxdev); 570 + 571 + debugfs_remove_recursive(tpmi_info->dbgfs_dir); 572 + } 775 573 776 574 static const struct auxiliary_device_id tpmi_id_table[] = { 777 575 { .name = "intel_vsec.tpmi" }, ··· 783 579 static struct auxiliary_driver tpmi_aux_driver = { 784 580 .id_table = tpmi_id_table, 785 581 .probe = tpmi_probe, 582 + .remove = tpmi_remove, 786 583 }; 787 584 788 585 module_auxiliary_driver(tpmi_aux_driver);