GNU Linux-libre 6.9.1-gnu
[releases.git] / drivers / soc / apple / rtkit-crashlog.c
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 }