aboutsummaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/pseries/htmdump.c
blob: 489a80e8708218ac8cbc88d3f7c7b1c1b81c1587 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (C) IBM Corporation, 2024
 */

#define pr_fmt(fmt) "htmdump: " fmt

#include <linux/debugfs.h>
#include <linux/module.h>
#include <asm/io.h>
#include <asm/machdep.h>
#include <asm/plpar_wrappers.h>
#include <asm/kvm_guest.h>

static void *htm_buf;
static void *htm_status_buf;
static void *htm_info_buf;
static void *htm_caps_buf;
static void *htm_mem_buf;
static u32 nodeindex;
static u32 nodalchipindex;
static u32 coreindexonchip;
static u32 htmtype;
static u32 htmconfigure;
static u32 htmstart;
static u32 htmsetup;
static u64 htmflags;

static struct dentry *htmdump_debugfs_dir;
#define	HTM_ENABLE	1
#define	HTM_DISABLE	0
#define	HTM_NOWRAP	1
#define	HTM_WRAP	0

/*
 * Check the return code for H_HTM hcall.
 * Return non-zero value (1) if either H_PARTIAL or H_SUCCESS
 * is returned. For other return codes:
 * Return zero if H_NOT_AVAILABLE.
 * Return -EBUSY if hcall return busy.
 * Return -EINVAL if any parameter or operation is not valid.
 * Return -EPERM if HTM Virtualization Engine Technology code
 * is not applied.
 * Return -EIO if the HTM state is not valid.
 */
static ssize_t htm_return_check(long rc)
{
	switch (rc) {
	case H_SUCCESS:
	/* H_PARTIAL for the case where all available data can't be
	 * returned due to buffer size constraint.
	 */
	case H_PARTIAL:
		break;
	/* H_NOT_AVAILABLE indicates reading from an offset outside the range,
	 * i.e. past end of file.
	 */
	case H_NOT_AVAILABLE:
		return 0;
	case H_BUSY:
	case H_LONG_BUSY_ORDER_1_MSEC:
	case H_LONG_BUSY_ORDER_10_MSEC:
	case H_LONG_BUSY_ORDER_100_MSEC:
	case H_LONG_BUSY_ORDER_1_SEC:
	case H_LONG_BUSY_ORDER_10_SEC:
	case H_LONG_BUSY_ORDER_100_SEC:
		return -EBUSY;
	case H_PARAMETER:
	case H_P2:
	case H_P3:
	case H_P4:
	case H_P5:
	case H_P6:
		return -EINVAL;
	case H_STATE:
		return -EIO;
	case H_AUTHORITY:
		return -EPERM;
	}

	/*
	 * Return 1 for H_SUCCESS/H_PARTIAL
	 */
	return 1;
}

static ssize_t htmdump_read(struct file *filp, char __user *ubuf,
			     size_t count, loff_t *ppos)
{
	void *htm_buf_data = filp->private_data;
	unsigned long page, read_size, available;
	loff_t offset;
	long rc, ret;

	page = ALIGN_DOWN(*ppos, PAGE_SIZE);
	offset = (*ppos) % PAGE_SIZE;

	/*
	 * Invoke H_HTM call with:
	 * - operation as htm dump (H_HTM_OP_DUMP_DATA)
	 * - last three values are address, size and offset
	 */
	rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
				   htmtype, H_HTM_OP_DUMP_DATA, virt_to_phys(htm_buf_data),
				   PAGE_SIZE, page);

	ret = htm_return_check(rc);
	if (ret <= 0) {
		pr_debug("H_HTM hcall failed for op: H_HTM_OP_DUMP_DATA, returning %ld\n", ret);
		return ret;
	}

	available = PAGE_SIZE;
	read_size = min(count, available);
	*ppos += read_size;
	return simple_read_from_buffer(ubuf, count, &offset, htm_buf_data, available);
}

