Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/powerpc/platforms/pseries/htmdump.c
26481 views
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
492