162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2014, Michael Ellerman, IBM Corp.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <errno.h>
762306a36Sopenharmony_ci#include <stdio.h>
862306a36Sopenharmony_ci#include <stdlib.h>
962306a36Sopenharmony_ci#include <string.h>
1062306a36Sopenharmony_ci#include <sys/mman.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "trace.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistruct trace_buffer *trace_buffer_allocate(u64 size)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	struct trace_buffer *tb;
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci	if (size < sizeof(*tb)) {
2062306a36Sopenharmony_ci		fprintf(stderr, "Error: trace buffer too small\n");
2162306a36Sopenharmony_ci		return NULL;
2262306a36Sopenharmony_ci	}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	tb = mmap(NULL, size, PROT_READ | PROT_WRITE,
2562306a36Sopenharmony_ci		  MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
2662306a36Sopenharmony_ci	if (tb == MAP_FAILED) {
2762306a36Sopenharmony_ci		perror("mmap");
2862306a36Sopenharmony_ci		return NULL;
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	tb->size = size;
3262306a36Sopenharmony_ci	tb->tail = tb->data;
3362306a36Sopenharmony_ci	tb->overflow = false;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return tb;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic bool trace_check_bounds(struct trace_buffer *tb, void *p)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return p < ((void *)tb + tb->size);
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic bool trace_check_alloc(struct trace_buffer *tb, void *p)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	/*
4662306a36Sopenharmony_ci	 * If we ever overflowed don't allow any more input. This prevents us
4762306a36Sopenharmony_ci	 * from dropping a large item and then later logging a small one. The
4862306a36Sopenharmony_ci	 * buffer should just stop when overflow happened, not be patchy. If
4962306a36Sopenharmony_ci	 * you're overflowing, make your buffer bigger.
5062306a36Sopenharmony_ci	 */
5162306a36Sopenharmony_ci	if (tb->overflow)
5262306a36Sopenharmony_ci		return false;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (!trace_check_bounds(tb, p)) {
5562306a36Sopenharmony_ci		tb->overflow = true;
5662306a36Sopenharmony_ci		return false;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return true;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void *trace_alloc(struct trace_buffer *tb, int bytes)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	void *p, *newtail;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	p = tb->tail;
6762306a36Sopenharmony_ci	newtail = tb->tail + bytes;
6862306a36Sopenharmony_ci	if (!trace_check_alloc(tb, newtail))
6962306a36Sopenharmony_ci		return NULL;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	tb->tail = newtail;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	return p;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct trace_entry *e;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	e = trace_alloc(tb, sizeof(*e) + payload_size);
8162306a36Sopenharmony_ci	if (e)
8262306a36Sopenharmony_ci		e->length = payload_size;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return e;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ciint trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct trace_entry *e;
9062306a36Sopenharmony_ci	u64 *p;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value));
9362306a36Sopenharmony_ci	if (!e)
9462306a36Sopenharmony_ci		return -ENOSPC;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	e->type = TRACE_TYPE_REG;
9762306a36Sopenharmony_ci	p = (u64 *)e->data;
9862306a36Sopenharmony_ci	*p++ = reg;
9962306a36Sopenharmony_ci	*p++ = value;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return 0;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ciint trace_log_counter(struct trace_buffer *tb, u64 value)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct trace_entry *e;
10762306a36Sopenharmony_ci	u64 *p;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	e = trace_alloc_entry(tb, sizeof(value));
11062306a36Sopenharmony_ci	if (!e)
11162306a36Sopenharmony_ci		return -ENOSPC;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	e->type = TRACE_TYPE_COUNTER;
11462306a36Sopenharmony_ci	p = (u64 *)e->data;
11562306a36Sopenharmony_ci	*p++ = value;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return 0;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciint trace_log_string(struct trace_buffer *tb, char *str)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct trace_entry *e;
12362306a36Sopenharmony_ci	char *p;
12462306a36Sopenharmony_ci	int len;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	len = strlen(str);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* We NULL terminate to make printing easier */
12962306a36Sopenharmony_ci	e = trace_alloc_entry(tb, len + 1);
13062306a36Sopenharmony_ci	if (!e)
13162306a36Sopenharmony_ci		return -ENOSPC;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	e->type = TRACE_TYPE_STRING;
13462306a36Sopenharmony_ci	p = (char *)e->data;
13562306a36Sopenharmony_ci	memcpy(p, str, len);
13662306a36Sopenharmony_ci	p += len;
13762306a36Sopenharmony_ci	*p = '\0';
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciint trace_log_indent(struct trace_buffer *tb)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct trace_entry *e;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	e = trace_alloc_entry(tb, 0);
14762306a36Sopenharmony_ci	if (!e)
14862306a36Sopenharmony_ci		return -ENOSPC;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	e->type = TRACE_TYPE_INDENT;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return 0;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ciint trace_log_outdent(struct trace_buffer *tb)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct trace_entry *e;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	e = trace_alloc_entry(tb, 0);
16062306a36Sopenharmony_ci	if (!e)
16162306a36Sopenharmony_ci		return -ENOSPC;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	e->type = TRACE_TYPE_OUTDENT;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void trace_print_header(int seq, int prefix)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	printf("%*s[%d]: ", prefix, "", seq);
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic char *trace_decode_reg(int reg)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	switch (reg) {
17662306a36Sopenharmony_ci		case 769: return "SPRN_MMCR2"; break;
17762306a36Sopenharmony_ci		case 770: return "SPRN_MMCRA"; break;
17862306a36Sopenharmony_ci		case 779: return "SPRN_MMCR0"; break;
17962306a36Sopenharmony_ci		case 804: return "SPRN_EBBHR"; break;
18062306a36Sopenharmony_ci		case 805: return "SPRN_EBBRR"; break;
18162306a36Sopenharmony_ci		case 806: return "SPRN_BESCR"; break;
18262306a36Sopenharmony_ci		case 800: return "SPRN_BESCRS"; break;
18362306a36Sopenharmony_ci		case 801: return "SPRN_BESCRSU"; break;
18462306a36Sopenharmony_ci		case 802: return "SPRN_BESCRR"; break;
18562306a36Sopenharmony_ci		case 803: return "SPRN_BESCRRU"; break;
18662306a36Sopenharmony_ci		case 771: return "SPRN_PMC1"; break;
18762306a36Sopenharmony_ci		case 772: return "SPRN_PMC2"; break;
18862306a36Sopenharmony_ci		case 773: return "SPRN_PMC3"; break;
18962306a36Sopenharmony_ci		case 774: return "SPRN_PMC4"; break;
19062306a36Sopenharmony_ci		case 775: return "SPRN_PMC5"; break;
19162306a36Sopenharmony_ci		case 776: return "SPRN_PMC6"; break;
19262306a36Sopenharmony_ci		case 780: return "SPRN_SIAR"; break;
19362306a36Sopenharmony_ci		case 781: return "SPRN_SDAR"; break;
19462306a36Sopenharmony_ci		case 768: return "SPRN_SIER"; break;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return NULL;
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic void trace_print_reg(struct trace_entry *e)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	u64 *p, *reg, *value;
20362306a36Sopenharmony_ci	char *name;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	p = (u64 *)e->data;
20662306a36Sopenharmony_ci	reg = p++;
20762306a36Sopenharmony_ci	value = p;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	name = trace_decode_reg(*reg);
21062306a36Sopenharmony_ci	if (name)
21162306a36Sopenharmony_ci		printf("register %-10s = 0x%016llx\n", name, *value);
21262306a36Sopenharmony_ci	else
21362306a36Sopenharmony_ci		printf("register %lld = 0x%016llx\n", *reg, *value);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic void trace_print_counter(struct trace_entry *e)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	u64 *value;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	value = (u64 *)e->data;
22162306a36Sopenharmony_ci	printf("counter = %lld\n", *value);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic void trace_print_string(struct trace_entry *e)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	char *str;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	str = (char *)e->data;
22962306a36Sopenharmony_ci	puts(str);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci#define BASE_PREFIX	2
23362306a36Sopenharmony_ci#define PREFIX_DELTA	8
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic void trace_print_entry(struct trace_entry *e, int seq, int *prefix)
23662306a36Sopenharmony_ci{
23762306a36Sopenharmony_ci	switch (e->type) {
23862306a36Sopenharmony_ci	case TRACE_TYPE_REG:
23962306a36Sopenharmony_ci		trace_print_header(seq, *prefix);
24062306a36Sopenharmony_ci		trace_print_reg(e);
24162306a36Sopenharmony_ci		break;
24262306a36Sopenharmony_ci	case TRACE_TYPE_COUNTER:
24362306a36Sopenharmony_ci		trace_print_header(seq, *prefix);
24462306a36Sopenharmony_ci		trace_print_counter(e);
24562306a36Sopenharmony_ci		break;
24662306a36Sopenharmony_ci	case TRACE_TYPE_STRING:
24762306a36Sopenharmony_ci		trace_print_header(seq, *prefix);
24862306a36Sopenharmony_ci		trace_print_string(e);
24962306a36Sopenharmony_ci		break;
25062306a36Sopenharmony_ci	case TRACE_TYPE_INDENT:
25162306a36Sopenharmony_ci		trace_print_header(seq, *prefix);
25262306a36Sopenharmony_ci		puts("{");
25362306a36Sopenharmony_ci		*prefix += PREFIX_DELTA;
25462306a36Sopenharmony_ci		break;
25562306a36Sopenharmony_ci	case TRACE_TYPE_OUTDENT:
25662306a36Sopenharmony_ci		*prefix -= PREFIX_DELTA;
25762306a36Sopenharmony_ci		if (*prefix < BASE_PREFIX)
25862306a36Sopenharmony_ci			*prefix = BASE_PREFIX;
25962306a36Sopenharmony_ci		trace_print_header(seq, *prefix);
26062306a36Sopenharmony_ci		puts("}");
26162306a36Sopenharmony_ci		break;
26262306a36Sopenharmony_ci	default:
26362306a36Sopenharmony_ci		trace_print_header(seq, *prefix);
26462306a36Sopenharmony_ci		printf("entry @ %p type %d\n", e, e->type);
26562306a36Sopenharmony_ci		break;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_civoid trace_buffer_print(struct trace_buffer *tb)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct trace_entry *e;
27262306a36Sopenharmony_ci	int i, prefix;
27362306a36Sopenharmony_ci	void *p;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	printf("Trace buffer dump:\n");
27662306a36Sopenharmony_ci	printf("  address  %p \n", tb);
27762306a36Sopenharmony_ci	printf("  tail     %p\n", tb->tail);
27862306a36Sopenharmony_ci	printf("  size     %llu\n", tb->size);
27962306a36Sopenharmony_ci	printf("  overflow %s\n", tb->overflow ? "TRUE" : "false");
28062306a36Sopenharmony_ci	printf("  Content:\n");
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	p = tb->data;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	i = 0;
28562306a36Sopenharmony_ci	prefix = BASE_PREFIX;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	while (trace_check_bounds(tb, p) && p < tb->tail) {
28862306a36Sopenharmony_ci		e = p;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		trace_print_entry(e, i, &prefix);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci		i++;
29362306a36Sopenharmony_ci		p = (void *)e + sizeof(*e) + e->length;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_civoid trace_print_location(struct trace_buffer *tb)
29862306a36Sopenharmony_ci{
29962306a36Sopenharmony_ci	printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb);
30062306a36Sopenharmony_ci}
301