File Index
Files and directories
Sign in
FILE

htmdump.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/platforms/pseries/htmdump.c C
  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
 15static void *htm_buf;
 16static void *htm_status_buf;
 17static void *htm_info_buf;
 18static void *htm_caps_buf;
 19static u32 nodeindex;
 20static u32 nodalchipindex;
 21static u32 coreindexonchip;
 22static u32 htmtype;
 23static u32 htmconfigure;
 24static u32 htmstart;
 25static u32 htmsetup;
 26static u64 htmflags;
 27
 28static 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 */
 45static 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
 86static 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
118static const struct file_operations htmdump_fops = {
119	.llseek = NULL,
120	.read	= htmdump_read,
121	.open	= simple_open,
122};
123
124static 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
173static int htmconfigure_get(void *data, u64 *val)
174{
175	*val = htmconfigure;
176	return 0;
177}
178
179static 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
220static int htmstart_get(void *data, u64 *val)
221{
222	*val = htmstart;
223	return 0;
224}
225
226static 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
267static const struct file_operations htmstatus_fops = {
268	.llseek = NULL,
269	.read	= htmstatus_read,
270	.open	= simple_open,
271};
272
273static 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
309static 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
334static const struct file_operations htminfo_fops = {
335	.llseek = NULL,
336	.read   = htminfo_read,
337	.open   = simple_open,
338};
339
340static const struct file_operations htmcaps_fops = {
341	.llseek = NULL,
342	.read   = htmcaps_read,
343	.open   = simple_open,
344};
345
346static 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
374static int htmsetup_get(void *data, u64 *val)
375{
376	*val = htmsetup;
377	return 0;
378}
379
380static 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
399static int htmflags_get(void *data, u64 *val)
400{
401	*val = htmflags;
402	return 0;
403}
404
405DEFINE_SIMPLE_ATTRIBUTE(htmconfigure_fops, htmconfigure_get, htmconfigure_set, "%llu\n");
406DEFINE_SIMPLE_ATTRIBUTE(htmstart_fops, htmstart_get, htmstart_set, "%llu\n");
407DEFINE_SIMPLE_ATTRIBUTE(htmsetup_fops, htmsetup_get, htmsetup_set, "%llu\n");
408DEFINE_SIMPLE_ATTRIBUTE(htmflags_fops, htmflags_get, htmflags_set, "%llu\n");
409
410static 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
467static 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
481static void __exit htmdump_exit(void)
482{
483	debugfs_remove_recursive(htmdump_debugfs_dir);
484	kfree(htm_buf);
485}
486
487module_init(htmdump_init);
488module_exit(htmdump_exit);
489MODULE_DESCRIPTION("PHYP Hardware Trace Macro (HTM) data dumper");
490MODULE_LICENSE("GPL");
~/ Read-only file preview.