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 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 { |
29 | u32 ; |
30 | u32 ; |
31 | u32 ; |
32 | u32 ; |
33 | u8 [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(p: ptr, c: '\n', size: 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 ; |
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(§ion_fourcc, bfr + offset, 4); |
210 | memcpy(§ion_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: bfr + offset + 16, |
218 | size: section_size); |
219 | break; |
220 | case APPLE_RTKIT_CRASHLOG_VERSION: |
221 | apple_rtkit_crashlog_dump_version( |
222 | rtk, bfr: bfr + offset + 16, size: section_size); |
223 | break; |
224 | case APPLE_RTKIT_CRASHLOG_MBOX: |
225 | apple_rtkit_crashlog_dump_mailbox( |
226 | rtk, bfr: bfr + offset + 16, size: section_size); |
227 | break; |
228 | case APPLE_RTKIT_CRASHLOG_TIME: |
229 | apple_rtkit_crashlog_dump_time(rtk, bfr: bfr + offset + 16, |
230 | size: section_size); |
231 | break; |
232 | case APPLE_RTKIT_CRASHLOG_REGS: |
233 | apple_rtkit_crashlog_dump_regs(rtk, bfr: bfr + offset + 16, |
234 | size: 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 | |