static ssize_t htmsystem_mem_read(struct file *filp, char __user *ubuf,
		size_t count, loff_t *ppos)
{
	void *htm_mem_data = filp->private_data;
	long rc, ret;
	u64 *num_entries;
	u64 to_copy = 0;
	loff_t offset = 0;
	u64 mem_offset = 0;

	/*
	 * Invoke H_HTM call with:
	 * - operation as htm status (H_HTM_OP_STATUS)
	 * - last three values as addr, size and offset. "offset"
	 *   is value from output buffer header that points to next
	 *   entry to dump. 0 is the first entry to dump. next entry
	 *   is read from the output bufferbyte offset 0x8.
	 *
	 *   When first time hcall is invoked, mem_offset should be
	 *   zero because zero is the first entry.
	 *   In the next hcall, offset of next entry to read from is
	 *   picked from output buffer header itself. So don't fill
	 *   mem_offset for first read.
	 *
	 *  If there is no further data to read in next iteration,
	 *  offset value from output buffer header will point to -1.
	 */
	if (*ppos) {
		mem_offset = *(u64 *)(htm_mem_data  + 0x8);
		if (mem_offset == -1)
			return 0;
	}
	rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
			htmtype, H_HTM_OP_DUMP_SYSMEM_CONF, virt_to_phys(htm_mem_data),
			PAGE_SIZE, be64_to_cpu(mem_offset));
	ret = htm_return_check(rc);
	if (ret <= 0) {
		pr_debug("H_HTM hcall returned for op: H_HTM_OP_DUMP_SYSMEM_CONF with hcall returning  %ld\n", ret);
		return ret;
	}

	/*
	 * HTM system mem buffer, start of buffer + 0x10 gives the
	 * number of HTM entries in the buffer.
	 * So total count to copy is:
	 * 32 bytes (for first 5 fields) + (number of HTM entries * entry size)
	 */
	num_entries = htm_mem_data + 0x10;
	to_copy = 32 + (be64_to_cpu(*num_entries) * 32);

	*ppos += to_copy;
	return simple_read_from_buffer(ubuf, count, &offset, htm_mem_data, to_copy);
}

static const struct file_operations htmdump_fops = {
	.llseek = NULL,
	.read	= htmdump_read,
	.open	= simple_open,
};

static const struct file_operations htmsystem_mem_fops = {
	.llseek = NULL,
	.read   = htmsystem_mem_read,
	.open   = simple_open,
};

static int  htmconfigure_set(void *data, u64 val)
{
	long rc, ret;
	unsigned long param1 = -1, param2 = -1;

	/*
	 * value as 1 : configure HTM.
	 * value as 0 : deconfigure HTM. Return -EINVAL for
	 * other values.
	 */
	if (val == HTM_ENABLE) {
		/*
		 * Invoke H_HTM call with:
		 * - operation as htm configure (H_HTM_OP_CONFIGURE)
		 * - If htmflags is set, param1 and param2 will be -1
		 *   which is an indicator to use default htm mode reg mask
		 *   and htm mode reg value.
		 * - last three values are unused, hence set to zero
		 */
		if (!htmflags) {
			param1 = 0;
			param2 = 0;
		}

		rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
			   htmtype, H_HTM_OP_CONFIGURE, param1, param2, 0);
	} else if (val == HTM_DISABLE) {
		/*
		 * Invoke H_HTM call with:
		 * - operation as htm deconfigure (H_HTM_OP_DECONFIGURE)
		 * - last three values are unused, hence set to zero
		 */
		rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
				htmtype, H_HTM_OP_DECONFIGURE, 0, 0, 0);
	} else
		return -EINVAL;

	ret = htm_return_check(rc);
	if (ret <= 0) {
		pr_debug("H_HTM hcall failed, returning %ld\n", ret);
		return ret;
	}

	/* Set htmconfigure if operation succeeds */
	htmconfigure = val;

	return 0;
}

static int htmconfigure_get(void *data, u64 *val)
{
	*val = htmconfigure;
	return 0;
}

static int  htmstart_set(void *data, u64 val)
{
	long rc, ret;

	/*
	 * value as 1: start HTM
	 * value as 0: stop HTM
	 * Return -EINVAL for other values.
	 */
	if (val == HTM_ENABLE) {
		/*
		 * Invoke H_HTM call with:
		 * - operation as htm start (H_HTM_OP_START)
		 * - last three values are unused, hence set to zero
		 */
		rc = htm_hcall_wrapper(htmflags, nodeindex, nodalchipindex, coreindexonchip,
			   htmtype, H_HTM_OP_START, 0, 0, 0);

	} else if (val == HTM_DISABLE) {
		/*
		 * Invoke H