1// SPDX-License-Identifier: GPL-2.0-only OR MIT
2/*
3 * Apple RTKit IPC library
4 * Copyright (C) The Asahi Linux Contributors
5 */
6#include "rtkit-internal.h"
7
8#define FOURCC(a, b, c, d) \
9	(((u32)(a) << 24) | ((u32)(b) << 16) | ((u32)(c) << 8) | ((u32)(d)))
10
11#define APPLE_RTKIT_CRASHLOG_HEADER FOURCC('C', 'L', 'H', 'E')
12#define APPLE_RTKIT_CRASHLOG_STR FOURCC('C', 's', 't', 'r')
13#define APPLE_RTKIT_CRASHLOG_VERSION FOURCC('C', 'v', 'e', 'r')
14#define APPLE_RTKIT_CRASHLOG_MBOX FOURCC('C', 'm', 'b', 'x')
15#define APPLE_RTKIT_CRASHLOG_TIME FOURCC('C', 't', 'i', 'm')
16#define APPLE_RTKIT_CRASHLOG_REGS FOURCC('C', 'r', 'g', '8')
17
18/* For COMPILE_TEST on non-ARM64 architectures */
19#ifndef PSR_MODE_EL0t
20#define PSR_MODE_EL0t	0x00000000
21#define PSR_MODE_EL1t	0x00000004
22#define PSR_MODE_EL1h	0x00000005
23#define PSR_MODE_EL2t	0x00000008
24#define PSR_MODE_EL2h	0x00000009
25#define PSR_MODE_MASK	0x0000000f
26#endif
27
28struct apple_rtkit_crashlog_header {
29	u32 fourcc;
30	u32 version;
31	u32 size;
32	u32 flags;
33	u8 _unk[16];
34};
35static_assert(sizeof(struct apple_rtkit_crashlog_header) == 0x20);
36
37struct apple_rtkit_crashlog_mbox_entry {
38	u64 msg0;
39	u64 msg1;
40	u32 timestamp;
41	u8 _unk[4];
42};
43static_assert(sizeof(struct apple_rtkit_crashlog_mbox_entry) == 0x18);
44
45struct apple_rtkit_crashlog_regs {
46	u32 unk_0;
47	u32 unk_4;
48	u64 regs[31];
49	u64 sp;
50	u64 pc;
51	u64 psr;
52	u64 cpacr;
53	u64 fpsr;
54	u64 fpcr;
55	u64 unk[64];
56	u64 far;
57	u64 unk_X;
58	u64 esr;
59	u64 unk_Z;
60} __packed;
61static_assert(sizeof(struct apple_rtkit_crashlog_regs) == 0x350);
62
63static void apple_rtkit_crashlog_dump_str(struct apple_rtkit *rtk, u8 *bfr,
64					  size_t size)
65{
66	u32 idx;
67	u8 *ptr, *end;
68
69	memcpy(&idx, bfr, 4);
70
71	ptr = bfr + 4;
72	end = bfr + size;
73	while (ptr < end) {
74		u8 *newline = memchr(ptr, '\n', end - ptr);
75
76		if (newline) {
77			u8 tmp = *newline;
78			*newline = '\0';
79			dev_warn(rtk->dev, "RTKit: Message (id=%x): %s\n", idx,
80				 ptr);
81			*newline = tmp;
82			ptr = newline + 1;
83		} else {
84			dev_warn(rtk->dev, "RTKit: Message (id=%x): %s", idx,
85				 ptr);
86			break;
87		}
88	}
89}
90
91static void apple_rtkit_crashlog_dump_version(struct apple_rtkit *rtk, u8 *bfr,
92					      size_t size)
93{
94	dev_warn(rtk->dev, "RTKit: Version: %s", bfr + 16);
95}
96
97static void apple_rtkit_crashlog_dump_time(struct apple_rtkit *rtk, u8 *bfr,
98					   size_t size)
99{
100	u64 crash_time;
101
102	memcpy(&crash_time, bfr, 8);
103	dev_warn(rtk->dev, "RTKit: Crash time: %lld", crash_time);
104}
105
106static void apple_rtkit_crashlog_dump_mailbox(struct apple_rtkit *rtk, u8 *bfr,
107					      size_t size)
108{
109	u32 type, index, i;
110	size_t n_messages;
111	struct apple_rtkit_crashlog_mbox_entry entry;
112
113	memcpy(&type, bfr + 16, 4);
114	memcpy(&index, bfr + 24, 4);
115	n_messages = (size - 28) / sizeof(entry);
116
117	dev_warn(rtk->dev, "RTKit: Mailbox history (type = %d, index = %d)",
118		 type, index);
119	for (i = 0; i < n_messages; ++i) {
120		memcpy(&entry, bfr + 28 + i * sizeof(entry), sizeof(entry));
121		dev_warn(rtk->dev, "RTKit:  #%03d@%08x: %016llx %016llx", i,
122			 entry.timestamp, entry.msg0, entry.msg1);
123	}
124}
125
126static void apple_rtkit_crashlog_dump_regs(struct apple_rtkit *rtk, u8 *bfr,
127					   size_t size)
128{
129	struct apple_rtkit_crashlog_regs *regs;
130	const char *el;
131	int i;
132
133	if (size < sizeof(*regs)) {
134		dev_warn(rtk->dev, "RTKit: Regs section too small: 0x%zx", size);
135		return;
136	}
137
138	regs = (struct apple_rtkit_crashlog_regs *)bfr;
139
140	switch (regs->psr & PSR_MODE_MASK) {
141	case PSR_MODE_EL0t:
142		el = "EL0t";
143		break;
144	case PSR_MODE_EL1t:
145		el = "EL1t";
146		break;
147	case PSR_MODE_EL1h:
148		el = "EL1h";
149		break;
150	case PSR_MODE_EL2t:
151		el = "EL2t";
152		break;
153	case PSR_MODE_EL2h:
154		el = "EL2h";
155		break;
156	default:
157		el = "unknown";
158		break;
159	}
160
161	dev_warn(rtk->dev, "RTKit: Exception dump:");
162	dev_warn(rtk->dev, "  == Exception taken from %s ==", el);
163	dev_warn(rtk->dev, "  PSR    = 0x%llx", regs->psr);
164	dev_warn(rtk->dev, "  PC     = 0x%llx\n", regs->pc);
165	dev_warn(rtk->dev, "  ESR    = 0x%llx\n", regs->esr);
166	dev_warn(rtk->dev, "  FAR    = 0x%llx\n", regs->far);
167	dev_warn(rtk->dev, "  SP     = 0x%llx\n", regs->sp);
168	dev_warn(rtk->dev, "\n");
169
170	for (i = 0; i < 31; i += 4) {
171		if (i < 28)
172			dev_warn(rtk->dev,
173					 "  x%02d-x%02d = %016llx %016llx %016llx %016llx\n",
174					 i, i + 3,
175					 regs->regs[i], regs->regs[i + 1],
176					 regs->regs[i + 2], regs->regs[i + 3]);
177		else
178			dev_warn(rtk->dev,
179					 "  x%02d-x%02d = %016llx %016llx %016llx\n", i, i + 3,
180					 regs->regs[i], regs->regs[i + 1], regs->regs[i + 2]);
181	}
182
183	dev_warn(rtk->dev, "\n");
184}
185
186void apple_rtkit_crashlog_dump(struct apple_rtkit *rtk, u8 *bfr, size_t size)
187{
188	size_t offset;
189	u32 section_fourcc, section_size;
190	struct apple_rtkit_crashlog_header header;
191
192	memcpy(&header, bfr, sizeof(header));
193	if (header.fourcc != APPLE_RTKIT_CRASHLOG_HEADER) {
194		dev_warn(rtk->dev, "RTKit: Expected crashlog header but got %x",
195			 header.fourcc);
196		return;
197	}
198
199	if (header.size > size) {
200		dev_warn(rtk->dev, "RTKit: Crashlog size (%x) is too large",
201			 header.size);
202		return;
203	}
204
205	size = header.size;
206	offset = sizeof(header);
207
208	while (offset < size) {
209		memcpy(&section_fourcc, bfr + offset, 4);
210		memcpy(&section_size, bfr + offset + 12, 4);
211
212		switch (section_fourcc) {
213		case APPLE_RTKIT_CRASHLOG_HEADER:
214			dev_dbg(rtk->dev, "RTKit: End of crashlog reached");
215			return;
216		case APPLE_RTKIT_CRASHLOG_STR:
217			apple_rtkit_crashlog_dump_str(rtk, bfr + offset + 16,
218						      section_size);
219			break;
220		case APPLE_RTKIT_CRASHLOG_VERSION:
221			apple_rtkit_crashlog_dump_version(
222				rtk, bfr + offset + 16, section_size);
223			break;
224		case APPLE_RTKIT_CRASHLOG_MBOX:
225			apple_rtkit_crashlog_dump_mailbox(
226				rtk, bfr + offset + 16, section_size);
227			break;
228		case APPLE_RTKIT_CRASHLOG_TIME:
229			apple_rtkit_crashlog_dump_time(rtk, bfr + offset + 16,
230						       section_size);
231			break;
232		case APPLE_RTKIT_CRASHLOG_REGS:
233			apple_rtkit_crashlog_dump_regs(rtk, bfr + offset + 16,
234						       section_size);
235			break;
236		default:
237			dev_warn(rtk->dev,
238				 "RTKit: Unknown crashlog section: %x",
239				 section_fourcc);
240		}
241
242		offset += section_size;
243	}
244
245	dev_warn(rtk->dev,
246		 "RTKit: End of crashlog reached but no footer present");
247}
248