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
htmdump_read(struct file * filp,char __user * ubuf,size_t count,loff_t * ppos)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
htmdump_init_debugfs(void)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
htmdump_init(void)104 static int __init htmdump_init(void)
105 {
106 if (htmdump_init_debugfs())
107 return -ENOMEM;
108
109 return 0;
110 }
111
htmdump_exit(void)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