162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only OR MIT
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Apple RTKit IPC library
462306a36Sopenharmony_ci * Copyright (C) The Asahi Linux Contributors
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "rtkit-internal.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define FOURCC(a, b, c, d) \
962306a36Sopenharmony_ci	(((u32)(a) << 24) | ((u32)(b) << 16) | ((u32)(c) << 8) | ((u32)(d)))
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define APPLE_RTKIT_CRASHLOG_HEADER FOURCC('C', 'L', 'H', 'E')
1262306a36Sopenharmony_ci#define APPLE_RTKIT_CRASHLOG_STR FOURCC('C', 's', 't', 'r')
1362306a36Sopenharmony_ci#define APPLE_RTKIT_CRASHLOG_VERSION FOURCC('C', 'v', 'e', 'r')
1462306a36Sopenharmony_ci#define APPLE_RTKIT_CRASHLOG_MBOX FOURCC('C', 'm', 'b', 'x')
1562306a36Sopenharmony_ci#define APPLE_RTKIT_CRASHLOG_TIME FOURCC('C', 't', 'i', 'm')
1662306a36Sopenharmony_ci#define APPLE_RTKIT_CRASHLOG_REGS FOURCC('C', 'r', 'g', '8')
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci/* For COMPILE_TEST on non-ARM64 architectures */
1962306a36Sopenharmony_ci#ifndef PSR_MODE_EL0t
2062306a36Sopenharmony_ci#define PSR_MODE_EL0t	0x00000000
2162306a36Sopenharmony_ci#define PSR_MODE_EL1t	0x00000004
2262306a36Sopenharmony_ci#define PSR_MODE_EL1h	0x00000005
2362306a36Sopenharmony_ci#define PSR_MODE_EL2t	0x00000008
2462306a36Sopenharmony_ci#define PSR_MODE_EL2h	0x00000009
2562306a36Sopenharmony_ci#define PSR_MODE_MASK	0x0000000f
2662306a36Sopenharmony_ci#endif
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistruct apple_rtkit_crashlog_header {
2962306a36Sopenharmony_ci	u32 fourcc;
3062306a36Sopenharmony_ci	u32 version;
3162306a36Sopenharmony_ci	u32 size;
3262306a36Sopenharmony_ci	u32 flags;
3362306a36Sopenharmony_ci	u8 _unk[16];
3462306a36Sopenharmony_ci};
3562306a36Sopenharmony_cistatic_assert(sizeof(struct apple_rtkit_crashlog_header) == 0x20);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_cistruct apple_rtkit_crashlog_mbox_entry {
3862306a36Sopenharmony_ci	u64 msg0;
3962306a36Sopenharmony_ci	u64 msg1;
4062306a36Sopenharmony_ci	u32 timestamp;
4162306a36Sopenharmony_ci	u8 _unk[4];
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_cistatic_assert(sizeof(struct apple_rtkit_crashlog_mbox_entry) == 0x18);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct apple_rtkit_crashlog_regs {
4662306a36Sopenharmony_ci	u32 unk_0;
4762306a36Sopenharmony_ci	u32 unk_4;
4862306a36Sopenharmony_ci	u64 regs[31];
4962306a36Sopenharmony_ci	u64 sp;
5062306a36Sopenharmony_ci	u64 pc;
5162306a36Sopenharmony_ci	u64 psr;
5262306a36Sopenharmony_ci	u64 cpacr;
5362306a36Sopenharmony_ci	u64 fpsr;
5462306a36Sopenharmony_ci	u64 fpcr;
5562306a36Sopenharmony_ci	u64 unk[64];
5662306a36Sopenharmony_ci	u64 far;
5762306a36Sopenharmony_ci	u64 unk_X;
5862306a36Sopenharmony_ci	u64 esr;
5962306a36Sopenharmony_ci	u64 unk_Z;
6062306a36Sopenharmony_ci} __packed;
6162306a36Sopenharmony_cistatic_assert(sizeof(struct apple_rtkit_crashlog_regs) == 0x350);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic void apple_rtkit_crashlog_dump_str(struct apple_rtkit *rtk, u8 *bfr,
6462306a36Sopenharmony_ci					  size_t size)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	u32 idx;
6762306a36Sopenharmony_ci	u8 *ptr, *end;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	memcpy(&idx, bfr, 4);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	ptr = bfr + 4;
7262306a36Sopenharmony_ci	end = bfr + size;
7362306a36Sopenharmony_ci	while (ptr < end) {
7462306a36Sopenharmony_ci		u8 *newline = memchr(ptr, '\n', end - ptr);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci		if (newline) {
7762306a36Sopenharmony_ci			u8 tmp = *newline;
7862306a36Sopenharmony_ci			*newline = '\0';
7962306a36Sopenharmony_ci			dev_warn(rtk->dev, "RTKit: Message (id=%x): %s\n", idx,
8062306a36Sopenharmony_ci				 ptr);
8162306a36Sopenharmony_ci			*newline = tmp;
8262306a36Sopenharmony_ci			ptr = newline + 1;
8362306a36Sopenharmony_ci		} else {
8462306a36Sopenharmony_ci			dev_warn(rtk->dev, "RTKit: Message (id=%x): %s", idx,
8562306a36Sopenharmony_ci				 ptr);
8662306a36Sopenharmony_ci			break;
8762306a36Sopenharmony_ci		}
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistatic void apple_rtkit_crashlog_dump_version(struct apple_rtkit *rtk, u8 *bfr,
9262306a36Sopenharmony_ci					      size_t size)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	dev_warn(rtk->dev, "RTKit: Version: %s", bfr + 16);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void apple_rtkit_crashlog_dump_time(struct apple_rtkit *rtk, u8 *bfr,
9862306a36Sopenharmony_ci					   size_t size)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	u64 crash_time;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	memcpy(&crash_time, bfr, 8);
10362306a36Sopenharmony_ci	dev_warn(rtk->dev, "RTKit: Crash time: %lld", crash_time);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic void apple_rtkit_crashlog_dump_mailbox(struct apple_rtkit *rtk, u8 *bfr,
10762306a36Sopenharmony_ci					      size_t size)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	u32 type, index, i;
11062306a36Sopenharmony_ci	size_t n_messages;
11162306a36Sopenharmony_ci	struct apple_rtkit_crashlog_mbox_entry entry;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	memcpy(&type, bfr + 16, 4);
11462306a36Sopenharmony_ci	memcpy(&index, bfr + 24, 4);
11562306a36Sopenharmony_ci	n_messages = (size - 28) / sizeof(entry);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	dev_warn(rtk->dev, "RTKit: Mailbox history (type = %d, index = %d)",
11862306a36Sopenharmony_ci		 type, index);
11962306a36Sopenharmony_ci	for (i = 0; i < n_messages; ++i) {
12062306a36Sopenharmony_ci		memcpy(&entry, bfr + 28 + i * sizeof(entry), sizeof(entry));
12162306a36Sopenharmony_ci		dev_warn(rtk->dev, "RTKit:  #%03d@%08x: %016llx %016llx", i,
12262306a36Sopenharmony_ci			 entry.timestamp, entry.msg0, entry.msg1);
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void apple_rtkit_crashlog_dump_regs(struct apple_rtkit *rtk, u8 *bfr,
12762306a36Sopenharmony_ci					   size_t size)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct apple_rtkit_crashlog_regs *regs;
13062306a36Sopenharmony_ci	const char *el;
13162306a36Sopenharmony_ci	int i;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (size < sizeof(*regs)) {
13462306a36Sopenharmony_ci		dev_warn(rtk->dev, "RTKit: Regs section too small: 0x%zx", size);
13562306a36Sopenharmony_ci		return;
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	regs = (struct apple_rtkit_crashlog_regs *)bfr;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	switch (regs->psr & PSR_MODE_MASK) {
14162306a36Sopenharmony_ci	case PSR_MODE_EL0t:
14262306a36Sopenharmony_ci		el = "EL0t";
14362306a36Sopenharmony_ci		break;
14462306a36Sopenharmony_ci	case PSR_MODE_EL1t:
14562306a36Sopenharmony_ci		el = "EL1t";
14662306a36Sopenharmony_ci		break;
14762306a36Sopenharmony_ci	case PSR_MODE_EL1h:
14862306a36Sopenharmony_ci		el = "EL1h";
14962306a36Sopenharmony_ci		break;
15062306a36Sopenharmony_ci	case PSR_MODE_EL2t:
15162306a36Sopenharmony_ci		el = "EL2t";
15262306a36Sopenharmony_ci		break;
15362306a36Sopenharmony_ci	case PSR_MODE_EL2h:
15462306a36Sopenharmony_ci		el = "EL2h";
15562306a36Sopenharmony_ci		break;
15662306a36Sopenharmony_ci	default:
15762306a36Sopenharmony_ci		el = "unknown";
15862306a36Sopenharmony_ci		break;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	dev_warn(rtk->dev, "RTKit: Exception dump:");
16262306a36Sopenharmony_ci	dev_warn(rtk->dev, "  == Exception taken from %s ==", el);
16362306a36Sopenharmony_ci	dev_warn(rtk->dev, "  PSR    = 0x%llx", regs->psr);
16462306a36Sopenharmony_ci	dev_warn(rtk->dev, "  PC     = 0x%llx\n", regs->pc);
16562306a36Sopenharmony_ci	dev_warn(rtk->dev, "  ESR    = 0x%llx\n", regs->esr);
16662306a36Sopenharmony_ci	dev_warn(rtk->dev, "  FAR    = 0x%llx\n", regs->far);
16762306a36Sopenharmony_ci	dev_warn(rtk->dev, "  SP     = 0x%llx\n", regs->sp);
16862306a36Sopenharmony_ci	dev_warn(rtk->dev, "\n");
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	for (i = 0; i < 31; i += 4) {
17162306a36Sopenharmony_ci		if (i < 28)
17262306a36Sopenharmony_ci			dev_warn(rtk->dev,
17362306a36Sopenharmony_ci					 "  x%02d-x%02d = %016llx %016llx %016llx %016llx\n",
17462306a36Sopenharmony_ci					 i, i + 3,
17562306a36Sopenharmony_ci					 regs->regs[i], regs->regs[i + 1],
17662306a36Sopenharmony_ci					 regs->regs[i + 2], regs->regs[i + 3]);
17762306a36Sopenharmony_ci		else
17862306a36Sopenharmony_ci			dev_warn(rtk->dev,
17962306a36Sopenharmony_ci					 "  x%02d-x%02d = %016llx %016llx %016llx\n", i, i + 3,
18062306a36Sopenharmony_ci					 regs->regs[i], regs->regs[i + 1], regs->regs[i + 2]);
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	dev_warn(rtk->dev, "\n");
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_civoid apple_rtkit_crashlog_dump(struct apple_rtkit *rtk, u8 *bfr, size_t size)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	size_t offset;
18962306a36Sopenharmony_ci	u32 section_fourcc, section_size;
19062306a36Sopenharmony_ci	struct apple_rtkit_crashlog_header header;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	memcpy(&header, bfr, sizeof(header));
19362306a36Sopenharmony_ci	if (header.fourcc != APPLE_RTKIT_CRASHLOG_HEADER) {
19462306a36Sopenharmony_ci		dev_warn(rtk->dev, "RTKit: Expected crashlog header but got %x",
19562306a36Sopenharmony_ci			 header.fourcc);
19662306a36Sopenharmony_ci		return;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (header.size > size) {
20062306a36Sopenharmony_ci		dev_warn(rtk->dev, "RTKit: Crashlog size (%x) is too large",
20162306a36Sopenharmony_ci			 header.size);
20262306a36Sopenharmony_ci		return;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	size = header.size;
20662306a36Sopenharmony_ci	offset = sizeof(header);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	while (offset < size) {
20962306a36Sopenharmony_ci		memcpy(&section_fourcc, bfr + offset, 4);
21062306a36Sopenharmony_ci		memcpy(&section_size, bfr + offset + 12, 4);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		switch (section_fourcc) {
21362306a36Sopenharmony_ci		case APPLE_RTKIT_CRASHLOG_HEADER:
21462306a36Sopenharmony_ci			dev_dbg(rtk->dev, "RTKit: End of crashlog reached");
21562306a36Sopenharmony_ci			return;
21662306a36Sopenharmony_ci		case APPLE_RTKIT_CRASHLOG_STR:
21762306a36Sopenharmony_ci			apple_rtkit_crashlog_dump_str(rtk, bfr + offset + 16,
21862306a36Sopenharmony_ci						      section_size);
21962306a36Sopenharmony_ci			break;
22062306a36Sopenharmony_ci		case APPLE_RTKIT_CRASHLOG_VERSION:
22162306a36Sopenharmony_ci			apple_rtkit_crashlog_dump_version(
22262306a36Sopenharmony_ci				rtk, bfr + offset + 16, section_size);
22362306a36Sopenharmony_ci			break;
22462306a36Sopenharmony_ci		case APPLE_RTKIT_CRASHLOG_MBOX:
22562306a36Sopenharmony_ci			apple_rtkit_crashlog_dump_mailbox(
22662306a36Sopenharmony_ci				rtk, bfr + offset + 16, section_size);
22762306a36Sopenharmony_ci			break;
22862306a36Sopenharmony_ci		case APPLE_RTKIT_CRASHLOG_TIME:
22962306a36Sopenharmony_ci			apple_rtkit_crashlog_dump_time(rtk, bfr + offset + 16,
23062306a36Sopenharmony_ci						       section_size);
23162306a36Sopenharmony_ci			break;
23262306a36Sopenharmony_ci		case APPLE_RTKIT_CRASHLOG_REGS:
23362306a36Sopenharmony_ci			apple_rtkit_crashlog_dump_regs(rtk, bfr + offset + 16,
23462306a36Sopenharmony_ci						       section_size);
23562306a36Sopenharmony_ci			break;
23662306a36Sopenharmony_ci		default:
23762306a36Sopenharmony_ci			dev_warn(rtk->dev,
23862306a36Sopenharmony_ci				 "RTKit: Unknown crashlog section: %x",
23962306a36Sopenharmony_ci				 section_fourcc);
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		offset += section_size;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	dev_warn(rtk->dev,
24662306a36Sopenharmony_ci		 "RTKit: End of crashlog reached but no footer present");
24762306a36Sopenharmony_ci}
248