xref: /linux/arch/powerpc/platforms/pseries/htmdump.c (revision 7a9b709e7cc5ce1ffb84ce07bf6d157e1de758df)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) IBM Corporation, 2024
4  */
5 
6 #define pr_fmt(fmt) "htmdump: " fmt
7 
8 #include <linux/debugfs.h>
9 #include <linux/module.h>
10 #include <asm/io.h>
11 #include <asm/machdep.h>
12 #include <asm/plpar_wrappers.h>
13 
14 static void *htm_buf;
15 static u32 nodeindex;
16 static u32 nodalchipindex;
17 static u32 coreindexonchip;
18 static u32 htmtype;
19 static struct dentry *htmdump_debugfs_dir;
20 
21 static ssize_t htmdump_read(struct file *filp, char __user *ubuf,
22 			     size_t count, loff_t *ppos)
23 {
24 	void *htm_buf = filp->private_data;
25 	unsigned long page, read_size, available;
26 	loff_t offset;
27 	long rc;
28 
29 	page = ALIGN_DOWN(*ppos, PAGE_SIZE);
30 	offset = (*ppos) % PAGE_SIZE;
31 
32 	rc = htm_get_dump_hardware(nodeindex, nodalchipindex, coreindexonchip,
33 				   htmtype, virt_to_phys(htm_buf), PAGE_SIZE, page);
34 
35 	switch (rc) {
36 	case H_SUCCESS:
37 	/* H_PARTIAL for the case where all available data can't be
38 	 * returned due to buffer size constraint.
39 	 */
40 	case H_PARTIAL:
41 		break;
42 	/* H_NOT_AVAILABLE indicates reading from an offset outside the range,
43 	 * i.e. past end of file.
44 	 */
45 	case H_NOT_AVAILABLE:
46 		return 0;
47 	case H_BUSY:
48 	case H_LONG_BUSY_ORDER_1_MSEC:
49 	case H_LONG_BUSY_ORDER_10_MSEC:
50 	case H_LONG_BUSY_ORDER_100_MSEC:
51 	case H_LONG_BUSY_ORDER_1_SEC:
52 	case H_LONG_BUSY_ORDER_10_SEC:
53 	case H_LONG_BUSY_ORDER_100_SEC:
54 		return -EBUSY;
55 	case H_PARAMETER:
56 	case H_P2:
57 	case H_P3:
58 	case H_P4:
59 	case H_P5:
60 	case H_P6:
61 		return -EINVAL;
62 	case H_STATE:
63 		return -EIO;
64 	case H_AUTHORITY:
65 		return -EPERM;
66 	}
67 
68 	available = PAGE_SIZE;
69 	read_size = min(count, available);
70 	*ppos += read_size;
71 	return simple_read_from_buffer(ubuf, count, &offset, htm_buf, available);
72 }
73 
74 static const struct file_operations htmdump_fops = {
75 	.llseek = NULL,
76 	.read	= htmdump_read,
77 	.open	= simple_open,
78 };
79 
80 static int htmdump_init_debugfs(void)
81 {
82 	htm_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
83 	if (!htm_buf) {
84 		pr_err("Failed to allocate htmdump buf\n");
85 		return -ENOMEM;
86 	}
87 
88 	htmdump_debugfs_dir = debugfs_create_dir("htmdump",
89 						  arch_debugfs_dir);
90 
91 	debugfs_create_u32("nodeindex", 0600,
92 			htmdump_debugfs_dir, &nodeindex);
93 	debugfs_create_u32("nodalchipindex", 0600,
94 			htmdump_debugfs_dir, &nodalchipindex);
95 	debugfs_create_u32("coreindexonchip", 0600,
96 			htmdump_debugfs_dir, &coreindexonchip);
97 	debugfs_create_u32("htmtype", 0600,
98 			htmdump_debugfs_dir, &htmtype);
99 	debugfs_create_file("trace", 0400, htmdump_debugfs_dir, htm_buf, &htmdump_fops);
100 
101 	return 0;
102 }
103 
104 static int __init htmdump_init(void)
105 {
106 	if (htmdump_init_debugfs())
107 		return -ENOMEM;
108 
109 	return 0;
110 }
111 
112 static void __exit htmdump_exit(void)
113 {
114 	debugfs_remove_recursive(htmdump_debugfs_dir);
115 	kfree(htm_buf);
116 }
117 
118 module_init(htmdump_init);
119 module_exit(htmdump_exit);
120 MODULE_DESCRIPTION("PHYP Hardware Trace Macro (HTM) data dumper");
121 MODULE_LICENSE("GPL");
122