xref: /linux/arch/powerpc/platforms/pseries/htmdump.c (revision fb7399cf2d0b33825b8039f95c45395c7deba25c)
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 #include <asm/kvm_guest.h>
14 
15 static void *htm_buf;
16 static void *htm_status_buf;
17 static void *htm_info_buf;
18 static void *htm_caps_buf;
19 static u32 nodeindex;
20 static u32 nodalchipindex;
21 static u32 coreindexonchip;
22 static u32 htmtype;
23 static u32 htmconfigure;
24 static u32 htmstart;
25 static u32 htmsetup;
26 static u64 htmflags;
27 
28 static struct dentry *htmdump_debugfs_dir;
29 #define	HTM_ENABLE	1
30 #define	HTM_DISABLE	0
31 #define	HTM_NOWRAP	1
32 #define	HTM_WRAP	0
33 
34 /*
35  * Check the return code for H_HTM hcall.
36  * Return non-zero value (1) if either H_PARTIAL or H_SUCCESS
37  * is returned. For other return codes:
38  * Return zero if H_NOT_AVAILABLE.
39  * Return -EBUSY if hcall return busy.
40  * Return -EINVAL if any parameter or operation is not valid.
41  * Return -EPERM if HTM Virtualization Engine Technology code
42  * is not applied.
43  * Return -EIO if the HTM state is not valid.
44  */
45 static ssize_t htm_return_check(long rc)
46 {
47 	switch (rc) {
48 	case H_SUCCESS:
49 	/* H_PARTIAL for the case where all available data can't be
50 	 * returned due to buffer size constraint.
51 	 */
52 	case H_PARTIAL:
53 		break;
54 	/* H_NOT_AVAILABLE indicates reading from an offset outside the range,
55 	 * i.e. past end of file.
56 	 */
57 	case H_NOT_AVAILABLE:
58 		return 0;
59 	case H_BUSY:
60 	case H_LONG_BUSY_ORDER_1_MSEC:
61 	case H_LONG_BUSY_ORDER_10_MSEC:
62 	case H_LONG_BUSY_ORDER_100_MSEC:
63 	case H_LONG_BUSY_ORDER_1_SEC:
64 	case H_LONG_BUSY_ORDER_10_SEC:
65 	case H_LONG_BUSY_ORDER_100_SEC:
66 		return -EBUSY;
67 	case H_PARAMETER:
68 	case H_P2:
69 	case H_P3:
70 	case H_P4:
71 	case H_P5:
72 	case H_P6:
73 		return -EINVAL;
74 	case H_STATE:
75 		return -EIO;
76 	case H_AUTHORITY:
77 		return -EPERM;
78 	}
79 
80 	/*
81 	 * Return 1 for H_SUCCESS/H_PARTIAL
82 	 */
83 	return 1;
84 }
85 
86 static ssize_t htmdump_read(struct file *filp, char __user *ubuf,
87 			     size_t count, loff_t *ppos)
88 {
89 	void *htm_buf = filp->private_data;
90 	unsigned long page, read_size, available;
91 	loff_t offset;
92 	long rc, ret;
93 
94 	page = ALIGN_DOWN(*ppos, PAGE_SIZE);
95 	offset = (*ppos) % PAGE_SIZE;
96 
97 	/*
98 	 * Invoke H_HTM call with:
99 	 * - operation as htm dump (H_HTM_OP_DUMP_DATA)
100 	 * - last three values are address, size and offset
101 	 */
102 	rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
103 				   htmtype, H_HTM_OP_DUMP_DATA, virt_to_phys(htm_buf),
104 				   PAGE_SIZE, page);
105 
106 	ret = htm_return_check(rc);
107 	if (ret <= 0) {
108 		pr_debug("H_HTM hcall failed for op: H_HTM_OP_DUMP_DATA, returning %ld\n", ret);
109 		return ret;
110 	}
111 
112 	available = PAGE_SIZE;
113 	read_size = min(count, available);
114 	*ppos += read_size;
115 	return simple_read_from_buffer(ubuf, count, &offset, htm_buf, available);
116 }
117 
118 static const struct file_operations htmdump_fops = {
119 	.llseek = NULL,
120 	.read	= htmdump_read,
121 	.open	= simple_open,
122 };
123 
124 static int  htmconfigure_set(void *data, u64 val)
125 {
126 	long rc, ret;
127 	unsigned long param1 = -1, param2 = -1;
128 
129 	/*
130 	 * value as 1 : configure HTM.
131 	 * value as 0 : deconfigure HTM. Return -EINVAL for
132 	 * other values.
133 	 */
134 	if (val == HTM_ENABLE) {
135 		/*
136 		 * Invoke H_HTM call with:
137 		 * - operation as htm configure (H_HTM_OP_CONFIGURE)
138 		 * - If htmflags is set, param1 and param2 will be -1
139 		 *   which is an indicator to use default htm mode reg mask
140 		 *   and htm mode reg value.
141 		 * - last three values are unused, hence set to zero
142 		 */
143 		if (!htmflags) {
144 			param1 = 0;
145 			param2 = 0;
146 		}
147 
148 		rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
149 			   htmtype, H_HTM_OP_CONFIGURE, param1, param2, 0);
150 	} else if (val == HTM_DISABLE) {
151 		/*
152 		 * Invoke H_HTM call with:
153 		 * - operation as htm deconfigure (H_HTM_OP_DECONFIGURE)
154 		 * - last three values are unused, hence set to zero
155 		 */
156 		rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
157 				htmtype, H_HTM_OP_DECONFIGURE, 0, 0, 0);
158 	} else
159 		return -EINVAL;
160 
161 	ret = htm_return_check(rc);
162 	if (ret <= 0) {
163 		pr_debug("H_HTM hcall failed, returning %ld\n", ret);
164 		return ret;
165 	}
166 
167 	/* Set htmconfigure if operation succeeds */
168 	htmconfigure = val;
169 
170 	return 0;
171 }
172 
173 static int htmconfigure_get(void *data, u64 *val)
174 {
175 	*val = htmconfigure;
176 	return 0;
177 }
178 
179 static int  htmstart_set(void *data, u64 val)
180 {
181 	long rc, ret;
182 
183 	/*
184 	 * value as 1: start HTM
185 	 * value as 0: stop HTM
186 	 * Return -EINVAL for other values.
187 	 */
188 	if (val == HTM_ENABLE) {
189 		/*
190 		 * Invoke H_HTM call with:
191 		 * - operation as htm start (H_HTM_OP_START)
192 		 * - last three values are unused, hence set to zero
193 		 */
194 		rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
195 			   htmtype, H_HTM_OP_START, 0, 0, 0);
196 
197 	} else if (val == HTM_DISABLE) {
198 		/*
199 		 * Invoke H_HTM call with:
200 		 * - operation as htm stop (H_HTM_OP_STOP)
201 		 * - last three values are unused, hence set to zero
202 		 */
203 		rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
204 				htmtype, H_HTM_OP_STOP, 0, 0, 0);
205 	} else
206 		return -EINVAL;
207 
208 	ret = htm_return_check(rc);
209 	if (ret <= 0) {
210 		pr_debug("H_HTM hcall failed, returning %ld\n", ret);
211 		return ret;
212 	}
213 
214 	/* Set htmstart if H_HTM_OP_START/H_HTM_OP_STOP operation succeeds */
215 	htmstart = val;
216 
217 	return 0;
218 }
219 
220 static int htmstart_get(void *data, u64 *val)
221 {
222 	*val = htmstart;
223 	return 0;
224 }
225 
226 static ssize_t htmstatus_read(struct file *filp, char __user *ubuf,
227 			     size_t count, loff_t *ppos)
228 {
229 	void *htm_status_buf = filp->private_data;
230 	long rc, ret;
231 	u64 *num_entries;
232 	u64 to_copy;
233 	int htmstatus_flag;
234 
235 	/*
236 	 * Invoke H_HTM call with:
237 	 * - operation as htm status (H_HTM_OP_STATUS)
238 	 * - last three values as addr, size and offset
239 	 */
240 	rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
241 				   htmtype, H_HTM_OP_STATUS, virt_to_phys(htm_status_buf),
242 				   PAGE_SIZE, 0);
243 
244 	ret = htm_return_check(rc);
245 	if (ret <= 0) {
246 		pr_debug("H_HTM hcall failed for op: H_HTM_OP_STATUS, returning %ld\n", ret);
247 		return ret;
248 	}
249 
250 	/*
251 	 * HTM status buffer, start of buffer + 0x10 gives the
252 	 * number of HTM entries in the buffer. Each nest htm status
253 	 * entry is 0x6 bytes where each core htm status entry is
254 	 * 0x8 bytes.
255 	 * So total count to copy is:
256 	 * 32 bytes (for first 7 fields) + (number of HTM entries * entry size)
257 	 */
258 	num_entries = htm_status_buf + 0x10;
259 	if (htmtype == 0x2)
260 		htmstatus_flag = 0x8;
261 	else
262 		htmstatus_flag = 0x6;
263 	to_copy = 32 + (be64_to_cpu(*num_entries) * htmstatus_flag);
264 	return simple_read_from_buffer(ubuf, count, ppos, htm_status_buf, to_copy);
265 }
266 
267 static const struct file_operations htmstatus_fops = {
268 	.llseek = NULL,
269 	.read	= htmstatus_read,
270 	.open	= simple_open,
271 };
272 
273 static ssize_t htminfo_read(struct file *filp, char __user *ubuf,
274 			     size_t count, loff_t *ppos)
275 {
276 	void *htm_info_buf = filp->private_data;
277 	long rc, ret;
278 	u64 *num_entries;
279 	u64 to_copy;
280 
281 	/*
282 	 * Invoke H_HTM call with:
283 	 * - operation as htm status (H_HTM_OP_STATUS)
284 	 * - last three values as addr, size and offset
285 	 */
286 	rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
287 				   htmtype, H_HTM_OP_DUMP_SYSPROC_CONF, virt_to_phys(htm_info_buf),
288 				   PAGE_SIZE, 0);
289 
290 	ret = htm_return_check(rc);
291 	if (ret <= 0) {
292 		pr_debug("H_HTM hcall failed for op: H_HTM_OP_DUMP_SYSPROC_CONF, returning %ld\n", ret);
293 		return ret;
294 	}
295 
296 	/*
297 	 * HTM status buffer, start of buffer + 0x10 gives the
298 	 * number of HTM entries in the buffer. Each entry of processor
299 	 * is 16 bytes.
300 	 *
301 	 * So total count to copy is:
302 	 * 32 bytes (for first 5 fields) + (number of HTM entries * entry size)
303 	 */
304 	num_entries = htm_info_buf + 0x10;
305 	to_copy = 32 + (be64_to_cpu(*num_entries) * 16);
306 	return simple_read_from_buffer(ubuf, count, ppos, htm_info_buf, to_copy);
307 }
308 
309 static ssize_t htmcaps_read(struct file *filp, char __user *ubuf,
310 			     size_t count, loff_t *ppos)
311 {
312 	void *htm_caps_buf = filp->private_data;
313 	long rc, ret;
314 
315 	/*
316 	 * Invoke H_HTM call with:
317 	 * - operation as htm capabilities (H_HTM_OP_CAPABILITIES)
318 	 * - last three values as addr, size (0x80 for Capabilities Output Buffer
319 	 *   and zero
320 	 */
321 	rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
322 				   htmtype, H_HTM_OP_CAPABILITIES, virt_to_phys(htm_caps_buf),
323 				   0x80, 0);
324 
325 	ret = htm_return_check(rc);
326 	if (ret <= 0) {
327 		pr_debug("H_HTM hcall failed for op: H_HTM_OP_CAPABILITIES, returning %ld\n", ret);
328 		return ret;
329 	}
330 
331 	return simple_read_from_buffer(ubuf, count, ppos, htm_caps_buf, 0x80);
332 }
333 
334 static const struct file_operations htminfo_fops = {
335 	.llseek = NULL,
336 	.read   = htminfo_read,
337 	.open   = simple_open,
338 };
339 
340 static const struct file_operations htmcaps_fops = {
341 	.llseek = NULL,
342 	.read   = htmcaps_read,
343 	.open   = simple_open,
344 };
345 
346 static int  htmsetup_set(void *data, u64 val)
347 {
348 	long rc, ret;
349 
350 	/*
351 	 * Input value: HTM buffer size in the power of 2
352 	 * example: hex value 0x21 ( decimal: 33 ) is for
353 	 * 8GB
354 	 * Invoke H_HTM call with:
355 	 * - operation as htm start (H_HTM_OP_SETUP)
356 	 * - parameter 1 set to input value.
357 	 * - last two values are unused, hence set to zero
358 	 */
359 	rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
360 			htmtype, H_HTM_OP_SETUP, val, 0, 0);
361 
362 	ret = htm_return_check(rc);
363 	if (ret <= 0) {
364 		pr_debug("H_HTM hcall failed for op: H_HTM_OP_SETUP, returning %ld\n", ret);
365 		return ret;
366 	}
367 
368 	/* Set htmsetup if H_HTM_OP_SETUP operation succeeds */
369 	htmsetup = val;
370 
371 	return 0;
372 }
373 
374 static int htmsetup_get(void *data, u64 *val)
375 {
376 	*val = htmsetup;
377 	return 0;
378 }
379 
380 static int  htmflags_set(void *data, u64 val)
381 {
382 	/*
383 	 * Input value:
384 	 * Currently supported flag value is to enable/disable
385 	 * HTM buffer wrap. wrap is used along with "configure"
386 	 * to prevent HTM buffer from wrapping.
387 	 * Writing 1 will set noWrap while configuring HTM
388 	 */
389 	if (val == HTM_NOWRAP)
390 		htmflags = H_HTM_FLAGS_NOWRAP;
391 	else if (val == HTM_WRAP)
392 		htmflags = 0;
393 	else
394 		return -EINVAL;
395 
396 	return 0;
397 }
398 
399 static int htmflags_get(void *data, u64 *val)
400 {
401 	*val = htmflags;
402 	return 0;
403 }
404 
405 DEFINE_SIMPLE_ATTRIBUTE(htmconfigure_fops, htmconfigure_get, htmconfigure_set, "%llu\n");
406 DEFINE_SIMPLE_ATTRIBUTE(htmstart_fops, htmstart_get, htmstart_set, "%llu\n");
407 DEFINE_SIMPLE_ATTRIBUTE(htmsetup_fops, htmsetup_get, htmsetup_set, "%llu\n");
408 DEFINE_SIMPLE_ATTRIBUTE(htmflags_fops, htmflags_get, htmflags_set, "%llu\n");
409 
410 static int htmdump_init_debugfs(void)
411 {
412 	htm_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
413 	if (!htm_buf) {
414 		pr_err("Failed to allocate htmdump buf\n");
415 		return -ENOMEM;
416 	}
417 
418 	htmdump_debugfs_dir = debugfs_create_dir("htmdump",
419 						  arch_debugfs_dir);
420 
421 	debugfs_create_u32("nodeindex", 0600,
422 			htmdump_debugfs_dir, &nodeindex);
423 	debugfs_create_u32("nodalchipindex", 0600,
424 			htmdump_debugfs_dir, &nodalchipindex);
425 	debugfs_create_u32("coreindexonchip", 0600,
426 			htmdump_debugfs_dir, &coreindexonchip);
427 	debugfs_create_u32("htmtype", 0600,
428 			htmdump_debugfs_dir, &htmtype);
429 	debugfs_create_file("trace", 0400, htmdump_debugfs_dir, htm_buf, &htmdump_fops);
430 
431 	/*
432 	 * Debugfs interface files to control HTM operations:
433 	 */
434 	debugfs_create_file("htmconfigure", 0600, htmdump_debugfs_dir, NULL, &htmconfigure_fops);
435 	debugfs_create_file("htmstart", 0600, htmdump_debugfs_dir, NULL, &htmstart_fops);
436 	debugfs_create_file("htmsetup", 0600, htmdump_debugfs_dir, NULL, &htmsetup_fops);
437 	debugfs_create_file("htmflags", 0600, htmdump_debugfs_dir, NULL, &htmflags_fops);
438 
439 	/* Debugfs interface file to present status of HTM */
440 	htm_status_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
441 	if (!htm_status_buf) {
442 		pr_err("Failed to allocate htmstatus buf\n");
443 		return -ENOMEM;
444 	}
445 
446 	/* Debugfs interface file to present System Processor Configuration */
447 	htm_info_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
448 	if (!htm_info_buf) {
449 		pr_err("Failed to allocate htm info buf\n");
450 		return -ENOMEM;
451 	}
452 
453 	/* Debugfs interface file to present HTM capabilities */
454 	htm_caps_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
455 	if (!htm_caps_buf) {
456 		pr_err("Failed to allocate htm caps buf\n");
457 		return -ENOMEM;
458 	}
459 
460 	debugfs_create_file("htmstatus", 0400, htmdump_debugfs_dir, htm_status_buf, &htmstatus_fops);
461 	debugfs_create_file("htminfo", 0400, htmdump_debugfs_dir, htm_info_buf, &htminfo_fops);
462 	debugfs_create_file("htmcaps", 0400, htmdump_debugfs_dir, htm_caps_buf, &htmcaps_fops);
463 
464 	return 0;
465 }
466 
467 static int __init htmdump_init(void)
468 {
469 	/* Disable on kvm guest */
470 	if (is_kvm_guest()) {
471 		pr_info("htmdump not supported inside KVM guest\n");
472 		return -EOPNOTSUPP;
473 	}
474 
475 	if (htmdump_init_debugfs())
476 		return -ENOMEM;
477 
478 	return 0;
479 }
480 
481 static void __exit htmdump_exit(void)
482 {
483 	debugfs_remove_recursive(htmdump_debugfs_dir);
484 	kfree(htm_buf);
485 }
486 
487 module_init(htmdump_init);
488 module_exit(htmdump_exit);
489 MODULE_DESCRIPTION("PHYP Hardware Trace Macro (HTM) data dumper");
490 MODULE_LICENSE("GPL");
491