1 /*
2 * Copyright © 2022 Imagination Technologies Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include <inttypes.h>
25 #include <stdbool.h>
26 #include <stddef.h>
27 #include <stdint.h>
28
29 #include "pvr_dump.h"
30 #include "pvr_util.h"
31 #include "util/u_math.h"
32
33 const struct pvr_dump_ctx __pvr_dump_ctx_invalid = {
34 .active_child = &__pvr_dump_ctx_invalid,
35 };
36
37 /*****************************************************************************
38 Hex dumps
39 *****************************************************************************/
40
41 #define HEX_WORD_SIZE ((unsigned)sizeof(uint32_t))
42 #define HEX_BYTE_FMT "%02" PRIx8
43
44 /* This must be even, and should probably always be a power of 2. */
45 #define HEX_LINE_SIZE (HEX_WORD_SIZE * 8)
46
47 struct pvr_dump_hex_ctx {
48 struct pvr_dump_ctx base;
49
50 const uint8_t *start_ptr;
51 const uint8_t *end_ptr;
52 uint64_t nr_bytes;
53 uint32_t offset_digits;
54
55 /* User-modifiable values */
56 const uint8_t *line_ptr;
57
58 uint32_t prev_non_zero_trailing_zero_bytes;
59 uint64_t prev_non_zero_leading_zero_lines;
60 const uint8_t *prev_non_zero_line;
61 uint64_t zero_lines;
62 };
63
pvr_dump_hex_ctx_push(struct pvr_dump_hex_ctx * const ctx,struct pvr_dump_buffer_ctx * const parent_ctx,const uint64_t nr_bytes)64 static bool pvr_dump_hex_ctx_push(struct pvr_dump_hex_ctx *const ctx,
65 struct pvr_dump_buffer_ctx *const parent_ctx,
66 const uint64_t nr_bytes)
67 {
68 const uint64_t real_nr_bytes = nr_bytes ? nr_bytes
69 : parent_ctx->remaining_size;
70 bool ret;
71
72 if (parent_ctx->remaining_size < nr_bytes)
73 return false;
74
75 ret = pvr_dump_ctx_push(&ctx->base, &parent_ctx->base);
76 if (!ret)
77 return false;
78
79 ctx->start_ptr = parent_ctx->ptr;
80 ctx->end_ptr = ctx->start_ptr + real_nr_bytes;
81 ctx->nr_bytes = real_nr_bytes;
82 ctx->offset_digits = u64_hex_digits(real_nr_bytes);
83
84 ctx->line_ptr = ctx->start_ptr;
85
86 ctx->prev_non_zero_trailing_zero_bytes = 0;
87 ctx->prev_non_zero_leading_zero_lines = 0;
88 ctx->prev_non_zero_line = NULL;
89 ctx->zero_lines = 0;
90
91 return true;
92 }
93
94 static struct pvr_dump_buffer_ctx *
pvr_dump_hex_ctx_pop(struct pvr_dump_hex_ctx * const ctx)95 pvr_dump_hex_ctx_pop(struct pvr_dump_hex_ctx *const ctx)
96 {
97 struct pvr_dump_buffer_ctx *parent;
98 struct pvr_dump_ctx *parent_base;
99
100 if (ctx->line_ptr != ctx->end_ptr) {
101 ctx->base.ok = false;
102 return NULL;
103 }
104
105 parent_base = pvr_dump_ctx_pop(&ctx->base);
106 if (!parent_base)
107 return NULL;
108
109 parent = container_of(parent_base, struct pvr_dump_buffer_ctx, base);
110
111 pvr_dump_buffer_advance(parent, ctx->nr_bytes);
112
113 return parent;
114 }
115
pvr_dump_hex_print_prefix(const struct pvr_dump_hex_ctx * ctx,const uint64_t offset)116 static inline void pvr_dump_hex_print_prefix(const struct pvr_dump_hex_ctx *ctx,
117 const uint64_t offset)
118 {
119 pvr_dump_printf(&ctx->base,
120 PVR_DUMP_OFFSET_PREFIX,
121 ctx->offset_digits,
122 offset);
123 }
124
125 #define pvr_dump_hex_println(ctx, offset, format, args...) \
126 pvr_dump_println(&(ctx)->base, \
127 PVR_DUMP_OFFSET_PREFIX format, \
128 (ctx)->offset_digits, \
129 offset, \
130 ##args)
131
132 #define pvr_dump_hex_println_no_prefix(ctx, format, args...) \
133 pvr_dump_println(&(ctx)->base, \
134 "%*c" format, \
135 (ctx)->offset_digits + 3, \
136 ' ', \
137 ##args)
138
pvr_dump_hex_print_line(const struct pvr_dump_hex_ctx * ctx,const uint8_t * const line_ptr,const uint32_t truncate)139 static void pvr_dump_hex_print_line(const struct pvr_dump_hex_ctx *ctx,
140 const uint8_t *const line_ptr,
141 const uint32_t truncate)
142 {
143 const uint32_t nr_bytes =
144 MIN2(HEX_LINE_SIZE - truncate, ctx->end_ptr - line_ptr);
145
146 pvr_dump_hex_print_prefix(ctx, line_ptr - ctx->start_ptr);
147
148 for (uint32_t i = 0; i < nr_bytes; i++) {
149 if (i == HEX_LINE_SIZE / 2)
150 pvr_dump_printf_cont(&ctx->base, " ");
151
152 if (i % HEX_WORD_SIZE == 0)
153 pvr_dump_printf_cont(&ctx->base, " ");
154
155 if (line_ptr[i])
156 pvr_dump_printf_cont(&ctx->base, HEX_BYTE_FMT, line_ptr[i]);
157 else
158 pvr_dump_printf_cont(&ctx->base, "..");
159 }
160
161 pvr_dump_print_eol(&ctx->base);
162 }
163
164 static void
pvr_dump_hex_print_zero_lines(const struct pvr_dump_hex_ctx * const ctx,const uint64_t zero_lines)165 pvr_dump_hex_print_zero_lines(const struct pvr_dump_hex_ctx *const ctx,
166 const uint64_t zero_lines)
167 {
168 const uint64_t zero_bytes = zero_lines * HEX_LINE_SIZE;
169
170 if (!zero_lines)
171 return;
172
173 /* If we've only buffered a single zero line, print it normally. We don't
174 * save any space by folding it, and it's more readable this way.
175 */
176 if (zero_lines == 1) {
177 pvr_dump_hex_print_line(ctx, ctx->prev_non_zero_line + HEX_LINE_SIZE, 0);
178 return;
179 }
180
181 pvr_dump_hex_println_no_prefix(ctx,
182 " + %" PRIu64 " zero line%s (%" PRIu64
183 "/0x%" PRIx64 " bytes)",
184 zero_lines,
185 zero_lines == 1 ? "" : "s",
186 zero_bytes,
187 zero_bytes);
188 }
189
190 static void
pvr_dump_hex_print_trailing_zeroes(const struct pvr_dump_hex_ctx * const ctx)191 pvr_dump_hex_print_trailing_zeroes(const struct pvr_dump_hex_ctx *const ctx)
192 {
193 const uint64_t zero_bytes =
194 ctx->zero_lines * HEX_LINE_SIZE + ctx->prev_non_zero_trailing_zero_bytes;
195
196 if (!ctx->prev_non_zero_trailing_zero_bytes)
197 return pvr_dump_hex_print_zero_lines(ctx, ctx->zero_lines);
198
199 if (!ctx->zero_lines)
200 return;
201
202 pvr_dump_hex_println_no_prefix(ctx,
203 " + %" PRIu64 "+%" PRIu32
204 " zero lines (%" PRIu64 "/0x%" PRIx64
205 " bytes)",
206 ctx->zero_lines,
207 ctx->prev_non_zero_trailing_zero_bytes,
208 zero_bytes,
209 zero_bytes);
210 }
211
pvr_dump_hex_process_line(struct pvr_dump_hex_ctx * const ctx,uint32_t truncate)212 static void pvr_dump_hex_process_line(struct pvr_dump_hex_ctx *const ctx,
213 uint32_t truncate)
214 {
215 const uint32_t max_bytes = HEX_LINE_SIZE - truncate;
216
217 uint32_t trailing_zero_bytes = max_bytes;
218
219 for (uint32_t i = max_bytes; i > 0; i--) {
220 if (ctx->line_ptr[i - 1]) {
221 trailing_zero_bytes = HEX_LINE_SIZE - i;
222 break;
223 }
224 }
225
226 if (trailing_zero_bytes == max_bytes) {
227 /* No non-zero words were found in this line; mark it and move on. */
228 ctx->zero_lines++;
229 return;
230 }
231
232 /* We have at least one non-zero word in this line. If we have a previous
233 * non-zero line stored, collapse and print any leading zero-only lines
234 * before it then print the stored line.
235 */
236 if (ctx->prev_non_zero_line) {
237 pvr_dump_hex_print_zero_lines(ctx, ctx->prev_non_zero_leading_zero_lines);
238 pvr_dump_hex_print_line(ctx, ctx->prev_non_zero_line, truncate);
239 }
240
241 /* Now we store the current non-zero line for printing later. This way we
242 * can treat the last non-zero line specially.
243 */
244 ctx->prev_non_zero_line = ctx->line_ptr;
245 ctx->prev_non_zero_leading_zero_lines = ctx->zero_lines;
246 ctx->prev_non_zero_trailing_zero_bytes = trailing_zero_bytes;
247 ctx->zero_lines = 0;
248 }
249
pvr_dump_hex(struct pvr_dump_hex_ctx * const ctx)250 static void pvr_dump_hex(struct pvr_dump_hex_ctx *const ctx)
251 {
252 while (ctx->line_ptr < (ctx->end_ptr - HEX_LINE_SIZE)) {
253 pvr_dump_hex_process_line(ctx, 0);
254 ctx->line_ptr += HEX_LINE_SIZE;
255 }
256
257 pvr_dump_hex_process_line(ctx,
258 HEX_LINE_SIZE - (ctx->end_ptr - ctx->line_ptr));
259 ctx->line_ptr = ctx->end_ptr;
260
261 if (ctx->prev_non_zero_line) {
262 /* If we don't have any zero lines to collapse, print the trailing zeroes
263 * on the last line.
264 */
265 if (!ctx->zero_lines) {
266 pvr_dump_hex_print_line(ctx, ctx->prev_non_zero_line, 0);
267 } else {
268 pvr_dump_hex_print_zero_lines(ctx,
269 ctx->prev_non_zero_leading_zero_lines);
270
271 pvr_dump_hex_print_line(ctx,
272 ctx->prev_non_zero_line,
273 ctx->prev_non_zero_trailing_zero_bytes);
274
275 /* Collapse and print any trailing zeroes. */
276 pvr_dump_hex_print_trailing_zeroes(ctx);
277 }
278 } else {
279 /* We made it to the end of the buffer without ever encountering a
280 * non-zero word. Make this known.
281 */
282 pvr_dump_hex_println(ctx, UINT64_C(0), " <empty buffer>");
283 }
284
285 pvr_dump_hex_println(ctx, ctx->nr_bytes, " <end of buffer>");
286 }
287
pvr_dump_buffer_hex(struct pvr_dump_buffer_ctx * const ctx,const uint64_t nr_bytes)288 bool pvr_dump_buffer_hex(struct pvr_dump_buffer_ctx *const ctx,
289 const uint64_t nr_bytes)
290 {
291 struct pvr_dump_hex_ctx hex_ctx;
292
293 if (!pvr_dump_hex_ctx_push(&hex_ctx, ctx, nr_bytes))
294 return false;
295
296 pvr_dump_hex(&hex_ctx);
297
298 return !!pvr_dump_hex_ctx_pop(&hex_ctx);
299 }
300