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 
28 struct apple_rtkit_crashlog_header {
29 	u32 fourcc;
30 	u32 version;
31 	u32 size;
32 	u32 flags;
33 	u8 _unk[16];
34 };
35 static_assert(sizeof(struct apple_rtkit_crashlog_header) == 0x20);
36 
37 struct apple_rtkit_crashlog_mbox_entry {
38 	u64 msg0;
39 	u64 msg1;
40 	u32 timestamp;
41 	u8 _unk[4];
42 };
43 static_assert(sizeof(struct apple_rtkit_crashlog_mbox_entry) == 0x18);
44 
45 struct 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;
61 static_assert(sizeof(struct apple_rtkit_crashlog_regs) == 0x350);
62 
63 static 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 
91 static 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 
97 static 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 
106 static 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 
126 static 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 
186 void 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