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